Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ functionality.

(These steps can only be executed by maintainers.)

1. Manually trigger the [GitHub Actions workflow](../../actions/workflows/build-wasm.yml). By
default, it uses the latest SQLite tag. This pull request will contain the latest `sqlite3.wasm`
and related bindings.

2. Once the above pull request is validated and merged, update the version number in `package.json`,
reflecting the current [SQLite version number](https://sqlite.org/download.html) and add a build
identifier suffix like `-build1`. The complete version number should read something like
`3.41.2-build1`.
1. Run the [Build SQLite Wasm workflow](../../actions/workflows/build-wasm.yml) with a full SQLite
commit SHA as `sqlite_ref`. Review the generated PR and sync types with the
[upstream API docs](https://sqlite.org/wasm/doc/trunk/api-index.md).

2. Merge the PR and update `package.json` and `package-lock.json` to the SQLite version plus a build
suffix, e.g. `3.55.0-build1`.

3. Run the [Publish package workflow](../../actions/workflows/publish.yml) from the default branch.
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
"main": "./dist/index.mjs",
"node": "./dist/node.mjs",
"types": "./dist/index.d.mts",
"sideEffects": [
"./dist/index.mjs",
"./dist/node.mjs",
"./dist/sqlite3-worker1.mjs",
"./dist/sqlite3-opfs-async-proxy.js"
],
"exports": {
".": {
"types": "./dist/index.d.mts",
Expand Down
13 changes: 1 addition & 12 deletions scripts/build-sqlite3-wasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ SQLITE_REF="${SQLITE_REF:-master}"
SRC_DIR="${WORKDIR:-/build/sqlite-src}"
HOST_UID="${HOST_UID:-}"
HOST_GID="${HOST_GID:-}"
WASM_OPT_WRAPPER=""
EXTRACT_DIR=""

log() { printf '\033[1;34m[build]\033[0m %s\n' "$*"; }
Expand All @@ -23,7 +22,6 @@ mkdir -p /src/bin
cleanup() {
local status=$?
trap - EXIT
if [ -n "$WASM_OPT_WRAPPER" ]; then rm -f "$WASM_OPT_WRAPPER"; fi
if [ -n "$EXTRACT_DIR" ]; then rm -rf "$EXTRACT_DIR"; fi
if [ -n "$HOST_UID" ] && [ -n "$HOST_GID" ]; then
log "Fixing ownership for /src/bin to $HOST_UID:$HOST_GID"
Expand Down Expand Up @@ -60,14 +58,6 @@ log "make sqlite3.c"
make sqlite3.c
cd ext/wasm

# SQLite invokes wasm-opt with --all-features. Newer Binaryen versions then
# emit compact imports, which are not supported by our browser/Node targets.
# Append the compatibility flag after SQLite's flags, preserving its passes.
WASM_OPT_WRAPPER="$(mktemp /tmp/sqlite-wasm-opt.XXXXXX)"
printf '#!/usr/bin/env bash\nexec %q "$@" --disable-compact-imports\n' \
"$(command -v wasm-opt)" > "$WASM_OPT_WRAPPER"
chmod +x "$WASM_OPT_WRAPPER"

# Check the generated defaults before overriding the per-loader flags. Refuse
# to silently discard additional flags introduced by an upstream SQLite update.
make .wasmbuilds.make WASM_CUSTOM_INSTANTIATE=0
Expand All @@ -86,8 +76,7 @@ log "Running make -j4 npm with Cross-Origin Storage for browser loaders (excludi
# SQLite's custom instantiateWasm bypasses Emscripten's COS loading path.
make -j4 npm WASM_CUSTOM_INSTANTIATE=0 \
'emcc.flags.bundler=-sCROSS_ORIGIN_STORAGE' \
'emcc.flags.esm=-sCROSS_ORIGIN_STORAGE' \
"bin.wasm-opt=$WASM_OPT_WRAPPER"
'emcc.flags.esm=-sCROSS_ORIGIN_STORAGE'

# Extract to staging and verify before replacing the mounted output.
log "Extracting npm-bundle.zip"
Expand Down
230 changes: 191 additions & 39 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ export class PreparedStatement {
* non-0 because this function is effectively a destructor and "destructors do
* not throw." This function will throw if it is called while the statement is
* in active use via a {@link Database#exec} callback.
*
* Since SQLite 3.55.0, native disposal via `capi.sqlite3_finalize()` also
* disconnects this OO1 object and shared wrappers. That low-level call
* bypasses this method's statement-locking checks.
*/
finalize(): number | undefined;

Expand Down Expand Up @@ -1045,6 +1049,10 @@ export class Database {
* Note that garbage collection of a db handle, if it happens at all, will
* never trigger `close()`, so {@link onclose} handlers are not a reliable way
* to implement close-time cleanup or maintenance of a db.
*
* Since SQLite 3.55.0, native disposal via `capi.sqlite3_close_v2()` also
* disconnects this OO1 object and shared wrappers, without calling `onclose`
* handlers. Calling `close()` after native disposal is a no-op.
*/
close(): void; // ✓

Expand Down Expand Up @@ -3017,33 +3025,31 @@ export type WASM_API = {
peek64f: (addr: WasmPointer) => Sqlite3Result;

/**
* Requires `n` to be one of:
* Requires n to be one of:
*
* - Integer 8, 16, or 32.
* - A integer-type TypedArray constructor: `Int8Array`, `Int16Array`,
* `Int32Array`, or their `Uint` counterparts.
* - integer 8, 16, or 32.
* - A integer-type TypedArray constructor: Int8Array, Int16Array,
* Int32Array, or their Uint counterparts.
*
* If `BigInt` support is enabled, it also accepts the value 64 or a
* `BigInt64Array`/`BigUint64Array`, else it throws if passed 64 or one of
* those constructors.
* If this.bigIntEnabled is true, it also accepts the value 64 or a
* BigInt64Array/BigUint64Array, else it throws if passed 64 or one
* of those constructors.
*
* Returns an integer-based `TypedArray` view of the WASM heap memory buffer
* associated with the given block size. If passed an integer as the first
* argument and unsigned is truthy then the `"U"` (unsigned) variant of that
* view is returned, else the signed variant is returned. If passed a
* `TypedArray` value, the 2nd argument is ignored. Note that `Float32Array`
* and `Float64Array` views are not supported by this function.
* Returns an integer-based TypedArray view of the WASM heap memory
* buffer associated with the given block size. If passed an integer
* as the first argument and unsigned is truthy then the "U"
* (unsigned) variant of that view is returned, else the signed
* variant is returned. If passed a TypedArray value, the 2nd
* argument is ignored.
*
* Be aware that growth of the heap may invalidate any references to this
* heap, so do not hold a reference longer than needed and do not use a
* reference after any operation which may allocate. Instead, re-fetch the
* reference by calling this function again, which automatically refreshes the
* view if needed.
* Growth of the heap will invalidate any references to this heap,
* so do not hold a reference longer than needed and do not use a
* reference after any operation which may allocate. Instead,
* re-fetch the reference by calling this function again.
*
* Throws if passed an invalid `n`.
* Throws if passed an invalid n.
*
* Use of this function in client code is very rare. In practice, one of the
* (faster) convenience forms is used.
* @see https://sqlite.org/wasm/doc/trunk/api-wasm.md
*/
heapForSize(n: 8, unsigned: false | undefined): Int8Array;
heapForSize(n: 8, unsigned: true): Uint8Array;
Expand All @@ -3059,8 +3065,10 @@ export type WASM_API = {
heapForSize(n: typeof Uint32Array): Uint32Array;
heapForSize(n: 64, unsigned: false | undefined): BigInt64Array;
heapForSize(n: 64, unsigned: true): BigUint64Array;
heapForSize(n: typeof Int32Array): BigInt64Array;
heapForSize(n: typeof Uint32Array): BigUint64Array;
heapForSize(n: typeof BigInt64Array): BigInt64Array;
heapForSize(n: typeof BigUint64Array): BigUint64Array;
heapForSize(n: typeof Float32Array): Float32Array;
heapForSize(n: typeof Float64Array): Float64Array;

/** Faster convenience method to get a Int8Array view of the WASM heap. */
heap8: () => Int8Array;
Expand All @@ -3086,6 +3094,18 @@ export type WASM_API = {
/** Faster convenience method to get a BigUint64Array view of the WASM heap. */
heap64u: () => BigUint64Array;

/**
* Convenience (and faster) equivalent of
* this.heapForSize(Float32Array).
*/
heap32f: () => Float32Array;

/**
* Convenience (and faster) equivalent of
* this.heapForSize(Float64Array).
*/
heap64f: () => Float64Array;

/**
* Fetches the `heapForSize()` for the given representation then writes the
* given numeric value to it. Only numbers may be written this way, and
Expand Down Expand Up @@ -3153,11 +3173,14 @@ export type WASM_API = {
cstrToJs: (ptr: WasmPointer) => string | null;

/**
* Expects its argument to be a pointer into the WASM heap memory which refers
* to a NUL-terminated C-style string encoded as UTF-8. Returns the length, in
* bytes, of the string, as for `strlen(3)`. As a special case, if the
* argument is falsy, then it returns null. Throws if the argument is out of
* range for `wasm.heap8u()`.
* Expects ptr to be a pointer into the WASM heap memory which
* refers to a NUL-terminated C-style string encoded as UTF-8.
* Returns the length, in bytes, of the string, as for `strlen(3)`.
* As a special case, if !ptr or if it's not a pointer then it
* returns `null`. Results are undefined if no NUL is found. Throws
* if ptr is out of range for target.heap8u().
*
* @see https://sqlite.org/wasm/doc/trunk/api-wasm.md
*/
cstrlen: (ptr: WasmPointer) => number | null;

Expand Down Expand Up @@ -3643,17 +3666,25 @@ export type WASM_API = {
*/

/**
* Returns true if its value is a WASM pointer type. That is, it's a a 32-bit
* integer greater than or equal to zero.
* isPtr() is an alias for isPtr32() or isPtr64(), depending on the
* value of target.ptr.size.
*
* Sidebar: `isPtr()` is an alias for `isPtr32()`. If/when 64-bit WASM pointer
* support becomes widespread, it will become an alias for either `isPtr32()`
* or the as-yet-hypothetical `isPtr64()`, depending on a configuration
* option.
* @see https://sqlite.org/wasm/doc/trunk/api-wasm.md
*/
isPtr: (v: unknown) => v is WasmPointer;

/** See {@link WASM_API#isPtr} */
/**
* Returns true if the given value appears to be legal for use as a
* WASM pointer value. Its _range_ of values is not (cannot be)
* validated except to ensure that it is a 32-bit integer with a
* value of 0 or greater. Likewise, it cannot verify whether the
* value actually refers to allocated memory in the WASM heap.
*
* Whether or not null or undefined are legal are context-dependent.
* They generally are legal but this function does not treat them as
* such because they're not strictly legal for passing as-is as WASM
* integer arguments.
*/
isPtr32: (v: unknown) => v is WasmPointer;

/** Size of a WASM pointer in bytes. */
Expand Down Expand Up @@ -4478,7 +4509,16 @@ export type CAPI = {
/** SQLITE_LIMIT_SCHEMA: maximum number of schema objects (since 3.54). */
| 13
/** SQLITE_LIMIT_TRIGGER_STEPS: maximum statements per trigger (since 3.54). */
| 14,
| 14
/**
* The maximum number of common table expressions (CTEs) allowed
* in a single SQL statements. This limit is used to limit possible
* exponential growth in the number of required cursors required for
* nested joins of CTEs. The limit is an absolute upper bound, and
* SQLite may refuse to compile a statement if the number of CTEs simply
* approaches this limit. Set this limit to 0 to disable the restriction.
*/
| 15,
newVal: number,
) => number;

Expand Down Expand Up @@ -4658,7 +4698,7 @@ export type CAPI = {
sqlite3_bind_blob: (
stmt: StmtPtr,
idx: number,
blob: WasmPointer | string | readonly string[] | Int8Array | Uint8Array | ArrayBuffer,
blob: WasmPointer | null | string | readonly string[] | Int8Array | Uint8Array | ArrayBuffer,
n: number,
dtor: DtorType,
) => Sqlite3Result;
Expand Down Expand Up @@ -4734,7 +4774,7 @@ export type CAPI = {
sqlite3_bind_text: (
stmt: StmtPtr,
idx: number,
text: string | WasmPointer | readonly string[] | Int8Array | Uint8Array | ArrayBuffer,
text: string | WasmPointer | null | readonly string[] | Int8Array | Uint8Array | ArrayBuffer,
n: number,
dtor: DtorType,
) => Sqlite3Result;
Expand Down Expand Up @@ -5435,7 +5475,7 @@ export type CAPI = {
ctx: WasmPointer,
n: number,
pAux: WasmPointer,
xDelete: (() => void) | WasmPointer,
xDelete: WasmPointer,
) => void;

/**
Expand Down Expand Up @@ -8014,6 +8054,118 @@ export type CAPI = {
*/
sqlite3_js_db_uses_vfs: (db: DbPtr, vfsName: string, dbName: string) => boolean;

/**
* Sets up the given callback to be called when the given WASM
* pointer is finalized by the library. The purposes of this is to
* provide client code with a way to map JS state to native
* pointers in such a way that the state can be cleaned up
* regardless of how the natives are finalized.
*
* typeName describes what data type ptr is. It must be one of:
*
* "sqlite3*", "sqlite3_stmt*"
*
* and the ptr argument must be a non-null pointer to that type.
*
* The callback must be either a Function or a WASM function
* pointer[^1] to a function with the signature `void(*)(void*)`,
* a.k.a. "v(p)" in the library's function-binding parlance.
*
* It is legal to call this multiple times for a given combination
* of pointer and callback. Each callback will be added to the
* queue only once for each distinct callback reference, with the
* caveat noted below regarding callback references. That is,
* calling it N times with the same ptr/callback combination will
* only install the callback once, but N calls with N distinct
* callbacks will install all N.
*
* When used in loops or client functions which may see the same
* pointer multiple times, it is generally important to **not pass
* function literals**, as each such literal is a different
* instance, so an identical callback may be added N times for a
* given pointer. Functions should be assigned to symbols and
* those symbols should be passed in so that callback
* deduplication works:
*
* ```
* // NO:
* sqlite3_js_on_pointer_dispose(..., (x)=>...);
* // YES:
* const myFunc = (x)=>...;
* ...
* sqlite3_js_on_pointer_dispose(..., myFunc);
* // Also YES:
* let myFunc;
* ...
* sqlite3_js_on_pointer_dispose(..., (myFunc ??= (x)=>...));
* ```
*
* For single-call cases, e.g. when passing a newly-initialized db
* or statement, a function literal is perfectly fine. Within
* loops and client-defined functions which are called more than
* once per native instance, function literals will cause grief in
* the form of potentially registering a separate callback for
* each invocation of the SQL function.
*
* On success it returns an unspecified truthy value. On error
* (argument validation issues) it returns an unspecified falsy
* value and has no side-effects. It does not throw because it's
* intended to be usable from cases where throwing is not
* permitted, but it only fails for argument validation.
*
* When SQLite is about to free that pointer, all callbacks
* queued for it will be passed that pointer and then the
* callback queue for that pointer is cleared.
*
* Because the callback is called during the native-level
* finalization process, it must observe certain limitations:
*
* - It absolutely must not finalize the object, or results are
* well and truly undefined. Best case, a WASM crash.
*
* - Because multiple on-dispose listeners may be installed for
* any given pointer, the callback must not leave the object in an
* unusable state.
*
* - It should not perform "real work" with the pointer. e.g. a
* finalizing statement must not be stepped nor its columns
* fetched. A finalizing db should probably not run new
* queries.
*
* - Any exceptions are ignored but may trigger an alert via
* sqlite3.config.error().
*
* Ideally, such callbacks do nothing more than remove any state
* which they have mapped to the given pointer value, without
* actually trying to use the object referred to by that pointer.
* Sometimes, however, cleanup work may be necessary which
* involves judicious use of that type's methods.
*
* Added in 3.55.0.
*
* Design note: ideally we could support `sqlite3_context*`, as
* that would neatly tie in cleanup for user-defined SQL
* functions. However, the C APIs do not guaranty stability of
* those pointers across calls. It currently provides
* _unguaranteed_ stability in two cases but does not for any
* aggregate-specific cases, making it unsuitable for the problem
* this API addresses (pun not intended).
*
* [^1]: Though callback may be a WASM function pointer, that is
* actually less efficient than passing a JS function. The
* on-dispose dispatching is called from native code, invoking a
* JS function which the library connects to it during
* bootstrapping. sqlite3_js_on_pointer_dispose() receives the
* notifications "second hand" through that mechanism. If
* callback is a WASM function, invoking it adds one more step
* from JS-space into C-space and back.
*/
sqlite3_js_on_pointer_dispose: (
typeName: 'sqlite3*' | 'sqlite3_stmt*',
ptr: WasmPointer,
callback: ((ptr: WasmPointer) => void) | WasmPointer,
) => boolean | null | undefined;

/**
* Calls a synchronous callback up to maxTimes total attempts. Retries when
* it returns SQLITE_BUSY or throws SQLite3Error with that resultCode.
Expand Down
Loading
Loading