diff options
author | dan <Dan Kennedy> | 2022-12-05 18:26:37 +0000 |
---|---|---|
committer | dan <Dan Kennedy> | 2022-12-05 18:26:37 +0000 |
commit | 77e171e8fa536f47f7a5108d7b8b5d88fc01ae05 (patch) | |
tree | 34037f08500bc7bc8f1b5b2879043593096de692 /ext/wasm/api | |
parent | a3d0c158a0e5942a2cfbfa05b1c1a629ed230ed0 (diff) | |
parent | 49d402684b86e0f49264b5fbbe1d0ca2e7f64b93 (diff) | |
download | sqlite-77e171e8fa536f47f7a5108d7b8b5d88fc01ae05.tar.gz sqlite-77e171e8fa536f47f7a5108d7b8b5d88fc01ae05.zip |
Merge latest trunk changes.
FossilOrigin-Name: 1a72777b1279f74f212fb2f675a4594a238e5d28f048879d7f5ad5287673c3c4
Diffstat (limited to 'ext/wasm/api')
-rw-r--r-- | ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api | 3 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-glue.js | 1 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-oo1.js | 41 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-prologue.js | 136 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-vfs-opfs.c-pp.js | 39 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-wasm.c | 12 |
6 files changed, 156 insertions, 76 deletions
diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 1f7908e3b..096bf4401 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -66,6 +66,8 @@ _sqlite3_result_int _sqlite3_result_int64 _sqlite3_result_null _sqlite3_result_text +_sqlite3_result_zeroblob +_sqlite3_result_zeroblob64 _sqlite3_serialize _sqlite3_shutdown _sqlite3_sourceid @@ -93,3 +95,4 @@ _sqlite3_vfs_register _sqlite3_vfs_unregister _malloc _free +_realloc diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index e60baeb7f..ac1e9fd6d 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -605,6 +605,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ //console.debug('wasm.ctype length =',wasm.cstrlen(cJson)); for(const t of ['access', 'blobFinalizers', 'dataTypes', 'encodings', 'fcntl', 'flock', 'ioCap', + 'limits', 'openFlags', 'prepareFlags', 'resultCodes', 'serialize', 'syncFlags', 'trace', 'udfFlags', 'version' diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index 45c2ba913..e077b0c50 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -78,8 +78,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }.bind({counter: 0})); /** - A map of sqlite3_vfs pointers to SQL code to run when the DB - constructor opens a database with the given VFS. + A map of sqlite3_vfs pointers to SQL code or a callback function + to run when the DB constructor opens a database with the given + VFS. In the latter case, the call signature is (theDbObject,sqlite3Namespace) + and the callback is expected to throw on error. */ const __vfsPostOpenSql = Object.create(null); @@ -160,15 +162,6 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT, __dbTraceToConsole, 0); } - // Check for per-VFS post-open SQL... - const pVfs = capi.sqlite3_js_db_vfs(pDb); - //console.warn("Opened db",fn,"with vfs",vfsName,pVfs); - if(!pVfs) toss3("Internal error: cannot get VFS for new db handle."); - const postInitSql = __vfsPostOpenSql[pVfs]; - if(postInitSql){ - rc = capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0); - checkSqlite3Rc(pDb, rc); - } }catch( e ){ if( pDb ) capi.sqlite3_close_v2(pDb); throw e; @@ -178,12 +171,34 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ this.filename = fnJs; __ptrMap.set(this, pDb); __stmtMap.set(this, Object.create(null)); + try{ + // Check for per-VFS post-open SQL/callback... + const pVfs = capi.sqlite3_js_db_vfs(pDb); + if(!pVfs) toss3("Internal error: cannot get VFS for new db handle."); + const postInitSql = __vfsPostOpenSql[pVfs]; + if(postInitSql instanceof Function){ + postInitSql(this, sqlite3); + }else if(postInitSql){ + checkSqlite3Rc( + pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0) + ); + } + }catch(e){ + this.close(); + throw e; + } }; /** Sets SQL which should be exec()'d on a DB instance after it is - opened with the given VFS pointer. This is intended only for use - by DB subclasses or sqlite3_vfs implementations. + opened with the given VFS pointer. The SQL may be any type + supported by the "flexible-string" function argument + conversion. Alternately, the 2nd argument may be a function, in + which case it is called with (theOo1DbObject,sqlite3Namespace) at + the end of the DB() constructor. The function must throw on + error, in which case the db is closed and the exception is + propagated. This function is intended only for use by DB + subclasses or sqlite3_vfs implementations. */ dbCtorHelper.setVfsPostOpenSql = function(pVfs, sql){ __vfsPostOpenSql[pVfs] = sql; diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index a99065663..5ebe7af05 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -75,6 +75,10 @@ the `free(3)`-compatible routine for the WASM environment. Defaults to `"sqlite3_free"`. + - `reallocExportName`: the name of the function, in `exports`, of + the `realloc(3)`-compatible routine for the WASM + environment. Defaults to `"sqlite3_realloc"`. + - `wasmfsOpfsDir`[^1]: if the environment supports persistent storage using OPFS-over-WASMFS , this directory names the "mount point" for that directory. It must be prefixed by `/` and may @@ -108,11 +112,23 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } return !!self.BigInt64Array; })(), - allocExportName: 'sqlite3_malloc', - deallocExportName: 'sqlite3_free', - wasmfsOpfsDir: '/opfs' + wasmfsOpfsDir: '/opfs', + /** + useStdAlloc is just for testing an allocator discrepancy. The + docs guarantee that this is false in the canonical builds. For + 99% of purposes it doesn't matter which allocators we use, but + it becomes significant with, e.g., sqlite3_deserialize() + and certain wasm.xWrap.resultAdapter()s. + */ + useStdAlloc: false }, apiConfig || {}); + Object.assign(config, { + allocExportName: config.useStdAlloc ? 'malloc' : 'sqlite3_malloc', + deallocExportName: config.useStdAlloc ? 'free' : 'sqlite3_free', + reallocExportName: config.useStdAlloc ? 'realloc' : 'sqlite3_realloc' + }, config); + [ // If any of these config options are functions, replace them with // the result of calling that function... @@ -284,12 +300,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( }; /** - Returns true if v appears to be one of our bind()-able - TypedArray types: Uint8Array or Int8Array. Support for - TypedArrays with element sizes >1 is TODO. + Returns true if v appears to be one of our bind()-able TypedArray + types: Uint8Array or Int8Array. Support for TypedArrays with + element sizes >1 is a potential TODO just waiting on a use case + to justify them. */ const isBindableTypedArray = (v)=>{ - return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); + return v && (v instanceof Uint8Array || v instanceof Int8Array); + //v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); }; /** @@ -302,7 +320,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( isSQLableTypedArray() list. */ const isSQLableTypedArray = (v)=>{ - return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); + return v && (v instanceof Uint8Array || v instanceof Int8Array); + //v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); }; /** Returns true if isBindableTypedArray(v) does, else throws with a message @@ -664,12 +683,12 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( "or config.memory (imported)."), /** - The API's one single point of access to the WASM-side memory - allocator. Works like malloc(3) (and is likely bound to - malloc()) but throws an WasmAllocError if allocation fails. It is - important that any code which might pass through the sqlite3 C - API NOT throw and must instead return SQLITE_NOMEM (or - equivalent, depending on the context). + The API's primary point of access to the WASM-side memory + allocator. Works like sqlite3_malloc() but throws a + WasmAllocError if allocation fails. It is important that any + code which might pass through the sqlite3 C API NOT throw and + must instead return SQLITE_NOMEM (or equivalent, depending on + the context). Very few cases in the sqlite3 JS APIs can result in client-defined functions propagating exceptions via the C-style @@ -681,7 +700,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( catch exceptions and convert them to appropriate error codes. For cases where non-throwing allocation is required, use - sqlite3.wasm.alloc.impl(), which is direct binding of the + this.alloc.impl(), which is direct binding of the underlying C-level allocator. Design note: this function is not named "malloc" primarily @@ -692,9 +711,27 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( alloc: undefined/*installed later*/, /** - The API's one single point of access to the WASM-side memory - deallocator. Works like free(3) (and is likely bound to - free()). + Rarely necessary in JS code, this routine works like + sqlite3_realloc(M,N), where M is either NULL or a pointer + obtained from this function or this.alloc() and N is the number + of bytes to reallocate the block to. Returns a pointer to the + reallocated block or 0 if allocation fails. + + If M is NULL and N is positive, this behaves like + this.alloc(N). If N is 0, it behaves like this.dealloc(). + Results are undefined if N is negative (sqlite3_realloc() + treats that as 0, but if this code is built with a different + allocator it may misbehave with negative values). + + Like this.alloc.impl(), this.realloc.impl() is a direct binding + to the underlying realloc() implementation which does not throw + exceptions, instead returning 0 on allocation error. + */ + realloc: undefined/*installed later*/, + + /** + The API's primary point of access to the WASM-side memory + deallocator. Works like sqlite3_free(). Design note: this function is not named "free" for the same reason that this.alloc() is not called this.malloc(). @@ -711,18 +748,20 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( returned pointer must eventually be passed to wasm.dealloc() to clean it up. + The argument may be a Uint8Array, Int8Array, or ArrayBuffer, + and it throws if passed any other type. + As a special case, to avoid further special cases where this is used, if srcTypedArray.byteLength is 0, it allocates a single byte and sets it to the value 0. Even in such cases, calls must behave as if the allocated memory has exactly srcTypedArray.byteLength bytes. - - ACHTUNG: this currently only works for Uint8Array and - Int8Array types and will throw if srcTypedArray is of - any other type. */ wasm.allocFromTypedArray = function(srcTypedArray){ + if(srcTypedArray instanceof ArrayBuffer){ + srcTypedArray = new Uint8Array(srcTypedArray); + } affirmBindableTypedArray(srcTypedArray); const pRet = wasm.alloc(srcTypedArray.byteLength || 1); wasm.heapForSize(srcTypedArray.constructor).set( @@ -731,20 +770,27 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return pRet; }; - const keyAlloc = config.allocExportName, - keyDealloc = config.deallocExportName; - for(const key of [keyAlloc, keyDealloc]){ - const f = wasm.exports[key]; - if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function."); - } + { + // Set up allocators... + const keyAlloc = config.allocExportName, + keyDealloc = config.deallocExportName, + keyRealloc = config.reallocExportName; + for(const key of [keyAlloc, keyDealloc, keyRealloc]){ + const f = wasm.exports[key]; + if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function."); + } - wasm.alloc = function f(n){ - const m = f.impl(n); - if(!m) throw new WasmAllocError("Failed to allocate",n," bytes."); - return m; - }; - wasm.alloc.impl = wasm.exports[keyAlloc]; - wasm.dealloc = wasm.exports[keyDealloc]; + wasm.alloc = function f(n){ + return f.impl(n) || WasmAllocError.toss("Failed to allocate",n," bytes."); + }; + wasm.alloc.impl = wasm.exports[keyAlloc]; + wasm.realloc = function f(m,n){ + const m2 = f.impl(m,n); + return n ? (m2 || WasmAllocError.toss("Failed to reallocate",n," bytes.")) : 0; + }; + wasm.realloc.impl = wasm.exports[keyRealloc]; + wasm.dealloc = wasm.exports[keyDealloc]; + } /** Reports info about compile-time options using @@ -899,15 +945,16 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( the range of supported argument types. */ ["sqlite3_realloc", "*","*","int"], ["sqlite3_reset", "int", "sqlite3_stmt*"], - ["sqlite3_result_blob",undefined, "*", "*", "int", "*"], - ["sqlite3_result_double",undefined, "*", "f64"], - ["sqlite3_result_error",undefined, "*", "string", "int"], - ["sqlite3_result_error_code", undefined, "*", "int"], - ["sqlite3_result_error_nomem", undefined, "*"], - ["sqlite3_result_error_toobig", undefined, "*"], - ["sqlite3_result_int",undefined, "*", "int"], - ["sqlite3_result_null",undefined, "*"], - ["sqlite3_result_text",undefined, "*", "string", "int", "*"], + ["sqlite3_result_blob",undefined, "sqlite3_context*", "*", "int", "*"], + ["sqlite3_result_double",undefined, "sqlite3_context*", "f64"], + ["sqlite3_result_error",undefined, "sqlite3_context*", "string", "int"], + ["sqlite3_result_error_code", undefined, "sqlite3_context*", "int"], + ["sqlite3_result_error_nomem", undefined, "sqlite3_context*"], + ["sqlite3_result_error_toobig", undefined, "sqlite3_context*"], + ["sqlite3_result_int",undefined, "sqlite3_context*", "int"], + ["sqlite3_result_null",undefined, "sqlite3_context*"], + ["sqlite3_result_text",undefined, "sqlite3_context*", "string", "int", "*"], + ["sqlite3_result_zeroblob", undefined, "sqlite3_context*", "int"], ["sqlite3_serialize","*", "sqlite3*", "string", "*", "int"], ["sqlite3_shutdown", undefined], ["sqlite3_sourceid", "string"], @@ -955,6 +1002,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( ["sqlite3_msize", "i64", "*"], ["sqlite3_realloc64", "*","*", "i64"], ["sqlite3_result_int64",undefined, "*", "i64"], + ["sqlite3_result_zeroblob64", "int", "*", "i64"], ["sqlite3_total_changes64", "i64", ["sqlite3*"]], ["sqlite3_uri_int64", "i64", ["sqlite3_filename", "string", "i64"]], ["sqlite3_value_int64","i64", "sqlite3_value*"], diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js index 4dc145a61..f5a1eb6cc 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -1163,24 +1163,27 @@ const installOpfsVfs = function callee(options){ OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype); sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql( opfsVfs.pointer, - [ - /* Truncate journal mode is faster than delete for - this vfs, per speedtest1. That gap seems to have closed with - Chrome version 108 or 109, but "persist" is very roughly 5-6% - faster than truncate in initial tests. */ - "pragma journal_mode=persist;", - /* Set a default busy-timeout handler to help OPFS dbs - deal with multi-tab/multi-worker contention. */ - "pragma busy_timeout=5000;", - /* - This vfs benefits hugely from cache on moderate/large - speedtest1 --size 50 and --size 100 workloads. We currently - rely on setting a non-default cache size when building - sqlite3.wasm. If that policy changes, the cache can - be set here. - */ - //"pragma cache_size=-16384;" - ].join("") + function(oo1Db, sqlite3){ + /* Set a relatively high default busy-timeout handler to + help OPFS dbs deal with multi-tab/multi-worker + contention. */ + sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000); + sqlite3.capi.sqlite3_exec(oo1Db, [ + /* Truncate journal mode is faster than delete for + this vfs, per speedtest1. That gap seems to have closed with + Chrome version 108 or 109, but "persist" is very roughly 5-6% + faster than truncate in initial tests. */ + "pragma journal_mode=persist;", + /* + This vfs benefits hugely from cache on moderate/large + speedtest1 --size 50 and --size 100 workloads. We + currently rely on setting a non-default cache size when + building sqlite3.wasm. If that policy changes, the cache + can be set here. + */ + "pragma cache_size=-16384;" + ], 0, 0, 0); + } ); } diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 9acc8020e..f6499243a 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -113,6 +113,12 @@ #endif /**********************************************************************/ +/* SQLITE_M... */ +#ifndef SQLITE_MAX_ALLOCATION_SIZE +# define SQLITE_MAX_ALLOCATION_SIZE 0x1fffffff +#endif + +/**********************************************************************/ /* SQLITE_O... */ #ifndef SQLITE_OMIT_DEPRECATED # define SQLITE_OMIT_DEPRECATED 1 @@ -497,6 +503,10 @@ const char * sqlite3_wasm_enum_json(void){ DefInt(SQLITE_IOCAP_BATCH_ATOMIC); } _DefGroup; + DefGroup(limits) { + DefInt(SQLITE_MAX_ALLOCATION_SIZE); + } _DefGroup; + DefGroup(openFlags) { /* Noting that not all of these will have any effect in ** WASM-space. */ @@ -1194,7 +1204,7 @@ void sqlite3_wasm_test_stack_overflow(int recurse){ /* For testing the 'string-free' whwasmutil.xWrap() conversion. */ SQLITE_WASM_KEEP char * sqlite3_wasm_test_str_hello(int fail){ - char * s = fail ? 0 : (char *)malloc(6); + char * s = fail ? 0 : (char *)sqlite3_malloc(6); if(s){ memcpy(s, "hello", 5); s[5] = 0; |