13961b263Sstephan/* 23961b263Sstephan 2022-07-22 33961b263Sstephan 43961b263Sstephan The author disclaims copyright to this source code. In place of a 53961b263Sstephan legal notice, here is a blessing: 63961b263Sstephan 73961b263Sstephan * May you do good and not evil. 83961b263Sstephan * May you find forgiveness for yourself and forgive others. 93961b263Sstephan * May you share freely, never taking more than you give. 103961b263Sstephan 113961b263Sstephan *********************************************************************** 123961b263Sstephan 133961b263Sstephan This file glues together disparate pieces of JS which are loaded in 143961b263Sstephan previous steps of the sqlite3-api.js bootstrapping process: 153961b263Sstephan sqlite3-api-prologue.js, whwasmutil.js, and jaccwabyt.js. It 163961b263Sstephan initializes the main API pieces so that the downstream components 173961b263Sstephan (e.g. sqlite3-api-oo1.js) have all that they need. 183961b263Sstephan*/ 19e3cd6760Sstephanself.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ 203961b263Sstephan 'use strict'; 213961b263Sstephan const toss = (...args)=>{throw new Error(args.join(' '))}; 22510a9d1cSstephan const toss3 = sqlite3.SQLite3Error.toss; 233961b263Sstephan const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util; 243961b263Sstephan self.WhWasmUtilInstaller(capi.wasm); 253961b263Sstephan delete self.WhWasmUtilInstaller; 263961b263Sstephan 273961b263Sstephan if(0){ 283961b263Sstephan /* "The problem" is that the following isn't type-safe. 293961b263Sstephan OTOH, nothing about WASM pointers is. */ 303961b263Sstephan /** 31d9801185Sstephan Add the `.pointer` xWrap() signature entry to extend the 32d9801185Sstephan `pointer` arg handler to check for a `pointer` property. This 33d9801185Sstephan can be used to permit, e.g., passing an sqlite3.oo1.DB instance 34d9801185Sstephan to a C-style sqlite3_xxx function which takes an `sqlite3*` 35d9801185Sstephan argument. 363961b263Sstephan */ 37e67a0f40Sstephan const xPointer = wasm.xWrap.argAdapter('pointer'); 383961b263Sstephan const adapter = function(v){ 39e67a0f40Sstephan if(v && v.constructor){ 403961b263Sstephan const x = v.pointer; 413961b263Sstephan if(Number.isInteger(x)) return x; 42e67a0f40Sstephan else toss("Invalid (object) type for .pointer-type argument."); 433961b263Sstephan } 44e67a0f40Sstephan return xPointer(v); 453961b263Sstephan }; 463961b263Sstephan wasm.xWrap.argAdapter('.pointer', adapter); 47e3cd6760Sstephan } /* ".pointer" xWrap() argument adapter */ 483961b263Sstephan 49e67a0f40Sstephan if(1){/* Convert Arrays and certain TypedArrays to strings for 50e67a0f40Sstephan 'flexible-string'-type arguments */ 51e67a0f40Sstephan const xString = wasm.xWrap.argAdapter('string'); 52e67a0f40Sstephan wasm.xWrap.argAdapter( 53*ef9cd12eSstephan 'flexible-string', (v)=>xString(util.flexibleString(v)) 54e67a0f40Sstephan ); 55e67a0f40Sstephan } 56e67a0f40Sstephan 57e67a0f40Sstephan if(1){// WhWasmUtil.xWrap() bindings... 583961b263Sstephan /** 5963e9ec2fSstephan Add some descriptive xWrap() aliases for '*' intended to (A) 6063e9ec2fSstephan initially improve readability/correctness of capi.signatures 6163e9ec2fSstephan and (B) eventually perhaps provide automatic conversion from 6263e9ec2fSstephan higher-level representations, e.g. capi.sqlite3_vfs to 6363e9ec2fSstephan `sqlite3_vfs*` via capi.sqlite3_vfs.pointer. 643961b263Sstephan */ 653961b263Sstephan const aPtr = wasm.xWrap.argAdapter('*'); 66510a9d1cSstephan wasm.xWrap.argAdapter('sqlite3*', aPtr) 67510a9d1cSstephan ('sqlite3_stmt*', aPtr) 68510a9d1cSstephan ('sqlite3_context*', aPtr) 69510a9d1cSstephan ('sqlite3_value*', aPtr) 70510a9d1cSstephan ('void*', aPtr); 71510a9d1cSstephan wasm.xWrap.resultAdapter('sqlite3*', aPtr) 72510a9d1cSstephan ('sqlite3_stmt*', aPtr) 73510a9d1cSstephan ('sqlite3_context*', aPtr) 74510a9d1cSstephan ('void*', aPtr); 753961b263Sstephan 763961b263Sstephan /** 773961b263Sstephan Populate api object with sqlite3_...() by binding the "raw" wasm 783961b263Sstephan exports into type-converting proxies using wasm.xWrap(). 793961b263Sstephan */ 803961b263Sstephan for(const e of wasm.bindingSignatures){ 813961b263Sstephan capi[e[0]] = wasm.xWrap.apply(null, e); 823961b263Sstephan } 8373079dbaSstephan for(const e of wasm.bindingSignatures.wasm){ 8473079dbaSstephan capi.wasm[e[0]] = wasm.xWrap.apply(null, e); 8573079dbaSstephan } 863961b263Sstephan 87e3cd6760Sstephan /* For C API functions which cannot work properly unless 883961b263Sstephan wasm.bigIntEnabled is true, install a bogus impl which 893961b263Sstephan throws if called when bigIntEnabled is false. */ 903961b263Sstephan const fI64Disabled = function(fname){ 913961b263Sstephan return ()=>toss(fname+"() disabled due to lack", 923961b263Sstephan "of BigInt support in this build."); 933961b263Sstephan }; 943961b263Sstephan for(const e of wasm.bindingSignatures.int64){ 953961b263Sstephan capi[e[0]] = wasm.bigIntEnabled 963961b263Sstephan ? wasm.xWrap.apply(null, e) 973961b263Sstephan : fI64Disabled(e[0]); 983961b263Sstephan } 993961b263Sstephan 1003961b263Sstephan if(wasm.exports.sqlite3_wasm_db_error){ 1013961b263Sstephan util.sqlite3_wasm_db_error = capi.wasm.xWrap( 1023961b263Sstephan 'sqlite3_wasm_db_error', 'int', 'sqlite3*', 'int', 'string' 1033961b263Sstephan ); 1043961b263Sstephan }else{ 1053961b263Sstephan util.sqlite3_wasm_db_error = function(pDb,errCode,msg){ 1063961b263Sstephan console.warn("sqlite3_wasm_db_error() is not exported.",arguments); 1073961b263Sstephan return errCode; 1083961b263Sstephan }; 1093961b263Sstephan } 1103961b263Sstephan 1113961b263Sstephan /** 1123961b263Sstephan When registering a VFS and its related components it may be 1133961b263Sstephan necessary to ensure that JS keeps a reference to them to keep 1143961b263Sstephan them from getting garbage collected. Simply pass each such value 1153961b263Sstephan to this function and a reference will be held to it for the life 1163961b263Sstephan of the app. 1173961b263Sstephan */ 1183961b263Sstephan capi.sqlite3_vfs_register.addReference = function f(...args){ 1193961b263Sstephan if(!f._) f._ = []; 1203961b263Sstephan f._.push(...args); 1213961b263Sstephan }; 1223961b263Sstephan 1233961b263Sstephan }/*xWrap() bindings*/; 1243961b263Sstephan 1253961b263Sstephan /** 126e67a0f40Sstephan Internal helper to assist in validating call argument counts in 127e67a0f40Sstephan the hand-written sqlite3_xyz() wrappers. We do this only for 128e67a0f40Sstephan consistency with non-special-case wrappings. 129e67a0f40Sstephan */ 130e67a0f40Sstephan const __dbArgcMismatch = (pDb,f,n)=>{ 131e67a0f40Sstephan return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE, 132e67a0f40Sstephan f+"() requires "+n+" argument"+ 133e67a0f40Sstephan (1===n?'':'s')+"."); 134e67a0f40Sstephan }; 135e67a0f40Sstephan 136e67a0f40Sstephan /** 137e67a0f40Sstephan Helper for flexible-string conversions which require a 138e67a0f40Sstephan byte-length counterpart argument. Passed a value and its 139e67a0f40Sstephan ostensible length, this function returns [V,N], where V 140e67a0f40Sstephan is either v or a transformed copy of v and N is either n, 141e67a0f40Sstephan -1, or the byte length of v (if it's a byte array). 142e67a0f40Sstephan */ 143e67a0f40Sstephan const __flexiString = function(v,n){ 144e67a0f40Sstephan if('string'===typeof v){ 145e67a0f40Sstephan n = -1; 146e67a0f40Sstephan }else if(util.isSQLableTypedArray(v)){ 147e67a0f40Sstephan n = v.byteLength; 148e67a0f40Sstephan v = util.typedArrayToString(v); 149e67a0f40Sstephan }else if(Array.isArray(v)){ 150e67a0f40Sstephan v = v.join(''); 151e67a0f40Sstephan n = -1; 152e67a0f40Sstephan } 153e67a0f40Sstephan return [v, n]; 154e67a0f40Sstephan }; 155e67a0f40Sstephan 156e67a0f40Sstephan if(1){/* Special-case handling of sqlite3_exec() */ 157e67a0f40Sstephan const __exec = wasm.xWrap("sqlite3_exec", "int", 158e67a0f40Sstephan ["sqlite3*", "flexible-string", "*", "*", "**"]); 159e67a0f40Sstephan /* Documented in the api object's initializer. */ 16092ede964Sstephan capi.sqlite3_exec = function f(pDb, sql, callback, pVoid, pErrMsg){ 16192ede964Sstephan if(f.length!==arguments.length){ 16292ede964Sstephan return __dbArgcMismatch(pDb,"sqlite3_exec",f.length); 163e67a0f40Sstephan }else if('function' !== typeof callback){ 164e67a0f40Sstephan return __exec(pDb, sql, callback, pVoid, pErrMsg); 165e67a0f40Sstephan } 166e67a0f40Sstephan /* Wrap the callback in a WASM-bound function and convert the callback's 167e67a0f40Sstephan `(char**)` arguments to arrays of strings... */ 168e67a0f40Sstephan const wasm = capi.wasm; 169e67a0f40Sstephan const cbwrap = function(pVoid, nCols, pColVals, pColNames){ 170e67a0f40Sstephan let rc = capi.SQLITE_ERROR; 171e67a0f40Sstephan try { 172e67a0f40Sstephan let aVals = [], aNames = [], i = 0, offset = 0; 173e67a0f40Sstephan for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){ 174e67a0f40Sstephan aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) ); 175e67a0f40Sstephan aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) ); 176e67a0f40Sstephan } 177e67a0f40Sstephan rc = callback(pVoid, nCols, aVals, aNames) | 0; 178e67a0f40Sstephan /* The first 2 args of the callback are useless for JS but 179e67a0f40Sstephan we want the JS mapping of the C API to be as close to the 180e67a0f40Sstephan C API as possible. */ 181e67a0f40Sstephan }catch(e){ 182e67a0f40Sstephan /* If we set the db error state here, the higher-level exec() call 183e67a0f40Sstephan replaces it with its own, so we have no way of reporting the 184e67a0f40Sstephan exception message except the console. We must not propagate 185e67a0f40Sstephan exceptions through the C API. */ 186e67a0f40Sstephan } 187e67a0f40Sstephan return rc; 188e67a0f40Sstephan }; 189e67a0f40Sstephan let pFunc, rc; 190e67a0f40Sstephan try{ 191e67a0f40Sstephan pFunc = wasm.installFunction("ipipp", cbwrap); 192e67a0f40Sstephan rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg); 193e67a0f40Sstephan }catch(e){ 194e67a0f40Sstephan rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 195e67a0f40Sstephan "Error running exec(): "+e.message); 196e67a0f40Sstephan }finally{ 197e67a0f40Sstephan if(pFunc) wasm.uninstallFunction(pFunc); 198e67a0f40Sstephan } 199e67a0f40Sstephan return rc; 200e67a0f40Sstephan }; 201e67a0f40Sstephan }/*sqlite3_exec() proxy*/; 202e67a0f40Sstephan 20392ede964Sstephan if(1){/* Special-case handling of sqlite3_create_function_v2() 20492ede964Sstephan and sqlite3_create_window_function() */ 2059892883eSstephan const sqlite3CreateFunction = wasm.xWrap( 2069892883eSstephan "sqlite3_create_function_v2", "int", 20792ede964Sstephan ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, 20892ede964Sstephan "int"/*eTextRep*/, "*"/*pApp*/, 20992ede964Sstephan "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, "*"/*xDestroy*/] 21092ede964Sstephan ); 21192ede964Sstephan const sqlite3CreateWindowFunction = wasm.xWrap( 21292ede964Sstephan "sqlite3_create_window_function", "int", 21392ede964Sstephan ["sqlite3*", "string"/*funcName*/, "int"/*nArg*/, 21492ede964Sstephan "int"/*eTextRep*/, "*"/*pApp*/, 21592ede964Sstephan "*"/*xStep*/,"*"/*xFinal*/, "*"/*xValue*/, 21692ede964Sstephan "*"/*xInverse*/, "*"/*xDestroy*/] 2179892883eSstephan ); 218510a9d1cSstephan 219*ef9cd12eSstephan const __udfSetResult = function(pCtx, val){ 220*ef9cd12eSstephan //console.warn("udfSetResult",typeof val, val); 2219892883eSstephan switch(typeof val) { 2229892883eSstephan case 'boolean': 223510a9d1cSstephan capi.sqlite3_result_int(pCtx, val ? 1 : 0); 224510a9d1cSstephan break; 225510a9d1cSstephan case 'bigint': 226510a9d1cSstephan if(wasm.bigIntEnabled){ 227510a9d1cSstephan if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val); 228510a9d1cSstephan else toss3("BigInt value",val.toString(),"is too BigInt for int64."); 229510a9d1cSstephan }else if(util.bigIntFits32(val)){ 230510a9d1cSstephan capi.sqlite3_result_int(pCtx, Number(val)); 231510a9d1cSstephan }else if(util.bigIntFitsDouble(val)){ 232510a9d1cSstephan capi.sqlite3_result_double(pCtx, Number(val)); 233510a9d1cSstephan }else{ 234510a9d1cSstephan toss3("BigInt value",val.toString(),"is too BigInt."); 235510a9d1cSstephan } 2369892883eSstephan break; 2379892883eSstephan case 'number': { 2389892883eSstephan (util.isInt32(val) 2399892883eSstephan ? capi.sqlite3_result_int 240510a9d1cSstephan : capi.sqlite3_result_double)(pCtx, val); 2419892883eSstephan break; 2429892883eSstephan } 2439892883eSstephan case 'string': 244510a9d1cSstephan capi.sqlite3_result_text(pCtx, val, -1, capi.SQLITE_TRANSIENT); 2459892883eSstephan break; 2469892883eSstephan case 'object': 24792ede964Sstephan if(null===val/*yes, typeof null === 'object'*/) { 248510a9d1cSstephan capi.sqlite3_result_null(pCtx); 2499892883eSstephan break; 2509892883eSstephan }else if(util.isBindableTypedArray(val)){ 2519892883eSstephan const pBlob = wasm.allocFromTypedArray(val); 25292ede964Sstephan capi.sqlite3_result_blob( 253510a9d1cSstephan pCtx, pBlob, val.byteLength, 25492ede964Sstephan wasm.exports[sqlite3.config.deallocExportName] 25592ede964Sstephan ); 2569892883eSstephan break; 2579892883eSstephan } 2589892883eSstephan // else fall through 2599892883eSstephan default: 260510a9d1cSstephan toss3("Don't not how to handle this UDF result value:",(typeof val), val); 2619892883eSstephan }; 262*ef9cd12eSstephan }/*__udfSetResult()*/; 263510a9d1cSstephan 264*ef9cd12eSstephan const __udfConvertArgs = function(argc, pArgv){ 2659892883eSstephan let i, pVal, valType, arg; 2669892883eSstephan const tgt = []; 2679892883eSstephan for(i = 0; i < argc; ++i){ 2689892883eSstephan pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i)); 2699892883eSstephan /** 2709892883eSstephan Curiously: despite ostensibly requiring 8-byte 2719892883eSstephan alignment, the pArgv array is parcelled into chunks of 2729892883eSstephan 4 bytes (1 pointer each). The values those point to 2739892883eSstephan have 8-byte alignment but the individual argv entries 2749892883eSstephan do not. 2759892883eSstephan */ 2769892883eSstephan valType = capi.sqlite3_value_type(pVal); 2779892883eSstephan switch(valType){ 2789892883eSstephan case capi.SQLITE_INTEGER: 279510a9d1cSstephan if(wasm.bigIntEnabled){ 280510a9d1cSstephan arg = capi.sqlite3_value_int64(pVal); 281510a9d1cSstephan if(util.bigIntFitsDouble(arg)) arg = Number(arg); 282510a9d1cSstephan } 283510a9d1cSstephan else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/; 284510a9d1cSstephan break; 2859892883eSstephan case capi.SQLITE_FLOAT: 2869892883eSstephan arg = capi.sqlite3_value_double(pVal); 2879892883eSstephan break; 2889892883eSstephan case capi.SQLITE_TEXT: 2899892883eSstephan arg = capi.sqlite3_value_text(pVal); 2909892883eSstephan break; 2919892883eSstephan case capi.SQLITE_BLOB:{ 2929892883eSstephan const n = capi.sqlite3_value_bytes(pVal); 2939892883eSstephan const pBlob = capi.sqlite3_value_blob(pVal); 294510a9d1cSstephan if(n && !pBlob) sqlite3.WasmAllocError.toss( 295510a9d1cSstephan "Cannot allocate memory for blob argument of",n,"byte(s)" 296510a9d1cSstephan ); 297510a9d1cSstephan arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null; 2989892883eSstephan break; 2999892883eSstephan } 3009892883eSstephan case capi.SQLITE_NULL: 3019892883eSstephan arg = null; break; 3029892883eSstephan default: 3039892883eSstephan toss3("Unhandled sqlite3_value_type()",valType, 3049892883eSstephan "is possibly indicative of incorrect", 3059892883eSstephan "pointer size assumption."); 3069892883eSstephan } 3079892883eSstephan tgt.push(arg); 3089892883eSstephan } 3099892883eSstephan return tgt; 310*ef9cd12eSstephan }/*__udfConvertArgs()*/; 3119892883eSstephan 312*ef9cd12eSstephan const __udfSetError = (pCtx, e)=>{ 313510a9d1cSstephan if(e instanceof sqlite3.WasmAllocError){ 314510a9d1cSstephan capi.sqlite3_result_error_nomem(pCtx); 3159892883eSstephan }else{ 316510a9d1cSstephan capi.sqlite3_result_error(pCtx, e.message, -1); 3179892883eSstephan } 3189892883eSstephan }; 3199892883eSstephan 3209892883eSstephan const __xFunc = function(callback){ 321510a9d1cSstephan return function(pCtx, argc, pArgv){ 322*ef9cd12eSstephan try{ __udfSetResult(pCtx, callback(pCtx, ...__udfConvertArgs(argc, pArgv))) } 323510a9d1cSstephan catch(e){ 324510a9d1cSstephan //console.error('xFunc() caught:',e); 325*ef9cd12eSstephan __udfSetError(pCtx, e); 326510a9d1cSstephan } 3279892883eSstephan }; 3289892883eSstephan }; 3299892883eSstephan 33092ede964Sstephan const __xInverseAndStep = function(callback){ 331510a9d1cSstephan return function(pCtx, argc, pArgv){ 332*ef9cd12eSstephan try{ callback(pCtx, ...__udfConvertArgs(argc, pArgv)) } 333*ef9cd12eSstephan catch(e){ __udfSetError(pCtx, e) } 3349892883eSstephan }; 3359892883eSstephan }; 3369892883eSstephan 33792ede964Sstephan const __xFinalAndValue = function(callback){ 338510a9d1cSstephan return function(pCtx){ 339*ef9cd12eSstephan try{ __udfSetResult(pCtx, callback(pCtx)) } 340*ef9cd12eSstephan catch(e){ __udfSetError(pCtx, e) } 3419892883eSstephan }; 3429892883eSstephan }; 3439892883eSstephan 3449892883eSstephan const __xDestroy = function(callback){ 3459892883eSstephan return function(pVoid){ 3469892883eSstephan try{ callback(pVoid) } 34792ede964Sstephan catch(e){ console.error("UDF xDestroy method threw:",e) } 34892ede964Sstephan }; 34992ede964Sstephan }; 35092ede964Sstephan 35192ede964Sstephan const __xMap = Object.assign(Object.create(null), { 35292ede964Sstephan xFunc: {sig:'v(pip)', f:__xFunc}, 35392ede964Sstephan xStep: {sig:'v(pip)', f:__xInverseAndStep}, 35492ede964Sstephan xInverse: {sig:'v(pip)', f:__xInverseAndStep}, 35592ede964Sstephan xFinal: {sig:'v(p)', f:__xFinalAndValue}, 35692ede964Sstephan xValue: {sig:'v(p)', f:__xFinalAndValue}, 35792ede964Sstephan xDestroy: {sig:'v(p)', f:__xDestroy} 35892ede964Sstephan }); 35992ede964Sstephan 36092ede964Sstephan const __xWrapFuncs = function(theFuncs, tgtUninst){ 36192ede964Sstephan const rc = [] 36292ede964Sstephan let k; 36392ede964Sstephan for(k in theFuncs){ 36492ede964Sstephan let fArg = theFuncs[k]; 36592ede964Sstephan if('function'===typeof fArg){ 36692ede964Sstephan const w = __xMap[k]; 36792ede964Sstephan fArg = wasm.installFunction(w.sig, w.f(fArg)); 36892ede964Sstephan tgtUninst.push(fArg); 3699892883eSstephan } 37092ede964Sstephan rc.push(fArg); 37192ede964Sstephan } 37292ede964Sstephan return rc; 3739892883eSstephan }; 37492ede964Sstephan 3759892883eSstephan /* Documented in the api object's initializer. */ 3769892883eSstephan capi.sqlite3_create_function_v2 = function f( 3779892883eSstephan pDb, funcName, nArg, eTextRep, pApp, 3789892883eSstephan xFunc, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**) 3799892883eSstephan xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) 3809892883eSstephan xFinal, //void (*xFinal)(sqlite3_context*) 3819892883eSstephan xDestroy //void (*xDestroy)(void*) 3829892883eSstephan ){ 38392ede964Sstephan if(f.length!==arguments.length){ 38492ede964Sstephan return __dbArgcMismatch(pDb,"sqlite3_create_function_v2",f.length); 3859892883eSstephan } 3869892883eSstephan /* Wrap the callbacks in a WASM-bound functions... */ 3879892883eSstephan const wasm = capi.wasm; 38892ede964Sstephan const uninstall = [/*funcs to uninstall on error*/]; 3899892883eSstephan let rc; 3909892883eSstephan try{ 39192ede964Sstephan const funcArgs = __xWrapFuncs({xFunc, xStep, xFinal, xDestroy}, 39292ede964Sstephan uninstall); 3939892883eSstephan rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, 3949892883eSstephan pApp, ...funcArgs); 3959892883eSstephan }catch(e){ 3969892883eSstephan console.error("sqlite3_create_function_v2() setup threw:",e); 3979892883eSstephan for(let v of uninstall){ 3989892883eSstephan wasm.uninstallFunction(v); 3999892883eSstephan } 4009892883eSstephan rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 4019892883eSstephan "Creation of UDF threw: "+e.message); 4029892883eSstephan } 4039892883eSstephan return rc; 4049892883eSstephan }; 4059892883eSstephan 40692ede964Sstephan capi.sqlite3_create_function = function f( 4079892883eSstephan pDb, funcName, nArg, eTextRep, pApp, 4089892883eSstephan xFunc, xStep, xFinal 4099892883eSstephan ){ 41092ede964Sstephan return (f.length===arguments.length) 41192ede964Sstephan ? capi.sqlite3_create_function_v2(pDb, funcName, nArg, eTextRep, 41292ede964Sstephan pApp, xFunc, xStep, xFinal, 0) 41392ede964Sstephan : __dbArgcMismatch(pDb,"sqlite3_create_function",f.length); 4149892883eSstephan }; 41592ede964Sstephan 41692ede964Sstephan /* Documented in the api object's initializer. */ 41792ede964Sstephan capi.sqlite3_create_window_function = function f( 41892ede964Sstephan pDb, funcName, nArg, eTextRep, pApp, 41992ede964Sstephan xStep, //void (*xStep)(sqlite3_context*,int,sqlite3_value**) 42092ede964Sstephan xFinal, //void (*xFinal)(sqlite3_context*) 42192ede964Sstephan xValue, //void (*xFinal)(sqlite3_context*) 42292ede964Sstephan xInverse,//void (*xStep)(sqlite3_context*,int,sqlite3_value**) 42392ede964Sstephan xDestroy //void (*xDestroy)(void*) 42492ede964Sstephan ){ 42592ede964Sstephan if(f.length!==arguments.length){ 42692ede964Sstephan return __dbArgcMismatch(pDb,"sqlite3_create_window_function",f.length); 42792ede964Sstephan } 42892ede964Sstephan /* Wrap the callbacks in a WASM-bound functions... */ 42992ede964Sstephan const wasm = capi.wasm; 43092ede964Sstephan const uninstall = [/*funcs to uninstall on error*/]; 43192ede964Sstephan let rc; 43292ede964Sstephan try{ 43392ede964Sstephan const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, 43492ede964Sstephan uninstall); 43592ede964Sstephan rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, 43692ede964Sstephan pApp, ...funcArgs); 43792ede964Sstephan }catch(e){ 43892ede964Sstephan console.error("sqlite3_create_function_v2() setup threw:",e); 43992ede964Sstephan for(let v of uninstall){ 44092ede964Sstephan wasm.uninstallFunction(v); 44192ede964Sstephan } 44292ede964Sstephan rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR, 44392ede964Sstephan "Creation of UDF threw: "+e.message); 44492ede964Sstephan } 44592ede964Sstephan return rc; 44692ede964Sstephan }; 447510a9d1cSstephan /** 448510a9d1cSstephan A helper for UDFs implemented in JS and bound to WASM by the 449*ef9cd12eSstephan client. Given a JS value, udfSetResult(pCtx,X) calls one of the 450510a9d1cSstephan sqlite3_result_xyz(pCtx,...) routines, depending on X's data 451510a9d1cSstephan type: 452510a9d1cSstephan 453510a9d1cSstephan - `null`: sqlite3_result_null() 454510a9d1cSstephan - `boolean`: sqlite3_result_int() 455510a9d1cSstephan - `number': sqlite3_result_int() or sqlite3_result_double() 456510a9d1cSstephan - `string`: sqlite3_result_text() 457510a9d1cSstephan - Uint8Array or Int8Array: sqlite3_result_blob() 458510a9d1cSstephan 459510a9d1cSstephan Anything else triggers sqlite3_result_error(). 460510a9d1cSstephan */ 461*ef9cd12eSstephan capi.sqlite3_create_function_v2.udfSetResult = 462*ef9cd12eSstephan capi.sqlite3_create_function.udfSetResult = 463*ef9cd12eSstephan capi.sqlite3_create_window_function.udfSetResult = __udfSetResult; 464510a9d1cSstephan 465510a9d1cSstephan /** 466510a9d1cSstephan A helper for UDFs implemented in JS and bound to WASM by the 467510a9d1cSstephan client. When passed the 468510a9d1cSstephan (argc,argv) values from the UDF-related functions which receive 469510a9d1cSstephan them (xFunc, xStep, xInverse), it creates a JS array 470510a9d1cSstephan representing those arguments, converting each to JS in a manner 471510a9d1cSstephan appropriate to its data type: numeric, text, blob 472510a9d1cSstephan (Uint8Array()), or null. 473510a9d1cSstephan 474510a9d1cSstephan Results are undefined if it's passed anything other than those 475510a9d1cSstephan two arguments from those specific contexts. 476510a9d1cSstephan 477510a9d1cSstephan Thus an argc of 4 will result in a length-4 array containing 478510a9d1cSstephan the converted values from the corresponding argv. 479510a9d1cSstephan 480510a9d1cSstephan The conversion will throw only on allocation error or an internal 481510a9d1cSstephan error. 482510a9d1cSstephan */ 483*ef9cd12eSstephan capi.sqlite3_create_function_v2.udfConvertArgs = 484*ef9cd12eSstephan capi.sqlite3_create_function.udfConvertArgs = 485*ef9cd12eSstephan capi.sqlite3_create_window_function.udfConvertArgs = __udfConvertArgs; 486510a9d1cSstephan 487510a9d1cSstephan /** 488510a9d1cSstephan A helper for UDFs implemented in JS and bound to WASM by the 489510a9d1cSstephan client. It expects to be a passed `(sqlite3_context*, Error)` 490510a9d1cSstephan (i.e. an exception object). And it sets the current UDF's 491510a9d1cSstephan result to sqlite3_result_error_nomem() or sqlite3_result_error(), 492510a9d1cSstephan depending on whether the 2nd argument is a 493510a9d1cSstephan sqlite3.WasmAllocError object or not. 494510a9d1cSstephan */ 495*ef9cd12eSstephan capi.sqlite3_create_function_v2.udfSetError = 496*ef9cd12eSstephan capi.sqlite3_create_function.udfSetError = 497*ef9cd12eSstephan capi.sqlite3_create_window_function.udfSetError = __udfSetError; 49892ede964Sstephan 49992ede964Sstephan }/*sqlite3_create_function_v2() and sqlite3_create_window_function() proxies*/; 5009892883eSstephan 501e67a0f40Sstephan if(1){/* Special-case handling of sqlite3_prepare_v2() and 502e67a0f40Sstephan sqlite3_prepare_v3() */ 503e67a0f40Sstephan /** 5043961b263Sstephan Scope-local holder of the two impls of sqlite3_prepare_v2/v3(). 5053961b263Sstephan */ 5063961b263Sstephan const __prepare = Object.create(null); 5073961b263Sstephan /** 5083961b263Sstephan This binding expects a JS string as its 2nd argument and 5093961b263Sstephan null as its final argument. In order to compile multiple 5103961b263Sstephan statements from a single string, the "full" impl (see 5113961b263Sstephan below) must be used. 5123961b263Sstephan */ 5133961b263Sstephan __prepare.basic = wasm.xWrap('sqlite3_prepare_v3', 5143961b263Sstephan "int", ["sqlite3*", "string", 515453af2f6Sstephan "int"/*ignored for this impl!*/, 5163961b263Sstephan "int", "**", 5173961b263Sstephan "**"/*MUST be 0 or null or undefined!*/]); 5183961b263Sstephan /** 5193961b263Sstephan Impl which requires that the 2nd argument be a pointer 5203961b263Sstephan to the SQL string, instead of being converted to a 5213961b263Sstephan string. This variant is necessary for cases where we 5223961b263Sstephan require a non-NULL value for the final argument 5233961b263Sstephan (exec()'ing multiple statements from one input 5243961b263Sstephan string). For simpler cases, where only the first 5253961b263Sstephan statement in the SQL string is required, the wrapper 5263961b263Sstephan named sqlite3_prepare_v2() is sufficient and easier to 5273961b263Sstephan use because it doesn't require dealing with pointers. 5283961b263Sstephan */ 5293961b263Sstephan __prepare.full = wasm.xWrap('sqlite3_prepare_v3', 5303961b263Sstephan "int", ["sqlite3*", "*", "int", "int", 5313961b263Sstephan "**", "**"]); 5323961b263Sstephan 5333961b263Sstephan /* Documented in the api object's initializer. */ 5343961b263Sstephan capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){ 53592ede964Sstephan if(f.length!==arguments.length){ 53692ede964Sstephan return __dbArgcMismatch(pDb,"sqlite3_prepare_v3",f.length); 537e67a0f40Sstephan } 538e67a0f40Sstephan const [xSql, xSqlLen] = __flexiString(sql, sqlLen); 539e67a0f40Sstephan switch(typeof xSql){ 540e67a0f40Sstephan case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null); 541e67a0f40Sstephan case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail); 5423961b263Sstephan default: 5433961b263Sstephan return util.sqlite3_wasm_db_error( 5443961b263Sstephan pDb, capi.SQLITE_MISUSE, 5453961b263Sstephan "Invalid SQL argument type for sqlite3_prepare_v2/v3()." 5463961b263Sstephan ); 5473961b263Sstephan } 5483961b263Sstephan }; 5493961b263Sstephan 550e67a0f40Sstephan /* Documented in the api object's initializer. */ 55192ede964Sstephan capi.sqlite3_prepare_v2 = function f(pDb, sql, sqlLen, ppStmt, pzTail){ 55292ede964Sstephan return (f.length===arguments.length) 553e67a0f40Sstephan ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail) 55492ede964Sstephan : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",f.length); 555e67a0f40Sstephan }; 556e67a0f40Sstephan }/*sqlite3_prepare_v2/v3()*/; 5573961b263Sstephan 55863e9ec2fSstephan if(1){// Extend wasm.pstack, now that the wasm utils are installed 55963e9ec2fSstephan /** 56063e9ec2fSstephan Allocates n chunks, each sz bytes, as a single memory block and 56163e9ec2fSstephan returns the addresses as an array of n element, each holding 56263e9ec2fSstephan the address of one chunk. 56363e9ec2fSstephan 56463e9ec2fSstephan Throws a WasmAllocError if allocation fails. 56563e9ec2fSstephan 56663e9ec2fSstephan Example: 56763e9ec2fSstephan 56863e9ec2fSstephan ``` 56963e9ec2fSstephan const [p1, p2, p3] = wasm.pstack.allocChunks(3,4); 57063e9ec2fSstephan ``` 57163e9ec2fSstephan */ 57263e9ec2fSstephan wasm.pstack.allocChunks = (n,sz)=>{ 57363e9ec2fSstephan const mem = wasm.pstack.alloc(n * sz); 57463e9ec2fSstephan const rc = []; 57563e9ec2fSstephan let i = 0, offset = 0; 57663e9ec2fSstephan for(; i < n; offset = (sz * ++i)){ 57763e9ec2fSstephan rc.push(mem + offset); 57863e9ec2fSstephan } 57963e9ec2fSstephan return rc; 58063e9ec2fSstephan }; 58163e9ec2fSstephan 58263e9ec2fSstephan /** 58363e9ec2fSstephan A convenience wrapper for allocChunks() which sizes each chunks 58463e9ec2fSstephan as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if 5859fdce595Sstephan safePtrSize is falsy). 58663e9ec2fSstephan 58763e9ec2fSstephan How it returns its result differs depending on its first 58863e9ec2fSstephan argument: if it's 1, it returns a single pointer value. If it's 58963e9ec2fSstephan more than 1, it returns the same as allocChunks(). 59063e9ec2fSstephan 5919fdce595Sstephan When a returned pointers will refer to a 64-bit value, e.g. a 5929fdce595Sstephan double or int64, and that value must be written or fetched, 59363e9ec2fSstephan e.g. using wasm.setMemValue() or wasm.getMemValue(), it is 59463e9ec2fSstephan important that the pointer in question be aligned to an 8-byte 59563e9ec2fSstephan boundary or else it will not be fetched or written properly and 59663e9ec2fSstephan will corrupt or read neighboring memory. 59763e9ec2fSstephan 5989fdce595Sstephan However, when all pointers involved point to "small" data, it 5999fdce595Sstephan is safe to pass a falsy value to save to memory. 60063e9ec2fSstephan */ 60163e9ec2fSstephan wasm.pstack.allocPtr = (n=1,safePtrSize=true) =>{ 60263e9ec2fSstephan return 1===n 60363e9ec2fSstephan ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) 60463e9ec2fSstephan : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); 60563e9ec2fSstephan }; 60663e9ec2fSstephan }/*wasm.pstack filler*/ 60763e9ec2fSstephan 6083961b263Sstephan /** 6093961b263Sstephan Install JS<->C struct bindings for the non-opaque struct types we 6103961b263Sstephan need... */ 6113961b263Sstephan sqlite3.StructBinder = self.Jaccwabyt({ 6123961b263Sstephan heap: 0 ? wasm.memory : wasm.heap8u, 6133961b263Sstephan alloc: wasm.alloc, 6143961b263Sstephan dealloc: wasm.dealloc, 6153961b263Sstephan functionTable: wasm.functionTable, 6163961b263Sstephan bigIntEnabled: wasm.bigIntEnabled, 6173961b263Sstephan memberPrefix: '$' 6183961b263Sstephan }); 6193961b263Sstephan delete self.Jaccwabyt; 6203961b263Sstephan 6213961b263Sstephan {/* Import C-level constants and structs... */ 6223961b263Sstephan const cJson = wasm.xCall('sqlite3_wasm_enum_json'); 6233961b263Sstephan if(!cJson){ 6243961b263Sstephan toss("Maintenance required: increase sqlite3_wasm_enum_json()'s", 6253961b263Sstephan "static buffer size!"); 6263961b263Sstephan } 6273961b263Sstephan wasm.ctype = JSON.parse(wasm.cstringToJs(cJson)); 6283961b263Sstephan //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); 6293961b263Sstephan for(const t of ['access', 'blobFinalizers', 'dataTypes', 6305b915007Sstephan 'encodings', 'fcntl', 'flock', 'ioCap', 6313961b263Sstephan 'openFlags', 'prepareFlags', 'resultCodes', 63253d4e01dSstephan 'serialize', 'syncFlags', 'udfFlags', 63353d4e01dSstephan 'version' 6343961b263Sstephan ]){ 635f75c0c70Sstephan for(const e of Object.entries(wasm.ctype[t])){ 636f75c0c70Sstephan // ^^^ [k,v] there triggers a buggy code transormation via one 637f75c0c70Sstephan // of the Emscripten-driven optimizers. 638f75c0c70Sstephan capi[e[0]] = e[1]; 6393961b263Sstephan } 6403961b263Sstephan } 6411f095d48Sstephan const __rcMap = Object.create(null); 6421f095d48Sstephan for(const t of ['resultCodes']){ 6431f095d48Sstephan for(const e of Object.entries(wasm.ctype[t])){ 6441f095d48Sstephan __rcMap[e[1]] = e[0]; 6451f095d48Sstephan } 6461f095d48Sstephan } 6471f095d48Sstephan /** 6481f095d48Sstephan For the given integer, returns the SQLITE_xxx result code as a 6491f095d48Sstephan string, or undefined if no such mapping is found. 6501f095d48Sstephan */ 65132781427Sstephan capi.sqlite3_web_rc_str = (rc)=>__rcMap[rc]; 6523961b263Sstephan /* Bind all registered C-side structs... */ 6533961b263Sstephan for(const s of wasm.ctype.structs){ 6543961b263Sstephan capi[s.name] = sqlite3.StructBinder(s); 6553961b263Sstephan } 6561f095d48Sstephan }/*end C constant imports*/ 657d18f1bbfSstephan 658d18f1bbfSstephan sqlite3.version = Object.assign(Object.create(null),{ 659d18f1bbfSstephan library: sqlite3.capi.sqlite3_libversion(), 660d18f1bbfSstephan sourceId: sqlite3.capi.sqlite3_sourceid() 661d18f1bbfSstephan }); 662e3cd6760Sstephan}); 663e67a0f40Sstephan 664