diff options
Diffstat (limited to 'ext/wasm/api/sqlite3-api-prologue.js')
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 147 |
1 files changed, 102 insertions, 45 deletions
diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 9f4069943..f21655b82 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*"], ]; /** |