diff options
Diffstat (limited to 'ext/wasm/api/sqlite3-api-prologue.js')
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 92 |
1 files changed, 80 insertions, 12 deletions
diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index faa2dbbbf..5dd37d303 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -139,7 +139,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( main thread (aborts via a failed assert() if it's attempted), which eliminates any(?) benefit to supporting it. */ false; - /** + /** The main sqlite3 binding API gets installed into this object, mimicking the C API as closely as we can. The numerous members names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as @@ -321,7 +321,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; /** - Returns v if v appears to be one of our bind()-able TypedArray + Returns true if v appears to be one of our bind()-able TypedArray types: Uint8Array or Int8Array or ArrayBuffer. Support for TypedArrays with element sizes >1 is a potential TODO just waiting on a use case to justify them. Until then, their `buffer` @@ -377,7 +377,11 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( returned. Else v is returned as-is. */ const flexibleString = function(v){ - if(isSQLableTypedArray(v)) return typedArrayToString(v); + if(isSQLableTypedArray(v)){ + return typedArrayToString( + (v instanceof ArrayBuffer) ? new Uint8Array(v) : v + ); + } else if(Array.isArray(v)) return v.join(""); else if(wasm.isPtr(v)) v = wasm.cstrToJs(v); return v; @@ -405,6 +409,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }else{ super("Allocation failed."); } + this.resultCode = capi.SQLITE_NOMEM; this.name = 'WasmAllocError'; } }; @@ -657,8 +662,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( terminated with a 0 byte. In usage (1), the 2nd argument must be of type string, - Uint8Array, or Int8Array (either of which is assumed to - hold SQL). If it is, this function assumes case (1) and + Uint8Array, Int8Array, or ArrayBuffer (all of which are assumed + to hold SQL). If it is, this function assumes case (1) and calls the underyling C function with the equivalent of: (pDb, sqlAsString, -1, prepFlags, ppStmt, null) @@ -750,7 +755,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( affirmBindableTypedArray, flexibleString, bigIntFits32, bigIntFits64, bigIntFitsDouble, isBindableTypedArray, - isInt32, isSQLableTypedArray, isTypedArray, + isInt32, isSQLableTypedArray, isTypedArray, typedArrayToString, isUIThread: ()=>(self.window===self && !!self.document), // is this true for ESM?: 'undefined'===typeof WorkerGlobalScope @@ -948,7 +953,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( const m = f._rx.exec(opt); rv[0] = (m ? m[1] : opt); rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; - }; + }; } const rc = {}, ov = [0,0]; let i = 0, k; @@ -1019,12 +1024,20 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( to the memory. On error, returns throws a WasmAllocError. The memory must eventually be released using restore(). + If n is a string, it must be a WASM "IR" value in the set + accepted by wasm.sizeofIR(), which is mapped to the size of + that data type. If passed a string not in that set, it throws a + WasmAllocError. + This method always adjusts the given value to be a multiple of 8 bytes because failing to do so can lead to incorrect results when reading and writing 64-bit values from/to the WASM heap. Similarly, the returned address is always 8-byte aligned. */ - alloc: (n)=>{ + alloc: function(n){ + if('string'===typeof n && !(n = wasm.sizeofIR(n))){ + WasmAllocError.toss("Invalid value for pstack.alloc(",arguments[0],")"); + } return wasm.exports.sqlite3_wasm_pstack_alloc(n) || WasmAllocError.toss("Could not allocate",n, "bytes from the pstack."); @@ -1034,6 +1047,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( returns the addresses as an array of n element, each holding the address of one chunk. + sz may optionally be an IR string accepted by wasm.sizeofIR(). + Throws a WasmAllocError if allocation fails. Example: @@ -1042,7 +1057,10 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( const [p1, p2, p3] = wasm.pstack.allocChunks(3,4); ``` */ - allocChunks: (n,sz)=>{ + allocChunks: function(n,sz){ + if('string'===typeof sz && !(sz = wasm.sizeofIR(sz))){ + WasmAllocError.toss("Invalid size value for allocChunks(",arguments[1],")"); + } const mem = wasm.pstack.alloc(n * sz); const rc = []; let i = 0, offset = 0; @@ -1418,7 +1436,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( wasm.dealloc(pData); } }; - + if( util.isUIThread() ){ /* Features specific to the main window thread... */ @@ -1749,8 +1767,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( argument of sqlite3_value_to_js(). If the sqlite3_column_value() returns NULL (e.g. because the column index is out of range), this function returns `undefined`, regardless of the 3rd - argument. 3rd argument is falsy and conversion fails, `undefined` - will be returned. + argument. If the 3rd argument is falsy and conversion fails, + `undefined` will be returned. Note that sqlite3_column_value() returns an "unprotected" value object, but in a single-threaded environment (like this one) @@ -1761,6 +1779,56 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return (0===v) ? undefined : capi.sqlite3_value_to_js(v, throwIfCannotConvert); }; + /** + Internal impl of sqlite3_preupdate_new/old_js() and + sqlite3changeset_new/old_js(). + */ + const __newOldValue = function(pObj, iCol, impl){ + impl = capi[impl]; + if(!this.ptr) this.ptr = wasm.allocPtr(); + else wasm.pokePtr(this.ptr, 0); + const rc = impl(pObj, iCol, this.ptr); + if(rc) return SQLite3Error.toss(rc,arguments[2]+"() failed with code "+rc); + const pv = wasm.peekPtr(this.ptr); + return pv ? capi.sqlite3_value_to_js( pv, true ) : undefined; + }.bind(Object.create(null)); + + /** + A wrapper around sqlite3_preupdate_new() which fetches the + sqlite3_value at the given index and returns the result of + passing it to sqlite3_value_to_js(). Throws on error. + */ + capi.sqlite3_preupdate_new_js = + (pDb, iCol)=>__newOldValue(pDb, iCol, 'sqlite3_preupdate_new'); + + /** + The sqlite3_preupdate_old() counterpart of + sqlite3_preupdate_new_js(), with an identical interface. + */ + capi.sqlite3_preupdate_old_js = + (pDb, iCol)=>__newOldValue(pDb, iCol, 'sqlite3_preupdate_old'); + + /** + A wrapper around sqlite3changeset_new() which fetches the + sqlite3_value at the given index and returns the result of + passing it to sqlite3_value_to_js(). Throws on error. + + If sqlite3changeset_new() succeeds but has no value to report, + this function returns the undefined value, noting that undefined + is a valid conversion from an `sqlite3_value`, so is unambiguous. + */ + capi.sqlite3changeset_new_js = + (pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol, + 'sqlite3changeset_new'); + + /** + The sqlite3changeset_old() counterpart of + sqlite3changeset_new_js(), with an identical interface. + */ + capi.sqlite3changeset_old_js = + (pChangesetIter, iCol)=>__newOldValue(pChangesetIter, iCol, + 'sqlite3changeset_old'); + /* The remainder of the API will be set up in later steps. */ const sqlite3 = { WasmAllocError: WasmAllocError, |