diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 0854d5a1c9..22ca64cf85 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -22,7 +22,9 @@ _sqlite3_column_text _sqlite3_column_type _sqlite3_compileoption_get _sqlite3_compileoption_used +_sqlite3_create_function _sqlite3_create_function_v2 +_sqlite3_create_window_function _sqlite3_data_count _sqlite3_db_filename _sqlite3_db_handle diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 268824d6d0..5ca6f4ec22 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -149,9 +149,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const __exec = wasm.xWrap("sqlite3_exec", "int", ["sqlite3*", "flexible-string", "*", "*", "**"]); /* Documented in the api object's initializer. */ - capi.sqlite3_exec = function(pDb, sql, callback, pVoid, pErrMsg){ - if(5!==arguments.length){ - return __dbArgcMismatch(pDb,"sqlite3_exec",5); + capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){ + if(f.length!==arguments.length){ + return __dbArgcMismatch(pDb,"sqlite3_exec",f.length); }else if('function' !== typeof callback){ return __exec(pDb, sql, callback, pVoid, pErrMsg); } @@ -192,11 +192,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; }/*sqlite3_exec() proxy*/; - if(1){/* Special-case handling of sqlite3_create_function_v2() */ + if(1){/* Special-case handling of sqlite3_create_function_v2() + and sqlite3_create_window_function() */ const sqlite3CreateFunction = wasm.xWrap( "sqlite3_create_function_v2", "int", - ["sqlite3*", "string", "int", "int", "*", - "*", "*", "*", "*"] + ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, + "int"/*eTextRep*/, "*"/*pApp*/, + "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/] + ); + const sqlite3CreateWindowFunction = wasm.xWrap( + "sqlite3_create_window_function", "int", + ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, + "int"/*eTextRep*/, "*"/*pApp*/, + "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, + "*"/*xInverse*/, "*"/*xDestroy*/] ); const __setResult = function(pCx, val){ switch(typeof val) { @@ -213,14 +222,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT); break; case 'object': - if(null===val) { + if(null===val/*yes, typeof null === 'object'*/) { capi.sqlite3_result_null(pCx); break; }else if(util.isBindableTypedArray(val)){ const pBlob = wasm.allocFromTypedArray(val); - capi.sqlite3_result_blob(pCx, pBlob, val.byteLength, - capi.SQLITE_TRANSIENT); - wasm.dealloc(pBlob); + capi.sqlite3_result_blob( + pCx, pBlob, val.byteLength, + wasm.exports[sqlite3.config.deallocExportName] + ); break; } // else fall through @@ -277,22 +287,27 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ capi.sqlite3_result_error(pCx, e.message, -1); } }; - + + /* TODO: pass on the pCx pointer to all callbacks. This requires + fixing test code and updating oodles of docs. Once that is in place, + export sqlite3_aggregate_context(). + */ + const __xFunc = function(callback){ return function(pCx, argc, pArgv){ - try{__setResult(pCx, callback(...__extractArgs(argc, pArgv)))} + try{ __setResult(pCx, callback(...__extractArgs(argc, pArgv))) } catch(e){ __setCxErr(pCx, e) } }; }; - const __xStep = function(callback){ + const __xInverseAndStep = function(callback){ return function(pCx, argc, pArgv){ try{ callback(...__extractArgs(argc, pArgv)) } catch(e){ __setCxErr(pCx, e) } }; }; - const __xFinal = function(callback){ + const __xFinalAndValue = function(callback){ return function(pCx){ try{ __setResult(pCx, callback()) } catch(e){ __setCxErr(pCx, e) } @@ -302,11 +317,34 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const __xDestroy = function(callback){ return function(pVoid){ try{ callback(pVoid) } - catch(e){ - console.error("UDF xDestroy method threw:",e); - } + catch(e){ console.error("UDF xDestroy method threw:",e) } }; }; + + const __xMap = Object.assign(Object.create(null), { + xFunc: {sig:'v(pip)', f:__xFunc}, + xStep: {sig:'v(pip)', f:__xInverseAndStep}, + xInverse: {sig:'v(pip)', f:__xInverseAndStep}, + xFinal: {sig:'v(p)', f:__xFinalAndValue}, + xValue: {sig:'v(p)', f:__xFinalAndValue}, + xDestroy: {sig:'v(p)', f:__xDestroy} + }); + + const __xWrapFuncs = function(theFuncs, tgtUninst){ + const rc = [] + let k; + for(k in theFuncs){ + let fArg = theFuncs[k]; + if('function'===typeof fArg){ + const w = __xMap[k]; + fArg = wasm.installFunction(w.sig, w.f(fArg)); + tgtUninst.push(fArg); + } + rc.push(fArg); + } + return rc; + }; + /* Documented in the api object's initializer. */ capi.sqlite3_create_function_v2 = function f( pDb, funcName, nArg, eTextRep, pApp, @@ -315,34 +353,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ xFinal, //void (*xFinal)(sqlite3_context*) xDestroy //void (*xDestroy)(void*) ){ - if(9!==arguments.length){ - return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",9); + if(f.length!==arguments.length){ + return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length); } - if(!f._sigs){ - f._wrap = Object.assign(Object.create(null), { - xFunc: {sig:'v(pip)', f:__xFunc}, - xStep: {sig:'v(pip)', f:__xStep}, - xFinal: {sig:'v(p)', f:__xFinal}, - xDestroy: {sig:'v(p)', f:__xDestroy} - }); - } - const callbacks = []; /* Wrap the callbacks in a WASM-bound functions... */ const wasm = capi.wasm; - const funcArgs = [], uninstall = [/*funcs to uninstall on error*/], - theFuncs = {xFunc, xStep, xFinal, xDestroy}; + const uninstall = [/*funcs to uninstall on error*/]; let rc; try{ - let k; - for(k in theFuncs){ - let fArg = theFuncs[k]; - if('function'===typeof fArg){ - const w = f._wrap[k]; - fArg = wasm.installFunction(w.sig, w.f(fArg)); - uninstall.push(fArg); - } - funcArgs.push(fArg); - } + const funcArgs = __xWrapFuncs({xFunc, xStep, xFinal, xDestroy}, + uninstall); rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, pApp, ...funcArgs); }catch(e){ @@ -356,21 +376,52 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ return rc; }; - capi.sqlite3_create_function = function( + capi.sqlite3_create_function = function f( pDb, funcName, nArg, eTextRep, pApp, xFunc, xStep, xFinal ){ - if(8!==arguments.length){ - return __dbArgcMismatch(pDb,"sqlite3_create_function",8); - } - return capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, - pApp, xFunc, xStep, xFinal, 0); - + return (f.length===arguments.length) + ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, + pApp, xFunc, xStep, xFinal, 0) + : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length); }; - }/*sqlite3_create_function_v2() proxy*/; + + /* Documented in the api object's initializer. */ + capi.sqlite3_create_window_function = function f( + pDb, funcName, nArg, eTextRep, pApp, + xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) + xFinal, //void (*xFinal)(sqlite3_context*) + xValue, //void (*xFinal)(sqlite3_context*) + xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**) + xDestroy //void (*xDestroy)(void*) + ){ + if(f.length!==arguments.length){ + return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length); + } + /* Wrap the callbacks in a WASM-bound functions... */ + const wasm = capi.wasm; + const uninstall = [/*funcs to uninstall on error*/]; + let rc; + try{ + const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, + uninstall); + rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, + pApp, ...funcArgs); + }catch(e){ + console.error("sqlite3_create_function_v2() setup threw:",e); + for(let v of uninstall){ + wasm.uninstallFunction(v); + } + rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, + "Creation of UDF threw: "+e.message); + } + return rc; + }; + + }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; if(1){/* Special-case handling of sqlite3_prepare_v2() and - sqlite3_prepare_v3() */ + sqlite3_prepare_v3() */ /** Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). */ @@ -403,8 +454,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /* Documented in the api object's initializer. */ capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ - if(6!==arguments.length){ - return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",6); + if(f.length!==arguments.length){ + return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); } const [xSql, xSqlLen] = __flexiString(sql, sqlLen); switch(typeof xSql){ @@ -419,10 +470,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }; /* Documented in the api object's initializer. */ - capi.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){ - return (5==arguments.length) + capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){ + return (f.length===arguments.length) ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) - : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",5); + : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length); }; }/*sqlite3_prepare_v2/v3()*/; diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index fd66eccfd6..9f40699439 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -136,35 +136,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( "Config and external initializers are ignored on calls after the first."); return sqlite3ApiBootstrap.sqlite3; } - apiConfig = apiConfig || {}; - const config = Object.create(null); - { - const configDefaults = { - exports: undefined, - memory: undefined, - bigIntEnabled: (()=>{ - if('undefined'!==typeof Module){ - /* Emscripten module will contain HEAPU64 when built with - -sWASM_BIGINT=1, else it will not. */ - return !!Module.HEAPU64; - } - return !!self.BigInt64Array; - })(), - allocExportName: 'malloc', - deallocExportName: 'free', - wasmfsOpfsDir: '/opfs' - }; - Object.keys(configDefaults).forEach(function(k){ - config[k] = Object.getOwnPropertyDescriptor(apiConfig, k) - ? apiConfig[k] : configDefaults[k]; - }); - // Copy over any properties apiConfig defines but configDefaults does not... - Object.keys(apiConfig).forEach(function(k){ - if(!Object.getOwnPropertyDescriptor(config, k)){ - config[k] = apiConfig[k]; + const config = Object.assign(Object.create(null),{ + exports: undefined, + memory: undefined, + bigIntEnabled: (()=>{ + if('undefined'!==typeof Module){ + /* Emscripten module will contain HEAPU64 when built with + -sWASM_BIGINT=1, else it will not. */ + return !!Module.HEAPU64; } - }); - } + return !!self.BigInt64Array; + })(), + allocExportName: 'malloc', + deallocExportName: 'free', + wasmfsOpfsDir: '/opfs' + }, apiConfig || {}); [ // If any of these config options are functions, replace them with @@ -302,10 +288,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( The semantics of JS functions are: - xFunc: is passed `(arrayOfValues)`. Its return value becomes + xFunc: is passed `(arrayOfValues)`. Its return value becomes the new SQL function's result. - xStep: is passed `(arrayOfValues)`. Its return value is + xStep: is passed `(arrayOfValues)`. Its return value is ignored. xFinal: is passed no arguments. Its return value becomes the @@ -373,6 +359,21 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( xStep, //function(arrayOfValues) xFinal //function() ){/*installed later*/}, + /** + The sqlite3_create_window_function() JS wrapper differs from + its native implementation in the exact same way that + sqlite3_create_function_v2() does. The additional function, + xInverse(), is treated identically to xStep() by the wrapping + layer. + */ + sqlite3_create_window_function: function( + pDb, funcName, nArg, eTextRep, pApp, + xStep, //function(arrayOfValues) + xFinal, //function() + xValue, //function() + xInverse,//function(arrayOfValues) + xDestroy //function(void*) + ){/*installed later*/}, /** The sqlite3_prepare_v3() binding handles two different uses with differing JS/WASM semantics: @@ -577,10 +578,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ capi.wasm.allocFromTypedArray = function(srcTypedArray){ affirmBindableTypedArray(srcTypedArray); - const pRet = this.alloc(srcTypedArray.byteLength || 1); - this.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); + const pRet = capi.wasm.alloc(srcTypedArray.byteLength || 1); + capi.wasm.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); return pRet; - }.bind(capi.wasm); + }; const keyAlloc = config.allocExportName || 'malloc', keyDealloc = config.deallocExportName || 'free'; @@ -590,10 +591,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } capi.wasm.alloc = function(n){ - const m = this.exports[keyAlloc](n); + const m = capi.wasm.exports[keyAlloc](n); if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes."); return m; - }.bind(capi.wasm) + }; capi.wasm.dealloc = (m)=>capi.wasm.exports[keyDealloc](m); diff --git a/manifest b/manifest index c522e4391c..f77b9db145 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C js:\simplement\sa\shand-written\swrapper\sfor\ssqlite3_create_function_v2()\swhich\sconverts,\sif\snecessary,\sJS-function-type\sargs\sto\sWASM\sfunction\swrappers.\sReplace\sDB.createFunction()\simpl\swith\sthe\snew\sone. -D 2022-10-02T18:47:39.889 +C JS:\sclean\sup\screate_function()\swrapper\sand\sadd\ssupport\sfor\screate_window_function().\sEliminate\san\sextraneous\sblob\scopy\swhen\sa\sUDF\sreturns\sa\sblob.\sMake\suse\sof\snewfound\sJS-fu\sto\sclean\sup\show\ssqlite3ApiBootstrap()\sconfig\sis\sinitialized. +D 2022-10-02T20:08:53.027 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -476,7 +476,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle 0e88c8cfc3719e4b7e74980d9da664c709e68acf863e48386cda376edfd3bfb0 F ext/wasm/GNUmakefile b313a82060c733c990b91afa981e10f5e21a0b33a483f33b739ce932ed6bc725 F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066 -F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c6b05641d733227a995cc05a2ba7ddc9ef2323081ae0cae5ed1d1f83d5b54b36 +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api e35ddfbfcde83571a1169a910dbcb59bf598a3a8f5283b42d88555b9ccaa6042 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md 1e350b611465566cfa2e5eccf7c9b29a34f48ee38bbf6d5fb086dd06ce32b3ff F ext/wasm/api/extern-post-js.js dc68cbf552d8ea085181400a6963907c32e0b088b03ffd8969b1869fea246629 @@ -485,10 +485,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814 F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b F ext/wasm/api/pre-js.js 2db711eb637991b383fc6b5c0f3df65ec48a7201e5730e304beba8de2d3f9b0b F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99 -F ext/wasm/api/sqlite3-api-glue.js d834733fbdc216beb65382655e61a436e29018412005aab413ff022d4e5c25eb +F ext/wasm/api/sqlite3-api-glue.js d1587736ed73fcb44e32f1ff1933e4c91a2d3b3c39acef0d13c0b3fd6859a7b1 F ext/wasm/api/sqlite3-api-oo1.js 48d2269544301cd97726ef2d9a82e4384350d12dcf832fa417f211811ae5272d F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541 -F ext/wasm/api/sqlite3-api-prologue.js 32795679b72a5ad685c58a9599d10dac04787907f623825408d539b703e080a4 +F ext/wasm/api/sqlite3-api-prologue.js d71ad817cdef8a9b3b64a394b781a8f64872d4983eac583167e29f9f96ef8e4e F ext/wasm/api/sqlite3-api-worker1.js 7f4f46cb6b512a48572d7567233896e6a9c46570c44bdc3d13419730c7c221c8 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 2a0f9e4bf1b141a787918951360601128d6a0a190a31a8e5cfe237c99fa640c6 @@ -2029,8 +2029,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 e528675da3971907666c7d2d09763975a105ec585dc5122145f65905d535bed8 -R 905afd6e962345097748ac52db8a0889 +P 435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c +R 8d670d2cae99839795cb81bda9741460 U stephan -Z 8de102f5519af227a27849d21742ab64 +Z 74556b5dc28de703cf507dc7ef6550bd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e0800ed178..aca6ce8b8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c \ No newline at end of file +d3bad9347c5423fa7f19ae729461636f1043c99a1f01f168efa10bebefb1cdd1 \ No newline at end of file