aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.msc5
-rwxr-xr-xautoconf/tea/configure17
-rw-r--r--autosetup/proj.tcl2
-rw-r--r--ext/fts5/fts5_index.c42
-rw-r--r--ext/fts5/test/fts5corrupt3.test5
-rw-r--r--ext/fts5/test/fts5corrupt8.test53
-rw-r--r--ext/fts5/test/fts5integrity.test6
-rw-r--r--ext/fts5/test/fts5leftjoin.test49
-rw-r--r--ext/misc/vtablog.c7
-rw-r--r--ext/rtree/rtree.c6
-rw-r--r--ext/rtree/rtreeH.test19
-rw-r--r--ext/session/sessionI.test88
-rw-r--r--ext/session/sqlite3session.c177
-rw-r--r--ext/session/sqlite3session.h67
-rw-r--r--ext/session/test_session.c225
-rw-r--r--ext/wasm/GNUmakefile343
-rw-r--r--ext/wasm/api/sqlite3-api-oo1.c-pp.js331
-rw-r--r--ext/wasm/api/sqlite3-api-prologue.js17
-rw-r--r--ext/wasm/api/sqlite3-api-worker1.c-pp.js13
-rw-r--r--ext/wasm/api/sqlite3-wasm.c1
-rw-r--r--ext/wasm/api/sqlite3-worker1-promiser.c-pp.js19
-rw-r--r--ext/wasm/common/whwasmutil.js32
-rw-r--r--ext/wasm/config.make.in13
-rw-r--r--ext/wasm/dist.make32
-rw-r--r--ext/wasm/fiddle.make48
-rw-r--r--ext/wasm/fiddle/fiddle-worker.js9
-rw-r--r--ext/wasm/fiddle/fiddle.js60
-rw-r--r--ext/wasm/fiddle/index.html89
-rw-r--r--ext/wasm/mkwasmbuilds.c347
-rw-r--r--ext/wasm/speedtest1.html7
-rw-r--r--ext/wasm/tester1.c-pp.js106
-rw-r--r--ext/wasm/wasmfs.make46
-rw-r--r--main.mk4
-rw-r--r--manifest147
-rw-r--r--manifest.uuid2
-rw-r--r--sqlite3.128
-rw-r--r--src/btree.c24
-rw-r--r--src/btree.h1
-rw-r--r--src/build.c15
-rw-r--r--src/expr.c25
-rw-r--r--src/os_unix.c4
-rw-r--r--src/os_win.c194
-rw-r--r--src/printf.c16
-rw-r--r--src/resolve.c16
-rw-r--r--src/select.c127
-rw-r--r--src/shell.c.in143
-rw-r--r--src/sqlite.h.in5
-rw-r--r--src/sqliteInt.h9
-rw-r--r--src/trigger.c7
-rw-r--r--src/vdbe.c32
-rw-r--r--src/vdbe.h2
-rw-r--r--src/vdbeInt.h13
-rw-r--r--src/vdbeaux.c107
-rw-r--r--src/where.c32
-rw-r--r--src/wherecode.c42
-rw-r--r--src/whereexpr.c4
-rw-r--r--test/between.test17
-rw-r--r--test/eqp.test3
-rw-r--r--test/existsexpr.test432
-rw-r--r--test/existsexpr2.test96
-rw-r--r--test/existsfault.test49
-rw-r--r--test/fuzzcheck.c7
-rw-r--r--test/incrblob4.test15
-rw-r--r--test/json101.test8
-rw-r--r--test/notnull2.test2
-rw-r--r--test/rowvalue.test37
-rw-r--r--test/speedtest1.c14
-rwxr-xr-xtest/testrunner.tcl112
-rw-r--r--test/testrunner_estwork.tcl488
-rw-r--r--test/vtabH.test4
-rw-r--r--tool/mkautoconfamal.sh12
-rw-r--r--tool/mktoolzip.tcl64
72 files changed, 3578 insertions, 1062 deletions
diff --git a/Makefile.msc b/Makefile.msc
index 1533cf3de..e67638287 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -2596,9 +2596,12 @@ extensiontest: testfixture.exe testloadext.dll
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS)
-tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe sqlite3_rsync.exe $(TOP)\tool\mktoolzip.tcl
+tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe sqlite3_rsync.exe sqlite3.dll $(TOP)\tool\mktoolzip.tcl
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl
+snapshot-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe sqlite3_rsync.exe sqlite3.dll $(TOP)\tool\mktoolzip.tcl
+ .\testfixture.exe $(TOP)\tool\mktoolzip.tcl --snapshot
+
coretestprogs: testfixture.exe sqlite3.exe
testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe
diff --git a/autoconf/tea/configure b/autoconf/tea/configure
index 47378126f..01b3abcc2 100755
--- a/autoconf/tea/configure
+++ b/autoconf/tea/configure
@@ -1,7 +1,20 @@
#!/bin/sh
+# Look for and run autosetup...
dir0="`dirname "$0"`"
-dirA="$dir0/../autosetup"
-# This is the case ^^^^^^^^^^^^ in the SQLite "autoconf" bundle.
+dirA="$dir0"
+if [ -d $dirA/autosetup ]; then
+ # A local copy of autosetup
+ dirA=$dirA/autosetup
+elif [ -d $dirA/../autosetup ]; then
+ # SQLite "autoconf" bundle
+ dirA=$dirA/../autosetup
+elif [ -d $dirA/../../autosetup ]; then
+ # SQLite canonical source tree
+ dirA=$dirA/../../autosetup
+else
+ echo "$0: Cannot find autosetup" 1>&2
+ exit 1
+fi
WRAPPER="$0"; export WRAPPER; exec "`"$dirA/autosetup-find-tclsh"`" \
"$dirA/autosetup" --teaish-extension-dir="$dir0" \
"$@"
diff --git a/autosetup/proj.tcl b/autosetup/proj.tcl
index 5cebc0472..fca85e9ad 100644
--- a/autosetup/proj.tcl
+++ b/autosetup/proj.tcl
@@ -1604,7 +1604,7 @@ proc proj-tclConfig-sh-to-autosetup {tclConfigSh} {
#
# Similar modifications may be made for --mandir.
#
-# Returns 1 if it modifies the environment, else 0.
+# Returns >0 if it modifies the environment, else 0.
#
proc proj-tweak-default-env-dirs {} {
set rc 0
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
index 5a5063748..6345352c5 100644
--- a/ext/fts5/fts5_index.c
+++ b/ext/fts5/fts5_index.c
@@ -1986,9 +1986,9 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
- const int nTomb = pIter->pSeg->nPgTombstone;
+ const i64 nTomb = (i64)pIter->pSeg->nPgTombstone;
if( nTomb>0 ){
- int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
+ i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
Fts5TombstoneArray *pNew;
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
@@ -6822,10 +6822,13 @@ int sqlite3Fts5IndexRollback(Fts5Index *p){
*/
int sqlite3Fts5IndexReinit(Fts5Index *p){
Fts5Structure *pTmp;
- u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
+ union {
+ Fts5Structure sFts;
+ u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
+ } uFts;
fts5StructureInvalidate(p);
fts5IndexDiscardData(p);
- pTmp = (Fts5Structure*)tmpSpace;
+ pTmp = &uFts.sFts;
memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
if( p->pConfig->bContentlessDelete ){
pTmp->nOriginCntr = 1;
@@ -8286,19 +8289,27 @@ static int fts5TestUtf8(const char *z, int n){
/*
** This function is also purely an internal test. It does not contribute to
** FTS functionality, or even the integrity-check, in any way.
+**
+** This function sets output variable (*pbFail) to true if the test fails. Or
+** leaves it unchanged if the test succeeds.
*/
static void fts5TestTerm(
Fts5Index *p,
Fts5Buffer *pPrev, /* Previous term */
const char *z, int n, /* Possibly new term to test */
u64 expected,
- u64 *pCksum
+ u64 *pCksum,
+ int *pbFail
){
int rc = p->rc;
if( pPrev->n==0 ){
fts5BufferSet(&rc, pPrev, n, (const u8*)z);
}else
- if( rc==SQLITE_OK && (pPrev->n!=n || memcmp(pPrev->p, z, n)) ){
+ if( *pbFail==0
+ && rc==SQLITE_OK
+ && (pPrev->n!=n || memcmp(pPrev->p, z, n))
+ && (p->pHash==0 || p->pHash->nEntry==0)
+ ){
u64 cksum3 = *pCksum;
const char *zTerm = (const char*)&pPrev->p[1]; /* term sans prefix-byte */
int nTerm = pPrev->n-1; /* Size of zTerm in bytes */
@@ -8348,7 +8359,7 @@ static void fts5TestTerm(
fts5BufferSet(&rc, pPrev, n, (const u8*)z);
if( rc==SQLITE_OK && cksum3!=expected ){
- rc = FTS5_CORRUPT;
+ *pbFail = 1;
}
*pCksum = cksum3;
}
@@ -8357,7 +8368,7 @@ static void fts5TestTerm(
#else
# define fts5TestDlidxReverse(x,y,z)
-# define fts5TestTerm(u,v,w,x,y,z)
+# define fts5TestTerm(t,u,v,w,x,y,z)
#endif
/*
@@ -8615,6 +8626,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
/* Used by extra internal tests only run if NDEBUG is not defined */
u64 cksum3 = 0; /* Checksum based on contents of indexes */
Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
+ int bTestFail = 0;
#endif
const int flags = FTS5INDEX_QUERY_NOOUTPUT;
@@ -8657,7 +8669,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
char *z = (char*)fts5MultiIterTerm(pIter, &n);
/* If this is a new term, query for it. Update cksum3 with the results. */
- fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
+ fts5TestTerm(p, &term, z, n, cksum2, &cksum3, &bTestFail);
if( p->rc ) break;
if( eDetail==FTS5_DETAIL_NONE ){
@@ -8675,7 +8687,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
}
}
}
- fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
+ fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3, &bTestFail);
fts5MultiIterFree(pIter);
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ){
@@ -8684,11 +8696,17 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
"fts5: checksum mismatch for table \"%s\"", p->pConfig->zName
);
}
-
- fts5StructureRelease(pStruct);
#ifdef SQLITE_DEBUG
+ /* In SQLITE_DEBUG builds, expensive extra checks were run as part of
+ ** the integrity-check above. If no other errors were detected, but one
+ ** of these tests failed, set the result to SQLITE_CORRUPT_VTAB here. */
+ if( p->rc==SQLITE_OK && bTestFail ){
+ p->rc = FTS5_CORRUPT;
+ }
fts5BufferFree(&term);
#endif
+
+ fts5StructureRelease(pStruct);
fts5BufferFree(&poslist);
return fts5IndexReturn(p);
}
diff --git a/ext/fts5/test/fts5corrupt3.test b/ext/fts5/test/fts5corrupt3.test
index 66acf07ee..eab4c3c91 100644
--- a/ext/fts5/test/fts5corrupt3.test
+++ b/ext/fts5/test/fts5corrupt3.test
@@ -6644,7 +6644,7 @@ do_test 48.0 {
do_catchsql_test 48.1 {
INSERT INTO t1(t1) VALUES('integrity-check');
-} {1 {database disk image is malformed}}
+} {1 {fts5: corruption on page 1, segment 1, table "t1"}}
#--------------------------------------------------------------------------
reset_db
@@ -10106,7 +10106,7 @@ do_test 68.0 {
do_catchsql_test 68.1 {
PRAGMA reverse_unordered_selects=ON;
INSERT INTO t1(t1) SELECT x FROM t2;
-} {1 {database disk image is malformed}}
+} {1 {fts5: corruption on page 1, segment 1, table "t1"}}
#-------------------------------------------------------------------------
reset_db
@@ -16126,4 +16126,3 @@ do_catchsql_test 83.1 {
sqlite3_fts5_may_be_corrupt 0
finish_test
-
diff --git a/ext/fts5/test/fts5corrupt8.test b/ext/fts5/test/fts5corrupt8.test
index a43bbaa03..471a1b0e3 100644
--- a/ext/fts5/test/fts5corrupt8.test
+++ b/ext/fts5/test/fts5corrupt8.test
@@ -90,5 +90,58 @@ do_execsql_test 3.7 {
SELECT * FROM sqlite_schema
}
+#-------------------------------------------------------------------------
+reset_db
+
+proc hex_to_blob {hex} {
+ binary encode hex $hex
+}
+db func hex_to_blob hex_to_blob
+
+do_execsql_test 4.0 {
+ CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
+ BEGIN;
+ INSERT INTO x1(rowid, x) VALUES(1, 'a b c d e f g h');
+ INSERT INTO x1(rowid, x) VALUES(2, 'a b c d e f g h');
+ COMMIT;
+ DELETE FROM x1 WHERE rowid=1;
+}
+
+do_execsql_test 4.1 {
+ SELECT hex(block) FROM x1_data WHERE id=10
+} {
+ 00000000FF00000101010200010101010101010102
+}
+
+do_execsql_test 4.2.1 {
+ UPDATE x1_data SET block=
+ X'00000000FF00000101010200010101010101819C9B95A8000102'
+ WHERE id=10;
+}
+
+do_catchsql_test 4.2.2 {
+ SELECT * FROM x1('c d e');
+} {1 {out of memory}}
+
+do_execsql_test 4.3.1 {
+ UPDATE x1_data SET block=
+ X'00000000FF000001010102000101010101019282AFF9A0000102'
+ WHERE id=10;
+}
+
+do_catchsql_test 4.3.2 {
+ SELECT * FROM x1('c d e');
+} {1 {out of memory}}
+
+do_execsql_test 4.4.1 {
+ UPDATE x1_data SET block=
+ X'00000000FF000001010102000101010101018181808080130102'
+ WHERE id=10;
+}
+
+do_catchsql_test 4.3.2 {
+ SELECT * FROM x1('c d e');
+} {1 {out of memory}}
+
finish_test
diff --git a/ext/fts5/test/fts5integrity.test b/ext/fts5/test/fts5integrity.test
index 5c4002180..4bf120c44 100644
--- a/ext/fts5/test/fts5integrity.test
+++ b/ext/fts5/test/fts5integrity.test
@@ -37,6 +37,12 @@ do_execsql_test 2.1 {
INSERT INTO yy(yy) VALUES('integrity-check');
}
+db close
+sqlite3 db test.db
+do_execsql_test 2.1 {
+ INSERT INTO yy(yy) VALUES('integrity-check');
+}
+
#--------------------------------------------------------------------
#
do_execsql_test 3.0 {
diff --git a/ext/fts5/test/fts5leftjoin.test b/ext/fts5/test/fts5leftjoin.test
index 4ef6a8961..69a172bd4 100644
--- a/ext/fts5/test/fts5leftjoin.test
+++ b/ext/fts5/test/fts5leftjoin.test
@@ -40,4 +40,53 @@ do_execsql_test 1.2 {
SELECT * FROM t1 LEFT JOIN vt ON (vt MATCH 'abc')
} {1 abc 2 abc}
+
+do_execsql_test 1.3 {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(14);
+}
+
+do_execsql_test 1.4 {
+ SELECT * FROM vt LEFT JOIN t1 ON vt.rowid = 1;
+} {
+ abc 14
+ xyz {}
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE t0 USING fts5(a,b);
+ INSERT INTO t0(a,b)VALUES(1,0);
+ CREATE TABLE t1(x);
+}
+
+do_execsql_test 2.1 {
+ SELECT * FROM t0 LEFT JOIN t1;
+} {1 0 {}}
+
+breakpoint
+do_catchsql_test 2.2 {
+ SELECT * FROM t0 LEFT JOIN t1 ON t0.b MATCH '1';
+} {1 {no query solution}}
+
+do_execsql_test 2.3 {
+ SELECT * FROM t0 LEFT JOIN t1 ON +b MATCH '1';
+} {1 0 {}}
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 3.0 {
+ CREATE VIRTUAL TABLE t0 USING fts5(c0, c1);
+ INSERT INTO t0(c0,c1) VALUES (1,0);
+}
+
+do_catchsql_test 3.1 {
+ SELECT * FROM t0
+ LEFT JOIN ( SELECT 0 AS col_0 )
+ ON ((((t0.c1 MATCH '1')AND(CASE WHEN t0.c0 THEN CAST(t0.c1 AS INTEGER) ELSE 1 END))));
+} {1 {no query solution}}
+
+
finish_test
diff --git a/ext/misc/vtablog.c b/ext/misc/vtablog.c
index e8e29a86e..44acc32e6 100644
--- a/ext/misc/vtablog.c
+++ b/ext/misc/vtablog.c
@@ -14,6 +14,13 @@
** on stdout when its key interfaces are called. This is intended for
** interactive analysis and debugging of virtual table interfaces.
**
+** To build this extension as a separately loaded shared library or
+** DLL, use compiler command-lines similar to the following:
+**
+** (linux) gcc -fPIC -shared vtablog.c -o vtablog.so
+** (mac) clang -fPIC -dynamiclib vtablog.c -o vtablog.dylib
+** (windows) cl vtablog.c -link -dll -out:vtablog.dll
+**
** Usage example:
**
** .load ./vtablog
diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c
index f7d3bda01..fb35bc10e 100644
--- a/ext/rtree/rtree.c
+++ b/ext/rtree/rtree.c
@@ -1135,6 +1135,12 @@ static void resetCursor(RtreeCursor *pCsr){
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
pCsr->pReadAux = pStmt;
+ /* The following will only fail if the previous sqlite3_step() call failed,
+ ** in which case the error has already been caught. This statement never
+ ** encounters an error within an sqlite3_column_xxx() function, as it
+ ** calls sqlite3_column_value(), which does not use malloc(). So it is safe
+ ** to ignore the error code here. */
+ sqlite3_reset(pStmt);
}
/*
diff --git a/ext/rtree/rtreeH.test b/ext/rtree/rtreeH.test
index e26107f07..79bf9808d 100644
--- a/ext/rtree/rtreeH.test
+++ b/ext/rtree/rtreeH.test
@@ -99,5 +99,24 @@ do_execsql_test rtreeH-300 {
ORDER BY id;
} {box-48,48 box-49,48 box-48,49 xbox-49,49}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test rtreeH-300 {
+ CREATE TABLE t0(c0);
+ INSERT INTO t0(c0) VALUES (NULL);
+ INSERT INTO t0(c0) VALUES (1);
+ CREATE VIRTUAL TABLE t1 USING rtree(c0, c1, c2, +c3 BLOB );
+ INSERT INTO t1(c2, c3, c0) VALUES (1, 2, 1);
+}
+
+do_execsql_test rtreeH-310 {
+ SELECT 1 FROM t1 WHERE t1.c3;
+} {1}
+
+do_execsql_test rtreeH-320 {
+ SELECT * FROM t0 WHERE NOT EXISTS (
+ SELECT 1 FROM t1 WHERE t1.c3 OR t0.c0 ISNULL
+ );
+} {}
finish_test
diff --git a/ext/session/sessionI.test b/ext/session/sessionI.test
new file mode 100644
index 000000000..2012f24dc
--- /dev/null
+++ b/ext/session/sessionI.test
@@ -0,0 +1,88 @@
+# 2015 July 14
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source [file join [file dirname [info script]] session_common.tcl]
+source $testdir/tester.tcl
+ifcapable !session {finish_test; return}
+
+set testprefix sessionI
+
+forcedelete test.db2
+sqlite3 db2 test.db2
+
+do_test 1.0 {
+ do_common_sql {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
+ }
+} {}
+
+set C [changeset_from_sql {
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1 VALUES(4, 'four');
+ INSERT INTO t1 VALUES(5, 'five');
+ INSERT INTO t1 VALUES(6, 'six');
+}]
+
+do_execsql_test 1.1 {
+ SELECT * FROM t1
+} {
+ 1 one 2 two 3 three 4 four 5 five 6 six
+}
+
+proc xFilter {data} {
+ foreach {op tname flag pk old new} $data {}
+ if {$op=="INSERT"} {
+ set ipk [lindex $new 1]
+ return [expr $ipk % 2]
+ }
+ return 1
+}
+proc xConflict {args} {
+}
+
+sqlite3changeset_apply_v3 db2 $C xConflict xFilter
+
+do_execsql_test -db db2 1.2 {
+ SELECT * FROM t1
+} {
+ 1 one 3 three 5 five
+}
+
+do_execsql_test -db db2 1.3 {
+ DELETE FROM t1
+}
+sqlite3changeset_apply_v3 db2 $C xConflict
+
+do_execsql_test -db db2 1.4 {
+ SELECT * FROM t1
+} {
+ 1 one 2 two 3 three 4 four 5 five 6 six
+}
+
+proc xFilter2 {data} {
+ return 0
+}
+do_execsql_test -db db2 1.5 {
+ DELETE FROM t1
+}
+sqlite3changeset_apply_v3 db2 $C xConflict xFilter2
+do_execsql_test -db db2 1.6 {
+ SELECT * FROM t1
+} { }
+
+finish_test
diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c
index 175cacbe8..df40fdc1c 100644
--- a/ext/session/sqlite3session.c
+++ b/ext/session/sqlite3session.c
@@ -5182,6 +5182,10 @@ static int sessionChangesetApply(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
+ int(*xFilterIter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ sqlite3_changeset_iter *p
+ ),
int(*xConflict)(
void *pCtx, /* Copy of fifth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
@@ -5322,6 +5326,9 @@ static int sessionChangesetApply(
** next change. A log message has already been issued. */
if( schemaMismatch ) continue;
+ /* If this is a call to apply_v3(), invoke xFilterIter here. */
+ if( xFilterIter && 0==xFilterIter(pCtx, pIter) ) continue;
+
rc = sessionApplyOneWithRetry(db, pIter, &sApply, xConflict, pCtx);
}
@@ -5390,17 +5397,64 @@ static int sessionChangesetApply(
}
/*
-** Apply the changeset passed via pChangeset/nChangeset to the main
-** database attached to handle "db".
+** This function is called by all six sqlite3changeset_apply() variants:
+**
+** + sqlite3changeset_apply()
+** + sqlite3changeset_apply_v2()
+** + sqlite3changeset_apply_v3()
+** + sqlite3changeset_apply_strm()
+** + sqlite3changeset_apply_strm_v2()
+** + sqlite3changeset_apply_strm_v3()
+**
+** Arguments passed to this function are as follows:
+**
+** db:
+** Database handle to apply changeset to main database of.
+**
+** nChangeset/pChangeset:
+** These are both passed zero for the streaming variants. For the normal
+** apply() functions, these are passed the size of and the buffer containing
+** the changeset, respectively.
+**
+** xInput/pIn:
+** These are both passed zero for the normal variants. For the streaming
+** apply() functions, these are passed the input callback and context
+** pointer, respectively.
+**
+** xFilter:
+** The filter function as passed to apply() or apply_v2() (to filter by
+** table name), if any. This is always NULL for apply_v3() calls.
+**
+** xFilterIter:
+** The filter function as passed to apply_v3(), if any.
+**
+** xConflict:
+** The conflict handler callback (must not be NULL).
+**
+** pCtx:
+** The context pointer passed to the xFilter and xConflict handler callbacks.
+**
+** ppRebase, pnRebase:
+** Zero for apply(). The rebase changeset output pointers, if any, for
+** apply_v2() and apply_v3().
+**
+** flags:
+** Zero for apply(). The flags parameter for apply_v2() and apply_v3().
*/
-int sqlite3changeset_apply_v2(
+static int sessionChangesetApplyV23(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
+ int(*xFilterIter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ sqlite3_changeset_iter *p /* Handle describing current change */
+ ),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
@@ -5411,19 +5465,75 @@ int sqlite3changeset_apply_v2(
int flags
){
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
- int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
-
+ int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ int rc = sessionChangesetStart(
+ &pIter, xInput, pIn, nChangeset, pChangeset, bInverse, 1
+ );
if( rc==SQLITE_OK ){
- rc = sessionChangesetApply(
- db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
+ rc = sessionChangesetApply(db, pIter,
+ xFilter, xFilterIter, xConflict, pCtx, ppRebase, pnRebase, flags
);
}
-
return rc;
}
/*
+** Apply the changeset passed via pChangeset/nChangeset to the main
+** database attached to handle "db".
+*/
+int sqlite3changeset_apply_v2(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx, /* First argument passed to xConflict */
+ void **ppRebase, int *pnRebase,
+ int flags
+){
+ return sessionChangesetApplyV23(db,
+ nChangeset, pChangeset, 0, 0,
+ xFilter, 0, xConflict, pCtx,
+ ppRebase, pnRebase, flags
+ );
+}
+
+/*
+** Apply the changeset passed via pChangeset/nChangeset to the main
+** database attached to handle "db".
+*/
+int sqlite3changeset_apply_v3(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ sqlite3_changeset_iter *p /* Handle describing current change */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx, /* First argument passed to xConflict */
+ void **ppRebase, int *pnRebase,
+ int flags
+){
+ return sessionChangesetApplyV23(db,
+ nChangeset, pChangeset, 0, 0,
+ 0, xFilter, xConflict, pCtx,
+ ppRebase, pnRebase, flags
+ );
+}
+
+/*
** Apply the changeset passed via pChangeset/nChangeset to the main database
** attached to handle "db". Invoke the supplied conflict handler callback
** to resolve any conflicts encountered while applying the change.
@@ -5443,8 +5553,10 @@ int sqlite3changeset_apply(
),
void *pCtx /* First argument passed to xConflict */
){
- return sqlite3changeset_apply_v2(
- db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0
+ return sessionChangesetApplyV23(db,
+ nChangeset, pChangeset, 0, 0,
+ xFilter, 0, xConflict, pCtx,
+ 0, 0, 0
);
}
@@ -5453,6 +5565,29 @@ int sqlite3changeset_apply(
** attached to handle "db". Invoke the supplied conflict handler callback
** to resolve any conflicts encountered while applying the change.
*/
+int sqlite3changeset_apply_v3_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ sqlite3_changeset_iter *p
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx, /* First argument passed to xConflict */
+ void **ppRebase, int *pnRebase,
+ int flags
+){
+ return sessionChangesetApplyV23(db,
+ 0, 0, xInput, pIn,
+ 0, xFilter, xConflict, pCtx,
+ ppRebase, pnRebase, flags
+ );
+}
int sqlite3changeset_apply_v2_strm(
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
@@ -5470,15 +5605,11 @@ int sqlite3changeset_apply_v2_strm(
void **ppRebase, int *pnRebase,
int flags
){
- sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
- int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1);
- if( rc==SQLITE_OK ){
- rc = sessionChangesetApply(
- db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
- );
- }
- return rc;
+ return sessionChangesetApplyV23(db,
+ 0, 0, xInput, pIn,
+ xFilter, 0, xConflict, pCtx,
+ ppRebase, pnRebase, flags
+ );
}
int sqlite3changeset_apply_strm(
sqlite3 *db, /* Apply change to "main" db of this handle */
@@ -5495,8 +5626,10 @@ int sqlite3changeset_apply_strm(
),
void *pCtx /* First argument passed to xConflict */
){
- return sqlite3changeset_apply_v2_strm(
- db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0
+ return sessionChangesetApplyV23(db,
+ 0, 0, xInput, pIn,
+ xFilter, 0, xConflict, pCtx,
+ 0, 0, 0
);
}
diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h
index 4dff5ce87..a3b6987b9 100644
--- a/ext/session/sqlite3session.h
+++ b/ext/session/sqlite3session.h
@@ -1115,13 +1115,22 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
** the changeset passed via the second and third arguments.
**
** The fourth argument (xFilter) passed to these functions is the "filter
-** callback". If it is not NULL, then for each table affected by at least one
-** change in the changeset, the filter callback is invoked with
-** the table name as the second argument, and a copy of the context pointer
-** passed as the sixth argument as the first. If the "filter callback"
-** returns zero, then no attempt is made to apply any changes to the table.
-** Otherwise, if the return value is non-zero or the xFilter argument to
-** is NULL, all changes related to the table are attempted.
+** callback". This may be passed NULL, in which case all changes in the
+** changeset are applied to the database. For sqlite3changeset_apply() and
+** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
+** for each table affected by at least one change in the changeset. In this
+** case the table name is passed as the second argument, and a copy of
+** the context pointer passed as the sixth argument to apply() or apply_v2()
+** as the first. If the "filter callback" returns zero, then no attempt is
+** made to apply any changes to the table. Otherwise, if the return value is
+** non-zero, all changes related to the table are attempted.
+**
+** For sqlite3_changeset_apply_v3(), the xFilter callback is invoked once
+** per change. The second argument in this case is an sqlite3_changeset_iter
+** that may be queried using the usual APIs for the details of the current
+** change. If the "filter callback" returns zero in this case, then no attempt
+** is made to apply the current change. If it returns non-zero, the change
+** is applied.
**
** For each table that is not excluded by the filter callback, this function
** tests that the target database contains a compatible table. A table is
@@ -1142,11 +1151,11 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
** one such warning is issued for each table in the changeset.
**
** For each change for which there is a compatible table, an attempt is made
-** to modify the table contents according to the UPDATE, INSERT or DELETE
-** change. If a change cannot be applied cleanly, the conflict handler
-** function passed as the fifth argument to sqlite3changeset_apply() may be
-** invoked. A description of exactly when the conflict handler is invoked for
-** each type of change is below.
+** to modify the table contents according to each UPDATE, INSERT or DELETE
+** change that is not excluded by a filter callback. If a change cannot be
+** applied cleanly, the conflict handler function passed as the fifth argument
+** to sqlite3changeset_apply() may be invoked. A description of exactly when
+** the conflict handler is invoked for each type of change is below.
**
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
** of passing anything other than a valid function pointer as the xConflict
@@ -1297,6 +1306,23 @@ int sqlite3changeset_apply_v2(
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
int flags /* SESSION_CHANGESETAPPLY_* flags */
);
+int sqlite3changeset_apply_v3(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ sqlite3_changeset_iter *p /* Handle describing change */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx, /* First argument passed to xConflict */
+ void **ppRebase, int *pnRebase, /* OUT: Rebase data */
+ int flags /* SESSION_CHANGESETAPPLY_* flags */
+);
/*
** CAPI3REF: Flags for sqlite3changeset_apply_v2
@@ -1716,6 +1742,23 @@ int sqlite3changeset_apply_v2_strm(
void **ppRebase, int *pnRebase,
int flags
);
+int sqlite3changeset_apply_v3_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ sqlite3_changeset_iter *p
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx, /* First argument passed to xConflict */
+ void **ppRebase, int *pnRebase,
+ int flags
+);
int sqlite3changeset_concat_strm(
int (*xInputA)(void *pIn, void *pData, int *pnData),
void *pInA,
diff --git a/ext/session/test_session.c b/ext/session/test_session.c
index f28604abc..6ad5b3774 100644
--- a/ext/session/test_session.c
+++ b/ext/session/test_session.c
@@ -520,6 +520,65 @@ static int test_obj_eq_string(Tcl_Obj *p, const char *z){
return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
}
+static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
+ Tcl_Obj *pVar = 0;
+ int nCol; /* Number of columns in table */
+ int nCol2; /* Number of columns in table */
+ int op; /* SQLITE_INSERT, UPDATE or DELETE */
+ const char *zTab; /* Name of table change applies to */
+ Tcl_Obj *pOld; /* Vector of old.* values */
+ Tcl_Obj *pNew; /* Vector of new.* values */
+ int bIndirect;
+
+ char *zPK;
+ unsigned char *abPK;
+ int i;
+
+ sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
+ pVar = Tcl_NewObj();
+
+ Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
+ op==SQLITE_INSERT ? "INSERT" :
+ op==SQLITE_UPDATE ? "UPDATE" :
+ "DELETE", -1
+ ));
+
+ Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
+ Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
+
+ zPK = ckalloc(nCol+1);
+ memset(zPK, 0, nCol+1);
+ sqlite3changeset_pk(pIter, &abPK, &nCol2);
+ assert( nCol==nCol2 );
+ for(i=0; i<nCol; i++){
+ zPK[i] = (abPK[i] ? 'X' : '.');
+ }
+ Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
+ ckfree(zPK);
+
+ pOld = Tcl_NewObj();
+ if( op!=SQLITE_INSERT ){
+ for(i=0; i<nCol; i++){
+ sqlite3_value *pVal;
+ sqlite3changeset_old(pIter, i, &pVal);
+ test_append_value(pOld, pVal);
+ }
+ }
+ pNew = Tcl_NewObj();
+ if( op!=SQLITE_DELETE ){
+ for(i=0; i<nCol; i++){
+ sqlite3_value *pVal;
+ sqlite3changeset_new(pIter, i, &pVal);
+ test_append_value(pNew, pVal);
+ }
+ }
+ Tcl_ListObjAppendElement(0, pVar, pOld);
+ Tcl_ListObjAppendElement(0, pVar, pNew);
+
+ return pVar;
+}
+
+
static int test_filter_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
const char *zTab /* Table name */
@@ -543,6 +602,29 @@ static int test_filter_handler(
return res;
}
+static int test_filter_v3_handler(
+ void *pCtx, /* Pointer to TestConflictHandler structure */
+ sqlite3_changeset_iter *pIter
+){
+ TestConflictHandler *p = (TestConflictHandler *)pCtx;
+ int res = 1;
+ Tcl_Obj *pEval = 0;
+ Tcl_Interp *interp = p->interp;
+
+ pEval = Tcl_DuplicateObj(p->pFilterScript);
+ Tcl_IncrRefCount(pEval);
+ Tcl_ListObjAppendElement(0, pEval, testIterData(pIter));
+
+ if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
+ || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
+ ){
+ Tcl_BackgroundError(interp);
+ }
+
+ Tcl_DecrRefCount(pEval);
+ return res;
+}
+
static int test_conflict_handler(
void *pCtx, /* Pointer to TestConflictHandler structure */
int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
@@ -776,7 +858,7 @@ static int testStreamInput(
static int SQLITE_TCLAPI testSqlite3changesetApply(
- int bV2,
+ int iVersion,
void * clientData,
Tcl_Interp *interp,
int objc,
@@ -793,11 +875,13 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
int nRebase = 0;
int flags = 0; /* Flags for apply_v2() */
+ assert( iVersion==1 || iVersion==2 || iVersion==3 );
+
memset(&sStr, 0, sizeof(sStr));
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
/* Check for the -nosavepoint, -invert or -ignorenoop switches */
- if( bV2 ){
+ if( iVersion==2 || iVersion==3 ){
while( objc>1 ){
const char *z1 = Tcl_GetString(objv[1]);
int n = (int)strlen(z1);
@@ -822,7 +906,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
if( objc!=4 && objc!=5 ){
const char *zMsg;
- if( bV2 ){
+ if( iVersion==2 || iVersion==3 ){
zMsg = "?-nosavepoint? ?-inverse? ?-ignorenoop? "
"DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
}else{
@@ -842,30 +926,49 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
ctx.interp = interp;
if( sStr.nStream==0 ){
- if( bV2==0 ){
- rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
- (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
- );
- }else{
- rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
- (objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
- &pRebase, &nRebase, flags
- );
+ switch( iVersion ){
+ case 1:
+ rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
+ (objc==5)?test_filter_handler:0, test_conflict_handler, (void*)&ctx
+ );
+ break;
+ case 2:
+ rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
+ (objc==5)?test_filter_handler:0, test_conflict_handler, (void*)&ctx,
+ &pRebase, &nRebase, flags
+ );
+ break;
+ case 3:
+ rc = sqlite3changeset_apply_v3(db, (int)nChangeset, pChangeset,
+ (objc==5)?test_filter_v3_handler:0, test_conflict_handler,
+ (void*)&ctx, &pRebase, &nRebase, flags
+ );
+ break;
}
}else{
sStr.aData = (unsigned char*)pChangeset;
sStr.nData = (int)nChangeset;
- if( bV2==0 ){
- rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
- (objc==5) ? test_filter_handler : 0,
- test_conflict_handler, (void *)&ctx
- );
- }else{
- rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
- (objc==5) ? test_filter_handler : 0,
- test_conflict_handler, (void *)&ctx,
- &pRebase, &nRebase, flags
- );
+ switch( iVersion ){
+ case 1:
+ rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
+ (objc==5) ? test_filter_handler : 0,
+ test_conflict_handler, (void *)&ctx
+ );
+ break;
+ case 2:
+ rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
+ (objc==5) ? test_filter_handler : 0,
+ test_conflict_handler, (void *)&ctx,
+ &pRebase, &nRebase, flags
+ );
+ break;
+ case 3:
+ rc = sqlite3changeset_apply_v3_strm(db, testStreamInput, (void*)&sStr,
+ (objc==5) ? test_filter_v3_handler : 0,
+ test_conflict_handler, (void *)&ctx,
+ &pRebase, &nRebase, flags
+ );
+ break;
}
}
@@ -873,7 +976,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
return test_session_error(interp, rc, 0);
}else{
Tcl_ResetResult(interp);
- if( bV2 && pRebase ){
+ if( (iVersion==2 || iVersion==3) && pRebase ){
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
}
}
@@ -890,7 +993,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
int objc,
Tcl_Obj *CONST objv[]
){
- return testSqlite3changesetApply(0, clientData, interp, objc, objv);
+ return testSqlite3changesetApply(1, clientData, interp, objc, objv);
}
/*
** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
@@ -901,7 +1004,18 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
int objc,
Tcl_Obj *CONST objv[]
){
- return testSqlite3changesetApply(1, clientData, interp, objc, objv);
+ return testSqlite3changesetApply(2, clientData, interp, objc, objv);
+}
+/*
+** sqlite3changeset_apply_v3 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
+*/
+static int SQLITE_TCLAPI test_sqlite3changeset_apply_v3(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ return testSqlite3changesetApply(3, clientData, interp, objc, objv);
}
/*
@@ -1034,64 +1148,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
return rc;
}
-static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
- Tcl_Obj *pVar = 0;
- int nCol; /* Number of columns in table */
- int nCol2; /* Number of columns in table */
- int op; /* SQLITE_INSERT, UPDATE or DELETE */
- const char *zTab; /* Name of table change applies to */
- Tcl_Obj *pOld; /* Vector of old.* values */
- Tcl_Obj *pNew; /* Vector of new.* values */
- int bIndirect;
-
- char *zPK;
- unsigned char *abPK;
- int i;
-
- sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
- pVar = Tcl_NewObj();
-
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
- op==SQLITE_INSERT ? "INSERT" :
- op==SQLITE_UPDATE ? "UPDATE" :
- "DELETE", -1
- ));
-
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
-
- zPK = ckalloc(nCol+1);
- memset(zPK, 0, nCol+1);
- sqlite3changeset_pk(pIter, &abPK, &nCol2);
- assert( nCol==nCol2 );
- for(i=0; i<nCol; i++){
- zPK[i] = (abPK[i] ? 'X' : '.');
- }
- Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
- ckfree(zPK);
-
- pOld = Tcl_NewObj();
- if( op!=SQLITE_INSERT ){
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_old(pIter, i, &pVal);
- test_append_value(pOld, pVal);
- }
- }
- pNew = Tcl_NewObj();
- if( op!=SQLITE_DELETE ){
- for(i=0; i<nCol; i++){
- sqlite3_value *pVal;
- sqlite3changeset_new(pIter, i, &pVal);
- test_append_value(pNew, pVal);
- }
- }
- Tcl_ListObjAppendElement(0, pVar, pOld);
- Tcl_ListObjAppendElement(0, pVar, pNew);
-
- return pVar;
-}
-
/*
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
*/
@@ -1751,6 +1807,7 @@ int TestSession_Init(Tcl_Interp *interp){
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
{ "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
+ { "sqlite3changeset_apply_v3", test_sqlite3changeset_apply_v3 },
{ "sqlite3changeset_apply_replace_all",
test_sqlite3changeset_apply_replace_all },
{ "sql_exec_changeset", test_sql_exec_changeset },
diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile
index bf1a49111..5cd0aa66a 100644
--- a/ext/wasm/GNUmakefile
+++ b/ext/wasm/GNUmakefile
@@ -39,10 +39,11 @@
########################################################################
default: all
#default: quick
-MAKEFILE := $(lastword $(MAKEFILE_LIST))
-CLEAN_FILES :=
-DISTCLEAN_FILES := config.make
-MAKING_CLEAN := $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
+MAKEFILE = $(lastword $(MAKEFILE_LIST))
+MAKEFILE.fiddle = fiddle.make
+CLEAN_FILES =
+DISTCLEAN_FILES = config.make
+MAKING_CLEAN = $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
.PHONY: clean distclean
clean:
-rm -f $(CLEAN_FILES)
@@ -53,17 +54,17 @@ distclean: clean
########################################################################
# 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
+# This also affects the default optimization level flags. 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
+ 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))
@@ -76,7 +77,7 @@ else
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/;')
+ 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))
####################################################################
@@ -98,7 +99,7 @@ else
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"
+ bin.wasm-strip = echo "not wasm-stripping"
endif
ifeq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS)))
$(info ==============================================================)
@@ -108,7 +109,7 @@ else
endif
endif
# ^^^ end of are-we-MAKING_CLEAN
-maybe-wasm-strip := $(bin.wasm-strip)
+maybe-wasm-strip = $(bin.wasm-strip)
########################################################################
# JS_BUILD_NAMES exists for documentation purposes only. It enumerates
@@ -118,7 +119,7 @@ maybe-wasm-strip := $(bin.wasm-strip)
#
# - sqlite3-wasmfs = WASMFS-capable library build
#
-JS_BUILD_NAMES := sqlite3 sqlite3-wasmfs
+JS_BUILD_NAMES = sqlite3 sqlite3-wasmfs
########################################################################
# JS_BUILD_MODES exists for documentation purposes only. It enumerates
@@ -140,31 +141,31 @@ JS_BUILD_NAMES := sqlite3 sqlite3-wasmfs
# that persistent storage (OPFS) is not available in these builds.
# These builds are UNTESTED and UNSUPPORTED!
#
-JS_BUILD_MODES := vanilla esm bunder-friendly node
+JS_BUILD_MODES = vanilla esm bunder-friendly node
########################################################################
# dir.top = the top dir of the canonical build tree, where
# sqlite3.[ch] live.
-dir.top := ../..
+dir.top = ../..
# Maintenance reminder: some Emscripten flags require absolute paths
# but we want relative paths for most stuff simply to reduce
# noise. The $(abspath...) GNU make function can transform relative
# paths to absolute.
-dir.wasm := $(patsubst %/,%,$(dir $(MAKEFILE)))
-dir.api := api
-dir.jacc := jaccwabyt
-dir.common := common
-dir.fiddle := fiddle
-dir.fiddle-debug := fiddle-debug
-dir.tool := $(dir.top)/tool
+dir.wasm = $(patsubst %/,%,$(dir $(MAKEFILE)))
+dir.api = api
+dir.jacc = jaccwabyt
+dir.common = common
+dir.fiddle = fiddle
+dir.fiddle-debug = fiddle-debug
+dir.tool = $(dir.top)/tool
# dir.dout = output dir for deliverables
-dir.dout := $(dir.wasm)/jswasm
+dir.dout = $(dir.wasm)/jswasm
# dir.tmp = output dir for intermediary build files, as opposed to
# end-user deliverables.
-dir.tmp := $(dir.wasm)/bld
-dir.wasmfs := $(dir.dout)
+dir.tmp = $(dir.wasm)/bld
+dir.wasmfs = $(dir.dout)
-MKDIR.bld := $(dir.tmp)
+MKDIR.bld = $(dir.tmp)
$(MKDIR.bld):
@mkdir -p $@ $(dir.dout)
@@ -188,17 +189,17 @@ CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ $(dir.fiddle)/*~
# $(sqlite3.canonical.c) must point to the sqlite3.c in
# the sqlite3 canonical source tree, as that source file
# is required for certain utility and test code.
-sqlite3.canonical.c := $(dir.top)/sqlite3.c
+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
+sqlite3.h = $(dir.top)/sqlite3.h
ifeq (1,$(MAKING_CLEAN))
- SQLITE_C_IS_SEE := 0
+ SQLITE_C_IS_SEE = 0
else
ifeq (,$(shell grep sqlite3_activate_see $(sqlite3.c)))
- SQLITE_C_IS_SEE := 0
+ SQLITE_C_IS_SEE = 0
else
- SQLITE_C_IS_SEE := 1
+ SQLITE_C_IS_SEE = 1
$(info This is an SEE build)
endif
endif
@@ -216,19 +217,19 @@ $(sqlite3.c): $(sqlite3.h)
# barebones=1 disables all "extraneous" stuff from sqlite3-wasm.c, the
# goal being to create a WASM file with only the core APIs.
ifeq (1,$(barebones))
- wasm-bare-bones := 1
+ wasm-bare-bones = 1
$(info ==============================================================)
$(info == This is a bare-bones build. It trades away features for)
$(info == a smaller .wasm file.)
$(info ==============================================================)
else
- wasm-bare-bones := 0
+ wasm-bare-bones = 0
endif
# undefine barebones # relatively new gmake feature, not ubiquitous
# Common options for building sqlite3-wasm.c and speedtest1.c.
# Explicit ENABLEs...
-SQLITE_OPT.common := \
+SQLITE_OPT.common = \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
@@ -249,7 +250,7 @@ SQLITE_OPT.common := \
SQLITE_OPT.common += -DSQLITE_WASM_ENABLE_C_TESTS
# Extra flags for full-featured builds...
-SQLITE_OPT.full-featured := \
+SQLITE_OPT.full-featured = \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_ENABLE_DBSTAT_VTAB \
@@ -265,12 +266,12 @@ SQLITE_OPT.full-featured := \
ifeq (0,$(wasm-bare-bones))
# The so-called canonical build is full-featured:
- SQLITE_OPT := \
+ SQLITE_OPT = \
$(SQLITE_OPT.common) \
$(SQLITE_OPT.full-featured)
else
# The so-called bare-bones build is exactly that:
- SQLITE_OPT := \
+ SQLITE_OPT = \
$(SQLITE_OPT.common) \
-DSQLITE_WASM_BARE_BONES
# SQLITE_WASM_BARE_BONES tells sqlite3-wasm.c to explicitly omit
@@ -345,10 +346,10 @@ endif
# See example_extra_init.c for an example implementation.
########################################################################
sqlite3_wasm_extra_init.c ?= $(wildcard sqlite3_wasm_extra_init.c)
-cflags.wasm_extra_init :=
+cflags.wasm_extra_init =
ifneq (,$(sqlite3_wasm_extra_init.c))
$(info Enabling SQLITE_EXTRA_INIT via $(sqlite3_wasm_extra_init.c).)
- cflags.wasm_extra_init := -DSQLITE_WASM_EXTRA_INIT
+ cflags.wasm_extra_init = -DSQLITE_WASM_EXTRA_INIT
endif
#########################################################################
@@ -362,7 +363,7 @@ endif
# end result is that the generated JS files may have static version
# info from $(bin.version-info) which differ from their runtime-emitted
# version info (e.g. from sqlite3_libversion()).
-bin.version-info := $(dir.top)/version-info
+bin.version-info = $(dir.top)/version-info
.NOTPARALLEL: $(bin.version-info)
$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
$(MAKE) -C $(dir.top) version-info
@@ -373,7 +374,7 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
# don't need for all builds. That app's -k flag is of particular
# importance here, as it allows us to retain the opening comment
# block(s), which contain the license header and version info.
-bin.stripccomments := $(dir.tool)/stripccomments
+bin.stripccomments = $(dir.tool)/stripccomments
$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE)
$(CC) -o $@ $<
DISTCLEAN_FILES += $(bin.stripccomments)
@@ -410,7 +411,7 @@ DISTCLEAN_FILES += $(bin.stripccomments)
#
# -D... flags which should be included in all invocations should be
# appended to $(SQLITE.CALL.C-PP.FILTER.global).
-bin.c-pp := ./c-pp
+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) \
-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \
@@ -429,13 +430,13 @@ define SQLITE.CALL.C-PP.FILTER
$(2): $(1) $$(MAKEFILE_LIST) $$(bin.c-pp)
@mkdir -p $$(dir $$@)
$$(bin.c-pp) -f $(1) -o $$@ $(3) $(SQLITE.CALL.C-PP.FILTER.global)
-#CLEAN_FILES += $(2)
+CLEAN_FILES += $(2)
endef
# /end SQLITE.CALL.C-PP.FILTER
########################################################################
# cflags.common = C compiler flags for all builds
-cflags.common := -I. -I$(dir $(sqlite3.c))
+cflags.common = -I. -I$(dir $(sqlite3.c))
# emcc.WASM_BIGINT = 1 for BigInt (C int64) support, else 0. The API
# disables certain features if BigInt is not enabled and such builds
# _are not tested_ on any regular basis.
@@ -456,9 +457,24 @@ else
emcc_opt ?= -Oz
endif
+# Our JS code installs bindings of each sqlite3_...() 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 a bit, by stripping them out. Emscripten-side changes
+# can "break" this, causing this to be a no-op, but the worst that can
+# happen in that case is that it doesn't actually strip anything,
+# leading to slightly larger JS files.
+#
+# This snippet is intended to be used in makefile targets which
+# generate an Emscripten module and where $@ is the module's .js/.mjs
+# file.
+SQLITE.strip-createExportWrapper = \
+ sed -i -e '/^.*= \(_sqlite3\|_fiddle\)[^=]* = createExportWrapper/d' $@ || exit; \
+ echo '(Probably) stripped out extraneous createExportWrapper() parts.'
+
# When passing emcc_opt from the CLI, += and re-assignment have no
# effect, so emcc_opt+=-g3 doesn't work. So...
-emcc_opt_full := $(emcc_opt) -g3
+emcc_opt_full = $(emcc_opt) -g3
# ^^^ ALWAYS use -g3. See below for why.
#
# ^^^ -flto improves runtime speed at -O0 considerably but doubles
@@ -489,31 +505,28 @@ emcc_opt_full := $(emcc_opt) -g3
########################################################################
# EXPORTED_FUNCTIONS.* = files for use with Emscripten's
# -sEXPORTED_FUNCTION flag.
-EXPORTED_FUNCTIONS.api.core := $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-core
-EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.core)
+EXPORTED_FUNCTIONS.api.core = $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-core
+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,$(wasm-bare-bones))
EXPORTED_FUNCTIONS.api.in += $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-extras
endif
-EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
+EXPORTED_FUNCTIONS.api = $(dir.tmp)/EXPORTED_FUNCTIONS.api
$(EXPORTED_FUNCTIONS.api): $(MKDIR.bld) $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE)
cat $(EXPORTED_FUNCTIONS.api.in) > $@
########################################################################
# sqlite3-license-version.js = generated JS file with the license
# header and version info.
-sqlite3-license-version.js := $(dir.tmp)/sqlite3-license-version.js
-# sqlite3-license-version-header.js = JS file containing only the
-# license header.
-sqlite3-license-version-header.js := $(dir.api)/sqlite3-license-version-header.js
+sqlite3-license-version.js = $(dir.tmp)/sqlite3-license-version.js
# sqlite3-api-build-version.js = generated JS file which populates the
# sqlite3.version object using $(bin.version-info).
-sqlite3-api-build-version.js := $(dir.tmp)/sqlite3-api-build-version.js
+sqlite3-api-build-version.js = $(dir.tmp)/sqlite3-api-build-version.js
# sqlite3-api.jses = the list of JS files which make up
# $(sqlite3-api.js.in), in the order they need to be assembled.
-sqlite3-api.jses := $(sqlite3-license-version.js)
+sqlite3-api.jses = $(sqlite3-license-version.js)
# sqlite3-api-prologue.js: initial bootstrapping bits:
sqlite3-api.jses += $(dir.api)/sqlite3-api-prologue.js
# whwhasm.js and jaccwabyt.js: Low-level utils, mostly replacing
@@ -547,13 +560,13 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
# SOAP.js is an external API file which is part of our distribution
# but not part of the sqlite3-api.js amalgamation. It's a component of
# the first OPFS VFS and necessarily an external file.
-SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
-SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
+SOAP.js = $(dir.api)/sqlite3-opfs-async-proxy.js
+SOAP.js.bld = $(dir.dout)/$(notdir $(SOAP.js))
#
# $(sqlite3-api.ext.jses) = API-related files which are standalone files,
# not part of the amalgamation.
#
-sqlite3-api.ext.jses := $(SOAP.js.bld)
+sqlite3-api.ext.jses = $(SOAP.js.bld)
$(SOAP.js.bld): $(SOAP.js)
cp $< $@
@@ -573,17 +586,12 @@ $(SOAP.js.bld): $(SOAP.js)
# Sidebar: some of the imports are used soley by the Emscripten glue,
# which the sqlite3 JS code does not rely on.
#
-# We build $(sqlite3-api*.*) "because we can" and because it might be
-# a useful point of experimentation for some clients, but the
-# above-described caveat may well make them unusable for real-life
-# clients.
-#
-# sqlite3-api.js.in = the generated sqlite3-api.js before it gets
+# sqlite3-api.js.in = the amalgamated sqlite3-api.js before it gets
# preprocessed. It contains all of $(sqlite3-api.jses) but none of the
# Emscripten-specific headers and footers.
-sqlite3-api.js.in := $(dir.tmp)/sqlite3-api.c-pp.js
+sqlite3-api.js.in = $(dir.tmp)/sqlite3-api.c-pp.js
$(sqlite3-api.js.in): $(MKDIR.bld) $(sqlite3-api.jses) $(MAKEFILE)
- @echo "Making $@..."
+ @echo "Making $@ ..."
@for i in $(sqlite3-api.jses); do \
echo "/* BEGIN FILE: $$i */"; \
cat $$i; \
@@ -592,7 +600,7 @@ $(sqlite3-api.js.in): $(MKDIR.bld) $(sqlite3-api.jses) $(MAKEFILE)
########################################################################
# emcc flags for .c/.o/.wasm/.js.
-emcc.flags :=
+emcc.flags =
ifeq (1,$(emcc.verbose))
emcc.flags += -v
# -v is _very_ loud but also informative about what it's doing
@@ -601,25 +609,25 @@ endif
########################################################################
# emcc flags for .c/.o.
-emcc.cflags :=
+emcc.cflags =
emcc.cflags += -std=c99 -fPIC
# -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c), primarily
# for variadic macros and snprintf() to implement
-# sqlite3_wasm_enum_json().
+# sqlite3__wasm_enum_json().
emcc.cflags += -I. -I$(dir.top)
########################################################################
# emcc flags specific to building .js/.wasm files...
-emcc.jsflags := -fPIC
+emcc.jsflags = -fPIC
emcc.jsflags += --no-entry
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
emcc.jsflags += -sMODULARIZE
emcc.jsflags += -sDYNAMIC_EXECUTION=0
emcc.jsflags += -sNO_POLYFILL
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
-emcc.exportedRuntimeMethods := \
- -sEXPORTED_RUNTIME_METHODS=wasmMemory,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAP64,HEAPU64
+emcc.exportedRuntimeMethods = \
+ -sEXPORTED_RUNTIME_METHODS=wasmMemory
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
-# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default
+# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default.
emcc.jsflags += $(emcc.exportedRuntimeMethods)
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
emcc.jsflags += -sIMPORTED_MEMORY
@@ -641,10 +649,10 @@ emcc.jsflags += -sSTRICT_JS=0
# tools should be installing, e.g. __syscall_geteuid32
# -sENVIRONMENT values for the various build modes:
-emcc.environment.vanilla := web,worker
-emcc.environment.bundler-friendly := $(emcc.environment.vanilla)
-emcc.environment.esm := $(emcc.environment.vanilla)
-emcc.environment.node := node
+emcc.environment.vanilla = web,worker
+emcc.environment.bundler-friendly = $(emcc.environment.vanilla)
+emcc.environment.esm = $(emcc.environment.vanilla)
+emcc.environment.node = node
# Note that adding ",node" to the list for the other builds causes
# Emscripten to generate code which confuses node: it cannot reliably
# determine whether the build is for a browser or for node.
@@ -666,12 +674,12 @@ emcc.environment.node := node
# supported in all configurations (#21071)."
# https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md
emcc.jsflags += -sALLOW_MEMORY_GROWTH
-emcc.INITIAL_MEMORY.128 := 134217728
-emcc.INITIAL_MEMORY.96 := 100663296
-emcc.INITIAL_MEMORY.64 := 67108864
-emcc.INITIAL_MEMORY.32 := 33554432
-emcc.INITIAL_MEMORY.16 := 16777216
-emcc.INITIAL_MEMORY.8 := 8388608
+emcc.INITIAL_MEMORY.128 = 134217728
+emcc.INITIAL_MEMORY.96 = 100663296
+emcc.INITIAL_MEMORY.64 = 67108864
+emcc.INITIAL_MEMORY.32 = 33554432
+emcc.INITIAL_MEMORY.16 = 16777216
+emcc.INITIAL_MEMORY.8 = 8388608
emcc.INITIAL_MEMORY ?= 16
ifeq (,$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY)))
$(error emcc.INITIAL_MEMORY must be one of: 8, 16, 32, 64, 96, 128 (megabytes))
@@ -704,7 +712,7 @@ emcc.jsflags += -sSTACK_SIZE=512KB
# symbols: we cannot "delete" the Emscripten-defined
# $(sqlite3.js.init-func) from vanilla builds (as opposed to ESM
# builds) because it's declared with "var".
-sqlite3.js.init-func := sqlite3InitModule
+sqlite3.js.init-func = sqlite3InitModule
emcc.jsflags += -sEXPORT_NAME=$(sqlite3.js.init-func)
emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr.
#emcc.jsflags += -sSTRICT # fails due to missing __syscall_...()
@@ -772,17 +780,17 @@ $(sqlite3-api-build-version.js): $(MKDIR.bld) $(bin.version-info) $(MAKEFILE)
#
# Maintenance reminder: there are awk binaries out there which do not
# support -e SCRIPT.
-$(sqlite3-license-version.js): $(MKDIR.bld) $(sqlite3.h) $(sqlite3-license-version-header.js) \
- $(MAKEFILE)
+$(sqlite3-license-version.js): $(MKDIR.bld) $(sqlite3.h) \
+ $(dir.api)/sqlite3-license-version-header.js $(MAKEFILE)
@echo "Making $@..."; { \
- cat $(sqlite3-license-version-header.js); \
+ cat $(dir.api)/sqlite3-license-version-header.js; \
echo '/*'; \
echo '** This code was built from sqlite3 version...'; \
echo "**"; \
awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \
awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \
echo "**"; \
- echo "** Using the Emscripten SDK version $(emcc.version)."; \
+ echo "** with the help of Emscripten SDK version $(emcc.version)."; \
echo '*/'; \
} > $@
@@ -794,9 +802,9 @@ $(sqlite3-license-version.js): $(MKDIR.bld) $(sqlite3.h) $(sqlite3-license-versi
# --post-js injects code which runs after the WASM module is loaded
# and includes the entirety of the library plus some
# Emscripten-specific post-bootstrapping code.
-pre-js.js.in := $(dir.api)/pre-js.c-pp.js
-post-js.js.in := $(dir.tmp)/post-js.c-pp.js
-post-jses.js := \
+pre-js.js.in = $(dir.api)/pre-js.c-pp.js
+post-js.js.in = $(dir.tmp)/post-js.c-pp.js
+post-jses.js = \
$(dir.api)/post-js-header.js \
$(sqlite3-api.js.in) \
$(dir.api)/post-js-footer.js
@@ -812,10 +820,10 @@ $(post-js.js.in): $(MKDIR.bld) $(post-jses.js) $(MAKEFILE)
# Undocumented Emscripten feature: if the target file extension is
# "mjs", it defaults to ES6 module builds:
# https://github.com/emscripten-core/emscripten/issues/14383
-sqlite3.wasm := $(dir.dout)/sqlite3.wasm
-sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
-sqlite3-wasm.cfiles := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
-sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
+sqlite3.wasm = $(dir.dout)/sqlite3.wasm
+sqlite3-wasm.c = $(dir.api)/sqlite3-wasm.c
+sqlite3-wasm.cfiles = $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
+sqlite3-wasmfs.cfiles = $(sqlite3-wasm.cfiles)
# sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter
# (predictably) results in a slightly faster binary. We're close
# enough to the target speed requirements that the 500ms makes a
@@ -860,17 +868,9 @@ if [ x1 = x$(1) ]; then \
fi
endef
-sqlite3-api.js := $(dir.dout)/sqlite3-api.js
-sqlite3.js := $(dir.dout)/sqlite3.js
-sqlite3-api.mjs := $(dir.dout)/sqlite3-api.mjs
-sqlite3.mjs := $(dir.dout)/sqlite3.mjs
-sqlite3-api-bundler-friendly.mjs := $(dir.dout)/sqlite3-api-bundler-friendly.mjs
-sqlite3-bundler-friendly.mjs := $(dir.dout)/sqlite3-bundler-friendly.mjs
-sqlite3-api-node.mjs := $(dir.dout)/sqlite3-api-node.mjs
-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
+sqlite3.js = $(dir.dout)/sqlite3.js
+sqlite3.mjs = $(dir.dout)/sqlite3.mjs
+EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
# The various -D... values used by *.c-pp.js include:
#
@@ -904,24 +904,10 @@ EXPORTED_FUNCTIONS.fiddle := $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
# build time).
$(sqlite3.wasm): $(sqlite3.js)
$(sqlite3.mjs): $(sqlite3.js)
-$(sqlite3-bundler-friendly.mjs): $(sqlite3.mjs)
-$(sqlite3-node.mjs): $(sqlite3.mjs)
+$(dir.dout)/sqlite3-bundler-friendly.mjs: $(sqlite3.mjs)
+$(dir.dout)/sqlite3-node.mjs: $(sqlite3.mjs)
#CLEAN_FILES += $(sqlite3.wasm)
-########################################################################
-# We need separate copies of certain supplementary JS files for the
-# bundler-friendly build. Concretely, any supplemental JS files which
-# themselves use importScripts() or Workers or URL() constructors
-# which refer to other in-tree (m)JS files require a bundler-friendly
-# copy.
-sqlite3-worker1.js.in := $(dir.api)/sqlite3-worker1.c-pp.js
-sqlite3-worker1-promiser.js.in := $(dir.api)/sqlite3-worker1-promiser.c-pp.js
-sqlite3-worker1.js := $(dir.dout)/sqlite3-worker1.js
-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
-
ifneq (1,$(MAKING_CLEAN))
# This block MUST come between the above definitions of
# sqlite3-...js/mjs and the $(eval) calls below this block which use
@@ -934,7 +920,10 @@ ifneq (1,$(MAKING_CLEAN))
# 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
+#
+# Maintenance note: the various $(c-pp.D.XYZ) vars are defined in this
+# step.
+bin.mkwb = ./mkwasmbuilds
$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE)
$(CC) -o $@ $<
DISTCLEAN_FILES += $(bin.mkwb)
@@ -946,45 +935,60 @@ DISTCLEAN_FILES += $(bin.mkwb)
endif
DISTCLEAN_FILES += .wasmbuilds.make
-$(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),\
+########################################################################
+# We need separate copies of certain supplementary JS files for the
+# bundler-friendly build. Concretely, any supplemental JS files which
+# themselves use importScripts() or Workers or URL() constructors
+# which refer to other in-tree (m)JS files require a bundler-friendly
+# copy. Bundler-friendly builds replace certain references to string
+# vars/expressions with string literals, as bundler tools are static
+# code analyzers and cannot cope with the former.
+#
+# Most of what follows is the generation of those copies.
+$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1.c-pp.js,\
+ $(dir.dout)/sqlite3-worker1.js))
+$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1.c-pp.js,\
+ $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs,\
$(c-pp.D.sqlite3-bundler-friendly)))
-$(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),\
+$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\
+ $(dir.dout)/sqlite3-worker1-promiser.js))
+$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\
+ $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js,\
$(c-pp.D.sqlite3-bundler-friendly)))
-$(eval $(call SQLITE.CALL.C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.mjs),\
+$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\
+ $(dir.dout)/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 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 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)
-demo-worker1-promiser.html: $(sqlite3-worker1-promiser.js) demo-worker1-promiser.js
+$(dir.dout)/sqlite3-bundler-friendly.mjs: \
+ $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \
+ $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js
+
+demo-worker1-promiser.html: $(dir.dout)/sqlite3-worker1-promiser.js demo-worker1-promiser.js
demo-worker1-promiser-esm.html: $(sqlite3-worker1-promiser.mjs) demo-worker1-promiser.mjs
all: demo-worker1-promiser.html demo-worker1-promiser-esm.html
sqlite3-api.ext.jses += \
- $(sqlite3-worker1-promiser.mjs) \
- $(sqlite3-worker1-bundler-friendly.mjs) \
- $(sqlite3-worker1.js)
+ $(dir.dout)/sqlite3-worker1-promiser.mjs \
+ $(dir.dout)/sqlite3-worker1-promiser.js \
+ $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \
+ $(dir.dout)/sqlite3-worker1.js
all quick: $(sqlite3-api.ext.jses)
q: quick
########################################################################
# batch-runner.js is part of one of the test apps which reads in SQL
# dumps generated by $(speedtest1) and executes them.
-dir.sql := sql
-speedtest1 := ../../speedtest1
-speedtest1.c := ../../test/speedtest1.c
-speedtest1.sql := $(dir.sql)/speedtest1.sql
-speedtest1.cliflags := --size 10 --big-transactions
+dir.sql = sql
+speedtest1 = ../../speedtest1
+speedtest1.c = ../../test/speedtest1.c
+speedtest1.sql = $(dir.sql)/speedtest1.sql
+speedtest1.cliflags = --size 10 --big-transactions
$(speedtest1):
$(MAKE) -C ../.. speedtest1
$(speedtest1.sql): $(speedtest1) $(MAKEFILE)
@@ -1005,8 +1009,8 @@ batch: batch-runner.list
#
# emcc.speedtest1.common = emcc flags used by multiple builds of speedtest1
# emcc.speedtest1 = emcc flags used by main build of speedtest1
-emcc.speedtest1.common := $(emcc_opt_full)
-emcc.speedtest1 := -I. -I$(dir $(sqlite3.canonical.c))
+emcc.speedtest1.common = $(emcc_opt_full)
+emcc.speedtest1 = -I. -I$(dir $(sqlite3.canonical.c))
emcc.speedtest1 += -sENVIRONMENT=web
emcc.speedtest1 += -sALLOW_MEMORY_GROWTH
emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
@@ -1019,7 +1023,7 @@ emcc.speedtest1.common += -Wno-limited-postlink-optimizations
emcc.speedtest1.common += -Wno-unused-main
# ^^^^ -Wno-unused-main is for emcc 3.1.52+. speedtest1 has a wasm_main() which is
# exported and called by the JS code.
-EXPORTED_FUNCTIONS.speedtest1 := $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
+EXPORTED_FUNCTIONS.speedtest1 = $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1)
emcc.speedtest1.common += -sSTACK_SIZE=512KB
emcc.speedtest1.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1)
emcc.speedtest1.common += $(emcc.exportedRuntimeMethods)
@@ -1028,8 +1032,8 @@ emcc.speedtest1.common += -sDYNAMIC_EXECUTION=0
emcc.speedtest1.common += --minify 0
emcc.speedtest1.common += -sEXPORT_NAME=$(sqlite3.js.init-func)
emcc.speedtest1.common += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
-speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0
-speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1
+speedtest1.exit-runtime0 = -sEXIT_RUNTIME=0
+speedtest1.exit-runtime1 = -sEXIT_RUNTIME=1
# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get
# this error from emscripten:
#
@@ -1050,10 +1054,9 @@ speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1
$(EXPORTED_FUNCTIONS.speedtest1): $(MKDIR.bld) $(EXPORTED_FUNCTIONS.api.core)
@echo "Making $@ ..."
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.core); } > $@
-speedtest1.js := $(dir.dout)/speedtest1.js
-speedtest1.wasm := $(dir.dout)/speedtest1.wasm
-emcc.flags.speedtest1-vanilla := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
-speedtest1.cfiles := $(speedtest1.c) $(sqlite3-wasm.c)
+speedtest1.js = $(dir.dout)/speedtest1.js
+emcc.flags.speedtest1-vanilla = $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
+speedtest1.cfiles = $(speedtest1.c) $(sqlite3-wasm.c)
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
$(pre-post-speedtest1-vanilla.deps) \
$(EXPORTED_FUNCTIONS.speedtest1)
@@ -1067,14 +1070,13 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
-USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \
$(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)
+ @chmod -x $(basename $@).wasm
+ @$(maybe-wasm-strip) $(basename $@).wasm
+ @$(SQLITE.strip-createExportWrapper)
+ @ls -la $@ $(speedtest1.wasm)
speedtest1: $(speedtest1.js)
all: speedtest1
-#CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
# end speedtest1.js
########################################################################
@@ -1099,7 +1101,7 @@ $(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlit
$(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
+# Note that we do not include $(dir.dout)/sqlite3-bundler-friendly.mjs in this
# because bundlers are client-specific.
all quick: tester1
quick: $(sqlite3.js)
@@ -1111,7 +1113,7 @@ quick: $(sqlite3.js)
# painful.
.PHONY: o0 o1 o2 o3 os oz
-emcc-opt-extra :=
+emcc-opt-extra =
#ifeq (1,$(wasm-bare-bones))
#emcc-opt-extra += -flto
# ^^^^ -flto can have a considerably performance boost at -O0 but
@@ -1139,7 +1141,7 @@ oz: clean
# Sub-makes...
# sqlite.org/fiddle application...
-include fiddle.make
+include $(MAKEFILE.fiddle)
# Only add wasmfs if wasmfs.enable=1 or we're running (dist)clean
ifneq (,$(filter wasmfs,$(MAKECMDGOALS)))
@@ -1157,16 +1159,17 @@ ifeq (1,$(wasmfs.enable))
# little benefit.
#
########################################################################
-# Some platforms do not support the WASMFS build. Raspberry Pi OS is one
-# of them. As such platforms are discovered, add their (uname -m) name
-# to PLATFORMS_WITH_NO_WASMFS to exclude the wasmfs build parts.
-PLATFORMS_WITH_NO_WASMFS := aarch64 # add any others here
-THIS_ARCH := $(shell /usr/bin/uname -m)
+# Some platforms do not support the WASMFS build. Raspberry Pi OS is
+# one of them (or was when that comment was initially written). As
+# such platforms are discovered, add their (uname -m) name to
+# PLATFORMS_WITH_NO_WASMFS to exclude the wasmfs build parts.
+PLATFORMS_WITH_NO_WASMFS = aarch64 # add any others here
+THIS_ARCH = $(shell /usr/bin/uname -m)
ifneq (,$(filter $(THIS_ARCH),$(PLATFORMS_WITH_NO_WASMFS)))
$(info This platform does not support the WASMFS build.)
-HAVE_WASMFS := 0
+HAVE_WASMFS = 0
else
-HAVE_WASMFS := 1
+HAVE_WASMFS = 1
include wasmfs.make
endif
endif
@@ -1204,7 +1207,7 @@ update-docs:
echo "Pass wasm.docs.home=/path/to/wasm/docs/checkout or edit this makefile to suit."; \
exit 127
else
-wasm.docs.jswasm := $(wasm.docs.home)/jswasm
+wasm.docs.jswasm = $(wasm.docs.home)/jswasm
update-docs: $(bin.stripccomments) $(sqlite3.js) $(sqlite3.wasm)
@echo "Copying files to the /wasm docs. Be sure to use an -Oz build for this!"
cp $(sqlite3.wasm) $(wasm.docs.jswasm)/.
diff --git a/ext/wasm/api/sqlite3-api-oo1.c-pp.js b/ext/wasm/api/sqlite3-api-oo1.c-pp.js
index 06f916002..8663dcdde 100644
--- a/ext/wasm/api/sqlite3-api-oo1.c-pp.js
+++ b/ext/wasm/api/sqlite3-api-oo1.c-pp.js
@@ -13,7 +13,7 @@
This file contains the so-called OO #1 API wrapper for the sqlite3
WASM build. It requires that sqlite3-api-glue.js has already run
- and it installs its deliverable as globalThis.sqlite3.oo1.
+ and it installs its deliverable as sqlite3.oo1.
*/
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)};
@@ -38,6 +38,21 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/
const __ptrMap = new WeakMap();
/**
+ A Set of oo1.DB or oo1.Stmt objects which are proxies for
+ (sqlite3*) resp. (sqlite3_stmt*) pointers which themselves are
+ owned elsewhere. Objects in this Set do not own their underlying
+ handle and that handle must be guaranteed (by the client) to
+ outlive the proxy. DB.close()/Stmt.finalize() methods will remove
+ the object from this Set _instead_ of closing/finalizing the
+ pointer. These proxies are primarily intended as a way to briefly
+ wrap an (sqlite3[_stmt]*) object as an oo1.DB/Stmt without taking
+ over ownership, to take advantage of simplifies usage compared to
+ the C API while not imposing any change of ownership.
+
+ See DB.wrapHandle() and Stmt.wrapHandle().
+ */
+ const __doesNotOwnHandle = new Set();
+ /**
Map of DB instances to objects, each object being a map of Stmt
wasm pointers to Stmt objects.
*/
@@ -234,73 +249,89 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
};
}
const opt = ctor.normalizeArgs(...args);
- let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags;
- if(('string'!==typeof fn && 'number'!==typeof fn)
- || 'string'!==typeof flagsStr
- || (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){
- sqlite3.config.error("Invalid DB ctor args",opt,arguments);
- toss3("Invalid arguments for DB constructor.");
- }
- let fnJs = ('number'===typeof fn) ? wasm.cstrToJs(fn) : fn;
- const vfsCheck = ctor._name2vfs[fnJs];
- if(vfsCheck){
- vfsName = vfsCheck.vfs;
- fn = fnJs = vfsCheck.filename(fnJs);
- }
- let pDb, oflags = 0;
- if( flagsStr.indexOf('c')>=0 ){
- oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
- }
- if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
- if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
- oflags |= capi.SQLITE_OPEN_EXRESCODE;
- const stack = wasm.pstack.pointer;
- try {
- const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */;
- let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0);
- pDb = wasm.peekPtr(pPtr);
- checkSqlite3Rc(pDb, rc);
- capi.sqlite3_extended_result_codes(pDb, 1);
- if(flagsStr.indexOf('t')>=0){
- capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT,
- __dbTraceToConsole, pDb);
+ //sqlite3.config.debug("DB ctor",opt);
+ let pDb;
+ if( (pDb = opt['sqlite3*']) ){
+ /* This property ^^^^^ is very specifically NOT DOCUMENTED and
+ NOT part of the public API. This is a back door for functions
+ like DB.wrapDbHandle(). */
+ //sqlite3.config.debug("creating proxy db from",opt);
+ if( !opt['sqlite3*:takeOwnership'] ){
+ /* This is object does not own its handle. */
+ __doesNotOwnHandle.add(this);
}
- }catch( e ){
- if( pDb ) capi.sqlite3_close_v2(pDb);
- throw e;
- }finally{
- wasm.pstack.restore(stack);
+ this.filename = capi.sqlite3_db_filename(pDb,'main');
+ }else{
+ let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags;
+ if(('string'!==typeof fn && 'number'!==typeof fn)
+ || 'string'!==typeof flagsStr
+ || (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){
+ sqlite3.config.error("Invalid DB ctor args",opt,arguments);
+ toss3("Invalid arguments for DB constructor.");
+ }
+ let fnJs = ('number'===typeof fn) ? wasm.cstrToJs(fn) : fn;
+ const vfsCheck = ctor._name2vfs[fnJs];
+ if(vfsCheck){
+ vfsName = vfsCheck.vfs;
+ fn = fnJs = vfsCheck.filename(fnJs);
+ }
+ let oflags = 0;
+ if( flagsStr.indexOf('c')>=0 ){
+ oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
+ }
+ if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
+ if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
+ oflags |= capi.SQLITE_OPEN_EXRESCODE;
+ const stack = wasm.pstack.pointer;
+ try {
+ const pPtr = wasm.pstack.allocPtr() /* output (sqlite3**) arg */;
+ let rc = capi.sqlite3_open_v2(fn, pPtr, oflags, vfsName || 0);
+ pDb = wasm.peekPtr(pPtr);
+ checkSqlite3Rc(pDb, rc);
+ capi.sqlite3_extended_result_codes(pDb, 1);
+ if(flagsStr.indexOf('t')>=0){
+ capi.sqlite3_trace_v2(pDb, capi.SQLITE_TRACE_STMT,
+ __dbTraceToConsole, pDb);
+ }
+ }catch( e ){
+ if( pDb ) capi.sqlite3_close_v2(pDb);
+ throw e;
+ }finally{
+ wasm.pstack.restore(stack);
+ }
+ this.filename = fnJs;
}
- this.filename = fnJs;
__ptrMap.set(this, pDb);
__stmtMap.set(this, Object.create(null));
- try{
+ if( !opt['sqlite3*'] ){
+ try{
//#if enable-see
- dbCtorApplySEEKey(this,opt);
+ dbCtorApplySEEKey(this,opt);
//#endif
- // Check for per-VFS post-open SQL/callback...
- const pVfs = capi.sqlite3_js_db_vfs(pDb)
- || toss3("Internal error: cannot get VFS for new db handle.");
- const postInitSql = __vfsPostOpenCallback[pVfs];
- if(postInitSql){
- /**
- Reminder: if this db is encrypted and the client did _not_ pass
- in the key, any init code will fail, causing the ctor to throw.
- We don't actually know whether the db is encrypted, so we cannot
- sensibly apply any heuristics which skip the init code only for
- encrypted databases for which no key has yet been supplied.
- */
- if(postInitSql instanceof Function){
- postInitSql(this, sqlite3);
- }else{
- checkSqlite3Rc(
- pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)
- );
+ // Check for per-VFS post-open SQL/callback...
+ const pVfs = capi.sqlite3_js_db_vfs(pDb)
+ || toss3("Internal error: cannot get VFS for new db handle.");
+ const postInitSql = __vfsPostOpenCallback[pVfs];
+ if(postInitSql){
+ /**
+ Reminder: if this db is encrypted and the client did _not_ pass
+ in the key, any init code will fail, causing the ctor to throw.
+ We don't actually know whether the db is encrypted, so we cannot
+ sensibly apply any heuristics which skip the init code only for
+ encrypted databases for which no key has yet been supplied.
+ */
+ if(postInitSql instanceof Function){
+ postInitSql(this, sqlite3);
+ }else{
+ checkSqlite3Rc(
+ pDb, capi.sqlite3_exec(pDb, postInitSql, 0, 0, 0)
+ );
+ }
}
+ }catch(e){
+ this.close();
+ throw e;
}
- }catch(e){
- this.close();
- throw e;
}
};
@@ -403,7 +434,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
- `vfs`: the VFS fname
//#if enable-see
-
SEE-capable builds optionally support ONE of the following
additional options:
@@ -429,7 +459,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
is supplied and the database is encrypted, execution of the
post-initialization SQL will fail, causing the constructor to
throw.
-
//#endif enable-see
The `filename` and `vfs` arguments may be either JS strings or
@@ -457,8 +486,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
/**
Internal-use enum for mapping JS types to DB-bindable types.
These do not (and need not) line up with the SQLITE_type
- values. All values in this enum must be truthy and distinct
- but they need not be numbers.
+ values. All values in this enum must be truthy and (mostly)
+ distinct but they need not be numbers.
*/
const BindTypes = {
null: 1,
@@ -467,7 +496,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
boolean: 4,
blob: 5
};
- BindTypes['undefined'] == BindTypes.null;
if(wasm.bigIntEnabled){
BindTypes.bigint = BindTypes.number;
}
@@ -486,26 +514,30 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
- `db`: the DB object which created the statement.
- `columnCount`: the number of result columns in the query, or 0
- for queries which cannot return results. This property is a proxy
- for sqlite3_column_count() and its use in loops should be avoided
- because of the call overhead associated with that. The
- `columnCount` is not cached when the Stmt is created because a
- schema change made via a separate db connection between this
- statement's preparation and when it is stepped may invalidate it.
+ for queries which cannot return results. This property is a
+ read-only proxy for sqlite3_column_count() and its use in loops
+ should be avoided because of the call overhead associated with
+ that. The `columnCount` is not cached when the Stmt is created
+ because a schema change made between this statement's preparation
+ and when it is stepped may invalidate it.
- - `parameterCount`: the number of bindable parameters in the query.
+ - `parameterCount`: the number of bindable parameters in the
+ query. Like `columnCount`, this property is ready-only and is a
+ proxy for a C API call.
As a general rule, most methods of this class will throw if
called on an instance which has been finalized. For brevity's
sake, the method docs do not all repeat this warning.
*/
- const Stmt = function(){
+ const Stmt = function(/*oo1db, stmtPtr, BindTypes [,takeOwnership=true] */){
if(BindTypes!==arguments[2]){
toss3(capi.SQLITE_MISUSE, "Do not call the Stmt constructor directly. Use DB.prepare().");
}
this.db = arguments[0];
__ptrMap.set(this, arguments[1]);
- this.parameterCount = capi.sqlite3_bind_parameter_count(this.pointer);
+ if( arguments.length>3 && !arguments[3] ){
+ __doesNotOwnHandle.add(this);
+ }
};
/** Throws if the given DB has been closed, else it is returned. */
@@ -698,10 +730,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
},
/**
Finalizes all open statements and closes this database
- connection. This is a no-op if the db has already been
- closed. After calling close(), `this.pointer` will resolve to
- `undefined`, so that can be used to check whether the db
- instance is still opened.
+ connection (with one exception noted below). This is a no-op if
+ the db has already been closed. After calling close(),
+ `this.pointer` will resolve to `undefined`, and that can be
+ used to check whether the db instance is still opened.
If this.onclose.before is a function then it is called before
any close-related cleanup.
@@ -721,14 +753,19 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
all, will never trigger close(), so onclose handlers are not a
reliable way to implement close-time cleanup or maintenance of
a db.
+
+ If this instance was created using DB.wrapHandle() and does not
+ own this.pointer then it does not close the db handle but it
+ does perform all other work, such as calling onclose callbacks
+ and disassociating this object from this.pointer.
*/
close: function(){
- if(this.pointer){
+ const pDb = this.pointer;
+ if(pDb){
if(this.onclose && (this.onclose.before instanceof Function)){
try{this.onclose.before(this)}
catch(e){/*ignore*/}
}
- const pDb = this.pointer;
Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
if(s && s.pointer){
try{s.finalize()}
@@ -737,7 +774,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
});
__ptrMap.delete(this);
__stmtMap.delete(this);
- capi.sqlite3_close_v2(pDb);
+ if( !__doesNotOwnHandle.delete(this) ){
+ capi.sqlite3_close_v2(pDb);
+ }
if(this.onclose && (this.onclose.after instanceof Function)){
try{this.onclose.after(this)}
catch(e){/*ignore*/}
@@ -1450,9 +1489,63 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
*/
checkRc: function(resultCode){
return checkSqlite3Rc(this, resultCode);
- }
+ },
}/*DB.prototype*/;
+ /**
+ Returns a new oo1.DB instance which wraps the given (sqlite3*)
+ WASM pointer, optionally with or without taking over ownership of
+ that pointer.
+
+ The first argument must be either a non-NULL (sqlite3*) WASM
+ pointer.
+
+ The second argument, defaulting to false, specifies ownership of
+ the first argument. If it is truthy, the returned object will
+ pass that pointer to sqlite3_close() when its close() method is
+ called, otherwise it will not.
+
+ Throws if pDb is not a non-0 WASM pointer.
+
+ The caller MUST GUARANTEE that the passed-in handle will outlive
+ the returned object, i.e. that it will not be closed. If it is closed,
+ this object will hold a stale pointer and results are undefined.
+
+ Aside from its lifetime, the proxy is to be treated as any other
+ DB instance, including the requirement of calling close() on
+ it. close() will free up internal resources owned by the proxy
+ and disassociate the proxy from that handle but will not
+ actually close the proxied db handle unless this function is
+ passed a thruthy second argument.
+
+ To stress:
+
+ - DO NOT call sqlite3_close() (or similar) on the being-proxied
+ pointer while a proxy is active.
+
+ - ALWAYS eventually call close() on the returned object. If the
+ proxy does not own the underlying handle then its MUST be
+ closed BEFORE the being-proxied handle is closed.
+
+ Design notes:
+
+ - wrapHandle() "could" accept a DB object instance as its first
+ argument and proxy thatDb.pointer but there is currently no use
+ case where doing so would be useful, so it does not allow
+ that. That restriction may be lifted in a future version.
+ */
+ DB.wrapHandle = function(pDb, takeOwnership=false){
+ if( !pDb || !wasm.isPtr(pDb) ){
+ throw new sqlite3.SQLite3Error(capi.SQLITE_MISUSE,
+ "Argument must be a WASM sqlite3 pointer");
+ }
+ return new DB({
+ /* This ctor call style is very specifically internal-use-only.
+ It is not documented and may change at any time. */
+ "sqlite3*": pDb,
+ "sqlite3*:takeOwnership": !!takeOwnership
+ });
+ };
/** Throws if the given Stmt has been finalized, else stmt is
returned. */
@@ -1474,8 +1567,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
case BindTypes.string:
return t;
case BindTypes.bigint:
- if(wasm.bigIntEnabled) return t;
- /* else fall through */
+ return wasm.bigIntEnabled ? t : undefined;
default:
return util.isBindableTypedArray(v) ? BindTypes.blob : undefined;
}
@@ -1641,12 +1733,19 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
This method always throws if called when it is illegal to do
so. Namely, when triggered via a per-row callback handler of a
DB.exec() call.
+
+ If Stmt does not own its underlying (sqlite3_stmt*) (see
+ Stmt.wrapHandle()) then this function will not pass it to
+ sqlite3_finalize().
*/
finalize: function(){
- if(this.pointer){
+ const ptr = this.pointer;
+ if(ptr){
affirmNotLockedByExec(this,'finalize()');
- const rc = capi.sqlite3_finalize(this.pointer);
- delete __stmtMap.get(this.db)[this.pointer];
+ const rc = (__doesNotOwnHandle.delete(this)
+ ? 0
+ : capi.sqlite3_finalize(ptr));
+ delete __stmtMap.get(this.db)[ptr];
__ptrMap.delete(this);
__execLock.delete(this);
__stmtMayGet.delete(this);
@@ -2134,6 +2233,64 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
set: ()=>toss3("The columnCount property is read-only.")
});
+ Object.defineProperty(Stmt.prototype, 'parameterCount', {
+ enumerable: false,
+ get: function(){return capi.sqlite3_bind_parameter_count(this.pointer)},
+ set: ()=>toss3("The parameterCount property is read-only.")
+ });
+
+ /**
+ The Stmt counterpart of oo1.DB.wrapHandle(), this creates a Stmt
+ instance which wraps a WASM (sqlite3_stmt*) in the oo1 API,
+ optionally with or without taking over ownership of that pointer.
+
+ The first argument must be an oo1.DB instance[^1].
+
+ The second argument must be a valid WASM (sqlite3_stmt*), as
+ produced by sqlite3_prepare_v2() and sqlite3_prepare_v3().
+
+ The third argument, defaulting to false, specifies whether the
+ returned Stmt object takes over ownership of the underlying
+ (sqlite3_stmt*). If true, the returned object's finalize() method
+ will finalize that handle, else it will not. If it is false,
+ ownership of pStmt is unchanged and pStmt MUST outlive the
+ returned object or results are undefined.
+
+ This function throws if the arguments are invalid. On success it
+ returns a new Stmt object which wraps the given statement
+ pointer.
+
+ Like all Stmt objects, the finalize() method must eventually be
+ called on the returned object to free up internal resources,
+ regardless of whether this function's third argument is true or
+ not.
+
+ [^1]: The first argument cannot be a (sqlite3*) because the
+ resulting Stmt object requires a parent DB object. It is not yet
+ determined whether it would be of general benefit to refactor the
+ DB/Stmt pair internals to communicate in terms of the underlying
+ (sqlite3*) rather than a DB object. If so, we could laxen the
+ first argument's requirement and allow an (sqlite3*). Because
+ DB.wrapHandle() enables multiple DB objects to proxy the same
+ (sqlite3*), we cannot unambiguously translate the first arugment
+ from (sqlite3*) to DB instances for us with this function's first
+ argument.
+ */
+ Stmt.wrapHandle = function(oo1db, pStmt, takeOwnership=false){
+ let ctor = Stmt;
+ if( !(oo1db instanceof DB) || !oo1db.pointer ){
+ throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE,
+ "First argument must be an opened "+
+ "sqlite3.oo1.DB instance");
+ }
+ if( !pStmt || !wasm.isPtr(pStmt) ){
+ throw new sqlite3.SQLite3Error(sqlite3.SQLITE_MISUSE,
+ "Second argument must be a WASM "+
+ "sqlite3_stmt pointer");
+ }
+ return new Stmt(oo1db, pStmt, BindTypes, !!takeOwnership);
+ }
+
/** The OO API's public namespace. */
sqlite3.oo1 = {
DB,
diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js
index 7e128a3fa..e3807a314 100644
--- a/ext/wasm/api/sqlite3-api-prologue.js
+++ b/ext/wasm/api/sqlite3-api-prologue.js
@@ -134,22 +134,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
const config = Object.assign(Object.create(null),{
exports: undefined,
memory: undefined,
- bigIntEnabled: (()=>{
- if('undefined'!==typeof Module){
- /* Emscripten module will contain HEAPU64 when built with
- -sWASM_BIGINT=1, else it will not.
-
- As of emsdk 3.1.55, when building in strict mode, HEAPxyz
- are only available if _explicitly_ included in the exports,
- else they are not. We do not (as of 2024-03-04) use -sSTRICT
- for the canonical builds.
- */
- if( !!Module.HEAPU64 ) return true;
- /* Else fall through and hope for the best. Nobody _really_
- builds this without BigInt support, do they? */
- }
- return !!globalThis.BigInt64Array;
- })(),
+ bigIntEnabled: !!globalThis.BigInt64Array,
debug: console.debug.bind(console),
warn: console.warn.bind(console),
error: console.error.bind(console),
diff --git a/ext/wasm/api/sqlite3-api-worker1.c-pp.js b/ext/wasm/api/sqlite3-api-worker1.c-pp.js
index 5e088f438..55ad16185 100644
--- a/ext/wasm/api/sqlite3-api-worker1.c-pp.js
+++ b/ext/wasm/api/sqlite3-api-worker1.c-pp.js
@@ -385,10 +385,19 @@ sqlite3.initWorker1API = function(){
const getDbId = function(db){
let id = wState.idMap.get(db);
if(id) return id;
- id = 'db#'+(++wState.idSeq)+'@'+db.pointer;
+ id = 'db#'+(++wState.idSeq)+':'+
+ Math.floor(Math.random() * 100000000)+':'+
+ Math.floor(Math.random() * 100000000);
/** ^^^ can't simply use db.pointer b/c closing/opening may re-use
the same address, which could map pending messages to a wrong
- instance. */
+ instance.
+
+ 2025-07: https://github.com/sqlite/sqlite-wasm/issues/113
+ demonstrates that two Worker1s can end up with the same IDs,
+ despite using different instances of the library, so we need
+ to add some randomness to the IDs instead of relying on the
+ pointer addresses.
+ */
wState.idMap.set(db, id);
return id;
};
diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c
index ee8a10209..574684ce9 100644
--- a/ext/wasm/api/sqlite3-wasm.c
+++ b/ext/wasm/api/sqlite3-wasm.c
@@ -939,6 +939,7 @@ const char * sqlite3__wasm_enum_json(void){
DefInt(SQLITE_INNOCUOUS);
DefInt(SQLITE_SUBTYPE);
DefInt(SQLITE_RESULT_SUBTYPE);
+ DefInt(SQLITE_SELFORDER1);
} _DefGroup;
DefGroup(version) {
diff --git a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js
index c043fd148..2edabe3e6 100644
--- a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js
+++ b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js
@@ -130,12 +130,10 @@
Notable shortcomings:
- - This API was not designed with ES6 modules in mind. Neither Firefox
- nor Safari support, as of March 2023, the {type:"module"} flag to the
- Worker constructor, so that particular usage is not something we're going
- to target for the time being:
-
- https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
+ - "v1" of this this API is not suitable for use as an ESM module
+ because ESM worker modules were not widely supported when it was
+ developed. For use as an ESM module, see the "v2" interface later
+ on in this file.
*/
globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfig){
// Inspired by: https://stackoverflow.com/a/52439530
@@ -296,7 +294,7 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = {
after calling the original function and will reject if that
function throws.
*/
-sqlite3Worker1Promiser.v2 = function(config){
+globalThis.sqlite3Worker1Promiser.v2 = function callee(config = callee.defaultConfig){
let oldFunc;
if( 'function' == typeof config ){
oldFunc = config;
@@ -326,11 +324,14 @@ sqlite3Worker1Promiser.v2 = function(config){
}
return p;
}.bind({
- /* We do this because clients are
- recommended to delete globalThis.sqlite3Worker1Promiser. */
+ /* We do this because clients are recommended to delete
+ globalThis.sqlite3Worker1Promiser. */
original: sqlite3Worker1Promiser
});
+globalThis.sqlite3Worker1Promiser.v2.defaultConfig =
+ globalThis.sqlite3Worker1Promiser.defaultConfig;
+
//#if target=es6-module
/**
When built as a module, we export sqlite3Worker1Promiser.v2()
diff --git a/ext/wasm/common/whwasmutil.js b/ext/wasm/common/whwasmutil.js
index b4d8f691b..e85669579 100644
--- a/ext/wasm/common/whwasmutil.js
+++ b/ext/wasm/common/whwasmutil.js
@@ -1773,10 +1773,10 @@ globalThis.WhWasmUtilInstaller = function(target){
does not have a stable interface. */
xArg.FuncPtrAdapter.warnOnUse = false;
- /** If true, convertArg() will FuncPtrAdapter.debugOut() when it
- (un)installs a function binding to/from WASM. Note that
- deinstallation of bindScope=transient bindings happens
- via scopedAllocPop() so will not be output. */
+ /** If true, convertArg() will call FuncPtrAdapter.debugOut() when
+ it (un)installs a function binding to/from WASM. Note that
+ deinstallation of bindScope=transient bindings happens via
+ scopedAllocPop() so will not be output. */
xArg.FuncPtrAdapter.debugFuncInstall = false;
/** Function used for debug output. */
@@ -1827,9 +1827,8 @@ globalThis.WhWasmUtilInstaller = function(target){
The first argument must be one of:
- A JavaScript function.
- - The name of a WASM-exported function. In the latter case xGet()
- is used to fetch the exported function, which throws if it's not
- found.
+ - The name of a WASM-exported function. xGet() is used to fetch
+ the exported function, which throws if it's not found.
- A pointer into the indirect function table. e.g. a pointer
returned from target.installFunction().
@@ -1874,9 +1873,6 @@ globalThis.WhWasmUtilInstaller = function(target){
which convert their argument to an integer and truncate it to
the given bit length.
- - `N*` (args): a type name in the form `N*`, where N is a numeric
- type name, is treated the same as WASM pointer.
-
- `*` and `pointer` (args): are assumed to be WASM pointer values
and are returned coerced to an appropriately-sized pointer
value (i32 or i64). Non-numeric values will coerce to 0 and
@@ -1887,7 +1883,15 @@ globalThis.WhWasmUtilInstaller = function(target){
WASM pointer numeric type.
- `**` (args): is simply a descriptive alias for the WASM pointer
- type. It's primarily intended to mark output-pointer arguments.
+ type. It's primarily intended to mark output-pointer arguments,
+ noting that JS's view of WASM does not distinguish between
+ pointers and pointers-to-pointers, so all such interpretation
+ of `**`, as distinct from `*`, necessarily happens at the
+ client level.
+
+ - `NumType*` (args): a type name in this form, where T is
+ the name of a numeric mapping, e.g. 'int16' or 'double',
+ is treated like `*`.
- `i64` (args and results): passes the value to BigInt() to
convert it to an int64. Only available if bigIntEnabled is
@@ -1916,7 +1920,7 @@ globalThis.WhWasmUtilInstaller = function(target){
UTF-8-encoded C-string to pass to the exported function,
cleaning it up before the wrapper returns. If a long-lived
C-string pointer is required, that requires client-side code
- to create the string, then pass its pointer to the function.
+ to create the string then pass its pointer to the function.
- Else the arg is assumed to be a pointer to a string the
client has already allocated and it's passed on as
@@ -2091,8 +2095,8 @@ globalThis.WhWasmUtilInstaller = function(target){
easily convert, e.g., to C-strings, and have them cleaned up
automatically before the wrapper returns to the caller. Likewise,
if a _result_ adapter uses scoped allocation, the result will be
- freed before because they would be freed before the wrapper
- returns, leading to chaos and undefined behavior.
+ freed before the wrapper returns, leading to chaos and undefined
+ behavior.
Except when called as a getter, this function returns itself.
*/
diff --git a/ext/wasm/config.make.in b/ext/wasm/config.make.in
index f30baac3f..4c8d7893b 100644
--- a/ext/wasm/config.make.in
+++ b/ext/wasm/config.make.in
@@ -1,15 +1,16 @@
-# Gets filtered by the configure script
+# config.make.in gets filtered by the top-most configure script to
+# create config.make.
bin.bash = @BIN_BASH@
bin.emcc = @EMCC_WRAPPER@
bin.wasm-strip = @BIN_WASM_STRIP@
bin.wasm-opt = @BIN_WASM_OPT@
-SHELL := $(bin.bash)
+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 :=
+#bin.bash =
+#bin.emcc =
+#bin.wasm-strip =
+#bin.wasm-opt =
diff --git a/ext/wasm/dist.make b/ext/wasm/dist.make
index 60699ff5c..176972fd7 100644
--- a/ext/wasm/dist.make
+++ b/ext/wasm/dist.make
@@ -11,7 +11,7 @@
# distinctly different zip file and top directory name to distinguish
# them from release builds.
#######################################################################
-MAKEFILE.dist := $(lastword $(MAKEFILE_LIST))
+MAKEFILE.dist = $(lastword $(MAKEFILE_LIST))
########################################################################
# Chicken/egg situation: we need $(bin.version-info) to get the
@@ -20,16 +20,16 @@ MAKEFILE.dist := $(lastword $(MAKEFILE_LIST))
# have to use a temporary name for the archive until we can get
# that binary built.
ifeq (1,$(SQLITE_C_IS_SEE))
-dist-name-extra := -see
+dist-name-extra = -see
else
-dist-name-extra :=
+dist-name-extra =
endif
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
-dist-name-prefix := sqlite-wasm$(dist-name-extra)
+dist-name-prefix = sqlite-wasm$(dist-name-extra)
else
-dist-name-prefix := sqlite-wasm$(dist-name-extra)-snapshot-$(shell /usr/bin/date +%Y%m%d)
+dist-name-prefix = sqlite-wasm$(dist-name-extra)-snapshot-$(shell /usr/bin/date +%Y%m%d)
endif
-dist-name := $(dist-name-prefix)-TEMP
+dist-name = $(dist-name-prefix)-TEMP
########################################################################
# dist.build must be the name of a target which triggers the build of
@@ -45,10 +45,10 @@ dist-name := $(dist-name-prefix)-TEMP
# reason not to.
dist.build ?= oz
-dist-dir.top := $(dist-name)
-dist-dir.jswasm := $(dist-dir.top)/$(notdir $(dir.dout))
-dist-dir.common := $(dist-dir.top)/common
-dist.top.extras := \
+dist-dir.top = $(dist-name)
+dist-dir.jswasm = $(dist-dir.top)/$(notdir $(dir.dout))
+dist-dir.common = $(dist-dir.top)/common
+dist.top.extras = \
demo-123.html demo-123-worker.html demo-123.js \
tester1.html tester1-worker.html tester1-esm.html \
tester1.js tester1.mjs \
@@ -56,9 +56,9 @@ dist.top.extras := \
demo-worker1.html demo-worker1.js \
demo-worker1-promiser.html demo-worker1-promiser.js \
demo-worker1-promiser-esm.html demo-worker1-promiser.mjs
-dist.jswasm.extras := $(sqlite3.wasm) \
+dist.jswasm.extras = $(sqlite3.wasm) \
$(sqlite3-api.ext.jses)
-dist.common.extras := \
+dist.common.extras = \
$(wildcard $(dir.common)/*.css) \
$(dir.common)/SqliteTestUtil.js
@@ -77,12 +77,12 @@ $(bin.stripccomments) $(2) < $(1) > $(dist-dir.jswasm)/$(notdir $(1)) || exit;
endef
# STRIP_K1.js = list of JS files which need to be passed through
# $(bin.stripcomments) with a single -k flag.
-STRIP_K1.js := $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) \
+STRIP_K1.js = $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) \
$(sqlite3-worker1-bundler-friendly.js) \
$(sqlite3-api.ext.jses)
# STRIP_K2.js = list of JS files which need to be passed through
# $(bin.stripcomments) with two -k flags.
-STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
+STRIP_K2.js = $(sqlite3.js) $(sqlite3.mjs) \
$(sqlite3-bundler-friendly.mjs) $(sqlite3-node.mjs)
########################################################################
# dist: create the end-user deliverable archive.
@@ -104,8 +104,8 @@ STRIP_K2.js := $(sqlite3.js) $(sqlite3.mjs) \
# 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
+#apply_comment_stripper = false
+apply_comment_stripper = true
# ^^^ shell command true or false
dist: \
$(bin.stripccomments) $(bin.version-info) \
diff --git a/ext/wasm/fiddle.make b/ext/wasm/fiddle.make
index 8110384a6..6bdf44195 100644
--- a/ext/wasm/fiddle.make
+++ b/ext/wasm/fiddle.make
@@ -3,13 +3,12 @@
#
# Intended to include'd by ./GNUmakefile.
#######################################################################
-MAKEFILE.fiddle := $(lastword $(MAKEFILE_LIST))
########################################################################
# shell.c and its build flags...
ifneq (1,$(MAKING_CLEAN))
- make-np-0 := make -C $(dir.top) -n -p
- make-np-1 := sed -e 's/(TOP)/(dir.top)/g'
+ make-np-0 = make -C $(dir.top) -n -p
+ make-np-1 = sed -e 's/(TOP)/(dir.top)/g'
# Extract SHELL_OPT and SHELL_DEP from the top-most makefile and import
# them as vars here...
$(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1)))
@@ -27,7 +26,7 @@ endif
# /shell.c
########################################################################
-EXPORTED_FUNCTIONS.fiddle := $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
+EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
fiddle.emcc-flags = \
$(emcc.cflags) $(emcc_opt_full) \
--minify 0 \
@@ -40,9 +39,8 @@ fiddle.emcc-flags = \
-sWASM_BIGINT=$(emcc.WASM_BIGINT) \
-sEXPORT_NAME=$(sqlite3.js.init-func) \
-Wno-limited-postlink-optimizations \
- $(emcc.exportedRuntimeMethods) \
+ $(emcc.exportedRuntimeMethods),FS \
-sEXPORTED_FUNCTIONS=@$(abspath $(EXPORTED_FUNCTIONS.fiddle)) \
- -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory \
$(SQLITE_OPT.full-featured) \
$(SQLITE_OPT.common) \
$(SHELL_OPT) \
@@ -53,12 +51,12 @@ fiddle.emcc-flags = \
# Flags specifically for debug builds of fiddle. Performance suffers
# greatly in debug builds.
-fiddle.emcc-flags.debug := $(fiddle.emcc-flags) \
+fiddle.emcc-flags.debug = $(fiddle.emcc-flags) \
-DSQLITE_DEBUG \
-DSQLITE_ENABLE_SELECTTRACE \
-DSQLITE_ENABLE_WHERETRACE
-fiddle.EXPORTED_FUNCTIONS.in := \
+fiddle.EXPORTED_FUNCTIONS.in = \
EXPORTED_FUNCTIONS.fiddle.in \
$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-core \
$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-extras
@@ -67,10 +65,7 @@ $(EXPORTED_FUNCTIONS.fiddle): $(MKDIR.bld) $(fiddle.EXPORTED_FUNCTIONS.in) \
$(MAKEFILE.fiddle)
sort -u $(fiddle.EXPORTED_FUNCTIONS.in) > $@
-fiddle.cses := $(dir.top)/shell.c $(sqlite3-wasm.c)
-
-fiddle: $(fiddle-module.js) $(fiddle-module.js.debug)
-fiddle.debug: $(fiddle-module.js.debug)
+fiddle.cses = $(dir.top)/shell.c $(sqlite3-wasm.c)
clean: clean-fiddle
clean-fiddle:
@@ -84,10 +79,9 @@ clean-fiddle:
all: fiddle
########################################################################
-# fiddle_remote is the remote destination for the fiddle app. It
-# must be a [user@]HOST:/path for rsync.
-# Note that the target "should probably" contain a symlink of
-# index.html -> fiddle.html.
+# fiddle_remote is the remote destination for the fiddle app. It must
+# be a [user@]HOST:/path for rsync. The target "should probably"
+# contain a symlink of index.html -> fiddle.html.
fiddle_remote ?=
ifeq (,$(fiddle_remote))
ifneq (,$(wildcard /home/stephan))
@@ -154,11 +148,6 @@ push-fiddle: fiddle
# because certain execution environments disallow those constructs.
# This flag is not strictly necessary, however.
#
-# -sWASM_BIGINT is UNTESTED but "should" allow the int64-using C APIs
-# to work with JS/wasm, insofar as the JS environment supports the
-# BigInt type. That support requires an extremely recent browser:
-# Safari didn't get that support until late 2020.
-#
# --no-entry: for compiling library code with no main(). If this is
# not supplied and the code has a main(), it is called as part of the
# module init process. Note that main() is #if'd out of shell.c
@@ -180,14 +169,15 @@ push-fiddle: fiddle
# minification makes little difference in terms of overall
# distributable size.
#
-# --minify 0: disables minification of the generated JS code,
-# regardless of optimization level. Minification of the JS has
-# minimal overall effect in the larger scheme of things and results
-# in JS files which can neither be edited nor viewed as text files in
-# Fossil (which flags them as binary because of their extreme line
-# lengths). Interestingly, whether or not the comments in the
-# generated JS file get stripped is unaffected by this setting and
-# depends entirely on the optimization level. Higher optimization
+# --minify 0: supposedly disables minification of the generated JS
+# code, regardless of optimization level, but that's not quite true:
+# search the main makefile for wasm-strip for details. Minification
+# of the JS has minimal overall effect in the larger scheme of things
+# and results in JS files which can neither be edited nor viewed as
+# text files in Fossil (which flags them as binary because of their
+# extreme line lengths). Interestingly, whether or not the comments
+# in the generated JS file get stripped is unaffected by this setting
+# and depends entirely on the optimization level. Higher optimization
# levels reduce the size of the JS considerably even without
# minification.
#
diff --git a/ext/wasm/fiddle/fiddle-worker.js b/ext/wasm/fiddle/fiddle-worker.js
index 27d915eb2..9c6cddb0f 100644
--- a/ext/wasm/fiddle/fiddle-worker.js
+++ b/ext/wasm/fiddle/fiddle-worker.js
@@ -163,9 +163,11 @@
fiddleModule.isDead = true;
return false;
}
- stdout("SQLite version", capi.sqlite3_libversion(),
- capi.sqlite3_sourceid().substr(0,19));
- stdout('Welcome to the "fiddle" shell.');
+ wMsg('sqlite-version', {
+ lib: capi.sqlite3_libversion(),
+ srcId: capi.sqlite3_sourceid()
+ });
+ stdout('Welcome to the "fiddle" shell. Tap the About button for more info.');
if(capi.sqlite3_vfs_find("opfs")){
stdout("\nOPFS is available. To open a persistent db, use:\n\n",
" .open file:name?vfs=opfs\n\nbut note that some",
@@ -281,6 +283,7 @@
stderr("'open' expects {buffer:Uint8Array} containing an uploaded db.");
return;
}
+ buffer.set([1,1], 18)/*force db out of WAL mode*/;
const fn = (
opt.filename
? opt.filename.split(/[/\\]/).pop().replace('"','_')
diff --git a/ext/wasm/fiddle/fiddle.js b/ext/wasm/fiddle/fiddle.js
index f0a89f25d..45ef69326 100644
--- a/ext/wasm/fiddle/fiddle.js
+++ b/ext/wasm/fiddle/fiddle.js
@@ -329,6 +329,21 @@
SF.worker = new Worker('fiddle-worker.js'+self.location.search);
SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data);
SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data));
+ SF.addMsgHandler('sqlite-version', (ev)=>{
+ const v = ev.data;
+ const a = E('#sqlite-version-link');
+ const li = v.srcId.split(' ')/*DATE TIME HASH*/;
+ a.setAttribute('href',
+ //'https://sqlite.org/src/timeline/?c='+li[2].substr(0,20)
+ 'https://sqlite.org/src/info/'+li[2].substr(0,20)
+ );
+ a.setAttribute('target', '_blank');
+ a.innerText = [
+ v.lib,
+ v.srcId.substr(0,34)
+ ].join(' ');
+ SF.echo("SQLite version",a.innerText);
+ });
/* querySelectorAll() proxy */
const EAll = function(/*[element=document,] cssSelector*/){
@@ -391,6 +406,19 @@
self.onSFLoaded();
});
+ /** Toggle the "About" view on and off. */
+ SF.toggleAbout = function(){
+ if( document.body.classList.toggle('about') ){
+ this.eAbout.classList.remove('hidden');
+ SF.eMainView.classList.add('hidden');
+ }else{
+ this.eAbout.classList.add('hidden');
+ SF.eMainView.classList.remove('hidden');
+ }
+ }.bind({
+ eAbout: E("#view-about")
+ });
+
/**
Performs all app initialization which must wait until after the
worker module is loaded. This function removes itself when it's
@@ -400,7 +428,16 @@
delete this.onSFLoaded;
// Unhide all elements which start out hidden
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
+ SF.eMainView = EAll('.app-view:not(.hidden)')[0]
+ /** The main view widget. Initially the first non-hidden
+ .app-view element. */;
+ if( (new URL(self.location.href).searchParams).has('about') ){
+ SF.toggleAbout() /* for use while editing the About page */;
+ }
E('#btn-reset').addEventListener('click',()=>SF.resetDb());
+ EAll('#btn-about, #btn-about-close').forEach((e)=>{
+ e.addEventListener('click',()=>SF.toggleAbout())
+ });
const taInput = E('#input');
const btnClearIn = E('#btn-clear');
const selectExamples = E('#select-examples');
@@ -792,28 +829,33 @@
//SF.echo(null/*clear any output generated by the init process*/);
if(window.jQuery && window.jQuery.terminal){
/* Set up the terminal-style view... */
- const eTerm = window.jQuery('#view-terminal').empty();
- SF.jqTerm = eTerm.terminal(SF.dbExec.bind(SF),{
+ const eTerm = E('#view-terminal');
+ const jqeTerm = window.jQuery(eTerm).empty();
+ SF.jqTerm = jqeTerm.terminal(SF.dbExec.bind(SF),{
prompt: 'sqlite> ',
greetings: false /* note that the docs incorrectly call this 'greeting' */
});
/* Set up a button to toggle the views... */
- const head = E('header#titlebar');
+ const head = E('#titlebar-buttons');
const btnToggleView = document.createElement('button');
btnToggleView.appendChild(document.createTextNode("Toggle View"));
head.appendChild(btnToggleView);
+ const eOthers = EAll('.app-view:not(#view-terminal)');
+ const eOtherMain = E('#view-split');
btnToggleView.addEventListener('click',function f(){
- EAll('.app-view').forEach(e=>e.classList.toggle('hidden'));
+ document.body.classList.remove('about');
if(document.body.classList.toggle('terminal-mode')){
- ForceResizeKludge();
+ eOthers.forEach((e)=>e.classList.add('hidden'));
+ SF.eMainView = eTerm;
+ }else{
+ eTerm.classList.add('hidden');
+ SF.eMainView = eOtherMain;
}
+ SF.eMainView.classList.remove('hidden');
+ ForceResizeKludge();
}, false);
btnToggleView.click()/*default to terminal view*/;
}
- SF.echo('This experimental app is provided in the hope that it',
- 'may prove interesting or useful but is not an officially',
- 'supported deliverable of the sqlite project. It is subject to',
- 'any number of changes or outright removal at any time.\n');
const urlParams = new URL(self.location.href).searchParams;
SF.dbExec(urlParams.get('sql') || null);
delete ForceResizeKludge.$disabled;
diff --git a/ext/wasm/fiddle/index.html b/ext/wasm/fiddle/index.html
index ca6788ef0..019612954 100644
--- a/ext/wasm/fiddle/index.html
+++ b/ext/wasm/fiddle/index.html
@@ -5,16 +5,27 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>SQLite3 Fiddle</title>
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
- <!-- to add a toggleable terminal-style view, uncomment the following
- two lines and ensure that these files are on the web server. -->
+ <!--
+ To add a terminal-style view using jquery.terminal[^1],
+ uncomment the following two HTML lines and ensure that these
+ files are on the web server.
+
+ jquery-bundle.min.js is a concatenation of jquery.min.js from
+ [^2] and jquery.terminal.min.js from [^1].
+ jquery.terminal.min.css is from [^1].
+
+ [^1]: https://github.com/jcubic/jquery.terminal
+ [^2]: https://jquery.com
+ -->
<!--script src="jqterm/jqterm-bundle.min.js"></script>
- <link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/-->
+ <link rel="stylesheet" href="jqterm/jquery.terminal.min.css"-->
<style>
/* The following styles are for app-level use. */
:root {
--sqlite-blue: #044a64;
- --textarea-color1: #044a64;
+ --textarea-color1: #000 /*044a64 is nice too*/;
--textarea-color2: white;
+ --size: 1.25 /* used by jqterm to calculate font size and the default is too tiny.*/;
}
textarea {
font-family: monospace;
@@ -170,6 +181,17 @@
display: flex;
flex-direction: column-reverse;
}
+ #view-about {
+ flex: auto;
+ overflow: auto;
+ }
+ #view-about h1:first-child {
+ display: flex;
+ }
+ #view-about h1:first-child > button {
+ align-self: center;
+ margin-left: 1em;
+ }
/* emcscript-related styling, used during the module load/intialization processes... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
@@ -200,8 +222,11 @@
<body>
<header id='titlebar'>
<span>SQLite3 Fiddle</span>
- <span class='powered-by'>Powered by
- <a href='https://sqlite.org'>SQLite3</a></span>
+ <span id='titlebar-buttons'>
+ <span class='powered-by'>Powered by
+ <a href='https://sqlite.org'>SQLite3</a></span>
+ <button id='btn-about'>About...</button>
+ </span>
</header>
<!-- emscripten bits -->
<figure id="module-spinner">
@@ -215,7 +240,7 @@
</figure>
<div class="emscripten" id="module-status">Downloading...</div>
<div class="emscripten">
- <progress value="0" max="100" id="module-progress" hidden='1'></progress>
+ <progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div><!-- /emscripten bits -->
<div id='view-terminal' class='app-view hidden initially-hidden'>
@@ -295,8 +320,54 @@
<div><textarea id="output" readonly
placeholder="Shell output."></textarea></div>
</fieldset>
- </div>
+ </div><!-- #main-wrapper -->
</div> <!-- #view-split -->
- <script src="fiddle.js"></script>
+
+<div class='hidden app-view' id='view-about'>
+ <h1>About SQLite Fiddle <button id='btn-about-close'>close</button></h1>
+
+ <p>Fiddle is a JavaScript application wrapping a <a href='https://webassembly.org'>WebAssembly</a>
+ build of <a href="https://sqlite.org/cli.html">the SQLite CLI shell</a>, slightly
+ modified to account for browser-based user input. Aside from the different layout,
+ it works just like the CLI shell. This copy was built with SQLite version
+ <a id='sqlite-version-link'></a>.
+ </p>
+
+ <p>This app is provided in the hope that it may prove interesting or useful
+ but it is not an officially-supported deliverable of the SQLite project.
+ It is subject to any number of changes or outright removal at any time.
+ That said, for as long as it's online we do respond to support requests
+ in <a href="https://sqlite.org/forum">the SQLite forum</a>.
+ </p>
+
+ <p>This app runs on your device. After loading, it does not interact
+ with the remote server at all. Similarly, this app does not use any
+ HTTP cookies.</p>
+
+ <p>Fiddle databases are transient in-memory databases unless they
+ specifically use a persistent storage option (if available, help
+ text in the SQL result output area will indicate how to use
+ persistent storage when this app starts up).
+ </p>
+
+ <h1>Usage Summary</h1>
+
+ <ul>
+ <li>Input can be run with either the Run button or tapping one of
+ Ctrl-enter or Shift-enter from within the text input field.
+ If a portion of the input field is selected, only that portion will be run.
+ </li>
+ <li>The various toggle checkboxes can be used to tweak the layout.
+ Those toggles are persistent if the JS environment allows it.</li>
+ <li>Databases can be imported and exported using the buttons in
+ the Options toolbar. No specific limit for imported database
+ sizes is imposed, but large databases may cause it to fail with
+ an out-of-memory error.</li>
+ <!--li></li-->
+ </ul>
+
+</div><!-- #view-about -->
+
+<script src="fiddle.js"></script>
</body>
</html>
diff --git a/ext/wasm/mkwasmbuilds.c b/ext/wasm/mkwasmbuilds.c
index 8aa29c0fe..d33a10c01 100644
--- a/ext/wasm/mkwasmbuilds.c
+++ b/ext/wasm/mkwasmbuilds.c
@@ -11,18 +11,17 @@
*************************************************************************
**
** This app's single purpose is to emit parts of the Makefile code for
-** building sqlite3's WASM build. The main motivation is to generate
-** code which "can" be created via GNU Make's eval command but is
+** sqlite3's canonical WASM build. The main motivation is to generate
+** code which "could" be created via GNU Make's eval command but is
** highly illegible when constructed that way. Attempts to write this
-** app in Bash and TCL have suffered from the problem that both
-** require escaping $ symbols, making the resulting script code as
-** illegible as the eval spaghetti we want to get away from. Writing
-** it in C is, somewhat surprisingly, _slightly_ less illegible than
-** writing it in bash, tcl, or native Make code.
+** app in Bash and TCL have suffered from the problem that those
+** languages require escaping $ symbols, making the resulting script
+** code as illegible as the eval spaghetti we want to get away
+** from. Maintaining it in C is, somewhat surprisingly, _slightly_
+** less illegible than writing it in bash, tcl, or native Make code.
**
** The emitted makefile code is not standalone - it depends on
** variables and $(call)able functions from the main makefile.
-**
*/
#undef NDEBUG
@@ -33,38 +32,145 @@
#define pf printf
#define ps puts
-/* Very common printf() args combo. */
-#define zNM zName, zMode
/*
-** Valid names for the zName arguments.
+** Valid build names. Each build is a combination of one of these and
+** one of JS_BUILD_MODES, but only certain combinations are legal.
+** This macro and JS_BUILD_MODES exist solely for documentation
+** purposes: they are not expanded into code anywhere.
*/
#define JS_BUILD_NAMES sqlite3 sqlite3-wasmfs
/*
-** Valid names for the zMode arguments of the "sqlite3" build. For the
-** "sqlite3-wasmfs" build, only "esm" (ES6 Module) is legal.
+** Valid build modes. For the "sqlite3-wasmfs" build, only "esm" (ES6
+** Module) is legal.
*/
#define JS_BUILD_MODES vanilla esm bundler-friendly node
-/* Separator to help eyeballs find the different sections */
+
+/* Separator to help eyeballs find the different output sections */
static const char * zBanner =
"\n########################################################################\n";
/*
+** Flags for use with BuildDef::flags and the 3rd argument to
+** mk_pre_post().
+**
+** Maintenance reminder: do not combine flags within this enum,
+** e.g. LIBMODE_BUNDLER_FRIENDLY=0x02|LIBMODE_ESM, as that will lead
+** to breakage in some of the flag checks.
+*/
+enum LibModeFlags {
+ /* Indicates an ESM module build. */
+ LIBMODE_ESM = 0x01,
+ /* Indicates a "bundler-friendly" build mode. */
+ LIBMODE_BUNDLER_FRIENDLY = 0x02,
+ /* Indicates that this build is unsupported. Such builds are not
+ ** added to the 'all' target. The unsupported builds exist primarily
+ ** for experimentation's sake. */
+ LIBMODE_UNSUPPORTED = 0x04,
+ /* Indicates a node.js-for-node.js build (untested and
+ ** unsupported). */
+ LIBMODE_NODEJS = 0x08,
+ /* Indicates a wasmfs build (untested and unsupported). */
+ LIBMODE_WASMFS = 0x10
+};
+
+/*
+** Info needed for building one combination of JS_BUILD_NAMES and
+** JS_BUILD_MODE, noting that only a subset of those combinations are
+** legal/sensical.
+*/
+struct BuildDef {
+ const char *zName; /* Name from JS_BUILD_NAMES */
+ const char *zMode; /* Name from JS_BUILD_MODES */
+ int flags; /* Flags from LibModeFlags */
+ const char *zJsOut; /* Name of generated sqlite3.js/.mjs */
+ /* TODO: dynamically determine zJsOut based on zName, zMode, and
+ flags. */
+ const char *zCmppD; /* Extra -D... flags for c-pp */
+ const char *zEmcc; /* Extra flags for emcc */
+};
+typedef struct BuildDef BuildDef;
+
+/*
+** The set of WASM builds for the library (as opposed to the apps
+** (fiddle, speedtest1)). This array must end with an empty sentinel
+** entry. Their order is mostly insignificant, but some makefile vars
+** used by some builds are set up by prior builds. Because of that,
+** the (sqlite3, vanilla), (sqlite3, esm), and (sqlite3,
+** bundler-friendly) builds should be defined first (in that order).
+*/
+const BuildDef aBuildDefs[] = {
+ {/* Core build */
+ "sqlite3", "vanilla", 0, "$(sqlite3.js)", 0, 0},
+
+ {/* Core ESM */
+ "sqlite3", "esm", LIBMODE_ESM, "$(sqlite3.mjs)",
+ "-Dtarget=es6-module", 0},
+
+ {/* Core bundler-friendly build. Untested and "not really"
+ ** supported, but required by the downstream npm subproject.
+ ** Testing these would require special-purpose node-based tools and
+ ** custom test apps. Or we can pass them off as-is to the npm
+ ** subproject and they spot failures pretty quickly ;). */
+ "sqlite3", "bundler-friendly",
+ LIBMODE_BUNDLER_FRIENDLY | LIBMODE_ESM,
+ "$(dir.dout)/sqlite3-bundler-friendly.mjs",
+ "$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly", 0},
+
+ {/* node.js mode. Untested and unsupported. */
+ "sqlite3", "node", LIBMODE_UNSUPPORTED | LIBMODE_NODEJS,
+ "$(dir.dout)/sqlite3-node.mjs",
+ "$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node", 0},
+
+ {/* Wasmfs build. Fully unsupported and largely untested. */
+ "sqlite3-wasmfs", "esm" ,
+ LIBMODE_UNSUPPORTED | LIBMODE_WASMFS | LIBMODE_ESM,
+ "$(dir.wasmfs)/sqlite3-wasmfs.mjs",
+ "$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs",
+ "-sEXPORT_ES6 -sUSE_ES6_IMPORT_META"},
+
+ {/*End-of-list sentinel*/0,0,0,0,0,0}
+};
+
+/*
** Emits common vars needed by the rest of the emitted code (but not
** needed by makefile code outside of these generated pieces).
*/
static void mk_prologue(void){
+ /* A 0-terminated list of makefile vars which we expect to have been
+ ** set up by this point in the build process. */
+ char const * aRequiredVars[] = {
+ "dir.top",
+ "dir.api", "dir.dout", "dir.tmp",
+ "sqlite3-license-version.js",
+ "MAKEFILE", "MAKEFILE_LIST",
+ /* Fiddle... */
+ "dir.fiddle", "dir.fiddle-debug",
+ "MAKEFILE.fiddle",
+ "EXPORTED_FUNCTIONS.fiddle",
+ /*"just-testing",*/
+ 0
+ };
+ char const * zVar;
+ int i;
+ pf("%s# Build setup sanity checks...\n", zBanner);
+ for( i = 0; (zVar = aRequiredVars[i]); ++i ){
+ pf("ifeq (,$(%s))\n", zVar);
+ pf(" $(error build process error: expecting make var $$(%s) to "
+ "have been set up by now)\n", zVar);
+ ps("endif");
+ }
pf("%s", zBanner);
ps("# extern-post-js* and extern-pre-js* are files for use with");
ps("# Emscripten's --extern-pre-js and --extern-post-js flags.");
- ps("extern-pre-js.js := $(dir.api)/extern-pre-js.js");
- ps("extern-post-js.js.in := $(dir.api)/extern-post-js.c-pp.js");
+ ps("extern-pre-js.js = $(dir.api)/extern-pre-js.js");
+ ps("extern-post-js.js.in = $(dir.api)/extern-post-js.c-pp.js");
ps("# Emscripten flags for --[extern-][pre|post]-js=... for the");
ps("# various builds.");
- ps("pre-post-common.flags := --extern-pre-js=$(sqlite3-license-version.js)");
- 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)");
+ ps("pre-post-common.flags = --extern-pre-js=$(sqlite3-license-version.js)");
+ ps("# pre-post-jses.deps.* = a list of dependencies for the\n"
+ "# --[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
@@ -127,7 +233,7 @@ static void mk_prologue(void){
"\t\techo -n 'After wasm-opt: '; \\\n"
"\t\tls -l $(1); \\\n"
"\telse \\\n"
- "\t\techo 'WARNING: ignoring wasm-opt failure'; \\\n"
+ "\t\techo 'WARNING: ignoring wasm-opt failure for $(1)'; \\\n"
"\tfi\n",
zOptFlags
);
@@ -137,52 +243,32 @@ static void mk_prologue(void){
}
/*
-** Flags for use with the 3rd argument to mk_pre_post() and
-** mk_lib_mode().
-**
-** Maintenance reminder: do not combine flags within this enum,
-** e.g. LIBMODE_BUNDLER_FRIENDLY=0x02|LIBMODE_ESM, as that will lead
-** to breakage in some of the flag checks.
-*/
-enum LibModeFlags {
- /* Indicates an ESM module build. */
- LIBMODE_ESM = 0x01,
- /* Indicates a "bundler-friendly" build mode. */
- LIBMODE_BUNDLER_FRIENDLY = 0x02,
- /* Indicates to _not_ add this build to the 'all' target. */
- LIBMODE_DONT_ADD_TO_ALL = 0x04,
- /* Indicates a node.js-for-node.js build (untested and
- ** unsupported). */
- LIBMODE_NODEJS = 0x08,
- /* Indicates a wasmfs build (untested and unsupported). */
- LIBMODE_WASMFS = 0x10
-};
-
-/*
** Emits makefile code for setting up values for the --pre-js=FILE,
** --post-js=FILE, and --extern-post-js=FILE emcc flags, as well as
** populating those files.
*/
static void mk_pre_post(const char *zName /* build name */,
const char *zMode /* build mode */,
- int flags /* LIBMODE_... mask */,
const char *zCmppD /* optional -D flags for c-pp for the
** --pre/--post-js files. */){
+/* Very common printf() args combo. */
+#define zNM 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("c-pp.D.%s-%s = %s\n", zNM, zCmppD ? zCmppD : "");
pf("pre-post-%s-%s.flags ?=\n", zNM);
/* --pre-js=... */
- pf("pre-js.js.%s-%s := $(dir.tmp)/pre-js.%s-%s.js\n",
+ pf("pre-js.js.%s-%s = $(dir.tmp)/pre-js.%s-%s.js\n",
zNM, zNM);
- pf("$(pre-js.js.%s-%s): $(MAKEFILE_LIST)\n", zNM);
+ pf("$(pre-js.js.%s-%s): $(MAKEFILE_LIST) $(sqlite3-license-version.js)\n", zNM);
#if 1
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",
+ pf("pre-js.js.%s-%s.intermediary = $(dir.tmp)/pre-js.%s-%s.intermediary.js\n",
zNM, zNM);
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);
@@ -200,17 +286,17 @@ static void mk_pre_post(const char *zName /* build name */,
#endif
/* --post-js=... */
- pf("post-js.js.%s-%s := $(dir.tmp)/post-js.%s-%s.js\n", zNM, zNM);
+ pf("post-js.js.%s-%s = $(dir.tmp)/post-js.%s-%s.js\n", zNM, zNM);
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("extern-post-js.js.%s-%s = $(dir.tmp)/extern-post-js.%s-%s.js\n", zNM, zNM);
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);
/* Combined flags for use with emcc... */
- pf("pre-post-common.flags.%s-%s := "
+ pf("pre-post-common.flags.%s-%s = "
"$(pre-post-common.flags) "
"--post-js=$(post-js.js.%s-%s) "
"--extern-post-js=$(extern-post-js.js.%s-%s)\n", zNM, zNM, zNM);
@@ -219,30 +305,29 @@ static void mk_pre_post(const char *zName /* build name */,
"--pre-js=$(pre-js.js.%s-%s)\n", zNM, zNM, zNM);
/* Set up deps... */
- pf("pre-post-jses.%s-%s.deps := $(pre-post-jses.deps.common) "
+ pf("pre-post-jses.%s-%s.deps = $(pre-post-jses.deps.common) "
"$(post-js.js.%s-%s) $(extern-post-js.js.%s-%s)\n",
zNM, zNM, zNM);
- pf("pre-post-%s-%s.deps := $(pre-post-jses.%s-%s.deps) $(dir.tmp)/pre-js.%s-%s.js\n",
+ 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", zNM, zBanner);
+#undef zNM
}
/*
** Emits rules for the fiddle builds.
-**
*/
-static void mk_fiddle(){
+static void mk_fiddle(void){
int i = 0;
- mk_pre_post("fiddle-module","vanilla", 0, 0);
+ mk_pre_post("fiddle-module","vanilla", 0);
for( ; i < 2; ++i ){
+ /* 0==normal, 1==debug */
const char *zTail = i ? ".debug" : "";
const char *zDir = i ? "$(dir.fiddle-debug)" : "$(dir.fiddle)";
pf("%s# Begin fiddle%s\n", zBanner, zTail);
- pf("fiddle-module.js%s := %s/fiddle-module.js\n", zTail, zDir);
- pf("fiddle-module.wasm%s := "
- "$(subst .js,.wasm,$(fiddle-module.js%s))\n", zTail, zTail);
+ pf("fiddle-module.js%s = %s/fiddle-module.js\n", zTail, zDir);
pf("$(fiddle-module.js%s):%s $(MAKEFILE_LIST) $(MAKEFILE.fiddle) "
"$(EXPORTED_FUNCTIONS.fiddle) "
"$(fiddle.cses) $(pre-post-fiddle-module-vanilla.deps) "
@@ -254,7 +339,9 @@ static void mk_fiddle(){
pf("\t$(bin.emcc) -o $@ $(fiddle.emcc-flags%s) "
"$(pre-post-fiddle-module-vanilla.flags) $(fiddle.cses)\n",
zTail);
- pf("\t$(maybe-wasm-strip) $(fiddle-module.wasm%s)\n", zTail);
+ ps("\t@chmod -x $(basename $@).wasm");
+ ps("\t@$(maybe-wasm-strip) $(basename $@).wasm");
+ ps("\t@$(SQLITE.strip-createExportWrapper)");
pf("\t@cp -p $(SOAP.js) $(dir $@)\n");
if( 1==i ){/*fiddle.debug*/
pf("\tcp -p $(dir.fiddle)/index.html "
@@ -263,13 +350,13 @@ static void mk_fiddle(){
"$(dir $@)\n");
}
pf("\t@for i in %s/*.*js %s/*.html %s/*.wasm; do \\\n"
- "\t\ttest -f $${i} || continue; \\\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{
- ps("fiddle-debug: $(fiddle-module-debug.js)");
+ ps("fiddle-debug: $(fiddle-module.js.debug)");
}
pf("# End fiddle%s%s", zTail, zBanner);
}
@@ -280,138 +367,110 @@ static void mk_fiddle(){
** by the combination of zName and zMode, each of which must be values
** from JS_BUILD_NAMES resp. JS_BUILD_MODES.
*/
-static void mk_lib_mode(const char *zName /* build name */,
- const char *zMode /* build mode */,
- int flags /* LIBMODE_... mask */,
- const char *zApiJsOut /* name of generated sqlite3-api.js/.mjs */,
- 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 */){
+static void mk_lib_mode(const BuildDef * pB){
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 );
- assert( zJsOut );
- if( !zCmppD ) zCmppD = "";
- if( !zEmcc ) zEmcc = "";
+ assert( pB->zName );
+ assert( pB->zMode );
+ assert( pB->zJsOut );
+/* Very common printf() args combo. */
+#define zNM pB->zName, pB->zMode
- pf("%s# Begin build [%s-%s]\n", zBanner, zNM);
- pf("# zApiJsOut=%s\n# zJsOut=%s\n# zCmppD=%s\n", zApiJsOut, zJsOut, zCmppD);
- pf("$(info Setting up build [%s-%s]: %s)\n", zNM, zJsOut);
- mk_pre_post(zNM, flags, zCmppD);
+ pf("%s# Begin build [%s-%s]. flags=0x%02x\n", zBanner, zNM, pB->flags);
+ pf("# zJsOut=%s\n# zCmppD=%s\n", pB->zJsOut,
+ pB->zCmppD ? pB->zCmppD : "<none>");
+ pf("$(info Setting up build [%s-%s]: %s)\n", zNM, pB->zJsOut);
+ mk_pre_post(zNM, pB->zCmppD);
pf("\nemcc.flags.%s.%s ?=\n", zNM);
- if( zEmcc[0] ){
- pf("emcc.flags.%s.%s += %s\n", zNM, zEmcc);
+ if( pB->zEmcc && pB->zEmcc[0] ){
+ pf("emcc.flags.%s.%s += %s\n", zNM, pB->zEmcc);
}
- pf("$(eval $(call SQLITE.CALL.C-PP.FILTER, $(sqlite3-api.js.in), %s, %s))\n",
- zApiJsOut, zCmppD);
- /* target zJsOut */
- pf("%s: %s $(MAKEFILE_LIST) $(sqlite3-wasm.cfiles) $(EXPORTED_FUNCTIONS.api) "
+ /* target pB->zJsOut */
+ pf("%s: $(MAKEFILE_LIST) $(sqlite3-wasm.cfiles) $(EXPORTED_FUNCTIONS.api) "
"$(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). */
+ ** 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);
+ pB->zJsOut, zNM);
pf("\t@echo \"Building $@ ...\"\n");
+ if( LIBMODE_UNSUPPORTED & pB->flags ){
+ ps("\t@echo 'ACHTUNG: $@ is an unsupported build. "
+ "Use at your own risk.'");
+ }
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$(emcc.jsflags) -sENVIRONMENT=$(emcc.environment.%s) \\\n",
+ pB->zMode);
pf("\t\t$(pre-post-%s-%s.flags) \\\n", zNM);
- pf("\t\t$(emcc.flags.%s) $(emcc.flags.%s.%s) \\\n", zName, zNM);
+ pf("\t\t$(emcc.flags.%s) $(emcc.flags.%s.%s) \\\n", pB->zName, zNM);
pf("\t\t$(cflags.common) $(SQLITE_OPT) \\\n"
"\t\t$(cflags.%s) $(cflags.%s.%s) \\\n"
- "\t\t$(cflags.wasm_extra_init) $(sqlite3-wasm.cfiles)\n", zName, zNM);
- if( (LIBMODE_ESM & flags) || (LIBMODE_NODEJS & flags) ){
+ "\t\t$(cflags.wasm_extra_init) $(sqlite3-wasm.cfiles)\n", pB->zName, zNM);
+ if( (LIBMODE_ESM & pB->flags) || (LIBMODE_NODEJS & pB->flags) ){
/* 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 SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,%d)\n",
- (LIBMODE_WASMFS & flags) ? 1 : 0);
+ (LIBMODE_WASMFS & pB->flags) ? 1 : 0);
}
- pf("\t@chmod -x %s; \\\n"
- "\t\t$(maybe-wasm-strip) %s;\n",
- zWasmOut, zWasmOut);
+ pf("\t@chmod -x %s\n", zWasmOut);
+ pf("\t@$(maybe-wasm-strip) %s\n", 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. */;
+ ps("\t@$(SQLITE.strip-createExportWrapper)");
/*
- ** 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
+ ** The above $(bin.emcc) call will write pB->zJsOut, a.k.a. $@, and
+ ** will create a like-named .wasm file (zWasmOut). That .wasm file
+ ** name gets hard-coded into $@ 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( (LIBMODE_BUNDLER_FRIENDLY & flags)
- || (LIBMODE_NODEJS & flags) ){
- pf("\t@echo 'Patching $@ for %s.wasm...'; \\\n", zName);
+ if( (LIBMODE_BUNDLER_FRIENDLY & pB->flags) ){
+ pf("\t@echo 'Patching $@ for %s.wasm...'; \\\n", pB->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);
+ /* ^^^^^^ reminder: Mac/BSD sed has no -i flag but this
+ ** build process explicitly requires a Linux system. */
+ zNM, pB->zName);
pf("\t@ls -la $@\n");
- if( LIBMODE_BUNDLER_FRIENDLY & flags ){
+ if( LIBMODE_BUNDLER_FRIENDLY & pB->flags ){
/* Avoid a 3rd occurrence of the bug fixed by 65798c09a00662a3,
** which was (in two cases) caused by makefile refactoring and
** not recognized until after a release was made with the broken
- ** sqlite3-bundler-friendly.mjs: */
+ ** sqlite3-bundler-friendly.mjs (which is used by the npm
+ ** subproject but is otherwise untested/unsupported): */
pf("\t@if grep -e '^ *importScripts(' $@; "
"then echo 'ERROR: bug fixed in 65798c09a00662a3 has re-appeared'; "
"exit 1; fi;\n");
}
-
}else{
pf("\t@ls -la %s $@\n", zWasmOut);
}
- if( 0==(LIBMODE_DONT_ADD_TO_ALL & flags) ){
- pf("all: %s\n", zJsOut);
+ if( 0==(LIBMODE_UNSUPPORTED & pB->flags) ){
+ pf("all: %s\n", pB->zJsOut);
}
pf("# End build [%s-%s]%s", zNM, zBanner);
+#undef zNM
}
int main(void){
int rc = 0;
+ const BuildDef *pB = &aBuildDefs[0];
pf("# What follows was GENERATED by %s. Edit at your own risk.\n", __FILE__);
mk_prologue();
- mk_lib_mode("sqlite3", "vanilla", 0,
- "$(sqlite3-api.js)", "$(sqlite3.js)", 0, 0);
- mk_lib_mode("sqlite3", "esm", LIBMODE_ESM,
- "$(sqlite3-api.mjs)", "$(sqlite3.mjs)",
- "-Dtarget=es6-module", 0);
- mk_lib_mode("sqlite3", "bundler-friendly",
- LIBMODE_BUNDLER_FRIENDLY | LIBMODE_ESM,
- "$(sqlite3-api-bundler-friendly.mjs)",
- "$(sqlite3-bundler-friendly.mjs)",
- "$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly", 0);
- mk_lib_mode("sqlite3" , "node",
- LIBMODE_NODEJS | LIBMODE_DONT_ADD_TO_ALL,
- "$(sqlite3-api-node.mjs)", "$(sqlite3-node.mjs)",
- "$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node", 0);
- mk_lib_mode("sqlite3-wasmfs", "esm" ,
- LIBMODE_WASMFS | LIBMODE_ESM | LIBMODE_DONT_ADD_TO_ALL,
- /* The sqlite3-wasmfs build is optional and needs to be invoked
- ** conditionally using info we don't have here. */
- "$(sqlite3-api-wasmfs.mjs)", "$(sqlite3-wasmfs.mjs)",
- "$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs",
- "-sEXPORT_ES6 -sUSE_ES6_IMPORT_META");
-
+ for( ; pB->zName; ++pB ){
+ mk_lib_mode( pB );
+ }
mk_fiddle();
- mk_pre_post("speedtest1","vanilla", 0, 0);
- mk_pre_post("speedtest1-wasmfs","esm", 0,
+ mk_pre_post("speedtest1","vanilla", 0);
+ mk_pre_post("speedtest1-wasmfs","esm",
"$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs");
return rc;
}
diff --git a/ext/wasm/speedtest1.html b/ext/wasm/speedtest1.html
index 3bad62006..a841c7fa0 100644
--- a/ext/wasm/speedtest1.html
+++ b/ext/wasm/speedtest1.html
@@ -23,11 +23,10 @@
</figure>
<div class="emscripten" id="module-status">Downloading...</div>
<div class="emscripten">
- <progress value="0" max="100" id="module-progress" hidden='1'></progress>
+ <progress value="0" max="100" id="module-progress" hidden='1'></progress>
</div><!-- /emscripten bits -->
<div class='warning'>This page starts running the main exe when it loads, which will
- block the UI until it finishes! Adding UI controls to manually configure and start it
- are TODO.</div>
+ block the UI until it finishes!</div>
</div>
<div class='warning'>Achtung: running it with the dev tools open may
<em>drastically</em> slow it down. For faster results, keep the dev
@@ -118,7 +117,7 @@
argv.push("--vfs", vfs);
log2('',"Using VFS:",vfs);
if('kvvfs' === vfs){
- forceSize = 2 /* 5 uses approx. 4.96mb */;
+ forceSize = 2 /* >2 is too big as of mid-2025 */;
dbFile = 'session';
log2('warning',"kvvfs VFS: forcing --size",forceSize,
"and filename '"+dbFile+"'.");
diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js
index 5b94c7c05..dd70024ab 100644
--- a/ext/wasm/tester1.c-pp.js
+++ b/ext/wasm/tester1.c-pp.js
@@ -41,7 +41,7 @@
ES6 worker module build:
- ./c-pp -f tester1.c-pp.js -o tester1-esm.js -Dtarget=es6-module
+ ./c-pp -f tester1.c-pp.js -o tester1-esm.mjs -Dtarget=es6-module
*/
//#if target=es6-module
import {default as sqlite3InitModule} from './jswasm/sqlite3.mjs';
@@ -221,7 +221,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
else if(filter instanceof Function) pass = filter(err);
else if('string' === typeof filter) pass = (err.message === filter);
if(!pass){
- throw new Error(msg || ("Filter rejected this exception: "+err.message));
+ throw new Error(msg || ("Filter rejected this exception: <<"+err.message+">>"));
}
return this;
},
@@ -1209,6 +1209,104 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
}
})
+
+ ////////////////////////////////////////////////////////////////////
+ .t({
+ name: "oo1.DB/Stmt.wrapDbHandle()",
+ test: function(sqlite3){
+ /* Maintenance reminder: this function is early in the list to
+ demonstrate that the wrappers for this.db created by this
+ function do not interfere with downstream tests, e.g. by
+ closing this.db.pointer. */
+ //sqlite3.config.debug("Proxying",this.db);
+ const misuseMsg = "SQLITE_MISUSE: Argument must be a WASM sqlite3 pointer";
+ T.mustThrowMatching(()=>sqlite3.oo1.DB.wrapHandle(this.db), misuseMsg)
+ .mustThrowMatching(()=>sqlite3.oo1.DB.wrapHandle(0), misuseMsg);
+ let dw = sqlite3.oo1.DB.wrapHandle(this.db.pointer);
+ //sqlite3.config.debug('dw',dw);
+ T.assert( dw, '!!dw' )
+ .assert( dw instanceof sqlite3.oo1.DB, 'dw is-a oo1.DB' )
+ .assert( dw.pointer, 'dw.pointer' )
+ .assert( dw.pointer === this.db.pointer, 'dw.pointer===db.pointer' )
+ .assert( dw.filename === this.db.filename, 'dw.filename===db.filename' );
+
+ T.assert( dw === dw.exec("select 1") );
+ let q;
+ try {
+ q = dw.prepare("select 1");
+ T.assert( q.step() )
+ .assert( !q.step() );
+ }finally{
+ if( q ) q.finalize();
+ }
+ dw.close();
+ T.assert( !dw.pointer )
+ .assert( this.db === this.db.exec("select 1") );
+ dw = undefined;
+
+ let pDb = 0, pStmt = 0;
+ const stack = wasm.pstack.pointer;
+ try {
+ const ppOut = wasm.pstack.allocPtr();
+ T.assert( 0===wasm.peekPtr(ppOut) );
+ let rc = capi.sqlite3_open_v2( ":memory:", ppOut,
+ capi.SQLITE_OPEN_CREATE
+ | capi.SQLITE_OPEN_READWRITE,
+ 0);
+ T.assert( 0===rc, 'open_v2()' );
+ pDb = wasm.peekPtr(ppOut);
+ wasm.pokePtr(ppOut, 0);
+ T.assert( pDb>0, 'pDb>0' );
+ const pTmp = pDb;
+ dw = sqlite3.oo1.DB.wrapHandle(pDb, true);
+ pDb = 0;
+ //sqlite3.config.debug("dw",dw);
+ T.assert( pTmp===dw.pointer, 'pDb===dw.pointer' );
+ T.assert( dw.filename === "", "dw.filename == "+dw.filename );
+ let q = dw.prepare("select 1");
+ try {
+ T.assert( q.step(), "step()" );
+ T.assert( !q.step(), "!step()" );
+ }finally{
+ q.finalize();
+ q = undefined;
+ }
+ T.assert( dw===dw.exec("select 1") );
+ dw.affirmOpen();
+ const select1 = "select 1";
+ rc = capi.sqlite3_prepare_v2( dw, select1, -1, ppOut, 0 );
+ T.assert( 0===rc, 'prepare_v2() rc='+rc );
+ pStmt = wasm.peekPtr(ppOut);
+ T.assert( pStmt && wasm.isPtr(pStmt), 'pStmt is valid?' );
+ try {
+ //log( "capi.sqlite3_sql() =",capi.sqlite3_sql(pStmt));
+ T.assert( select1 === capi.sqlite3_sql(pStmt), 'SQL mismatch' );
+ q = sqlite3.oo1.Stmt.wrapHandle(dw, pStmt, false);
+ //log("q@"+pStmt+" does not own handle");
+ T.assert( q.step(), "step()" )
+ .assert( !q.step(), "!step()" );
+ q.finalize();
+ q = undefined;
+ T.assert( select1 === capi.sqlite3_sql(pStmt), 'SQL mismatch'
+ /* This will fail if we've mismanaged pStmt's lifetime */);
+ q = sqlite3.oo1.Stmt.wrapHandle(dw, pStmt, true);
+ pStmt = 0;
+ q.reset();
+ T.assert( q.step(), "step()" )
+ .assert( !q.step(), "!step()" );
+ }finally{
+ if( pStmt ) capi.sqlite3_finalize(pStmt)
+ if( q ) q.finalize();
+ }
+
+ }finally{
+ wasm.pstack.restore(stack);
+ if( pDb ){ capi.sqlite3_close_v2(pDb); }
+ else if( dw ){ dw.close(); }
+ }
+ }
+ })/*oo1.DB/Stmt.wrapHandle()*/
+
////////////////////////////////////////////////////////////////////
.t('sqlite3_db_config() and sqlite3_db_status()', function(sqlite3){
let rc = capi.sqlite3_db_config(this.db, capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, 0, 0);
@@ -1268,6 +1366,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
/columnCount property is read-only/)
.assert(1===st.columnCount)
.assert(0===st.parameterCount)
+ .assert(0===capi.sqlite3_bind_parameter_count(st))
.mustThrow(()=>st.bind(1,null))
.assert(true===st.step())
.assert(3 === st.get(0))
@@ -1490,6 +1589,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
let st = db.prepare("update t set b=:b where a='blob'");
try {
T.assert(0===st.columnCount)
+ .assert(1===st.parameterCount)
+ .assert(1===capi.sqlite3_bind_parameter_count(st))
.assert( false===st.isReadOnly() );
const ndx = st.getParamIndex(':b');
T.assert(1===ndx);
@@ -3329,6 +3430,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
db.exec("create table t(a)");
const stmt = db.prepare("insert into t(a) values($a)");
T.assert( 1===capi.sqlite3_bind_parameter_count(stmt) )
+ .assert( 1===stmt.parameterCount )
.assert( 1===capi.sqlite3_bind_parameter_index(stmt, "$a") )
.assert( 0===capi.sqlite3_bind_parameter_index(stmt, ":a") )
.assert( 1===stmt.getParamIndex("$a") )
diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make
index 2c6fa35bd..0d1fb4043 100644
--- a/ext/wasm/wasmfs.make
+++ b/ext/wasm/wasmfs.make
@@ -5,31 +5,28 @@
# sqlite3.wasm. It is intended to be "include"d from the main
# GNUMakefile.
########################################################################
-MAKEFILE.wasmfs := $(lastword $(MAKEFILE_LIST))
+MAKEFILE.wasmfs = $(lastword $(MAKEFILE_LIST))
# ensure that the following message starts on line 10 or higher for proper
# $(warning) alignment!
ifneq (1,$(MAKING_CLEAN))
$(warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)
- $(warning !! The WASMFS build is not well-supported. WASMFS is a proverbial)
- $(warning !! moving target, sometimes changing in incompatible ways between)
- $(warning !! Emscripten versions. This build is provided for adventurous folks)
- $(warning !! and is not a supported deliverable of the SQLite project.)
+ $(warning !! The WASMFS build is unsupported. Use at your own risk.
$(warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)
endif
-sqlite3-wasmfs.js := $(dir.wasmfs)/sqlite3-wasmfs.js
-sqlite3-wasmfs.wasm := $(dir.wasmfs)/sqlite3-wasmfs.wasm
+sqlite3-wasmfs.js = $(dir.wasmfs)/sqlite3-wasmfs.js
+sqlite3-wasmfs.wasm = $(dir.wasmfs)/sqlite3-wasmfs.wasm
########################################################################
# emcc flags for .c/.o.
-cflags.sqlite3-wasmfs :=
+cflags.sqlite3-wasmfs =
cflags.sqlite3-wasmfs += -std=c99 -fPIC
cflags.sqlite3-wasmfs += -pthread
cflags.sqlite3-wasmfs += -DSQLITE_ENABLE_WASMFS
########################################################################
# emcc flags specific to building the final .js/.wasm file...
-emcc.flags.sqlite3-wasmfs :=
+emcc.flags.sqlite3-wasmfs =
emcc.flags.sqlite3-wasmfs += \
-sEXPORTED_RUNTIME_METHODS=wasmMemory
# wasmMemory ==> for -sIMPORTED_MEMORY
@@ -43,7 +40,7 @@ emcc.flags.sqlite3-wasmfs += -Wno-limited-postlink-optimizations
emcc.flags.sqlite3-wasmfs += -sMEMORY64=0
emcc.flags.sqlite3-wasmfs += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.128)
# ^^^^ 64MB is not enough for WASMFS/OPFS test runs using batch-runner.js
-sqlite3-wasmfs.fsflags := -pthread -sWASMFS \
+sqlite3-wasmfs.fsflags = -pthread -sWASMFS \
-sPTHREAD_POOL_SIZE=1 \
-sERROR_ON_UNDEFINED_SYMBOLS=0 -sLLD_REPORT_UNDEFINED
# ^^^^^ why undefined symbols are necessary for the wasmfs build is anyone's guess.
@@ -53,10 +50,9 @@ emcc.flags.sqlite3-wasmfs += -sALLOW_MEMORY_GROWTH=0
# USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code slowly,
# see https://github.com/WebAssembly/design/issues/1271 [-Wpthreads-mem-growth]
# And, indeed, it runs slowly if memory is permitted to grow.
-#emcc.flags.sqlite3-wasmfs.vanilla :=
-#emcc.flags.sqlite3-wasmfs.esm := -sEXPORT_ES6 -sUSE_ES6_IMPORT_META
-all: $(sqlite3-wasmfs.mjs)
-$(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs): $(MAKEFILE.wasmfs)
+#emcc.flags.sqlite3-wasmfs.vanilla =
+#emcc.flags.sqlite3-wasmfs.esm = -sEXPORT_ES6 -sUSE_ES6_IMPORT_META
+$(sqlite3-wasmfs.js) $(dir.wasmfs)/sqlite3-wasmfs.mjs: $(MAKEFILE.wasmfs)
########################################################################
# Build quirk: we cannot build BOTH .js and .mjs with our current
# build infrastructure because the supplemental *.worker.js files get
@@ -68,31 +64,31 @@ $(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs): $(MAKEFILE.wasmfs)
# names is that it means that the corresponding .wasm file is also
# built/saved multiple times. It is likely that anyone wanting to use
# WASMFS will want an ES6 module, so that's what we build here.
-wasmfs.build.ext := mjs
-$(sqlite3-wasmfs.js) $(sqlite3-wasmfs.mjs): $(SOAP.js.bld)
+wasmfs.build.ext = mjs
+$(sqlite3-wasmfs.js) $(dir.wasmfs)/sqlite3-wasmfs.mjs: $(SOAP.js.bld)
ifeq (js,$(wasmfs.build.ext))
$(sqlite3-wasmfs.wasm): $(sqlite3-wasmfs.js)
wasmfs: $(sqlite3-wasmfs.js)
else
- $(sqlite3-wasmfs.wasm): $(sqlite3-wasmfs.mjs)
- wasmfs: $(sqlite3-wasmfs.mjs)
+ $(sqlite3-wasmfs.wasm): $(dir.wasmfs)/sqlite3-wasmfs.mjs
+ wasmfs: $(dir.wasmfs)/sqlite3-wasmfs.mjs
endif
+all: wasmfs
########################################################################
# speedtest1 for wasmfs.
-speedtest1-wasmfs.mjs := $(dir.wasmfs)/speedtest1-wasmfs.mjs
-speedtest1-wasmfs.wasm := $(subst .mjs,.wasm,$(speedtest1-wasmfs.mjs))
-emcc.flags.speedtest1-wasmfs := $(sqlite3-wasmfs.fsflags)
+speedtest1-wasmfs.mjs = $(dir.wasmfs)/speedtest1-wasmfs.mjs
+speedtest1-wasmfs.wasm = $(subst .mjs,.wasm,$(speedtest1-wasmfs.mjs))
+emcc.flags.speedtest1-wasmfs = $(sqlite3-wasmfs.fsflags)
emcc.flags.speedtest1-wasmfs += $(SQLITE_OPT)
emcc.flags.speedtest1-wasmfs += -sALLOW_MEMORY_GROWTH=0
emcc.flags.speedtest1-wasmfs += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.128)
-#$(eval $(call call-make-pre-js,speedtest1-wasmfs,ems))
+#$(info speedtest DEPS=pre-post-sqlite3-wasmfs-esm.deps=$(pre-post-sqlite3-wasmfs-esm.deps))
$(speedtest1-wasmfs.mjs): $(speedtest1.cfiles) $(sqlite3-wasmfs.js) \
- $(MAKEFILE) $(MAKEFILE.wasmfs) \
- $(pre-post-sqlite3-wasmfs-esm.deps) \
+ $(MAKEFILE) $(MAKEFILE.wasmfs) $(pre-post-sqlite3-wasmfs-esm.deps) \
$(EXPORTED_FUNCTIONS.speedtest1)
@echo "Building $@ ..."
- $(emcc.bin) \
+ $(bin.emcc) \
$(pre-post-sqlite3-wasmfs-esm.flags) \
$(cflags.common) \
$(cflags.sqlite3-wasmfs) \
diff --git a/main.mk b/main.mk
index 34c1cc3cf..40d059ad4 100644
--- a/main.mk
+++ b/main.mk
@@ -2113,6 +2113,10 @@ tool-zip: testfixture$(T.exe) sqlite3$(T.exe) sqldiff$(T.exe) \
sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe) $(TOP)/tool/mktoolzip.tcl
strip sqlite3$(T.exe) sqldiff$(T.exe) sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe)
./testfixture$(T.exe) $(TOP)/tool/mktoolzip.tcl
+snapshot-zip: testfixture$(T.exe) sqlite3$(T.exe) sqldiff$(T.exe) \
+ sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe) $(TOP)/tool/mktoolzip.tcl
+ strip sqlite3$(T.exe) sqldiff$(T.exe) sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe)
+ ./testfixture$(T.exe) $(TOP)/tool/mktoolzip.tcl --snapshot
clean-tool-zip:
rm -f sqlite-tools-*.zip
clean: clean-tool-zip
diff --git a/manifest b/manifest
index 696aaba12..88cbbe017 100644
--- a/manifest
+++ b/manifest
@@ -1,12 +1,12 @@
-C Avoid\sinvoking\sthe\spreupdate\shook\sfrom\swithin\ssqlite3_blob_write()\sif\sthe\scursor\sis\salready\sinvalid.
-D 2025-07-07T11:37:55.009
+C Fiddle:\smove\sthe\sAbout\sbutton\sinto\sthe\sheader\sbar\sand\sget\sit\sworking\stogether\swith\sthe\soptional\sjquery.terminal\sconsole\smode.
+D 2025-08-02T13:21:07.208
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
F Makefile.in a6c14b6906f5322920dd5d1ff9a84808337911068f2a6e777ec7088a87cbc3a8
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
-F Makefile.msc ec2011bbdfc917d6a1c7c173dabb29c14ead0dd8e2e0b67278a00ae4ba576a77
+F Makefile.msc e17608035fb75d3e5b322f846c6c3cbe80a5f34abf5df9e400c40dd55436dc14
F README.md e28077cfbef795e99c9c75ed95aa7257a1166709b562076441a8506ac421b7c1
F VERSION 16eddb43056a79c1977427ab7a05f3457c373fa159dcdced8754eb89ce7e06b8
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -26,7 +26,7 @@ F autoconf/tea/Makefile.in bf6b43eafcd18766d81a8f0085cfc9cb051d8abae9031a8e7c3f5
F autoconf/tea/README.txt 23475876343498ef2b514cc7510e8f1559a17e8e03fbc7a41c1c8a3b89e7b7e3
F autoconf/tea/_teaish.tester.tcl.in 8253b44be88e2e3f21de95a65d3a90c2be8e70b7bdd08a5b80e337ba7402f8f1
F autoconf/tea/auto.def ce95b9450e2fa4ba5dc857e208fe10f4e6f2d737796ac3278aee6079db417529
-F autoconf/tea/configure d0b12b984edca6030d1976375b80157ac78b5b90a5b4f0dcee39357f63f4a80b x
+F autoconf/tea/configure 993eb27dafb35253965f9c0eb0eeefd113cae0508361c8fd90a4b58c3caf14ec x
F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
F autoconf/tea/pkgIndex.tcl.in e07da6b94561f4aa382bab65b1ccceb04701b97bf59d007c1d1f20a222b22d07
F autoconf/tea/teaish.tcl 81feb417e718ed75cdd7e2fdf6771f3da80dae97377a90c4d5b62b3754abbf1d
@@ -46,7 +46,7 @@ F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e
F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e
F autosetup/jimsh0.c 563b966c137a4ce3c9333e5196723b7ac0919140a9d7989eb440463cd855c367
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
-F autosetup/proj.tcl 6aac1eb3059fc511c8e1659f33b96eb2a216a371ed9b28be6661374061be9b15
+F autosetup/proj.tcl 7eaa83ccb6f5b250ee9192fa914918b0e2075edacf984c75fbf82cc2a6449c6c
F autosetup/sqlite-config.tcl 3177dedd7bd49465fa06677fd743c4966eee5702a9ddf4914c2c1af5e5972a52
F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9
F autosetup/teaish/README.txt b40071e6f8506500a2f7f71d5fc69e0bf87b9d7678dd9da1e5b4d0acbf40b1ca
@@ -113,7 +113,7 @@ F ext/fts5/fts5_buffer.c f1e6d0324d7c55329d340673befc26681a372a4d36086caa8d1ec7d
F ext/fts5/fts5_config.c e7d8dd062b44a66cd77e5a0f74f23a2354cd1f3f8575afb967b2773c3384f7f8
F ext/fts5/fts5_expr.c be9e5f7f11d87e7bd3680832c93c13050fe351994b5052b0215c2ef40312c23a
F ext/fts5/fts5_hash.c a6266cedd801ab7964fa9e74ebcdda6d30ec6a96107fa24148ec6b7b5b80f6e0
-F ext/fts5/fts5_index.c ed562b75c87efaa80c40e55f6c1102904728e35baa293c371f96b1c8e151399d
+F ext/fts5/fts5_index.c 2a1be0fb3c1b185f84b08b8032ba332c82defa182ff125833c0fecba0a4938b0
F ext/fts5/fts5_main.c e558225168845dc708abeb2ad10415696e5a3249bcba1810ba3c7ef80764962e
F ext/fts5/fts5_storage.c 19bc7c4cbe1e6a2dd9849ef7d84b5ca1fcbf194cefc3e386b901e00e08bf05c2
F ext/fts5/fts5_tcl.c 7fb5a3d3404099075aaa2457307cb459bbc257c0de3dbd52b1e80a5b503e0329
@@ -162,12 +162,12 @@ F ext/fts5/test/fts5contentless4.test ec34dc69ef474ca9997dae6d91e072906e0e9a5a4b
F ext/fts5/test/fts5contentless5.test 38cd0392c730dc7090c550321ce3c24ba4c392bc97308b51a4180e9959dca7b5
F ext/fts5/test/fts5corrupt.test 237fce1c3261bb3a5bec333b0f0dbf5b105ec32627ef14cccbda3cfe13833193
F ext/fts5/test/fts5corrupt2.test 4a03a158c2cb617c9f76d26b35c1ef2534124bc0bbddcea38dfd5b170ebea27b
-F ext/fts5/test/fts5corrupt3.test 03a6118a8fe5a7c217c28c92b6b25ba04643640a4ac0a1d2b8d10de8191dc5f4
+F ext/fts5/test/fts5corrupt3.test 43d6a836892d79ab738ab89b3b6f4ae46c07ee966193e4b357bbb14e7f81d5da
F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733
F ext/fts5/test/fts5corrupt5.test 73985d4fe6d8f0d5d5c7bcf79ae7c6522c376cd6ad710a0ff2f26e0c2e222abe
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
F ext/fts5/test/fts5corrupt7.test 814aab492d7a09abb5bfdd81cc66fc206d7f3868f9a3bae91876e02efc466fb3
-F ext/fts5/test/fts5corrupt8.test 7618b102b9b3a5a3494271f4975ab5837e2fb3f61f5adfcdeeb31772c859e6df
+F ext/fts5/test/fts5corrupt8.test 0b10750caf8aa23fa1c379ca4caf6130d41454505e4d5315590f4061eedcbe44
F ext/fts5/test/fts5delete.test 2a5008f8b1174ef41d1974e606928c20e4f9da77d9f8347aed818994d89cced4
F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
@@ -197,11 +197,11 @@ F ext/fts5/test/fts5first.test bfd685b96905bf541d99d8644e0a7219d1d833455a08ab64e
F ext/fts5/test/fts5full.test 97d263c1072f4a560929cca31e70f65d2ae232610e17e6affcf7e979df59547b
F ext/fts5/test/fts5fuzz1.test 238d8c45f3b81342aa384de3e581ff2fa330bf922a7b69e484bbc06051a1080e
F ext/fts5/test/fts5hash.test fd3e0367fbf0b0944d6936fdb22696350f57b9871069c6766251578a103e8a14
-F ext/fts5/test/fts5integrity.test 646796671205dae46af5bb12a49b5696483cfe8e12d71d21454940b13ace95ab
+F ext/fts5/test/fts5integrity.test c423ce16fd1ccadcac7fc22f794226b2bb00f5a187c0ab1d9f8502521b1bae05
F ext/fts5/test/fts5integrity2.test 4c3636615c0201232c44a8105d5cb14fd5499fd0ee3014d7ffd7e83aac76ece8
F ext/fts5/test/fts5interrupt.test 20d04204d3e341b104c0c24a41596b6393a3a81eba1044c168db0e106f9ac92c
F ext/fts5/test/fts5lastrowid.test f36298a1fb9f988bde060a274a7ce638faa9c38a31400f8d2d27ea9373e0c4a1
-F ext/fts5/test/fts5leftjoin.test c0b4cafb9661379e576dc4405c0891d8fcc2782680740513c4d1fc114b43d4ad
+F ext/fts5/test/fts5leftjoin.test 1c14b51f4d1344a89e488160882f05a2246dd7e70c5cf077c8fb473e03c66338
F ext/fts5/test/fts5limits.test 8ab67cf5d311c124b6ceb0062d0297767176df4572d955fce79fa43004dff01c
F ext/fts5/test/fts5locale.test 83ba7ee12628b540d3098f39c39c1de0c0440eddff8f7512c8c698d0c4a3ae3c
F ext/fts5/test/fts5matchinfo.test bc9e74157773db7f00aec1e85587f1145956ebdf1672c136f0f04323b2752aa0
@@ -456,7 +456,7 @@ F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505
F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20
F ext/misc/vfsstat.c 0b23c0a69a2b63dc0ef0af44f9c1fc977300c480a1f7a9814500369d8211f56e
F ext/misc/vfstrace.c 0e4b8b17ac0675ea90f6d168d8214687e06ca3efbc0060aad4814994d82b41fb
-F ext/misc/vtablog.c 62d54fb0f4ff9df72ffa44f4a226451a964e1d96ac550f2b8906feebdad00b58
+F ext/misc/vtablog.c 9f7e02e9e8de585f3bfb48405db36c2eb4b680a23a67d7a4b738dd20f6ad7aec
F ext/misc/vtshim.c e5bce24ab8c532f4fdc600148718fe1802cb6ed57417f1c1032d8961f72b0e8f
F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f65b4fc0668
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
@@ -539,7 +539,7 @@ F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c3350
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 734aa36238bcd2dee91db5dba107d5fcbdb02396612811377a8ad50f1272b1c1
F ext/rtree/geopoly.c f0573d5109fdc658a180db0db6eec86ab2a1cf5ce58ec66cbf3356167ea757eb
-F ext/rtree/rtree.c a1f04a2013bc8f982307760615d1cee591355ac4723c74c3761c128d5c6954ab
+F ext/rtree/rtree.c 86967c5a501f895b9705366b8cd9c37f15a9ebdff770ceb719c7deeeb2c22b72
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
F ext/rtree/rtree1.test e0608db762b2aadca0ecb6f97396cf66244490adc3ba88f2a292b27be3e1da3e
F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
@@ -557,7 +557,7 @@ F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c
F ext/rtree/rtreeE.test e65d3fc625da1800b412fc8785817327d43ccfec5f5973912d8c9e471928caa9
F ext/rtree/rtreeF.test 81ffa7ef51c4e4618d497a57328c265bf576990c7070633b623b23cd450ed331
F ext/rtree/rtreeG.test 1b9ca6e3effb48f4161edaa463ddeaa8fca4b2526d084f9cbf5dbe4e0184939c
-F ext/rtree/rtreeH.test 0885151ee8429242625600ae47142cca935332c70a06737f35af53a7bd7aaf90
+F ext/rtree/rtreeH.test c304651ee87dbb60296366d2c802f3043c3d00506c79a5c85bd20f2c9274498b
F ext/rtree/rtreeI.test 608e77f7fde9be5a12eae316baef640fffaafcfa90a3d67443e78123e19c4ca4
F ext/rtree/rtreeJ.test 93227ccd4d6c328f5ac46a902b8880041509dd2d68f6ce71560f0d8ab5bb507a
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
@@ -594,6 +594,7 @@ F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
+F ext/session/sessionI.test 11e7b6729fc942982a5104a40132f70a2e964d64d60dc5809b8206465af74822
F ext/session/session_common.tcl a31f537a929a695a852d241c9434f2847cadf329856401921139fbb03a5a7697
F ext/session/session_gen.test 942a0002df10da53c45b40b581cc3ed25e7ff42bda1e7ba497273dc2887aa8e6
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
@@ -617,11 +618,11 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
-F ext/session/sqlite3session.c 6b0877fe1ab832aa4b85eaca72606dfd1630a1363a1be7af10ee1042a5ec719e
-F ext/session/sqlite3session.h 9bb1a6687b467764b35178dc29bbd2c57ab8cd3acdc8a62f088c34ad17e4fe2b
-F ext/session/test_session.c 2ddff73ea368d827028c32851b291416e1008845832feb27b751d15e57e13cc3
+F ext/session/sqlite3session.c 19e14bcca2fbc63a8022ffd708ea6e6986c4003a1e9bbca9b2989fd230362e15
+F ext/session/sqlite3session.h b81e8536ce4b83babafd700f4ff67017804b6c1d71df963b30d3972958e7f4a7
+F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
-F ext/wasm/GNUmakefile a05bb1766f97c88cb42fbfe9e349c799c691f8ae0dc959e9d9469f0bcee89350
+F ext/wasm/GNUmakefile 35e730a01b32481f5483ea5bd72c3d4609e25f34cb5aab9f85eb3eba6f0c4935
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md b89605f65661cf35bf034ff6d43e448cc169b8017fc105d498e33b81218b482c
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -641,17 +642,17 @@ F ext/wasm/api/post-js-header.js 53740d824e5d9027eb1e6fd59e216abbd2136740ce260ea
F ext/wasm/api/pre-js.c-pp.js a614a2c82b12c4d96d8e3ba77330329efc53c4d56a8a7e60ade900f341866cfb
F ext/wasm/api/sqlite3-api-cleanup.js 3ac1786e461ada63033143be8c3b00b26b939540661f3e839515bb92f2e35359
F ext/wasm/api/sqlite3-api-glue.c-pp.js 0b76510f3650053bac67ca8947cb6ab9d050ad2218118a2e7796dd37be832ffa
-F ext/wasm/api/sqlite3-api-oo1.c-pp.js c68d6da0088c2527156fca9163a721abe08e7bd077b15404fd8d292f4612adc1
-F ext/wasm/api/sqlite3-api-prologue.js 8708570165f5b4bce9a78ccd91bc9ddf8735970ac1c4d659e36c9a7d9a644bb4
-F ext/wasm/api/sqlite3-api-worker1.c-pp.js f646a65257973b8c4481f8a6a216370b85644f23e64b126e7ae113570587c0ab
+F ext/wasm/api/sqlite3-api-oo1.c-pp.js 852f2cd6acddbae487fc4f1c3ec952e6c1e2033aa4e6c7091d330d983c87c032
+F ext/wasm/api/sqlite3-api-prologue.js 4f1c2a9dc9caf631907766e9872c27d11b255ccae779e8af01c7f8b932817214
+F ext/wasm/api/sqlite3-api-worker1.c-pp.js 760191cd13416e6f5adfd9fcc8a97fed5645c9e0a5fbac213a2d4ce2d79a4334
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 0f68a64e508598910e7c01214ae27d603dfc8baec6a184506fafac603a901931
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 4ab0704ee198de7d1059eccedc7703c931510b588d10af0ee36ea5b3ebbac284
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js e809739d71e8b35dfe1b55d24d91f02d04239e6aef7ca1ea92a15a29e704f616
-F ext/wasm/api/sqlite3-wasm.c d2672a83b99a89db6bab31eccf9f93fca15a01838d8b0224967db6d4c4bff560
-F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc65debfe43b81fc39fb25c40ad0cc1946bd82580fbf644351107b544d6177ee
+F ext/wasm/api/sqlite3-wasm.c 292db8be42a6ba29a7df2cc2723553a47627853d7c1fdcf8fa37743cf5d85497
+F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 4ad256b4ff7f839ad18931ed35d46cced544207bd2209665ec552e193f7f4544
F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5
F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd
@@ -661,8 +662,8 @@ F ext/wasm/c-pp.c cca55c5b55ebd8d29916adbedb0e40baa12caa9a2e8429f812683c308f9b0e
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
-F ext/wasm/common/whwasmutil.js 5e7fe4a2547831e003356c201ca28d9a0d6a19ab1fe2c8f63f82464fecd2cef7
-F ext/wasm/config.make.in 4bc43443f768a61efd43cf995a5e618f58ac9afc0936706014193537d82c41cb
+F ext/wasm/common/whwasmutil.js ea50e847cf08c2a96694f87d00b49fb97ee5fe62c15439cc269c8e1d9ab74c0a
+F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
F ext/wasm/demo-123.js c7b3cca50c55841c381a9ca4f9396e5bbdc6114273d0b10a43e378e32e7be5bf
@@ -672,17 +673,17 @@ F ext/wasm/demo-worker1-promiser.c-pp.html 635cf90685805e21772a5f7a35d1ace80f98a
F ext/wasm/demo-worker1-promiser.c-pp.js af168699d3cab1c27ad2364ebe06cd49db300bdbf404e23b00d5742ed52816ba
F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d
F ext/wasm/demo-worker1.js 08720227e98fa5b44761cf6e219269cee3e9dd0421d8d91459535da776950314
-F ext/wasm/dist.make 92ef4ffe33022a50f92d602acabad10bd8dd91759f3eb7df27fc6d7d37072b96
+F ext/wasm/dist.make c29018b4db479a4c170569393e5399f0625446123a7eb6ffb0677495292bb954
F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f
-F ext/wasm/fiddle.make c6d7a3d6cc03bb5f21acb295c1233820d0dbf5c6a89b28dc2e093edcc001c45a
-F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea162765d660277cd84ce
-F ext/wasm/fiddle/fiddle.js 2a2f27b4be2674f501fff61c4a09e44dcf2295731a26b5c28e439f3a573bd269
-F ext/wasm/fiddle/index.html 7fcfb221165183bef0e05d5af9ceb79b527e799b1708ab05de0ec0eaebd5b7bf
+F ext/wasm/fiddle.make ea505d11aa2a89551e1693ed4c71ee6a163364ca14f806dda295d0beb26ec0ea
+F ext/wasm/fiddle/fiddle-worker.js 50d3edf54c0c0e3657e876724ec2c10069f55f3e40af20864d72f6f6e9ad00f8
+F ext/wasm/fiddle/fiddle.js 384048afaf938680ce8bf4f7295256dd43ced7943c1172373742d34e379c8530
+F ext/wasm/fiddle/index.html 63051ab7ae7489f3b30dff7fd048f00b7bb7ba2bd6d8b2340fdaf646c18341f8
F ext/wasm/index-dist.html 56132399702b15d70c474c3f1952541e25cb0922942868f70daf188f024b3730
F ext/wasm/index.html bcaa00eca521b372a6a62c7e7b17a870b0fcdf3e418a5921df1fd61e5344080d
F ext/wasm/jaccwabyt/jaccwabyt.js 6e4f26d0edb5c2e7d381b7eff1924832a040a12274afab2d1e1789027e9f6c5c
F ext/wasm/jaccwabyt/jaccwabyt.md 1128e3563e7eff90b5a373395251fc76cb32386fad1fea6075b0f34a8f1b9bdf
-F ext/wasm/mkwasmbuilds.c 5b096a3c9fdf6e67eb20329dc685f995e820248a67fa970633c2c8f49bf472ad
+F ext/wasm/mkwasmbuilds.c cc66cfaf8673ece3c30ca7fe28f6111481090648098a143ea619a8820b8fbe82
F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337
F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
@@ -690,7 +691,7 @@ F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2
F ext/wasm/speedtest1-wasmfs.mjs c77c7231338ed5c0e1ce16aa29106df8e5b5cf11a48319c49433490a8d3ded30
F ext/wasm/speedtest1-worker.html d24d1e06caf3dcd83430c8c3d87761ff7555fd06eaeaf2fc02ce49cf45f0d032
F ext/wasm/speedtest1-worker.js 95e549e13a4d35863a9a7fc66122b5f546c0130d3be7b06dfcc556eb66d24bde
-F ext/wasm/speedtest1.html 217bf15d43065634a27a06dbd5c9a320f6da9932dbcdfc963d482bc2f99dbea9
+F ext/wasm/speedtest1.html e2a0e0bd12243ca34b11235bf9f3c229f4574ea1125f2ecf2bf0589853d6f9c8
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
@@ -698,7 +699,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
-F ext/wasm/tester1.c-pp.js 766a2ba51a2619d41a49be7c6a1ad014c1d23fc97b67496e4f103038203eb17d
+F ext/wasm/tester1.c-pp.js 0abba4bd54f6b22adaadf836c04d3163399f7a8a490fd60f20daac5f9c42b47d
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -707,9 +708,9 @@ F ext/wasm/tests/opfs/sahpool/digest.html 206d08a34dc8bd570b2581d3d9ab3ecad3201b
F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca01385e2732294b53f4c842328
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
-F ext/wasm/wasmfs.make 68999f5bd8c489239592d59a420f8c627c99169bbd6fa16a404751f757b9f702
+F ext/wasm/wasmfs.make 411dd94b40406572caddf88392a1ccc4deed0f88d260516e59ca6e0c887ee861
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
-F main.mk 3ced12d068b22957f4e82a0b94a2c1d83b37dcf39c087263691267f7bfb39a84
+F main.mk a5d698cf8d38e4eb42a89f08a9521906b24f4acac61bbafa56d72cd9b39617b6
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -717,7 +718,7 @@ F mptest/crash02.subtest f4ef05adcd15d60e5d2bd654204f2c008b519df8
F mptest/mptest.c aa41ace6dbc5050d76b02548d3521e6bbccae4f0
F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
-F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc
+F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366
F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398
F src/alter.c fc7bbbeb9e89c7124bf5772ce474b333b7bdc18d6e080763211a40fde69fb1da
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
@@ -726,17 +727,17 @@ F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
-F src/btree.c 783f9999f9ca56846619ba902f5970e181d897c23cc923c915fef225af6dda8a
-F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50
+F src/btree.c cb5b8ceb9baa02a63a2f83dec09c4153e1cfbdf9c2adef5c62c26d2160eeb067
+F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
-F src/build.c 67c1db4c5e89a8519fe9b6dafc287f6bc3627696b5b8536dc5e06db570d8c05f
+F src/build.c cc4f287348790bbb7219f7e8dee13b1c345c3377fcdd98eca866e7457ecd07e7
F src/callback.c acae8c8dddda41ee85cfdf19b926eefe830f371069f8aadca3aa39adf5b1c859
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/date.c 9db4d604e699a73e10b8e85a44db074a1f04c0591a77e2abfd77703f50dce1e9
F src/dbpage.c b3e218f8ed74fcbb7fa805df8ca669a3718d397617b3d8a8aac3307dc315c4d6
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
-F src/expr.c 12d8a79f6a0eb9bfae2d468a6d5bd7b9a0ff00988b3f5326ec9bbaad61bbf385
+F src/expr.c 974d5fc1969f0ac2cf1222ef6fa30925ec487e95979b03f622962127ecdbe143
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
F src/func.c de47a8295503aa130baae5e6d9868ecf4f7c4dbffa65d83ad1f70bdbac0ee2d6
@@ -770,8 +771,8 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
-F src/os_unix.c 04e054ab86d86a7be99ebe5265922687791a40df5afc781d059beb47f4a40acd
-F src/os_win.c b8d3cfdf2f40e2f9715b7d8df64f3c0c7ee18743a2dd0c4fc70c1d57fa1aadc7
+F src/os_unix.c 690107e26cc4e9809eeb9826c0efdbff4a42b9cc59d0f0b855ca3e6021e1ae73
+F src/os_win.c 7ac69df49d2ff0432b9c96fd2d9a17a100cced6860479e584cd3337e18d09334
F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
F src/pager.c 23c0f17deb892da6b32fef1f465507df7ab5cd01d774288cb43695658a649259
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
@@ -781,16 +782,16 @@ F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
F src/pragma.c 30b535d0a66348df844ee36f890617b4cf45e9a22dcbc47ec3ca92909c50aaf1
F src/prepare.c 1832be043fce7d489959aae6f994c452d023914714c4d5457beaed51c0f3d126
-F src/printf.c 71b6d3a0093bf23f473e25480ca0024e8962681506c75f4ffd3d343a3f0ab113
+F src/printf.c 5f0c957af9699e849d786e8fbaa3baab648ca5612230dc17916434c14bc8698f
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
-F src/resolve.c d40fe18d7c2fd0339f5846ffcf7d6809866e380acdf14c76fb2af87e9fe13f64
+F src/resolve.c 1fba451b87114182213ead347113d15c845dac4e61755754600c85595ec92244
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
-F src/select.c 700e98061a61bf8e8b0f2707ed22ffc44c7a7b660dbf7c569430e04d2f95d8a5
-F src/shell.c.in 4f14a1f5196b6006abc8e73cc8fd6c1a62cf940396f8ba909d6711f35f074bb6
-F src/sqlite.h.in 5c54f2461a1ea529bab8499148a2b238e2d4bb571d59e8ea5322d0c190abb693
+F src/select.c a6be657216e1fb72f85dad7df0dba0eb79fe76527c08caa65da8fe44f0e4db44
+F src/shell.c.in 7918c9355667b3b348e5850f0dad9095476ef942ee3b96ee9b8bc2710adda1da
+F src/sqlite.h.in 6fba714744f9b2b43c210d8ff27543c19119f822e7046f65efd7014b1b498484
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 0bfd049bb2088cc44c2ad54f2079d1c6e43091a4e1ce8868779b75f6c1484f1e
-F src/sqliteInt.h 005542f8760edf9b62f014abccb876cf64533b64475a40a89402054d62535288
+F src/sqliteInt.h a54f83985985655d1276e9e356dd6ae19b8d0b62c2abc75cc9e8f402ea141207
F src/sqliteLimit.h 6d817c28a8f19af95e6f4921933b7fbbca48a962bce0eb0ec81e8bb3ef38e68b
F src/status.c 0e72e4f6be6ccfde2488eb63210297e75f569f3ce9920f6c3d77590ec6ce5ffd
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -846,17 +847,17 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 8400646d2830afc2f2dc465a75e3a92e4bedeea623f19dbd79c0c12d0dd6dda2
F src/treeview.c d85ce76e6d1498d781957c07cb234da6d77ce0ed2d196480d516f54dabc62279
-F src/trigger.c 3ffb8ed6b64dbcc0ccae6e82435d01be3bf547e13b814e2d46f7df9bef84748e
+F src/trigger.c 2cbf345eb2913ee7dfa3622e753ac08c84ace17438f30ae97d98d41db5ca5a4f
F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F src/util.c 36fb1150062957280777655976f3f9a75db236cb8207a0770ceae8d5ec17fcd3
F src/vacuum.c 1bacdd0a81d2b5dc1c508fbf0d938c89fa78dd8d5b46ec92686d44030d4f4789
-F src/vdbe.c d2c13c0001f5ec40ec1f010a7f9badcff3ce09bbd7a78528682ad49d8566df54
-F src/vdbe.h 93761ed7c6b8bc19524912fd9b9b587d41bf4f1d0ade650a00dadc10518d8958
-F src/vdbeInt.h 0bc581a9763be385e3af715e8c0a503ba8422c2b7074922faf4bb0d6ae31b15e
+F src/vdbe.c a5873cd566a0e2a0344a86dd946add9d34fae3feeae8b126277ef7af8dc11f91
+F src/vdbe.h ea1f1b52f0efe422f80d88da3c57e4eadc72856e29a22f1ff08e502ec6ba5f08
+F src/vdbeInt.h 52896dd4d5b62190c53db14b09fc2484434eb594c963df0fa66eb8a94527b02e
F src/vdbeapi.c f9a4881a9674fec3fa13da35044a1484d3c4b95f9ec891cc8ffb02ef2b7a41df
-F src/vdbeaux.c fd2c6b19a8892c31a2adc719f156f313560f9cc490cdbd04ff08fdae5d7aedb7
+F src/vdbeaux.c b701e5920fe74b907eb8211d1f63fef96adc65dfd6e1ad6ed0843c71d8c65205
F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
F src/vdbemem.c e67d9c6484d868c879d20c70d00bf4a9058082f1d4058607ca15d50eb3aebc21
F src/vdbesort.c cb6f472e83ca12c46aa7de0ac0a9d11458b357986f2617a1c90dfb19a542ecbe
@@ -867,10 +868,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 20be6f0a25a80b7897cf2a5369bfd37ef198e6f0b6cdef16d83eee856056b159
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
-F src/where.c f58d41d0923eeb21cab8e4fc87a0b36c0724ff4f279ce95ab2731b4696b8e75a
+F src/where.c f2f075bd17065922235632feb368efe92a7f03d42797eb575267574fbf6d4218
F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da
-F src/wherecode.c 504f3c1270c3ffd51ebcdf7a31de08aa51a63b33a2ccdf8f5736afe3dfa73d45
-F src/whereexpr.c d007dc41364de5902181739632380afd671e14f0c5cc9978e64a2c6df8f28c6c
+F src/wherecode.c 71c5c6804b7f882dec8ec858758accae02fcfca13df3cc720f1f258e663ec7c5
+F src/whereexpr.c 78c28a8da187816d5d82049f2e343fb39f4a8e30b5bf1bda9b96cecde40ca8bd
F src/window.c d01227141f622f24fbe36ca105fbe6ef023f9fd98f1ccd65da95f88886565db5
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test 4d7a34d328e58ca2a2d78fd76c27614a41ca7ddf4312ded9c68c04f430b3b47d
@@ -964,7 +965,7 @@ F test/bestindexA.test e1b5def6b190797cacf008e6815ffb78fb30261999030d60a728d572e
F test/bestindexB.test 328b97b69cd1a20928d5997f9ecb04d2e00f1d18e19ab27f9e9adb44d7bc51ce
F test/bestindexC.test 95b4a527b1a5d07951d731604a6d4cf7e5a806b39cea0e7819d4c9667e11c3fc
F test/bestindexD.test 6a8f6f84990bcf17dfa59652a1f935beddb7afd96f8302830fbc86b0a13df3c3
-F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
+F test/between.test e7587149796101cbe8d5f8abae8d2a7b87f04d8226610aa1091615005dcf4d54
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
F test/bigmmap.test 6021e205487347c6d7e5a541aa472a4b8efc4e9f4a3799a823b61a8e6616105d
@@ -1131,7 +1132,7 @@ F test/enc.test b5503a87b31cea8a5084c6e447383f9ca08933bd2f29d97b6b6201081b2343eb
F test/enc2.test 872afe58db772e7dfa1ad8e0759f8cc820e9efc8172d460fae83023101c2e435
F test/enc3.test 55ef64416d72975c66167310a51dc9fc544ba3ae4858b8d5ab22f4cb6500b087
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
-F test/eqp.test 82f221e8cd588434d7f3bba9a0f4c78cbe7a541615a41632e12f50608bfb4a99
+F test/eqp.test 746db9fe11629a0d00328e1721cc2a2e4726d574b677ab14de35fd914f54cc82
F test/eqp2.test 6e8996148de88f0e7670491e92e712a2920a369b4406f21a27c3c9b6a46b68dd
F test/errmsg.test eae9f091eb39ce7e20305de45d8e5d115b68fa856fba4ea6757b6ca3705ff7f9
F test/errofst1.test 6da78363739ba8991f498396ab331b5d64e7ab5c4172c12b5884683ef523ac53
@@ -1140,6 +1141,9 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
F test/exclusive2.test cd70b1d9c6fffd336f9795b711dcc5d9ceba133ad3f7001da3fda63615bdc91e
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
+F test/existsexpr.test 9c4b77c4729281cc2ae63b9b460d0598ce28cc7876135e3e2c21629bbc8d077a
+F test/existsexpr2.test dc23e76389eff3d29f6488ff733012a3560cd67ec8cfaecbecd52cced5d5af11
+F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e
F test/expr.test 4ada8eb822c45ef27a36851a258004d43c1e95e7c82585a1217e732084e4482c
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
F test/exprfault.test da33606d799718e2f8e34efd0e5858884a1ad87f608774c552a7f5517cc27181
@@ -1281,7 +1285,7 @@ F test/fuzz3.test 70ba57260364b83e964707b9d4b5625284239768ab907dd387c740c0370ce3
F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
-F test/fuzzcheck.c 19f8af47a5c4ee2c3943fdee270f1f14e3d83fe968a9737a7557fb4e3c06efc1
+F test/fuzzcheck.c d425d07adcb31160d07f3070420ff217d30566c62eb9cf5fae61c0348bc7abed
F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
@@ -1315,7 +1319,7 @@ F test/in7.test d9efdee00b074a60c6343993b2eda78bc369ab080dad864513c73f8aca89d566
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f
F test/incrblob3.test 67621a04b3084113bf38ce03797d70eca012d9d8f948193b8f655df577b0da6f
-F test/incrblob4.test 10f4537febe1774c02f8d490d393322b4a50898d4027868b67055e9c3adc22db
+F test/incrblob4.test a8d6b5ff04055fcec747a50b78485ebf4fcd80074e0b3cedbe952bde346da54a
F test/incrblob_err.test 89372a28f1d98254f03fed705f9efcd34ef61a674df16d2dbb4726944a2de5e9
F test/incrblobfault.test de274b1e329169c2c3438f9528994807ea8201ebf38ae9f157d34bf3ec0cc549
F test/incrcorrupt.test 6c567fbf870aa9e91866fe52ce6f200cd548939a
@@ -1387,7 +1391,7 @@ F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd286
F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307
F test/json/json-speed-check.sh 7d5898808ce7542762318306ae6075a30f5e7ee115c4a409f487e123afe91d88 x
F test/json/jsonb-q1.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7
-F test/json101.test 8237a484c256965eab1678fd950a32ac56325bb7d0dadbd095a46b0ddd95d62b
+F test/json101.test cf53254f0f0c1399a01b21fc58fee0e63a12a556be91b9ee9faccdb8b82c083c
F test/json102.test 9b2e5ada10845ff84853b3feaae2ce51ce7145ae458f74c6a6cecc6ef6ee3ae1
F test/json103.test 355746a6b66aa438f214b4fae454b13068fad2444b5f693e0d538ad1c059b264
F test/json104.test 1b844a70cddcfa2e4cd81a5db0657b2e61e7f00868310f24f56a9ba0114348c1
@@ -1494,7 +1498,7 @@ F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/notify3.test 796c7b7157f55c93b4e672b724e9c923a6fc6aa72ac419379a623e2350472e22
F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
-F test/notnull2.test 2ac7b4e04917148c7a1a9ed36df20150175ce942f07f5714375b29acbaca7106
+F test/notnull2.test 5b7dd6e82c409b2d011ad6acf19ae4bf0816a9c69ccf600b529d7405d7c49874
F test/notnullfault.test fc4bb7845582a2b3db376001ef49118393b1b11abe0d24adb03db057ee2b73d5
F test/null.test b7ff206a1c60fe01aa2abd33ef9ea83c93727d993ca8a613de86e925c9f2bc6f
F test/nulls1.test 7a5e4346ee4285034100b4cd20e6784f16a9d6c927e44ecdf10034086bbee9c9
@@ -1576,7 +1580,7 @@ F test/round1.test 29c3c9039936ed024d672f003c4d35ee11c14c0acb75c5f7d6188ff16190c
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test d27191b5ce794c05bf61081e8b2c546a1844c1641321dcaf7fb785234256cc8e
-F test/rowvalue.test 9c873b2f6e7ce72b24ef133f93515c07a6a7dac4846a344ebc2af7b8bfdf5147
+F test/rowvalue.test 8a3f0fea3a3cbbfc7cb9885b76185a774cd8d891e0c133e289567c755d39eb9f
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
F test/rowvalue3.test 103e9a224ca0548dd0d67e439f39c5dd16de4200221a333927372408c025324c
F test/rowvalue4.test bac9326d1e886656650f67c0ec484eb5f452244a8209c6af508e9a862ace08ed
@@ -1686,7 +1690,7 @@ F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
F test/speedtest.md ee958457ae1b729d9715ae33c0320600000bf1d9ddea1a88dcf79f56729d6fad
F test/speedtest.tcl 405411356fbc54af15987b7ffeec330a49138f71584220fb8fe1948b2f7ac907 x
-F test/speedtest1.c 64b8804b053a796eab22f8b23fb181000f05d7b3e2aa44f022117ea543bc5a2a
+F test/speedtest1.c a9b002a7bfed99ba3166c2a9b8ae45a95b4c2d37f891e1637c022f9e1d15e3f9
F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
@@ -1733,8 +1737,9 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 463ae33b8bf75ac77451df19bd65e7c415c2e9891227c7c9e657d0a2d8e1074a
-F test/testrunner.tcl 614c4a28f7f730acd7bec53e17d76602fb480e0d538b6ec548169e03a093f92d x
+F test/testrunner.tcl b42f7736968cafc9e69bb5d0b87fc81b375fb4c3f44e19b472cccd91d41a416a x
F test/testrunner_data.tcl 02dd645b647d907c959fbf232b7ff7d869c2ae430d5117443fc1e16a0d32243a
+F test/testrunner_estwork.tcl 7927a84327259a32854926f68a75292e33a61e7e052fdbfcb01f18696c99c724
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1989,7 +1994,7 @@ F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
F test/vtabE.test 2a143fe75a11275781d1fd1988d86b66a3f69cb98f4add62e3da8fd0f637b45f
F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b
-F test/vtabH.test a9417d92111629e2c33e0f3b06cc84bcd611753d0fc017b3ce6ae96572bc2f2f
+F test/vtabH.test 3fe4c1cbc93a85971990f379116255e2d4f14b583be4d0b9cca67e90226041f1
F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
F test/vtabJ.test a6aef49d558af90fae10565b29501f82a95781cb4f797f2d13e2d19f9b6bc77b
F test/vtabK.test 13293177528fada1235c0112db0d187d754af1355c5a39371abd365104e3afbf
@@ -2152,7 +2157,7 @@ F tool/logest.c c34e5944318415de513d29a6098df247a9618c96d83c38d4abd88641fe46e669
F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
F tool/mkamalzip.tcl 8aa5ebe7973c8b8774062d34e15fea9815c4cc2ceea3a9b184695f005910876a
-F tool/mkautoconfamal.sh 564378ae48cc8f4c8c68ef6d00228a0b2a70e2e3e4c67f26be1dd05d9730fefd
+F tool/mkautoconfamal.sh cf72166369d9d1df9df1ae9fcad03a7939808d7273784b56fd78b913587637a1
F tool/mkccode.tcl c42a8f8cf78f92e83795d5447460dbce7aaf78a3bbf9082f1507dc71a3665f3c x
F tool/mkctimec.tcl 11c9eda4a8d18c74b79280b30506d832849fd1855e6d9e95e1fd506f1d211c37 x
F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559
@@ -2168,7 +2173,7 @@ F tool/mksqlite3c.tcl f11b63445c4840509248bd4aa151a81aea25d5415fef71943c8d436eba
F tool/mksqlite3h.tcl 989948c6a26e188e673d7c2f2f093ea3acd816ad6ac65bab596280075c8f3a45
F tool/mksqlite3internalh.tcl 46ef6ed6ccd3c36e23051109dd25085d8edef3887635cea25afa81c4adf4d4db
F tool/mksrczip.tcl 81efd9974dbb36005383f2cd655520057a2ae5aa85ac2441a80c7c28f803ac52
-F tool/mktoolzip.tcl 34b4e92be544f820e2cc26f143f7d5aec511e826ec394cc82969a5dcf7c7a27c
+F tool/mktoolzip.tcl c9f388b2b0751982aef29a0c1d80f7959dbe38065ebb4421c39844e92622b086
F tool/mkvsix.tcl 67b40996a50f985a573278eea32fc5a5eb6110bdf14d33f1d8086e48c69e540a
F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c6115285845
F tool/omittest-msvc.tcl d6b8f501ac1d7798c4126065030f89812379012cad98a1735d6d7221492abc08
@@ -2208,8 +2213,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 4eefab44941fc6e17742fa49c8734e7f00a2177d82bc572e596228add53aad39
-R 8dd2c3d3469e31d78df8ad758ebdebcb
-U dan
-Z 1af57e86c85e1876b1c7c9bf1cb0169c
+P 27d1d0100c0cb6e5c0c576be0f99209bb905f302008d8c257039adf8c5402f7d
+R c1c07ea01fe59d26da505e28561c5adc
+U stephan
+Z 413b972a6b543fbfac24c1068223678c
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index 843f25873..48574e3a2 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-9f335b9a4e9e761a0c6afd6dc69665a24506141bde88530bf59fcbdf957ae881
+9639382c5478115df7c1584c14a52c176fe747df73078419be4ab276374a704b
diff --git a/sqlite3.1 b/sqlite3.1
index 08b1ff262..e4ba5a0cb 100644
--- a/sqlite3.1
+++ b/sqlite3.1
@@ -137,17 +137,37 @@ continue prompt = " ...> "
.sp
.fi
-o If the file
+o If the environment variable XDG_CONFIG_HOME is set then
.B ${XDG_CONFIG_HOME}/sqlite3/sqliterc
-or
+is checked, else
+.B ~/.config/sqlite3/sqliterc
+is checked. If the selected file does not exist then the fallback of
.B ~/.sqliterc
-exists, the first of those to be found is processed during startup.
-It should generally only contain meta-commands.
+is used. It should generally only contain meta-commands.
o If the -init option is present, the specified file is processed.
o All other command line options are processed.
+.SH HISTORY FILE
+.B sqlite3
+may be configured to use a history file to save SQL statements and
+meta-commands entered interactively. These statements and commands can be
+retrieved, edited and, reused at the main and continue prompts. If the
+environment variable
+.B SQLITE_HISTORY
+is set, it will be used as the name of the history file, whether it
+already exists or not. If it is not set but the XDG_STATE_HOME
+environment variable is then
+.B ${XDG_STATE_HOME}/sqlite_history
+is used. If XDG_STATE_HOME is not set then
+.B ~/.local/state/sqlite_history
+is used. If the selected file does not exist then
+.B ~/.sqlite_history
+will be used as the history file. If any history file is found, it
+will be written if the shell exits interactive mode normally,
+regardless of whether it existed previously, though saving will
+silently fail if the history file's directory does not exist.
.SH SEE ALSO
https://sqlite.org/cli.html
.br
diff --git a/src/btree.c b/src/btree.c
index 00cdc9760..a931b0d12 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -5667,6 +5667,30 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
return rc;
}
+/* Set *pRes to 1 (true) if the BTree pointed to by cursor pCur contains zero
+** rows of content. Set *pRes to 0 (false) if the table contains content.
+** Return SQLITE_OK on success or some error code (ex: SQLITE_NOMEM) if
+** something goes wrong.
+*/
+int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){
+ int rc;
+
+ assert( cursorOwnsBtShared(pCur) );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
+ if( pCur->eState==CURSOR_VALID ){
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ rc = moveToRoot(pCur);
+ if( rc==SQLITE_EMPTY ){
+ *pRes = 1;
+ rc = SQLITE_OK;
+ }else{
+ *pRes = 0;
+ }
+ return rc;
+}
+
#ifdef SQLITE_DEBUG
/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that
** this flags are true for a consistent database.
diff --git a/src/btree.h b/src/btree.h
index 241261dc6..96f4c4c60 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -317,6 +317,7 @@ struct BtreePayload {
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
int flags, int seekResult);
int sqlite3BtreeFirst(BtCursor*, int *pRes);
+int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes);
int sqlite3BtreeLast(BtCursor*, int *pRes);
int sqlite3BtreeNext(BtCursor*, int flags);
int sqlite3BtreeEof(BtCursor*);
diff --git a/src/build.c b/src/build.c
index 5bd3aac3c..5495cef18 100644
--- a/src/build.c
+++ b/src/build.c
@@ -4219,7 +4219,6 @@ void sqlite3CreateIndex(
assert( j<=0x7fff );
if( j<0 ){
j = pTab->iPKey;
- pIndex->bIdxRowid = 1;
}else{
if( pTab->aCol[j].notNull==0 ){
pIndex->uniqNotNull = 0;
@@ -5138,16 +5137,22 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
** are deleted by this function.
*/
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
- assert( p1 && p1->nSrc==1 );
+ assert( p1 );
+ assert( p2 || pParse->nErr );
+ assert( p2==0 || p2->nSrc>=1 );
+ testcase( p1->nSrc==0 );
if( p2 ){
- SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1);
+ int nOld = p1->nSrc;
+ SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, nOld);
if( pNew==0 ){
sqlite3SrcListDelete(pParse->db, p2);
}else{
p1 = pNew;
- memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
+ memcpy(&p1->a[nOld], p2->a, p2->nSrc*sizeof(SrcItem));
+ assert( nOld==1 || (p2->a[0].fg.jointype & JT_LTORJ)==0 );
+ assert( p1->nSrc>=1 );
+ p1->a[0].fg.jointype |= (JT_LTORJ & p2->a[0].fg.jointype);
sqlite3DbFree(pParse->db, p2);
- p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype);
}
}
return p1;
diff --git a/src/expr.c b/src/expr.c
index 3f040309a..dba0aa2de 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -4989,6 +4989,12 @@ expr_code_doover:
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
return target;
}
+ case TK_NULLS: {
+ /* Set a range of registers to NULL. pExpr->y.nReg registers starting
+ ** with target */
+ sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
+ return target;
+ }
default: {
/* Make NULL the default case so that if a bug causes an illegal
** Expr node to be passed into this function, it will be handled
@@ -5699,6 +5705,25 @@ int sqlite3ExprCodeRunJustOnce(
}
/*
+** Make arrangements to invoke OP_Null on a range of registers
+** during initialization.
+*/
+SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
+ Parse *pParse, /* Parsing context */
+ int iReg, /* First register to set to NULL */
+ int nReg /* Number of sequential registers to NULL out */
+){
+ u8 okConstFactor = pParse->okConstFactor;
+ Expr t;
+ memset(&t, 0, sizeof(t));
+ t.op = TK_NULLS;
+ t.y.nReg = nReg;
+ pParse->okConstFactor = 1;
+ sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
+ pParse->okConstFactor = okConstFactor;
+}
+
+/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
** are stored.
diff --git a/src/os_unix.c b/src/os_unix.c
index 784bc3517..7e26c8456 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -1452,6 +1452,10 @@ static int findInodeInfo(
storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
+ if( fsync(fd) ){
+ storeLastErrno(pFile, errno);
+ return SQLITE_IOERR_FSYNC;
+ }
rc = osFstat(fd, &statbuf);
if( rc!=0 ){
storeLastErrno(pFile, errno);
diff --git a/src/os_win.c b/src/os_win.c
index c7c923e77..623e30104 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -3990,6 +3990,103 @@ static int winDeviceCharacteristics(sqlite3_file *id){
*/
static SYSTEM_INFO winSysInfo;
+/*
+** Convert a UTF-8 filename into whatever form the underlying
+** operating system wants filenames in. Space to hold the result
+** is obtained from malloc and must be freed by the calling
+** function
+**
+** On Cygwin, 3 possible input forms are accepted:
+** - If the filename starts with "<drive>:/" or "<drive>:\",
+** it is converted to UTF-16 as-is.
+** - If the filename contains '/', it is assumed to be a
+** Cygwin absolute path, it is converted to a win32
+** absolute path in UTF-16.
+** - Otherwise it must be a filename only, the win32 filename
+** is returned in UTF-16.
+** Note: If the function cygwin_conv_path() fails, only
+** UTF-8 -> UTF-16 conversion will be done. This can only
+** happen when the file path >32k, in which case winUtf8ToUnicode()
+** will fail too.
+*/
+static void *winConvertFromUtf8Filename(const char *zFilename){
+ void *zConverted = 0;
+ if( osIsNT() ){
+#ifdef __CYGWIN__
+ int nChar;
+ LPWSTR zWideFilename;
+
+ if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
+ && winIsDirSep(zFilename[2])) ){
+ i64 nByte;
+ int convertflag = CCP_POSIX_TO_WIN_W;
+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
+ nByte = (i64)osCygwin_conv_path(convertflag,
+ zFilename, 0, 0);
+ if( nByte>0 ){
+ zConverted = sqlite3MallocZero(12+(u64)nByte);
+ if ( zConverted==0 ){
+ return zConverted;
+ }
+ zWideFilename = zConverted;
+ /* Filenames should be prefixed, except when converted
+ * full path already starts with "\\?\". */
+ if( osCygwin_conv_path(convertflag, zFilename,
+ zWideFilename+4, nByte)==0 ){
+ if( (convertflag&CCP_RELATIVE) ){
+ memmove(zWideFilename, zWideFilename+4, nByte);
+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
+ memcpy(zWideFilename, L"\\\\?\\", 8);
+ }else if( zWideFilename[6]!='?' ){
+ memmove(zWideFilename+6, zWideFilename+4, nByte);
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
+ }else{
+ memmove(zWideFilename, zWideFilename+4, nByte);
+ }
+ return zConverted;
+ }
+ sqlite3_free(zConverted);
+ }
+ }
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
+ zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
+ if( zWideFilename==0 ){
+ return 0;
+ }
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
+ zWideFilename, nChar);
+ if( nChar==0 ){
+ sqlite3_free(zWideFilename);
+ zWideFilename = 0;
+ }else if( nChar>MAX_PATH
+ && winIsDriveLetterAndColon(zFilename)
+ && winIsDirSep(zFilename[2]) ){
+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
+ zWideFilename[2] = '\\';
+ memcpy(zWideFilename, L"\\\\?\\", 8);
+ }else if( nChar>MAX_PATH
+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
+ && zFilename[2] != '?' ){
+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
+ }
+ zConverted = zWideFilename;
+#else
+ zConverted = winUtf8ToUnicode(zFilename);
+#endif /* __CYGWIN__ */
+ }
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
+ else{
+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+
#ifndef SQLITE_OMIT_WAL
/*
@@ -4185,103 +4282,6 @@ static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
/*
-** Convert a UTF-8 filename into whatever form the underlying
-** operating system wants filenames in. Space to hold the result
-** is obtained from malloc and must be freed by the calling
-** function
-**
-** On Cygwin, 3 possible input forms are accepted:
-** - If the filename starts with "<drive>:/" or "<drive>:\",
-** it is converted to UTF-16 as-is.
-** - If the filename contains '/', it is assumed to be a
-** Cygwin absolute path, it is converted to a win32
-** absolute path in UTF-16.
-** - Otherwise it must be a filename only, the win32 filename
-** is returned in UTF-16.
-** Note: If the function cygwin_conv_path() fails, only
-** UTF-8 -> UTF-16 conversion will be done. This can only
-** happen when the file path >32k, in which case winUtf8ToUnicode()
-** will fail too.
-*/
-static void *winConvertFromUtf8Filename(const char *zFilename){
- void *zConverted = 0;
- if( osIsNT() ){
-#ifdef __CYGWIN__
- int nChar;
- LPWSTR zWideFilename;
-
- if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
- && winIsDirSep(zFilename[2])) ){
- i64 nByte;
- int convertflag = CCP_POSIX_TO_WIN_W;
- if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
- nByte = (i64)osCygwin_conv_path(convertflag,
- zFilename, 0, 0);
- if( nByte>0 ){
- zConverted = sqlite3MallocZero(12+(u64)nByte);
- if ( zConverted==0 ){
- return zConverted;
- }
- zWideFilename = zConverted;
- /* Filenames should be prefixed, except when converted
- * full path already starts with "\\?\". */
- if( osCygwin_conv_path(convertflag, zFilename,
- zWideFilename+4, nByte)==0 ){
- if( (convertflag&CCP_RELATIVE) ){
- memmove(zWideFilename, zWideFilename+4, nByte);
- }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
- memcpy(zWideFilename, L"\\\\?\\", 8);
- }else if( zWideFilename[6]!='?' ){
- memmove(zWideFilename+6, zWideFilename+4, nByte);
- memcpy(zWideFilename, L"\\\\?\\UNC", 14);
- }else{
- memmove(zWideFilename, zWideFilename+4, nByte);
- }
- return zConverted;
- }
- sqlite3_free(zConverted);
- }
- }
- nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
- if( nChar==0 ){
- return 0;
- }
- zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
- if( zWideFilename==0 ){
- return 0;
- }
- nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
- zWideFilename, nChar);
- if( nChar==0 ){
- sqlite3_free(zWideFilename);
- zWideFilename = 0;
- }else if( nChar>MAX_PATH
- && winIsDriveLetterAndColon(zFilename)
- && winIsDirSep(zFilename[2]) ){
- memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
- zWideFilename[2] = '\\';
- memcpy(zWideFilename, L"\\\\?\\", 8);
- }else if( nChar>MAX_PATH
- && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
- && zFilename[2] != '?' ){
- memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
- memcpy(zWideFilename, L"\\\\?\\UNC", 14);
- }
- zConverted = zWideFilename;
-#else
- zConverted = winUtf8ToUnicode(zFilename);
-#endif /* __CYGWIN__ */
- }
-#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
- else{
- zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
- }
-#endif
- /* caller will handle out of memory */
- return zConverted;
-}
-
-/*
** This function is used to open a handle on a *-shm file.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
diff --git a/src/printf.c b/src/printf.c
index 669ca26b0..9d95fbdd9 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -536,7 +536,21 @@ void sqlite3_str_vappendf(
}
}
if( s.sign=='-' ){
- prefix = '-';
+ if( flag_alternateform
+ && !flag_prefix
+ && xtype==etFLOAT
+ && s.iDP<=iRound
+ ){
+ /* Suppress the minus sign if all of the following are true:
+ ** * The value displayed is zero
+ ** * The '#' flag is used
+ ** * The '+' flag is not used, and
+ ** * The format is %f
+ */
+ prefix = 0;
+ }else{
+ prefix = '-';
+ }
}else{
prefix = flag_prefix;
}
diff --git a/src/resolve.c b/src/resolve.c
index 3961a2009..b60197866 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -926,8 +926,8 @@ static void notValidImpl(
/*
** Expression p should encode a floating point value between 1.0 and 0.0.
-** Return 1024 times this value. Or return -1 if p is not a floating point
-** value between 1.0 and 0.0.
+** Return 134,217,728 (2^27) times this value. Or return -1 if p is not
+** a floating point value between 1.0 and 0.0.
*/
static int exprProbability(Expr *p){
double r = -1.0;
@@ -1358,11 +1358,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
return WRC_Prune;
}
#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_EXISTS:
case TK_SELECT:
- case TK_EXISTS: testcase( pExpr->op==TK_EXISTS );
#endif
case TK_IN: {
testcase( pExpr->op==TK_IN );
+ testcase( pExpr->op==TK_EXISTS );
+ testcase( pExpr->op==TK_SELECT );
if( ExprUseXSelect(pExpr) ){
int nRef = pNC->nRef;
testcase( pNC->ncFlags & NC_IsCheck );
@@ -1370,6 +1372,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
assert( pExpr->x.pSelect );
+ if( pExpr->op==TK_EXISTS ) pParse->bHasExists = 1;
if( pNC->ncFlags & NC_SelfRef ){
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
@@ -2280,13 +2283,16 @@ int sqlite3ResolveSelfReference(
SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
int rc;
- u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
+ union {
+ SrcList sSrc;
+ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
+ } uSrc;
assert( type==0 || pTab!=0 );
assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
|| type==NC_GenCol || pTab==0 );
memset(&sNC, 0, sizeof(sNC));
- pSrc = (SrcList*)srcSpace;
+ pSrc = &uSrc.sSrc;
memset(pSrc, 0, SZ_SRCLIST_1);
if( pTab ){
pSrc->nSrc = 1;
diff --git a/src/select.c b/src/select.c
index 1e54747fc..db41cb493 100644
--- a/src/select.c
+++ b/src/select.c
@@ -384,7 +384,7 @@ static int tableAndColumnIndex(
int iEnd, /* Last member of pSrc->a[] to check */
const char *zCol, /* Name of the column we are looking for */
int *piTab, /* Write index of pSrc->a[] here */
- int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */
+ int *piCol, /* Write index of pSrc->a[*piTab].pSTab->aCol[] here */
int bIgnoreHidden /* Ignore hidden columns */
){
int i; /* For looping over tables in pSrc */
@@ -3036,7 +3036,9 @@ static int multiSelect(
int priorOp; /* The SRT_ operation to apply to prior selects */
Expr *pLimit; /* Saved values of p->nLimit */
int addr;
+ int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */
SelectDest uniondest;
+
testcase( p->op==TK_EXCEPT );
testcase( p->op==TK_UNION );
@@ -3075,6 +3077,8 @@ static int multiSelect(
*/
if( p->op==TK_EXCEPT ){
op = SRT_Except;
+ emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab);
+ VdbeCoverage(v);
}else{
assert( p->op==TK_UNION );
op = SRT_Union;
@@ -3095,6 +3099,7 @@ static int multiSelect(
if( p->op==TK_UNION ){
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
}
+ if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass);
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->iLimit = 0;
@@ -3125,9 +3130,10 @@ static int multiSelect(
int tab1, tab2;
int iCont, iBreak, iStart;
Expr *pLimit;
- int addr;
+ int addr, iLimit, iOffset;
SelectDest intersectdest;
int r1;
+ int emptyBypass;
/* INTERSECT is different from the others since it requires
** two temporary tables. Hence it has its own case. Begin
@@ -3151,15 +3157,29 @@ static int multiSelect(
if( rc ){
goto multi_select_end;
}
+
+ /* Initialize LIMIT counters before checking to see if the LHS
+ ** is empty, in case the jump is taken */
+ iBreak = sqlite3VdbeMakeLabel(pParse);
+ computeLimitRegisters(pParse, p, iBreak);
+ emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v);
/* Code the current SELECT into temporary table "tab2"
*/
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
assert( p->addrOpenEphm[1] == -1 );
p->addrOpenEphm[1] = addr;
- p->pPrior = 0;
+
+ /* Disable prior SELECTs and the LIMIT counters during the computation
+ ** of the RHS select */
pLimit = p->pLimit;
+ iLimit = p->iLimit;
+ iOffset = p->iOffset;
+ p->pPrior = 0;
p->pLimit = 0;
+ p->iLimit = 0;
+ p->iOffset = 0;
+
intersectdest.iSDParm = tab2;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
sqlite3SelectOpName(p->op)));
@@ -3172,19 +3192,21 @@ static int multiSelect(
p->nSelectRow = pPrior->nSelectRow;
}
sqlite3ExprDelete(db, p->pLimit);
+
+ /* Reinstate the LIMIT counters prior to running the final intersect */
p->pLimit = pLimit;
+ p->iLimit = iLimit;
+ p->iOffset = iOffset;
/* Generate code to take the intersection of the two temporary
** tables.
*/
if( rc ) break;
assert( p->pEList );
- iBreak = sqlite3VdbeMakeLabel(pParse);
- iCont = sqlite3VdbeMakeLabel(pParse);
- computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
+ sqlite3VdbeAddOp1(v, OP_Rewind, tab1);
r1 = sqlite3GetTempReg(pParse);
iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
+ iCont = sqlite3VdbeMakeLabel(pParse);
sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, r1);
@@ -3194,6 +3216,7 @@ static int multiSelect(
sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
+ sqlite3VdbeJumpHere(v, emptyBypass);
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
break;
}
@@ -4650,7 +4673,7 @@ static int flattenSubquery(
** complete, since there may still exist Expr.pTab entries that
** refer to the subquery even after flattening. Ticket #3346.
**
- ** pSubitem->pTab is always non-NULL by test restrictions and tests above.
+ ** pSubitem->pSTab is always non-NULL by test restrictions and tests above.
*/
if( ALWAYS(pSubitem->pSTab!=0) ){
Table *pTabToDel = pSubitem->pSTab;
@@ -5764,7 +5787,7 @@ With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
** CTE expression, through routine checks to see if the reference is
** a recursive reference to the CTE.
**
-** If pFrom matches a CTE according to either of these two above, pFrom->pTab
+** If pFrom matches a CTE according to either of these two above, pFrom->pSTab
** and other fields are populated accordingly.
**
** Return 0 if no match is found.
@@ -7392,6 +7415,84 @@ static int fromClauseTermCanBeCoroutine(
}
/*
+** Argument pWhere is the WHERE clause belonging to SELECT statement p. This
+** function attempts to transform expressions of the form:
+**
+** EXISTS (SELECT ...)
+**
+** into joins. For example, given
+**
+** CREATE TABLE sailors(sid INTEGER PRIMARY KEY, name TEXT);
+** CREATE TABLE reserves(sid INT, day DATE, PRIMARY KEY(sid, day));
+**
+** SELECT name FROM sailors AS S WHERE EXISTS (
+** SELECT * FROM reserves AS R WHERE S.sid = R.sid AND R.day = '2022-10-25'
+** );
+**
+** the SELECT statement may be transformed as follows:
+**
+** SELECT name FROM sailors AS S, reserves AS R
+** WHERE S.sid = R.sid AND R.day = '2022-10-25';
+**
+** **Approximately**. Really, we have to ensure that the FROM-clause term
+** that was formerly inside the EXISTS is only executed once. This is handled
+** by setting the SrcItem.fg.fromExists flag, which then causes code in
+** the where.c file to exit the corresponding loop after the first successful
+** match (if any).
+*/
+static SQLITE_NOINLINE void existsToJoin(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The SELECT statement being optimized */
+ Expr *pWhere /* part of the WHERE clause currently being examined */
+){
+ if( pParse->nErr==0
+ && pWhere!=0
+ && !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON)
+ && ALWAYS(p->pSrc!=0)
+ && p->pSrc->nSrc<BMS
+ ){
+ if( pWhere->op==TK_AND ){
+ Expr *pRight = pWhere->pRight;
+ existsToJoin(pParse, p, pWhere->pLeft);
+ existsToJoin(pParse, p, pRight);
+ }
+ else if( pWhere->op==TK_EXISTS ){
+ Select *pSub = pWhere->x.pSelect;
+ Expr *pSubWhere = pSub->pWhere;
+ if( pSub->pSrc->nSrc==1
+ && (pSub->selFlags & SF_Aggregate)==0
+ && !pSub->pSrc->a[0].fg.isSubquery
+ && pSub->pLimit==0
+ ){
+ memset(pWhere, 0, sizeof(*pWhere));
+ pWhere->op = TK_INTEGER;
+ pWhere->u.iValue = 1;
+ ExprSetProperty(pWhere, EP_IntValue);
+
+ assert( p->pWhere!=0 );
+ pSub->pSrc->a[0].fg.fromExists = 1;
+ pSub->pSrc->a[0].fg.jointype |= JT_CROSS;
+ p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc);
+ if( pSubWhere ){
+ p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere);
+ pSub->pWhere = 0;
+ }
+ pSub->pSrc = 0;
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSub);
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x100000 ){
+ TREETRACE(0x100000,pParse,p,
+ ("After EXISTS-to-JOIN optimization:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
+ existsToJoin(pParse, p, pSubWhere);
+ }
+ }
+ }
+}
+
+/*
** Generate byte-code for the SELECT statement given in the p argument.
**
** The results are returned according to the SelectDest structure.
@@ -7759,6 +7860,13 @@ int sqlite3Select(
}
#endif
+ /* If there may be an "EXISTS (SELECT ...)" in the WHERE clause, attempt
+ ** to change it into a join. */
+ if( pParse->bHasExists && OptimizationEnabled(db,SQLITE_ExistsToJoin) ){
+ existsToJoin(pParse, p, p->pWhere);
+ pTabList = p->pSrc;
+ }
+
/* Do the WHERE-clause constant propagation optimization if this is
** a join. No need to spend time on this operation for non-join queries
** as the equivalent optimization will be handled by query planner in
@@ -8375,6 +8483,7 @@ int sqlite3Select(
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
+ sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
diff --git a/src/shell.c.in b/src/shell.c.in
index 33dd30697..3b797223a 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -106,6 +106,9 @@ typedef sqlite3_uint64 u64;
typedef unsigned char u8;
#include <ctype.h>
#include <stdarg.h>
+#ifndef _WIN32
+# include <sys/time.h>
+#endif
#if !defined(_WIN32) && !defined(WIN32)
# include <signal.h>
@@ -267,20 +270,23 @@ static int cli_strncmp(const char *a, const char *b, size_t n){
return strncmp(a,b,n);
}
-/* Return the current wall-clock time */
+/* Return the current wall-clock time in microseconds since the
+** Unix epoch (1970-01-01T00:00:00Z)
+*/
static sqlite3_int64 timeOfDay(void){
- static sqlite3_vfs *clockVfs = 0;
- sqlite3_int64 t;
- if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
- if( clockVfs==0 ) return 0; /* Never actually happens */
- if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
- clockVfs->xCurrentTimeInt64(clockVfs, &t);
- }else{
- double r;
- clockVfs->xCurrentTime(clockVfs, &r);
- t = (sqlite3_int64)(r*86400000.0);
- }
+#if defined(_WIN32)
+ sqlite3_uint64 t;
+ FILETIME tm;
+ GetSystemTimePreciseAsFileTime(&tm);
+ t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
+ t += 116444736000000000LL;
+ t /= 10;
return t;
+#else
+ struct timeval sNow;
+ (void)gettimeofday(&sNow,0);
+ return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
+#endif
}
#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
@@ -325,8 +331,8 @@ static void endTimer(FILE *out){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
- (iEnd - iBegin)*0.001,
+ sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
+ (iEnd - iBegin)*0.000001,
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
@@ -404,8 +410,8 @@ static void endTimer(FILE *out){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
- (ftWallEnd - ftWallBegin)*0.001,
+ sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
+ (ftWallEnd - ftWallBegin)*0.000001,
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
}
@@ -11710,6 +11716,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0x20000000, 1, "StarQuery" },
+ { 0x40000000, 1, "ExistsToJoin" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
@@ -12759,59 +12766,79 @@ static char *find_home_dir(int clearFlag){
}
/*
-** On non-Windows platforms, look for $XDG_CONFIG_HOME.
-** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
-** the path to it. If there is no $(XDG_CONFIG_HOME) then
-** look for $(HOME)/.config/sqlite3/sqliterc and if found
-** return that. If none of these are found, return 0.
+** On non-Windows platforms, look for:
+**
+** - ${zEnvVar}/${zBaseName}
+** - ${HOME}/${zSubdir}/${zBaseName}
**
-** The string returned is obtained from sqlite3_malloc() and
-** should be freed by the caller.
+** $zEnvVar is intended to be the name of an XDG_... environment
+** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
+** NULL or getenv(zEnvVar) is NULL then fall back to the second
+** option. If the selected option is not found in the filesystem,
+** return 0.
+**
+** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
+** becomes the fallback.
+**
+** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
+** will conventionally be ".config" or ".local/state", which, not
+** coincidentally, is the typical subdir of the corresponding XDG_...
+** var with the XDG var's $HOME prefix.
+**
+** The returned string is obtained from sqlite3_malloc() and should be
+** sqlite3_free()'d by the caller.
*/
-static char *find_xdg_config(void){
+static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
+ const char *zBaseName){
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
|| defined(__RTP__) || defined(_WRS_KERNEL)
return 0;
#else
- char *zConfig = 0;
- const char *zXdgHome;
+ char *zConfigFile = 0;
+ const char *zXdgDir;
- zXdgHome = getenv("XDG_CONFIG_HOME");
- if( zXdgHome==0 ){
- const char *zHome = getenv("HOME");
- if( zHome==0 ) return 0;
- zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
+ zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
+ if( zXdgDir ){
+ zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
}else{
- zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
+ const char * zHome = find_home_dir(0);
+ if( zHome==0 ) return 0;
+ zConfigFile = (zSubdir && *zSubdir)
+ ? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
+ : sqlite3_mprintf("%s/%s", zHome, zBaseName);
}
- shell_check_oom(zConfig);
- if( access(zConfig,0)!=0 ){
- sqlite3_free(zConfig);
- zConfig = 0;
+ shell_check_oom(zConfigFile);
+ if( access(zConfigFile,0)!=0 ){
+ sqlite3_free(zConfigFile);
+ zConfigFile = 0;
}
- return zConfig;
+ return zConfigFile;
#endif
}
/*
-** Read input from the file given by sqliterc_override. Or if that
-** parameter is NULL, take input from the first of find_xdg_config()
-** or ~/.sqliterc which is found.
+** Read input from the file sqliterc_override. If that parameter is
+** NULL, take it from find_xdg_file(), if found, or fall back to
+** ~/.sqliterc.
**
-** Returns the number of errors.
+** Failure to read the config is only considered a failure if
+** sqliterc_override is not NULL, in which case this function may emit
+** a warning or, if ::bail_on_error is true, fail fatally if the file
+** named by sqliterc_override is not found.
*/
static void process_sqliterc(
ShellState *p, /* Configuration data */
const char *sqliterc_override /* Name of config file. NULL to use default */
){
char *home_dir = NULL;
- const char *sqliterc = sqliterc_override;
- char *zBuf = 0;
+ char *sqliterc = (char*)sqliterc_override;
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if( sqliterc == NULL ){
- sqliterc = zBuf = find_xdg_config();
+ sqliterc = find_xdg_file("XDG_CONFIG_HOME",
+ ".config",
+ "sqlite3/sqliterc");
}
if( sqliterc == NULL ){
home_dir = find_home_dir(0);
@@ -12820,11 +12847,10 @@ static void process_sqliterc(
" cannot read ~/.sqliterc\n");
return;
}
- zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
- shell_check_oom(zBuf);
- sqliterc = zBuf;
+ sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
+ shell_check_oom(sqliterc);
}
- p->in = sqlite3_fopen(sqliterc,"rb");
+ p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
if( p->in ){
if( stdin_is_interactive ){
sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
@@ -12837,7 +12863,9 @@ static void process_sqliterc(
}
p->in = inSaved;
p->lineno = savedLineno;
- sqlite3_free(zBuf);
+ if( sqliterc != sqliterc_override ){
+ sqlite3_free(sqliterc);
+ }
}
/*
@@ -13605,7 +13633,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
- int nHistory;
sqlite3_fprintf(stdout,
"SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for usage hints.\n",
@@ -13618,11 +13645,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
- zHistory = strdup(zHistory);
- }else if( (zHome = find_home_dir(0))!=0 ){
- nHistory = strlen30(zHome) + 20;
- if( (zHistory = malloc(nHistory))!=0 ){
- sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
+ zHistory = sqlite3_mprintf("%s", zHistory);
+ shell_check_oom(zHistory);
+ }else{
+ zHistory = find_xdg_file("XDG_STATE_HOME",
+ ".local/state",
+ "sqlite_history");
+ if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
+ zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
+ shell_check_oom(zHistory);
}
}
if( zHistory ){ shell_read_history(zHistory); }
@@ -13638,7 +13669,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
- free(zHistory);
+ sqlite3_free(zHistory);
}
}else{
data.in = stdin;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 836c09e2a..8184a5be6 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -497,6 +497,9 @@ int sqlite3_exec(
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
+#define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
+#define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
+#define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
@@ -531,6 +534,8 @@ int sqlite3_exec(
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
+#define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
+#define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 36a21d92e..a09c94ae6 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1154,6 +1154,7 @@ extern u32 sqlite3TreeTrace;
** 0x00040000 SELECT tree dump after all code has been generated
** 0x00080000 NOT NULL strength reduction
** 0x00100000 Pointers are all shown as zero
+** 0x00200000 EXISTS-to-JOIN optimization
*/
/*
@@ -1926,6 +1927,7 @@ struct sqlite3 {
#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */
#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */
#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */
+#define SQLITE_ExistsToJoin 0x40000000 /* The EXISTS-to-JOIN optimization */
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
@@ -2164,7 +2166,7 @@ struct FuncDestructor {
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|\
SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
- pArg, 0, xFunc, 0, 0, 0, #zName, }
+ pArg, 0, xFunc, 0, 0, 0, #zName, {0} }
#define LIKEFUNC(zName, nArg, arg, flags) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
(void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} }
@@ -2808,7 +2810,6 @@ struct Index {
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
- unsigned bIdxRowid:1; /* One or more of the index keys is the ROWID */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
unsigned bHasExpr:1; /* Index contains an expression, either a literal
** expression, or a reference to a VIRTUAL column */
@@ -3081,6 +3082,7 @@ struct Expr {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
+ int nReg; /* TK_NULLS: Number of registers to NULL out */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@@ -3370,6 +3372,7 @@ struct SrcItem {
unsigned rowidUsed :1; /* The ROWID of this table is referenced */
unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */
unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */
+ unsigned fromExists :1; /* Comes from WHERE EXISTS(...) */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
@@ -3900,6 +3903,7 @@ struct Parse {
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
+ u8 bHasExists; /* Has a correlated "EXISTS (SELECT ....)" expression */
u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 bReturning; /* Coding a RETURNING trigger */
@@ -5116,6 +5120,7 @@ void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
void sqlite3ExprCodeCopy(Parse*, Expr*, int);
void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
+void sqlite3ExprNullRegisterRange(Parse*, int, int);
int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
int sqlite3ExprCodeTarget(Parse*, Expr*, int);
int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
diff --git a/src/trigger.c b/src/trigger.c
index 779da5e5f..600f008f3 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -1040,7 +1040,10 @@ static void codeReturningTrigger(
Returning *pReturning;
Select sSelect;
SrcList *pFrom;
- u8 fromSpace[SZ_SRCLIST_1];
+ union {
+ SrcList sSrc;
+ u8 fromSpace[SZ_SRCLIST_1];
+ } uSrc;
assert( v!=0 );
if( !pParse->bReturning ){
@@ -1056,7 +1059,7 @@ static void codeReturningTrigger(
return;
}
memset(&sSelect, 0, sizeof(sSelect));
- pFrom = (SrcList*)fromSpace;
+ pFrom = &uSrc.sSrc;
memset(pFrom, 0, SZ_SRCLIST_1);
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
sSelect.pSrc = pFrom;
diff --git a/src/vdbe.c b/src/vdbe.c
index 0020b52bf..0465ba27a 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -1726,7 +1726,7 @@ case OP_IntCopy: { /* out2 */
** RETURNING clause.
*/
case OP_FkCheck: {
- if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
+ if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){
goto abort_due_to_error;
}
break;
@@ -3910,7 +3910,7 @@ case OP_Savepoint: {
*/
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
if( isTransaction && p1==SAVEPOINT_RELEASE ){
- if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
+ if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
goto vdbe_return;
}
db->autoCommit = 1;
@@ -4028,7 +4028,7 @@ case OP_AutoCommit: {
"SQL statements in progress");
rc = SQLITE_BUSY;
goto abort_due_to_error;
- }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
+ }else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
goto vdbe_return;
}else{
db->autoCommit = (u8)desiredAutoCommit;
@@ -6398,6 +6398,32 @@ case OP_Rewind: { /* jump0, ncycle */
break;
}
+/* Opcode: IfEmpty P1 P2 * * *
+** Synopsis: if( empty(P1) ) goto P2
+**
+** Check to see if the b-tree table that cursor P1 references is empty
+** and jump to P2 if it is.
+*/
+case OP_IfEmpty: { /* jump */
+ VdbeCursor *pC;
+ BtCursor *pCrsr;
+ int res;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p2>=0 && pOp->p2<p->nOp );
+
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
+ assert( pCrsr );
+ rc = sqlite3BtreeIsEmpty(pCrsr, &res);
+ if( rc ) goto abort_due_to_error;
+ VdbeBranchTaken(res!=0,2);
+ if( res ) goto jump_to_p2;
+ break;
+}
+
/* Opcode: Next P1 P2 P3 * P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
diff --git a/src/vdbe.h b/src/vdbe.h
index a7aedfbb0..9a9cf3dec 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -314,7 +314,9 @@ int sqlite3VdbeHasSubProgram(Vdbe*);
void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val);
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
int sqlite3NotPureFunc(sqlite3_context*);
+#endif
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
int sqlite3VdbeBytecodeVtabInit(sqlite3*);
#endif
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 0faa32747..ca28b075a 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -288,7 +288,7 @@ struct sqlite3_value {
** MEM_Int, MEM_Real, and MEM_IntReal.
**
** * MEM_Blob|MEM_Zero A blob in Mem.z of length Mem.n plus
-** MEM.u.i extra 0x00 bytes at the end.
+** Mem.u.nZero extra 0x00 bytes at the end.
**
** * MEM_Int Integer stored in Mem.u.i.
**
@@ -557,7 +557,10 @@ struct PreUpdate {
Table *pTab; /* Schema object being updated */
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
sqlite3_value **apDflt; /* Array of default values, if required */
- u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
+ union {
+ KeyInfo sKey;
+ u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
+ } uKey;
};
/*
@@ -721,9 +724,11 @@ int sqlite3VdbeCheckMemInvariants(Mem*);
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
-int sqlite3VdbeCheckFk(Vdbe *, int);
+int sqlite3VdbeCheckFkImmediate(Vdbe*);
+int sqlite3VdbeCheckFkDeferred(Vdbe*);
#else
-# define sqlite3VdbeCheckFk(p,i) 0
+# define sqlite3VdbeCheckFkImmediate(p) 0
+# define sqlite3VdbeCheckFkDeferred(p) 0
#endif
#ifdef SQLITE_DEBUG
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 8a900aeff..c8b86e6f6 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -2994,10 +2994,12 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
|| nTrans<=1
){
- for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
- Btree *pBt = db->aDb[i].pBt;
- if( pBt ){
- rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
+ if( needXcommit ){
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( sqlite3BtreeTxnState(pBt)>=SQLITE_TXN_WRITE ){
+ rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
+ }
}
}
@@ -3008,7 +3010,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
*/
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( pBt ){
+ int txn = sqlite3BtreeTxnState(pBt);
+ if( txn!=SQLITE_TXN_NONE ){
+ assert( needXcommit || txn==SQLITE_TXN_READ );
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
}
}
@@ -3263,28 +3267,31 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
/*
-** This function is called when a transaction opened by the database
+** These functions are called when a transaction opened by the database
** handle associated with the VM passed as an argument is about to be
-** committed. If there are outstanding deferred foreign key constraint
-** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
+** committed. If there are outstanding foreign key constraint violations
+** return an error code. Otherwise, SQLITE_OK.
**
** If there are outstanding FK violations and this function returns
-** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
-** and write an error message to it. Then return SQLITE_ERROR.
+** non-zero, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
+** and write an error message to it.
*/
#ifndef SQLITE_OMIT_FOREIGN_KEY
-int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
+static SQLITE_NOINLINE int vdbeFkError(Vdbe *p){
+ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
+ p->errorAction = OE_Abort;
+ sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
+ return SQLITE_CONSTRAINT_FOREIGNKEY;
+}
+int sqlite3VdbeCheckFkImmediate(Vdbe *p){
+ if( p->nFkConstraint==0 ) return SQLITE_OK;
+ return vdbeFkError(p);
+}
+int sqlite3VdbeCheckFkDeferred(Vdbe *p){
sqlite3 *db = p->db;
- if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
- || (!deferred && p->nFkConstraint>0)
- ){
- p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
- p->errorAction = OE_Abort;
- sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
- if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
- return SQLITE_CONSTRAINT_FOREIGNKEY;
- }
- return SQLITE_OK;
+ if( (db->nDeferredCons+db->nDeferredImmCons)==0 ) return SQLITE_OK;
+ return vdbeFkError(p);
}
#endif
@@ -3378,7 +3385,7 @@ int sqlite3VdbeHalt(Vdbe *p){
/* Check for immediate foreign key violations. */
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
- (void)sqlite3VdbeCheckFk(p, 0);
+ (void)sqlite3VdbeCheckFkImmediate(p);
}
/* If the auto-commit flag is set and this is the only active writer
@@ -3392,7 +3399,7 @@ int sqlite3VdbeHalt(Vdbe *p){
&& db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
- rc = sqlite3VdbeCheckFk(p, 1);
+ rc = sqlite3VdbeCheckFkDeferred(p);
if( rc!=SQLITE_OK ){
if( NEVER(p->readOnly) ){
sqlite3VdbeLeave(p);
@@ -4257,15 +4264,15 @@ void sqlite3VdbeRecordUnpack(
pMem->z = 0;
sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
d += sqlite3VdbeSerialTypeLen(serial_type);
- pMem++;
if( (++u)>=p->nField ) break;
+ pMem++;
}
if( d>(u32)nKey && u ){
assert( CORRUPT_DB );
/* In a corrupt record entry, the last pMem might have been set up using
** uninitialized memory. Overwrite its value with NULL, to prevent
** warnings from MSAN. */
- sqlite3VdbeMemSetNull(pMem-1);
+ sqlite3VdbeMemSetNull(pMem-(u<p->nField));
}
testcase( u == pKeyInfo->nKeyField + 1 );
testcase( u < pKeyInfo->nKeyField + 1 );
@@ -4436,6 +4443,32 @@ static void vdbeAssertFieldCountWithinLimits(
** or positive value if *pMem1 is less than, equal to or greater than
** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
*/
+static SQLITE_NOINLINE int vdbeCompareMemStringWithEncodingChange(
+ const Mem *pMem1,
+ const Mem *pMem2,
+ const CollSeq *pColl,
+ u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
+){
+ int rc;
+ const void *v1, *v2;
+ Mem c1;
+ Mem c2;
+ sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
+ sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
+ sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
+ sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
+ v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
+ v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
+ if( (v1==0 || v2==0) ){
+ if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
+ rc = 0;
+ }else{
+ rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
+ }
+ sqlite3VdbeMemReleaseMalloc(&c1);
+ sqlite3VdbeMemReleaseMalloc(&c2);
+ return rc;
+}
static int vdbeCompareMemString(
const Mem *pMem1,
const Mem *pMem2,
@@ -4447,25 +4480,7 @@ static int vdbeCompareMemString(
** comparison function directly */
return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
}else{
- int rc;
- const void *v1, *v2;
- Mem c1;
- Mem c2;
- sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
- sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
- sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
- sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
- v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
- v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
- if( (v1==0 || v2==0) ){
- if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
- rc = 0;
- }else{
- rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
- }
- sqlite3VdbeMemReleaseMalloc(&c1);
- sqlite3VdbeMemReleaseMalloc(&c2);
- return rc;
+ return vdbeCompareMemStringWithEncodingChange(pMem1,pMem2,pColl,prcErr);
}
}
@@ -5378,6 +5393,7 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
}
}
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
/*
** Cause a function to throw an error if it was call from OP_PureFunc
** rather than OP_Function.
@@ -5411,6 +5427,7 @@ int sqlite3NotPureFunc(sqlite3_context *pCtx){
}
return 1;
}
+#endif /* SQLITE_OMIT_DATETIME_FUNCS */
#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG)
/*
@@ -5521,7 +5538,7 @@ void sqlite3VdbePreUpdateHook(
preupdate.pCsr = pCsr;
preupdate.op = op;
preupdate.iNewReg = iReg;
- preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
+ preupdate.pKeyinfo = &preupdate.uKey.sKey;
preupdate.pKeyinfo->db = db;
preupdate.pKeyinfo->enc = ENC(db);
preupdate.pKeyinfo->nKeyField = pTab->nCol;
diff --git a/src/where.c b/src/where.c
index ddf3f7499..b60c4d1c0 100644
--- a/src/where.c
+++ b/src/where.c
@@ -3239,6 +3239,7 @@ static int whereLoopAddBtreeIndex(
if( ExprUseXSelect(pExpr) ){
/* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
int i;
+ int bRedundant = 0;
nIn = 46; assert( 46==sqlite3LogEst(25) );
/* The expression may actually be of the form (x, y) IN (SELECT...).
@@ -3247,7 +3248,20 @@ static int whereLoopAddBtreeIndex(
** for each such term. The following loop checks that pTerm is the
** first such term in use, and sets nIn back to 0 if it is not. */
for(i=0; i<pNew->nLTerm-1; i++){
- if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0;
+ if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ){
+ nIn = 0;
+ if( pNew->aLTerm[i]->u.x.iField == pTerm->u.x.iField ){
+ /* Detect when two or more columns of an index match the same
+ ** column of a vector IN operater, and avoid adding the column
+ ** to the WhereLoop more than once. See tag-20250707-01
+ ** in test/rowvalue.test */
+ bRedundant = 1;
+ }
+ }
+ }
+ if( bRedundant ){
+ pNew->nLTerm--;
+ continue;
}
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
@@ -3479,7 +3493,7 @@ static int whereLoopAddBtreeIndex(
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
&& pNew->u.btree.nEq<pProbe->nColumn
&& (pNew->u.btree.nEq<pProbe->nKeyCol ||
- (pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY && !pProbe->bIdxRowid))
+ pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY)
){
if( pNew->u.btree.nEq>3 ){
sqlite3ProgressCheck(pParse);
@@ -3518,6 +3532,7 @@ static int whereLoopAddBtreeIndex(
&& pProbe->hasStat1!=0
&& OptimizationEnabled(db, SQLITE_SkipScan)
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
+ && pSrc->fg.fromExists==0
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
){
LogEst nIter;
@@ -4022,6 +4037,7 @@ static int whereLoopAddBtree(
pNew->u.btree.nEq = 0;
pNew->u.btree.nBtm = 0;
pNew->u.btree.nTop = 0;
+ pNew->u.btree.nDistinctCol = 0;
pNew->nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
@@ -5090,8 +5106,6 @@ static i8 wherePathSatisfiesOrderBy(
obSat = obDone;
}
break;
- }else if( wctrlFlags & WHERE_DISTINCTBY ){
- pLoop->u.btree.nDistinctCol = 0;
}
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
@@ -7134,6 +7148,13 @@ WhereInfo *sqlite3WhereBegin(
sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
(const u8*)&pTabItem->colUsed, P4_INT64);
#endif
+ if( ii>=2
+ && (pTabItem[0].fg.jointype & (JT_LTORJ|JT_LEFT))==0
+ && pLevel->addrHalt==pWInfo->a[0].addrHalt
+ ){
+ sqlite3VdbeAddOp2(v, OP_IfEmpty, pTabItem->iCursor, pWInfo->iBreak);
+ VdbeCoverage(v);
+ }
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
@@ -7390,6 +7411,9 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2);
}
#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
+ if( pTabList->a[pLevel->iFrom].fg.fromExists ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
+ }
/* The common case: Advance to the next row */
if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
diff --git a/src/wherecode.c b/src/wherecode.c
index cc672aa83..1efa34a5d 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -126,7 +126,6 @@ void sqlite3WhereAddExplainText(
#endif
{
VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
-
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
@@ -149,7 +148,10 @@ void sqlite3WhereAddExplainText(
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
str.printfFlags = SQLITE_PRINTF_INTERNAL;
- sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem);
+ sqlite3_str_appendf(&str, "%s %S%s",
+ isSearch ? "SEARCH" : "SCAN",
+ pItem,
+ pItem->fg.fromExists ? " EXISTS" : "");
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
const char *zFmt = 0;
Index *pIdx;
@@ -598,7 +600,9 @@ static Expr *removeUnindexableInClauseTerms(
int iField;
assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
iField = pLoop->aLTerm[i]->u.x.iField - 1;
- if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
+ if( NEVER(pOrigRhs->a[iField].pExpr==0) ){
+ continue; /* Duplicate PK column */
+ }
pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
pOrigRhs->a[iField].pExpr = 0;
if( pRhs ) pRhs->a[pRhs->nExpr-1].u.x.iOrderByCol = iField+1;
@@ -695,7 +699,7 @@ static SQLITE_NOINLINE void codeINTerm(
return;
}
}
- for(i=iEq;i<pLoop->nLTerm; i++){
+ for(i=iEq; i<pLoop->nLTerm; i++){
assert( pLoop->aLTerm[i]!=0 );
if( pLoop->aLTerm[i]->pExpr==pX ) nEq++;
}
@@ -704,22 +708,13 @@ static SQLITE_NOINLINE void codeINTerm(
if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab);
}else{
- Expr *pExpr = pTerm->pExpr;
- if( pExpr->iTable==0 || !ExprHasProperty(pExpr, EP_Subrtn) ){
- sqlite3 *db = pParse->db;
- pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX);
- if( !db->mallocFailed ){
- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab);
- pExpr->iTable = iTab;
- }
- sqlite3ExprDelete(db, pX);
- }else{
- int n = sqlite3ExprVectorSize(pX->pLeft);
- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n));
- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab);
+ sqlite3 *db = pParse->db;
+ Expr *pXMod = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX);
+ if( !db->mallocFailed ){
+ aiMap = (int*)sqlite3DbMallocZero(db, sizeof(int)*nEq);
+ eType = sqlite3FindInIndex(pParse, pXMod, IN_INDEX_LOOP, 0, aiMap, &iTab);
}
- pX = pExpr;
+ sqlite3ExprDelete(db, pXMod);
}
if( eType==IN_INDEX_INDEX_DESC ){
@@ -749,7 +744,7 @@ static SQLITE_NOINLINE void codeINTerm(
if( pIn ){
int iMap = 0; /* Index in aiMap[] */
pIn += i;
- for(i=iEq;i<pLoop->nLTerm; i++){
+ for(i=iEq; i<pLoop->nLTerm; i++){
if( pLoop->aLTerm[i]->pExpr==pX ){
int iOut = iTarget + i - iEq;
if( eType==IN_INDEX_ROWID ){
@@ -2850,7 +2845,10 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
WhereLoop *pLoop = pLevel->pWLoop;
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
SrcList *pFrom;
- u8 fromSpace[SZ_SRCLIST_1];
+ union {
+ SrcList sSrc;
+ u8 fromSpace[SZ_SRCLIST_1];
+ } uSrc;
Bitmask mAll = 0;
int k;
@@ -2894,7 +2892,7 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
}
}
- pFrom = (SrcList*)fromSpace;
+ pFrom = &uSrc.sSrc;
pFrom->nSrc = 1;
pFrom->nAlloc = 1;
memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
diff --git a/src/whereexpr.c b/src/whereexpr.c
index e4be8d9d6..0d17b0d75 100644
--- a/src/whereexpr.c
+++ b/src/whereexpr.c
@@ -948,7 +948,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){
if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* (3) */
assert( pSrc!=0 );
if( pExpr->op==TK_IS
- && pSrc->nSrc
+ && pSrc->nSrc>=2
&& (pSrc->a[0].fg.jointype & JT_LTORJ)!=0
){
return 0; /* (4) */
@@ -1541,7 +1541,7 @@ static void exprAnalyze(
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
pNewTerm = &pWC->a[idxNew];
- pNewTerm->prereqRight = prereqExpr;
+ pNewTerm->prereqRight = prereqExpr | extraRight;
pNewTerm->leftCursor = pLeft->iTable;
pNewTerm->u.x.leftColumn = pLeft->iColumn;
pNewTerm->eOperator = WO_AUX;
diff --git a/test/between.test b/test/between.test
index 16c3913d1..5e02ef9b2 100644
--- a/test/between.test
+++ b/test/between.test
@@ -140,4 +140,21 @@ foreach {tn expr res} {
do_execsql_test between-2.1.$tn $sql $res
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test between-3.0 {
+ CREATE TABLE t1(x, y);
+ CREATE INDEX i1 ON t1(x);
+ INSERT INTO t1 VALUES(4, 4);
+ CREATE TABLE t2(a, b);
+}
+
+do_execsql_test between-3.1 {
+ SELECT * FROM t1 LEFT JOIN t2 ON (x BETWEEN 1 AND 3);
+} {4 4 {} {}}
+
+do_execsql_test between-3.2 {
+ SELECT * FROM t1 LEFT JOIN t2 ON (x BETWEEN 5 AND 7);
+} {4 4 {} {}}
+
finish_test
diff --git a/test/eqp.test b/test/eqp.test
index 5d2659be7..147b5ceaf 100644
--- a/test/eqp.test
+++ b/test/eqp.test
@@ -338,8 +338,7 @@ det 3.3.3 {
} {
QUERY PLAN
|--SCAN t1
- `--CORRELATED SCALAR SUBQUERY xxxxxx
- `--SCAN t2
+ `--SCAN t2 EXISTS
}
#-------------------------------------------------------------------------
diff --git a/test/existsexpr.test b/test/existsexpr.test
new file mode 100644
index 000000000..d02f8c5c1
--- /dev/null
+++ b/test/existsexpr.test
@@ -0,0 +1,432 @@
+# 2024 May 25
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix existsexpr
+
+
+do_execsql_test 1.0 {
+ CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
+ INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
+ CREATE INDEX x1b ON x1(b);
+
+ CREATE TABLE x2(x, y);
+ INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
+}
+
+do_execsql_test 1.1 {
+ SELECT 1 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=5)
+} {1}
+
+do_execsql_test 1.2 {
+ SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
+} {1 2 3 4 5 6}
+
+# With "a=x", the UNIQUE index means the EXIST can be transformed to a join.
+# So no "SUBQUERY". With "b=x", the index is not UNIQUE and so there is a
+# "SUBQUERY".
+do_execsql_test 1.3.1 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
+} {~/SUBQUERY/}
+do_execsql_test 1.3.2 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE b=x)
+} {~/SUBQUERY/}
+
+do_execsql_test 1.4.1 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM x2 WHERE x=1 AND EXISTS (SELECT 1 FROM x1 WHERE a=x)
+} {~/SUBQUERY/}
+do_execsql_test 1.4.2 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y=2
+} {~/SUBQUERY/}
+
+do_execsql_test 1.5 {
+ SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x)
+} {3}
+
+#-------------------------------------------------------------------------
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a, b);
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
+ ) INSERT INTO t1 SELECT i, i FROM s;
+
+ CREATE TABLE t2(c, d);
+ WITH s(i) AS (
+ SELECT 10 UNION ALL SELECT i+10 FROM s WHERE i<1000
+ ) INSERT INTO t2 SELECT i, i FROM s;
+}
+
+do_execsql_test 2.1 {
+ SELECT count(*) FROM t1;
+ SELECT count(*) FROM t2;
+} {1000 100}
+
+do_execsql_test 2.2 {
+ SELECT count(*) FROM t1, t2 WHERE a=c;
+} {100}
+
+do_execsql_test 2.3 {
+ SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a)
+} {100}
+do_eqp_test 2.4 {
+ SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a)
+} {SCAN t1}
+
+do_execsql_test 2.4.0 {
+ CREATE UNIQUE INDEX t2c ON t2(c);
+ CREATE UNIQUE INDEX t1a ON t1(a);
+}
+
+do_eqp_test 2.4.1 {
+ SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
+} {SCAN t1*t2 EXISTS}
+do_execsql_test 2.4.2 {
+ ANALYZE;
+}
+do_eqp_test 2.4.3 {
+ SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
+} {SCAN t1*t2 EXISTS}
+do_execsql_test 2.4.4 {
+ SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a);
+} {100}
+
+do_execsql_test 2.5.1 {
+ EXPLAIN QUERY PLAN
+ SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.rowid=a);
+} {~/SUBQUERY/}
+
+#-------------------------------------------------------------------------
+proc do_subquery_test {tn bSub sql res} {
+ set r1(0) ~/SUBQUERY/
+ set r1(1) /SUBQUERY/
+ do_execsql_test $tn.1 "explain query plan $sql" $r1($bSub)
+ do_execsql_test $tn.2 $sql $res
+}
+
+do_execsql_test 3.0 {
+ CREATE TABLE y1(a, b, c);
+ CREATE TABLE y2(x, y, z);
+ CREATE UNIQUE INDEX y2zy ON y2(z, y);
+
+ INSERT INTO y1 VALUES(1, 1, 1);
+ INSERT INTO y1 VALUES(2, 2, 2);
+ INSERT INTO y1 VALUES(3, 3, 3);
+ INSERT INTO y1 VALUES(4, 4, 4);
+
+ INSERT INTO y2 VALUES(1, 1, 1);
+ INSERT INTO y2 VALUES(3, 3, 3);
+}
+
+do_subquery_test 3.1 0 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 WHERE z=a AND y=b AND x=z
+ )
+} {
+ 1 1 1 3 3 3
+}
+
+do_subquery_test 3.2 0 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND x=z
+ )
+} {
+ 1 1 1 3 3 3
+}
+
+do_subquery_test 3.3 0 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND c!=3
+ )
+} {
+ 1 1 1
+}
+
+do_subquery_test 3.4 0 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 WHERE z=max(a,b) AND b=3
+ )
+} {
+ 3 3 3
+}
+
+do_subquery_test 3.5 0 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 WHERE z=a-1 AND y=a-1
+ )
+} {
+ 2 2 2
+ 4 4 4
+}
+
+do_subquery_test 3.6 0 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 WHERE z=a-1 AND y+1=a
+ )
+} {
+ 2 2 2
+ 4 4 4
+}
+
+do_subquery_test 3.7 1 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT count(*) FROM y2 WHERE z=a-1 AND y=a-1
+ )
+} {
+ 1 1 1
+ 2 2 2
+ 3 3 3
+ 4 4 4
+}
+
+do_subquery_test 3.8 0 {
+ SELECT * FROM y1 WHERE EXISTS ( SELECT a+1 FROM y2 )
+} {
+ 1 1 1
+ 2 2 2
+ 3 3 3
+ 4 4 4
+}
+
+do_subquery_test 3.9 1 {
+ SELECT * FROM y1 WHERE EXISTS (
+ SELECT 1 FROM y2 one, y2 two WHERE one.z=a-1 AND one.y=a-1
+ )
+} {
+ 2 2 2
+ 4 4 4
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 4.0 {
+ CREATE TABLE tx1(a TEXT COLLATE nocase, b TEXT);
+ CREATE UNIQUE INDEX tx1ab ON tx1(a, b);
+
+ INSERT INTO tx1 VALUES('a', 'a');
+ INSERT INTO tx1 VALUES('B', 'b');
+ INSERT INTO tx1 VALUES('c', 'c');
+ INSERT INTO tx1 VALUES('D', 'd');
+ INSERT INTO tx1 VALUES('e', 'e');
+
+ CREATE TABLE tx2(x, y);
+ INSERT INTO tx2 VALUES('A', 'a');
+ INSERT INTO tx2 VALUES('b', 'b');
+ INSERT INTO tx2 VALUES('C', 'c');
+ INSERT INTO tx2 VALUES('D', 'd');
+}
+
+do_subquery_test 4.1 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE a=x AND b=y
+ )
+} {
+ A a
+ b b
+ C c
+ D d
+}
+
+do_subquery_test 4.1.1 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE (a COLLATE nocase)=x AND b=y
+ )
+} {
+ A a b b C c D d
+}
+do_subquery_test 4.1.2 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE a=x AND (b COLLATE binary)=y
+ )
+} {
+ A a b b C c D d
+}
+do_subquery_test 4.1.1 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE x=(a COLLATE nocase) AND b=y
+ )
+} {
+ A a b b C c D d
+}
+do_subquery_test 4.1.2 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE a=x AND y=(b COLLATE binary)
+ )
+} {
+ A a b b C c D d
+}
+
+do_subquery_test 4.2 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE a=x AND b=y COLLATE nocase
+ )
+} {
+ A a
+ b b
+ C c
+ D d
+}
+
+do_execsql_test 4.3 {
+ DROP INDEX tx1ab;
+ CREATE UNIQUE INDEX tx1ab ON tx1(a COLLATE binary, b);
+}
+
+do_subquery_test 4.4 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE a=x AND b=y
+ )
+} {
+ A a
+ b b
+ C c
+ D d
+}
+
+do_subquery_test 4.4 0 {
+ SELECT * FROM tx2 WHERE EXISTS (
+ SELECT 1 FROM tx1 WHERE a=x COLLATE binary AND b=y
+ )
+} {
+ D d
+}
+
+do_subquery_test 4.4 1 {
+ SELECT EXISTS ( SELECT x FROM tx1 ) FROM tx2
+} {
+ 1 1 1 1
+}
+
+do_subquery_test 4.4 1 {
+ SELECT (SELECT EXISTS ( SELECT x FROM tx1 ) WHERE 1) FROM tx2
+} {
+ 1 1 1 1
+}
+
+#-------------------------------------------------------------------------
+proc cols {s f} {
+ set lCols [list]
+ for {set i $s} {$i<=$f} {incr i} {
+ lappend lCols [format "c%02d" $i]
+ }
+ join $lCols ", "
+}
+proc vals {n val} {
+ set lVal [list]
+ for {set i 0} {$i<$n} {incr i} {
+ lappend lVal $val
+ }
+ join $lVal ", "
+}
+proc exprs {s f} {
+ set lExpr [list]
+ for {set i $s} {$i<=$f} {incr i} {
+ lappend lExpr [format "c%02d = o" $i]
+ }
+ join $lExpr " AND "
+}
+
+
+do_execsql_test 5.0 "
+ CREATE TABLE a1( [cols 0 99] );
+"
+do_execsql_test 5.1 "
+ -- 63 column index
+ CREATE UNIQUE INDEX a1idx1 ON a1( [cols 0 62] );
+"
+do_execsql_test 5.2 "
+ -- 64 column index
+ CREATE UNIQUE INDEX a1idx2 ON a1( [cols 10 73] );
+"
+do_execsql_test 5.2 "
+ -- 65 column index
+ CREATE UNIQUE INDEX a1idx3 ON a1( [cols 20 84] );
+"
+
+do_test 5.3 {
+ foreach v {1 2 3 4 5 6} {
+ execsql "INSERT INTO a1 VALUES( [vals 100 $v] )"
+ }
+} {}
+
+do_execsql_test 5.4 {
+ CREATE TABLE a2(o);
+ INSERT INTO a2 VALUES(2), (5);
+}
+
+do_subquery_test 5.5 0 "
+ SELECT o FROM a2 WHERE EXISTS (
+ SELECT 1 FROM a1 WHERE [exprs 0 62]
+ )
+" {
+ 2 5
+}
+
+do_subquery_test 5.6 0 "
+ SELECT o FROM a2 WHERE EXISTS (
+ SELECT 1 FROM a1 WHERE [exprs 10 73]
+ )
+" {
+ 2 5
+}
+
+do_subquery_test 5.7 0 "
+ SELECT o FROM a2 WHERE EXISTS (
+ SELECT 1 FROM a1 WHERE [exprs 20 84]
+ )
+" {
+ 2 5
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 6.0 {
+ CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
+ CREATE TABLE t2(a INfEGER PRIMARY KEY, b);
+ CREATE UNIQUE INDEX t2b ON t2(b);
+}
+
+do_catchsql_test 6.1 {
+ SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c COLLATE f = a)
+} {1 {no such collation sequence: f}}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 7.0 {
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(y UNIQUE);
+
+ INSERT INTO t1 VALUES(1), (2);
+ INSERT INTO t2 VALUES(1), (3);
+
+ SELECT * FROM t1 one LEFT JOIN t1 two ON (one.x=two.x AND EXISTS (
+ SELECT 1 FROM t2 WHERE y=one.x
+ ));
+} {
+ 1 1
+ 2 {}
+}
+
+# https://sqlite.org/forum/forumpost/2025-07-23T10:59:14z
+reset_db
+do_execsql_test 8.0 {
+ CREATE TABLE t0 (c0 INT); INSERT INTO t0(c0) VALUES (1);
+ CREATE TABLE t1(c0 INT); INSERT INTO t1(c0) VALUES (2);
+ SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t0 LIMIT 0);
+} {}
+
+finish_test
diff --git a/test/existsexpr2.test b/test/existsexpr2.test
new file mode 100644
index 000000000..f7644bf80
--- /dev/null
+++ b/test/existsexpr2.test
@@ -0,0 +1,96 @@
+# 2024 June 14
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix existsexpr2
+
+
+do_execsql_test 1.0 {
+ CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
+ INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
+ CREATE INDEX x1b ON x1(b);
+
+ CREATE TABLE x2(x, y);
+ INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
+}
+
+do_execsql_test 1.1 {
+ SELECT * FROM x1 WHERE EXISTS (SELECT 1 FROM x2 WHERE a!=123)
+} {1 2 3 4 5 6}
+
+do_execsql_test 1.2 {
+ CREATE TABLE x3(u, v);
+ CREATE INDEX x3u ON x3(u);
+ INSERT INTO x3 VALUES
+ (1, 1), (1, 2), (1, 3),
+ (2, 1), (2, 2), (2, 3);
+}
+
+do_execsql_test 1.3 {
+ SELECT * FROM x1 WHERE EXISTS (
+ SELECT 1 FROM x3 WHERE u IN (1, 2, 3, 4) AND v=b
+ );
+} {
+ 1 2
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 2.0 {
+ CREATE TABLE t1(a, b, c);
+ CREATE INDEX t1ab ON t1(a,b);
+
+ INSERT INTO t1 VALUES
+ ('abc', 1, 1),
+ ('abc', 2, 2),
+ ('abc', 2, 3),
+
+ ('def', 1, 1),
+ ('def', 2, 2),
+ ('def', 2, 3);
+
+ CREATE TABLE t2(x, y);
+ INSERT INTO t2 VALUES(1, 1), (2, 2), (3, 3);
+
+ ANALYZE;
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1 VALUES('t1','t1ab','10000 5000 2');
+ ANALYZE sqlite_master;
+}
+
+
+do_execsql_test 2.1 {
+ SELECT a,b,c FROM t1 WHERE b=2 ORDER BY a
+} {
+ abc 2 2
+ abc 2 3
+ def 2 2
+ def 2 3
+}
+
+do_execsql_test 2.2 {
+ SELECT x, y FROM t2 WHERE EXISTS (
+ SELECT 1 FROM t1 WHERE b=x
+ )
+} {
+ 1 1
+ 2 2
+}
+
+
+
+finish_test
+
+
diff --git a/test/existsfault.test b/test/existsfault.test
new file mode 100644
index 000000000..4b335d84c
--- /dev/null
+++ b/test/existsfault.test
@@ -0,0 +1,49 @@
+# 2024 May 25
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/malloc_common.tcl
+set testprefix existsfault
+
+db close
+sqlite3_shutdown
+sqlite3_config_lookaside 0 0
+sqlite3_initialize
+autoinstall_test_functions
+sqlite3 db test.db
+
+do_execsql_test 1.0 {
+ CREATE TABLE x1(a, b);
+ INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6);
+ CREATE UNIQUE INDEX x1a ON x1(a);
+ CREATE INDEX x1b ON x1(b);
+
+ CREATE TABLE x2(x, y);
+ INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6);
+}
+
+do_faultsim_test 1 -faults oom* -prep {
+ sqlite3 db test.db
+ execsql { SELECT * FROM sqlite_schema }
+} -body {
+ execsql {
+ SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y!=11
+ }
+} -test {
+ faultsim_test_result {0 3}
+}
+
+finish_test
+
+
diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c
index 09898d7b3..6157dc443 100644
--- a/test/fuzzcheck.c
+++ b/test/fuzzcheck.c
@@ -541,9 +541,12 @@ static void blobListLoadFromDb(
int n = 0;
int rc;
char *z2;
- unsigned char tmp[SZ_BLOB(8)];
+ union {
+ Blob * sBlob;
+ unsigned char tmp[SZ_BLOB(8)];
+ } uBlob;
- head = (Blob*)tmp;
+ head = &uBlob.sBlob;
if( firstId>0 ){
z2 = sqlite3_mprintf("%s WHERE rowid BETWEEN %d AND %d", zSql,
firstId, lastId);
diff --git a/test/incrblob4.test b/test/incrblob4.test
index 31040e91b..c9bcee8a3 100644
--- a/test/incrblob4.test
+++ b/test/incrblob4.test
@@ -108,6 +108,8 @@ close $blob
#-------------------------------------------------------------------------
+ifcapable preupdate {
+
reset_db
do_execsql_test 5.1 {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
@@ -127,7 +129,8 @@ do_test 5.2.1 {
do_test 5.2.2 {
puts -nonewline $blob "world"
- list [catch { flush $blob } msg] $msg
+ set rc [catch { flush $blob } msg]
+ list $rc [regsub {input/output} $msg {I/O}]
} "1 {error flushing \"$blob\": I/O error}"
catch { close $blob }
@@ -147,7 +150,8 @@ do_test 5.3.1 {
do_test 5.3.2 {
puts -nonewline $blob "world"
- list [catch { flush $blob } msg] $msg
+ set rc [catch { flush $blob } msg]
+ list $rc [regsub {input/output} $msg {I/O}]
} "1 {error flushing \"$blob\": I/O error}"
catch { close $blob }
@@ -188,13 +192,16 @@ do_test 5.4.3 {
do_test 5.4.4 {
puts -nonewline $blob "world"
- list [catch { flush $blob } msg] $msg
+ set rc [catch { flush $blob } msg]
+ list $rc [regsub {input/output} $msg {I/O}]
} "1 {error flushing \"$blob\": I/O error}"
catch { close $blob }
catchsql { ROLLBACK }
-do_test 5.3.3 {
+do_test 5.4.5 {
set ::preupdate_count
} {2}
+}
+
finish_test
diff --git a/test/json101.test b/test/json101.test
index e22902f86..7582d14a6 100644
--- a/test/json101.test
+++ b/test/json101.test
@@ -892,15 +892,15 @@ do_execsql_test json101-13.100 {
INSERT INTO t2(id,json) VALUES(4,'{"value":4}');
INSERT INTO t2(id,json) VALUES(5,'{"value":5}');
INSERT INTO t2(id,json) VALUES(6,'{"value":6}');
- SELECT * FROM t1 CROSS JOIN t2
+ SELECT *, 'NL' FROM t1 CROSS JOIN t2
WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
WHERE Z.value==t2.id);
-} {1 {{"items":[3,5]}} 3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}}}
+} {1 {{"items":[3,5]}} 3 {{"value":3}} NL 1 {{"items":[3,5]}} 5 {{"value":5}} NL}
do_execsql_test json101-13.110 {
- SELECT * FROM t2 CROSS JOIN t1
+ SELECT *, 'NL' FROM t2 CROSS JOIN t1
WHERE EXISTS(SELECT 1 FROM json_each(t1.json,'$.items') AS Z
WHERE Z.value==t2.id);
-} {3 {{"value":3}} 1 {{"items":[3,5]}} 5 {{"value":5}} 1 {{"items":[3,5]}}}
+} {3 {{"value":3}} 1 {{"items":[3,5]}} NL 5 {{"value":5}} 1 {{"items":[3,5]}} NL}
# 2018-05-16
# Incorrect fullkey output from json_each()
diff --git a/test/notnull2.test b/test/notnull2.test
index 09161efbd..67d7c26a8 100644
--- a/test/notnull2.test
+++ b/test/notnull2.test
@@ -66,7 +66,7 @@ do_vmstep_test 1.5.2 {
SELECT count(*) FROM t2 WHERE EXISTS(
SELECT 1 FROM t1 WHERE t1.a=450 AND t2.c IS NULL
)
-} +8000 {0}
+} 4000 {0}
#-------------------------------------------------------------------------
reset_db
diff --git a/test/rowvalue.test b/test/rowvalue.test
index e2688e903..387780c45 100644
--- a/test/rowvalue.test
+++ b/test/rowvalue.test
@@ -788,6 +788,9 @@ do_execsql_test 33.3 {
# INTEGER PRIMARY KEY, and the columns that UNIQUE constraint are
# used in a rowvalue-IN operator constraint.
#
+# 2025-07-07 Discovered that the original fix was incomplete and
+# new tests added. See tag-20250707-01 in the code.
+#
reset_db
do_execsql_test 34.1 {
CREATE TABLE items (
@@ -804,5 +807,39 @@ do_execsql_test 34.1 {
WHERE (Id, Item) IN (SELECT Id, Item FROM items);
SELECT Id, Item, test FROM items ORDER BY id;
} {1 2 ok 2 2 ok 3 3 ok 4 5 ok}
+db null NULL
+do_execsql_test 34.2 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
+ CREATE INDEX idx ON t1(b,a);
+ INSERT INTO t1(a,b) VALUES (1, 22);
+ SELECT * FROM t1 INDEXED BY idx WHERE (b,a) IN (SELECT b,a FROM t1);
+} {1 22 NULL NULL}
+do_execsql_test 34.3 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a, b, c, d);
+ CREATE INDEX idx ON t1(b,a,a);
+ INSERT INTO t1(a,b) VALUES (1, 22);
+ SELECT * FROM t1 INDEXED BY idx WHERE (b,a) IN (SELECT b,a FROM t1);
+} {1 22 NULL NULL}
+do_execsql_test 34.4 {
+ DROP TABLE t1;
+ CREATE TABLE t1(id INTEGER PRIMARY KEY, a INT);
+ CREATE INDEX t1a ON t1(a,id); -- index includes PRIMARY KEY
+ CREATE TABLE t2(id INTEGER PRIMARY KEY);
+ WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<100)
+ INSERT INTO t1(id,a) SELECT n, 777 FROM c;
+ INSERT INTO t2 SELECT id FROM t1;
+ SELECT *
+ FROM t1 JOIN t2 USING(id)
+ WHERE t1.a=777 AND t2.id>999
+ ORDER BY t1.id;
+} {}
+do_execsql_test 34.5 {
+ EXPLAIN QUERY PLAN
+ SELECT *
+ FROM t1 JOIN t2 USING(id)
+ WHERE t1.a=777 AND t2.id>999
+ ORDER BY t1.id;
+} {/SEARCH t1 USING COVERING INDEX t1a .a=. AND id>../}
finish_test
diff --git a/test/speedtest1.c b/test/speedtest1.c
index 10cd30f1c..968f2cb00 100644
--- a/test/speedtest1.c
+++ b/test/speedtest1.c
@@ -159,6 +159,12 @@ static void fatal_error(const char *zMsg, ...){
va_start(ap, zMsg);
vfprintf(stderr, zMsg, ap);
va_end(ap);
+#ifdef SQLITE_SPEEDTEST1_WASM
+ /* Emscripten complains when exit() is called and anything is left
+ in the I/O buffers. */
+ fflush(stdout);
+ fflush(stderr);
+#endif
exit(1);
}
@@ -2994,7 +3000,13 @@ int main(int argc, char **argv){
/* "mix1" is a macro testset: */
static char zMix1Tests[] =
- "main,orm/25,cte/20,json,fp/3,parsenumber/25,rtree/10,star,app";
+ "main,orm/25,cte/20,json,fp/3,parsenumber/25,rtree/10,star"
+#if !defined(SQLITE_SPEEDTEST1_WASM)
+ ",app"
+ /* This test misbehaves in WASM builds: sqlite3_open_v2() is
+ failing to find the db file for reasons not yet understood. */
+#endif
+ ;
#ifdef SQLITE_SPEEDTEST1_WASM
/* Resetting all state is important for the WASM build, which may
diff --git a/test/testrunner.tcl b/test/testrunner.tcl
index 0c6982f42..52bc0f34b 100755
--- a/test/testrunner.tcl
+++ b/test/testrunner.tcl
@@ -7,6 +7,18 @@ set testdir [file normalize [file dirname $argv0]]
set saved $argv
set argv [list]
source [file join $testdir testrunner_data.tcl]
+
+# Estimated amount of work required by displaytype, relative to 'tcl'
+#
+set estwork(tcl) 1
+set estwork(fuzz) 22
+set estwork(bld) 66
+set estwork(make) 102
+
+set estworkfile [file join $testdir testrunner_estwork.tcl]
+if {[file readable $estworkfile]} {
+ source $estworkfile
+}
source [file join $testdir permutations.test]
set argv $saved
cd $dir
@@ -92,6 +104,7 @@ Usage:
$a0 script ?-msvc? CONFIG
$a0 status ?-d SECS? ?--cls?
$a0 halt
+ $a0 estwork
where SWITCHES are:
--buildonly Build test exes but do not run tests
@@ -341,6 +354,7 @@ set TRG(schema) {
endtime INTEGER, -- End time
span INTEGER, -- Total run-time in milliseconds
estwork INTEGER, -- Estimated amount of work
+ estkey TEXT, -- Key used to compute estwork
state TEXT CHECK( state IN ('','ready','running','done','failed','omit','halt') ),
ntest INT, -- Number of test cases run
nerr INT, -- Number of errors reported
@@ -359,13 +373,6 @@ set TRG(schema) {
}
#-------------------------------------------------------------------------
-# Estimated amount of work required by displaytype, relative to 'tcl'
-#
-set estwork(tcl) 1
-set estwork(fuzz) 11
-set estwork(bld) 56
-set estwork(make) 97
-
#--------------------------------------------------------------------------
# Check if this script is being invoked to run a single file. If so,
# run it.
@@ -453,6 +460,59 @@ if {[llength $argv]==1
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------
+# Check if this is the "estwork" command:
+#
+# Generate (on standard output) a set of estwork() values based on the lastest
+# test case, that can be used to replace the test/testrunner_estwork.tcl file.
+#
+if {[llength $argv]==1
+ && [string compare -nocase estwork [lindex $argv 0]]==0
+} {
+ sqlite3 mydb $TRG(dbname)
+ set njob [mydb one {SELECT count(*) FROM jobs WHERE state='done'}]
+ if {$njob<1000} {
+ puts "Too few completed jobs to do a work estimate."
+ puts "Have $njob but not need at least 1000."
+ mydb close
+ exit 1
+ }
+ set badjobs [mydb one {SELECT count(*) FROM jobs WHERE state<>'done'}]
+ if {$badjobs} {
+ puts "Database contains $badjobs incomplete jobs."
+ mydb close
+ exit 1
+ }
+ set half [mydb one {SELECT count(*)/2 FROM jobs WHERE displaytype='tcl'}]
+ set scale [mydb one {SELECT span FROM jobs WHERE displaytype='tcl'
+ ORDER BY span LIMIT 1 OFFSET $half}]
+ mydb eval {
+ SELECT estkey, CAST(avg(span)/$scale AS INT) AS cost
+ FROM jobs
+ GROUP BY estkey
+ HAVING cost>=2
+ } {
+ set estwork($estkey) $cost
+ }
+ set avgtcl [mydb one {SELECT avg(span) FROM jobs WHERE displaytype='tcl'}]
+ set estwork(tcl) 1
+ foreach type {bld fuzz make} {
+ set avg [mydb one {SELECT avg(span) FROM jobs WHERE displaytype=$type}]
+ if {$avg!=""} {
+ set estwork($type) [expr {int($avg/$avgtcl)}]
+ }
+ }
+ mydb close
+ puts "# Estimated relative cost of various jobs, based on the \"estkey\" field."
+ puts "# Computed by the \"test/testrunner.tcl estwork\" command."
+ puts "#"
+ foreach key [lsort [array names estwork]] {
+ puts "set [list estwork($key)] $estwork($key)"
+ }
+ exit
+}
+#--------------------------------------------------------------------------
+
+#--------------------------------------------------------------------------
# Check if this is the "help" command:
#
if {[string compare -nocase help [lindex $argv 0]]==0} {
@@ -562,8 +622,8 @@ proc show_status {db cls} {
set srcdir [file dirname [file dirname $TRG(info_script)]]
set line "Running: $S(running) (max: $nJob)"
- if {$S(running)>0 && $fin>10} {
- set tmleft [expr {($tm/$fin)*($totalw-$fin)}]
+ if {$S(running)>0 && [set pct [expr {int(($fin*100.0)/$totalw)}]]>=4} {
+ set tmleft [expr {($tm/double($fin))*($totalw-$fin)}]
if {$tmleft<0.02*$tm} {
set tmleft [expr {$tm*0.02}]
}
@@ -571,6 +631,7 @@ proc show_status {db cls} {
if {[string length $line]+[string length $etc]<80} {
append line $etc
}
+ # append line " $pct%"
}
puts [format %-79.79s $line]
if {$S(running)>0} {
@@ -985,12 +1046,31 @@ proc add_job {args} {
set state ""
if {$A(-depid)==""} { set state ready }
set type $A(-displaytype)
- set ew $estwork($type)
+ set displayname $A(-displayname)
+ switch $type {
+ tcl {
+ set ek [file tail [lindex $displayname end]]
+ }
+ bld {
+ set ek [lindex $displayname end]
+ }
+ fuzz {
+ set ek [lrange $displayname 1 2]
+ }
+ make {
+ set ek [lindex $displayname end]
+ }
+ }
+ if {[info exists estwork($ek)]} {
+ set ew $estwork($ek)
+ } else {
+ set ew $estwork($type)
+ }
trdb eval {
INSERT INTO jobs(
- displaytype, displayname, build, dirname, cmd, depid, priority, estwork,
- state
+ displaytype, displayname, build, dirname, cmd, depid, priority,
+ estwork, estkey, state
) VALUES (
$type,
$A(-displayname),
@@ -1000,6 +1080,7 @@ proc add_job {args} {
$A(-depid),
$A(-priority),
$ew,
+ $ek,
$state
)
}
@@ -1586,12 +1667,13 @@ proc progress_report {} {
}
}
set report "[elapsetime $tmms] [join $text { }]"
- if {$wdone>0} {
- set tmleft [expr {($tmms/$wdone)*($wtotal-$wdone)}]
- set etc " ETC [elapsetime $tmleft]"
+ if {$wdone>0 && [set pct [expr {int(($wdone*100.0)/$wtotal)}]]>=4} {
+ set tmleft [expr {($tmms/double($wdone))*($wtotal-$wdone)}]
+ set etc " ETC [elapsetime $tmleft]"
if {[string length $report]+[string length $etc]<80} {
append report $etc
}
+ # append report " $pct%"
}
puts -nonewline [format %-79.79s $report]\r
flush stdout
diff --git a/test/testrunner_estwork.tcl b/test/testrunner_estwork.tcl
new file mode 100644
index 000000000..c139394a5
--- /dev/null
+++ b/test/testrunner_estwork.tcl
@@ -0,0 +1,488 @@
+# Estimated relative cost of various jobs, based on the "estkey" field.
+# Computed by the "test/testrunner.tcl estwork" command.
+#
+set estwork((fuzzcheck)) 1291
+set estwork((fuzzcheck-asan)) 2427
+set estwork((fuzzcheck-ubsan)) 2749
+set estwork((sessionfuzz)) 1276
+set estwork((sqlite3)) 1281
+set estwork((testfixture)) 1546
+set estwork(aggerror.test) 5
+set estwork(alter.test) 20
+set estwork(alter3.test) 2
+set estwork(alterauth.test) 5
+set estwork(altercol.test) 5
+set estwork(alterdropcol.test) 17
+set estwork(alterlegacy.test) 2
+set estwork(altertab.test) 34
+set estwork(altertab2.test) 2
+set estwork(altertab3.test) 12
+set estwork(amatch1.test) 40
+set estwork(atof1.test) 146
+set estwork(atomic2.test) 2
+set estwork(auth.test) 2
+set estwork(autoindex1.test) 5
+set estwork(autoindex3.test) 5
+set estwork(autovacuum.test) 15
+set estwork(avfs.test) 16
+set estwork(avtrans.test) 285
+set estwork(backup2.test) 15
+set estwork(bestindex4.test) 12
+set estwork(bigrow.test) 5
+set estwork(bitvec.test) 94
+set estwork(bld) 99
+set estwork(blob.test) 2
+set estwork(boundary1.test) 8
+set estwork(boundary2.test) 16
+set estwork(boundary3.test) 14
+set estwork(btree01.test) 20
+set estwork(busy.test) 2
+set estwork(busy2.test) 288
+set estwork(capi3.test) 5
+set estwork(capi3b.test) 6
+set estwork(capi3c.test) 5
+set estwork(capi3d.test) 8
+set estwork(capi3e.test) 20
+set estwork(changes.test) 11
+set estwork(chunksize.test) 5
+set estwork(closure01.test) 63
+set estwork(collate5.test) 2
+set estwork(conflict.test) 8
+set estwork(conflict2.test) 4
+set estwork(conflict3.test) 8
+set estwork(corrupt2.test) 2
+set estwork(corrupt4.test) 91
+set estwork(corrupt7.test) 5
+set estwork(corrupt8.test) 3
+set estwork(corruptB.test) 4
+set estwork(corruptF.test) 6
+set estwork(count.test) 18
+set estwork(crash8.test) 14
+set estwork(createtab.test) 3
+set estwork(csv01.test) 7
+set estwork(cursorhint.test) 4
+set estwork(date.test) 17
+set estwork(date4.test) 64
+set estwork(date5.test) 3
+set estwork(dbdata.test) 77
+set estwork(dbfuzz001.test) 2
+set estwork(dbstatus.test) 4
+set estwork(decimal.test) 2
+set estwork(delete.test) 4
+set estwork(diskfull.test) 34
+set estwork(e_blobbytes.test) 11
+set estwork(e_createtable.test) 14
+set estwork(e_delete.test) 3
+set estwork(e_droptrigger.test) 6
+set estwork(e_dropview.test) 2
+set estwork(e_expr.test) 251
+set estwork(e_select.test) 5
+set estwork(e_select2.test) 5
+set estwork(e_vacuum.test) 6
+set estwork(e_walauto.test) 51
+set estwork(e_walckpt.test) 6
+set estwork(enc.test) 2
+set estwork(enc3.test) 11
+set estwork(enc4.test) 3
+set estwork(eval.test) 2
+set estwork(exists.test) 4
+set estwork(existsexpr.test) 44
+set estwork(expert1.test) 14
+set estwork(expr.test) 5
+set estwork(filectrl.test) 5
+set estwork(filefmt.test) 6
+set estwork(fkey2.test) 9
+set estwork(fpconv1.test) 40
+set estwork(fts3aa.test) 18
+set estwork(fts3ad.test) 10
+set estwork(fts3ag.test) 6
+set estwork(fts3aj.test) 8
+set estwork(fts3am.test) 5
+set estwork(fts3auto.test) 29
+set estwork(fts3b.test) 41
+set estwork(fts3c.test) 22
+set estwork(fts3conf.test) 3
+set estwork(fts3corrupt4.test) 7
+set estwork(fts3corrupt5.test) 8
+set estwork(fts3corrupt6.test) 13
+set estwork(fts3d.test) 2
+set estwork(fts3defer2.test) 2
+set estwork(fts3expr.test) 10
+set estwork(fts3expr2.test) 8
+set estwork(fts3expr3.test) 145
+set estwork(fts3f.test) 9
+set estwork(fts3first.test) 3
+set estwork(fts3matchinfo.test) 4
+set estwork(fts3misc.test) 54
+set estwork(fts3prefix.test) 4
+set estwork(fts3prefix2.test) 8
+set estwork(fts3query.test) 23
+set estwork(fts3varint.test) 2
+set estwork(fts4aa.test) 34
+set estwork(fts4content.test) 5
+set estwork(fts4incr.test) 19
+set estwork(fts4noti.test) 3
+set estwork(fts4opt.test) 129
+set estwork(fts4unicode.test) 45
+set estwork(fts5aa.test) 442
+set estwork(fts5ab.test) 54
+set estwork(fts5ad.test) 33
+set estwork(fts5ae.test) 3
+set estwork(fts5af.test) 13
+set estwork(fts5ag.test) 13
+set estwork(fts5ah.test) 265
+set estwork(fts5al.test) 2
+set estwork(fts5auto.test) 92
+set estwork(fts5connect.test) 5
+set estwork(fts5content.test) 9
+set estwork(fts5contentless.test) 23
+set estwork(fts5contentless2.test) 189
+set estwork(fts5contentless4.test) 234
+set estwork(fts5contentless5.test) 9
+set estwork(fts5delete.test) 62
+set estwork(fts5doclist.test) 11
+set estwork(fts5expr.test) 10
+set estwork(fts5full.test) 42
+set estwork(fts5hash.test) 17
+set estwork(fts5integrity.test) 84
+set estwork(fts5interrupt.test) 16
+set estwork(fts5locale.test) 5
+set estwork(fts5matchinfo.test) 11
+set estwork(fts5merge.test) 29
+set estwork(fts5merge2.test) 9
+set estwork(fts5misc.test) 8
+set estwork(fts5multiclient.test) 2
+set estwork(fts5optimize.test) 13
+set estwork(fts5optimize2.test) 737
+set estwork(fts5optimize3.test) 280
+set estwork(fts5origintext.test) 144
+set estwork(fts5origintext3.test) 8
+set estwork(fts5origintext4.test) 30
+set estwork(fts5origintext5.test) 462
+set estwork(fts5origintext6.test) 55
+set estwork(fts5porter.test) 57
+set estwork(fts5query.test) 8
+set estwork(fts5restart.test) 10
+set estwork(fts5rowid.test) 35
+set estwork(fts5secure.test) 61
+set estwork(fts5secure3.test) 796
+set estwork(fts5secure4.test) 141
+set estwork(fts5secure5.test) 29
+set estwork(fts5secure6.test) 33
+set estwork(fts5secure7.test) 680
+set estwork(fts5simple.test) 7
+set estwork(fts5simple2.test) 2
+set estwork(fts5synonym.test) 13
+set estwork(fts5synonym2.test) 384
+set estwork(fts5tok2.test) 92
+set estwork(fts5tokenizer.test) 2
+set estwork(fts5tokenizer3.test) 8
+set estwork(fts5trigram.test) 2
+set estwork(fts5unicode2.test) 28
+set estwork(fts5unicode3.test) 72
+set estwork(fts5unindexed.test) 7
+set estwork(fts5update.test) 161
+set estwork(fts5update2.test) 11
+set estwork(fts5vocab.test) 11
+set estwork(fts5vocab2.test) 15
+set estwork(func.test) 36
+set estwork(fuzz) 68
+set estwork(fuzz-oss1.test) 11
+set {estwork(fuzzcheck --slice)} 499
+set {estwork(fuzzcheck fuzzdata1.db)} 301
+set {estwork(fuzzcheck fuzzdata2.db)} 275
+set {estwork(fuzzcheck fuzzdata3.db)} 72
+set {estwork(fuzzcheck fuzzdata4.db)} 30
+set {estwork(fuzzcheck fuzzdata5.db)} 299
+set {estwork(fuzzcheck fuzzdata6.db)} 91
+set {estwork(fuzzcheck fuzzdata7.db)} 201
+set {estwork(fuzzcheck fuzzdata8.db)} 331
+set {estwork(fuzzcheck-asan --slice)} 2037
+set {estwork(fuzzcheck-asan fuzzdata1.db)} 3112
+set {estwork(fuzzcheck-asan fuzzdata2.db)} 8753
+set {estwork(fuzzcheck-asan fuzzdata3.db)} 459
+set {estwork(fuzzcheck-asan fuzzdata4.db)} 123
+set {estwork(fuzzcheck-asan fuzzdata5.db)} 1178
+set {estwork(fuzzcheck-asan fuzzdata6.db)} 1356
+set {estwork(fuzzcheck-asan fuzzdata7.db)} 938
+set {estwork(fuzzcheck-asan fuzzdata8.db)} 1451
+set {estwork(fuzzcheck-ubsan --slice)} 1729
+set {estwork(fuzzcheck-ubsan fuzzdata1.db)} 1180
+set {estwork(fuzzcheck-ubsan fuzzdata2.db)} 876
+set {estwork(fuzzcheck-ubsan fuzzdata3.db)} 306
+set {estwork(fuzzcheck-ubsan fuzzdata4.db)} 95
+set {estwork(fuzzcheck-ubsan fuzzdata5.db)} 1356
+set {estwork(fuzzcheck-ubsan fuzzdata6.db)} 333
+set {estwork(fuzzcheck-ubsan fuzzdata7.db)} 883
+set {estwork(fuzzcheck-ubsan fuzzdata8.db)} 1124
+set estwork(fuzzer1.test) 6
+set estwork(gencol1.test) 3
+set estwork(hook.test) 2
+set estwork(in4.test) 4
+set estwork(in7.test) 6
+set estwork(incrblob2.test) 2
+set estwork(incrblob3.test) 5
+set estwork(incrvacuum.test) 10
+set estwork(incrvacuum2.test) 17
+set estwork(incrvacuum3.test) 17
+set estwork(index.test) 3
+set estwork(index2.test) 8
+set estwork(index4.test) 33
+set estwork(index5.test) 99
+set estwork(index6.test) 2
+set estwork(indexexpr1.test) 2
+set estwork(insert3.test) 5
+set estwork(insert4.test) 2
+set estwork(intarray.test) 8
+set estwork(intck1.test) 34
+set estwork(intck2.test) 37
+set estwork(interrupt.test) 28
+set estwork(io.test) 3
+set estwork(join.test) 3
+set estwork(join3.test) 6
+set estwork(join5.test) 20
+set estwork(join7.test) 2
+set estwork(join8.test) 3
+set estwork(join9.test) 2
+set estwork(joinA.test) 11
+set estwork(joinB.test) 7
+set estwork(joinC.test) 6
+set estwork(joinD.test) 168
+set estwork(json103.test) 6
+set estwork(json106.test) 690
+set estwork(keyword1.test) 3
+set estwork(like2.test) 2
+set estwork(like3.test) 2
+set estwork(limit.test) 3
+set estwork(literal.test) 6
+set estwork(lock.test) 39
+set estwork(lock4.test) 2
+set estwork(lock5.test) 4
+set estwork(make) 102
+set estwork(manydb.test) 12
+set estwork(mem5.test) 2
+set estwork(memdb.test) 9
+set estwork(memdb1.test) 2
+set estwork(memjournal2.test) 164
+set estwork(memsubsys1.test) 8
+set estwork(misc1.test) 6
+set estwork(misc2.test) 6
+set estwork(misc5.test) 11
+set estwork(misc8.test) 5
+set estwork(mmap1.test) 8
+set estwork(mmap2.test) 10
+set estwork(mmapwarm.test) 2
+set estwork(multiplex.test) 30
+set estwork(multiplex2.test) 7
+set estwork(nan.test) 2
+set estwork(notify3.test) 5
+set estwork(orderby1.test) 11
+set estwork(orderby2.test) 2
+set estwork(orderby5.test) 11
+set estwork(orderby6.test) 6
+set estwork(orderby8.test) 20
+set estwork(orderbyA.test) 2
+set estwork(oserror.test) 7
+set estwork(ovfl.test) 11
+set estwork(pager1.test) 81
+set estwork(pager2.test) 129
+set estwork(pagesize.test) 3
+set estwork(percentile.test) 86
+set estwork(pragma.test) 4
+set estwork(pragma4.test) 18
+set estwork(printf.test) 21
+set estwork(printf2.test) 5
+set estwork(quota.test) 14
+set estwork(randexpr1.test) 35
+set estwork(rbu10.test) 16
+set estwork(rbu13.test) 5
+set estwork(rbuexlock.test) 5
+set estwork(rbumisc.test) 2
+set estwork(rbutemplimit.test) 2
+set estwork(readonly.test) 95
+set estwork(recover.test) 3
+set estwork(recover1.test) 7
+set estwork(recovercorrupt3.test) 5
+set estwork(recoverold.test) 7
+set estwork(recoverpgsz.test) 6
+set estwork(recoverrowid.test) 3
+set estwork(returning1.test) 6
+set estwork(rollback2.test) 2
+set estwork(round1.test) 261
+set estwork(rowhash.test) 85
+set estwork(rowid.test) 3
+set estwork(rowvalue.test) 2
+set estwork(rowvalue2.test) 38
+set estwork(rowvalue4.test) 2
+set estwork(rowvalueA.test) 2
+set estwork(rowvaluevtab.test) 2
+set estwork(rtree1.test) 4
+set estwork(rtree2.test) 758
+set estwork(rtree6.test) 6
+set estwork(rtree8.test) 15
+set estwork(rtree9.test) 15
+set estwork(rtreeA.test) 7
+set estwork(rtreeB.test) 7
+set estwork(rtreeE.test) 47
+set estwork(rtreeH.test) 24
+set estwork(rtreecheck.test) 2
+set estwork(rtreedoc.test) 8
+set estwork(rtreedoc3.test) 76
+set estwork(savepoint.test) 10
+set estwork(savepoint2.test) 57
+set estwork(schema2.test) 15
+set estwork(schema3.test) 2
+set estwork(schema5.test) 5
+set estwork(select1.test) 2
+set estwork(select2.test) 17
+set estwork(select3.test) 2
+set estwork(selectA.test) 2
+set estwork(selectB.test) 2
+set estwork(selectG.test) 30
+set estwork(session1.test) 5
+set estwork(session2.test) 33
+set estwork(session5.test) 63
+set estwork(session9.test) 3
+set estwork(sessionG.test) 60
+set estwork(sessionH.test) 18
+set estwork(sessionalter.test) 3
+set estwork(sessionat.test) 2
+set estwork(sessionblob.test) 3
+set {estwork(sessionfuzz sessionfuzz-data1.db)} 5
+set estwork(sessioninvert.test) 2
+set estwork(sessionnoop.test) 2
+set estwork(sessionnoop2.test) 8
+set estwork(sessionrebase.test) 8
+set estwork(shared.test) 7
+set estwork(sharedA.test) 48
+set estwork(shell1.test) 36
+set estwork(shell2.test) 11
+set estwork(shell3.test) 3
+set estwork(shell4.test) 2
+set estwork(shell5.test) 16
+set estwork(shell6.test) 3
+set estwork(shell8.test) 104
+set estwork(shell9.test) 3
+set estwork(shellA.test) 2
+set estwork(shmlock.test) 27
+set estwork(sidedelete.test) 10
+set estwork(skipscan1.test) 7
+set estwork(skipscan2.test) 5
+set estwork(sort.test) 38
+set estwork(sort2.test) 540
+set estwork(sort5.test) 16
+set estwork(spellfix.test) 5
+set estwork(spellfix2.test) 5
+set estwork(spellfix4.test) 11
+set estwork(starschema1.test) 2
+set estwork(strict1.test) 5
+set estwork(swarmvtab.test) 110
+set estwork(swarmvtab3.test) 14
+set estwork(syscall.test) 4
+set estwork(table.test) 62
+set estwork(tableapi.test) 8
+set estwork(tcl) 1
+set estwork(tclsqlite.test) 29
+set estwork(temptable2.test) 274
+set estwork(thread3.test) 21
+set estwork(timediff1.test) 5
+set estwork(tkt-2d1a5c67d.test) 6
+set estwork(tkt-38cb5df375.test) 2
+set estwork(tkt-4dd95f6943.test) 2
+set estwork(tkt-5e10420e8d.test) 5
+set estwork(tkt-6bfb98dfc0.test) 5
+set estwork(tkt-80e031a00f.test) 25
+set estwork(tkt-9d68c883.test) 3
+set estwork(tkt-9f2eb3abac.test) 5
+set estwork(tkt-b1d3a2e531.test) 5
+set estwork(tkt-b72787b1.test) 5
+set estwork(tkt-b75a9ca6b0.test) 8
+set estwork(tkt-d11f09d36e.test) 9
+set estwork(tkt-fc62af4523.test) 10
+set estwork(tkt1435.test) 7
+set estwork(tkt1644.test) 5
+set estwork(tkt1667.test) 20
+set estwork(tkt1873.test) 2
+set estwork(tkt2192.test) 3
+set estwork(tkt2285.test) 19
+set estwork(tkt2332.test) 2
+set estwork(tkt2409.test) 24
+set estwork(tkt3334.test) 5
+set estwork(tkt3357.test) 10
+set estwork(tkt3630.test) 2
+set estwork(tkt3832.test) 8
+set estwork(tkt3838.test) 5
+set estwork(tkt3918.test) 3
+set estwork(tkt4018.test) 48
+set estwork(tokenize.test) 9
+set estwork(tpch01.test) 6
+set estwork(trans.test) 258
+set estwork(trigger2.test) 15
+set estwork(trigger5.test) 2
+set estwork(trigger8.test) 30
+set estwork(triggerA.test) 36
+set estwork(triggerB.test) 4
+set estwork(triggerC.test) 67
+set estwork(triggerD.test) 3
+set estwork(types.test) 2
+set estwork(types2.test) 2
+set estwork(unionall2.test) 9
+set estwork(unionvtab.test) 2
+set estwork(update.test) 7
+set estwork(upsert3.test) 6
+set estwork(upsert5.test) 5
+set estwork(vacuum.test) 2
+set estwork(vacuum5.test) 3
+set estwork(vacuum6.test) 174
+set estwork(vacuummem.test) 132
+set estwork(varint.test) 8
+set estwork(view.test) 2
+set estwork(vtab1.test) 13
+set estwork(vtab6.test) 12
+set estwork(vtabC.test) 33
+set estwork(vtabD.test) 56
+set estwork(vtab_alter.test) 6
+set estwork(wal.test) 38
+set estwork(wal2.test) 7
+set estwork(wal3.test) 196
+set estwork(wal4.test) 70
+set estwork(wal5.test) 22
+set estwork(wal64k.test) 20
+set estwork(wal7.test) 3
+set estwork(wal9.test) 24
+set estwork(walbak.test) 2
+set estwork(walcksum.test) 3
+set estwork(walcrash4.test) 59
+set estwork(waloverwrite.test) 3
+set estwork(walpersist.test) 6
+set estwork(walprotocol2.test) 2
+set estwork(walro2.test) 11
+set estwork(walsetlk.test) 1610
+set estwork(walsetlk3.test) 3
+set estwork(walsetlk_recover.test) 193
+set estwork(walshared.test) 5
+set estwork(walvfs.test) 476
+set estwork(where.test) 24
+set estwork(where3.test) 4
+set estwork(where6.test) 2
+set estwork(where7.test) 20
+set estwork(where8.test) 95
+set estwork(where9.test) 16
+set estwork(whereB.test) 5
+set estwork(whereE.test) 6
+set estwork(whereN.test) 2
+set estwork(window1.test) 5
+set estwork(window2.test) 8
+set estwork(window3.test) 89
+set estwork(window4.test) 3
+set estwork(window5.test) 2
+set estwork(window8.test) 33
+set estwork(windowA.test) 5
+set estwork(windowD.test) 6
+set estwork(with1.test) 114
+set estwork(with2.test) 4
+set estwork(with5.test) 2
+set estwork(withM.test) 4
+set estwork(without_rowid1.test) 2
+set estwork(without_rowid3.test) 9
+set estwork(without_rowid4.test) 6
diff --git a/test/vtabH.test b/test/vtabH.test
index 07704cefb..1496f49b5 100644
--- a/test/vtabH.test
+++ b/test/vtabH.test
@@ -190,10 +190,10 @@ if {$tcl_platform(platform) ne "windows" || \
lappend res "/$p"
}
}
- set num_root_files [llength $root_files]
+ set num_root_files [llength $res]
do_test 3.1 {
sort_files [execsql {
- SELECT path FROM fstree WHERE path NOT GLOB '*\$*' LIMIT $num_root_files;
+ SELECT path FROM fstree WHERE path NOT GLOB '*$*' LIMIT $num_root_files
}] true
} [sort_files $res true]
diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh
index b750593c9..2d7ea84e5 100644
--- a/tool/mkautoconfamal.sh
+++ b/tool/mkautoconfamal.sh
@@ -62,10 +62,6 @@ cp $TOP/main.mk $TMPSPACE
cd $TMPSPACE
-# Clean up emacs-generated backup files from the target
-rm -f ./autosetup/*~ ./autosetup/teaish/*~
-rm -f ./*~
-
#if true; then
# Clean up *~ files (emacs-generated backups).
# This bit is only for use during development of
@@ -83,8 +79,12 @@ cat <<EOF > tea/generic/tclsqlite3.c
EOF
cat $TOP/src/tclsqlite.c >> tea/generic/tclsqlite3.c
-find . -type f -name '*~' -exec rm -f \{} \;
-find . -type f -name '#*#' -exec rm -f \{} \;
+# Clean up some local remnants from the tarball.
+rm -f tea/.env-* # autosetup environment overrides
+find . -type f -name '*~' -exec rm -f \{} \; # backup files
+find . -type f -name '#*#' -exec rm -f \{} \; # emacs lock files
+find . -type f -name '*.o' -exec rm -f \{} \;
+find . -type f -name '*.so' -exec rm -f \{} \;
./configure && make dist
tar xzf sqlite-$VERSION.tar.gz
diff --git a/tool/mktoolzip.tcl b/tool/mktoolzip.tcl
index c22318441..041bf28cd 100644
--- a/tool/mktoolzip.tcl
+++ b/tool/mktoolzip.tcl
@@ -12,9 +12,27 @@
# sqlite3_analyzer -- Space analyzer
# sqlite3_rsync -- Remote db sync
#
+# On Windows, add:
+#
+# sqlite3.def
+# sqlite3.dll
+#
+# Add the --snapshot option to generate a snapshot ZIP archive instead of
+# a release ZIP archive.
+#
+set bSnapshot 0
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ set a [lindex $argv $i]
+ if {$a eq "-snapshot" || $a eq "--snapshot"} {
+ set bSnapshot 1
+ continue
+ }
+ puts stderr "unknown argument: $a"
+ exit 1
+}
switch $tcl_platform(os) {
{Windows NT} {
- set OS win32
+ set OS win
set EXE .exe
}
Linux {
@@ -49,13 +67,39 @@ switch $tcl_platform(machine) {
set ARCH unk
}
}
-set in [open [file join [file dirname [file dirname [info script]]] VERSION]]
-set vers [read $in]
-close $in
-scan $vers %d.%d.%d v1 v2 v3
-set v2 [format 3%02d%02d00 $v2 $v3]
+if {$bSnapshot} {
+ set in [open [file join [file dirname [file dirname [info script]]] manifest]]
+ set manifest [read $in]
+ close $in
+ regexp {\nD (.{16})} $manifest all date
+ regsub -all {[-:T]} $date {} v2
+} else {
+ set in [open [file join [file dirname [file dirname [info script]]] VERSION]]
+ set vers [read $in]
+ close $in
+ scan $vers %d.%d.%d v1 v2 v3
+ set v2 [format 3%02d%02d00 $v2 $v3]
+}
+
set name sqlite-tools-$OS-$ARCH-$v2.zip
-set toollist "sqlite3$EXE sqldiff$EXE sqlite3_analyzer$EXE sqlite3_rsync$EXE"
-puts "zip $name {*}$toollist"
-exec zip $name {*}$toollist
-puts "$name: [file size $name] bytes"
+set filelist "sqlite3$EXE sqldiff$EXE sqlite3_analyzer$EXE sqlite3_rsync$EXE"
+proc make_zip_archive {name filelist} {
+ file delete -force $name
+ puts "fossil test-filezip $name $filelist"
+ if {[catch {exec fossil test-filezip $name {*}$filelist}]} {
+ puts "^--- Unable. Trying again as:"
+ puts "zip $name $filelist"
+ file delete -force $name
+ exec zip $name {*}$filelist
+ }
+ puts "$name: [file size $name] bytes"
+}
+make_zip_archive $name $filelist
+
+# On Windows, also try to construct a DLL
+#
+if {$OS eq "win" && [file exists sqlite3.dll] && [file exists sqlite3.def]} {
+ set name sqlite-dll-win-$ARCH-$v2.zip
+ set filelist [list sqlite3.def sqlite3.dll]
+ make_zip_archive $name $filelist
+}