Skip to content

Commit 1af8d7f

Browse files
committed
Added support commit and rollbacks hooks
1 parent 5d5deff commit 1af8d7f

File tree

7 files changed

+234
-31
lines changed

7 files changed

+234
-31
lines changed

cpp/bindings.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ std::string basePath;
1919
std::shared_ptr<react::CallInvoker> invoker;
2020
ThreadPool pool;
2121
std::unordered_map<std::string, std::shared_ptr<jsi::Value>> updateHooks = std::unordered_map<std::string, std::shared_ptr<jsi::Value>>();
22+
std::unordered_map<std::string, std::shared_ptr<jsi::Value>> commitHooks = std::unordered_map<std::string, std::shared_ptr<jsi::Value>>();
23+
std::unordered_map<std::string, std::shared_ptr<jsi::Value>> rollbackHooks = std::unordered_map<std::string, std::shared_ptr<jsi::Value>>();
2224

2325

2426
// React native will try to clean the module on JS context invalidation (CodePush/Hot Reload)
@@ -479,6 +481,55 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
479481
return {};
480482
});
481483

484+
auto commitHook = HOSTFN("commitHook", 2)
485+
{
486+
if (sizeof(args) < 2)
487+
{
488+
throw jsi::JSError(rt, "[op-sqlite][loadFileAsync] Incorrect parameters: dbName and callback needed");
489+
return {};
490+
}
491+
492+
auto dbName = args[0].asString(rt).utf8(rt);
493+
auto callback = std::make_shared<jsi::Value>(rt, args[1]);
494+
commitHooks[dbName] = callback;
495+
496+
auto hook = [&rt, callback](std::string dbName) {
497+
invoker->invokeAsync([&rt, callback]
498+
{
499+
callback->asObject(rt).asFunction(rt).call(rt);
500+
});
501+
};
502+
503+
registerCommitHook(dbName, std::move(hook));
504+
505+
return {};
506+
});
507+
508+
auto rollbackHook = HOSTFN("rollbackHook", 2)
509+
{
510+
if (sizeof(args) < 2)
511+
{
512+
throw jsi::JSError(rt, "[op-sqlite][loadFileAsync] Incorrect parameters: dbName and callback needed");
513+
return {};
514+
}
515+
516+
auto dbName = args[0].asString(rt).utf8(rt);
517+
auto callback = std::make_shared<jsi::Value>(rt, args[1]);
518+
rollbackHooks[dbName] = callback;
519+
520+
auto hook = [&rt, callback](std::string dbName) {
521+
522+
invoker->invokeAsync([&rt, callback] {
523+
callback->asObject(rt).asFunction(rt).call(rt);
524+
525+
});
526+
};
527+
528+
registerRollbackHook(dbName, std::move(hook));
529+
530+
return {};
531+
});
532+
482533
jsi::Object module = jsi::Object(rt);
483534

484535
module.setProperty(rt, "open", std::move(open));
@@ -492,6 +543,8 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> jsCallInvoker
492543
module.setProperty(rt, "executeBatchAsync", std::move(executeBatchAsync));
493544
module.setProperty(rt, "loadFile", std::move(loadFile));
494545
module.setProperty(rt, "updateHook", std::move(updateHook));
546+
module.setProperty(rt, "commitHook", std::move(commitHook));
547+
module.setProperty(rt, "rollbackHook", std::move(rollbackHook));
495548

496549
rt.global().setProperty(rt, "__OPSQLiteProxy", std::move(module));
497550
}

cpp/bridge.cpp

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@ namespace opsqlite {
1616
std::unordered_map<std::string, sqlite3 *> dbMap = std::unordered_map<std::string, sqlite3 *>();
1717
std::unordered_map<
1818
std::string,
19-
std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)>> callbackMap =
19+
std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)>> updateCallbackMap =
2020
std::unordered_map<
2121
std::string,
2222
std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)>>();
23+
24+
std::unordered_map<
25+
std::string,
26+
std::function<void (std::string dbName)>> commitCallbackMap =
27+
std::unordered_map<std::string, std::function<void (std::string dbName)>>();
28+
29+
std::unordered_map<
30+
std::string,
31+
std::function<void (std::string dbName)>> rollbackCallbackMap =
32+
std::unordered_map<std::string, std::function<void (std::string dbName)>>();
2333

2434
bool folder_exists(const std::string &foldername)
2535
{
@@ -490,32 +500,34 @@ namespace opsqlite {
490500
dbMap.clear();
491501
}
492502

493-
std::string operationToString(int operation_type) {
494-
switch (operation_type) {
495-
case SQLITE_INSERT:
496-
return "INSERT";
497-
498-
case SQLITE_DELETE:
499-
return "DELETE";
503+
std::string operationToString(int operation_type) {
504+
switch (operation_type) {
505+
case SQLITE_INSERT:
506+
return "INSERT";
500507

501-
case SQLITE_UPDATE:
502-
return "UPDATE";
503-
504-
default:
505-
throw std::invalid_argument("Uknown SQLite operation on hook");
508+
case SQLITE_DELETE:
509+
return "DELETE";
510+
511+
case SQLITE_UPDATE:
512+
return "UPDATE";
513+
514+
default:
515+
throw std::invalid_argument("Uknown SQLite operation on hook");
516+
}
506517
}
507-
}
508518

509-
void update_callback ( void *dbName, int operation_type,
510-
char const *database, char const *table,
511-
sqlite3_int64 rowid)
512-
{
513-
std::string &strDbName = *(static_cast<std::string*>(dbName));
514-
auto callback = callbackMap[strDbName];
515-
callback(strDbName, std::string(table), operationToString(operation_type), static_cast<int>(rowid));
519+
void update_callback(void *dbName,
520+
int operation_type,
521+
char const *database,
522+
char const *table,
523+
sqlite3_int64 rowid) {
524+
std::string &strDbName = *(static_cast<std::string*>(dbName));
525+
auto callback = updateCallbackMap[strDbName];
526+
callback(strDbName, std::string(table), operationToString(operation_type), static_cast<int>(rowid));
516527
}
517528

518-
BridgeResult registerUpdateHook(std::string const dbName, std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)> const callback) {
529+
BridgeResult registerUpdateHook(std::string const dbName,
530+
std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)> const callback) {
519531
if (dbMap.count(dbName) == 0)
520532
{
521533
return {
@@ -525,7 +537,7 @@ void update_callback ( void *dbName, int operation_type,
525537
}
526538

527539
sqlite3 *db = dbMap[dbName];
528-
callbackMap[dbName] = callback;
540+
updateCallbackMap[dbName] = callback;
529541
const std::string *key = nullptr;
530542

531543
// TODO find a more elegant way to retrieve a reference to the key
@@ -544,4 +556,80 @@ void update_callback ( void *dbName, int operation_type,
544556
SQLiteOk
545557
};
546558
}
559+
560+
int commit_callback(void *dbName) {
561+
std::string &strDbName = *(static_cast<std::string*>(dbName));
562+
auto callback = commitCallbackMap[strDbName];
563+
callback(strDbName);
564+
// You need to return 0 to allow commits to continue
565+
return 0;
566+
}
567+
568+
BridgeResult registerCommitHook(std::string const dbName,
569+
std::function<void (std::string dbName)> const callback) {
570+
if (dbMap.count(dbName) == 0)
571+
{
572+
return {
573+
SQLiteError,
574+
"[op-sqlite] Database not opened: " + dbName
575+
};
576+
}
577+
578+
sqlite3 *db = dbMap[dbName];
579+
commitCallbackMap[dbName] = callback;
580+
const std::string *key = nullptr;
581+
582+
// TODO find a more elegant way to retrieve a reference to the key
583+
for (auto const& element : dbMap) {
584+
if(element.first == dbName) {
585+
key = &element.first;
586+
}
587+
}
588+
589+
sqlite3_commit_hook(
590+
db,
591+
&commit_callback,
592+
(void *)key);
593+
594+
return {
595+
SQLiteOk
596+
};
597+
}
598+
599+
void rollback_callback(void *dbName) {
600+
std::string &strDbName = *(static_cast<std::string*>(dbName));
601+
auto callback = rollbackCallbackMap[strDbName];
602+
callback(strDbName);
603+
}
604+
605+
BridgeResult registerRollbackHook(std::string const dbName,
606+
std::function<void (std::string dbName)> const callback) {
607+
if (dbMap.count(dbName) == 0)
608+
{
609+
return {
610+
SQLiteError,
611+
"[op-sqlite] Database not opened: " + dbName
612+
};
613+
}
614+
615+
sqlite3 *db = dbMap[dbName];
616+
rollbackCallbackMap[dbName] = callback;
617+
const std::string *key = nullptr;
618+
619+
// TODO find a more elegant way to retrieve a reference to the key
620+
for (auto const& element : dbMap) {
621+
if(element.first == dbName) {
622+
key = &element.first;
623+
}
624+
}
625+
626+
sqlite3_rollback_hook(
627+
db,
628+
&rollback_callback,
629+
(void *)key);
630+
631+
return {
632+
SQLiteOk
633+
};
634+
}
547635
}

cpp/bridge.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ BridgeResult sqliteExecuteLiteral(std::string const dbName, std::string const &q
3131

3232
void sqliteCloseAll();
3333

34-
BridgeResult registerUpdateHook(std::string const dbName, std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)> const callback);
34+
BridgeResult registerUpdateHook(std::string const dbName,
35+
std::function<void (std::string dbName, std::string tableName, std::string operation, int rowId)> const callback);
36+
BridgeResult registerCommitHook(std::string const dbName,
37+
std::function<void (std::string dbName)> const callback);
38+
BridgeResult registerRollbackHook(std::string const dbName,
39+
std::function<void (std::string dbName)> const callback);
3540

3641
}
3742

example/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {dbSetupTests, queriesTests, runTests, blobTests} from './tests/index';
1414
import pak from '../package.json';
1515
import {styled} from 'nativewind';
1616
import RNRestart from 'react-native-restart';
17-
import {updateHookTests} from './tests/updateHook.spec';
17+
import {registerHooksTests} from './tests/hooks';
1818

1919
const StyledScrollView = styled(ScrollView, {
2020
props: {
@@ -30,7 +30,7 @@ export default function App() {
3030

3131
useEffect(() => {
3232
setResults([]);
33-
runTests(dbSetupTests, queriesTests, blobTests, updateHookTests).then(
33+
runTests(dbSetupTests, queriesTests, blobTests, registerHooksTests).then(
3434
setResults,
3535
);
3636
}, []);

example/src/tests/updateHook.spec.ts renamed to example/src/tests/hooks.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ async function sleep(ms: number): Promise<void> {
1212

1313
const expect = chai.expect;
1414
const DB_CONFIG = {
15-
name: 'updateHookDb',
15+
name: 'hooksDb',
1616
};
1717
const chance = new Chance();
1818

1919
let db: OPSQLiteConnection;
2020

21-
export function updateHookTests() {
21+
export function registerHooksTests() {
2222
beforeEach(() => {
2323
try {
2424
if (db) {
@@ -37,8 +37,8 @@ export function updateHookTests() {
3737
}
3838
});
3939

40-
describe('Update hook', () => {
41-
it('Should register an update hook', async () => {
40+
describe('Hooks', () => {
41+
it('update hook', async () => {
4242
let promiseResolve: any;
4343
let promise = new Promise(resolve => {
4444
promiseResolve = resolve;
@@ -64,5 +64,56 @@ export function updateHookTests() {
6464

6565
expect(operation).to.equal('INSERT');
6666
});
67+
68+
it('commit hook', async () => {
69+
let promiseResolve: any;
70+
let promise = new Promise(resolve => {
71+
promiseResolve = resolve;
72+
});
73+
74+
db.commitHook(() => {
75+
promiseResolve?.();
76+
});
77+
78+
const id = chance.integer();
79+
const name = chance.name();
80+
const age = chance.integer();
81+
const networth = chance.floating();
82+
await db.transaction(async tx => {
83+
tx.execute(
84+
'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)',
85+
[id, name, age, networth],
86+
);
87+
});
88+
89+
await promise;
90+
});
91+
92+
it('rollback hook', async () => {
93+
let promiseResolve: any;
94+
let promise = new Promise(resolve => {
95+
promiseResolve = resolve;
96+
});
97+
98+
db.rollbackHook(() => {
99+
promiseResolve?.();
100+
});
101+
102+
const id = chance.integer();
103+
const name = chance.name();
104+
const age = chance.integer();
105+
const networth = chance.floating();
106+
console.warn('1');
107+
try {
108+
await db.transaction(async tx => {
109+
throw new Error('Blah');
110+
});
111+
} catch (e) {
112+
// intentionally left blank
113+
}
114+
115+
console.warn('2');
116+
await promise;
117+
});
67118
});
68119
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@op-engineering/op-sqlite",
3-
"version": "1.0.12",
3+
"version": "1.0.13",
44
"description": "Next generation SQLite for React Native",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ interface ISQLite {
168168
rowId: number;
169169
}) => void
170170
) => void;
171+
commitHook: (dbName: string, callback: () => void) => void;
172+
rollbackHook: (dbName: string, callback: () => void) => void;
171173
}
172174

173175
const locks: Record<
@@ -382,6 +384,8 @@ export type OPSQLiteConnection = {
382384
rowId: number;
383385
}) => void
384386
) => void;
387+
commitHook: (callback: () => void) => void;
388+
rollbackHook: (callback: () => void) => void;
385389
};
386390

387391
export const open = (options: {
@@ -412,5 +416,7 @@ export const open = (options: {
412416
OPSQLite.executeBatchAsync(options.name, commands),
413417
loadFile: (location: string) => OPSQLite.loadFile(options.name, location),
414418
updateHook: (callback) => OPSQLite.updateHook(options.name, callback),
419+
commitHook: (callback) => OPSQLite.commitHook(options.name, callback),
420+
rollbackHook: (callback) => OPSQLite.rollbackHook(options.name, callback),
415421
};
416422
};

0 commit comments

Comments
 (0)