From: stephan Date: Tue, 27 Dec 2022 12:13:01 +0000 (+0000) Subject: Expose sqlite3_commit/rollback/update_hook() to JS API. X-Git-Tag: version-3.41.0~167 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=55a21fbcef97b5cbcc3b0d692ebd21741156b8da;p=thirdparty%2Fsqlite.git Expose sqlite3_commit/rollback/update_hook() to JS API. FossilOrigin-Name: f99f8e3ecfe205337996ee61c0b9f139d3e8788b14f32e26560888b3a16564de --- diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 8ba6e515c5..93ab8b7950 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -31,6 +31,7 @@ _sqlite3_column_name _sqlite3_column_text _sqlite3_column_type _sqlite3_column_value +_sqlite3_commit_hook _sqlite3_compileoption_get _sqlite3_compileoption_used _sqlite3_complete @@ -98,6 +99,7 @@ _sqlite3_result_subtype _sqlite3_result_text _sqlite3_result_zeroblob _sqlite3_result_zeroblob64 +_sqlite3_rollback_hook _sqlite3_serialize _sqlite3_set_authorizer _sqlite3_set_auxdata @@ -120,6 +122,7 @@ _sqlite3_total_changes _sqlite3_total_changes64 _sqlite3_trace_v2 _sqlite3_txn_state +_sqlite3_update_hook _sqlite3_uri_boolean _sqlite3_uri_int64 _sqlite3_uri_key diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index ddb4a58fa6..e8fe5aba6b 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -103,6 +103,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_column_text","string", "sqlite3_stmt*", "int"], ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], ["sqlite3_column_value","sqlite3_value*", "sqlite3_stmt*", "int"], + ["sqlite3_commit_hook", "void*", [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: 'sqlite3_commit_hook', + signature: 'i(p)', + contextKey: (argv)=>argv[0/* sqlite3* */] + }), + '*' + ]], ["sqlite3_compileoption_get", "string", "int"], ["sqlite3_compileoption_used", "int", "string"], ["sqlite3_complete", "int", "string:flexible"], @@ -206,6 +215,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_result_subtype", undefined, "sqlite3_value*", "int"], ["sqlite3_result_text", undefined, "sqlite3_context*", "string", "int", "*"], ["sqlite3_result_zeroblob", undefined, "sqlite3_context*", "int"], + ["sqlite3_rollback_hook", "void*", [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: 'sqlite3_rollback_hook', + signature: 'v(p)', + contextKey: (argv)=>argv[0/* sqlite3* */] + }), + '*' + ]], ["sqlite3_set_authorizer", "int", [ "sqlite3*", new wasm.xWrap.FuncPtrAdapter({ @@ -327,6 +345,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_set_last_insert_rowid", undefined, ["sqlite3*", "i64"]], ["sqlite3_status64", "int", "int", "*", "*", "int"], ["sqlite3_total_changes64", "i64", ["sqlite3*"]], + ["sqlite3_update_hook", "*", [ + "sqlite3*", + new wasm.xWrap.FuncPtrAdapter({ + name: 'sqlite3_update_hook', + signature: "v(iippj)", + contextKey: (argv)=>argv[0/* sqlite3* */], + callProxy: (callback)=>{ + return (p,op,z0,z1,rowid)=>{ + callback(p, op, wasm.cstrToJs(z0), wasm.cstrToJs(z1), rowid); + }; + } + }), + "*" + ]], ["sqlite3_uri_int64", "i64", ["sqlite3_filename", "string", "i64"]], ["sqlite3_value_int64","i64", "sqlite3_value*"], ["sqlite3_vtab_collation","string","sqlite3_index_info*","int"], @@ -704,7 +736,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ message = message || ''+resultCode; resultCode = (resultCode.resultCode || capi.SQLITE_ERROR); } - return __db_err(pDb, resultCode, message); + return pDb ? __db_err(pDb, resultCode, message) : resultCode; }; }else{ util.sqlite3_wasm_db_error = function(pDb,errCode,msg){ @@ -891,9 +923,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ installed for pDb. */ try{capi.sqlite3_busy_handler(pDb, 0, 0)} catch(e){/*ignored*/} + try{capi.sqlite3_commit_hook(pDb, 0, 0)} catch(e){/*ignored*/} try{capi.sqlite3_progress_handler(pDb, 0, 0, 0)} catch(e){/*ignored*/} - try{capi.sqlite3_trace_v2(pDb, 0, 0, 0, 0)} catch(e){/*ignored*/} + try{capi.sqlite3_rollback_hook(pDb, 0, 0)} catch(e){/*ignored*/} try{capi.sqlite3_set_authorizer(pDb, 0, 0)} catch(e){/*ignored*/} + try{capi.sqlite3_trace_v2(pDb, 0, 0, 0, 0)} catch(e){/*ignored*/} + try{capi.sqlite3_update_hook(pDb, 0, 0)} catch(e){/*ignored*/} const m = __dbCleanupMap(pDb, 0); if(!m) return; if(m.collation){ diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 7cb6c260eb..2f085650c5 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -2687,6 +2687,88 @@ self.sqlite3InitModule = sqlite3InitModule; }/*OPFS util sanity checks*/) ;/* end OPFS tests */ + //////////////////////////////////////////////////////////////////////// + T.g('Hook APIs') + .t({ + name: "Commit/update/rollback hooks.", + predicate: ()=>wasm.bigIntEnabled || "Update hook requires int64", + test: function(sqlite3){ + let countCommit = 0, countRollback = 0;; + const db = new sqlite3.oo1.DB(':memory:',1 ? 'c' : 'ct'); + let rc = capi.sqlite3_commit_hook(db, (p)=>{ + ++countCommit; + return (1 === p) ? 0 : capi.SQLITE_ERROR; + }, 1); + T.assert( 0 === rc /*void pointer*/ ); + + // Commit hook... + db.exec("BEGIN; SELECT 1; COMMIT"); + T.assert(0 === countCommit, + "No-op transactions (mostly) do not trigger commit hook."); + db.exec("BEGIN EXCLUSIVE; SELECT 1; COMMIT"); + T.assert(1 === countCommit, + "But EXCLUSIVE transactions do."); + db.transaction((d)=>{d.exec("create table t(a)");}); + T.assert(2 === countCommit); + + // Rollback hook: + rc = capi.sqlite3_rollback_hook(db, (p)=>{ + ++countRollback; + T.assert( 2 === p ); + }, 2); + T.assert( 0 === rc /*void pointer*/ ); + T.mustThrowMatching(()=>{ + db.transaction('drop table t',()=>{}) + }, (e)=>{ + return (capi.SQLITE_MISUSE === e.resultCode) + && ( e.message.indexOf('Invalid argument') > 0 ); + }); + T.assert(0 === countRollback, "Transaction was not started."); + T.mustThrowMatching(()=>{ + db.transaction('immediate', ()=>{ + sqlite3.SQLite3Error.toss(capi.SQLITE_FULL,'testing rollback hook'); + }); + }, (e)=>{ + return capi.SQLITE_FULL === e.resultCode + }); + T.assert(1 === countRollback); + + // Update hook... + const countUpdate = Object.create(null); + capi.sqlite3_update_hook(db, (p,op,db,tbl,rowid)=>{ + switch(op){ + case capi.SQLITE_INSERT: + case capi.SQLITE_UPDATE: + case capi.SQLITE_DELETE: + countUpdate[op] = (countUpdate[op]||0) + 1; + break; + default: return; + } + T.assert(3===p).assert('bigint' === typeof rowid); + }, 3); + db.transaction((d)=>{ + d.exec([ + "insert into t(a) values(1);", + "update t set a=2;", + "update t set a=3;", + "delete from t where a=3" + // update hook is not called for an unqualified DELETE + ]); + }); + T.assert(1 === countRollback) + .assert(3 === countCommit) + .assert(1 === countUpdate[capi.SQLITE_INSERT]) + .assert(2 === countUpdate[capi.SQLITE_UPDATE]) + .assert(1 === countUpdate[capi.SQLITE_DELETE]); + //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true; + T.assert(1 === capi.sqlite3_commit_hook(db, 0, 0)); + T.assert(2 === capi.sqlite3_rollback_hook(db, 0, 0)); + T.assert(3 === capi.sqlite3_update_hook(db, 0, 0)); + //wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false; + db.close(); + } + }); + //////////////////////////////////////////////////////////////////////// T.g('Auto-extension API') .t({ diff --git a/manifest b/manifest index 90f98310f7..59b4ef7d55 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\san\soptional\sargument\sto\soo1.DB.transaction()\sto\sspecify\san\sexplicit\sBEGIN\squalifier. -D 2022-12-27T11:40:05.031 +C Expose\ssqlite3_commit/rollback/update_hook()\sto\sJS\sAPI. +D 2022-12-27T12:13:01.660 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -494,7 +494,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile ffe0e9818a3b6b36c85a1d10dab76b220a8f5cd83439c62e50223a7970b3d68a F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20 F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 1e61a3ff4085974080ecf782d4defea6b7d443eaa98f9aea773b5f5b416b417c +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 445ae63749f47c50c251de53388ca2d1247f06b62f71289d3d2b9c3cee014575 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md 77a2f1f2fc60a35def7455dffc8d3f2c56385d6ac5c6cecc60fa938252ea2c54 F ext/wasm/api/extern-post-js.c-pp.js 8923f76c3d2213159e12d641dc750523ead5c848185dc4996fae5cc12397f88d @@ -503,7 +503,7 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4 -F ext/wasm/api/sqlite3-api-glue.js b55e13aadf33a037e9a6950f370d16e2b0e858b74a2070faa2c87d7cde48c80c +F ext/wasm/api/sqlite3-api-glue.js 7d619834cc0d378e9f46afc1eab0587f8f097944936aff5723da7243f36bc787 F ext/wasm/api/sqlite3-api-oo1.js e9fba119e9b1716b3f731838ed1ab18741401bcf4c51d2a4a6e9d1d23cf9d771 F ext/wasm/api/sqlite3-api-prologue.js e862e5b79d565bd79c8ff59ebb6618a07ecb1a0262a1560dc6a10aa0f4d6f531 F ext/wasm/api/sqlite3-api-worker1.js c9ef8865f072e61251260b218aa4ed614a21a25e9e3cc6f22acf81794d32fc0b @@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9 F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406 -F ext/wasm/tester1.c-pp.js 93e3ebae7f0caa1b54265447f76b0485d4a28799e02b7c2a3cc92883aa84fda6 +F ext/wasm/tester1.c-pp.js e183830a1c09e9535fea0f28ad8ed60049797d0461450e590f39e61aee48ae81 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2067,8 +2067,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 52b229d11d82bfb81c8b63e252c51c57a34dc50498dd685451588c185873c628 -R 907b947f3596b78852af2b253c74cbfb +P 507335c12b1dbe21d180cf6f0a0deb4cc737417acb44c8f1d8fac98b86f62b01 +R 1861a6bdb0fe2e8302265dde3d3ce972 U stephan -Z 14582b10a3620eea75adde1bd5a1eeb7 +Z 65af223f5379fb35b8546fe3024152a5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a86e29ff13..6fc68debea 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -507335c12b1dbe21d180cf6f0a0deb4cc737417acb44c8f1d8fac98b86f62b01 \ No newline at end of file +f99f8e3ecfe205337996ee61c0b9f139d3e8788b14f32e26560888b3a16564de \ No newline at end of file