diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp --- a/web/cpp/SQLiteQueryExecutorBindings.cpp +++ b/web/cpp/SQLiteQueryExecutorBindings.cpp @@ -71,7 +71,10 @@ .function("replaceThreadWeb", &SQLiteQueryExecutor::replaceThreadWeb) .function("getAllThreadsWeb", &SQLiteQueryExecutor::getAllThreadsWeb) .function("removeAllThreads", &SQLiteQueryExecutor::removeAllThreads) - .function("removeThreads", &SQLiteQueryExecutor::removeThreads); + .function("removeThreads", &SQLiteQueryExecutor::removeThreads) + .function("beginTransaction", &SQLiteQueryExecutor::beginTransaction) + .function("commitTransaction", &SQLiteQueryExecutor::commitTransaction) + .function("rollbackTransaction", &SQLiteQueryExecutor::rollbackTransaction); } } // namespace comm diff --git a/web/database/_generated/comm_query_executor.wasm b/web/database/_generated/comm_query_executor.wasm index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ module.FS.stat(FILE_PATH)).toThrow(); }); }); + +describe('Database transactions', () => { + it('should commit transaction', async () => { + const module = getDatabaseModule(); + const queryExecutor = new module.SQLiteQueryExecutor(FILE_PATH); + queryExecutor.beginTransaction(); + queryExecutor.setMetadata(TEST_KEY, TEST_VAL); + queryExecutor.commitTransaction(); + expect(queryExecutor.getMetadata(TEST_KEY)).toBe(TEST_VAL); + }); + + it('should rollback transaction', async () => { + const module = getDatabaseModule(); + const queryExecutor = new module.SQLiteQueryExecutor(FILE_PATH); + queryExecutor.beginTransaction(); + queryExecutor.setMetadata(TEST_KEY, TEST_VAL); + queryExecutor.rollbackTransaction(); + expect(queryExecutor.getMetadata(TEST_KEY)).toBe(''); + }); + + it('should throw when beginning transaction twice', async () => { + const module = getDatabaseModule(); + const queryExecutor = new module.SQLiteQueryExecutor(FILE_PATH); + queryExecutor.beginTransaction(); + expect(() => queryExecutor.beginTransaction()).toThrow(); + }); + + it('should throw when commit/rollback without beginning transaction', async () => { + const module = getDatabaseModule(); + const queryExecutor = new module.SQLiteQueryExecutor(FILE_PATH); + expect(() => queryExecutor.commitTransaction()).toThrow(); + expect(() => queryExecutor.rollbackTransaction()).toThrow(); + }); +}); diff --git a/web/database/types/sqlite-query-executor.js b/web/database/types/sqlite-query-executor.js --- a/web/database/types/sqlite-query-executor.js +++ b/web/database/types/sqlite-query-executor.js @@ -37,6 +37,10 @@ removeAllThreads(): void; getAllThreadsWeb(): WebClientDBThreadInfo[]; + beginTransaction(): void; + commitTransaction(): void; + rollbackTransaction(): void; + // method is provided to manually signal that a C++ object // is no longer needed and can be deleted delete(): void; diff --git a/web/database/worker/process-operations.js b/web/database/worker/process-operations.js --- a/web/database/worker/process-operations.js +++ b/web/database/worker/process-operations.js @@ -82,14 +82,21 @@ const { draftStoreOperations, reportStoreOperations, threadStoreOperations } = storeOperations; - if (draftStoreOperations) { - processDraftStoreOperations(sqliteQueryExecutor, draftStoreOperations); - } - if (reportStoreOperations) { - processReportStoreOperations(sqliteQueryExecutor, reportStoreOperations); - } - if (threadStoreOperations) { - processThreadStoreOperations(sqliteQueryExecutor, threadStoreOperations); + try { + sqliteQueryExecutor.beginTransaction(); + if (draftStoreOperations && draftStoreOperations.length > 0) { + processDraftStoreOperations(sqliteQueryExecutor, draftStoreOperations); + } + if (reportStoreOperations && reportStoreOperations.length > 0) { + processReportStoreOperations(sqliteQueryExecutor, reportStoreOperations); + } + if (threadStoreOperations && threadStoreOperations.length > 0) { + processThreadStoreOperations(sqliteQueryExecutor, threadStoreOperations); + } + sqliteQueryExecutor.commitTransaction(); + } catch (e) { + sqliteQueryExecutor.rollbackTransaction(); + console.log('Error while processing store ops: ', e); } }