"*"/*xInverse*/, "*"/*xDestroy*/]
);
- const __udfSetResult = function(pCtx, val){
- //console.warn("udfSetResult",typeof val, val);
- switch(typeof val) {
- case 'undefined':
- /* Assume that the client already called sqlite3_result_xxx(). */
- break;
- case 'boolean':
- 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)(pCtx, val);
- break;
- }
- case 'string':
- capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
- break;
- case 'object':
- if(null===val/*yes, typeof null === 'object'*/) {
- capi.sqlite3_result_null(pCtx);
- break;
- }else if(util.isBindableTypedArray(val)){
- const pBlob = wasm.allocFromTypedArray(val);
- capi.sqlite3_result_blob(
- 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:",(typeof val), val);
- };
- }/*__udfSetResult()*/;
-
- const __udfConvertArgs = function(argc, pArgv){
- let i;
- const tgt = [];
- for(i = 0; i < argc; ++i){
- /**
- Curiously: despite ostensibly requiring 8-byte
- alignment, the pArgv array is parcelled into chunks of
- 4 bytes (1 pointer each). The values those point to
- have 8-byte alignment but the individual argv entries
- do not.
- */
- tgt.push(capi.sqlite3_value_to_js(
- wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
- ));
- }
- return tgt;
- }/*__udfConvertArgs()*/;
-
- const __udfSetError = (pCtx, e)=>{
- if(e instanceof sqlite3.WasmAllocError){
- capi.sqlite3_result_error_nomem(pCtx);
- }else{
- const msg = ('string'===typeof e) ? e : e.message;
- capi.sqlite3_result_error(pCtx, msg, -1);
- }
- };
-
const __xFunc = function(callback){
return function(pCtx, argc, pArgv){
- try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) }
- catch(e){
+ try{
+ capi.sqlite3_result_js(
+ pCtx,
+ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv))
+ );
+ }catch(e){
//console.error('xFunc() caught:',e);
- __udfSetError(pCtx, e);
+ capi.sqlite3_result_error_js(pCtx, e);
}
};
};
const __xInverseAndStep = function(callback){
return function(pCtx, argc, pArgv){
- try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) }
- catch(e){ __udfSetError(pCtx, e) }
+ try{ callback(pCtx, ...capi.sqlite3_values_to_js(argc, pArgv)) }
+ catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
};
};
const __xFinalAndValue = function(callback){
return function(pCtx){
- try{ __udfSetResult(pCtx, callback(pCtx)) }
- catch(e){ __udfSetError(pCtx, e) }
+ try{ capi.sqlite3_result_js(pCtx, callback(pCtx)) }
+ catch(e){ capi.sqlite3_result_error_js(pCtx, e) }
};
};
return rc;
};
/**
- A helper for UDFs implemented in JS and bound to WASM by the
- client. Given a JS value, udfSetResult(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()
- - `undefined`: indicates that the UDF called one of the
- `sqlite3_result_xyz()` routines on its own, making this
- function a no-op. Results are _undefined_ if this function is
- passed the `undefined` value but did _not_ call one of the
- `sqlite3_result_xyz()` routines.
-
- Anything else triggers sqlite3_result_error().
+ A _deprecated_ alias for capi.sqlite3_result_js() which
+ predates the addition of that function in the public API.
*/
capi.sqlite3_create_function_v2.udfSetResult =
capi.sqlite3_create_function.udfSetResult =
- capi.sqlite3_create_window_function.udfSetResult = __udfSetResult;
+ capi.sqlite3_create_window_function.udfSetResult = capi.sqlite3_result_js;
/**
- 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.
+ A _deprecated_ alias for capi.sqlite3_values_to_js() which
+ predates the addition of that function in the public API.
*/
capi.sqlite3_create_function_v2.udfConvertArgs =
capi.sqlite3_create_function.udfConvertArgs =
- capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs;
+ capi.sqlite3_create_window_function.udfConvertArgs = capi.sqlite3_values_to_js;
/**
- A helper for UDFs implemented in JS and bound to WASM by the
- client. It expects to be a passed `(sqlite3_context*, Error)`
- (an exception object or message string). 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.
+ A _deprecated_ alias for capi.sqlite3_result_error_js() which
+ predates the addition of that function in the public API.
*/
capi.sqlite3_create_function_v2.udfSetError =
capi.sqlite3_create_function.udfSetError =
- capi.sqlite3_create_window_function.udfSetError = __udfSetError;
+ capi.sqlite3_create_window_function.udfSetError = capi.sqlite3_result_error_js;
}/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/;
By default it throws if it cannot determine any sensible
conversion. If passed a falsy second argument, it instead returns
- `undefined` if no suitable conversion is found. Note that there
+ `undefined` if no suitable conversion is found. Note that there
is no conversion from SQL to JS which results in the `undefined`
- value, so `undefined` has an unambiguous meaning here.
+ value, so `undefined` has an unambiguous meaning here. It will
+ always throw a WasmAllocError if allocating memory for a
+ conversion fails.
Caveats:
return arg;
};
+ /**
+ Requires a C-style array of `sqlite3_value*` objects and the
+ number of entries in that array. Returns a JS array containing
+ the results of passing each C array entry to
+ sqlite3_value_to_js(). The 3rd argument to this function is
+ passed on as the 2nd argument to that one.
+ */
+ capi.sqlite3_values_to_js = function(argc,pArgv,throwIfCannotConvert=true){
+ let i;
+ const tgt = [];
+ for(i = 0; i < argc; ++i){
+ /**
+ Curiously: despite ostensibly requiring 8-byte
+ alignment, the pArgv array is parcelled into chunks of
+ 4 bytes (1 pointer each). The values those point to
+ have 8-byte alignment but the individual argv entries
+ do not.
+ */
+ tgt.push(capi.sqlite3_value_to_js(
+ wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
+ ));
+ }
+ return tgt;
+ };
+
+ /**
+ Calls either sqlite3_result_error_nomem(), if e is-a
+ WasmAllocError, or sqlite3_result_error(). In the latter case,
+ the second arugment is coerced to a string to create the error
+ message.
+
+ The first argument is a (sqlite3_context*). Returns void.
+ Does not throw.
+ */
+ capi.sqlite3_result_error_js = function(pCtx,e){
+ if(e instanceof WasmAllocError){
+ capi.sqlite3_result_error_nomem(pCtx);
+ }else{
+ /* Maintenance reminder: ''+e, rather than e.message,
+ will prefix e.message with e.name, so it includes
+ the exception's type name in the result. */;
+ capi.sqlite3_result_error(pCtx, ''+e, -1);
+ }
+ };
+
+ /**
+ This function passes its 2nd argument to one of the
+ sqlite3_result_xyz() routines, depending on the type of that
+ argument:
+
+ - If (val instanceof Error), this function passes it to
+ sqlite3_result_error_js().
+ - `null`: `sqlite3_result_null()`
+ - `boolean`: `sqlite3_result_int()` with a value of 0 or 1.
+ - `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or
+ `sqlite3_result_double()`, depending on the range of the number
+ and whether or not int64 support is enabled.
+ - `bigint`: similar to `number` but will trigger an error if the
+ value is too big to store in an int64.
+ - `string`: `sqlite3_result_text()`
+ - Uint8Array or Int8Array: `sqlite3_result_blob()`
+ - `undefined`: is a no-op provided to simplify certain use cases.
+
+ Anything else triggers `sqlite3_result_error()` with a
+ description of the problem.
+
+ The first argument to this function is a `(sqlite3_context*)`.
+ Returns void. Does not throw.
+ */
+ capi.sqlite3_result_js = function(pCtx,val){
+ if(val instanceof Error){
+ capi.sqlite3_result_error_js(pCtx, val);
+ return;
+ }
+ try{
+ switch(typeof val) {
+ case 'undefined':
+ /* This is a no-op. This routine originated in the create_function()
+ family of APIs and in that context, passing in undefined indicated
+ that the caller was responsible for calling sqlite3_result_xxx()
+ (if needed). */
+ break;
+ case 'boolean':
+ capi.sqlite3_result_int(pCtx, val ? 1 : 0);
+ break;
+ case 'bigint':
+ if(util.bigIntFits32(val)){
+ capi.sqlite3_result_int(pCtx, Number(val));
+ }else if(util.bigIntFitsDouble(val)){
+ capi.sqlite3_result_double(pCtx, Number(val));
+ }else 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{
+ toss3("BigInt value",val.toString(),"is too BigInt.");
+ }
+ break;
+ case 'number': {
+ let f;
+ if(util.isInt32(val)){
+ f = capi.sqlite3_result_int;
+ }else if(wasm.bigIntEnabled
+ && Number.isInteger(val)
+ && util.bigIntFits64(BigInt(val))){
+ f = capi.sqlite3_result_int64;
+ }else{
+ f = capi.sqlite3_result_double;
+ }
+ f(pCtx, val);
+ break;
+ }
+ case 'string':
+ capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT);
+ break;
+ case 'object':
+ if(null===val/*yes, typeof null === 'object'*/) {
+ capi.sqlite3_result_null(pCtx);
+ break;
+ }else if(util.isBindableTypedArray(val)){
+ const pBlob = wasm.allocFromTypedArray(val);
+ capi.sqlite3_result_blob(
+ 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:",(typeof val), val);
+ }
+ }catch(e){
+ capi.sqlite3_result_error_js(pCtx, e);
+ }
+ };
+
/* The remainder of the API will be set up in later steps. */
const sqlite3 = {
WasmAllocError: WasmAllocError,
-C Export\ssqlite3_result_subtype()\sand\ssqlite3_value_dup/free/subtype()\sto\sWASM.
-D 2022-12-09T15:26:58.074
+C Refactor\sthe\sinternal\sJS\sroutines\sfor\sconverting\sUDF\sresults\sand\serrors\sto\sJS\sinto\spublic\sAPIs.
+D 2022-12-10T10:24:46.478
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
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 88e62a380b39a924728d9ed4f74f5424f0ea36e8d124df7da9268cc01ad0b191
+F ext/wasm/api/sqlite3-api-glue.js fc2b58b3309fa404d3e58499609e5c5b17177687f53f1a6703c50067904d7f72
F ext/wasm/api/sqlite3-api-oo1.js 6d10849609231ccd46fa11b1d3fbbe0f45d9fe84c66a0b054601036540844300
-F ext/wasm/api/sqlite3-api-prologue.js 2582c5a983e6213687153125a12e7f7496a1cd474ee24d777675b104028b0a61
+F ext/wasm/api/sqlite3-api-prologue.js 5173cf1f434410c40fa7c6c6c77aca2a969ef7750f9e099175439d6f9c381407
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 vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P de8fc4bf34f80f320012a0e506ed8e3e24806daf67845d5dabb00b916108f6ef
-R cd32a70b8175f1ae6d43525e76cb6794
+P 4600a7bbdc4cbe14591d48ea19fe5f7de3a0c10a14cdd97fd51e263a13163d10
+R 87bf3540e42b9fcd39b8a805ab39f0fe
U stephan
-Z 75119f06284b4ae77a74934eba10a318
+Z e910f830b2b784709a3d3c5d3fc7fb9d
# Remove this line to create a well-formed Fossil manifest.
-4600a7bbdc4cbe14591d48ea19fe5f7de3a0c10a14cdd97fd51e263a13163d10
\ No newline at end of file
+35d1d63c7d60119b64341c561294890812837d5432d1d7bed3ed88d6212fbfa0
\ No newline at end of file