1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-01 06:27:03 +03:00

Merge the latest trunk changes into the reuse-schema branch.

FossilOrigin-Name: 858163f93893b0f481b27e39f4f7b3f51290606ad96e5f38cea2933c92398ff2
This commit is contained in:
drh
2025-02-03 15:17:31 +00:00
99 changed files with 4198 additions and 2607 deletions

View File

@ -32,19 +32,16 @@
#
# - Emscripten SDK: https://emscripten.org/docs/getting_started/downloads.html
# - The bash shell
# - GNU make, GNU sed, GNU awk, GNU grep (all in the $PATH)
# - GNU make, GNU sed, GNU awk, GNU grep (all in the $PATH and without
# a "g" prefix like they have on some non-GNU systems)
# - wasm-strip for release builds: https://github.com/WebAssembly/wabt
# - InfoZip for 'dist' zip file
########################################################################
default: all
#default: quick
SHELL := $(firstword $(shell which bash) $(wildcard /usr/local/bin/bash /usr/bin/bash /bin/bash))
ifeq (,$(SHELL))
$(error Cannot find the bash shell)
endif
MAKEFILE := $(lastword $(MAKEFILE_LIST))
CLEAN_FILES :=
DISTCLEAN_FILES :=
DISTCLEAN_FILES := config.make
MAKING_CLEAN := $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
.PHONY: clean distclean
clean:
@ -52,6 +49,67 @@ clean:
distclean: clean
-rm -f $(DISTCLEAN_FILES)
########################################################################
# Special-case builds for which we require certain pre-conditions
# which, if not met, may cause warnings or fatal errors in the build.
# This also affects the default optimization level flags. Note that
# the fiddle targets are in this list because they are used for
# generating sqlite.org/fiddle.
OPTIMIZED_TARGETS := dist snapshot fiddle fiddle.debug
ifeq (1,$(MAKING_CLEAN))
bin.wasm-strip := echo "not stripping"
bin.wasm-opt := irrelevant
bin.emcc := irrelevant
bin.bash := irrelevant
emcc.version := unknown
else
# Include config.make and perform some bootstrapping...
ifeq (,$(wildcard ./config.make))
$(error Missing config.make (gets generated by the configure script if the EMSDK is found))
endif
include ./config.make
ifeq (,$(bin.bash))
$(error Configure script did not find the bash shell)
endif
ifeq (,$(bin.emcc))
$(error Configure script did not find emcc)
endif
emcc.version := $(shell $(bin.emcc) --version | sed -n 1p | sed -e 's/^.* \([3-9][^ ]*\) .*$$/\1/;')
$(info using emcc version [$(emcc.version)])
ifeq (,$(bin.wasm-strip))
####################################################################
# We need wasm-strip for release builds (see below for why) but
# not strictly for non-release builds.
$(info WARNING: *******************************************************************)
$(info WARNING: Builds using -Oz will minify WASM-exported names, breaking)
$(info WARNING: _All The Things_. The workaround for that is to build)
$(info WARNING: with -g3 (which explodes the file size) and then strip the debug)
$(info WARNING: info after compilation, using wasm-strip, to shrink the wasm file.)
$(info WARNING: wasm-strip was not found in the PATH so we cannot strip those.)
$(info WARNING: If this build uses any optimization level higher than -O1 then)
$(info WARNING: the ***resulting JS code WILL NOT BE USABLE***.)
$(info WARNING: wasm-strip is part of the wabt package:)
$(info WARNING: https://github.com/WebAssembly/wabt)
$(info WARNING: on Ubuntu-like systems it can be installed with:)
$(info WARNING: sudo apt install wabt)
$(info WARNING: *******************************************************************)
ifneq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS)))
$(error Cannot make release-quality binary because wasm-strip is not available.)
endif
bin.wasm-strip := echo "not wasm-stripping"
endif
ifeq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS)))
$(info ==============================================================)
$(info == Development build. Make one of (dist, snapshot) for a)
$(info == smaller release build.)
$(info ==============================================================)
endif
endif
# ^^^ end of are-we-MAKING_CLEAN
maybe-wasm-strip := $(bin.wasm-strip)
########################################################################
# JS_BUILD_NAMES exists for documentation purposes only. It enumerates
# the core build styles:
@ -133,13 +191,15 @@ sqlite3.canonical.c := $(dir.top)/sqlite3.c
sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c))
sqlite3.h := $(dir.top)/sqlite3.h
ifneq (1,$(MAKING_CLEAN))
ifeq (,$(shell grep sqlite3_activate_see $(sqlite3.c)))
ifeq (1,$(MAKING_CLEAN))
SQLITE_C_IS_SEE := 0
else
SQLITE_C_IS_SEE := 1
$(info This is an SEE build)
endif
ifeq (,$(shell grep sqlite3_activate_see $(sqlite3.c)))
SQLITE_C_IS_SEE := 0
else
SQLITE_C_IS_SEE := 1
$(info This is an SEE build)
endif
endif
########################################################################@
@ -151,65 +211,6 @@ $(sqlite3.h):
$(MAKE) -C $(dir.top) sqlite3.c
$(sqlite3.c): $(sqlite3.h)
########################################################################
# Special-case builds for which we require certain pre-conditions
# which, if not met, may cause warnings or fatal errors in the build.
# This also affects the default optimization level flags. Note that
# the fiddle targets are in this list because they are used for
# generating sqlite.org/fiddle.
OPTIMIZED_TARGETS := dist snapshot fiddle fiddle.debug
ifneq (1,$(MAKING_CLEAN))
ifeq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS)))
$(info ==============================================================)
$(info == Development build. Make one of (dist, snapshot) for a)
$(info == smaller release build.)
$(info ==============================================================)
endif
endif
########################################################################
# Find emcc (Emscripten compiler)...
ifeq (1,$(MAKING_CLEAN))
emcc.bin := echo
emcc.version := unknown
else
emcc.bin := $(dir.tool)/emcc.sh
ifeq (,$(wildcard $(emcc.bin)))
$(error Configure script did not find emcc.)
endif
emcc.version := $(shell $(emcc.bin) --version | sed -n 1p | sed -e 's/^.* \([3-9][^ ]*\) .*$$/\1/;')
$(info using emcc version [$(emcc.version)])
endif
#########################################################################
# Find wasm-strip, which we need for release builds (see below for
# why) but not strictly for non-release builds.
ifeq (1,$(MAKING_CLEAN))
wasm-strip-bin := irrelevant
else
wasm-strip.bin ?= $(shell which wasm-strip 2>/dev/null)
ifeq (,$(wasm-strip.bin))
$(info WARNING: *******************************************************************)
$(info WARNING: Builds using -O2/-O3/-Os/-Oz will minify WASM-exported names,)
$(info WARNING: breaking _All The Things_. The workaround for that is to build)
$(info WARNING: with -g3 (which explodes the file size) and then strip the debug)
$(info WARNING: info after compilation, using wasm-strip, to shrink the wasm file.)
$(info WARNING: wasm-strip was not found in the PATH so we cannot strip those.)
$(info WARNING: If this build uses any optimization level higher than -O1 then)
$(info WARNING: the ***resulting JS code WILL NOT BE USABLE***.)
$(info WARNING: wasm-strip is part of the wabt package:)
$(info WARNING: https://github.com/WebAssembly/wabt)
$(info WARNING: on Ubuntu-like systems it can be installed with:)
$(info WARNING: sudo apt install wabt)
$(info WARNING: *******************************************************************)
ifneq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS)))
$(error Cannot make release-quality binary because wasm-strip is not available.)
endif
wasm-strip.bin := echo "not wasm-stripping"
endif
endif
maybe-wasm-strip := $(wasm-strip.bin)
########################################################################
# barebones=1 disables all "extraneous" stuff from sqlite3-wasm.c, the
# goal being to create a WASM file with only the core APIs.
@ -376,20 +377,7 @@ $(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE)
DISTCLEAN_FILES += $(bin.stripccomments)
########################################################################
# bin.mkwb is used for generating some of the makefile code for the
# various wasm builds. It used to be generated in this makefile via a
# difficult-to-read/maintain block of $(eval)'d code. Attempts were
# made to generate it from tcl and bash (shell) but having to escape
# the $ references in those languages made it just as illegible as the
# native makefile code. Somewhat surprisingly, moving that code generation
# to C makes it slightly less illegible than the previous 3 options.
bin.mkwb := ./mkwasmbuilds
$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE)
$(CC) -o $@ $<
DISTCLEAN_FILES += $(bin.mkwb)
########################################################################
# C-PP.FILTER: a $(call)able to transform $(1) to $(2) via:
# SQLITE.CALL.C-PP.FILTER: a $(call)able to transform $(1) to $(2) via:
#
# ./c-pp -f $(1) -o $(2) $(3)
#
@ -419,7 +407,7 @@ DISTCLEAN_FILES += $(bin.mkwb)
# JS/WASM build. They are solely for use with $(bin.c-pp) itself.
#
# -D... flags which should be included in all invocations should be
# appended to $(C-PP.FILTER.global).
# appended to $(SQLITE.CALL.C-PP.FILTER.global).
bin.c-pp := ./c-pp
$(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE)
$(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \
@ -427,20 +415,20 @@ $(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE)
-DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \
-DSQLITE_TEMP_STORE=3
DISTCLEAN_FILES += $(bin.c-pp)
C-PP.FILTER.global ?=
SQLITE.CALL.C-PP.FILTER.global ?=
ifeq (1,$(SQLITE_C_IS_SEE))
C-PP.FILTER.global += -Denable-see
SQLITE.CALL.C-PP.FILTER.global += -Denable-see
endif
define C-PP.FILTER
define SQLITE.CALL.C-PP.FILTER
# Create $2 from $1 using $(bin.c-pp)
# $1 = Input file: c-pp -f $(1).js
# $2 = Output file: c-pp -o $(2).js
# $3 = optional c-pp -D... flags
$(2): $(1) $$(MAKEFILE) $$(bin.c-pp)
$$(bin.c-pp) -f $(1) -o $$@ $(3) $(C-PP.FILTER.global)
$$(bin.c-pp) -f $(1) -o $$@ $(3) $(SQLITE.CALL.C-PP.FILTER.global)
#CLEAN_FILES += $(2)
endef
# /end C-PP.FILTER
# /end SQLITE.CALL.C-PP.FILTER
########################################################################
# cflags.common = C compiler flags for all builds
@ -618,7 +606,6 @@ emcc.cflags += -I. -I$(dir.top)
########################################################################
# emcc flags specific to building .js/.wasm files...
emcc.jsflags := -fPIC
emcc.jsflags += --minify 0
emcc.jsflags += --no-entry
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
emcc.jsflags += -sMODULARIZE
@ -826,7 +813,7 @@ sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
########################################################################
########################################################################
# SQLITE3.xJS.ESM-EXPORT-DEFAULT is used by mkwasmbuilds.c and the
# SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT is used by mkwasmbuilds.c and the
# wasmfs build. $1 is 1 if the build mode needs this workaround
# (modes: esm, bundler-friendly, node) and 0 if not (vanilla). $2 must
# be 0 for all builds except sqlite3-wasmfs.mjs, in which case it must
@ -847,9 +834,9 @@ sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
#
# Maintenance reminder: Mac sed works differently than GNU sed, so we
# use awk instead of sed for this.
define SQLITE3.xJS.ESM-EXPORT-DEFAULT
define SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT
if [ x1 = x$(1) ]; then \
echo "Fragile workaround for emscripten/issues/18237. See SQLITE3.xJS.ESM-EXPORT-DEFAULT."; \
echo "Fragile workaround for emscripten/issues/18237. See SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT."; \
{\
awk '/^export default/ && !f{f=1; next} 1' $@ > $@.tmp && mv $@.tmp $@; \
} || exit $$?; \
@ -873,14 +860,6 @@ sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs
sqlite3-api-wasmfs.mjs := $(dir.tmp)/sqlite3-api-wasmfs.mjs
sqlite3-wasmfs.mjs := $(dir.wasmfs)/sqlite3-wasmfs.mjs
EXPORTED_FUNCTIONS.fiddle := $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
ifneq (1,$(MAKING_CLEAN))
.wasmbuilds.make: $(bin.mkwb)
@rm -f $@
$(bin.mkwb) > $@
@chmod -w $@
-include .wasmbuilds.make
endif
DISTCLEAN_FILES += .wasmbuilds.make
# The various -D... values used by *.c-pp.js include:
#
@ -930,22 +909,22 @@ sqlite3-worker1-promiser.js := $(dir.dout)/sqlite3-worker1-promiser.js
sqlite3-worker1-promiser.mjs := $(dir.dout)/sqlite3-worker1-promiser.mjs
sqlite3-worker1-bundler-friendly.mjs := $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs
sqlite3-worker1-promiser-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\
$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js)))
$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.mjs),\
$(c-pp.D.sqlite3-bundler-friendly)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\
$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js)))
$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\
$(sqlite3-worker1-promiser-bundler-friendly.js),\
$(c-pp.D.sqlite3-bundler-friendly)))
$(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\
$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\
-Dtarget=es6-module -Dtarget=es6-bundler-friendly))
$(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.mjs) \
$(sqlite3-worker1-promiser-bundler-friendly.js)
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js))
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\
$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js))
$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\
-Dtarget=es6-module))
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html))
$(eval $(call C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\
$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html))
$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\
-Dtarget=es6-module))
all: $(sqlite3-worker1.js) \
$(sqlite3-worker1-promiser.js) $(sqlite3-worker1-promiser.mjs)
@ -961,6 +940,27 @@ sqlite3-api.ext.jses += \
all quick: $(sqlite3-api.ext.jses)
q: quick
ifneq (1,$(MAKING_CLEAN))
########################################################################
# bin.mkwb is used for generating some of the makefile code for the
# various wasm builds. It used to be generated in this makefile via a
# difficult-to-read/maintain block of $(eval)'d code. Attempts were
# made to generate it from tcl and bash (shell) but having to escape
# the $ references in those languages made it just as illegible as the
# native makefile code. Somewhat surprisingly, moving that code generation
# to C makes it slightly less illegible than the previous 3 options.
bin.mkwb := ./mkwasmbuilds
$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE)
$(CC) -o $@ $<
DISTCLEAN_FILES += $(bin.mkwb)
.wasmbuilds.make: $(bin.mkwb)
@rm -f $@
$(bin.mkwb) > $@
@chmod -w $@
-include .wasmbuilds.make
endif
DISTCLEAN_FILES += .wasmbuilds.make
########################################################################
# batch-runner.js is part of one of the test apps which reads in SQL
# dumps generated by $(speedtest1) and executes them.
@ -1042,7 +1042,7 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
$(pre-post-speedtest1-vanilla.deps) \
$(EXPORTED_FUNCTIONS.speedtest1)
@echo "Building $@ ..."
$(emcc.bin) \
$(bin.emcc) \
$(emcc.speedtest1) \
$(emcc.speedtest1.common) \
$(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \
@ -1052,6 +1052,7 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
$(speedtest1.exit-runtime0) \
-o $@ $(speedtest1.cfiles) -lm
$(maybe-wasm-strip) $(speedtest1.wasm)
sed -i -e '/^var _sqlite3.*createExportWrapper/d' $@
chmod -x $(speedtest1.wasm)
ls -la $@ $(speedtest1.wasm)
@ -1077,10 +1078,10 @@ all: speedtest1
# tester1.mjs: cases 3 and 4
#
# To create those, we filter tester1.c-pp.js with $(bin.c-pp)...
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js))
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm)))
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm)))
$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.js))
$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm)))
$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.html,tester1.html))
$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm)))
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
# Note that we do not include $(sqlite3-bundler-friendly.mjs) in this
# because bundlers are client-specific.

View File

@ -31,16 +31,20 @@ build:
use of and is not demonstrated here.
Browsers will not serve WASM files from file:// URLs, so the test and
demonstration apps require a web server and that server must include
the following headers in its response when serving the files:
demonstration apps require a web server and that server must, for the
OPFS[^1]-related features, include the following headers in its response
when serving the files:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
The core library will function without those headers but certain
features, most notably OPFS storage, will not be available.
Most functionality will work without those headers but the OPFS[^1]
storage capability will not be available without them.
One simple way to get the demo apps up and running on Unix-style
systems is to install althttpd (https://sqlite.org/althttpd) and run:
althttpd --enable-sab --page index.html
[^1]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system

View File

@ -29,8 +29,16 @@ $ ./emsdk install latest
$ ./emsdk activate latest
```
The following needs to be run for each shell instance which needs the
`emcc` compiler:
(Sidebar: Emscripten updates can and do _change things_, i.e. _break
things_, so it's considered _required practice_ to test thoroughly
after upgrading it! Our build process makes no guarantees about which
Emscripten version(s) will or won't work, but it's important that
production builds are built using a compatible version. During active
development, the EMSDK is frequently updated, the goal being to keep
`sqlite3.wasm` working with "the latest" EMSDK.)
The SQLite configure script will search for the EMSDK. One way
to ensure that it finds it is:
```
# Activate PATH and other environment variables in the current terminal:
@ -38,15 +46,27 @@ $ source ./emsdk_env.sh
$ which emcc
/path/to/emsdk/upstream/emscripten/emcc
$ ./configure ...
```
Optionally, add that to your login shell's resource file (`~/.bashrc`
or equivalent).
Optionally, add that `source` part to your login shell's resource file
(`~/.bashrc` or equivalent).
That `env` script needs to be sourced for building this application
from the top of the sqlite3 build tree:
Another way is to pass the EMSDK dir to configure:
```
$ ./configure --with-emsdk=/path/to/emsdk
```
The build tree uses a small wrapper for invoking the `emcc` (the
Emscripten compiler): `tool/emcc.sh` is generated from
`tool/emcc.sh.in` using the EMSDK path found by the configure process.
With that in place, the most common build approaches are:
```
# From the top of the tree:
$ make fiddle
```
@ -57,7 +77,7 @@ $ cd ext/wasm
$ make
```
That will generate the a number of files required for a handful of
Those will generate the a number of files required for a handful of
test and demo applications which can be accessed via
`index.html`. WASM content cannot, due to XMLHttpRequest security
limitations, be loaded if the containing HTML file is opened directly
@ -68,16 +88,21 @@ needs to be served via an HTTP server. For example, using
```
$ cd ext/wasm
$ althttpd --enable-sab --max-age 1 --page index.html
# Or, more simply, from the ext/wasm dir:
$ make httpd
```
That will open the system's browser and run the index page, from which
all of the test and demo applications can be accessed.
That will open the system's browser and visit the index page, from
which (almost) all of the test and demo applications can be accessed.
(`ext/wasm/SQLTester` is not listed in that page because it's only of
real utility when it's used in conjunction with the project's
proprietary test suite, which most users don't have access to.)
Note that when serving this app via [althttpd][], it must be a version
from 2022-09-26 or newer so that it recognizes the `--enable-sab`
flag, which causes althttpd to emit two HTTP response headers which
are required to enable JavaScript's `SharedArrayBuffer` and `Atomics`
APIs. Those APIs are required in order to enable the OPFS-related
APIs. Those APIs are required in order to enable the [OPFS][]-related
features in the apps which use them.
# Testing on a remote machine that is accessed via SSH
@ -104,3 +129,4 @@ be tunneled using SSH.
[emscripten]: https://emscripten.org
[althttpd]: https://sqlite.org/althttpd
[SharedArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
[OPFS]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system

View File

@ -85,17 +85,18 @@ browser client:
Installs the `sqlite3.vtab` namespace, which contain helpers for use
by downstream code which creates `sqlite3_module` implementations.
- **`sqlite3-vfs-opfs.c-pp.js`**\
is an sqlite3 VFS implementation which supports the Origin-Private
FileSystem (OPFS) as a storage layer to provide persistent storage
for database files in a browser. It requires...
is an sqlite3 VFS implementation which supports the [Origin-Private
FileSystem (OPFS)][OPFS] as a storage layer to provide persistent
storage for database files in a browser. It requires...
- **`sqlite3-opfs-async-proxy.js`**\
is the asynchronous backend part of the OPFS proxy. It speaks
directly to the (async) OPFS API and channels those results back
to its synchronous counterpart. This file, because it must be
started in its own Worker, is not part of the amalgamation.
is the asynchronous backend part of the [OPFS][] proxy. It
speaks directly to the (async) OPFS API and channels those
results back to its synchronous counterpart. This file, because
it must be started in its own Worker, is not part of the
amalgamation.
- **`sqlite3-vfs-opfs-sahpool.c-pp.js`**\
is another sqlite3 VFS supporting the OPFS, but uses a completely
different approach that the above-listed one.
is another sqlite3 VFS supporting the [OPFS][], but uses a
completely different approach that the above-listed one.
- **`sqlite3-api-cleanup.js`**\
The previous files do not immediately extend the library. Instead
they add callback functions to be called during its
@ -152,7 +153,7 @@ into the build-generated `sqlite3.js` along with `sqlite3-api.js`.
flag. This file overwrites the Emscripten-installed
`sqlite3InitModule()` function with one which, after the module is
loaded, also initializes the asynchronous parts of the sqlite3
module. For example, the OPFS VFS support.
module. For example, the [OPFS][] VFS support.
<a id='c-pp'></a>
Preprocessing of Source Files
@ -164,3 +165,6 @@ builds. The preprocessor application itself is in
[`c-pp.c`](/file/ext/wasm/c-pp.c) and the complete technical details
of such preprocessing are maintained in
[`GNUMakefile`](/file/ext/wasm/GNUmakefile).
[OPFS]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system

View File

@ -12,6 +12,7 @@
const toExportForESM =
//#endif
(function(){
//console.warn("this is extern-post-js");
/**
In order to hide the sqlite3InitModule()'s resulting
Emscripten module from downstream clients (and simplify our
@ -62,6 +63,16 @@ const toExportForESM =
globalThis.sqlite3InitModule = function ff(...args){
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
return originalInit(...args).then((EmscriptenModule)=>{
//console.warn("originalInit() then() arg =",EmscriptenModule);
//console.warn("initModuleState =",initModuleState);
EmscriptenModule.runSQLite3PostLoadInit(EmscriptenModule);
const s = EmscriptenModule.sqlite3;
s.scriptInfo = initModuleState;
//console.warn("sqlite3.scriptInfo =",s.scriptInfo);
if(ff.__isUnderTest) s.__isUnderTest = true;
const f = s.asyncPostInit;
delete s.asyncPostInit;
const rv = f();
//#if wasmfs
if('undefined'!==typeof WorkerGlobalScope &&
EmscriptenModule['ENVIRONMENT_IS_PTHREAD']){
@ -74,14 +85,7 @@ const toExportForESM =
return EmscriptenModule;
}
//#endif
//console.warn("sqlite3InitModule() returning sqlite3 object.");
const s = EmscriptenModule.sqlite3;
s.scriptInfo = initModuleState;
//console.warn("sqlite3.scriptInfo =",s.scriptInfo);
if(ff.__isUnderTest) s.__isUnderTest = true;
const f = s.asyncPostInit;
delete s.asyncPostInit;
return f();
return rv;
}).catch((e)=>{
console.error("Exception loading sqlite3 module:",e);
throw e;

View File

@ -1,4 +1,6 @@
/* The current function scope was opened via post-js-header.js, which
gets prepended to this at build-time. This file closes that
scope. */
})/*postRun.push(...)*/;
//console.warn("This is the end of the Module.runSQLite3PostLoadInit handler.");
}/*Module.runSQLite3PostLoadInit()*/;
//console.warn("This is the end of the setup of the (pending) Module.runSQLite3PostLoadInit");

View File

@ -7,17 +7,22 @@
installs will be run after the WASM module is loaded, at which
point the sqlite3 JS API bits will get set up.
*/
if(!Module.postRun) Module.postRun = [];
Module.postRun.push(function(Module/*the Emscripten-style module object*/){
Module.runSQLite3PostLoadInit = function(EmscriptenModule/*the Emscripten-style module object*/){
/** ^^^ As don't use Module.postRun, as that runs a different time
depending on whether this file is built with emcc 3.1.x or
4.0.x. This function name is intentionally obnoxiously verbose to
ensure that we don't collide with current and future Emscripten
symbol names. */
'use strict';
//console.warn("This is the start of the Module.postRun handler.");
/* This function will contain at least the following:
- post-js-header.js (this file)
- sqlite3-api-prologue.js => Bootstrapping bits to attach the rest to
- common/whwasmutil.js => Replacements for much of Emscripten's glue
- jaccwaby/jaccwabyt.js => Jaccwabyt (C/JS struct binding)
- jaccwabyt/jaccwabyt.js => Jaccwabyt (C/JS struct binding)
- sqlite3-api-glue.js => glues previous parts together
- sqlite3-api-oo.js => SQLite3 OO API #1
- sqlite3-api-oo1.js => SQLite3 OO API #1
- sqlite3-api-worker1.js => Worker-based API
- sqlite3-vfs-helper.c-pp.js => Utilities for VFS impls
- sqlite3-vtab-helper.c-pp.js => Utilities for virtual table impls

View File

@ -14,6 +14,9 @@
intended to be appended after all other sqlite3-api-*.js files so
that it can finalize any setup and clean up any global symbols
temporarily used for setting up the API's various subsystems.
In Emscripten builds it's run in the context of a Module.postRun
handler.
*/
'use strict';
if('undefined' !== typeof Module){ // presumably an Emscripten build

View File

@ -228,11 +228,36 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}),
'*'
]],
/**
2025-02-03: We do not have a way to automatically clean up
destructors which are automatically converted from JS functions
via the final argument to sqlite3_set_auxdata(). Because of
that, it is strongly recommended that clients use
wasm.installFunction() to create such callbacks, then pass that
pointer to sqlite3_set_auxdata(). Relying on automated
conversions here will lead to leaks of JS/WASM proxy functions
because sqlite3_set_auxdata() is frequently called in UDFs.
The sqlite3.oo1.DB class's onclose handlers can be used for this
purpose. For example:
const pAuxDtor = wasm.installFunction('v(p)', function(ptr){
//free ptr
});
myDb.onclose = {
after: ()=>{
wasm.uninstallFunction(pAuxDtor);
}
};
Then pass pAuxDtor as the final argument to appropriate
sqlite3_set_auxdata() calls.
*/
["sqlite3_set_auxdata", undefined, [
"sqlite3_context*", "int", "*",
new wasm.xWrap.FuncPtrAdapter({
name: 'xDestroyAuxData',
signature: 'v(*)',
signature: 'v(p)',
contextKey: (argv, argIndex)=>argv[0/* sqlite3_context* */]
})
]],
@ -1047,6 +1072,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
'sqlite3_set_authorizer',
'sqlite3_trace_v2',
'sqlite3_update_hook'
/*
We do not yet have a way to clean up automatically-converted
sqlite3_set_auxdata() finalizers.
*/
]) {
const x = wasm.exports[name];
if( !x ){

View File

@ -12,12 +12,12 @@
This file is intended to be combined at build-time with other
related code, most notably a header and footer which wraps this
whole file into an Emscripten Module.postRun() handler. The sqlite3
JS API has no hard requirements on Emscripten and does not expose
any Emscripten APIs to clients. It is structured such that its build
can be tweaked to include it in arbitrary WASM environments which
can supply the necessary underlying features (e.g. a POSIX file I/O
layer).
whole file into an Emscripten Module.postRun()-style handler. The
sqlite3 JS API has no hard requirements on Emscripten and does not
expose any Emscripten APIs to clients. It is structured such that
its build can be tweaked to include it in arbitrary WASM
environments which can supply the necessary underlying features
(e.g. a POSIX file I/O layer).
Main project home page: https://sqlite.org
@ -1712,41 +1712,48 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
missing or falsy pointer argument as 0.
*/
capi.sqlite3_db_config = function(pDb, op, ...args){
if(!this.s){
this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int',
['sqlite3*', 'int', 'string:static']
/* MAINDBNAME requires a static string */);
this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int',
['sqlite3*', 'int', '*','int', 'int']);
this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int',
['sqlite3*', 'int', 'int','*']);
}
switch(op){
case capi.SQLITE_DBCONFIG_ENABLE_FKEY:
case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER:
case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
case capi.SQLITE_DBCONFIG_ENABLE_QPSG:
case capi.SQLITE_DBCONFIG_TRIGGER_EQP:
case capi.SQLITE_DBCONFIG_RESET_DATABASE:
case capi.SQLITE_DBCONFIG_DEFENSIVE:
case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA:
case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
case capi.SQLITE_DBCONFIG_DQS_DML:
case capi.SQLITE_DBCONFIG_DQS_DDL:
case capi.SQLITE_DBCONFIG_ENABLE_VIEW:
case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA:
case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS:
case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER:
return this.ip(pDb, op, args[0], args[1] || 0);
case capi.SQLITE_DBCONFIG_LOOKASIDE:
return this.pii(pDb, op, args[0], args[1], args[2]);
case capi.SQLITE_DBCONFIG_MAINDBNAME:
return this.s(pDb, op, args[0]);
default:
return capi.SQLITE_MISUSE;
case capi.SQLITE_DBCONFIG_ENABLE_FKEY:
case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER:
case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
case capi.SQLITE_DBCONFIG_ENABLE_QPSG:
case capi.SQLITE_DBCONFIG_TRIGGER_EQP:
case capi.SQLITE_DBCONFIG_RESET_DATABASE:
case capi.SQLITE_DBCONFIG_DEFENSIVE:
case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA:
case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
case capi.SQLITE_DBCONFIG_DQS_DML:
case capi.SQLITE_DBCONFIG_DQS_DDL:
case capi.SQLITE_DBCONFIG_ENABLE_VIEW:
case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA:
case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS:
case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER:
case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE:
case capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE:
case capi.SQLITE_DBCONFIG_ENABLE_COMMENTS:
if( !this.ip ){
this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int',
['sqlite3*', 'int', 'int', '*']);
}
return this.ip(pDb, op, args[0], args[1] || 0);
case capi.SQLITE_DBCONFIG_LOOKASIDE:
if( !this.pii ){
this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int',
['sqlite3*', 'int', '*', 'int', 'int']);
}
return this.pii(pDb, op, args[0], args[1], args[2]);
case capi.SQLITE_DBCONFIG_MAINDBNAME:
if(!this.s){
this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int',
['sqlite3*', 'int', 'string:static']
/* MAINDBNAME requires a static string */);
}
return this.s(pDb, op, args[0]);
default:
return capi.SQLITE_MISUSE;
}
}.bind(Object.create(null));

View File

@ -331,7 +331,6 @@ SQLITE_WASM_EXPORT void sqlite3__wasm_pstack_restore(unsigned char * p){
*/
SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_alloc(int n){
if( n<=0 ) return 0;
//if( n & 0x7 ) n += 8 - (n & 0x7) /* align to 8-byte boundary */;
n = (n + 7) & ~7 /* align to 8-byte boundary */;
if( PStack.pBegin + n > PStack.pPos /*not enough space left*/
|| PStack.pBegin + n <= PStack.pBegin /*overflow*/ ) return 0;
@ -597,6 +596,9 @@ const char * sqlite3__wasm_enum_json(void){
DefInt(SQLITE_DBCONFIG_TRUSTED_SCHEMA);
DefInt(SQLITE_DBCONFIG_STMT_SCANSTATUS);
DefInt(SQLITE_DBCONFIG_REVERSE_SCANORDER);
DefInt(SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE);
DefInt(SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE);
DefInt(SQLITE_DBCONFIG_ENABLE_COMMENTS);
DefInt(SQLITE_DBCONFIG_MAX);
} _DefGroup;
@ -1630,6 +1632,9 @@ int sqlite3__wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){
case SQLITE_DBCONFIG_TRUSTED_SCHEMA:
case SQLITE_DBCONFIG_STMT_SCANSTATUS:
case SQLITE_DBCONFIG_REVERSE_SCANORDER:
case SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE:
case SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE:
case SQLITE_DBCONFIG_ENABLE_COMMENTS:
return sqlite3_db_config(pDb, op, arg1, pArg2);
default: return SQLITE_MISUSE;
}

View File

@ -51,7 +51,7 @@
Its global-scope symbol is intended only to provide an easy way to
make it available to 3rd-party scripts and "should" be deleted
after calling it. That symbols is _not_ used within the library.
after calling it. That symbol is _not_ used within the library.
Forewarning: this API explicitly targets only browser
environments. If a given non-browser environment has the
@ -69,7 +69,8 @@
- WASM-exported "indirect function table" access and
manipulation. e.g. creating new WASM-side functions using JS
functions, analog to Emscripten's addFunction() and
uninstallFunction() but slightly different.
uninstallFunction() but slightly different and with more useful
lifetime semantics.
- Get/set specific heap memory values, analog to Emscripten's
getValue() and setValue().
@ -165,11 +166,11 @@
This code is developed and maintained in conjunction with the
Jaccwabyt project:
https://fossil.wanderinghorse.net/r/jaccwabbyt
https://fossil.wanderinghorse.net/r/jaccwabyt
More specifically:
https://fossil.wanderinghorse.net/r/jaccwabbyt/file/common/whwasmutil.js
https://fossil.wanderinghorse.net/r/jaccwabyt/file/common/whwasmutil.js
*/
globalThis.WhWasmUtilInstaller = function(target){
'use strict';
@ -1627,7 +1628,7 @@ globalThis.WhWasmUtilInstaller = function(target){
need a level of hand-written wrappers around them, depending on
how they're used, in order to provide the client with JS
strings. Alternately, clients will need to perform such conversions
on their own, e.g. using cstrtojs(). Or maybe we can find a way
on their own, e.g. using cstrToJs(). Or maybe we can find a way
to perform such conversions here, via addition of an xWrap()-style
function signature to the options argument.
*/

15
ext/wasm/config.make.in Normal file
View File

@ -0,0 +1,15 @@
# Gets filtered by the configure script
bin.bash = @BIN_BASH@
bin.emcc = @EMCC_WRAPPER@
bin.wasm-strip = @BIN_WASM_STRIP@
bin.wasm-opt = @BIN_WASM_OPT@
SHELL := $(bin.bash)
# The following overrides can be uncommented to test various
# validation and if/else branches the makefile code:
#
#bin.bash :=
#bin.emcc :=
#bin.wasm-strip :=
#bin.wasm-opt :=

View File

@ -97,6 +97,16 @@ STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
# Note that we require $(bin.version-info) in order to figure out the
# dist file's name, so cannot (without a recursive make) have the
# target name equal to the archive name.
#
# 2025-01-15: Emsdk 4.0.0 introduces, in its generated code, a regex
# which contains the pattern /*. That, of course, confuses any C-style
# comment-stripper which is not specifically JS-aware and smart enough
# to know that it's in a regex or string literal. Because of that,
# comment-stripping is currently disabled, which means the builds will
# be significantly larger than before.
#apply_comment_stripper := false
apply_comment_stripper := true
# ^^^ shell command true or false
dist: \
$(bin.stripccomments) $(bin.version-info) \
$(dist.build) $(STRIP_K1.js) $(STRIP_K2.js) \
@ -109,8 +119,8 @@ dist: \
@cp -p README-dist.txt $(dist-dir.top)/README.txt
@cp -p index-dist.html $(dist-dir.top)/index.html
@cp -p $(dist.jswasm.extras) $(dist-dir.jswasm)
@$(foreach JS,$(STRIP_K1.js),$(call DIST_STRIP_COMMENTS,$(JS),-k))
@$(foreach JS,$(STRIP_K2.js),$(call DIST_STRIP_COMMENTS,$(JS),-k -k))
@if $(apply_comment_stripper); then $(foreach JS,$(STRIP_K1.js),$(call DIST_STRIP_COMMENTS,$(JS),-k)) fi
@if $(apply_comment_stripper); then $(foreach JS,$(STRIP_K2.js),$(call DIST_STRIP_COMMENTS,$(JS),-k -k)) fi
@cp -p $(dist.common.extras) $(dist-dir.common)
@set -e; \
vnum=$$($(bin.version-info) --download-version); \

View File

@ -97,8 +97,8 @@
wrapper is significantly easier to use, however.</li>
<li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>:
a demo of the Promise-based wrapper of the Worker1 API.</li>
<li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
same as the previous demo except loads the promiser from an ESM module.</li>
<!--li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
same as the previous demo except loads the promiser from an ESM module.</li-->
</ul>
</li>
</ul>

View File

@ -84,8 +84,8 @@
wrapper is significantly easier to use, however.</li>
<li><a href='demo-worker1-promiser.html'>demo-worker1-promiser</a>:
a demo of the Promise-based wrapper of the Worker1 API.</li>
<li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
same as the previous demo except loads the promiser from an ESM module.</li>
<!--li><a href='demo-worker1-promiser-esm.html'>demo-worker1-promiser-esm</a>:
same as the previous demo except loads the promiser from an ESM module.</li-->
</ul>
</li>
<li>speedtest1 ports (sqlite3's primary benchmarking tool)...

View File

@ -64,6 +64,75 @@ static void mk_prologue(void){
ps("# pre-post-jses.deps.* = a list of dependencies for the");
ps("# --[extern-][pre/post]-js files.");
ps("pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)");
{
/* SQLITE.CALL.WASM-OPT = shell code to run $(1) (source wasm file
** name) through $(bin.wasm-opt) */
const char * zOptFlags =
/*
** Flags for wasm-opt. It has many, many, MANY "passes" options
** and the ones which appear here were selected solely on the
** basis of trial and error.
**
** All wasm file size savings/costs mentioned below are based on
** the vanilla build of sqlite3.wasm with -Oz (our shipping
** configuration). Comments like "saves nothing" may not be
** technically correct: "nothing" means "some neglible amount."
**
** Note that performance gains/losses are _not_ taken into
** account here: only wasm file size.
*/
"--enable-bulk-memory-opt " /* required */
"--all-features " /* required */
"--post-emscripten " /* Saves roughly 12kb */
"--strip-debug " /* We already wasm-strip, but in
** case this environment has no
** wasm-strip... */
/*
** The rest are trial-and-error. See wasm-opt --help and search
** for "Optimization passes" to find the full list.
**
** With many flags this gets unusuably slow.
*/
/*"--converge " saves nothing for the options we're using */
/*"--dce " saves nothing */
/*"--directize " saves nothing */
/*"--gsi " no: requires --closed-world flag, which does not
** sound like something we want. */
/*"--gufa --gufa-cast-all --gufa-optimizing " costs roughly 2kb */
/*"--heap-store-optimization " saves nothing */
/*"--heap2local " saves nothing */
//"--inlining --inlining-optimizing " costs roughly 3kb */
"--local-cse " /* saves roughly 1kb */
/*"--once-reduction " saves nothing */
/*"--remove-memory-init " presumably a performance tweak */
/*"--remove-unused-names " saves nothing */
/*"--safe-heap "*/
/*"--vacuum " saves nothing */
;
ps("ifeq (,$(bin.wasm-opt))");
ps("define SQLITE.CALL.WASM-OPT");
ps("echo 'wasm-opt not available for $(1)'");
ps("endef");
ps("else");
ps("define SQLITE.CALL.WASM-OPT");
pf("echo -n 'Before wasm-opt:'; ls -l $(1);\\\n"
"\trm -f wasm-opt-tmp.wasm;\\\n"
/* It's very likely that the set of wasm-opt flags varies from
** version to version, so we'll ignore any errors here. */
"\tif $(bin.wasm-opt) $(1) -o wasm-opt-tmp.wasm \\\n"
"\t\t%s; then \\\n"
"\t\tmv wasm-opt-tmp.wasm $(1); \\\n"
"\t\techo -n 'After wasm-opt: '; \\\n"
"\t\tls -l $(1); \\\n"
"\telse \\\n"
"\t\techo 'WARNING: ignoring wasm-opt failure'; \\\n"
"\tfi\n",
zOptFlags
);
ps("endef");
ps("endif");
}
}
/*
@ -75,7 +144,7 @@ static void mk_pre_post(const char *zName /* build name */,
const char *zMode /* build mode */,
const char *zCmppD /* optional -D flags for c-pp for the
** --pre/--post-js files. */){
pf("%s# Begin --pre/--post flags for %s-%s\n", zBanner, zName, zMode);
pf("%s# Begin --pre/--post flags for %s-%s\n", zBanner, zNM);
pf("c-pp.D.%s-%s := %s\n", zNM, zCmppD ? zCmppD : "");
pf("pre-post-%s-%s.flags ?=\n", zNM);
@ -84,14 +153,14 @@ static void mk_pre_post(const char *zName /* build name */,
zNM, zNM);
pf("$(pre-js.js.%s-%s): $(MAKEFILE)\n", zNM);
#if 1
pf("$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s),"
pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s),"
"$(c-pp.D.%s-%s)))\n", zNM, zNM);
#else
/* This part is needed if/when we re-enable the custom
** Module.instantiateModule() impl in api/pre-js.c-pp.js. */
pf("pre-js.js.%s-%s.intermediary := $(dir.tmp)/pre-js.%s-%s.intermediary.js\n",
zNM, zNM);
pf("$(eval $(call C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s.intermediary),"
pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.js.in),$(pre-js.js.%s-%s.intermediary),"
"$(c-pp.D.%s-%s) -Dcustom-Module.instantiateModule))\n", zNM, zNM);
pf("$(pre-js.js.%s-%s): $(pre-js.js.%s-%s.intermediary)\n", zNM, zNM);
pf("\tcp $(pre-js.js.%s-%s.intermediary) $@\n", zNM);
@ -107,12 +176,12 @@ static void mk_pre_post(const char *zName /* build name */,
/* --post-js=... */
pf("post-js.js.%s-%s := $(dir.tmp)/post-js.%s-%s.js\n", zNM, zNM);
pf("$(eval $(call C-PP.FILTER,$(post-js.js.in),"
pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(post-js.js.in),"
"$(post-js.js.%s-%s),$(c-pp.D.%s-%s)))\n", zNM, zNM);
/* --extern-post-js=... */
pf("extern-post-js.js.%s-%s := $(dir.tmp)/extern-post-js.%s-%s.js\n", zNM, zNM);
pf("$(eval $(call C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.%s-%s),"
pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(extern-post-js.js.in),$(extern-post-js.js.%s-%s),"
"$(c-pp.D.%s-%s)))\n", zNM, zNM);
/* Combine flags for use with emcc... */
@ -130,7 +199,7 @@ static void mk_pre_post(const char *zName /* build name */,
zNM, zNM, zNM);
pf("pre-post-%s-%s.deps := $(pre-post-jses.%s-%s.deps) $(dir.tmp)/pre-js.%s-%s.js\n",
zNM, zNM, zNM);
pf("# End --pre/--post flags for %s-%s%s", zName, zMode, zBanner);
pf("# End --pre/--post flags for %s-%s%s", zNM, zBanner);
}
/*
@ -155,23 +224,23 @@ static void mk_fiddle(){
"$(SOAP.js)\n",
zTail, (i ? " $(fiddle-module.js)" : ""));
if( 1==i ){/*fiddle.debug*/
pf(" @test -d \"$(dir $@)\" || mkdir -p \"$(dir $@)\"\n");
pf("\t@test -d \"$(dir $@)\" || mkdir -p \"$(dir $@)\"\n");
}
pf(" $(emcc.bin) -o $@ $(fiddle.emcc-flags%s) "
pf("\t$(bin.emcc) -o $@ $(fiddle.emcc-flags%s) "
"$(pre-post-fiddle-module-vanilla.flags) $(fiddle.cses)\n",
zTail);
pf(" $(maybe-wasm-strip) $(fiddle-module.wasm%s)\n", zTail);
pf(" @cp -p $(SOAP.js) $(dir $@)\n");
pf("\t$(maybe-wasm-strip) $(fiddle-module.wasm%s)\n", zTail);
pf("\t@cp -p $(SOAP.js) $(dir $@)\n");
if( 1==i ){/*fiddle.debug*/
pf(" cp -p $(dir.fiddle)/index.html "
pf("\tcp -p $(dir.fiddle)/index.html "
"$(dir.fiddle)/fiddle.js "
"$(dir.fiddle)/fiddle-worker.js "
"$(dir $@)\n");
}
pf(" @for i in %s/*.*js %s/*.html %s/*.wasm; do \\\n"
" test -f $${i} || continue; \\\n"
" gzip < $${i} > $${i}.gz; \\\n"
" done\n", zDir, zDir, zDir);
pf("\t@for i in %s/*.*js %s/*.html %s/*.wasm; do \\\n"
"\t\ttest -f $${i} || continue; \\\n"
"\t\tgzip < $${i} > $${i}.gz; \\\n"
"\tdone\n", zDir, zDir, zDir);
if( 0==i ){
ps("fiddle: $(fiddle-module.js)");
}else{
@ -193,6 +262,10 @@ static void mk_lib_mode(const char *zName /* build name */,
const char *zJsOut /* name of generated sqlite3.js/.mjs */,
const char *zCmppD /* extra -D flags for c-pp */,
const char *zEmcc /* extra flags for emcc */){
const char * zWasmOut = "$(basename $@).wasm"
/* The various targets named X.js or X.mjs (zJsOut) also generate
** X.wasm, and we need that part of the name to perform some
** post-processing after Emscripten generates X.wasm. */;
assert( zName );
assert( zMode );
assert( zApiJsOut );
@ -201,22 +274,28 @@ static void mk_lib_mode(const char *zName /* build name */,
if( !zEmcc ) zEmcc = "";
pf("%s# Begin build [%s-%s]\n", zBanner, zNM);
pf("ifneq (1,$(MAKING_CLEAN))\n");
pf("$(info Setting up build [%s-%s]: %s)\n", zNM, zJsOut);
mk_pre_post(zNM, zCmppD);
pf("\nemcc.flags.%s.%s ?=\n", zNM);
if( zEmcc[0] ){
pf("emcc.flags.%s.%s += %s\n", zNM, zEmcc);
}
pf("$(eval $(call C-PP.FILTER, $(sqlite3-api.js.in), %s, %s))\n",
pf("$(eval $(call SQLITE.CALL.C-PP.FILTER, $(sqlite3-api.js.in), %s, %s))\n",
zApiJsOut, zCmppD);
/* target zJsOut */
pf("%s: %s $(MAKEFILE) $(sqlite3-wasm.cfiles) $(EXPORTED_FUNCTIONS.api) "
"$(pre-post-%s-%s.deps)\n",
"$(pre-post-%s-%s.deps) "
"$(sqlite3-api.ext.jses)"
/* ^^^ maintenance reminder: we set these as deps so that they
get copied into place early. That allows the developer to
reload the base-most test pages while the later-stage builds
are still compiling, which is especially helpful when running
builds with long build times (like -Oz). */
"\n",
zJsOut, zApiJsOut, zNM);
pf("\t@echo \"Building $@ ...\"\n");
pf("\t$(emcc.bin) -o $@ $(emcc_opt_full) $(emcc.flags) \\\n");
pf("\t$(bin.emcc) -o $@ $(emcc_opt_full) $(emcc.flags) \\\n");
pf("\t\t$(emcc.jsflags) -sENVIRONMENT=$(emcc.environment.%s) \\\n", zMode);
pf("\t\t$(pre-post-%s-%s.flags) \\\n", zNM);
pf("\t\t$(emcc.flags.%s) $(emcc.flags.%s.%s) \\\n", zName, zNM);
@ -227,33 +306,45 @@ static void mk_lib_mode(const char *zName /* build name */,
/* TODO? Replace this CALL with the corresponding makefile code.
** OTOH, we also use this $(call) in the speedtest1-wasmfs build,
** which is not part of the rules emitted by this program. */
pf("\t@$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,1,%d)\n",
pf("\t@$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,%d)\n",
0==strcmp("sqlite3-wasmfs", zName) ? 1 : 0);
}
pf("\t@dotwasm=$(basename $@).wasm; \\\n"
"\tchmod -x $$dotwasm; \\\n"
"\t$(maybe-wasm-strip) $$dotwasm; \\\n");
pf("\t@chmod -x %s; \\\n"
"\t\t$(maybe-wasm-strip) %s;\n",
zWasmOut, zWasmOut);
pf("\t@$(call SQLITE.CALL.WASM-OPT,%s)\n", zWasmOut);
pf("\t@sed -i -e '/^var _sqlite3.*createExportWrapper/d' %s || exit; \\\n"
/* ^^^^^^ reminder: Mac/BSD sed has no -i flag */
"\t\techo 'Stripped out createExportWrapper() parts.'\n",
zJsOut) /* Our JS code installs bindings of each WASM export. The
generated Emscripten JS file does the same using its
own framework, but we don't use those results and can
speed up lib init, and reduce memory cost
considerably, by stripping them out. */;
/*
** The above $(emcc.bin) call will write zJsOut and will create a
** like-named .wasm file. That .wasm file name gets hard-coded into
** zJsOut so we need to, for some cases, patch zJsOut to use the
** name sqlite3.wasm instead. Note that the resulting .wasm file is
** identical for all builds for which zEmcc is empty.
** The above $(bin.emcc) call will write zJsOut and will create a
** like-named .wasm file (zWasmOut). That .wasm file name gets
** hard-coded into zJsOut so we need to, for some cases, patch
** zJsOut to use the name sqlite3.wasm instead. Note that the
** resulting .wasm file is identical for all builds for which zEmcc
** is empty.
*/
if( 0==strcmp("bundler-friendly", zMode)
|| 0==strcmp("node", zMode) ) {
pf("\techo 'Patching $@ for %s.wasm...' \\\n", zName);
pf("\trm -f $$dotwasm; dotwasm=; \\\n"
"\tsed -i -e 's/%s-%s.wasm/%s.wasm/g' $@ || exit $$?; \\\n",
|| 0==strcmp("node", zMode) ){
pf("\t@echo 'Patching $@ for %s.wasm...'; \\\n", zName);
pf("\t\trm -f %s; \\\n", zWasmOut);
pf("\t\tsed -i -e 's/%s-%s.wasm/%s.wasm/g' $@ || exit;\n",
/* ^^^^^^ reminder: Mac/BSD sed has no -i flag */
zNM, zName);
pf("\t@ls -la $@\n");
}else{
pf("\t@ls -la %s $@\n", zWasmOut);
}
pf("\tls -la $$dotwasm $@\n");
if( 0!=strcmp("sqlite3-wasmfs", zName) ){
/* The sqlite3-wasmfs build is optional and needs to be invoked
** conditionally using info we don't have here. */
pf("all: %s\n", zJsOut);
}
ps("endif\n# ^^^ !$(MAKING_CLEAN)");
pf("# End build [%s-%s]%s", zNM, zBanner);
}

View File

@ -1241,6 +1241,12 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}finally{
wasm.pstack.restore(stack);
}
capi.sqlite3_db_config(this.db, capi.SQLITE_DBCONFIG_ENABLE_COMMENTS, 0, null);
T.mustThrow(()=>this.db.exec("select 1 /* with comments */"), "SQL comments are disallowed");
capi.sqlite3_db_config(this.db, capi.SQLITE_DBCONFIG_ENABLE_COMMENTS, 1, null);
this.db.exec("select 1 /* with comments */");
/* SQLITE_DBCONFIG_ENABLE_ATTACH_... are in the ATTACH-specific tests */
})
////////////////////////////////////////////////////////////////////
@ -1999,7 +2005,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}/*window UDFs*/)
////////////////////////////////////////////////////////////////////
.t("ATTACH", function(){
.t("ATTACH", function(sqlite3){
const db = this.db;
const resultRows = [];
db.exec({
@ -2078,7 +2084,36 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
db.exec("detach foo");
T.mustThrow(()=>db.exec("select * from foo.bar"),
"Because foo is no longer attached.");
})
/* SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE/WRITE... */
const db2 = new sqlite3.oo1.DB();
try{
capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, 0, null);
T.mustThrow(()=>db2.exec("attach 'attached.db' as foo"),
"Cannot create a new db via ATTACH");
capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, 1, null);
db2.exec([
"attach 'attached.db' as foo;",
"create table foo.t(a);",
"insert into foo.t(a) values(1);",
"detach foo;"
]);
capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, 0, null);
db2.exec("attach 'attached.db' as foo");
T.mustThrow(()=>db2.exec("insert into foo.t(a) values(2)"),
"ATTACH_WRITE is false");
capi.sqlite3_db_config(db2, capi.SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, 1, null);
db2.exec([
"detach foo;",
"attach 'attached.db' as foo;",
"insert into foo.t(a) values(2);",
"drop table foo.t;",
"detach foo"
]);
}finally{
db2.close();
}
})/*ATTACH tests*/
////////////////////////////////////////////////////////////////////
.t("Read-only", function(sqlite3){
@ -3402,6 +3437,73 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
}
})
.t({
/* https://github.com/sqlite/sqlite-wasm/issues/92 */
name: 'sqlite3_set_auxdata() binding signature',
test: function(sqlite3){
const db = new sqlite3.oo1.DB();
const stack = wasm.pstack.pointer;
const pAux = wasm.pstack.alloc(4);
let pAuxDestructed = 0;
const args = [];
const pAuxDtor = wasm.installFunction('v(p)', function(ptr){
//log("freeing auxdata");
++pAuxDestructed;
});
let pAuxDtorDestructed = false;
db.onclose = {
after: ()=>{
pAuxDtorDestructed = true;
wasm.uninstallFunction(pAuxDtor);
}
};
try{
db.createFunction("auxtest",{
xFunc: function(pCx, x, y){
args.push(x);
T.assert(wasm.isPtr(pCx));
const localAux = capi.sqlite3_get_auxdata(pCx, 0);
if( !localAux ){
//log("setting auxdata");
/**
We do not currently an automated way to clean up
auxdata finalizer functions (the 4th argument to
sqlite3_set_auxdata()) which get automatically
converted from JS to WASM. Because of that, relying
on automated conversions for those is not
recommended. Instead, follow the pattern show in
this function: use wasm.installFunction() to create
the function, then pass the resulting function
pointer this function, and cleanup (at some point)
using wasm.uninstallFunction().
*/
capi.sqlite3_set_auxdata(pCx, 0, pAux, pAuxDtor);
}else{
/* This is never actually hit in this example and it's
not entirely clear how to cause it to. The point of
this test, however, is to demonstrate that the
finalizer impl gets triggered, so we're not going to
fret over this at the moment. */
//log("seen auxdata",localAux);
T.assert(pAux===localAux);
}
return x;
}
});
db.exec([
"create table t(a);",
"insert into t(a) values(1),(2),(3);",
"select auxtest(a,a), auxtest(a,a) from t order by a"
]);
}finally{
db.close();
wasm.pstack.restore(stack);
}
T.assert(6===args.length);
T.assert(pAuxDestructed>0);
T.assert(pAuxDtorDestructed);
}
})
;/*end of Bug Reports group*/;
////////////////////////////////////////////////////////////////////////

View File

@ -101,7 +101,7 @@ $(speedtest1-wasmfs.mjs): $(speedtest1.cfiles) $(sqlite3-wasmfs.js) \
$(emcc.flags.sqlite3-wasmfs) \
$(emcc.flags.speedtest1-wasmfs) \
-o $@ $(speedtest1.cfiles) -lm
@$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,1,1)
@$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,1)
$(maybe-wasm-strip) $(speedtest1-wasmfs.wasm)
chmod -x $(speedtest1-wasmfs.wasm)
ls -la $@ $(speedtest1-wasmfs.wasm)