diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 47a7e4a062..a6559cbb34 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -1,3 +1,6 @@ +_malloc +_free +_realloc _sqlite3_aggregate_context _sqlite3_bind_blob _sqlite3_bind_double @@ -144,6 +147,45 @@ _sqlite3_vtab_in_next _sqlite3_vtab_nochange _sqlite3_vtab_on_conflict _sqlite3_vtab_rhs_value -_malloc -_free -_realloc +_sqlite3changegroup_add +_sqlite3changegroup_add_strm +_sqlite3changegroup_delete +_sqlite3changegroup_new +_sqlite3changegroup_output +_sqlite3changegroup_output_strm +_sqlite3changeset_apply +_sqlite3changeset_apply_strm +_sqlite3changeset_apply_v2 +_sqlite3changeset_apply_v2_strm +_sqlite3changeset_concat +_sqlite3changeset_concat_strm +_sqlite3changeset_conflict +_sqlite3changeset_finalize +_sqlite3changeset_fk_conflicts +_sqlite3changeset_invert +_sqlite3changeset_invert_strm +_sqlite3changeset_new +_sqlite3changeset_next +_sqlite3changeset_old +_sqlite3changeset_op +_sqlite3changeset_pk +_sqlite3changeset_start +_sqlite3changeset_start_strm +_sqlite3changeset_start_v2 +_sqlite3changeset_start_v2_strm +_sqlite3session_attach +_sqlite3session_changeset +_sqlite3session_changeset_size +_sqlite3session_changeset_strm +_sqlite3session_config +_sqlite3session_create +_sqlite3session_delete +_sqlite3session_diff +_sqlite3session_enable +_sqlite3session_indirect +_sqlite3session_isempty +_sqlite3session_memory_used +_sqlite3session_object_config +_sqlite3session_patchset +_sqlite3session_patchset_strm +_sqlite3session_table_filter diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index dcdc179428..8a0d8147f0 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -337,6 +337,184 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_vtab_rhs_value","int", "sqlite3_index_info*", "int", "**"] ]; + // Add session/changeset APIs... + if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){ + /* ACHTUNG: 2022-12-23: the session/changeset API bindings are + COMPLETELY UNTESTED. */ + /** + FuncPtrAdapter options for session-related callbacks with the + native signature "i(ps)". This proxy converts the 2nd argument + from a C string to a JS string before passing the arguments on + to the client-provided JS callback. + */ + const __ipsProxy = { + signature: 'i(ps)', + callProxy:(callback)=>{ + return (p,s)=>{ + try{return callback(p, wasm.cstrToJs(s)) | 0} + catch(e){return e.resultCode || capi.SQLITE_ERROR} + } + } + }; + + wasm.bindingSignatures.int64.push(...[ + ['sqlite3changegroup_add', 'int', ['sqlite3_changegroup*', 'int', 'void*']], + ['sqlite3changegroup_add_strm', 'int', [ + 'sqlite3_changegroup*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changegroup_delete', undefined, ['sqlite3_changegroup*']], + ['sqlite3changegroup_new', 'int', ['**']], + ['sqlite3changegroup_output', 'int', ['sqlite3_changegroup*', 'int*', '**']], + ['sqlite3changegroup_output_strm', 'int', [ + 'sqlite3_changegroup*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xOutput', signature: 'i(ppi)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changeset_apply', 'int', [ + 'sqlite3*', 'int', 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xFilter', bindScope: 'transient', ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changeset_apply_strm', 'int', [ + 'sqlite3*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xFilter', bindScope: 'transient', ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changeset_apply_v2', 'int', [ + 'sqlite3*', 'int', 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xFilter', bindScope: 'transient', ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' + }), + 'void*', '**', 'int*', 'int' + + ]], + ['sqlite3changeset_apply_v2_strm', 'int', [ + 'sqlite3*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xFilter', bindScope: 'transient', ...__ipsProxy + }), + new wasm.xWrap.FuncPtrAdapter({ + name: 'xConflict', signature: 'i(pip)', bindScope: 'transient' + }), + 'void*', '**', 'int*', 'int' + ]], + ['sqlite3changeset_concat', 'int', ['int','void*', 'int', 'void*', 'int*', '**']], + ['sqlite3changeset_concat_strm', 'int', [ + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInputA', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInputB', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xOutput', signature: 'i(ppi)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changeset_conflict', 'int', ['sqlite3_changeset_iter*', 'int', '**']], + ['sqlite3changeset_finalize', 'int', ['sqlite3_changeset_iter*']], + ['sqlite3changeset_fk_conflicts', 'int', ['sqlite3_changeset_iter*', 'int*']], + ['sqlite3changeset_invert', 'int', ['int', 'void*', 'int*', '**']], + ['sqlite3changeset_invert_strm', 'int', [ + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xOutput', signature: 'i(ppi)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changeset_new', 'int', ['sqlite3_changeset_iter*', 'int', '**']], + ['sqlite3changeset_next', 'int', ['sqlite3_changeset_iter*']], + ['sqlite3changeset_old', 'int', ['sqlite3_changeset_iter*', 'int', '**']], + ['sqlite3changeset_op', 'int', [ + 'sqlite3_changeset_iter*', '**', 'int*', 'int*','int*' + ]], + ['sqlite3changeset_pk', 'int', ['sqlite3_changeset_iter*', '**', 'int*']], + ['sqlite3changeset_start', 'int', ['**', 'int', '*']], + ['sqlite3changeset_start_strm', 'int', [ + '**', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3changeset_start_v2', 'int', ['**', 'int', '*', 'int']], + ['sqlite3changeset_start_v2_strm', 'int', [ + '**', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xInput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*', 'int' + ]], + ['sqlite3session_attach', 'int', ['sqlite3_session*', 'string']], + ['sqlite3session_changeset', 'int', ['sqlite3_session*', 'int*', '**']], + ['sqlite3session_changeset_size', 'i64', ['sqlite3_session*']], + ['sqlite3session_changeset_strm', 'int', [ + 'sqlite3_session*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xOutput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3session_config', 'int', ['int', 'void*']], + ['sqlite3session_create', 'int', ['sqlite3*', 'string', '**']], + ['sqlite3session_delete', undefined, ['sqlite3_session*']], + ['sqlite3session_diff', 'int', ['sqlite3_session*', 'string', 'string', '**']], + ['sqlite3session_enable', 'int', ['sqlite3_session*', 'int']], + ['sqlite3session_indirect', 'int', ['sqlite3_session*', 'int']], + ['sqlite3session_isempty', 'int', ['sqlite3_session*']], + ['sqlite3session_memory_used', 'i64', ['sqlite3_session*']], + ['sqlite3session_object_config', 'int', ['sqlite3_session*', 'int', 'void*']], + ['sqlite3session_patchset', 'int', ['sqlite3_session*', '*', '**']], + ['sqlite3session_patchset_strm', 'int', [ + 'sqlite3_session*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xOutput', signature: 'i(ppp)', bindScope: 'transient' + }), + 'void*' + ]], + ['sqlite3session_table_filter', undefined, [ + 'sqlite3_session*', + new wasm.xWrap.FuncPtrAdapter({ + name: 'xFilter', ...__ipsProxy, + contextKey: (argv,argIndex)=>argv[0/* (sqlite3_session*) */] + }), + '*' + ]] + ]); + }/*session/changeset APIs*/ + /** Functions which are intended solely for API-internal use by the WASM components, not client code. These get installed into @@ -406,10 +584,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ if(1){// wasm.xWrap() bindings... /** Add some descriptive xWrap() aliases for '*' intended to (A) - initially improve readability/correctness of capi.signatures - and (B) provide automatic conversion from higher-level - representations, e.g. capi.sqlite3_vfs to `sqlite3_vfs*` via - capi.sqlite3_vfs.pointer. + initially improve readability/correctness of + wasm.bindingSignatures and (B) provide automatic conversion + from higher-level representations, e.g. capi.sqlite3_vfs to + `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. */ const aPtr = wasm.xWrap.argAdapter('*'); const nilType = function(){}; @@ -417,6 +595,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ('sqlite3_context*', aPtr) ('sqlite3_value*', aPtr) ('void*', aPtr) + ('sqlite3_changegroup*', aPtr) + ('sqlite3_changeset_iter*', aPtr) + //('sqlite3_rebaser*', aPtr) + ('sqlite3_session*', aPtr) ('sqlite3_stmt*', (v)=> aPtr((v instanceof (sqlite3?.oo1?.Stmt || nilType)) ? v.pointer : v)) @@ -1001,7 +1183,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ wasm.ctype = JSON.parse(wasm.cstrToJs(cJson)); // Groups of SQLITE_xyz macros... const defineGroups = ['access', 'authorizer', - 'blobFinalizers', 'config', 'dataTypes', + 'blobFinalizers', 'changeset', + 'config', 'dataTypes', 'dbConfig', 'dbStatus', 'encodings', 'fcntl', 'flock', 'ioCap', 'limits', 'openFlags', @@ -1011,7 +1194,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'trace', 'txnState', 'udfFlags', 'version' ]; if(wasm.bigIntEnabled){ - defineGroups.push('serialize', 'vtab'); + defineGroups.push('serialize', 'session', 'vtab'); } for(const t of defineGroups){ for(const e of Object.entries(wasm.ctype[t])){ diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 16f5f00b15..1823773dec 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -72,7 +72,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ wasm.installFunction('i(ippp)', function(t,c,p,x){ if(capi.SQLITE_TRACE_STMT===t){ // x == SQL, p == sqlite3_stmt* - console.log("SQL TRACE #"+(++this.counter), + console.log("SQL TRACE #"+(++this.counter)+' via sqlite3@'+c+':', wasm.cstrToJs(x)); } }.bind({counter: 0})); @@ -161,7 +161,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ capi.sqlite3_extended_result_codes(pDb, 1); if(flagsStr.indexOf('t')>=0){ capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, - __dbTraceToConsole, 0); + __dbTraceToConsole, pDb); } }catch( e ){ if( pDb ) capi.sqlite3_close_v2(pDb); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 307ab43f5e..9bac704483 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -1020,12 +1020,20 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( to the memory. On error, returns throws a WasmAllocError. The memory must eventually be released using restore(). + If n is a string, it must be a WASM "IR" value in the set + accepted by wasm.irSizeof(), which is mapped to the size of + that data type. If passed a string not in that set, it throws a + WasmAllocError. + This method always adjusts the given value to be a multiple of 8 bytes because failing to do so can lead to incorrect results when reading and writing 64-bit values from/to the WASM heap. Similarly, the returned address is always 8-byte aligned. */ - alloc: (n)=>{ + alloc: function(n){ + if('string'===typeof n && !(n = wasm.irSizeof(n))){ + WasmAllocError.toss("Invalid value for pstack.alloc(",arguments[0],")"); + } return wasm.exports.sqlite3_wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate",n, "bytes from the pstack."); @@ -1035,6 +1043,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( returns the addresses as an array of n element, each holding the address of one chunk. + sz may optionally be an IR string accepted by wasm.irSizeof(). + Throws a WasmAllocError if allocation fails. Example: @@ -1043,7 +1053,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( const [p1, p2, p3] = wasm.pstack.allocChunks(3,4); ``` */ - allocChunks: (n,sz)=>{ + allocChunks: function(n,sz){ + if('string'===typeof sz && !(sz = wasm.irSizeof(sz))){ + WasmAllocError.toss("Invalid size value for allocChunks(",arguments[1],")"); + } const mem = wasm.pstack.alloc(n * sz); const rc = []; let i = 0, offset = 0; diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 5c9b533184..b39e252b17 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -99,15 +99,21 @@ #ifndef SQLITE_ENABLE_FTS4 # define SQLITE_ENABLE_FTS4 1 #endif -#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC -# define SQLITE_ENABLE_OFFSET_SQL_FUNC 1 -#endif #ifndef SQLITE_ENABLE_MATH_FUNCTIONS # define SQLITE_ENABLE_MATH_FUNCTIONS 1 #endif +#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC +# define SQLITE_ENABLE_OFFSET_SQL_FUNC 1 +#endif +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK +# define SQLITE_ENABLE_PREUPDATE_HOOK 1 /*required by session extension*/ +#endif #ifndef SQLITE_ENABLE_RTREE # define SQLITE_ENABLE_RTREE 1 #endif +#ifndef SQLITE_ENABLE_SESSION +# define SQLITE_ENABLE_SESSION 1 +#endif #ifndef SQLITE_ENABLE_STMTVTAB # define SQLITE_ENABLE_STMTVTAB 1 #endif @@ -460,6 +466,22 @@ const char * sqlite3_wasm_enum_json(void){ out("\"SQLITE_STATIC\":0, \"SQLITE_TRANSIENT\":-1"); } _DefGroup; + DefGroup(changeset){ + DefInt(SQLITE_CHANGESETSTART_INVERT); + DefInt(SQLITE_CHANGESETAPPLY_NOSAVEPOINT); + DefInt(SQLITE_CHANGESETAPPLY_INVERT); + + DefInt(SQLITE_CHANGESET_DATA); + DefInt(SQLITE_CHANGESET_NOTFOUND); + DefInt(SQLITE_CHANGESET_CONFLICT); + DefInt(SQLITE_CHANGESET_CONSTRAINT); + DefInt(SQLITE_CHANGESET_FOREIGN_KEY); + + DefInt(SQLITE_CHANGESET_OMIT); + DefInt(SQLITE_CHANGESET_REPLACE); + DefInt(SQLITE_CHANGESET_ABORT); + } _DefGroup; + DefGroup(config){ DefInt(SQLITE_CONFIG_SINGLETHREAD); DefInt(SQLITE_CONFIG_MULTITHREAD); @@ -796,6 +818,11 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_DESERIALIZE_RESIZEABLE); } _DefGroup; + DefGroup(session){ + DefInt(SQLITE_SESSION_CONFIG_STRMSIZE); + DefInt(SQLITE_SESSION_OBJCONFIG_SIZE); + } _DefGroup; + DefGroup(sqlite3Status){ DefInt(SQLITE_STATUS_MEMORY_USED); DefInt(SQLITE_STATUS_PAGECACHE_USED); diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js index 7a7747ef35..bfe66dfb15 100644 --- a/ext/wasm/common/whwasmutil.js +++ b/ext/wasm/common/whwasmutil.js @@ -246,6 +246,25 @@ self.WhWasmUtilInstaller = function(target){ cache.utf8Decoder = new TextDecoder(); cache.utf8Encoder = new TextEncoder('utf-8'); + /** + For the given IR-like string in the set ('i8', 'i16', 'i32', + 'f32', 'float', 'i64', 'f64', 'double', '*'), or any string value + ending in '*', returns the sizeof for that value + (target.ptrSizeof in the latter case). For any other value, it + returns the undefined value. + */ + target.irSizeof = (n)=>{ + switch(n){ + case 'i8': return 1; + case 'i16': return 2; + case 'i32': case 'f32': case 'float': return 4; + case 'i64': case 'f64': case 'double': return 8; + case '*': return ptrSizeof; + default: + return (''+n).endsWith('*') ? ptrSizeof : undefined; + } + }; + /** If (cache.heapSize !== cache.memory.buffer.byteLength), i.e. if the heap has grown since the last call, updates cache.HEAPxyz. @@ -447,7 +466,7 @@ self.WhWasmUtilInstaller = function(target){ type(s) of the given function signature, or throws if the signature is invalid. */ /******** // only valid for use with the WebAssembly.Function ctor, which - // is not yet documented on MDN. + // is not yet documented on MDN. sigToWasm: function(sig){ const rc = {parameters:[], results: []}; if('v'!==sig[0]) rc.results.push(f.sigTypes(sig[0])); @@ -1590,10 +1609,20 @@ self.WhWasmUtilInstaller = function(target){ not actually bind any functions. Its convertArg() method is called via xWrap() to perform any bindings. - Shortcomings: function pointers which include C-string arguments - may still need a level of hand-written wrappers around them, - depending on how they're used, in order to provide the client - with JS strings. + Shortcomings: + + - These "reverse" bindings, i.e. calling into a JS-defined + function from a WASM-defined function (the generated proxy + wrapper), lack all type conversion support. That means, for + example, that... + + - Function pointers which include C-string arguments may still + need a level of hand-written wrappers around them, depending on + how they're used, in order to provide the client with JS + strings. Alternately, clients will need to perform such conversions + on their own, e.g. using cstrtojs(). Or maybe we can find a way + to perform such conversions here, via addition of an xWrap()-style + function signature to the options argument. */ xArg.FuncPtrAdapter = class FuncPtrAdapter extends AbstractArgAdapter { constructor(opt) { diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index c7fa893954..d03acd4cb6 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -1010,7 +1010,12 @@ self.sqlite3InitModule = sqlite3InitModule; T.assert(P.quota >= 4096) .assert(remaining === P.quota) .mustThrowMatching(()=>P.alloc(0), isAllocErr) - .mustThrowMatching(()=>P.alloc(-1), isAllocErr); + .mustThrowMatching(()=>P.alloc(-1), isAllocErr) + .mustThrowMatching( + ()=>P.alloc('i33'), + (e)=>e instanceof sqlite3.WasmAllocError + ); + ; let p1 = P.alloc(12); T.assert(p1 === stack - 16/*8-byte aligned*/) .assert(P.pointer === p1); @@ -1029,7 +1034,7 @@ self.sqlite3InitModule = sqlite3InitModule; T.assert(P.pointer === stack); try { - const [p1, p2, p3] = P.allocChunks(3,4); + const [p1, p2, p3] = P.allocChunks(3,'i32'); T.assert(P.pointer === stack-16/*always rounded to multiple of 8*/) .assert(p2 === p1 + 4) .assert(p3 === p2 + 4); @@ -1143,7 +1148,7 @@ self.sqlite3InitModule = sqlite3InitModule; } } }; - + T.assert(wasm.isPtr(db.pointer)) .mustThrowMatching(()=>db.pointer=1, /read-only/) .assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)) @@ -1183,7 +1188,7 @@ self.sqlite3InitModule = sqlite3InitModule; T.assert(0 === rc); const stack = wasm.pstack.pointer; try { - const [pCur, pHi] = wasm.pstack.allocChunks(2,8); + const [pCur, pHi] = wasm.pstack.allocChunks(2,'i64'); rc = capi.sqlite3_db_status(this.db, capi.SQLITE_DBSTATUS_LOOKASIDE_USED, pCur, pHi, 0); T.assert(0===rc); @@ -1729,7 +1734,7 @@ self.sqlite3InitModule = sqlite3InitModule; // sqlite3_result_xyz() and return undefined. Both are // functionally equivalent. } - }); + }); T.assert(18===db.selectValue('select summerN(1,8,9), summerN(2,3,4)')); T.mustThrowMatching(()=>{ db.createFunction('nope',{ @@ -1856,7 +1861,6 @@ self.sqlite3InitModule = sqlite3InitModule; .assert('select 2;' === rc[1]) .assert('-- empty\n; select 3' === rc[2] /* Strange but true. */); - T.mustThrowMatching(()=>{ db.exec({sql:'', returnValue: 'nope'}); }, /^Invalid returnValue/); @@ -2635,6 +2639,79 @@ self.sqlite3InitModule = sqlite3InitModule; }/*OPFS util sanity checks*/) ;/* end OPFS tests */ + T.g('Session API') + .t({ + name: 'Session API sanity checks', + predicate: ()=>!!capi.sqlite3changegroup_add, + test: function(sqlite3){ + warn("The session API tests could use some expansion."); + const db1 = new sqlite3.oo1.DB(), db2 = new sqlite3.oo1.DB(); + const sqlInit = [ + "create table t(rowid INTEGER PRIMARY KEY,a,b); ", + "insert into t(rowid,a,b) values", + "(1,'a1','b1'),", + "(2,'a2','b2'),", + "(3,'a3','b3');" + ].join(''); + db1.exec(sqlInit); + db2.exec(sqlInit); + T.assert(3 === db1.selectValue("select count(*) from t")) + .assert('b3' === db1.selectValue('select b from t where rowid=3')); + const stackPtr = wasm.pstack.pointer; + try{ + let ppOut = wasm.pstack.allocPtr(); + let rc = capi.sqlite3session_create(db1, "main", ppOut); + T.assert(0===rc); + let pSession = wasm.peekPtr(ppOut); + T.assert(pSession && wasm.isPtr(pSession)); + if(1){ + capi.sqlite3session_table_filter(pSession, (pCtx, tbl)=>{ + T.assert('t' === tbl).assert( 99 === pCtx ); + return 1; + }, 99); + }else{ + rc = capi.sqlite3session_attach(pSession, "t"); + T.assert( 0 === rc ); + } + db1.exec([ + "update t set b='bTwo' where rowid=2;", + "update t set a='aThree' where rowid=3;", + "delete from t where rowid=1;", + "insert into t(rowid,a,b) values(4,'a4','b4')" + ]); + T.assert('bTwo' === db1.selectValue("select b from t where rowid=2")) + .assert(undefined === db1.selectValue('select a from t where rowid=1')) + .assert('b4' === db1.selectValue('select b from t where rowid=4')); + + let pnChanges = wasm.pstack.alloc('i32'), + ppChanges = wasm.pstack.allocPtr(); + rc = capi.sqlite3session_changeset(pSession, pnChanges, ppChanges); + T.assert( 0 === rc ); + capi.sqlite3session_delete(pSession); + pSession = 0; + const pChanges = wasm.peekPtr(ppChanges), + nChanges = wasm.peek32(pnChanges); + T.assert( pChanges && wasm.isPtr( pChanges ) ).assert( nChanges > 0 ); + pnChanges = ppChanges = 0; + //log("pnChanges =", pnChanges, wasm.peek32(pnChanges), '@', pChanges); + rc = capi.sqlite3changeset_apply( + db2, nChanges, pChanges, 0, (pCtx, eConflict, pIter)=>{ + return pCtx ? 1 : 0 + }, 1 + ); + wasm.dealloc( pChanges ); + T.assert( 0 === rc ) + .assert( 3 === db2.selectValue('select count(*) from t')) + .assert( 'b4' === db2.selectValue('select b from t where rowid=4') ); + }finally{ + wasm.pstack.restore(stackPtr); + db1.close(); + db2.close(); + } + } + }) + ;/*end of session API group*/; + //////////////////////////////////////////////////////////////////////// log("Loading and initializing sqlite3 WASM module..."); if(!self.sqlite3InitModule && !isUIThread()){ diff --git a/manifest b/manifest index 827df733d6..690055e610 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stests\sconfirming\sthat\sJS\ssqlite3_exec()'s\sSQL\sargument\sparticipates\sin\sthe\s'string:flexible'\stype\sconversion. -D 2022-12-25T17:12:24.209 +C Merge\swasm-session-api\sbranch\sinto\strunk,\sadding\sthe\ssession\sAPI\sto\sthe\sJS/WASM\scomponents. +D 2022-12-25T20:25:44.871 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 7e0468ad0294786da90b2062043288f734a65243a458a541f2f880a785be0bb9 +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api beec6b5993e234153446aaa48e68d2a75db590b3c7eed26bd61e5fb5f8c9a881 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,16 +503,16 @@ 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 7e8bbf3a987b7220db8220045c4d575744bac9faa0d6bc2924ce93e89d1176e3 -F ext/wasm/api/sqlite3-api-oo1.js 5393fb0b325d2fdafada7fdbfb9219af9a865631acb351d5c5196a982b632c8b -F ext/wasm/api/sqlite3-api-prologue.js 30dc037af2f33e773ea6c5e0362a44f1de69e174d729dd33e8049cf2d0676fa2 +F ext/wasm/api/sqlite3-api-glue.js 594741f7cbff68f0b4a0c1a066bce335146de1124366377292e27d30f9a5f751 +F ext/wasm/api/sqlite3-api-oo1.js ae4f6950913b4703b767f640a533675b45e9e6c462bf01e357cec16bc68943e2 +F ext/wasm/api/sqlite3-api-prologue.js 9dfbb41ebc0aadfac9c41ae8d050187af39819fff4e4f22ac6a8a4f3008f722b F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 F ext/wasm/api/sqlite3-opfs-async-proxy.js 7795b84b66a7a8dedc791340709b310bb497c3c72a80bef364fa2a58e2ddae3f F ext/wasm/api/sqlite3-v-helper.js 6f6c3e390a72e08b0a5b16a0d567d7af3c04d172831853a29d72a6f1dd40ff24 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 66daf6fb6843bea615fe193109e1542efbeca24f560ee9da63375a910bb48115 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c 325ca43b982ef608243493c42249ba4e6b2e549f7cbd9b332e2f7ee8227d2ae3 +F ext/wasm/api/sqlite3-wasm.c 2bee083def8f832c863d5f0a0fcbff86c236412663c6d3458128db154c0fccc8 F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b F ext/wasm/api/sqlite3-worker1.js 1e54ea3d540161bcfb2100368a2fc0cad871a207b8336afee1c445715851ec54 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07 F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css 0ff15602a3ab2bad8aef2c3bd120c7ee3fd1c2054ad2ace7e214187ae68d926f -F ext/wasm/common/whwasmutil.js 02d04a086a16130e97dc2a04555686dc19c48bd41842a96934737365678ca839 +F ext/wasm/common/whwasmutil.js 77aedbe53b05bfe08f4ca52e96d520eb14d792df09e107df6e3d1300de37e02f F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6 @@ -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 1396611f11d75fc124ffb7600dc05d3ad7cc25885c4a8f842b9291ea9e0b8abd +F ext/wasm/tester1.c-pp.js 314e7bdbc73f558cf12c050c5af42f866195ee17bf451d63f7337052fcf44c7a 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,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4888957baf18c6763f959fbba998a74156ff656368779107f502b926e9e9d949 -R cfaf4d7e6ebced21987511a642eb3cd6 +P 926d0c61a391c601adc2804d3fdaa8b667ae2abd565939cddfa12d5151b098f8 64e032602cf420851c8029603c76f5512000d1c9a40fa7a545528d69d6d1d4cc +R 5a9d5d7e14f03170b616d0bb48ba8894 +T +closed 64e032602cf420851c8029603c76f5512000d1c9a40fa7a545528d69d6d1d4cc Closed\sby\sintegrate-merge. U stephan -Z c6e0af2cef9d9cd33fe188241216b8ea +Z 42f975ba9994385873d5d768be40b720 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 47997d8d14..b020053bce 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -926d0c61a391c601adc2804d3fdaa8b667ae2abd565939cddfa12d5151b098f8 \ No newline at end of file +dfb8b651fa4faef2d3307a05512cdc479398484c3a59715827179c363861a777 \ No newline at end of file