diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 22ca64cf85..014812946d 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -1,3 +1,4 @@ +_sqlite3_aggregate_context _sqlite3_bind_blob _sqlite3_bind_double _sqlite3_bind_int @@ -61,6 +62,7 @@ _sqlite3_result_error_code _sqlite3_result_error_nomem _sqlite3_result_error_toobig _sqlite3_result_int +_sqlite3_result_int64 _sqlite3_result_null _sqlite3_result_text _sqlite3_serialize @@ -76,9 +78,12 @@ _sqlite3_uri_boolean _sqlite3_uri_int64 _sqlite3_uri_key _sqlite3_uri_parameter +_sqlite3_user_data _sqlite3_value_blob _sqlite3_value_bytes _sqlite3_value_double +_sqlite3_value_int +_sqlite3_value_int64 _sqlite3_value_text _sqlite3_value_type _sqlite3_vfs_find diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index 5ca6f4ec22..f80bade385 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -19,6 +19,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 'use strict'; const toss = (...args)=>{throw new Error(args.join(' '))}; + const toss3 = sqlite3.SQLite3Error.toss; const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util; self.WhWasmUtilInstaller(capi.wasm); delete self.WhWasmUtilInstaller; @@ -62,8 +63,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. */ const aPtr = wasm.xWrap.argAdapter('*'); - wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); - wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr); + wasm.xWrap.argAdapter('sqlite3*', aPtr) + ('sqlite3_stmt*', aPtr) + ('sqlite3_context*', aPtr) + ('sqlite3_value*', aPtr) + ('void*', aPtr); + wasm.xWrap.resultAdapter('sqlite3*', aPtr) + ('sqlite3_stmt*', aPtr) + ('sqlite3_context*', aPtr) + ('void*', aPtr); /** Populate api object with sqlite3_...() by binding the "raw" wasm @@ -207,38 +215,53 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xInverse*/, "*"/*xDestroy*/] ); - const __setResult = function(pCx, val){ + + const __setUdfResult = function(pCtx, val){ + //console.warn("setUdfResult",typeof val, val); switch(typeof val) { case 'boolean': - capi.sqlite3_result_int(pCx, val ? 1 : 0); + capi.sqlite3_result_int(pCtx, val ? 1 : 0); + break; + case 'bigint': + if(wasm.bigIntEnabled){ + if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); + else toss3("BigInt value",val.toString(),"is too BigInt for int64."); + }else if(util.bigIntFits32(val)){ + capi.sqlite3_result_int(pCtx, Number(val)); + }else if(util.bigIntFitsDouble(val)){ + capi.sqlite3_result_double(pCtx, Number(val)); + }else{ + toss3("BigInt value",val.toString(),"is too BigInt."); + } break; case 'number': { (util.isInt32(val) ? capi.sqlite3_result_int - : capi.sqlite3_result_double)(pCx, val); + : capi.sqlite3_result_double)(pCtx, val); break; } case 'string': - capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT); + capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT); break; case 'object': if(null===val/*yes, typeof null === 'object'*/) { - capi.sqlite3_result_null(pCx); + capi.sqlite3_result_null(pCtx); break; }else if(util.isBindableTypedArray(val)){ const pBlob = wasm.allocFromTypedArray(val); capi.sqlite3_result_blob( - pCx, pBlob, val.byteLength, + pCtx, pBlob, val.byteLength, wasm.exports[sqlite3.config.deallocExportName] ); break; } // else fall through default: - toss3("Don't not how to handle this UDF result value:",val); + toss3("Don't not how to handle this UDF result value:",(typeof val), val); }; - }/*__setResult()*/; - const __extractArgs = function(argc, pArgv){ + }/*__setUdfResult()*/; + + const __convertUdfArgs = function(argc, pArgv){ let i, pVal, valType, arg; const tgt = []; for(i = 0; i < argc; ++i){ @@ -253,6 +276,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ valType = capi.sqlite3_value_type(pVal); switch(valType){ case capi.SQLITE_INTEGER: + if(wasm.bigIntEnabled){ + arg = capi.sqlite3_value_int64(pVal); + if(util.bigIntFitsDouble(arg)) arg = Number(arg); + } + else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/; + break; case capi.SQLITE_FLOAT: arg = capi.sqlite3_value_double(pVal); break; @@ -262,10 +291,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ case capi.SQLITE_BLOB:{ const n = capi.sqlite3_value_bytes(pVal); const pBlob = capi.sqlite3_value_blob(pVal); - arg = new Uint8Array(n); - let i; - const heap = n ? wasm.heap8() : false; - for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i]; + if(n && !pBlob) sqlite3.WasmAllocError.toss( + "Cannot allocate memory for blob argument of",n,"byte(s)" + ); + arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null; break; } case capi.SQLITE_NULL: @@ -278,39 +307,37 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ tgt.push(arg); } return tgt; - }/*__extractArgs()*/; + }/*__convertUdfArgs()*/; - const __setCxErr = (pCx, e)=>{ - if(e instanceof capi.WasmAllocError){ - capi.sqlite3_result_error_nomem(pCx); + const __setUdfError = (pCtx, e)=>{ + if(e instanceof sqlite3.WasmAllocError){ + capi.sqlite3_result_error_nomem(pCtx); }else{ - capi.sqlite3_result_error(pCx, e.message, -1); + capi.sqlite3_result_error(pCtx, 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))) } - catch(e){ __setCxErr(pCx, e) } + return function(pCtx, argc, pArgv){ + try{ __setUdfResult(pCtx, callback(pCtx, ...__convertUdfArgs(argc, pArgv))) } + catch(e){ + //console.error('xFunc() caught:',e); + __setUdfError(pCtx, e); + } }; }; const __xInverseAndStep = function(callback){ - return function(pCx, argc, pArgv){ - try{ callback(...__extractArgs(argc, pArgv)) } - catch(e){ __setCxErr(pCx, e) } + return function(pCtx, argc, pArgv){ + try{ callback(pCtx, ...__convertUdfArgs(argc, pArgv)) } + catch(e){ __setUdfError(pCtx, e) } }; }; const __xFinalAndValue = function(callback){ - return function(pCx){ - try{ __setResult(pCx, callback()) } - catch(e){ __setCxErr(pCx, e) } + return function(pCtx){ + try{ __setUdfResult(pCtx, callback(pCtx)) } + catch(e){ __setUdfError(pCtx, e) } }; }; @@ -417,6 +444,57 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ } return rc; }; + /** + A helper for UDFs implemented in JS and bound to WASM by the + client. Given a JS value, setUdfResult(pCtx,X) calls one of the + sqlite3_result_xyz(pCtx,...) routines, depending on X's data + type: + + - `null`: sqlite3_result_null() + - `boolean`: sqlite3_result_int() + - `number': sqlite3_result_int() or sqlite3_result_double() + - `string`: sqlite3_result_text() + - Uint8Array or Int8Array: sqlite3_result_blob() + + Anything else triggers sqlite3_result_error(). + */ + capi.sqlite3_create_function_v2.setUdfResult = + capi.sqlite3_create_function.setUdfResult = + capi.sqlite3_create_window_function.setUdfResult = __setUdfResult; + + /** + A helper for UDFs implemented in JS and bound to WASM by the + client. When passed the + (argc,argv) values from the UDF-related functions which receive + them (xFunc, xStep, xInverse), it creates a JS array + representing those arguments, converting each to JS in a manner + appropriate to its data type: numeric, text, blob + (Uint8Array()), or null. + + Results are undefined if it's passed anything other than those + two arguments from those specific contexts. + + Thus an argc of 4 will result in a length-4 array containing + the converted values from the corresponding argv. + + The conversion will throw only on allocation error or an internal + error. + */ + capi.sqlite3_create_function_v2.convertUdfArgs = + capi.sqlite3_create_function.convertUdfArgs = + capi.sqlite3_create_window_function.convertUdfArgs = __convertUdfArgs; + + /** + A helper for UDFs implemented in JS and bound to WASM by the + client. It expects to be a passed `(sqlite3_context*, Error)` + (i.e. an exception object). And it sets the current UDF's + result to sqlite3_result_error_nomem() or sqlite3_result_error(), + depending on whether the 2nd argument is a + sqlite3.WasmAllocError object or not. + */ + capi.sqlite3_create_function_v2.setUdfError = + capi.sqlite3_create_function.setUdfError = + capi.sqlite3_create_window_function.setUdfError = __setUdfError; }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index f701c463f0..e6685d35a7 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -784,12 +784,21 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ the function is defined: - .arity: the number of arguments which SQL calls to this - function expect or require. The default value is the - callback's length property (i.e. the number of declared - parameters it has). A value of -1 means that the function - is variadic and may accept any number of arguments, up to - sqlite3's compile-time limits. sqlite3 will enforce the - argument count if is zero or greater. + function expect or require. The default value is + `callback.length` (i.e. the number of declared parameters it + has) **MINUS 1** (see below for why). As a special case, if + callback.length is 0, its arity is also 0 instead of -1. A + negative arity value means that the function is variadic and + may accept any number of arguments, up to sqlite3's + compile-time limits. sqlite3 will enforce the argument count if + is zero or greater. + + The callback always receives a pointer to an `sqlite3_context` + object as its first argument. Any arguments after that are from + SQL code. The leading context argument does _not_ count towards + the function's arity. See the docs for + sqlite3.capi.sqlite3_create_function_v2() for why that argument + is needed in the interface. The following properties correspond to flags documented at: @@ -827,7 +836,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ name = name.toLowerCase(); DB.checkRc(this, capi.sqlite3_create_function_v2( this.pointer, name, - (opt.hasOwnProperty('arity') ? +opt.arity : callback.length), + (opt.hasOwnProperty('arity') + ? +opt.arity + : (callback.length ? callback.length-1/*for pCtx arg*/ : 0)), capi.SQLITE_UTF8 | fFlags, null/*pApp*/, callback, null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); return this; @@ -992,10 +1003,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const bindOne = function f(stmt,ndx,bindType,val){ affirmUnlocked(stmt, 'bind()'); if(!f._){ - if(wasm.bigIntEnabled){ - f._maxInt = BigInt("0x7fffffffffffffff"); - f._minInt = ~f._maxInt; - } + f._tooBigInt = (v)=>toss3( + "BigInt value is too big to store without precision loss:", v + ); /* Reminder: when not in BigInt mode, it's impossible for JS to represent a number out of the range we can bind, so we have no range checking. */ @@ -1041,15 +1051,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ let m; if(util.isInt32(val)) m = capi.sqlite3_bind_int; else if('bigint'===typeof val){ - if(valf._maxInt){ - toss3("BigInt value is out of range for storing as int64: "+val); + if(!util.bigIntFits64(val)){ + f._tooBigInt(val); }else if(wasm.bigIntEnabled){ m = capi.sqlite3_bind_int64; - }else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){ + }else if(util.bigIntFitsDouble(val)){ val = Number(val); m = capi.sqlite3_bind_double; }else{ - toss3("BigInt value is out of range for storing as double: "+val); + f._tooBigInt(val); } }else{ // !int32, !bigint val = Number(val); @@ -1298,8 +1308,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ case capi.SQLITE_ROW: return this._mayGet = true; default: this._mayGet = false; - console.warn("sqlite3_step() rc=",rc,"SQL =", - capi.sqlite3_sql(this.pointer)); + console.warn("sqlite3_step() rc=",rc, + capi.sqlite3_web_rc_str(rc), + "SQL =", capi.sqlite3_sql(this.pointer)); DB.checkRc(this.db.pointer, rc); } }, diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 9f40699439..f21655b823 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -176,10 +176,39 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( double-type DB operations for integer values in order to keep more precision. */ - const isInt32 = function(n){ + const isInt32 = (n)=>{ return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); }; + /** + Returns true if the given BigInt value is small enough to fit + into an int64 value, else false. + */ + const bigIntFits64 = function f(b){ + if(!f._max){ + f._max = BigInt("0x7fffffffffffffff"); + f._min = ~f._max; + } + return b >= f._min && b <= f._max; + }; + + /** + Returns true if the given BigInt value is small enough to fit + into an int32, else false. + */ + const bigIntFits32 = (b)=>(b >= (-0x7fffffffn - 1n) && b <= 0x7fffffffn); + + /** + Returns true if the given BigInt value is small enough to fit + into a double value without loss of precision, else false. + */ + const bigIntFitsDouble = function f(b){ + if(!f._min){ + f._min = Number.MIN_SAFE_INTEGER; + f._max = Number.MAX_SAFE_INTEGER; + } + return b >= f._min && b <= f._max; + }; /** Returns v if v appears to be a TypedArray, else false. */ const isTypedArray = (v)=>{ @@ -288,29 +317,52 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( The semantics of JS functions are: - xFunc: is passed `(arrayOfValues)`. Its return value becomes + xFunc: is passed `(pCtx, ...values)`. Its return value becomes the new SQL function's result. - xStep: is passed `(arrayOfValues)`. Its return value is + xStep: is passed `(pCtx, ...values)`. Its return value is ignored. - xFinal: is passed no arguments. Its return value becomes the - new aggragate SQL function's result. + xFinal: is passed `(pCtx)`. Its return value becomes the new + aggregate SQL function's result. xDestroy: is passed `(void*)`. Its return value is ignored. The pointer passed to it is the one from the 5th argument to sqlite3_create_function_v2(). - Note that JS callback implementations have different call - signatures than their native counterparts (namely, they do not - get passed an `sqlite3_context*` argument) because practice has - shown that this is almost always more convenient and desirable - in JS code. Clients which need access to the full range of - native arguments will have to create a WASM-bound function - themselves (using wasm.installFunction() or equivalent) and - pass that function's WASM pointer to this function, rather than - passing a JS function. Be warned, however, that working with - UDFs at that level from JS is quite tedious. + Note that: + + - `pCtx` in the above descriptions is a `sqlite3_context*`. 99 + times out of a hundred, or maybe more, that initial argument + will be irrelevant for JS UDF bindings, but it needs to be + there so that the cases where it _is_ relevant, in particular + with window and aggregate functions, have full access to the + underlying sqlite3 APIs. + + - When wrapping JS functions, the remaining arguments arrive as + positional arguments, not as an array of arguments, because + that allows callback definitions to be more JS-idiomatic than + C-like, for example `(pCtx,a,b)=>a+b` is more intuitive and + legible than `(pCtx,args)=>args[0]+args[1]`. For cases where + an array of arguments would be more convenient, the callbacks + simply need to be declared like `(pCtx,...args)=>{...}`, in + which case `args` will be an array. + + - If a JS wrapper throws, it gets translated to + sqlite3_result_error() or sqlite3_result_error_nomem(), + depending on whether the exception is an + sqlite3.WasmAllocError object or not. + + - When passing on WASM function pointers, arguments are _not_ + converted or reformulated. They are passed on as-is in raw + pointer form using their native C signatures. Only JS + functions passed in to this routine, and thus wrapped by this + routine, get automatic conversions of arguments and result + values. The routines which perform those conversions are + exposed for client-side use as + sqlite3_create_function_v2.convertUdfArgs() and + sqlite3_create_function_v2.setUdfResult(). sqlite3_create_function() + and sqlite3_create_window_function() have those same methods. For xFunc(), xStep(), and xFinal(): @@ -323,19 +375,26 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( doubles. TODO: use BigInt support if enabled. That feature was added after this functionality was implemented. - If any JS-side functions throw, those exceptions are - intercepted and converted to database-side errors with - the exception of xFinal(): any exception from it is - ignored, possibly generating a console.error() message. - Destructors must not throw. + If any JS-side bound functions throw, those exceptions are + intercepted and converted to database-side errors with the + exception of xDestroy(): any exception from it is ignored, + possibly generating a console.error() message. Destructors + must not throw. Once installed, there is currently no way to uninstall the - bound methods from WASM. They can be uninstalled from the - database as documented in the C API, but this wrapper currently - has no infrastructure in place to also free the WASM-bound JS - wrappers, effectively resulting in a memory leak if the client - uninstalls the UDF. Improving that is a potential TODO, but - removing client-installed UDFs is rare in practice. + automatically-converted WASM-bound JS functions from WASM. They + can be uninstalled from the database as documented in the C + API, but this wrapper currently has no infrastructure in place + to also free the WASM-bound JS wrappers, effectively resulting + in a memory leak if the client uninstalls the UDF. Improving that + is a potential TODO, but removing client-installed UDFs is rare + in practice. If this factor is relevant for a given client, + they can create WASM-bound JS functions themselves, hold on to their + pointers, and pass the pointers in to here. Later on, they can + free those pointers (using `wasm.uninstallFunction()` or + equivalent). + + C reference: https://www.sqlite.org/c3ref/create_function.html Maintenance reminder: the ability to add new WASM-accessible functions to the runtime requires that the @@ -344,10 +403,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ sqlite3_create_function_v2: function( pDb, funcName, nArg, eTextRep, pApp, - xFunc, //function(arrayOfValues) - xStep, //function(arrayOfValues) - xFinal, //function() - xDestroy //function(void*) + xFunc, xStep, xFinal, xDestroy ){/*installed later*/}, /** Equivalent to passing the same arguments to @@ -355,9 +411,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ sqlite3_create_function:function( pDb, funcName, nArg, eTextRep, pApp, - xFunc, //function(arrayOfValues) - xStep, //function(arrayOfValues) - xFinal //function() + xFunc, xStep, xFinal ){/*installed later*/}, /** The sqlite3_create_window_function() JS wrapper differs from @@ -368,11 +422,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ sqlite3_create_window_function: function( pDb, funcName, nArg, eTextRep, pApp, - xStep, //function(arrayOfValues) - xFinal, //function() - xValue, //function() - xInverse,//function(arrayOfValues) - xDestroy //function(void*) + xStep, xFinal, xValue, xInverse, xDestroy ){/*installed later*/}, /** The sqlite3_prepare_v3() binding handles two different uses @@ -466,7 +516,9 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( removed. */ util:{ - affirmBindableTypedArray, arrayToString, isBindableTypedArray, + affirmBindableTypedArray, arrayToString, + bigIntFits32, bigIntFits64, bigIntFitsDouble, + isBindableTypedArray, isInt32, isSQLableTypedArray, isTypedArray, typedArrayToString, isMainWindow: ()=>{ @@ -680,6 +732,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( */ capi.wasm.bindingSignatures = [ // Please keep these sorted by function name! + ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*" /* We should arguably write a custom wrapper which knows how to handle Blob, TypedArrays, and JS strings. */ @@ -766,11 +819,13 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_uri_boolean", "int", "string", "string", "int"], ["sqlite3_uri_key", "string", "string", "int"], ["sqlite3_uri_parameter", "string", "string", "string"], - ["sqlite3_value_blob", "*", "*"], - ["sqlite3_value_bytes","int", "*"], - ["sqlite3_value_double","f64", "*"], - ["sqlite3_value_text", "string", "*"], - ["sqlite3_value_type", "int", "*"], + ["sqlite3_user_data","void*", "sqlite3_context*"], + ["sqlite3_value_blob", "*", "sqlite3_value*"], + ["sqlite3_value_bytes","int", "sqlite3_value*"], + ["sqlite3_value_double","f64", "sqlite3_value*"], + ["sqlite3_value_int","int", "sqlite3_value*"], + ["sqlite3_value_text", "string", "sqlite3_value*"], + ["sqlite3_value_type", "int", "sqlite3_value*"], ["sqlite3_vfs_find", "*", "string"], ["sqlite3_vfs_register", "int", "*", "int"] ]/*capi.wasm.bindingSignatures*/; @@ -794,8 +849,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_malloc64", "*","i64"], ["sqlite3_msize", "i64", "*"], ["sqlite3_realloc64", "*","*", "i64"], + ["sqlite3_result_int64",undefined, "*", "i64"], ["sqlite3_total_changes64", "i64", ["sqlite3*"]], - ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]] + ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]], + ["sqlite3_value_int64","i64", "sqlite3_value*"], ]; /** diff --git a/ext/wasm/demo-123.js b/ext/wasm/demo-123.js index 37dc7a7af1..67c1094f02 100644 --- a/ext/wasm/demo-123.js +++ b/ext/wasm/demo-123.js @@ -160,7 +160,7 @@ log("Create a scalar UDF..."); db.createFunction({ name: 'twice', - callback: function(arg){ // note the call arg count + callback: function(pCx, arg){ // note the call arg count return arg + arg; } }); diff --git a/ext/wasm/testing1.js b/ext/wasm/testing1.js index 053fb6f277..7c20fdbbbd 100644 --- a/ext/wasm/testing1.js +++ b/ext/wasm/testing1.js @@ -194,38 +194,37 @@ }/*testBasicSanity()*/; const testUDF = function(db){ - db.createFunction("foo",function(a,b){return a+b}); + db.createFunction("foo",(pCx,a,b)=>a+b); T.assert(7===db.selectValue("select foo(3,4)")). assert(5===db.selectValue("select foo(3,?)",2)). assert(5===db.selectValue("select foo(?,?2)",[1,4])). assert(5===db.selectValue("select foo($a,$b)",{$a:0,$b:5})); db.createFunction("bar", { arity: -1, - callback: function(){ + callback: function(pCx){ var rc = 0; - for(let i = 0; i < arguments.length; ++i) rc += arguments[i]; + for(let i = 1; i < arguments.length; ++i) rc += arguments[i]; return rc; } }).createFunction({ name: "asis", - callback: (arg)=>arg + callback: (pCx,arg)=>arg }); - + //log("Testing DB::selectValue() w/ UDF..."); T.assert(0===db.selectValue("select bar()")). assert(1===db.selectValue("select bar(1)")). assert(3===db.selectValue("select bar(1,2)")). assert(-1===db.selectValue("select bar(1,2,-4)")). - assert('hi'===db.selectValue("select asis('hi')")); - - T.assert('hi' === db.selectValue("select ?",'hi')). - assert(null===db.selectValue("select null")). - assert(null === db.selectValue("select ?",null)). - assert(null === db.selectValue("select ?",[null])). - assert(null === db.selectValue("select $a",{$a:null})). + assert('hi' === db.selectValue("select asis('hi')")). + assert('hi' === db.selectValue("select ?",'hi')). + assert(null === db.selectValue("select null")). + assert(null === db.selectValue("select asis(null)")). + assert(1 === db.selectValue("select ?",1)). + assert(2 === db.selectValue("select ?",[2])). + assert(3 === db.selectValue("select $a",{$a:3})). assert(eqApprox(3.1,db.selectValue("select 3.0 + 0.1"))). - assert(eqApprox(1.3,db.selectValue("select asis(1 + 0.3)"))) - ; + assert(eqApprox(1.3,db.selectValue("select asis(1 + 0.3)"))); //log("Testing binding and UDF propagation of blobs..."); let blobArg = new Uint8Array(2); @@ -354,7 +353,7 @@ T.assert(g64(pMin) === minMaxI64[0]). assert(minMaxI64[0] === db.selectValue("select ?",g64(pMin))). assert(minMaxI64[1] === db.selectValue("select ?",g64(pMax))); - const rxRange = /out of range for storing as int64/; + const rxRange = /too big/; T.mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[0] - BigInt(1))}, rxRange). mustThrowMatching(()=>{db.prepare("select ?").bind(minMaxI64[1] + BigInt(1))}, diff --git a/manifest b/manifest index f77b9db145..2c0461c342 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 +C More\scleanups\sin\sthe\sUDF\sargument\sand\sresult\shandling,\sin\sparticular\sint64.\sConsolidate\ssome\sduplicate\sint64/bigint\srange\schecking\scode.\sExpose\sthe\sUDF\slow-level\sutilities\s(arg/result\sconversion)\sto\sclient\scode.\sAdd\sthe\ssqlite3_context\spointer\sto\sthe\sJS-side\sUDF\swrappers\sfor\sAPI\sconsistency. +D 2022-10-02T22:50:04.828 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 e35ddfbfcde83571a1169a910dbcb59bf598a3a8f5283b42d88555b9ccaa6042 +F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 9b16040f37805ee7c30f922a970a57d3f2a822d0675a8f5d70f15061e300c4ce 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 d1587736ed73fcb44e32f1ff1933e4c91a2d3b3c39acef0d13c0b3fd6859a7b1 -F ext/wasm/api/sqlite3-api-oo1.js 48d2269544301cd97726ef2d9a82e4384350d12dcf832fa417f211811ae5272d +F ext/wasm/api/sqlite3-api-glue.js b962bad752b62366651dae26c0b969d297f81e17879685025fb12130786509cb +F ext/wasm/api/sqlite3-api-oo1.js 484f9ea5c7140d07745f4b534a1f6dd67120c65ef34abcf7cdb3a388d73f5ef4 F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541 -F ext/wasm/api/sqlite3-api-prologue.js d71ad817cdef8a9b3b64a394b781a8f64872d4983eac583167e29f9f96ef8e4e +F ext/wasm/api/sqlite3-api-prologue.js bf270c17e759814decf57f6dd29fee9b5e44dd89a798a1ba9ba1e34d6f76ceaf 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 @@ -500,7 +500,7 @@ F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f5 F ext/wasm/common/whwasmutil.js 427eb8b695bd5f38497601a6bda933e83d1a5900b75f5f1af8dbb381898d2ee4 F ext/wasm/demo-123-worker.html e419b66495d209b5211ec64903b4cfb3ca7df20d652b41fcd28bf018a773234f F ext/wasm/demo-123.html aa281d33b7eefa755f3122b7b5a18f39a42dc5fb69c8879171bf14b4c37c4ec4 -F ext/wasm/demo-123.js 536579fd587974c2511c5bf82034b253d4fdeceabb726927ad7599ef6b7578e8 +F ext/wasm/demo-123.js 9fbc5cd3af842d361e9f8353ae4af9f471c2b2517e55446474406620485b9ee6 F ext/wasm/demo-kvvfs1.html 7d4f28873de67f51ac18c584b7d920825139866a96049a49c424d6f5a0ea5e7f F ext/wasm/demo-kvvfs1.js 105596bd2ccd0b1deb5fde8e99b536e8242d4bb5932fac0c8403ff3a6bc547e8 F ext/wasm/fiddle.make 3f4efd62bc2a9c883bfcea52ae2755114a62d444d6d042df287f4aef301d6c6c @@ -530,7 +530,7 @@ F ext/wasm/test-opfs-vfs.js a59ff9210b17d46b0c6fbf6a0ba60143c033327865f2e556e14f F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893 F ext/wasm/testing-worker1-promiser.js bd788e33c1807e0a6dda9c9a9d784bd3350ca49c9dd8ae2cc8719b506b6e013e F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae -F ext/wasm/testing1.js bdea170b16189028c1f63023c620df52ddf31ed416bad56d729c60031b1e27ae +F ext/wasm/testing1.js 0f87073086eff3a152f013874f1c9a710e63d2e069f90dfeb8333ffe82476d04 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3 F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494 F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f @@ -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 435ab33384017967e46f52b70bee851a85a28808990a0e58dd5288f606b89c9c -R 8d670d2cae99839795cb81bda9741460 +P d3bad9347c5423fa7f19ae729461636f1043c99a1f01f168efa10bebefb1cdd1 +R 46b070d886b6400d4ed82000c86b9d6e U stephan -Z 74556b5dc28de703cf507dc7ef6550bd +Z fc77e81a038294d78aff4ec7221b0be3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index aca6ce8b8a..e633b1fc49 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d3bad9347c5423fa7f19ae729461636f1043c99a1f01f168efa10bebefb1cdd1 \ No newline at end of file +10ab77af952abf09f93f342a9d07a3b133f2c4c0a3588df3390cd3a923cafae4 \ No newline at end of file