aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/api
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm/api')
-rw-r--r--ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api3
-rw-r--r--ext/wasm/api/sqlite3-api-glue.js1
-rw-r--r--ext/wasm/api/sqlite3-api-oo1.js41
-rw-r--r--ext/wasm/api/sqlite3-api-prologue.js136
-rw-r--r--ext/wasm/api/sqlite3-vfs-opfs.c-pp.js39
-rw-r--r--ext/wasm/api/sqlite3-wasm.c12
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;