diff options
author | stephan <stephan@noemail.net> | 2024-07-25 14:00:26 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2024-07-25 14:00:26 +0000 |
commit | 520d1a84867fa59e063b66a276471cbfc9f6cef5 (patch) | |
tree | ea50fe07962fff9258eaedca74e42113e909a440 /ext | |
parent | cc65612e35a677dc7252229dadaba92b6d3997b0 (diff) | |
download | sqlite-520d1a84867fa59e063b66a276471cbfc9f6cef5.tar.gz sqlite-520d1a84867fa59e063b66a276471cbfc9f6cef5.zip |
More work on the minimal-mode wasm build (now 603kb uncompressed). Remove the hard-coded feature-enable flags from sqlite3-wasm.c and rely on the build to provide them. Some wasm build cleanup, but attempts to completely overhaul it have been thwarted by my inability to make script-generated makefile code more legible/maintainable than the current eval spaghetti.
FossilOrigin-Name: b029c4067943e366a9b25b8303136fab10822bd771ea4658ac4cd716ff4a0d8f
Diffstat (limited to 'ext')
-rw-r--r-- | ext/wasm/GNUmakefile | 93 | ||||
-rw-r--r-- | ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core | 1 | ||||
-rw-r--r-- | ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras | 1 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-api-glue.c-pp.js | 2 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-wasm.c | 79 | ||||
-rwxr-xr-x | ext/wasm/make-make.sh | 26 | ||||
-rw-r--r-- | ext/wasm/tester1.c-pp.js | 6 |
7 files changed, 107 insertions, 101 deletions
diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 2d5abebc4..785d501f3 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -15,8 +15,9 @@ # by the target name. Rebuild is necessary for all components to get # the desired optimization level. # -# quick, q = do just a minimal build (sqlite3.js/wasm, tester1) for -# faster development-mode turnaround. +# quick, q = do just build the essentials for testing +# (sqlite3.js/wasm, tester1) for faster development-mode +# turnaround. # # dist = create end user deliverables. Add dist.build=oX to build # with a specific optimization level, where oX is one of the @@ -45,16 +46,24 @@ # related to each build variant $(JS_BUILD_MODES). (Update: an # external script was attempted but generating properly-escaped # makefile code from within a shell script is even less legible -# than the $(eval) indirection going on in this file.) +# than the $(eval) indirection going on in this file.) (Update 2: +# a second attempt at that make yet another mess of it.) # default: all #default: quick -SHELL := $(shell which bash 2>/dev/null) +SHELL := $(firstword $(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 := +CLEAN_FILES := .gen.make DISTCLEAN_FILES := ./--dummy-- -release: oz MAKING_CLEAN := $(if $(filter %clean,$(MAKECMDGOALS)),1,0) +.PHONY: clean distclean +clean: + -rm -f $(CLEAN_FILES) +distclean: clean + -rm -f $(DISTCLEAN_FILES) ######################################################################## # JS_BUILD_NAMES exists for documentation purposes only. It enumerates @@ -143,33 +152,49 @@ $(sqlite3.h): $(MAKE) -C $(dir.top) sqlite3.c $(sqlite3.c): $(sqlite3.h) -# .cache.make is generated by a shell script and holds certain parts +######################################################################## +# .gen.make is generated by a shell script and holds certain parts # of the makefile, some of which are relatively expensive to calculate # on each run (that is, they can take a human-visible amount of time). +# +# .gen.make is also home to make-side vars which are needed for +# generating makefile code via shell code. ifeq (0,$(MAKING_CLEAN)) -.cache.make: $(sqlite3.c) $(sqlite3.canonical.c) $(MAKEFILE) make-make.sh +.gen.make: $(MAKEFILE) $(sqlite3.c) rm -f $@ - $(SHELL) make-make.sh "$(sqlite3.c)" > $@ + $(SHELL) make-make.sh $(sqlite3.c) > $@ chmod -w $@ --include .cache.make -.cache.make: $(emcc.bin) +-include .gen.make +endif + +ifeq (,$(filter release snapshot,$(MAKECMDGOALS))) + $(info Development build. Use 'release' or 'snapshot' target for a smaller release build.) endif -CLEAN_FILES += .cache.make # Common options for building sqlite3-wasm.c and speedtest1.c. +# Explicit ENABLEs... SQLITE_OPT = \ - -DSQLITE_ENABLE_FTS5 \ - -DSQLITE_ENABLE_RTREE \ - -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \ - -DSQLITE_ENABLE_STMTVTAB \ + -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_ENABLE_DBSTAT_VTAB \ - -DSQLITE_ENABLE_BYTECODE_VTAB \ + -DSQLITE_ENABLE_FTS5 \ + -DSQLITE_ENABLE_MATH_FUNCTIONS \ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ + -DSQLITE_ENABLE_PREUPDATE_HOOK \ + -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_SESSION \ + -DSQLITE_ENABLE_STMTVTAB \ + -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + +# Explicit OMITs... +SQLITE_OPT += \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_UTF16 \ - -DSQLITE_OMIT_SHARED_CACHE \ + -DSQLITE_OMIT_SHARED_CACHE + +# Misc. build-time config options... +SQLITE_OPT += \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_OS_KV_OPTIONAL=1 \ @@ -177,6 +202,7 @@ SQLITE_OPT = \ -DSQLITE_USE_URI=1 \ -DSQLITE_WASM_ENABLE_C_TESTS \ -DSQLITE_C=$(sqlite3.c) + #SQLITE_OPT += -DSQLITE_DEBUG # Enabling SQLITE_DEBUG will break sqlite3_wasm_vfs_create_file() # (and thus sqlite3_js_vfs_create_file()). Those functions are @@ -187,25 +213,16 @@ SQLITE_OPT = \ ######################################################################## # minimal=1 disables all "extraneous" stuff from sqlite3-wasm.c, the # goal being to create a WASM file with only the core APIs. -minimal ?= 0 ifeq (1,$(minimal)) SQLITE_OPT += -DSQLITE_WASM_MINIMAL -endif - -.PHONY: clean distclean -clean: - -rm -f $(CLEAN_FILES) -distclean: clean - -rm -f $(DISTCLEAN_FILES) - -ifeq (release,$(filter release,$(MAKECMDGOALS))) - ifeq (,$(wasm-strip)) - $(error Cannot make release-quality binary because wasm-strip is not available. \ - See notes in the warning above) - endif + # SQLITE_WASM_MINIMAL tells sqlite3-wasm.c to explicitly omit + # a bunch of stuff, in the interest of keeping the wasm file size + # down. + wasm-minimal := 1 else - $(info Development build. Use '$(MAKE) release' for a smaller release build.) + wasm-minimal := 0 endif +undefine minimal ######################################################################## # Adding custom C code via sqlite3_wasm_extra_init.c: @@ -368,11 +385,11 @@ EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.core) ifeq (1,$(SQLITE_C_IS_SEE)) EXPORTED_FUNCTIONS.api.in += $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see endif -ifeq (0,$(minimal)) +ifeq (0,$(wasm-minimal)) EXPORTED_FUNCTIONS.api.in += $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-extras else $(info ========================================) - $(info This is a minimal-mode build) + $(info This is a minimal-mode build. It is missing many features.) $(info ========================================) endif EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api @@ -408,8 +425,10 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.c-pp.js sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.c-pp.js # sqlite3-vfs-helper = helper APIs for VFSes: sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js -# sqlite3-vtab-helper = helper APIs for VTABLEs: -sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js +ifeq (0,$(wasm-minimal)) + # sqlite3-vtab-helper = helper APIs for VTABLEs: + sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js +endif # sqlite3-vfs-opfs = the first OPFS VFS: sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js # sqlite3-vfs-opfs-sahpool = the second OPFS VFS: diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core index db47ee7db..263542bc9 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core @@ -49,7 +49,6 @@ _sqlite3_db_name _sqlite3_db_readonly _sqlite3_db_status _sqlite3_deserialize -_sqlite3_drop_modules _sqlite3_errcode _sqlite3_errmsg _sqlite3_error_offset diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras index f8af685ed..2e831447d 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras @@ -50,6 +50,7 @@ _sqlite3session_table_filter _sqlite3_create_module _sqlite3_create_module_v2 _sqlite3_declare_vtab +_sqlite3_drop_modules _sqlite3_vtab_collation _sqlite3_vtab_distinct _sqlite3_vtab_in diff --git a/ext/wasm/api/sqlite3-api-glue.c-pp.js b/ext/wasm/api/sqlite3-api-glue.c-pp.js index 55dd300d7..3c4bf582d 100644 --- a/ext/wasm/api/sqlite3-api-glue.c-pp.js +++ b/ext/wasm/api/sqlite3-api-glue.c-pp.js @@ -385,7 +385,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ of this, the canonical builds of sqlite3.wasm/js guarantee that sqlite3.wasm.alloc() and friends use those allocators. Custom builds may not guarantee that, however. */, - ["sqlite3_drop_modules", "int", ["sqlite3*", "**"]], ["sqlite3_last_insert_rowid", "i64", ["sqlite3*"]], ["sqlite3_malloc64", "*","i64"], ["sqlite3_msize", "i64", "*"], @@ -422,6 +421,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ ["sqlite3_create_module_v2", "int", ["sqlite3*","string","sqlite3_module*","*","*"]], ["sqlite3_declare_vtab", "int", ["sqlite3*", "string:flexible"]], + ["sqlite3_drop_modules", "int", ["sqlite3*", "**"]], ["sqlite3_vtab_collation","string","sqlite3_index_info*","int"], ["sqlite3_vtab_distinct","int", "sqlite3_index_info*"], ["sqlite3_vtab_in","int", "sqlite3_index_info*", "int", "int"], diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index 7f7e69689..c0da2c623 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -14,16 +14,17 @@ */ #define SQLITE_WASM #ifdef SQLITE_WASM_ENABLE_C_TESTS +# undef SQLITE_WASM_ENABLE_C_TESTS +# define SQLITE_WASM_ENABLE_C_TESTS 1 /* -** Code blocked off by SQLITE_WASM_TESTS is intended solely for use in -** unit/regression testing. They may be safely omitted from +** Code blocked off by SQLITE_WASM_ENABLE_C_TESTS is intended solely +** for use in unit/regression testing. They may be safely omitted from ** client-side builds. The main unit test script, tester1.js, will ** skip related tests if it doesn't find the corresponding functions ** in the WASM exports. */ -# define SQLITE_WASM_TESTS 1 #else -# define SQLITE_WASM_TESTS 0 +# define SQLITE_WASM_ENABLE_C_TESTS 0 #endif /* @@ -92,43 +93,6 @@ #undef SQLITE_ENABLE_API_ARMOR #define SQLITE_ENABLE_API_ARMOR 1 -#ifndef SQLITE_ENABLE_BYTECODE_VTAB -# define SQLITE_ENABLE_BYTECODE_VTAB 1 -#endif -#ifndef SQLITE_ENABLE_DBPAGE_VTAB -# define SQLITE_ENABLE_DBPAGE_VTAB 1 -#endif -#ifndef SQLITE_ENABLE_DBSTAT_VTAB -# define SQLITE_ENABLE_DBSTAT_VTAB 1 -#endif -#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS -# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1 -#endif -#ifndef SQLITE_ENABLE_FTS5 -# define SQLITE_ENABLE_FTS5 1 -#endif -#ifndef SQLITE_ENABLE_MATH_FUNCTIONS -# define SQLITE_ENABLE_MATH_FUNCTIONS 1 -#endif -#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC -# define SQLITE_ENABLE_OFFSET_SQL_FUNC 1 -#endif -#ifndef SQLITE_ENABLE_PREUPDATE_HOOK -# define SQLITE_ENABLE_PREUPDATE_HOOK 1 /*required by session extension*/ -#endif -#ifndef SQLITE_ENABLE_RTREE -# define SQLITE_ENABLE_RTREE 1 -#endif -#ifndef SQLITE_ENABLE_SESSION -# define SQLITE_ENABLE_SESSION 1 -#endif -#ifndef SQLITE_ENABLE_STMTVTAB -# define SQLITE_ENABLE_STMTVTAB 1 -#endif -#ifndef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION -# define SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION -#endif - /**********************************************************************/ /* SQLITE_O... */ #ifndef SQLITE_OMIT_DEPRECATED @@ -214,6 +178,12 @@ # define SQLITE_OMIT_JSON #endif +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_WASM_MINIMAL) +# define SQLITE_WASM_HAS_VTAB 1 +#else +# define SQLITE_WASM_HAS_VTAB 0 +#endif + #include <assert.h> /* @@ -412,7 +382,7 @@ int sqlite3__wasm_db_error(sqlite3*db, int err_code, const char *zMsg){ return err_code; } -#if SQLITE_WASM_TESTS +#if SQLITE_WASM_ENABLE_C_TESTS struct WasmTestStruct { int v4; void * ppV; @@ -432,7 +402,7 @@ void sqlite3__wasm_test_struct(WasmTestStruct * s){ } return; } -#endif /* SQLITE_WASM_TESTS */ +#endif /* SQLITE_WASM_ENABLE_C_TESTS */ /* ** This function is NOT part of the sqlite3 public API. It is strictly @@ -967,7 +937,7 @@ const char * sqlite3__wasm_enum_json(void){ } _DefGroup; DefGroup(vtab) { -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_WASM_MINIMAL) +#if SQLITE_WASM_HAS_VTAB DefInt(SQLITE_INDEX_SCAN_UNIQUE); DefInt(SQLITE_INDEX_CONSTRAINT_EQ); DefInt(SQLITE_INDEX_CONSTRAINT_GT); @@ -995,7 +965,7 @@ const char * sqlite3__wasm_enum_json(void){ DefInt(SQLITE_FAIL); //DefInt(SQLITE_ABORT); // Also an error code DefInt(SQLITE_REPLACE); -#endif /*!SQLITE_OMIT_VIRTUALTABLE*/ +#endif /*SQLITE_WASM_HAS_VTAB*/ } _DefGroup; #undef DefGroup @@ -1110,6 +1080,7 @@ const char * sqlite3__wasm_enum_json(void){ #undef CurrentStruct +#if SQLITE_WASM_HAS_VTAB #define CurrentStruct sqlite3_vtab StructBinder { M(pModule, "p"); @@ -1155,7 +1126,6 @@ const char * sqlite3__wasm_enum_json(void){ } _StructBinder; #undef CurrentStruct -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_WASM_MINIMAL) /** ** Workaround: in order to map the various inner structs from ** sqlite3_index_info, we have to uplift those into constructs we @@ -1232,9 +1202,9 @@ const char * sqlite3__wasm_enum_json(void){ } _StructBinder; #undef CurrentStruct -#endif /*!SQLITE_OMIT_VIRTUALTABLE*/ +#endif /*SQLITE_WASM_HAS_VTAB*/ -#if SQLITE_WASM_TESTS +#if SQLITE_WASM_ENABLE_C_TESTS #define CurrentStruct WasmTestStruct StructBinder { M(v4, "i"); @@ -1244,7 +1214,7 @@ const char * sqlite3__wasm_enum_json(void){ M(xFunc, "v(p)"); } _StructBinder; #undef CurrentStruct -#endif +#endif /*SQLITE_WASM_ENABLE_C_TESTS*/ } out( "]"/*structs*/); @@ -1603,7 +1573,7 @@ sqlite3_kvvfs_methods * sqlite3__wasm_kvvfs_methods(void){ return &sqlite3KvvfsMethods; } -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_WASM_MINIMAL) +#if SQLITE_WASM_HAS_VTAB /* ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own JS/WASM bindings. @@ -1626,7 +1596,7 @@ int sqlite3__wasm_vtab_config(sqlite3 *pDb, int op, int arg){ return SQLITE_MISUSE; } } -#endif /*!SQLITE_OMIT_VIRTUALTABLE*/ +#endif /*SQLITE_WASM_HAS_VTAB*/ /* ** This function is NOT part of the sqlite3 public API. It is strictly @@ -1801,7 +1771,7 @@ int sqlite3__wasm_init_wasmfs(const char *zUnused){ } #endif /* __EMSCRIPTEN__ && SQLITE_ENABLE_WASMFS */ -#if SQLITE_WASM_TESTS +#if SQLITE_WASM_ENABLE_C_TESTS SQLITE_WASM_EXPORT int sqlite3__wasm_test_intptr(int * p){ @@ -1967,6 +1937,9 @@ int sqlite3__wasm_SQLTester_strglob(const char *zGlob, const char *z){ return !sqlite3__wasm_SQLTester_strnotglob(zGlob, z); } -#endif /* SQLITE_WASM_TESTS */ +#endif /* SQLITE_WASM_ENABLE_C_TESTS */ #undef SQLITE_WASM_EXPORT +#undef SQLITE_WASM_HAS_VTAB +#undef SQLITE_WASM_MINIMAL +#undef SQLITE_WASM_ENABLE_C_TESTS diff --git a/ext/wasm/make-make.sh b/ext/wasm/make-make.sh index e5b81b3d1..6f93d8883 100755 --- a/ext/wasm/make-make.sh +++ b/ext/wasm/make-make.sh @@ -23,7 +23,14 @@ function die(){ SQLITE3_C="${1-../../sqlite3.c}" -echo "# GENERATED makefile code. DO NOT EDIT." +separator='########################################################################' + +cat <<EOF +# GENERATED makefile code. DO NOT EDIT. +# Generated by $0 on $(date) +_GEN_MAKE := \$(lastword \$(MAKEFILE_LIST)) +\$(_GEN_MAKE): ${SQLITE3_C} $0 +EOF if grep sqlite3_activate_see "$SQLITE3_C" &>/dev/null; then echo 'SQLITE_C_IS_SEE := 1' @@ -34,6 +41,7 @@ fi ######################################################################## # Locate the emcc (Emscripten) binary... +echo $separator EMCC_BIN=$(which emcc) if [[ x = "x${EMCC_BIN}" ]]; then if [[ x != "x${EMSDK_HOME}" ]]; then @@ -51,6 +59,7 @@ echo '$(info using emcc version [$(emcc.version)])' ######################################################################### # wasm-strip binary... +echo $separator WASM_STRIP_BIN=$(which wasm-strip 2>/dev/null) echo "wasm-strip ?= ${WASM_STRIP_BIN}" if [[ x = "x${WASM_STRIP_BIN}" ]]; then @@ -71,14 +80,19 @@ ifeq (,\$(filter clean,\$(MAKECMDGOALS)))' \$(info WARNING: sudo apt install wabt) \$(info WARNING: *******************************************************************) endif +ifneq (,\$(filter release snapshot,\$(MAKECMDGOALS))) + \$(error Cannot make release-quality binary because wasm-strip is not available.) +endif EOF else - echo 'maybe-wasm-strip = $(wasm-strip)' +echo 'maybe-wasm-strip = $(wasm-strip)' fi +# /$(wasm-strip) +######################################################################## - -dir_dout=jswasm -dir_tmp=bld -for d in $dir_dout $dir_tmp; do +######################################################################## +# Make necessary dirs. Note that these need to align with their names +# in the main makefile. +for d in jswasm bld; do [ -d $d ] || mkdir -p $d done diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 3338bbb2a..b8db1a59b 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -2153,7 +2153,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; //////////////////////////////////////////////////////////////////////// .t({ name: 'virtual table #1: eponymous w/ manual exception handling', - predicate: ()=>!!capi.sqlite3_create_module || "Missing vtab support", + predicate: (sqlite3)=>!!sqlite3.capi.sqlite3_vtab || "Missing vtab support", test: function(sqlite3){ const VT = sqlite3.vtab; const tmplCols = Object.assign(Object.create(null),{ @@ -2350,7 +2350,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; //////////////////////////////////////////////////////////////////////// .t({ name: 'virtual table #2: non-eponymous w/ automated exception wrapping', - predicate: ()=>!!capi.sqlite3_create_module || "Missing vtab support", + predicate: (sqlite3)=>!!sqlite3.capi.sqlite3_vtab || "Missing vtab support", test: function(sqlite3){ const VT = sqlite3.vtab; const tmplCols = Object.assign(Object.create(null),{ @@ -3300,7 +3300,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; T.g('Bug Reports') .t({ name: 'Delete via bound parameter in subquery', - predicate: ()=>wasm.compileOptionUsed('ENABLE_FTS5') || "FTS5 is not available", + predicate: ()=>wasm.compileOptionUsed('ENABLE_FTS5') || "Missing FTS5", test: function(sqlite3){ // Testing https://sqlite.org/forum/forumpost/40ce55bdf5 // with the exception that that post uses "external content" |