Skip to content

Commit

Permalink
Add inMemory support
Browse files Browse the repository at this point in the history
  • Loading branch information
ospfranco committed Nov 9, 2023
1 parent ab9f90d commit 0280f49
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 57 deletions.
3 changes: 3 additions & 0 deletions .watchmanconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"prefer_split_fsevents_watcher": true
}
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ The library creates/opens databases by appending the passed name plus, the [libr
If you have an existing database file you want to load you can navigate from these directories using dot notation. e.g.:

```ts
import { open } from '@op-engineering/op-sqlite';

const largeDb = open({
name: 'largeDB',
location: '../files/databases',
Expand All @@ -56,7 +58,20 @@ const largeDb = open({

Note that on iOS the file system is sand-boxed, so you cannot access files/directories outside your app bundle directories.

## API
## In memory

Using SQLite in memory mode is supported:

```ts
import { open } from '@op-engineering/op-sqlite';

const largeDb = open({
name: 'inMemoryDb',
inMemory: true,
});
```

# API

```typescript
import {open} from '@op-engineering/op-sqlite'
Expand Down Expand Up @@ -89,7 +104,7 @@ The basic query is **synchronous**, it will block rendering on large operations,
import { open } from '@op-engineering/op-sqlite';

try {
const db = open('myDb.sqlite');
const db = open({ name: 'myDb.sqlite' });

let { rows } = db.execute('SELECT somevalue FROM sometable');

Expand Down
13 changes: 11 additions & 2 deletions cpp/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
pool = std::make_shared<ThreadPool>();
invoker = jsCallInvoker;

auto open = HOSTFN("open", 2) {
auto open = HOSTFN("open", 3) {
if (count == 0)
{
throw jsi::JSError(rt, "[op-sqlite][open] database name is required");
Expand All @@ -41,6 +41,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker

std::string dbName = args[0].asString(rt).utf8(rt);
std::string tempDocPath = std::string(basePath);
bool memoryStorage = false;

if (count > 1 && !args[1].isUndefined() && !args[1].isNull())
{
Expand All @@ -52,7 +53,15 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
tempDocPath = tempDocPath + "/" + args[1].asString(rt).utf8(rt);
}

BridgeResult result = sqliteOpenDb(dbName, tempDocPath);
if(count == 3) {
if(!args[2].isBool()) {
throw jsi::JSError(rt, "[op-sqlite][open] memoryStorage must be a bool");
}

memoryStorage = args[2].asBool();
}

BridgeResult result = sqliteOpenDb(dbName, tempDocPath, memoryStorage);

if (result.type == SQLiteError)
{
Expand Down
4 changes: 2 additions & 2 deletions cpp/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ std::string get_db_path(std::string const dbName, std::string const docPath)
return docPath + "/" + dbName;
}

BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath)
BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath, bool memoryStorage)
{
std::string dbPath = get_db_path(dbName, docPath);
std::string dbPath = memoryStorage ? ":memory:" : get_db_path(dbName, docPath);

int sqlOpenFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;

Expand Down
2 changes: 1 addition & 1 deletion cpp/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace osp {

namespace jsi = facebook::jsi;

BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath);
BridgeResult sqliteOpenDb(std::string const dbName, std::string const docPath, bool memoryStorage);

BridgeResult sqliteCloseDb(std::string const dbName);

Expand Down
11 changes: 6 additions & 5 deletions example/src/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,17 @@ const DB_NAME = 'largeDB';
export async function createLargeDB() {
let largeDb = open({
name: DB_NAME,
// inMemory: true,
});

largeDb.execute('DROP TABLE IF EXISTS Test;');
largeDb.execute(
'CREATE TABLE Test ( id INT PRIMARY KEY, v1 TEXT, v2 TEXT, v3 TEXT, v4 TEXT, v5 TEXT, v6 INT, v7 INT, v8 INT, v9 INT, v10 INT, v11 REAL, v12 REAL, v13 REAL, v14 REAL) STRICT;',
);

let insertions: [string, any[]][] = [];
for (let i = 0; i < ROWS; i++) {
if (i % 100 === 0) {
console.log(`Inserted ${i}`);
}
await largeDb.executeAsync(
insertions.push([
'INSERT INTO "Test" (id, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[
i,
Expand All @@ -41,9 +40,11 @@ export async function createLargeDB() {
chance.floating(),
chance.floating(),
],
);
]);
}

await largeDb.executeBatchAsync(insertions);

console.log(`inserted ${ROWS}`);

largeDb.close();
Expand Down
1 change: 0 additions & 1 deletion example/src/tests/all.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export function registerTests() {
});
expect.fail('Should throw');
} catch (e) {
console.warn('blah');
expect(!!e).to.equal(true);
}
});
Expand Down
81 changes: 41 additions & 40 deletions ios/OPSQLite.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,53 @@ @implementation OPSQLite

RCT_EXPORT_MODULE(OPSQLite)


RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
NSLog(@"Installing OPSQLite module...");

RCTBridge *bridge = [RCTBridge currentBridge];
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)bridge;
if (cxxBridge == nil) {
return @false;
}

using namespace facebook;

auto jsiRuntime = (jsi::Runtime *)cxxBridge.runtime;
if (jsiRuntime == nil) {
return @false;
}
auto &runtime = *jsiRuntime;
auto callInvoker = bridge.jsCallInvoker;

NSLog(@"Initializing OP-SQLite module...");
RCTBridge *bridge = [RCTBridge currentBridge];
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)bridge;
if (cxxBridge == nil) {
return @false;
}
using namespace facebook;
auto jsiRuntime = (jsi::Runtime *)cxxBridge.runtime;
if (jsiRuntime == nil) {
return @false;
}
auto &runtime = *jsiRuntime;
auto callInvoker = bridge.jsCallInvoker;
// Get appGroupID value from Info.plist using key "AppGroup"
NSString *appGroupID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"OPSQLite_AppGroup"];
NSString *documentPath;

if (appGroupID != nil) {
// Get the app groups container storage url
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *storeUrl = [fileManager containerURLForSecurityApplicationGroupIdentifier:appGroupID];

if (storeUrl == nil) {
NSLog(@"Invalid AppGroup ID provided (%@). Check the value of \"AppGroup\" in your Info.plist file", appGroupID);
return @false;
NSString *appGroupID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"OPSQLite_AppGroup"];
NSString *documentPath;

if (appGroupID != nil) {
// Get the app groups container storage url
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *storeUrl = [fileManager containerURLForSecurityApplicationGroupIdentifier:appGroupID];

if (storeUrl == nil) {
NSLog(@"Invalid AppGroup ID provided (%@). Check the value of \"AppGroup\" in your Info.plist file", appGroupID);
return @false;
}
NSLog(@"Configured with AppGroup ID: %@", appGroupID);

documentPath = [storeUrl path];
} else {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, true);
documentPath = [paths objectAtIndex:0];
}
NSLog(@"Configured with AppGroup ID: %@", appGroupID);

documentPath = [storeUrl path];
} else {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, true);
documentPath = [paths objectAtIndex:0];
}

osp::install(runtime, callInvoker, [documentPath UTF8String]);
return @true;

osp::install(runtime, callInvoker, [documentPath UTF8String]);

NSLog(@"OP-SQLite initialized");
return @true;
}

- (void)invalidate {
osp::clearState();
osp::clearState();
}

@end
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@op-engineering/op-sqlite",
"version": "1.0.3",
"version": "1.0.4",
"description": "Next generation SQLite for React Native",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
7 changes: 4 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export interface PendingTransaction {
}

interface ISQLite {
open: (dbName: string, location?: string) => void;
open: (dbName: string, location?: string, inMemory?: boolean) => void;
close: (dbName: string) => void;
delete: (dbName: string, location?: string) => void;
attach: (
Expand Down Expand Up @@ -181,7 +181,7 @@ const enhanceQueryResult = (result: QueryResult): void => {
};

const _open = OPSQLite.open;
OPSQLite.open = (dbName: string, location?: string) => {
OPSQLite.open = (dbName: string, location?: string, inMemory?: boolean) => {
_open(dbName, location);

locks[dbName] = {
Expand Down Expand Up @@ -347,8 +347,9 @@ export type OPSQLiteConnection = {
export const open = (options: {
name: string;
location?: string;
inMemory?: boolean;
}): OPSQLiteConnection => {
OPSQLite.open(options.name, options.location);
OPSQLite.open(options.name, options.location, options.inMemory);

return {
close: () => OPSQLite.close(options.name),
Expand Down

0 comments on commit 0280f49

Please sign in to comment.