diff --git a/cpp/DBHostObject.cpp b/cpp/DBHostObject.cpp index cc32a83..57a4665 100644 --- a/cpp/DBHostObject.cpp +++ b/cpp/DBHostObject.cpp @@ -238,6 +238,8 @@ void DBHostObject::create_jsi_functions() { }); function_map["close"] = HOSTFN("close") { + invalidated = true; + #ifdef OP_SQLITE_USE_LIBSQL opsqlite_libsql_close(db); #else @@ -248,6 +250,8 @@ void DBHostObject::create_jsi_functions() { }); function_map["delete"] = HOSTFN("delete") { + invalidated = true; + std::string path = std::string(base_path); if (count == 1) { @@ -808,6 +812,10 @@ void DBHostObject::set(jsi::Runtime &_rt, const jsi::PropNameID &name, } void DBHostObject::invalidate() { + if(invalidated) { + return; + } + invalidated = true; _thread_pool->restartPool(); #ifdef OP_SQLITE_USE_LIBSQL diff --git a/example/src/tests/dbsetup.spec.ts b/example/src/tests/dbsetup.spec.ts index 734b838..4750f3d 100644 --- a/example/src/tests/dbsetup.spec.ts +++ b/example/src/tests/dbsetup.spec.ts @@ -82,10 +82,26 @@ export function dbSetupTests() { 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;', ); - await androidDb.close(); + androidDb.close(); + }); + + it('Creates db in external nested directory on Android', async () => { + let androidDb = open({ + name: 'AndroidSDCardDB.sqlite', + location: `${ANDROID_EXTERNAL_FILES_PATH}/nested`, + encryptionKey: 'test', + }); + + await androidDb.execute('DROP TABLE IF EXISTS User;'); + await androidDb.execute( + 'CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;', + ); + + androidDb.close(); }); } + // Currently this only tests the function is there it('Should load extension', async () => { let db = open({ name: 'extensionDb', @@ -112,13 +128,16 @@ export function dbSetupTests() { }); it('Should delete db with absolute path', async () => { + let location = + Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH; let db = open({ name: 'deleteTest', encryptionKey: 'test', - location: - Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH, + location, }); + expect(db.getDbPath()).to.contain(location); + db.delete(); }); @@ -129,6 +148,20 @@ export function dbSetupTests() { location: 'myFolder', }); + let path = db.getDbPath(); + expect(path).to.contain('myFolder'); + db.delete(); + }); + + it('Should create nested folders', async () => { + let db = open({ + name: 'nestedFolderTest.sqlite', + encryptionKey: 'test', + location: 'myFolder/nested', + }); + + let path = db.getDbPath(); + expect(path).to.contain('myFolder/nested'); db.delete(); }); @@ -155,18 +188,17 @@ export function dbSetupTests() { }); expect(copied).to.equal(true); - }); - // it('Should fail creating in-memory with non-bool arg', async () => { - // try { - // open({ - // name: 'inMemoryTest', - // }); - // expect.fail('Should throw'); - // } catch (e) { - // expect(!!e).to.equal(true); - // } - // }); + let db = open({ + name: 'sample2.sqlite', + encryptionKey: 'test', + location: 'sqlite', + }); + + let path = db.getDbPath(); + expect(path).to.contain('sqlite/sample2.sqlite'); + db.delete(); + }); it('Creates new connections per query and closes them', async () => { for (let i = 0; i < 100; i++) { @@ -235,10 +267,11 @@ export function dbSetupTests() { }); if (isSQLCipher()) { - it('Can open without encryption key', () => { + it('Can open SQLCipher db without encryption key', () => { let db = open({ name: 'pathTest.sqlite', }); + db.close(); }); } diff --git a/example/src/tests/queries.spec.ts b/example/src/tests/queries.spec.ts index 77de95a..17913ef 100644 --- a/example/src/tests/queries.spec.ts +++ b/example/src/tests/queries.spec.ts @@ -238,9 +238,6 @@ export function queriesTests() { const countRes = await db.execute('SELECT COUNT(*) as count FROM User'); - // console.log(countRes); - - // expect(countRes.metadata?.[0]?.type).to.equal('UNKNOWN'); expect(countRes.rows?.length).to.equal(1); expect(countRes.rows?.[0]?.count).to.equal(1); @@ -257,18 +254,14 @@ export function queriesTests() { const sumRes = await db.execute('SELECT SUM(age) as sum FROM User;'); - // expect(sumRes.metadata?.[0]?.type).to.equal('UNKNOWN'); expect(sumRes.rows[0]!.sum).to.equal(age + age2); - // MAX(networth), MIN(networth) const maxRes = await db.execute( 'SELECT MAX(networth) as `max` FROM User;', ); const minRes = await db.execute( 'SELECT MIN(networth) as `min` FROM User;', ); - // expect(maxRes.metadata?.[0]?.type).to.equal('UNKNOWN'); - // expect(minRes.metadata?.[0]?.type).to.equal('UNKNOWN'); const maxNetworth = Math.max(networth, networth2); const minNetworth = Math.min(networth, networth2); @@ -707,6 +700,32 @@ export function queriesTests() { await db.execute('SELECT ?; ', [1]); }); + it('Handles concurrent transactions correctly', async () => { + const id = chance.integer(); + const name = chance.name(); + const age = chance.integer(); + const networth = chance.floating(); + + const transaction1 = db.transaction(async tx => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id, name, age, networth], + ); + }); + + const transaction2 = db.transaction(async tx => { + await tx.execute( + 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', + [id + 1, name, age, networth], + ); + }); + + await Promise.all([transaction1, transaction2]); + + const res = await db.execute('SELECT * FROM User'); + expect(res.rows.length).to.equal(2); + }); + it('Pragma user_version', () => { const res = db.executeSync('PRAGMA user_version'); console.warn(res.rows);