aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/api/sqlite3-api-glue.js
diff options
context:
space:
mode:
authorstephan <stephan@noemail.net>2022-09-30 20:35:37 +0000
committerstephan <stephan@noemail.net>2022-09-30 20:35:37 +0000
commite67a0f40e419f4945d8157cb047ae302bf1f2852 (patch)
treea31e7863c9e6867e74ce53be5545eef1e5e02c9a /ext/wasm/api/sqlite3-api-glue.js
parentd18f1bbfe06e40713de908cd894d9857f6c11606 (diff)
downloadsqlite-e67a0f40e419f4945d8157cb047ae302bf1f2852.tar.gz
sqlite-e67a0f40e419f4945d8157cb047ae302bf1f2852.zip
Add JS wrapper for sqlite3_exec() which knows how to handle a JS callback. Add some console.error() reporting of module-load failures, as they otherwise often get silently swallowed up by the loader's mechanisms. Add 'flexible-string' JS-to-WASM argument converter which performs more X-to-string conversions than the 'string' arg converter does.
FossilOrigin-Name: 96818aa83f4ccc574f558231249ecbdd39763b4351cf4cf6d33f53774a3ee5e6
Diffstat (limited to 'ext/wasm/api/sqlite3-api-glue.js')
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js186
1 files changed, 141 insertions, 45 deletions
diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js
index 2082f427b..347c03bc4 100644
--- a/ext/wasm/api/sqlite3-api-glue.js
+++ b/ext/wasm/api/sqlite3-api-glue.js
@@ -33,20 +33,27 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
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
@@ -107,51 +114,139 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}/*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
@@ -208,3 +303,4 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
sourceId: sqlite3.capi.sqlite3_sourceid()
});
});
+