if(!originalInit){
throw new Error("Expecting self.sqlite3InitModule to be defined by the Emscripten build.");
}
- self.sqlite3InitModule.ready = originalInit.ready;
self.sqlite3InitModule = (...args)=>{
//console.warn("Using replaced sqlite3InitModule()",self.location);
return originalInit(...args).then((EmscriptenModule)=>{
const f = EmscriptenModule.sqlite3.asyncPostInit;
delete EmscriptenModule.sqlite3.asyncPostInit;
return f();
+ }).catch((e)=>{
+ console.error("Exception loading sqlite3 module:",e);
+ throw e;
});
};
+ self.sqlite3InitModule.ready = originalInit.ready;
//console.warn("Replaced sqlite3InitModule()");
})();
let sqlite3;
try{
sqlite3 = self.sqlite3ApiBootstrap();
+ }catch(e){
+ console.error("sqlite3ApiBootstrap() error:",e);
+ throw e;
}finally{
delete self.sqlite3ApiBootstrap;
if(rmApiConfig) delete self.sqlite3ApiConfig;
to a C-style sqlite3_xxx function which takes an `sqlite3*`
argument.
*/
- const oldP = wasm.xWrap.argAdapter('pointer');
+ const xPointer = wasm.xWrap.argAdapter('pointer');
const adapter = function(v){
- if(v && 'object'===typeof v && v.constructor){
+ if(v && v.constructor){
const x = v.pointer;
if(Number.isInteger(x)) return x;
- else toss("Invalid (object) type for pointer-type argument.");
+ else toss("Invalid (object) type for .pointer-type argument.");
}
- return oldP(v);
+ return xPointer(v);
};
wasm.xWrap.argAdapter('.pointer', adapter);
} /* ".pointer" xWrap() argument adapter */
- // WhWasmUtil.xWrap() bindings...
- {
+ if(1){/* Convert Arrays and certain TypedArrays to strings for
+ 'flexible-string'-type arguments */
+ const xString = wasm.xWrap.argAdapter('string');
+ wasm.xWrap.argAdapter(
+ 'flexible-string', (v)=>xString(util.arrayToString(v))
+ );
+ }
+
+ if(1){// WhWasmUtil.xWrap() bindings...
/**
Add some descriptive xWrap() aliases for '*' intended to
(A) initially improve readability/correctness of capi.signatures
}/*xWrap() bindings*/;
/**
- Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
+ Internal helper to assist in validating call argument counts in
+ the hand-written sqlite3_xyz() wrappers. We do this only for
+ consistency with non-special-case wrappings.
*/
- const __prepare = Object.create(null);
- /**
- This binding expects a JS string as its 2nd argument and
- null as its final argument. In order to compile multiple
- statements from a single string, the "full" impl (see
- below) must be used.
- */
- __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
- "int", ["sqlite3*", "string",
- "int"/*ignored for this impl!*/,
- "int", "**",
- "**"/*MUST be 0 or null or undefined!*/]);
+ const __dbArgcMismatch = (pDb,f,n)=>{
+ return sqlite3.util.sqlite3_wasm_db_error(pDb, capi.SQLITE_MISUSE,
+ f+"() requires "+n+" argument"+
+ (1===n?'':'s')+".");
+ };
+
/**
- Impl which requires that the 2nd argument be a pointer
- to the SQL string, instead of being converted to a
- string. This variant is necessary for cases where we
- require a non-NULL value for the final argument
- (exec()'ing multiple statements from one input
- string). For simpler cases, where only the first
- statement in the SQL string is required, the wrapper
- named sqlite3_prepare_v2() is sufficient and easier to
- use because it doesn't require dealing with pointers.
+ Helper for flexible-string conversions which require a
+ byte-length counterpart argument. Passed a value and its
+ ostensible length, this function returns [V,N], where V
+ is either v or a transformed copy of v and N is either n,
+ -1, or the byte length of v (if it's a byte array).
*/
- __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
- "int", ["sqlite3*", "*", "int", "int",
- "**", "**"]);
-
- /* Documented in the api object's initializer. */
- capi.sqlite3_prepare_v3 = function f(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail){
- if(util.isSQLableTypedArray(sql)) sql = util.typedArrayToString(sql);
- switch(typeof sql){
- case 'string': return __prepare.basic(pDb, sql, -1, prepFlags, ppStmt, null);
- case 'number': return __prepare.full(pDb, sql, sqlLen, prepFlags, ppStmt, pzTail);
- default:
- return util.sqlite3_wasm_db_error(
- pDb, capi.SQLITE_MISUSE,
- "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
- );
+ const __flexiString = function(v,n){
+ if('string'===typeof v){
+ n = -1;
+ }else if(util.isSQLableTypedArray(v)){
+ n = v.byteLength;
+ v = util.typedArrayToString(v);
+ }else if(Array.isArray(v)){
+ v = v.join('');
+ n = -1;
}
+ return [v, n];
};
- capi.sqlite3_prepare_v2 =
- (pDb, sql, sqlLen, ppStmt, pzTail)=>capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail);
+ if(1){/* Special-case handling of sqlite3_exec() */
+ 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);
+ }else if('function' !== typeof callback){
+ return __exec(pDb, sql, callback, pVoid, pErrMsg);
+ }
+ /* Wrap the callback in a WASM-bound function and convert the callback's
+ `(char**)` arguments to arrays of strings... */
+ const wasm = capi.wasm;
+ const cbwrap = function(pVoid, nCols, pColVals, pColNames){
+ let rc = capi.SQLITE_ERROR;
+ try {
+ let aVals = [], aNames = [], i = 0, offset = 0;
+ for( ; i < nCols; offset += (wasm.ptrSizeof * ++i) ){
+ aVals.push( wasm.cstringToJs(wasm.getPtrValue(pColVals + offset)) );
+ aNames.push( wasm.cstringToJs(wasm.getPtrValue(pColNames + offset)) );
+ }
+ rc = callback(pVoid, nCols, aVals, aNames) | 0;
+ /* The first 2 args of the callback are useless for JS but
+ we want the JS mapping of the C API to be as close to the
+ C API as possible. */
+ }catch(e){
+ /* If we set the db error state here, the higher-level exec() call
+ replaces it with its own, so we have no way of reporting the
+ exception message except the console. We must not propagate
+ exceptions through the C API. */
+ }
+ return rc;
+ };
+ let pFunc, rc;
+ try{
+ pFunc = wasm.installFunction("ipipp", cbwrap);
+ rc = __exec(pDb, sql, pFunc, pVoid, pErrMsg);
+ }catch(e){
+ rc = util.sqlite3_wasm_db_error(pDb, capi.SQLITE_ERROR,
+ "Error running exec(): "+e.message);
+ }finally{
+ if(pFunc) wasm.uninstallFunction(pFunc);
+ }
+ return rc;
+ };
+ }/*sqlite3_exec() proxy*/;
+
+ if(1){/* Special-case handling of sqlite3_prepare_v2() and
+ sqlite3_prepare_v3() */
+ /**
+ Scope-local holder of the two impls of sqlite3_prepare_v2/v3().
+ */
+ const __prepare = Object.create(null);
+ /**
+ This binding expects a JS string as its 2nd argument and
+ null as its final argument. In order to compile multiple
+ statements from a single string, the "full" impl (see
+ below) must be used.
+ */
+ __prepare.basic = wasm.xWrap('sqlite3_prepare_v3',
+ "int", ["sqlite3*", "string",
+ "int"/*ignored for this impl!*/,
+ "int", "**",
+ "**"/*MUST be 0 or null or undefined!*/]);
+ /**
+ Impl which requires that the 2nd argument be a pointer
+ to the SQL string, instead of being converted to a
+ string. This variant is necessary for cases where we
+ require a non-NULL value for the final argument
+ (exec()'ing multiple statements from one input
+ string). For simpler cases, where only the first
+ statement in the SQL string is required, the wrapper
+ named sqlite3_prepare_v2() is sufficient and easier to
+ use because it doesn't require dealing with pointers.
+ */
+ __prepare.full = wasm.xWrap('sqlite3_prepare_v3',
+ "int", ["sqlite3*", "*", "int", "int",
+ "**", "**"]);
+
+ /* 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);
+ }
+ const [xSql, xSqlLen] = __flexiString(sql, sqlLen);
+ switch(typeof xSql){
+ case 'string': return __prepare.basic(pDb, xSql, xSqlLen, prepFlags, ppStmt, null);
+ case 'number': return __prepare.full(pDb, xSql, xSqlLen, prepFlags, ppStmt, pzTail);
+ default:
+ return util.sqlite3_wasm_db_error(
+ pDb, capi.SQLITE_MISUSE,
+ "Invalid SQL argument type for sqlite3_prepare_v2/v3()."
+ );
+ }
+ };
+
+ /* Documented in the api object's initializer. */
+ capi.sqlite3_prepare_v2 = function(pDb, sql, sqlLen, ppStmt, pzTail){
+ return (5==arguments.length)
+ ? capi.sqlite3_prepare_v3(pDb, sql, sqlLen, 0, ppStmt, pzTail)
+ : __dbArgcMismatch(pDb,"sqlite3_prepare_v2",5);
+ };
+ }/*sqlite3_prepare_v2/v3()*/;
/**
Install JS<->C struct bindings for the non-opaque struct types we
sourceId: sqlite3.capi.sqlite3_sourceid()
});
});
+
);
};
+ /**
+ If v is-a Array, its join('') result is returned. If
+ isSQLableTypedArray(v) then typedArrayToString(v) is
+ returned. Else v is returned as-is.
+ */
+ const arrayToString = function(v){
+ if(isSQLableTypedArray(v)) return typedArrayToString(v);
+ else if(Array.isArray(v)) return v.join('');
+ return v;
+ };
+
/**
An Error subclass specifically for reporting Wasm-level malloc()
failure and enabling clients to unambiguously identify such
comments or an empty SQL expression, 0 is returned but the result
output pointer will be NULL.
*/
- sqlite3_prepare_v3: function(dbPtr, sql, sqlByteLen, prepFlags,
- stmtPtrPtr, strPtrPtr){}/*installed later*/,
+ sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags,
+ stmtPtrPtr, strPtrPtr)=>{}/*installed later*/,
/**
Equivalent to calling sqlite3_prapare_v3() with 0 as its 4th argument.
*/
- sqlite3_prepare_v2: function(dbPtr, sql, sqlByteLen, stmtPtrPtr,
- strPtrPtr){}/*installed later*/,
+ sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen,
+ stmtPtrPtr,strPtrPtr)=>{}/*installed later*/,
+ /**
+ This binding enables the callback argument to be a JavaScript.
+
+ If the callback is a function, then for the duration of the
+ sqlite3_exec() call, it installs a WASM-bound function which
+ acts as a proxy for the given callback. That proxy will
+ also perform a conversion of the callback's arguments from
+ `(char**)` to JS arrays of strings. However, for API
+ consistency's sake it will still honor the C-level
+ callback parameter order and will call it like:
+
+ `callback(pVoid, colCount, listOfValues, listOfColNames)`
+
+ If the callback is not a JS function then this binding performs
+ no translation of the callback, but the sql argument is still
+ converted to a WASM string for the call using the
+ "flexible-string" argument converter.
+ */
+ sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
/**
Various internal-use utilities are added here as needed. They
are bound to an object only so that we have access to them in
removed.
*/
util:{
- isInt32, isTypedArray, isBindableTypedArray, isSQLableTypedArray,
- affirmBindableTypedArray, typedArrayToString,
+ affirmBindableTypedArray, arrayToString, isBindableTypedArray,
+ isInt32, isSQLableTypedArray, isTypedArray,
+ typedArrayToString,
isMainWindow: ()=>{
return self.window===self && self.document;
}
["sqlite3_errmsg", "string", "sqlite3*"],
["sqlite3_error_offset", "int", "sqlite3*"],
["sqlite3_errstr", "string", "int"],
- ["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"],
- // TODO?: ^^^ add a wrapper around sqlite3_exec() which accepts a
- // JS callback function and handles the (un)installation of that
- // function before/after the exec call.
+ /*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"
+ Handled seperately to perform translation of the callback
+ into a WASM-usable one. ],*/
["sqlite3_expanded_sql", "string", "sqlite3_stmt*"],
["sqlite3_extended_errcode", "int", "sqlite3*"],
["sqlite3_extended_result_codes", "int", "sqlite3*", "int"],
// Custom db error message handling via sqlite3_prepare_v2/v3()
if(capi.wasm.exports.sqlite3_wasm_db_error){
log("Testing custom error message via prepare_v3()...");
- let rc = capi.sqlite3_prepare_v3(db.pointer, [/*invalid*/], -1, 0, null, null);
+ let rc = capi.sqlite3_prepare_v3(db.pointer, {/*invalid*/}, -1, 0, null, null);
T.assert(capi.SQLITE_MISUSE === rc)
.assert(0 === capi.sqlite3_errmsg(db.pointer).indexOf("Invalid SQL"));
log("errmsg =",capi.sqlite3_errmsg(db.pointer));
const resultRows = [];
db.exec({
sql:new TextEncoder('utf-8').encode([
- // ^^^ testing string-vs-typedarray handling in execMulti()
+ // ^^^ testing string-vs-typedarray handling in exec()
"attach 'session' as foo;" /* name 'session' is magic for kvvfs! */,
"create table foo.bar(a);",
"insert into foo.bar(a) values(1),(2),(3);",
T.assert(3===resultRows.length)
.assert(2===resultRows[1]);
T.assert(2===db.selectValue('select a from foo.bar where a>1 order by a'));
+ let colCount = 0, rowCount = 0;
+ const execCallback = function(pVoid, nCols, aVals, aNames){
+ colCount = nCols;
+ ++rowCount;
+ T.assert(2===aVals.length)
+ .assert(2===aNames.length)
+ .assert(+(aVals[1]) === 2 * +(aVals[0]));
+ };
+ const capi = sqlite3.capi;
+ let rc = capi.sqlite3_exec(
+ db.pointer, "select a, a*2 from foo.bar", execCallback,
+ 0, 0
+ );
+ T.assert(0===rc).assert(3===rowCount).assert(2===colCount);
+ rc = capi.sqlite3_exec(
+ db.pointer, "select a from foo.bar", ()=>{
+ toss("Testing throwing from exec() callback.");
+ }, 0, 0
+ );
+ T.assert(capi.SQLITE_ABORT === rc);
db.exec("detach foo");
T.mustThrow(()=>db.exec("select * from foo.bar"));
};
runOneTest('close',{unlink:true},function(ev){
ev = ev.result;
T.assert(undefined === ev.filename);
+ logHtml('warning',"This is the final test.");
});
+ logHtml('warning',"Finished posting tests. Waiting on async results.");
};
const runTests = function(){
-C Add\ssqlite3.version\sobject.\sAdd\smore\sstate\sto\sthe\sWorker\s#1\sconfig-get\sresponse,\sincluding\ssqlite3.version.
-D 2022-09-30T16:49:03.804
+C Add\sJS\swrapper\sfor\ssqlite3_exec()\swhich\sknows\show\sto\shandle\sa\sJS\scallback.\sAdd\ssome\sconsole.error()\sreporting\sof\smodule-load\sfailures,\sas\sthey\sotherwise\soften\sget\ssilently\sswallowed\sup\sby\sthe\sloader's\smechanisms.\sAdd\s'flexible-string'\sJS-to-WASM\sargument\sconverter\swhich\sperforms\smore\sX-to-string\sconversions\sthan\sthe\s'string'\sarg\sconverter\sdoes.
+D 2022-09-30T20:35:37.991
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api c6b05641d733227a995cc05a2ba7ddc9ef2323081ae0cae5ed1d1f83d5b54b36
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md f54102d74cfde01ebe242fa1411e126a9cda8f19b3ac378afd1103b21abfad05
-F ext/wasm/api/extern-post-js.js b0df294159c290bec06cd67cce1a882d61944959ffe66a2f4ccbcb337e357781
+F ext/wasm/api/extern-post-js.js dc68cbf552d8ea085181400a6963907c32e0b088b03ffd8969b1869fea246629
F ext/wasm/api/extern-pre-js.js 20143b16b672d0a576fbf768a786d12ee1f84e222126477072389b992542a5b2
F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
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 98905936119a555659b5cf43844211809ab9f436c52a569004e5585d2842b5c2
-F ext/wasm/api/sqlite3-api-glue.js 90206acd5cf556bd8a7a9275619aa680c9075ad8072ed1e18f47fc1d5cfbcf26
+F ext/wasm/api/sqlite3-api-cleanup.js 5d22d1d3818ecacb23bfa223d5970cd0617d8cdbb48c8bc4bbd463f05b021a99
+F ext/wasm/api/sqlite3-api-glue.js ead29e6008ba148e7c67ad2bd928819dc72313ad2dcd34510cc61e089ee01c4e
F ext/wasm/api/sqlite3-api-oo1.js 9caed0757a5e039ed92467e827fd3ca347fa08f19fe086fcbdd14a4ebe9c2f01
F ext/wasm/api/sqlite3-api-opfs.js 1b097808b7b081b0f0700cf97d49ef19760e401706168edff9cd45cf9169f541
-F ext/wasm/api/sqlite3-api-prologue.js 3a93497a9bc542c7202f32063cd070aa9c4cd03171a5e354cf99dd4357585b7a
+F ext/wasm/api/sqlite3-api-prologue.js cac3bc095171dca4aaf3611e0dd60a850c8e9fbeeeba8f21792ed1948d24dacc
F ext/wasm/api/sqlite3-api-worker1.js 2e8a037a76d20c7653e5a392d9830f17a22ce6736ffd0597aa39b69d1c01ad2c
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
F ext/wasm/api/sqlite3-wasm.c 336389b23c9b83763177499e49a0967949c392b2f7d84fbbb52ad6678e159f18
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
F ext/wasm/testing-worker1-promiser.js ee1ac4950720c07541e791b7f35bdf420e8ab81a74049cc65394371b0c53c480
F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
-F ext/wasm/testing1.js 06b9a439ada636c5478c581c86b2b968f799e0468eae54dc5a4565dfd7afbb88
+F ext/wasm/testing1.js 5584e9b68f797dbc0f6161360f2c6840169533813d92c74d355a3e78dd5bb539
F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
-F ext/wasm/testing2.js 34737da985c4cbd4812b2e7f200942662cae991c5a58ffa5d0350be089d0d410
+F ext/wasm/testing2.js 88f40ef3cd8201bdadd120a711c36bbf0ce56cc0eab1d5e7debb71fed7822494
F ext/wasm/wasmfs.make 3cce1820006196de140f90f2da4b4ea657083fb5bfee7d125be43f7a85748c8f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 1e09efe7fa15b8908f8b8353164a8361de778e27ea6c0b11c402bf4e1c56333d
-R 4c776ad1a7aec6f0bcde0b14554ad6b2
+P 711f458d188a0dbe6612069c856ade29323ab426dfa4f80e7b82757ccc474cb8
+R 84d8e08f60a70faceba1089503bc1c19
U stephan
-Z 125edfcbc5b4725eac67cee184f7f0b1
+Z f9cd8bd536b2dd8409558133f7a063eb
# Remove this line to create a well-formed Fossil manifest.
-711f458d188a0dbe6612069c856ade29323ab426dfa4f80e7b82757ccc474cb8
\ No newline at end of file
+96818aa83f4ccc574f558231249ecbdd39763b4351cf4cf6d33f53774a3ee5e6
\ No newline at end of file