aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in97
-rw-r--r--Makefile.msc37
-rw-r--r--README.md16
-rw-r--r--autoconf/Makefile.msc9
-rw-r--r--ext/fts5/fts5_index.c2
-rw-r--r--ext/fts5/fts5_main.c36
-rw-r--r--ext/fts5/fts5_tcl.c111
-rw-r--r--ext/fts5/test/fts5corrupt3.test14
-rw-r--r--ext/fts5/test/fts5corrupt8.test94
-rw-r--r--ext/fts5/test/fts5fault4.test2
-rw-r--r--ext/fts5/test/fts5faultI.test21
-rw-r--r--ext/fts5/test/fts5integrity2.test56
-rw-r--r--ext/fts5/test/fts5misc.test8
-rw-r--r--ext/fts5/test/fts5simple.test2
-rw-r--r--ext/misc/percentile.c390
-rw-r--r--main.mk87
-rw-r--r--manifest81
-rw-r--r--manifest.uuid2
-rw-r--r--src/btree.c2
-rw-r--r--src/ctime.c3
-rw-r--r--src/func.c8
-rw-r--r--src/main.c3
-rw-r--r--src/parse.y72
-rw-r--r--src/sqlite.h.in10
-rw-r--r--src/sqliteInt.h2
-rw-r--r--src/test_config.c16
-rw-r--r--src/vdbeblob.c5
-rw-r--r--test/fuzzcheck.c6
-rw-r--r--test/percentile.test486
-rw-r--r--test/shell2.test4
-rw-r--r--test/shell5.test8
-rw-r--r--test/testrunner.tcl153
-rw-r--r--test/testrunner_data.tcl8
-rw-r--r--test/windowE.test32
-rwxr-xr-xtool/mkctimec.tcl2
-rw-r--r--tool/mkkeywordhash.c6
-rw-r--r--tool/omittest.tcl544
37 files changed, 1720 insertions, 715 deletions
diff --git a/Makefile.in b/Makefile.in
index b8149f846..237714641 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -83,9 +83,13 @@ TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@
# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
# The same set of OMIT and ENABLE flags should be passed to the
# LEMON parser generator and the mkkeywordhash tool as well.
-OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
+#
+# Add OPTIONS=... on the command line to append additional options
+# to the OPT_FEATURE_FLAGS.
+#
+OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ $(OPTIONS)
-TCC += $(OPT_FEATURE_FLAGS)
+TCC += $(OPT_FEATURE_FLAGS)
# Add in any optional parameters specified on the make commane line
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
@@ -646,6 +650,7 @@ FUZZCHECK_SRC += $(TOP)/test/fuzzinvariants.c
FUZZCHECK_SRC += $(TOP)/ext/recover/dbdata.c
FUZZCHECK_SRC += $(TOP)/ext/recover/sqlite3recover.c
FUZZCHECK_SRC += $(TOP)/test/vt02.c
+FUZZCHECK_SRC += $(TOP)/ext/misc/percentile.c
FUZZCHECK_SRC += $(TOP)/ext/misc/randomjson.c
DBFUZZ_OPT =
ST_OPT = -DSQLITE_OS_KV_OPTIONAL
@@ -1178,6 +1183,7 @@ SHELL_DEP = \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/memtrace.c \
$(TOP)/ext/misc/pcachetrace.c \
+ $(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/shathree.c \
@@ -1363,9 +1369,14 @@ tcltest: ./testfixture$(TEXE)
testrunner: testfixture$(TEXE)
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl
-# Runs both fuzztest and testrunner, consecutively.
+# This is the testing target preferred by the core SQLite developers.
+# It runs tests under a standard configuration, regardless of how
+# ./configure was run. The devs run "make devtest" prior to each
+# check-in, at a minimum. Probably other tests too, but at least this
+# one.
#
-devtest: srctree-check testfixture$(TEXE) fuzztest testrunner
+devtest: srctree-check sourcetest
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)
mdevtest: srctree-check has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)
@@ -1381,16 +1392,19 @@ srctree-check: $(TOP)/tool/srctree-check.tcl
# Testing for a release
#
-releasetest: srctree-check testfixture$(TEXE)
- ./testfixture$(TEXE) $(TOP)/test/testrunner.tcl release $(TSTRNNR_OPTS)
+releasetest: srctree-check has_tclsh85 verify-source
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl release $(TSTRNNR_OPTS)
# Minimal testing that runs in less than 3 minutes
#
quicktest: ./testfixture$(TEXE)
./testfixture$(TEXE) $(TOP)/test/extraquick.test $(TESTOPTS)
-# This is the common case. Run many tests that do not take too long,
-# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
+# Try to run tests on whatever options are specified by the
+# ./configure. The developers seldom use this target. Instead
+# they use "make devtest" which runs tests on a standard set of
+# options regardless of how SQLite is configured. This "test"
+# target is provided for legacy only.
#
test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
@@ -1407,8 +1421,8 @@ valgrindtest: $(TESTPROGS) valgrindfuzz
smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
./testfixture$(TEXE) $(TOP)/test/main.test $(TESTOPTS)
-shelltest: $(TESTPROGS)
- ./testfixture$(TEXT) $(TOP)/test/permutations.test shell
+shelltest:
+ $(TCLSH_CMD) $(TOP)/test/testrunner.tcl release shell
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
@@ -1594,43 +1608,40 @@ tclextension-uninstall:
tclextension-list:
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info
-clean:
- rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la
- rm -f sqlite3.h opcodes.*
- rm -rf .libs .deps
- rm -f lemon$(BEXE) lempar.c parse.* sqlite*.tar.gz
- rm -f mkkeywordhash$(BEXE) keywordhash.h
- rm -f mksourceid$(BEXE)
- rm -f *.da *.bb *.bbg gmon.out
- rm -rf tsrc .target_source
- rm -f tclsqlite3$(TEXE)
- rm -f testfixture$(TEXE) test.db
+
+# Remove build products sufficient so that subsequent makes will recompile
+# everything from scratch. Do not remove:
+#
+# * test results and test logs
+# * output from ./configure
+#
+tidy:
+ rm -f *.lo *.la *.o *.c *.da *.bb *.bbg gmon.* *.rws sqlite3$(TEXE)
+ rm -f fts5.h keywordhash.h opcodes.h sqlite3.h sqlite3ext.h sqlite3session.h
+ rm -rf .libs .deps tsrc .target_source
+ rm -f lemon$(BEXE) sqlite*.tar.gz
+ rm -f mkkeywordhash$(BEXE) mksourceid$(BEXE)
+ rm -f parse.* fts5parse.*
+ rm -f tclsqlite3$(TEXE) $(TESTPROGS)
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
- rm -f wordcount$(TEXE) changeset$(TEXE)
- rm -f version-info$(TEXT)
- rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
- rm -f sqlite3.c
- rm -f sqlite3rc.h
- rm -f shell.c sqlite3ext.h
- rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c
- rm -f sqlite-*-output.vsix
- rm -f mptester mptester.exe
- rm -f rbu rbu.exe
- rm -f srcck1 srcck1.exe
- rm -f fuzzershell fuzzershell.exe
- rm -f fuzzcheck fuzzcheck.exe
- rm -f sqldiff sqldiff.exe
- rm -f dbhash dbhash.exe
- rm -f fts5.* fts5parse.*
- rm -f threadtest5
- rm -f src-verify
- rm -f custom.rws
- rm -f has_tclsh84 has_tclsh85
+ rm -f wordcount$(TEXE) changeset$(TEXE) version-info$(TEXE)
+ rm -f *.dll *.lib *.exp *.def *.pc *.vsix
+ rm -f sqlite3_analyzer$(TEXE)
+ rm -f mptester$(TEXE) rbu$(TEXE) srcck1$(TEXE)
+ rm -f fuzzershell$(TEXE) fuzzcheck$(TEXE) sqldiff$(TEXE) dbhash$(TEXE)
+ rm -f threadtest5$(TEXE)
+ rm -f src-verify has_tclsh*
+
+# Removes build products and test logs. Retains ./configure outputs.
+#
+clean: tidy
+ rm -rf omittest* testrunner* testdir*
+# Clean up everything. No exceptions.
+#
distclean: clean
- rm -f sqlite_cfg.h config.log config.status libtool Makefile sqlite3.pc \
- $(TESTPROGS)
+ rm -f sqlite_cfg.h config.log config.status Makefile $(LIBTOOL)
#
# Windows section
diff --git a/Makefile.msc b/Makefile.msc
index e31aee089..fde1f31a9 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -379,6 +379,7 @@ SQLITE_TCL_DEP =
# the Windows platform.
#
!IFNDEF OPT_FEATURE_FLAGS
+OPT_FEATURE_FLAGS = $(OPT_XTRA)
!IF $(MINIMAL_AMALGAMATION)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
@@ -392,6 +393,14 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
!ENDIF
+# Additional feature-options above and beyond what are normally used can be
+# be added using OPTIONS=.... on the command-line. These values are
+# appended to the OPT_FEATURE_FLAGS variable.
+#
+!IFDEF OPTIONS
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) $(OPTIONS)
+!ENDIF
+
# Should the session extension be enabled? If so, add compilation options
# to enable it.
#
@@ -1758,6 +1767,7 @@ FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c
+FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\percentile.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
@@ -2304,6 +2314,7 @@ SHELL_DEP = \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\memtrace.c \
$(TOP)\ext\misc\pcachetrace.c \
+ $(TOP)\ext\misc\percentile.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\shathree.c \
@@ -2535,6 +2546,13 @@ queryplantest: testfixture.exe shell
fuzztest: fuzzcheck.exe
.\fuzzcheck.exe $(FUZZDATA)
+# Legacy testing target for third-party integrators. The SQLite
+# developers seldom use this target themselves. Instead
+# they use "nmake /f Makefile.msc devtest" which runs tests on
+# a standard set of options
+#
+test: $(TESTPROGS) sourcetest fuzztest tcltest
+
# Minimal testing that runs in less than 3 minutes (on a fast machine)
#
quicktest: testfixture.exe sourcetest
@@ -2544,7 +2562,6 @@ quicktest: testfixture.exe sourcetest
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
-test: $(TESTPROGS) sourcetest fuzztest tcltest
# The veryquick.test TCL tests.
#
@@ -2558,17 +2575,27 @@ tcltest: testfixture.exe
testrunner: testfixture.exe
.\testfixture.exe $(TOP)\test\testrunner.tcl
-# Runs both fuzztest and testrunner, consecutively.
+# This is the testing target preferred by the core SQLite developers.
+# It runs tests under a standard configuration. The devs run
+# "nmake /f Makefile.msc devtest" prior to each check-in, at a minimum.
+# Probably other tests too, but at least this one.
#
-devtest: testfixture.exe fuzztest testrunner
+devtest: srctree-check sourcetest
+ $(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
mdevtest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
+# Validate that various generated files in the source tree
+# are up-to-date.
+#
+srctree-check: $(TOP)\tool\srctree-check.tcl
+ $(TCLSH_CMD) $(TOP)\tool\srctree-check.tcl
+
# Testing for a release
#
-releasetest: testfixture.exe
- testfixture.exe $(TOP)\test\testrunner.tcl release
+releasetest:
+ $(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
smoketest: $(TESTPROGS)
diff --git a/README.md b/README.md
index 4cddb4775..c208f11ea 100644
--- a/README.md
+++ b/README.md
@@ -109,7 +109,7 @@ For example:
../sqlite/configure ;# Run the configure script
make sqlite3 ;# Builds the "sqlite3" command-line tool
make sqlite3.c ;# Build the "amalgamation" source file
- make mdevtest ;# Run development tests (requires tcl-dev)
+ make devtest ;# Run development tests (requires tcl-dev)
make releasetest ;# Run full release tests (requires tcl-dev)
make sqldiff ;# Builds the "sqldiff" command-line tool
make sqlite3_analyzer ;# Builds the "sqlite3_analyzer" tool (requires tcl-dev)
@@ -128,6 +128,13 @@ Almost all makefile targets require a "tclsh" TCL interpreter
version 8.6 or later. The targets marked with "(requires tcl-dev)" also require
the TCL development libraries.
+On "make" command-lines, one can add "OPTIONS=..." to specify additional
+compile-time options over and above those set by ./configure. For example,
+to compile with the SQLITE_OMIT_DEPRECATED compile-time option, one could say:
+
+ ./configure --enable-all
+ make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
+
The configure script uses autoconf 2.61 and libtool. If the configure
script does not work out for you, there is a generic makefile named
"Makefile.linux-gcc" in the top directory of the source tree that you
@@ -156,13 +163,18 @@ Build using Makefile.msc. Example:
nmake /f Makefile.msc sqlite3.exe
nmake /f Makefile.msc sqlite3.c
- nmake /f Makefile.msc mdevtest
+ nmake /f Makefile.msc devtest
nmake /f Makefile.msc releasetest
nmake /f Makefile.msc tclextension-install
There are many other makefile targets. See comments in Makefile.msc for
details.
+As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
+command-line to enable new compile-time options. For example:
+
+ nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
+
## Source Tree Map
* **src/** - This directory contains the primary source code for the
diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc
index a4270fb2a..f8a65e90c 100644
--- a/autoconf/Makefile.msc
+++ b/autoconf/Makefile.msc
@@ -301,6 +301,7 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
# the Windows platform.
#
!IFNDEF OPT_FEATURE_FLAGS
+OPT_FEATURE_FLAGS = $(OPT_XTRA)
!IF $(MINIMAL_AMALGAMATION)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
@@ -314,6 +315,14 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
!ENDIF
+# Additional feature-options above and beyond what are normally used can be
+# be added using OPTIONS=.... on the command-line. These values are
+# appended to the OPT_FEATURE_FLAGS variable.
+#
+!IFDEF OPTIONS
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) $(OPTIONS)
+!ENDIF
+
# Should the session extension be enabled? If so, add compilation options
# to enable it.
#
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
index 1f0a68d3e..4363305a5 100644
--- a/ext/fts5/fts5_index.c
+++ b/ext/fts5/fts5_index.c
@@ -2185,7 +2185,7 @@ static void fts5SegIterNext_None(
if( iOff<pIter->iEndofDoclist ){
/* Next entry is on the current page */
- i64 iDelta;
+ u64 iDelta;
iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta);
pIter->iLeafOffset = iOff;
pIter->iRowid += iDelta;
diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c
index 4addc0729..03c1bb83f 100644
--- a/ext/fts5/fts5_main.c
+++ b/ext/fts5/fts5_main.c
@@ -427,8 +427,7 @@ static int fts5InitVtab(
/* Load the initial configuration */
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
- sqlite3Fts5IndexRollback(pTab->p.pIndex);
+ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1);
}
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
@@ -2068,9 +2067,11 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){
** Implementation of xBegin() method.
*/
static int fts5BeginMethod(sqlite3_vtab *pVtab){
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0);
- fts5NewTransaction((Fts5FullTable*)pVtab);
- return SQLITE_OK;
+ int rc = fts5NewTransaction((Fts5FullTable*)pVtab);
+ if( rc==SQLITE_OK ){
+ fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0);
+ }
+ return rc;
}
/*
@@ -3267,7 +3268,9 @@ static int fts5NewTokenizerModule(
*/
typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer;
struct Fts5VtoVTokenizer {
- Fts5TokenizerModule *pMod;
+ int bV2Native; /* True if v2 native tokenizer */
+ fts5_tokenizer x1; /* Tokenizer functions */
+ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */
Fts5Tokenizer *pReal;
};
@@ -3287,7 +3290,9 @@ static int fts5VtoVCreate(
pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew));
if( rc==SQLITE_OK ){
- pNew->pMod = pMod;
+ pNew->x1 = pMod->x1;
+ pNew->x2 = pMod->x2;
+ pNew->bV2Native = pMod->bV2Native;
if( pMod->bV2Native ){
rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal);
}else{
@@ -3309,11 +3314,10 @@ static int fts5VtoVCreate(
static void fts5VtoVDelete(Fts5Tokenizer *pTok){
Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok;
if( p ){
- Fts5TokenizerModule *pMod = p->pMod;
- if( pMod->bV2Native ){
- pMod->x2.xDelete(p->pReal);
+ if( p->bV2Native ){
+ p->x2.xDelete(p->pReal);
}else{
- pMod->x1.xDelete(p->pReal);
+ p->x1.xDelete(p->pReal);
}
sqlite3_free(p);
}
@@ -3331,9 +3335,8 @@ static int fts5V1toV2Tokenize(
int (*xToken)(void*, int, const char*, int, int, int)
){
Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok;
- Fts5TokenizerModule *pMod = p->pMod;
- assert( pMod->bV2Native );
- return pMod->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken);
+ assert( p->bV2Native );
+ return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken);
}
/*
@@ -3348,10 +3351,9 @@ static int fts5V2toV1Tokenize(
int (*xToken)(void*, int, const char*, int, int, int)
){
Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok;
- Fts5TokenizerModule *pMod = p->pMod;
- assert( pMod->bV2Native==0 );
+ assert( p->bV2Native==0 );
UNUSED_PARAM2(pLocale,nLocale);
- return pMod->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken);
+ return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken);
}
/*
diff --git a/ext/fts5/fts5_tcl.c b/ext/fts5/fts5_tcl.c
index 1e9f7bbb6..a8ab44096 100644
--- a/ext/fts5/fts5_tcl.c
+++ b/ext/fts5/fts5_tcl.c
@@ -96,14 +96,14 @@ static int SQLITE_TCLAPI f5tDbAndApi(
rc = sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
return TCL_ERROR;
}
sqlite3_bind_pointer(pStmt, 1, (void*)&pApi, "fts5_api_ptr", 0);
sqlite3_step(pStmt);
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
- Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
return TCL_ERROR;
}
@@ -392,7 +392,7 @@ static int SQLITE_TCLAPI xF5tApi(
CASE(12, "xSetAuxdata") {
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
if( pData==0 ){
- Tcl_AppendResult(interp, "out of memory", 0);
+ Tcl_AppendResult(interp, "out of memory", (char*)0);
return TCL_ERROR;
}
pData->pObj = objv[2];
@@ -452,7 +452,7 @@ static int SQLITE_TCLAPI xF5tApi(
rc = p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
+ Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
return TCL_ERROR;
}
for( ;iCol>=0; p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff) ){
@@ -683,7 +683,7 @@ static int SQLITE_TCLAPI f5tCreateFunction(
pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy
);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
return TCL_ERROR;
}
@@ -750,7 +750,7 @@ static int SQLITE_TCLAPI f5tTokenize(
if( objc==5 ){
char *zOpt = Tcl_GetString(objv[1]);
if( strcmp("-subst", zOpt) ){
- Tcl_AppendResult(interp, "unrecognized option: ", zOpt, 0);
+ Tcl_AppendResult(interp, "unrecognized option: ", zOpt, (char*)0);
return TCL_ERROR;
}
}
@@ -759,7 +759,7 @@ static int SQLITE_TCLAPI f5tTokenize(
return TCL_ERROR;
}
if( nArg==0 ){
- Tcl_AppendResult(interp, "no such tokenizer: ", 0);
+ Tcl_AppendResult(interp, "no such tokenizer: ", (char*)0);
Tcl_Free((void*)azArg);
return TCL_ERROR;
}
@@ -767,13 +767,13 @@ static int SQLITE_TCLAPI f5tTokenize(
rc = pApi->xFindTokenizer(pApi, azArg[0], &pUserdata, &tokenizer);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], 0);
+ Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], (char*)0);
return TCL_ERROR;
}
rc = tokenizer.xCreate(pUserdata, &azArg[1], (int)(nArg-1), &pTok);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, "error in tokenizer.xCreate()", 0);
+ Tcl_AppendResult(interp, "error in tokenizer.xCreate()", (char*)0);
return TCL_ERROR;
}
@@ -787,7 +787,7 @@ static int SQLITE_TCLAPI f5tTokenize(
);
tokenizer.xDelete(pTok);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", 0);
+ Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", (char*)0);
Tcl_DecrRefCount(pRet);
return TCL_ERROR;
}
@@ -1049,7 +1049,7 @@ static int SQLITE_TCLAPI f5tTokenizerLocale(
if( p->xToken==0 ){
Tcl_AppendResult(interp,
- "sqlite3_fts5_locale may only be used by tokenizer callback", 0
+ "sqlite3_fts5_locale may only be used by tokenizer callback", (char*)0
);
return TCL_ERROR;
}
@@ -1098,7 +1098,7 @@ static int SQLITE_TCLAPI f5tTokenizerReturn(
if( p->xToken==0 ){
Tcl_AppendResult(interp,
- "sqlite3_fts5_token may only be used by tokenizer callback", 0
+ "sqlite3_fts5_token may only be used by tokenizer callback", (char*)0
);
return TCL_ERROR;
}
@@ -1250,7 +1250,7 @@ static int SQLITE_TCLAPI f5tCreateTokenizer(
Tcl_AppendResult(interp, (
bV2 ? "error in fts5_api.xCreateTokenizer_v2()"
: "error in fts5_api.xCreateTokenizer()"
- ), 0);
+ ), (char*)0);
return TCL_ERROR;
}
@@ -1540,13 +1540,93 @@ static int SQLITE_TCLAPI f5tRegisterOriginText(
Tcl_ResetResult(interp);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
+ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
+** This function is used to DROP an fts5 table. It works even if the data
+** structures fts5 stores within the database are corrupt, which sometimes
+** prevents a straight "DROP TABLE" command from succeeding.
+**
+** The first parameter is the database handle to use for the DROP TABLE
+** operation. The second is the name of the database to drop the fts5 table
+** from (i.e. "main", "temp" or the name of an attached database). The
+** third parameter is the name of the fts5 table to drop.
+**
+** SQLITE_OK is returned if the table is successfully dropped. Or, if an
+** error occurs, an SQLite error code.
+*/
+static int sqlite3_fts5_drop_corrupt_table(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Database name ("main", "temp" etc.) */
+ const char *zTab /* Name of fts5 table to drop */
+){
+ int rc = SQLITE_OK;
+ int bDef = 0;
+
+ rc = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDef);
+ if( rc==SQLITE_OK ){
+ char *zScript = sqlite3_mprintf(
+ "DELETE FROM %Q.'%q_data';"
+ "DELETE FROM %Q.'%q_config';"
+ "INSERT INTO %Q.'%q_data' VALUES(10, X'0000000000');"
+ "INSERT INTO %Q.'%q_config' VALUES('version', 4);"
+ "DROP TABLE %Q.'%q';",
+ zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab
+ );
+
+ if( zScript==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
+ rc = sqlite3_exec(db, zScript, 0, 0, 0);
+ if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
+ sqlite3_free(zScript);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** sqlite3_fts5_drop_corrupt_table DB DATABASE TABLE
+**
+** Description...
+*/
+static int SQLITE_TCLAPI f5tDropCorruptTable(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3 *db = 0;
+ const char *zDb = 0;
+ const char *zTab = 0;
+ int rc = SQLITE_OK;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB DATABASE TABLE");
+ return TCL_ERROR;
+ }
+ if( f5tDbPointer(interp, objv[1], &db) ){
+ return TCL_ERROR;
+ }
+ zDb = Tcl_GetString(objv[2]);
+ zTab = Tcl_GetString(objv[3]);
+
+ rc = sqlite3_fts5_drop_corrupt_table(db, zDb, zTab);
+ if( rc!=SQLITE_OK ){
+ Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
** Entry point.
*/
int Fts5tcl_Init(Tcl_Interp *interp){
@@ -1564,7 +1644,8 @@ int Fts5tcl_Init(Tcl_Interp *interp){
{ "sqlite3_fts5_token_hash", f5tTokenHash, 0 },
{ "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 },
{ "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 },
- { "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 }
+ { "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 },
+ { "sqlite3_fts5_drop_corrupt_table", f5tDropCorruptTable, 0 }
};
int i;
F5tTokenizerContext *pContext;
diff --git a/ext/fts5/test/fts5corrupt3.test b/ext/fts5/test/fts5corrupt3.test
index c5faaa87b..3e8b0377c 100644
--- a/ext/fts5/test/fts5corrupt3.test
+++ b/ext/fts5/test/fts5corrupt3.test
@@ -680,11 +680,11 @@ do_test 12.0 {
do_catchsql_test 11.1 {
SELECT * FROM t1 WHERE t1 MATCH 'abandon';
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
do_catchsql_test 11.2 {
INSERT INTO t1(t1, rank) VALUES('merge', 500);
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
#
@@ -1040,7 +1040,7 @@ do_test 16.0 {
do_catchsql_test 16.1 {
INSERT INTO t1(t1) VALUES('integrity-check');
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
#--------------------------------------------------------------------------
reset_db
@@ -1126,7 +1126,7 @@ do_test 17.0 {
do_catchsql_test 17.1 {
SELECT * FROM t1 WHERE t1 MATCH 'abandon';
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
#--------------------------------------------------------------------------
reset_db
@@ -1630,7 +1630,7 @@ do_test 20.0 {
do_catchsql_test 20.1 {
SELECT * FROM t1 WHERE t1 MATCH 'abandon';
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
reset_db
@@ -2100,7 +2100,7 @@ do_test 22.0 {
do_catchsql_test 22.1 {
INSERT INTO t1(t1) VALUES('optimize');
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
#--------------------------------------------------------------------------
reset_db
@@ -3700,7 +3700,7 @@ do_catchsql_test 32.1 {
highlight(t1, 2, '[', ']')
FROM t1('g + h')
WHERE rank MATCH 'bm25(1.0, 1.0)' ORDER BY rank;
-} {1 {vtable constructor failed: t1}}
+} {1 {database disk image is malformed}}
do_catchsql_test 32.2 {
SELECT * FROM t3;
diff --git a/ext/fts5/test/fts5corrupt8.test b/ext/fts5/test/fts5corrupt8.test
new file mode 100644
index 000000000..d642920e4
--- /dev/null
+++ b/ext/fts5/test/fts5corrupt8.test
@@ -0,0 +1,94 @@
+# 2024 Aug 28
+#
+# 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.
+#
+#***********************************************************************
+#
+
+source [file join [file dirname [info script]] fts5_common.tcl]
+set testprefix fts5corrupt8
+
+# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(x);
+}
+
+do_execsql_test 1.1 {
+ UPDATE t1_data SET block='hello world' WHERE id=10
+}
+
+db close
+sqlite3 db test.db
+
+do_catchsql_test 1.2 {
+ SELECT * FROM t1
+} {1 {database disk image is malformed}}
+do_catchsql_test 1.3 {
+ DROP TABLE t1
+} {0 {}}
+do_execsql_test 1.4 {
+ SELECT * FROM sqlite_schema
+}
+
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(x);
+}
+do_execsql_test 2.1 {
+ UPDATE t1_config SET v=555 WHERE k='version'
+}
+db close
+sqlite3 db test.db
+do_catchsql_test 2.2 {
+ SELECT * FROM t1
+} {1 {invalid fts5 file format (found 555, expected 4 or 5) - run 'rebuild'}}
+do_catchsql_test 2.3 {
+ DROP TABLE t1
+} {1 {invalid fts5 file format (found 555, expected 4 or 5) - run 'rebuild'}}
+do_test 2.4 {
+ sqlite3_fts5_drop_corrupt_table db main t1
+} {}
+do_execsql_test 2.5 {
+ SELECT * FROM sqlite_schema
+}
+
+do_execsql_test 3.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(x);
+}
+do_execsql_test 3.1 {
+ DELETE FROM t1_config;
+}
+db close
+sqlite3 db test.db
+do_catchsql_test 3.2 {
+ SELECT * FROM t1
+} {1 {invalid fts5 file format (found 0, expected 4 or 5) - run 'rebuild'}}
+do_catchsql_test 3.3 {
+ DROP TABLE t1
+} {1 {invalid fts5 file format (found 0, expected 4 or 5) - run 'rebuild'}}
+
+
+do_test 3.4 {
+ sqlite3_db_config db DEFENSIVE 1
+} {1}
+do_test 3.5 {
+ sqlite3_fts5_drop_corrupt_table db main t1
+} {}
+do_test 3.6 {
+ sqlite3_db_config db DEFENSIVE -1
+} {1}
+do_execsql_test 3.7 {
+ SELECT * FROM sqlite_schema
+}
+
+finish_test
+
diff --git a/ext/fts5/test/fts5fault4.test b/ext/fts5/test/fts5fault4.test
index 1d0d5c9b7..2b4f6c4d2 100644
--- a/ext/fts5/test/fts5fault4.test
+++ b/ext/fts5/test/fts5fault4.test
@@ -90,7 +90,7 @@ set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
do_faultsim_test 4 -faults oom-* -body {
db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
} -test {
- faultsim_test_result {0 {0 {} 3}}
+ faultsim_test_result {0 {0 {} 2}}
}
#-------------------------------------------------------------------------
diff --git a/ext/fts5/test/fts5faultI.test b/ext/fts5/test/fts5faultI.test
index 63bfdd68a..e74162ee3 100644
--- a/ext/fts5/test/fts5faultI.test
+++ b/ext/fts5/test/fts5faultI.test
@@ -256,6 +256,27 @@ do_faultsim_test 10 -faults oom* -prep {
faultsim_test_result {0 hello}
}
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 11.0 {
+ CREATE VIRTUAL TABLE f1 USING fts5(content);
+ CREATE TABLE g1(id, content);
+ INSERT INTO g1 VALUES(30000, 'a b c');
+ INSERT INTO g1 VALUES(40000, 'd e f');
+}
+
+faultsim_save_and_close
+
+do_faultsim_test 11 -faults oom* -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql {
+ INSERT INTO f1(rowid, content) SELECT id, content FROM g1;
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
finish_test
diff --git a/ext/fts5/test/fts5integrity2.test b/ext/fts5/test/fts5integrity2.test
new file mode 100644
index 000000000..968be3bdd
--- /dev/null
+++ b/ext/fts5/test/fts5integrity2.test
@@ -0,0 +1,56 @@
+# 2024 September 3
+#
+# 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.
+#
+#***********************************************************************
+#
+# This file contains tests focused on the integrity-check procedure.
+#
+
+source [file join [file dirname [info script]] fts5_common.tcl]
+set testprefix fts5integrity2
+
+# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE t2 USING fts5(a, detail='none');
+ BEGIN;
+ INSERT INTO t2(rowid, a) VALUES(-1, 'hello world');
+ INSERT INTO t2(rowid, a) VALUES(9223372036854775807, 'hello world');
+ COMMIT;
+}
+
+do_execsql_test 2.1 {
+ SELECT rowid FROM t2('hello AND world');
+} {-1 9223372036854775807}
+
+#-------------------------------------------------------------------------
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(a, detail='none');
+ CREATE TABLE r1(r);
+
+ WITH c(x) AS (VALUES(1) UNION SELECT x<<1 FROM c)
+ INSERT INTO r1(r) SELECT -1-x FROM c;
+
+ INSERT INTO t1(rowid, a) SELECT r, 'abc' FROM r1;
+}
+
+do_execsql_test 2.1 {
+ PRAGMA integrity_check;
+} {ok}
+
+do_execsql_test 2.2 {
+ SELECT rowid FROM t1('abc') ORDER BY +rowid;
+} [db eval {SELECT r FROM r1 ORDER BY r}]
+
+
+finish_test
diff --git a/ext/fts5/test/fts5misc.test b/ext/fts5/test/fts5misc.test
index 534c42fff..c2e580c56 100644
--- a/ext/fts5/test/fts5misc.test
+++ b/ext/fts5/test/fts5misc.test
@@ -44,12 +44,12 @@ do_catchsql_test 1.2.2 {
do_catchsql_test 1.3.1 {
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads');
-} {1 {no such cursor: 1}}
+} {1 {no such cursor: 0}}
do_catchsql_test 1.3.2 {
SELECT a FROM t1
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
-} {1 {no such cursor: 1}}
+} {1 {no such cursor: 0}}
db close
sqlite3 db test.db
@@ -57,12 +57,12 @@ sqlite3 db test.db
do_catchsql_test 1.3.3 {
SELECT a FROM t1
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
-} {1 {no such cursor: 1}}
+} {1 {no such cursor: 0}}
fts5_aux_test_functions db
do_catchsql_test 1.3.4 {
SELECT fts5_columntext(t1) FROM t1('*reads');
-} {1 {no such cursor: 1}}
+} {1 {no such cursor: 0}}
#-------------------------------------------------------------------------
reset_db
diff --git a/ext/fts5/test/fts5simple.test b/ext/fts5/test/fts5simple.test
index 60ccb5a9c..ad59bf0d9 100644
--- a/ext/fts5/test/fts5simple.test
+++ b/ext/fts5/test/fts5simple.test
@@ -350,7 +350,7 @@ do_execsql_test 14.3 {
do_execsql_test 14.4 {
SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'
-} {0 {} 3}
+} {0 {} 2}
#-------------------------------------------------------------------------
reset_db
diff --git a/ext/misc/percentile.c b/ext/misc/percentile.c
index cccbaf025..1c6191d42 100644
--- a/ext/misc/percentile.c
+++ b/ext/misc/percentile.c
@@ -11,7 +11,7 @@
******************************************************************************
**
** This file contains code to implement the percentile(Y,P) SQL function
-** as described below:
+** and similar as described below:
**
** (1) The percentile(Y,P) function is an aggregate function taking
** exactly two arguments.
@@ -60,31 +60,105 @@
**
** (13) A separate median(Y) function is the equivalent percentile(Y,50).
**
-** (14) A separate percentile_cond(Y,X) function is the equivalent of
-** percentile(Y,X*100.0).
+** (14) A separate percentile_cont(Y,P) function is equivalent to
+** percentile(Y,P/100.0). In other words, the fraction value in
+** the second argument is in the range of 0 to 1 instead of 0 to 100.
+**
+** (15) A separate percentile_disc(Y,P) function is like
+** percentile_cont(Y,P) except that instead of returning the weighted
+** average of the nearest two input values, it returns the next lower
+** value. So the percentile_disc(Y,P) will always return a value
+** that was one of the inputs.
+**
+** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
+** percentile_disc(Y,P) can be used as window functions.
+**
+** Differences from standard SQL:
+**
+** * The percentile_cont(X,P) function is equivalent to the following in
+** standard SQL:
+**
+** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
+**
+** The SQLite syntax is much more compact. The standard SQL syntax
+** is also supported if SQLite is compiled with the
+** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
+**
+** * No median(X) function exists in the SQL standard. App developers
+** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
+**
+** * No percentile(Y,P) function exists in the SQL standard. Instead of
+** percential(Y,P), developers must write this:
+** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
+** the fraction parameter to percentile() goes from 0 to 100 whereas
+** the fraction parameter in SQL standard percentile_cont() goes from
+** 0 to 1.
+**
+** Implementation notes as of 2024-08-31:
+**
+** * The regular aggregate-function versions of these routines work
+** by accumulating all values in an array of doubles, then sorting
+** that array using quicksort before computing the answer. Thus
+** the runtime is O(NlogN) where N is the number of rows of input.
+**
+** * For the window-function versions of these routines, the array of
+** inputs is sorted as soon as the first value is computed. Thereafter,
+** the array is kept in sorted order using an insert-sort. This
+** results in O(N*K) performance where K is the size of the window.
+** One can imagine alternative implementations that give O(N*logN*logK)
+** performance, but they require more complex logic and data structures.
+** The developers have elected to keep the asymptotically slower
+** algorithm for now, for simplicity, under the theory that window
+** functions are seldom used and when they are, the window size K is
+** often small. The developers might revisit that decision later,
+** should the need arise.
*/
-#include "sqlite3ext.h"
-SQLITE_EXTENSION_INIT1
+#if defined(SQLITE3_H)
+ /* no-op */
+#elif defined(SQLITE_STATIC_PERCENTILE)
+# include "sqlite3.h"
+#else
+# include "sqlite3ext.h"
+ SQLITE_EXTENSION_INIT1
+#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
-/* The following object is the session context for a single percentile()
-** function. We have to remember all input Y values until the very end.
+/* The following object is the group context for a single percentile()
+** aggregate. Remember all input Y values until the very end.
** Those values are accumulated in the Percentile.a[] array.
*/
typedef struct Percentile Percentile;
struct Percentile {
unsigned nAlloc; /* Number of slots allocated for a[] */
unsigned nUsed; /* Number of slots actually used in a[] */
- double rPct; /* 1.0 more than the value for P */
+ char bSorted; /* True if a[] is already in sorted order */
+ char bKeepSorted; /* True if advantageous to keep a[] sorted */
+ char bPctValid; /* True if rPct is valid */
+ double rPct; /* Fraction. 0.0 to 1.0 */
double *a; /* Array of Y values */
};
+/* Details of each function in the percentile family */
+typedef struct PercentileFunc PercentileFunc;
+struct PercentileFunc {
+ const char *zName; /* Function name */
+ char nArg; /* Number of arguments */
+ char mxFrac; /* Maximum value of the "fraction" input */
+ char bDiscrete; /* True for percentile_disc() */
+};
+static const PercentileFunc aPercentFunc[] = {
+ { "median", 1, 1, 0 },
+ { "percentile", 2, 100, 0 },
+ { "percentile_cont", 2, 1, 0 },
+ { "percentile_disc", 2, 1, 1 },
+};
+
/*
** Return TRUE if the input floating-point number is an infinity.
*/
-static int isInfinity(double r){
+static int percentIsInfinity(double r){
sqlite3_uint64 u;
assert( sizeof(u)==sizeof(r) );
memcpy(&u, &r, sizeof(u));
@@ -92,14 +166,65 @@ static int isInfinity(double r){
}
/*
-** Return TRUE if two doubles differ by 0.001 or less
+** Return TRUE if two doubles differ by 0.001 or less.
*/
-static int sameValue(double a, double b){
+static int percentSameValue(double a, double b){
a -= b;
return a>=-0.001 && a<=0.001;
}
/*
+** Search p (which must have p->bSorted) looking for an entry with
+** value y. Return the index of that entry.
+**
+** If bExact is true, return -1 if the entry is not found.
+**
+** If bExact is false, return the index at which a new entry with
+** value y should be insert in order to keep the values in sorted
+** order. The smallest return value in this case will be 0, and
+** the largest return value will be p->nUsed.
+*/
+static int percentBinarySearch(Percentile *p, double y, int bExact){
+ int iFirst = 0; /* First element of search range */
+ int iLast = p->nUsed - 1; /* Last element of search range */
+ while( iLast>=iFirst ){
+ int iMid = (iFirst+iLast)/2;
+ double x = p->a[iMid];
+ if( x<y ){
+ iFirst = iMid + 1;
+ }else if( x>y ){
+ iLast = iMid - 1;
+ }else{
+ return iMid;
+ }
+ }
+ if( bExact ) return -1;
+ return iFirst;
+}
+
+/*
+** Generate an error for a percentile function.
+**
+** The error format string must have exactly one occurrance of "%%s()"
+** (with two '%' characters). That substring will be replaced by the name
+** of the function.
+*/
+static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
+ PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
+ char *zMsg1;
+ char *zMsg2;
+ va_list ap;
+
+ va_start(ap, zFormat);
+ zMsg1 = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0;
+ sqlite3_result_error(pCtx, zMsg2, -1);
+ sqlite3_free(zMsg1);
+ sqlite3_free(zMsg2);
+}
+
+/*
** The "step" function for percentile(Y,P) is called once for each
** input row.
*/
@@ -112,28 +237,20 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
if( argc==1 ){
/* Requirement 13: median(Y) is the same as percentile(Y,50). */
- rPct = 50.0;
- }else if( sqlite3_user_data(pCtx)==0 ){
- /* Requirement 3: P must be a number between 0 and 100 */
- eType = sqlite3_value_numeric_type(argv[1]);
- rPct = sqlite3_value_double(argv[1]);
- if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
- || rPct<0.0 || rPct>100.0 ){
- sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
- "a number between 0.0 and 100.0", -1);
- return;
- }
+ rPct = 0.5;
}else{
- /* Requirement 3: P must be a number between 0 and 1 */
+ /* Requirement 3: P must be a number between 0 and 100 */
+ PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
eType = sqlite3_value_numeric_type(argv[1]);
- rPct = sqlite3_value_double(argv[1]);
+ rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac;
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
- || rPct<0.0 || rPct>1.0 ){
- sqlite3_result_error(pCtx, "2nd argument to percentile_cont() is not "
- "a number between 0.0 and 1.0", -1);
+ || rPct<0.0 || rPct>1.0
+ ){
+ percentError(pCtx, "the fraction argument to %%s()"
+ " is not between 0.0 and %.1f",
+ (double)pFunc->mxFrac);
return;
}
- rPct *= 100.0;
}
/* Allocate the session context. */
@@ -142,11 +259,12 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
/* Remember the P value. Throw an error if the P value is different
** from any prior row, per Requirement (2). */
- if( p->rPct==0.0 ){
- p->rPct = rPct+1.0;
- }else if( !sameValue(p->rPct,rPct+1.0) ){
- sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
- "same for all input rows", -1);
+ if( !p->bPctValid ){
+ p->rPct = rPct;
+ p->bPctValid = 1;
+ }else if( !percentSameValue(p->rPct,rPct) ){
+ percentError(pCtx, "the fraction argument to %%s()"
+ " is not the same for all input rows");
return;
}
@@ -157,15 +275,14 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
/* If not NULL, then Y must be numeric. Otherwise throw an error.
** Requirement 4 */
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
- sqlite3_result_error(pCtx, "1st argument to percentile() is not "
- "numeric", -1);
+ percentError(pCtx, "input to %%s() is not numeric");
return;
}
/* Throw an error if the Y value is infinity or NaN */
y = sqlite3_value_double(argv[0]);
- if( isInfinity(y) ){
- sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
+ if( percentIsInfinity(y) ){
+ percentError(pCtx, "Inf input to %%s()");
return;
}
@@ -182,50 +299,80 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
p->nAlloc = n;
p->a = a;
}
- p->a[p->nUsed++] = y;
+ if( p->nUsed==0 ){
+ p->a[p->nUsed++] = y;
+ p->bSorted = 1;
+ }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
+ p->a[p->nUsed++] = y;
+ }else if( p->bKeepSorted ){
+ int i;
+ i = percentBinarySearch(p, y, 0);
+ if( i<(int)p->nUsed ){
+ memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
+ }
+ p->a[i] = y;
+ p->nUsed++;
+ }else{
+ p->a[p->nUsed++] = y;
+ p->bSorted = 0;
+ }
}
/*
+** Interchange two doubles.
+*/
+#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
+
+/*
** Sort an array of doubles.
+**
+** Algorithm: quicksort
+**
+** This is implemented separately rather than using the qsort() routine
+** from the standard library because:
+**
+** (1) To avoid a dependency on qsort()
+** (2) To avoid the function call to the comparison routine for each
+** comparison.
*/
-static void sortDoubles(double *a, int n){
- int iLt; /* Entries with index less than iLt are less than rPivot */
- int iGt; /* Entries with index iGt or more are greater than rPivot */
+static void percentSort(double *a, unsigned int n){
+ int iLt; /* Entries before a[iLt] are less than rPivot */
+ int iGt; /* Entries at or after a[iGt] are greater than rPivot */
int i; /* Loop counter */
double rPivot; /* The pivot value */
- double rTmp; /* Temporary used to swap two values */
-
- if( n<2 ) return;
- if( n>5 ){
- rPivot = (a[0] + a[n/2] + a[n-1])/3.0;
- }else{
- rPivot = a[n/2];
+
+ assert( n>=2 );
+ if( a[0]>a[n-1] ){
+ SWAP_DOUBLE(a[0],a[n-1])
+ }
+ if( n==2 ) return;
+ iGt = n-1;
+ i = n/2;
+ if( a[0]>a[i] ){
+ SWAP_DOUBLE(a[0],a[i])
+ }else if( a[i]>a[iGt] ){
+ SWAP_DOUBLE(a[i],a[iGt])
}
- iLt = i = 0;
- iGt = n;
- while( i<iGt ){
+ if( n==3 ) return;
+ rPivot = a[i];
+ iLt = i = 1;
+ do{
if( a[i]<rPivot ){
- if( i>iLt ){
- rTmp = a[i];
- a[i] = a[iLt];
- a[iLt] = rTmp;
- }
+ if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
iLt++;
i++;
}else if( a[i]>rPivot ){
do{
iGt--;
}while( iGt>i && a[iGt]>rPivot );
- rTmp = a[i];
- a[i] = a[iGt];
- a[iGt] = rTmp;
+ SWAP_DOUBLE(a[i],a[iGt])
}else{
i++;
}
- }
- if( iLt>=2 ) sortDoubles(a, iLt);
- if( n-iGt>=2 ) sortDoubles(a+iGt, n-iGt);
-
+ }while( i<iGt );
+ if( iLt>=2 ) percentSort(a, iLt);
+ if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
+
/* Uncomment for testing */
#if 0
for(i=0; i<n-1; i++){
@@ -234,12 +381,61 @@ static void sortDoubles(double *a, int n){
#endif
}
+
/*
-** Called to compute the final output of percentile() and to clean
-** up all allocated memory.
+** The "inverse" function for percentile(Y,P) is called to remove a
+** row that was previously inserted by "step".
*/
-static void percentFinal(sqlite3_context *pCtx){
+static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
+ Percentile *p;
+ int eType;
+ double y;
+ int i;
+ assert( argc==2 || argc==1 );
+
+ /* Allocate the session context. */
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ assert( p!=0 );
+
+ /* Ignore rows for which Y is NULL */
+ eType = sqlite3_value_type(argv[0]);
+ if( eType==SQLITE_NULL ) return;
+
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
+ ** Requirement 4 */
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
+ return;
+ }
+
+ /* Ignore the Y value if it is infinity or NaN */
+ y = sqlite3_value_double(argv[0]);
+ if( percentIsInfinity(y) ){
+ return;
+ }
+ if( p->bSorted==0 ){
+ assert( p->nUsed>1 );
+ percentSort(p->a, p->nUsed);
+ p->bSorted = 1;
+ }
+ p->bKeepSorted = 1;
+
+ /* Find and remove the row */
+ i = percentBinarySearch(p, y, 1);
+ if( i>=0 ){
+ p->nUsed--;
+ if( i<(int)p->nUsed ){
+ memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
+ }
+ }
+}
+
+/*
+** Compute the final output of percentile(). Clean up all allocated
+** memory if and only if bIsFinal is true.
+*/
+static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
Percentile *p;
+ PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
unsigned i1, i2;
double v1, v2;
double ix, vx;
@@ -247,21 +443,38 @@ static void percentFinal(sqlite3_context *pCtx){
if( p==0 ) return;
if( p->a==0 ) return;
if( p->nUsed ){
- sortDoubles(p->a, p->nUsed);
- ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
+ if( p->bSorted==0 ){
+ assert( p->nUsed>1 );
+ percentSort(p->a, p->nUsed);
+ p->bSorted = 1;
+ }
+ ix = p->rPct*(p->nUsed-1);
i1 = (unsigned)ix;
- i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
- v1 = p->a[i1];
- v2 = p->a[i2];
- vx = v1 + (v2-v1)*(ix-i1);
+ if( pFunc->bDiscrete ){
+ vx = p->a[i1];
+ }else{
+ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
+ v1 = p->a[i1];
+ v2 = p->a[i2];
+ vx = v1 + (v2-v1)*(ix-i1);
+ }
sqlite3_result_double(pCtx, vx);
}
- sqlite3_free(p->a);
- memset(p, 0, sizeof(*p));
+ if( bIsFinal ){
+ sqlite3_free(p->a);
+ memset(p, 0, sizeof(*p));
+ }else{
+ p->bKeepSorted = 1;
+ }
+}
+static void percentFinal(sqlite3_context *pCtx){
+ percentCompute(pCtx, 1);
+}
+static void percentValue(sqlite3_context *pCtx){
+ percentCompute(pCtx, 0);
}
-
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE)
__declspec(dllexport)
#endif
int sqlite3_percentile_init(
@@ -270,20 +483,21 @@ int sqlite3_percentile_init(
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
+ unsigned int i;
+#if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE)
+ (void)pApi; /* Unused parameter */
+#else
SQLITE_EXTENSION_INIT2(pApi);
+#endif
(void)pzErrMsg; /* Unused parameter */
- rc = sqlite3_create_function(db, "percentile", 2,
- SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
- 0, percentStep, percentFinal);
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(db, "median", 1,
- SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
- 0, percentStep, percentFinal);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(db, "percentile_cont", 2,
- SQLITE_UTF8|SQLITE_INNOCUOUS, &percentStep,
- 0, percentStep, percentFinal);
+ for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
+ rc = sqlite3_create_window_function(db,
+ aPercentFunc[i].zName,
+ aPercentFunc[i].nArg,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
+ (void*)&aPercentFunc[i],
+ percentStep, percentFinal, percentValue, percentInverse, 0);
+ if( rc ) break;
}
return rc;
}
diff --git a/main.mk b/main.mk
index baf826041..b379d2f8f 100644
--- a/main.mk
+++ b/main.mk
@@ -42,6 +42,10 @@
# build the SQLite library and testing tools.
################################################################################
+# If OPTIONS... is specified on the command-line, append its value to OPTS
+#
+OPTS += $(OPTIONS)
+
# This is how we compile
#
TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP)
@@ -537,6 +541,7 @@ FUZZSRC += $(TOP)/test/vt02.c
FUZZSRC += $(TOP)/test/fuzzinvariants.c
FUZZSRC += $(TOP)/ext/recover/dbdata.c
FUZZSRC += $(TOP)/ext/recover/sqlite3recover.c
+FUZZSRC += $(TOP)/ext/misc/percentile.c
FUZZSRC += $(TOP)/ext/misc/randomjson.c
DBFUZZ_OPT =
KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
@@ -760,6 +765,7 @@ SHELL_DEP = \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/memtrace.c \
$(TOP)/ext/misc/pcachetrace.c \
+ $(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/shathree.c \
@@ -958,9 +964,14 @@ tcltest: ./testfixture$(EXE)
testrunner: testfixture$(EXE)
./testfixture$(EXE) $(TOP)/test/testrunner.tcl
-# Runs both fuzztest and testrunner, consecutively.
+# This is the testing target preferred by the core SQLite developers.
+# It runs tests under a standard configuration, regardless of how
+# ./configure was run. The devs run "make devtest" prior to each
+# check-in, at a minimum. Probably other tests too, but at least this
+# one.
#
-devtest: testfixture$(EXE) fuzztest testrunner
+devtest: srctree-check sourcetest
+ tclsh $(TOP)/test/testrunner.tcl mdevtest
mdevtest:
tclsh $(TOP)/test/testrunner.tcl mdevtest
@@ -971,11 +982,20 @@ mdevtest:
quicktest: ./testfixture$(EXE)
./testfixture$(EXE) $(TOP)/test/extraquick.test $(TESTOPTS)
-# The default test case. Runs most of the faster standard TCL tests,
-# and fuzz tests, and sqlite3_analyzer and sqldiff tests.
+# Validate that various generated files in the source tree
+# are up-to-date.
+#
+srctree-check: $(TOP)/tool/srctree-check.tcl
+ tclsh $(TOP)/tool/srctree-check.tcl
+
+# Try to run tests on whatever options are specified by the
+# environment variables. The SQLite developers seldom use this target.
+# Instead# they use "make devtest" which runs tests on a standard set of
+# options regardless of how SQLite is configured. This "test"
+# target is provided for legacy only.
+#
test: fuzztest sourcetest $(TESTPROGS) tcltest
-
# Run a test using valgrind. This can take a really long time
# because valgrind is so much slower than a native machine.
#
@@ -1112,41 +1132,22 @@ install: sqlite3 libsqlite3.a sqlite3.h
mv sqlite3.h /usr/include
clean:
- rm -f *.o sqlite3 sqlite3.exe libsqlite3.a sqlite3.h opcodes.*
- rm -f lemon lemon.exe lempar.c parse.* sqlite*.tar.gz
- rm -f mkkeywordhash mkkeywordhash.exe keywordhash.h
- rm -f $(PUBLISH)
- rm -f *.da *.bb *.bbg gmon.out
- rm -rf tsrc target_source
- rm -f testloadext.dll libtestloadext.so
- rm -f amalgamation-testfixture amalgamation-testfixture.exe
- rm -f fts3-testfixture fts3-testfixture.exe
- rm -f testfixture testfixture.exe
- rm -f threadtest3 threadtest3.exe
- rm -f LogEst LogEst.exe
- rm -f fts3view fts3view.exe
- rm -f rollback-test rollback-test.exe
- rm -f showdb showdb.exe
- rm -f showjournal showjournal.exe
- rm -f showstat4 showstat4.exe
- rm -f showwal showwal.exe
- rm -f changeset changeset.exe
- rm -f speedtest1 speedtest1.exe
- rm -f wordcount wordcount.exe
- rm -f rbu rbu.exe
- rm -f srcck1 srcck1.exe
- rm -f sqlite3.c sqlite3-*.c fts?amal.c tclsqlite3.c
- rm -f sqlite3rc.h
- rm -f shell.c sqlite3ext.h
- rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c
- rm -f sqlite3_expert sqlite3_expert.exe
- rm -f sqlite-*-output.vsix
- rm -f mptester mptester.exe
- rm -f fuzzershell fuzzershell.exe
- rm -f fuzzcheck fuzzcheck.exe
- rm -f sessionfuzz
- rm -f sqldiff sqldiff.exe
- rm -f fts5.* fts5parse.*
- rm -f lsm.h lsm1.c
- rm -f threadtest5
- rm -f src-verify
+ rm -f *.lo *.la *.o *.c *.h *.da *.bb *.bbg gmon.* *.rws sqlite3$(TEXE)
+ rm -rf .libs .deps tsrc libtool target_source testrunner_*
+ rm -f lemon$(BEXE) sqlite*.tar.gz
+ rm -f mkkeywordhash$(BEXE) mksourceid$(BEXE)
+ rm -f parse.* fts5parse.*
+ rm -rf tsrc .target_source testrunner_bld_* testdir*
+ rm -f tclsqlite3$(TEXE)
+ rm -f $(TESTPROGS) testrunner.*
+ rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
+ rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
+ rm -f wordcount$(TEXE) changeset$(TEXE) version-info$(TEXE)
+ rm -f *.dll *.lib *.exp *.def *.pc *.vsix
+ rm -f sqlite3_analyzer$(TEXE)
+ rm -f mptester$(TEXE) rbu$(TEXE) srcck1$(TEXE)
+ rm -f fuzzershell$(TEXE) fuzzcheck$(TEXE) sqldiff$(TEXE) dbhash$(TEXE)
+ rm -f threadtest5$(TEXE)
+ rm -f src-verify has_tclsh*
+
+distclean: clean
diff --git a/manifest b/manifest
index 55e9207db..6f14866d0 100644
--- a/manifest
+++ b/manifest
@@ -1,12 +1,12 @@
-C Cause\scfGets()\s(under\sSQLITE_USE_ONLY_WIN32)\sto\sbetter\semulate\sfgets().
-D 2024-09-03T02:09:13.849
+C Fix\sext/consio\sso\sthat\sit\sworks\scorrectly\swith\sSQLITE_USE_ONLY_WIN32.
+D 2024-09-03T14:15:57.425
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in 101cb90f75c16ca3f8e7088364322441f9b01d6fdcef4a9c7f5fdcf78a3601a4
+F Makefile.in af5fbc3453b745daa68c7aa5dfdb945c09cb724971db3b783d6b5e1a62279e28
F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
-F Makefile.msc 6c3fe8b6ce60e73f34a148c957d78b4648745c8d30e792423aa1a8d8bf12d065
-F README.md 3d47ef5758e2de29d6f4e1aca714d51424baba86c8b561fef330f6ef2dc9282c
+F Makefile.msc 2c905f4c795a628d7fd892294e8fdec6d5f719fa5b252cb839fed147e64435a0
+F README.md 5b678e264236788390d11991f2c0052bd73f19790173883fc56d638bcb849154
F VERSION 0db40f92c04378404eb45bff93e9e42c148c7e54fd3da99469ed21e22411f5a6
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -17,7 +17,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
F autoconf/Makefile.am adedc1324b6a87fdd1265ddd336d2fb7d4f36a0e77b86ea553ae7cc4ea239347
F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
-F autoconf/Makefile.msc 7ac6c331fc3b8aa57b6782db995b8c0e49230352decd4e2662fd07c06a9ed623
+F autoconf/Makefile.msc 2aced6442addab13ed115696eba28d9ed29caa3dd604a31392c2c7a5da301492
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
F autoconf/README.txt 42cfd21d0b19dc7d5d85fb5c405c5f3c6a4c923021c39128f6ba685355d8fd56
F autoconf/configure.ac ec7fa914c5e74ff212fe879f9bb6918e1234497e05facfb641f30c4d5893b277
@@ -99,10 +99,10 @@ F ext/fts5/fts5_buffer.c 0eec58bff585f1a44ea9147eae5da2447292080ea435957f7488c70
F ext/fts5/fts5_config.c 353d2a0d12678cae6ab5b9ce54aed8dac0825667b69248b5a4ed81cbefc109ea
F ext/fts5/fts5_expr.c 9a56f53700d1860f0ee2f373c2b9074eaf2a7aa0637d0e27a6476de26a3fee33
F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1
-F ext/fts5/fts5_index.c eb9a0dda3bc6ef969a6be8d2746af56856e67251810ddba08622b45be8477abe
-F ext/fts5/fts5_main.c 3d8b778f65fe5be218f6a8e4019048f092c0c09621b035ce3e8804093036b578
+F ext/fts5/fts5_index.c 571483823193f09439356741669aa8c81da838ae6f5e1bfa7517f7ee2fb3addd
+F ext/fts5/fts5_main.c 1fddb53f495425d9314c74b30c5848a9dd254be0e5f445bfe38292d5ab21c288
F ext/fts5/fts5_storage.c 9a9b880be12901f1962ae2a5a7e1b74348b3099a1e728764e419f75d98e3e612
-F ext/fts5/fts5_tcl.c 1dcf08028141c40a32634bdcf2d5601622ce4edc48f82ac4ce0cbe0a92a6961d
+F ext/fts5/fts5_tcl.c 4db9258a7882c5eac0da4433042132aaf15b87dd1e1636c7a6ca203abd2c8bfe
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b
F ext/fts5/fts5_tokenize.c ae9c4fa93174ef06ffc138bd4280a1c37f7e13624d3d2706aad4b80573f23c41
@@ -148,11 +148,12 @@ F ext/fts5/test/fts5contentless4.test ec34dc69ef474ca9997dae6d91e072906e0e9a5a4b
F ext/fts5/test/fts5contentless5.test 40cdcb4fe751672450829c5a96bd32c25fc2f6076279dd2ce5c58ac9a390132a
F ext/fts5/test/fts5corrupt.test 6485f721b88ba355ca5d701e7ee87a4efa3ea578d8e6adb26f51ef956c8328bd
F ext/fts5/test/fts5corrupt2.test 335911e3f68b9625d850325f9e29a128db3f4276a8c9d4e32134580da8f924c4
-F ext/fts5/test/fts5corrupt3.test 621e9bca3e7299f487e1b29ff4179d9fc9560f5847dfc5b50a16010c9d2a0e5f
+F ext/fts5/test/fts5corrupt3.test 4fc3bf129f1616bea00884a23fd9d7b0e46d01791d2b57fe8d68ac36e8d3ff7c
F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733
F ext/fts5/test/fts5corrupt5.test 11b47126f5772cc37b67e3e8b2ed05895c4d07c05338bc07e4eea225bfe32c76
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
F ext/fts5/test/fts5corrupt7.test 4e830875c33b9ea3c4cf1ba71e692b63893cbb4faae8c69b1071889dc26e211c
+F ext/fts5/test/fts5corrupt8.test b81d802e41631e98100f49a1aadeeffef860e30a62d6ed7d743c2797c477239e
F ext/fts5/test/fts5delete.test 619295b20dbc1d840b403ee07c878f52378849c3c02e44f2ee143b3e978a0aa7
F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
@@ -164,7 +165,7 @@ F ext/fts5/test/fts5expr.test c7e208813df7a90badc856fde3796da79569b39382e0fdb430
F ext/fts5/test/fts5fault1.test d28a65caee75db6897c3cf1358c5230d3bb2a3bf7fb31062c19c7e5382b3d2bd
F ext/fts5/test/fts5fault2.test 69c8fdbef830cd0d450908d4504d5bb86609e255af99c421c20a0756251fe344
F ext/fts5/test/fts5fault3.test da2f9e3e56ff5740d68ebdd6877c97089e7ed28ddff28a0da87a6afea27e5522
-F ext/fts5/test/fts5fault4.test 87a10d0caee57da587c7588b0c8d25d2930197399b4812ad1e4d574c75324cee
+F ext/fts5/test/fts5fault4.test a5c0e849127c24e1751bc453a817f09a1b8d460e75f9ae4764017e216a870db3
F ext/fts5/test/fts5fault5.test a336e4e11847de24c9497f80cce18e00bb3fab7fb11f97d04eb9af898900a762
F ext/fts5/test/fts5fault6.test 40f49976c6ca8927bf7d65d0b8df46009d7ea172e1d4050b294610e7ea0a2979
F ext/fts5/test/fts5fault7.test 0acbec416edb24b8881f154e99c31e9ccf73f539cfcd164090be139e9e97ed4c
@@ -177,12 +178,13 @@ F ext/fts5/test/fts5faultE.test 844586ce71dab4be85bb86880e87b624d089f851654cd22e
F ext/fts5/test/fts5faultF.test 4abef99f86e99d9f0c6460dd68c586a766b6b9f1f660ada55bf2e8266bd1bbc1
F ext/fts5/test/fts5faultG.test 0544411ffcb3e19b42866f757a8a5e0fb8fef3a62c06f61d14deebc571bb7ea9
F ext/fts5/test/fts5faultH.test 2b2b5b8cb1b3fd7679f488c06e22af44107fbc6137eaf45b3e771dc7b149312d
-F ext/fts5/test/fts5faultI.test e1e8a927122a8c3be8614a59717e838cad0505ba0a6f404983da8430e7aeb14d
+F ext/fts5/test/fts5faultI.test ae4b83ac953200bd7b66d53038f7d6a4fc29cd64831b8e1795538babcea7c638
F ext/fts5/test/fts5first.test bfd685b96905bf541d99d8644e0a7219d1d833455a08ab64e344071a613b6ba9
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/fts5integrity2.test 4c3636615c0201232c44a8105d5cb14fd5499fd0ee3014d7ffd7e83aac76ece8
F ext/fts5/test/fts5interrupt.test 20d04204d3e341b104c0c24a41596b6393a3a81eba1044c168db0e106f9ac92c
F ext/fts5/test/fts5lastrowid.test f36298a1fb9f988bde060a274a7ce638faa9c38a31400f8d2d27ea9373e0c4a1
F ext/fts5/test/fts5leftjoin.test c0b4cafb9661379e576dc4405c0891d8fcc2782680740513c4d1fc114b43d4ad
@@ -191,7 +193,7 @@ F ext/fts5/test/fts5locale.test 797cf6f5e017462ab11313ce884b9f1df8ff063811e74ef4
F ext/fts5/test/fts5matchinfo.test 877520582feb86bbfd95ab780099bcba4526f18ac75ee34979144cf86ba3a5a3
F ext/fts5/test/fts5merge.test 2654df0bcdb2d117c2d38b6aeb0168061be01c643f9e9194b36c43a2970e8082
F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2
-F ext/fts5/test/fts5misc.test 60bb2be4a2d83d7a45047c1812781e2e337a27efa539d86356ef7f4acaf08eab
+F ext/fts5/test/fts5misc.test 8c3cc771f773dc4bb4973620c51e7729e324ca2cc80eb8894f1c2c605e361f0b
F ext/fts5/test/fts5multi.test a15bc91cdb717492e6e1b66fec1c356cb57386b980c7ba5af1915f97fe878581
F ext/fts5/test/fts5multiclient.test 5ff811c028d6108045ffef737f1e9f05028af2458e456c0937c1d1b8dea56d45
F ext/fts5/test/fts5near.test 33d60867581066e5db7016deb5d651628125d7ff4e0233a88175aa5b65874c74
@@ -225,7 +227,7 @@ F ext/fts5/test/fts5secure6.test 74bf04733cc523bccca519bb03d3b4e2ed6f6e3db7c59bf
F ext/fts5/test/fts5secure7.test fd03d0868d64340a1db8615b02e5508fea409de13910114e4f19eaefc120777a
F ext/fts5/test/fts5secure8.test 808ade9d172ed07b24b85c57dd53b6d2b1aba018b4e634d267ce572221de80e0
F ext/fts5/test/fts5securefault.test c34a28c7cd2f31a8b8907563889e1329a97da975c08df2d951422bcef8e2ebc5
-F ext/fts5/test/fts5simple.test ed7c3815c9fa1c16166258cb98edb2e014c63c7589958d76c5487df0df913d61
+F ext/fts5/test/fts5simple.test 302cdb4f8a3350b091f4f1bccd82d05610428657f6f9e81c17703ba48267ec40
F ext/fts5/test/fts5simple2.test d10d963a357b8ec77b99032e4c816459b4dbdb1f6eee25eada7ef3ed245cb2dc
F ext/fts5/test/fts5simple3.test 146ec3dc8f5763d6212641c9f0a2f1cba41679353d2add7b963beceb115dc7f4
F ext/fts5/test/fts5synonym.test becc8cea6cfc958a50b30c572c68cbfdf7455971d0fe988202ce67638d2c6cf6
@@ -409,7 +411,7 @@ F ext/misc/nextchar.c 7877914c2a80c2f181dd04c3dbef550dfb54c93495dc03da2403b5dd58
F ext/misc/noop.c f1a21cc9b7a4e667e5c8458d80ba680b8bd4315a003f256006046879f679c5a0
F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f
F ext/misc/pcachetrace.c f4227ce03fb16aa8d6f321b72dd051097419d7a028a9853af048bee7645cb405
-F ext/misc/percentile.c af1941dc87d45dd0c2698a3087fbfe9ee0d157e5e72da521430c4b784abcbe81
+F ext/misc/percentile.c 42eb041edab407e512aaa087f99ed9287fe0b3224f7a6d194456c00fc1454312
F ext/misc/prefixes.c 82645f79229877afab08c8b08ca1e7fa31921280906b90a61c294e4f540cd2a6
F ext/misc/qpvtab.c fc189e127f68f791af90a487f4460ec91539a716daf45a0c357e963fd47cc06c
F ext/misc/randomjson.c ef835fc64289e76ac4873b85fe12f9463a036168d7683cf2b773e36e6262c4ed
@@ -684,7 +686,7 @@ F ext/wasm/wasmfs.make 8a4955882aaa0783b3f60a9484a1f0f3d8b6f775c0fcd17c082f31966
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
-F main.mk 9541ffdce424ddddb463e2480c3f1cb4067bbd3a6d2c84b3f083cb128a2fa721
+F main.mk 5a2e7d4a852c058373efc78407816de41595d06975148c766092b3cf0fea4298
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -703,13 +705,13 @@ F src/auth.c 4c1ea890e0069ad73bead5d17a5b12c34cfa4f1a24175c8147ea439b64be271c
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
-F src/btree.c 8b42fc7d9efdb2df05c30e8f91ff6cfbd979724ae24bf90269028468b7a13333
+F src/btree.c 8c5592c618741c5fc9733e7efe3927bfafad3e999d15b0a0f3f1d3f3e17b919e
F src/btree.h 55066f513eb095db935169dab1dc2f7c7a747ef223c533f5d4ad4dfed346cbd0
F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6
F src/build.c 3a1840d9d171ce2d24f4c1f7acda7266ab796c664290c1acba65ff98ce2bd01e
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
-F src/ctime.c 64e4b1227b4ed123146f0aa2989131d1fbd9b927b11e80c9d58c6a68f9cd5ce3
+F src/ctime.c b224d3db0f28c4a5f1407c50107a0a8133bd244ff3c7f6f8cedeb896a8cf1b64
F src/date.c 89ce1ff20512a7fa5070ba6e7dd5c171148ca7d580955795bf97c79c2456144a
F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
@@ -717,7 +719,7 @@ F src/delete.c 444c4d1eaac40103461e3b6f0881846dd3aafc1cec1dd169d3482fa331667da7
F src/expr.c 6d5f2c38fe3ec06a7eac599dac822788b36064124e20112a844e9cd5156cb239
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
-F src/func.c 1f61e32e7a357e615b5d2e774bee563761fce4f2fd97ecb0f72c33e62a2ada5f
+F src/func.c df400a1d3f4625997d4dd8a81951c303e066277c29b861d37e03cd152d7858dd
F src/global.c 61a419dd9e993b9be0f91de4c4ccf322b053eb829868e089f0321dd669be3b90
F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
@@ -727,7 +729,7 @@ F src/insert.c f8d1a0f8ee258411009c6b7f2d93170e351bd19f5ad89d57e1180644297cbe70
F src/json.c 5b6a1d6015997b9ee848a32948720bdb26a0ef2de5a2127ebf7355ce66dbdc0d
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
-F src/main.c a520c325c7400b249242945f2c602acb19662c40f5db1c04d88664cfaa8b85ac
+F src/main.c e7b53893f9fb3ad76baa8513f85c167b34d5c8e25ce64608db440f5637d0fe9e
F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
@@ -753,7 +755,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c b08600ebf0db90b6d1e9b8b6577c6fa3877cbe1a100bd0b2899e4c6e9adad4b3
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
-F src/parse.y 5972b7d00af4c8d96fdad781af1ea1d5d51fc3b907ad61bda60e49503274e5ed
+F src/parse.y a7a8d42eeff01d267444ddb476029b0b1726fb70ae3d77984140f17ad02e2d61
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 49516ad7718a3626f28f710fa7448ef1fce3c07fd169acbb4817341950264319
@@ -766,10 +768,10 @@ F src/resolve.c 2c127880c0634962837f16f2f48a295e514357af959330cc038de73015d5b5e8
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4b14337a2742f0c0beeba490e9a05507e9b4b12184b9cd12773501d08d48e3fe
F src/shell.c.in 40de636c1d90fb8a9ca7f49dc8f50d930f1b60736e73aca5eb37c4c7d0e47f9d
-F src/sqlite.h.in f07bff4225a1244efd604a0ffef81ed69f29d3dbaed7e22f906f26229ba3ca9e
+F src/sqlite.h.in 77f55bd1978a04a14db211732f0a609077cf60ba4ccf9baf39988f508945419c
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
-F src/sqliteInt.h 28c878bdf528879afefe1994ac007d094f8061f2fdacdc55d6055d7e9341151e
+F src/sqliteInt.h 889cd632f4386bbd8619b166abb7d25f1c8ce6514e90cb7f22f63bd530fc6107
F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -789,7 +791,7 @@ F src/test_backup.c bd901e3c116c7f3b3bbbd4aae4ce87d99b400c9cbb0a9e7b4610af451d97
F src/test_bestindex.c 3401bee51665cbf7f9ed2552b5795452a8b86365e4c9ece745b54155a55670c6
F src/test_blob.c bcdf6a6c22d0bcc13c41479d63692ef413add2a4d30e1e26b9f74ab85b9fb4d5
F src/test_btree.c 28283787d32b8fa953eb77412ad0de2c9895260e4e5bd5a94b3c7411664f90d5
-F src/test_config.c 46eaf39842cace9d540aeefb50fe24dd3204a622893a97952cbb49c20b2f8b21
+F src/test_config.c 345b8e383f71cecc36d0fa05f2f06639c254b188f98101c3c97749df43037086
F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
F src/test_demovfs.c 3efa2adf4f21e10d95521721687d5ca047aea91fa62dd8cc22ac9e5a9c942383
F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
@@ -840,7 +842,7 @@ F src/vdbe.h c2549a215898a390de6669cfa32adba56f0d7e17ba5a7f7b14506d6fd5f0c36a
F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
F src/vdbeapi.c 80235ac380e9467fec1cb0883354d841f2a771976e766995f7e0c77f845406df
F src/vdbeaux.c 25d685cafe119ff890c94345e884ea558a6b5d823bfa52ba708eb8ff3c70aa71
-F src/vdbeblob.c 13f9287b55b6356b4b1845410382d6bede203ceb29ef69388a4a3d007ffacbe5
+F src/vdbeblob.c 255be187436da38b01f276c02e6a08103489bbe2a7c6c21537b7aecbe0e1f797
F src/vdbemem.c 831a244831eaa45335f9ae276b50a7a82ee10d8c46c2c72492d4eb8c98d94d89
F src/vdbesort.c d0a3c7056c081703c8b6d91ad60f17da5e062a5c64bf568ed0fa1b5f4cae311f
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
@@ -1265,7 +1267,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 6e87c27df3d95c556870187987dff6efdc712b5cea60abedc8ab9215f471907a
+F test/fuzzcheck.c 20be6c96bd0da2e91d25f089a037c1b3f8142211c97104f20629bf15610019e6
F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
@@ -1516,7 +1518,7 @@ F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035c
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
-F test/percentile.test 74e383216a075251512d6ba98beb9dccd6da465e3f73817fc438379e3a628de7
+F test/percentile.test 52ba89d6ee6b65f770972b67dace358bab7cdbd532803d3db157845268e789cd
F test/permutations.test 405542f1d659942994a6b38a9e024cf5cfd23eaa68c806aeb24a72d7c9186e80
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
F test/pragma.test 11cb9310c42f921918f7f563e3c0b6e70f9f9c3a6a1cf12af8fccb6c574f3882
@@ -1625,10 +1627,10 @@ F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8e
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test 490bf9d0c7c9564fea318c46d49369f4690b825b584c9a544dbdccf61bc0babc
-F test/shell2.test 56da24128304c9ab67da2964cc80beff7b35761c446ec6e6e98bff2775b15026
+F test/shell2.test 01a01f76ed98088ce598794fbf5b359e148271541a8ddbf79d21cc353cc67a24
F test/shell3.test db1953a8e59d08e9240b7cc5948878e184f7eb2623591587f8fd1f1a5bd536d8
F test/shell4.test 522fdc628c55eff697b061504fb0a9e4e6dfc5d9087a633ab0f3dd11bcc4f807
-F test/shell5.test bafa4c0b67b7a8027e729970a625c9225cb7ef854acc4e52624c45074faaaddf
+F test/shell5.test 0e5f8ce08206b9998a778cfe1989e20e47839153c05af2da29198150172e22fc
F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bdbb8
F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3
F test/shell8.test aea51ecbcd4494c746b096aeff51d841d04d5f0dc4b62eb42427f16109b87acd
@@ -1713,8 +1715,8 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl 2c203a2dd664298f239f0ec3ce22fbc65b5f021c1e09edbae8452af8a694e052
-F test/testrunner.tcl 5d02deeba7a53baeadae6aa7641d90aac58fdfa3a7bcac85cfcfd752b1aab87c
-F test/testrunner_data.tcl c5ae2b1f9a99210b0600d002fb3af1fee350997cee9416551e83b93501360ebf
+F test/testrunner.tcl 92e3c63072362cd56d7dec2548284425104b5b3a5c0af3371ee2911712d89bf6
+F test/testrunner_data.tcl dbc0bb1c5b912dfd1e32b25d544318e412edd6085bd5fc9e6619cb93a739b786
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -2069,7 +2071,7 @@ F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af
F test/windowB.test aad7c31739999f68a98a813cfd78390918fc70f56d2d925317a1523cab548ecf
F test/windowC.test 6fd75f5bb2f1343d34e470e36e68f0ff638d8a42f6aa7d99471261b31a0d42f2
F test/windowD.test 65cf5a765fb8072450e8a0de2979ce7f09a38d87724fe1280c6444073e3da49b
-F test/windowE.test c98507e0b0d95980ad25845db758557757be2d7054198ccf522c7d277057a3df
+F test/windowE.test d045a5fbaaf50ecac9483e1249dd317ba4f9d189c405a730ba6effdefb87b94f
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
F test/windowfault.test 15094c1529424e62f798bc679e3fe9dfab6e8ba2f7dfe8c923b6248c31660a7c
@@ -2130,8 +2132,8 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
F tool/mkautoconfamal.sh cbdcf993fa83dccbef7fb77b39cdeb31ef9f77d9d88c9e343b58d35ca3898a6a
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
-F tool/mkctimec.tcl 060e9785e9503bf51f8b1b11b542bdeef90fd0ceb0738154f6762acec0c61e5f x
-F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd
+F tool/mkctimec.tcl e3af51acc2ef92062fe6d622de010a27a34b497258a248dada04388b916c61c6 x
+F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
@@ -2148,7 +2150,7 @@ F tool/mktoolzip.tcl c7a9b685f5131d755e7d941cec50cee7f34178b9e34c9a89811eeb08617
F tool/mkvsix.tcl 67b40996a50f985a573278eea32fc5a5eb6110bdf14d33f1d8086e48c69e540a
F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c6115285845
F tool/omittest-msvc.tcl d6b8f501ac1d7798c4126065030f89812379012cad98a1735d6d7221492abc08
-F tool/omittest.tcl e99c9fecc3f7a8ca2fa75d8ec8bdbb5acce33dc69f0c280aae53064693387f65
+F tool/omittest.tcl 5ca5e4e01716d5f35b48b00fd351d929f01fbb98169a5a3cd00baf3d2e2019a9
F tool/opcodesum.tcl 740ed206ba8c5040018988129abbf3089a0ccf4a
F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
F tool/replace.tcl 511c61acfe563dfb58675efb4628bb158a13d48ff8322123ac447e9d25a82d9a
@@ -2210,8 +2212,9 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 164b1e1962aa1e16bdf52e9e86d4cf9c9e09220c0821932ac8e390e82074185f
-R 0ea7d1c943863a7b1da4b295016a8f50
-U larrybr
-Z 8cf571eafc534d90679b77113d98b6b5
+P 49e8b1635f29d9fd0dc2ef6e312fd4129f2283f68e9423d95ff9b55077688ad7 2d783524d1671d988ebb1b21c83de99c9f335963b6c20cf4df612f58c13da913
+R 1f005fca5fe5df8e4ebc8001e171645f
+T +closed 2d783524d1671d988ebb1b21c83de99c9f335963b6c20cf4df612f58c13da913
+U drh
+Z 207a73144d24fc75006dde9e7a085311
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index 47b76c643..f51ba4769 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-2d783524d1671d988ebb1b21c83de99c9f335963b6c20cf4df612f58c13da913
+efc6f3d7e92a25f440fb8d392daf325af5ca7dca104a5bb4bd59f7df93af53b0
diff --git a/src/btree.c b/src/btree.c
index c752b0771..f985ce340 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -5989,7 +5989,7 @@ int sqlite3BtreeIndexMoveto(
&& indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0
&& pIdxKey->errCode==SQLITE_OK
){
- pCur->curFlags &= ~BTCF_ValidOvfl;
+ pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast);
if( !pCur->pPage->isInit ){
return SQLITE_CORRUPT_BKPT;
}
diff --git a/src/ctime.c b/src/ctime.c
index 0ffe2a5bd..fe7849fec 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -295,6 +295,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
"ENABLE_OFFSET_SQL_FUNC",
#endif
+#ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+ "ENABLE_ORDERED_SET_AGGREGATES",
+#endif
#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
"ENABLE_OVERSIZE_CELL_CHECK",
#endif
diff --git a/src/func.c b/src/func.c
index 8fcda11dc..a634a7fbe 100644
--- a/src/func.c
+++ b/src/func.c
@@ -2049,7 +2049,11 @@ static void minMaxFinalize(sqlite3_context *context){
** group_concat(EXPR, ?SEPARATOR?)
** string_agg(EXPR, SEPARATOR)
**
-** The SEPARATOR goes before the EXPR string. This is tragic. The
+** Content is accumulated in GroupConcatCtx.str with the SEPARATOR
+** coming before the EXPR value, except for the first entry which
+** omits the SEPARATOR.
+**
+** It is tragic that the SEPARATOR goes before the EXPR string. The
** groupConcatInverse() implementation would have been easier if the
** SEPARATOR were appended after EXPR. And the order is undocumented,
** so we could change it, in theory. But the old behavior has been
@@ -2153,7 +2157,7 @@ static void groupConcatInverse(
/* pGCC is always non-NULL since groupConcatStep() will have always
** run first to initialize it */
if( ALWAYS(pGCC) ){
- int nVS;
+ int nVS; /* Number of characters to remove */
/* Must call sqlite3_value_text() to convert the argument into text prior
** to invoking sqlite3_value_bytes(), in case the text encoding is UTF16 */
(void)sqlite3_value_text(argv[0]);
diff --git a/src/main.c b/src/main.c
index 5d6212208..ac08eea04 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1926,7 +1926,8 @@ int sqlite3CreateFunc(
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
- SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE);
+ SQLITE_SUBTYPE|SQLITE_INNOCUOUS|
+ SQLITE_RESULT_SUBTYPE|SQLITE_SELFORDER1);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
diff --git a/src/parse.y b/src/parse.y
index 926dd6e7d..a6a5e046d 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -264,6 +264,9 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);}
CURRENT FOLLOWING PARTITION PRECEDING RANGE UNBOUNDED
EXCLUDE GROUPS OTHERS TIES
%endif SQLITE_OMIT_WINDOWFUNC
+%ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+ WITHIN
+%endif SQLITE_ENABLE_ORDERED_SET_AGGREGATES
%ifndef SQLITE_OMIT_GENERATED_COLUMNS
GENERATED ALWAYS
%endif
@@ -1179,6 +1182,65 @@ expr(A) ::= idj(X) LP STAR RP. {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
}
+%ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+%include {
+ /* Generate an expression node that represents an ordered-set aggregate function.
+ **
+ ** SQLite does not do anything special to evaluate ordered-set aggregates. The
+ ** aggregate function itself is expected to do any required ordering on its own.
+ ** This is just syntactic sugar.
+ **
+ ** This syntax: percentile(f) WITHIN GROUP ( ORDER BY y )
+ **
+ ** Is equivalent to: percentile(y,f)
+ **
+ ** The purpose of this function is to generate an Expr node from the first syntax
+ ** into a TK_FUNCTION node that looks like it came from the second syntax.
+ **
+ ** Only functions that have the SQLITE_SELFORDER1 perperty are allowed to do this
+ ** transformation. Because DISTINCT is not allowed in the ordered-set aggregate
+ ** syntax, an error is raised if DISTINCT is used.
+ */
+ static Expr *sqlite3ExprAddOrderedsetFunction(
+ Parse *pParse, /* Parsing context */
+ Token *pFuncname, /* Name of the function */
+ int isDistinct, /* DISTINCT or ALL qualifier */
+ ExprList *pOrig, /* Arguments to the function */
+ Expr *pOrderby /* Expression in the ORDER BY clause */
+ ){
+ ExprList *p; /* Modified argument list */
+ Expr *pExpr; /* Final result */
+ p = sqlite3ExprListAppend(pParse, 0, pOrderby);
+ if( pOrig ){
+ int i;
+ for(i=0; i<pOrig->nExpr; i++){
+ p = sqlite3ExprListAppend(pParse, p, pOrig->a[i].pExpr);
+ pOrig->a[i].pExpr = 0;
+ }
+ sqlite3ExprListDelete(pParse->db, pOrig);
+ }
+ pExpr = sqlite3ExprFunction(pParse, p, pFuncname, 0);
+ if( pParse->nErr==0 ){
+ FuncDef *pDef;
+ u8 enc = ENC(pParse->db);
+ assert( pExpr!=0 ); /* Because otherwise pParse->nErr would not be zero */
+ assert( p!=0 ); /* Because otherwise pParse->nErr would not be zero */
+ pDef = sqlite3FindFunction(pParse->db, pExpr->u.zToken, -2, enc, 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_SELFORDER1)==0 ){
+ sqlite3ErrorMsg(pParse, "%#T() is not an ordered-set aggregate", pExpr);
+ }else if( isDistinct==SF_Distinct ){
+ sqlite3ErrorMsg(pParse, "DISTINCT not allowed on ordered-set aggregate %T()",
+ pFuncname);
+ }
+ }
+ return pExpr;
+ }
+}
+expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP WITHIN GROUP LP ORDER BY expr(E) RP. {
+ A = sqlite3ExprAddOrderedsetFunction(pParse, &X, D, Y, E);
+}
+%endif SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+
%ifndef SQLITE_OMIT_WINDOWFUNC
expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP filter_over(Z). {
A = sqlite3ExprFunction(pParse, Y, &X, D);
@@ -1193,7 +1255,15 @@ expr(A) ::= idj(X) LP STAR RP filter_over(Z). {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
sqlite3WindowAttach(pParse, A, Z);
}
-%endif
+%ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP WITHIN GROUP LP ORDER BY expr(E) RP
+ filter_over(Z). {
+ A = sqlite3ExprAddOrderedsetFunction(pParse, &X, D, Y, E);
+ sqlite3WindowAttach(pParse, A, Z);
+}
+%endif SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+
+%endif SQLITE_OMIT_WINDOWFUNC
term(A) ::= CTIME_KW(OP). {
A = sqlite3ExprFunction(pParse, 0, &OP, 0);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 810ccecc9..5546793c9 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -5615,6 +5615,15 @@ int sqlite3_create_window_function(
** [sqlite3_result_subtype()] should avoid setting this property, as the
** purpose of this property is to disable certain optimizations that are
** incompatible with subtypes.
+**
+** [[SQLITE_SELFORDER1]] <dt>SQLITE_SELFORDER1</dt><dd>
+** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate
+** that internally orders the values provided to the first argument. The
+** ordered-set aggregate SQL notation with a single ORDER BY term can be
+** used to invoke this function. If the ordered-set aggregate notation is
+** used on a function that lacks this flag, then an error is raised. Note
+** that the ordered-set aggregate syntax is only available if SQLite is
+** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option.
** </dd>
** </dl>
*/
@@ -5623,6 +5632,7 @@ int sqlite3_create_window_function(
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
#define SQLITE_RESULT_SUBTYPE 0x001000000
+#define SQLITE_SELFORDER1 0x002000000
/*
** CAPI3REF: Deprecated Functions
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index d1ffce6d8..0dab59f7a 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -658,6 +658,8 @@
#ifdef SQLITE_OMIT_FLOATING_POINT
# define double sqlite_int64
# define float sqlite_int64
+# define fabs(X) ((X)<0?-(X):(X))
+# define sqlite3IsOverflow(X) 0
# define LONGDOUBLE_TYPE sqlite_int64
# ifndef SQLITE_BIG_DBL
# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50)
diff --git a/src/test_config.c b/src/test_config.c
index 58de5a462..49527861a 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -189,6 +189,14 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "offset_sql_func","0",TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+ Tcl_SetVar2(interp, "sqlite_options",
+ "ordered_set_aggregates","1",TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options",
+ "ordered_set_aggregates","0",TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
Tcl_SetVar2(interp, "sqlite_options", "preupdate", "1", TCL_GLOBAL_ONLY);
#else
@@ -341,6 +349,14 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "columnmetadata", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ENABLE_ORDEREDSETFUNC
+ Tcl_SetVar2(interp, "sqlite_options", "ordered_set_funcs", "1",
+ TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "ordered_set_funcs", "0",
+ TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
Tcl_SetVar2(interp, "sqlite_options", "oversize_cell_check", "1",
TCL_GLOBAL_ONLY);
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 522447dbc..6cb36da37 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -167,6 +167,11 @@ int sqlite3_blob_open(
pTab = 0;
sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
}
+ if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){
+ pTab = 0;
+ sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s",
+ zTable);
+ }
#ifndef SQLITE_OMIT_VIEW
if( pTab && IsView(pTab) ){
pTab = 0;
diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c
index eaeb54d87..ea0d949b5 100644
--- a/test/fuzzcheck.c
+++ b/test/fuzzcheck.c
@@ -159,10 +159,11 @@ static struct GlobalVars {
} g;
/*
-** Include the external vt02.c and randomjson.c modules.
+** Include various extensions.
*/
extern int sqlite3_vt02_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_randomjson_init(sqlite3*,char**,const sqlite3_api_routines*);
+extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
/*
@@ -1329,7 +1330,8 @@ int runCombinedDbSqlInput(
/* Add the vt02 virtual table */
sqlite3_vt02_init(cx.db, 0, 0);
- /* Add the random_json() and random_json5() functions */
+ /* Activate extensions */
+ sqlite3_percentile_init(cx.db, 0, 0);
sqlite3_randomjson_init(cx.db, 0, 0);
/* Add support for sqlite_dbdata and sqlite_dbptr virtual tables used
diff --git a/test/percentile.test b/test/percentile.test
index ab5b4883f..a6a29da34 100644
--- a/test/percentile.test
+++ b/test/percentile.test
@@ -9,7 +9,8 @@
#
#***********************************************************************
# This file implements regression tests for SQLite library. The
-# focus of this file is percentile.c extension
+# focus of this file is percentile.c extension. This also tests
+# the SQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option.
#
set testdir [file dirname $argv0]
@@ -25,35 +26,65 @@ do_test percentile-1.0 {
}
execsql {SELECT percentile(x,0) FROM t1}
} {1.0}
-foreach {in out} {
- 100 11.0
- 50 8.0
- 12.5 4.0
- 15 4.4
- 20 5.2
- 80 11.0
- 89 11.0
+foreach {in out disc} {
+ 100 11.0 11.0
+ 50 8.0 8.0
+ 12.5 4.0 4.0
+ 15 4.4 4.0
+ 20 5.2 4.0
+ 80 11.0 11.0
+ 89 11.0 11.0
} {
- do_test percentile-1.1.$in {
+ do_test percentile-1.1.$in.1 {
execsql {SELECT percentile(x,$in) FROM t1}
} $out
+ do_test percentile-1.1.$in.2 {
+ execsql {SELECT percentile_cont(x,$in*0.01) FROM t1}
+ } $out
+ do_test percentile-1.1.$in.3 {
+ execsql {SELECT percentile_disc(x,$in*0.01) FROM t1}
+ } $disc
+ if {$in==50} {
+ do_test percentile-1.1.$in.4 {
+ execsql {SELECT median(x) FROM t1}
+ } $out
+ }
+ ifcapable ordered_set_aggregates {
+ do_test percentile-1.1.$in.5 {
+ execsql {SELECT percentile($in)WITHIN GROUP(ORDER BY x) FROM t1}
+ } $out
+ do_test percentile-1.1.$in.6 {
+ execsql {SELECT percentile_cont($in*0.01) WITHIN GROUP(ORDER BY x)
+ FROM t1}
+ } $out
+ do_test percentile-1.1.$in.7 {
+ execsql {SELECT percentile_disc($in*0.01) WITHIN GROUP(ORDER BY x)
+ FROM t1}
+ } $disc
+ if {$in==50} {
+ do_test percentile-1.1.$in.8 {
+ execsql {SELECT median() WITHIN GROUP (ORDER BY x) FROM t1}
+ } $out
+ }
+ }
}
do_execsql_test percentile-1.1.median {
SELECT median(x) FROM t1;
} 8.0
-
-foreach {in out} {
- 1.0 11.0
- 0.5 8.0
- 0.125 4.0
- 0.15 4.4
- 0.2 5.2
- 0.8 11.0
- 0.89 11.0
-} {
- do_test percentile-1.1b-$in {
- execsql {SELECT percentile_cont(x,$in) FROM t1}
- } $out
+ifcapable ordered_set_aggregates {
+ do_execsql_test percentile-1.1.median {
+ SELECT median() WITHIN GROUP (ORDER BY x) FROM t1;
+ } 8.0
+ do_execsql_test percentile-1.1.distinct.1 {
+ SELECT median(DISTINCT x) FROM t1;
+ } 7.0
+ do_catchsql_test percentile-1.1.distinct.2 {
+ SELECT percentile(DISTINCT 50) WITHIN GROUP (ORDER BY x) FROM t1;
+ } {1 {DISTINCT not allowed on ordered-set aggregate percentile()}}
+} else {
+ do_catchsql_test percentile-1.1.median {
+ SELECT median() WITHIN GROUP (ORDER BY x) FROM t1;
+ } {1 {near "(": syntax error}}
}
# Add some NULL values.
@@ -61,28 +92,69 @@ foreach {in out} {
do_test percentile-1.2 {
execsql {INSERT INTO t1 VALUES(NULL),(NULL);}
} {}
-foreach {in out} {
- 100 11.0
- 50 8.0
- 12.5 4.0
- 15 4.4
- 20 5.2
- 80 11.0
- 89 11.0
+foreach {in out disc} {
+ 100 11.0 11.0
+ 50 8.0 8.0
+ 12.5 4.0 4.0
+ 15 4.4 4.0
+ 20 5.2 4.0
+ 80 11.0 11.0
+ 89 11.0 11.0
} {
- do_test percentile-1.3.$in {
+ do_test percentile-1.3.$in.1 {
execsql {SELECT percentile(x,$in) FROM t1}
} $out
+ do_test percentile-1.3.$in.2 {
+ execsql {SELECT percentile_cont(x,$in*0.01) FROM t1}
+ } $out
+ do_test percentile-1.3.$in.3 {
+ execsql {SELECT percentile_disc(x,$in*0.01) FROM t1}
+ } $disc
+ if {$in==50} {
+ do_test percentile-1.3.$in.4 {
+ execsql {SELECT median(x) FROM t1}
+ } $out
+ }
+ ifcapable ordered_set_aggregates {
+ do_test percentile-1.3.$in.5 {
+ execsql {SELECT percentile($in)WITHIN GROUP(ORDER BY x) FROM t1}
+ } $out
+ do_test percentile-1.3.$in.6 {
+ execsql {SELECT percentile_cont($in*0.01) WITHIN GROUP(ORDER BY x)
+ FROM t1}
+ } $out
+ do_test percentile-1.3.$in.7 {
+ execsql {SELECT percentile_disc($in*0.01) WITHIN GROUP(ORDER BY x)
+ FROM t1}
+ } $disc
+ if {$in==50} {
+ do_test percentile-1.3.$in.8 {
+ execsql {SELECT median() WITHIN GROUP (ORDER BY x) FROM t1}
+ } $out
+ }
+ }
}
# The second argument to percentile can change some, but not much.
#
-do_test percentile-1.4 {
+do_test percentile-1.4.1 {
catchsql {SELECT round(percentile(x, 15+0.000001*rowid),1) FROM t1}
} {0 4.4}
-do_test percentile-1.5 {
- catchsql {SELECT round(percentile(x, 15+0.1*rowid),1) FROM t1}
-} {1 {2nd argument to percentile() is not the same for all input rows}}
+do_test percentile-1.4.2 {
+ catchsql {SELECT round(percentile_cont(x,(15+0.000001*rowid)*0.01),1) FROM t1}
+} {0 4.4}
+do_test percentile-1.4.3 {
+ catchsql {SELECT percentile_disc(x, (15+0.000001*rowid)*0.01) FROM t1}
+} {0 4.0}
+do_test percentile-1.5.1 {
+ catchsql {SELECT percentile(x, 15+0.1*rowid) FROM t1}
+} {1 {the fraction argument to percentile() is not the same for all input rows}}
+do_test percentile-1.5.2 {
+ catchsql {SELECT percentile_cont(x, (15+0.1*rowid)*0.01) FROM t1}
+} {1 {the fraction argument to percentile_cont() is not the same for all input rows}}
+do_test percentile-1.5.3 {
+ catchsql {SELECT percentile_disc(x, (15+0.1*rowid)*0.01) FROM t1}
+} {1 {the fraction argument to percentile_disc() is not the same for all input rows}}
# Input values in a random order
#
@@ -92,65 +164,152 @@ do_test percentile-1.6 {
INSERT INTO t2 SELECT x+0.0 FROM t1 ORDER BY random();
}
} {}
-foreach {in out} {
- 100 11.0
- 50 8.0
- 12.5 4.0
- 15 4.4
- 20 5.2
- 80 11.0
- 89 11.0
+foreach {in out disc} {
+ 100 11.0 11.0
+ 50 8.0 8.0
+ 12.5 4.0 4.0
+ 15 4.4 4.0
+ 20 5.2 4.0
+ 80 11.0 11.0
+ 89 11.0 11.0
} {
- do_test percentile-1.7.$in {
+ do_test percentile-1.7.$in.1 {
execsql {SELECT percentile(x,$in) FROM t2}
} $out
+ do_test percentile-1.7.$in.2 {
+ execsql {SELECT percentile_cont(x,$in*0.01) FROM t2}
+ } $out
+ do_test percentile-1.7.$in.3 {
+ execsql {SELECT percentile_disc(x,$in*0.01) FROM t2}
+ } $disc
+ if {$in==50} {
+ do_test percentile-1.7.$in.4 {
+ execsql {SELECT median(x) FROM t2}
+ } $out
+ }
+ ifcapable ordered_set_aggregates {
+ do_test percentile-1.7.$in.5 {
+ execsql {SELECT percentile($in)WITHIN GROUP(ORDER BY x) FROM t2}
+ } $out
+ do_test percentile-1.7.$in.6 {
+ execsql {SELECT percentile_cont($in*0.01) WITHIN GROUP(ORDER BY x)
+ FROM t2}
+ } $out
+ do_test percentile-1.7.$in.7 {
+ execsql {SELECT percentile_disc($in*0.01) WITHIN GROUP(ORDER BY x)
+ FROM t2}
+ } $disc
+ if {$in==50} {
+ do_test percentile-1.7.$in.8 {
+ execsql {SELECT median() WITHIN GROUP (ORDER BY x) FROM t2}
+ } $out
+ }
+ }
}
# Wrong number of arguments
#
-do_test percentile-1.8 {
+do_test percentile-1.8.1 {
catchsql {SELECT percentile(x,0,1) FROM t1}
} {1 {wrong number of arguments to function percentile()}}
-do_test percentile-1.9 {
+do_test percentile-1.8.2 {
+ catchsql {SELECT percentile_cont(x,0,1) FROM t1}
+} {1 {wrong number of arguments to function percentile_cont()}}
+do_test percentile-1.8.3 {
+ catchsql {SELECT percentile_disc(x,0,1) FROM t1}
+} {1 {wrong number of arguments to function percentile_disc()}}
+do_test percentile-1.8.4 {
+ catchsql {SELECT median(x,0) FROM t1}
+} {1 {wrong number of arguments to function median()}}
+ifcapable ordered_set_aggregates {
+ do_test percentile-1.8.5 {
+ catchsql {SELECT percentile(0,1) WITHIN GROUP(ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function percentile()}}
+ do_test percentile-1.8.2 {
+ catchsql {SELECT percentile_cont(0,1)WITHIN GROUP (ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function percentile_cont()}}
+ do_test percentile-1.8.3 {
+ catchsql {SELECT percentile_disc(0,1)WITHIN GROUP (ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function percentile_disc()}}
+ do_test percentile-1.8.4 {
+ catchsql {SELECT median(x) WITHIN GROUP (ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function median()}}
+}
+do_test percentile-1.9.1 {
catchsql {SELECT percentile(x) FROM t1}
} {1 {wrong number of arguments to function percentile()}}
+do_test percentile-1.9.2 {
+ catchsql {SELECT percentile_cont(x) FROM t1}
+} {1 {wrong number of arguments to function percentile_cont()}}
+do_test percentile-1.9.3 {
+ catchsql {SELECT percentile_disc(x) FROM t1}
+} {1 {wrong number of arguments to function percentile_disc()}}
+do_test percentile-1.9.4 {
+ catchsql {SELECT median() FROM t1}
+} {1 {wrong number of arguments to function median()}}
+ifcapable ordered_set_aggregates {
+ do_test percentile-1.9.5 {
+ catchsql {SELECT percentile() WITHIN GROUP(ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function percentile()}}
+ do_test percentile-1.9.6 {
+ catchsql {SELECT percentile_cont()WITHIN GROUP (ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function percentile_cont()}}
+ do_test percentile-1.9.7 {
+ catchsql {SELECT percentile_disc()WITHIN GROUP (ORDER BY x) FROM t1}
+ } {1 {wrong number of arguments to function percentile_disc()}}
+}
# Second argument must be numeric
#
do_test percentile-1.10 {
catchsql {SELECT percentile(x,null) FROM t1}
-} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+} {1 {the fraction argument to percentile() is not between 0.0 and 100.0}}
do_test percentile-1.11 {
catchsql {SELECT percentile(x,'fifty') FROM t1}
-} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+} {1 {the fraction argument to percentile() is not between 0.0 and 100.0}}
do_test percentile-1.12 {
catchsql {SELECT percentile(x,x'3530') FROM t1}
-} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
-do_test percentile-1.12b {
- catchsql {SELECT percentile_cont(x,x'3530') FROM t1}
-} {1 {2nd argument to percentile_cont() is not a number between 0.0 and 1.0}}
+} {1 {the fraction argument to percentile() is not between 0.0 and 100.0}}
# Second argument is out of range
#
do_test percentile-1.13 {
catchsql {SELECT percentile(x,-0.0000001) FROM t1}
-} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+} {1 {the fraction argument to percentile() is not between 0.0 and 100.0}}
do_test percentile-1.14 {
catchsql {SELECT percentile(x,100.0000001) FROM t1}
-} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
-do_test percentile-1.14b {
+} {1 {the fraction argument to percentile() is not between 0.0 and 100.0}}
+do_test percentile-1.14.2 {
catchsql {SELECT percentile_cont(x,1.0000001) FROM t1}
-} {1 {2nd argument to percentile_cont() is not a number between 0.0 and 1.0}}
+} {1 {the fraction argument to percentile_cont() is not between 0.0 and 1.0}}
+do_test percentile-1.14.3 {
+ catchsql {SELECT percentile_disc(x,1.0000001) FROM t1}
+} {1 {the fraction argument to percentile_disc() is not between 0.0 and 1.0}}
# First argument is not NULL and is not NUMERIC
#
-do_test percentile-1.15 {
+do_test percentile-1.15.1 {
catchsql {
BEGIN;
UPDATE t1 SET x='50' WHERE x IS NULL;
SELECT percentile(x, 50) FROM t1;
}
-} {1 {1st argument to percentile() is not numeric}}
+} {1 {input to percentile() is not numeric}}
+do_test percentile-1.15.2 {
+ catchsql {
+ SELECT percentile_cont(x, 0.50) FROM t1;
+ }
+} {1 {input to percentile_cont() is not numeric}}
+do_test percentile-1.15.3 {
+ catchsql {
+ SELECT percentile_disc(x, 0.50) FROM t1;
+ }
+} {1 {input to percentile_disc() is not numeric}}
+do_test percentile-1.15.4 {
+ catchsql {
+ SELECT median(x) FROM t1;
+ }
+} {1 {input to median() is not numeric}}
do_test percentile-1.16 {
catchsql {
ROLLBACK;
@@ -158,7 +317,7 @@ do_test percentile-1.16 {
UPDATE t1 SET x=x'3530' WHERE x IS NULL;
SELECT percentile(x, 50) FROM t1;
}
-} {1 {1st argument to percentile() is not numeric}}
+} {1 {input to percentile() is not numeric}}
do_test percentile-1.17 {
catchsql {
ROLLBACK;
@@ -186,7 +345,7 @@ do_test percentile-1.19 {
# Infinity as an input
#
-do_test percentile-1.20 {
+do_test percentile-1.20.1 {
catchsql {
DELETE FROM t1;
INSERT INTO t1 SELECT x+0.0 FROM t2;
@@ -194,6 +353,43 @@ do_test percentile-1.20 {
SELECT percentile(x,50) from t1;
}
} {1 {Inf input to percentile()}}
+do_test percentile-1.20.2 {
+ catchsql {
+ SELECT percentile_cont(x,0.50) from t1;
+ }
+} {1 {Inf input to percentile_cont()}}
+do_test percentile-1.20.3 {
+ catchsql {
+ SELECT percentile_disc(x,0.50) from t1;
+ }
+} {1 {Inf input to percentile_disc()}}
+do_test percentile-1.20.4 {
+ catchsql {
+ SELECT median(x) from t1;
+ }
+} {1 {Inf input to median()}}
+ifcapable ordered_set_aggregates {
+ do_test percentile-1.20.5 {
+ catchsql {
+ SELECT percentile(50) WITHIN GROUP (ORDER BY x) from t1;
+ }
+ } {1 {Inf input to percentile()}}
+ do_test percentile-1.20.6 {
+ catchsql {
+ SELECT percentile_cont(0.50) WITHIN GROUP (ORDER BY x) from t1;
+ }
+ } {1 {Inf input to percentile_cont()}}
+ do_test percentile-1.20.7 {
+ catchsql {
+ SELECT percentile_disc(0.50) WITHIN GROUP(ORDER BY X) from t1;
+ }
+ } {1 {Inf input to percentile_disc()}}
+ do_test percentile-1.20.8 {
+ catchsql {
+ SELECT median() WITHIN GROUP (ORDER BY x) from t1;
+ }
+ } {1 {Inf input to median()}}
+}
do_test percentile-1.21 {
catchsql {
UPDATE t1 SET x=-1.0e300*1.0e300 WHERE rowid=5;
@@ -229,4 +425,172 @@ ifcapable vtab {
}
}
+# median() as a window function. (2024-08-31)
+#
+do_execsql_test percentile-3.0 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
+ INSERT INTO t1 VALUES (1, 'A', 'one', 8.4),
+ (2, 'B', 'two', 7.1),
+ (3, 'C', 'three', 5.9),
+ (4, 'D', 'one', 11.0),
+ (5, 'E', 'two', 12.5),
+ (6, 'F', 'three', 0.0),
+ (7, 'G', 'one', 2.7);
+}
+foreach {id oba expr} {
+ 1 0 "median(d)"
+ 2 0 "percentile(d,50)"
+ 3 0 "percentile_cont(d,0.5)"
+ 4 1 "median() WITHIN GROUP (ORDER BY d)"
+ 5 1 "percentile(50) WITHIN GROUP (ORDER BY d)"
+ 6 1 "percentile_cont(0.5) WITHIN GROUP (ORDER BY d)"
+} {
+ if {$oba} {
+ ifcapable !ordered_set_aggregates break
+ }
+ set sql "SELECT a, b, c, d, \
+ group_concat(b,'.') OVER w1 AS 'elements', \
+ $expr OVER w1 AS 'median' \
+ FROM t1 \
+ WINDOW w1 AS (ORDER BY c, a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)"
+ do_execsql_test percentile-3.$id.1 $sql {
+ 1 A one 8.4 A.D 9.7
+ 4 D one 11.0 A.D.G 8.4
+ 7 G one 2.7 D.G.C 5.9
+ 3 C three 5.9 G.C.F 2.7
+ 6 F three 0.0 C.F.B 5.9
+ 2 B two 7.1 F.B.E 7.1
+ 5 E two 12.5 B.E 9.8
+ }
+
+ set sql "SELECT a, b, c, d, \
+ group_concat(b,'.') OVER w1 AS 'elements', \
+ $expr OVER w1 AS 'median' \
+ FROM t1 \
+ WINDOW w1 AS (ORDER BY c, a \
+ ROWS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING)"
+ do_execsql_test percentile-3.$id.2 $sql {
+ 1 A one 8.4 A.D 9.7
+ 4 D one 11.0 A.D.G 8.4
+ 7 G one 2.7 A.D.G.C 7.15
+ 3 C three 5.9 A.D.G.C.F 5.9
+ 6 F three 0.0 A.D.G.C.F.B 6.5
+ 2 B two 7.1 A.D.G.C.F.B.E 7.1
+ 5 E two 12.5 A.D.G.C.F.B.E 7.1
+ }
+
+ set sql "SELECT a, b, c, d, \
+ group_concat(b,'.') OVER w1 AS 'elements', \
+ $expr OVER w1 AS 'median' \
+ FROM t1 \
+ WINDOW w1 AS (ORDER BY c, a \
+ ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING)"
+ do_execsql_test percentile-3.$id.3 $sql {
+ 1 A one 8.4 A.D.G.C.F.B.E 7.1
+ 4 D one 11.0 A.D.G.C.F.B.E 7.1
+ 7 G one 2.7 D.G.C.F.B.E 6.5
+ 3 C three 5.9 G.C.F.B.E 5.9
+ 6 F three 0.0 C.F.B.E 6.5
+ 2 B two 7.1 F.B.E 7.1
+ 5 E two 12.5 B.E 9.8
+ }
+}
+
+# Test case adapted from examples shown at
+# https://database.guide/3-functions-to-calculate-the-median-in-sql/
+#
+do_execsql_test percential-4.0 {
+ CREATE TABLE products(
+ vendorId INT,
+ productId INTEGER PRIMARY KEY,
+ productName REAL,
+ price REAL
+ );
+ INSERT INTO products VALUES
+ (1001, 17, 'Left-handed screwdriver', 25.99),
+ (1001, 49, 'Right-handed screwdriver', 25.99),
+ (1001, 216, 'Long weight (blue)', 14.75),
+ (1001, 31, 'Long weight (green)', 11.99),
+ (1002, 37, 'Sledge hammer', 33.49),
+ (1003, 7, 'Chainsaw', 245.00),
+ (1003, 8, 'Straw dog box', 55.99),
+ (1003, 12, 'Hammock', 11.01),
+ (1004, 113, 'Teapot', 12.45),
+ (1004, 117, 'Bottomless coffee mug', 9.99);
+}
+do_execsql_test percentile-4.1 {
+ SELECT VendorId, ProductId, /* ProductName,*/ Price,
+ avg(price) OVER (PARTITION BY vendorId) AS "Average",
+ median(price) OVER (PARTITION BY vendorId) AS "Median"
+ FROM products
+ ORDER BY vendorId, productId;
+} {
+ 1001 17 25.99 19.68 20.37
+ 1001 31 11.99 19.68 20.37
+ 1001 49 25.99 19.68 20.37
+ 1001 216 14.75 19.68 20.37
+ 1002 37 33.49 33.49 33.49
+ 1003 7 245.0 104.0 55.99
+ 1003 8 55.99 104.0 55.99
+ 1003 12 11.01 104.0 55.99
+ 1004 113 12.45 11.22 11.22
+ 1004 117 9.99 11.22 11.22
+}
+do_execsql_test percentile-4.2 {
+ SELECT vendorId, median(price) FROM products
+ GROUP BY 1 ORDER BY 1;
+} {1001 20.37 1002 33.49 1003 55.99 1004 11.22}
+
+do_execsql_test percentile-5.0 {
+ CREATE TABLE user(name TEXT, class TEXT, cost REAL);
+ INSERT INTO user VALUES
+ ('Alice', 'Y', 3578.27),
+ ('Bob', 'X', 3399.99),
+ ('Cindy', 'Z', 699.10),
+ ('Dave', 'Y', 3078.27),
+ ('Emma', 'Z', 2319.99),
+ ('Fred', 'Y', 539.99),
+ ('Gina', 'X', 2320.49),
+ ('Hank', 'W', 24.99),
+ ('Irma', 'W', 24.99),
+ ('Jake', 'X', 2234.99),
+ ('Kim', 'Y', 4319.99),
+ ('Liam', 'X', 4968.59),
+ ('Mia', 'W', 59.53),
+ ('Nate', 'W', 23.50);
+}
+do_execsql_test percentile-5.1 {
+ SELECT name, class, cost,
+ percentile(cost, 0) OVER w1 AS 'P0',
+ percentile(cost, 25) OVER w1 AS 'P1',
+ percentile(cost, 50) OVER w1 AS 'P2',
+ percentile(cost, 75) OVER w1 AS 'P3',
+ percentile(cost, 100) OVER w1 AS 'P4'
+ FROM user
+ WINDOW w1 AS (PARTITION BY class)
+ ORDER BY class, cost;
+} {
+ Nate W 23.5 23.5 24.6175 24.99 33.625 59.53
+ Hank W 24.99 23.5 24.6175 24.99 33.625 59.53
+ Irma W 24.99 23.5 24.6175 24.99 33.625 59.53
+ Mia W 59.53 23.5 24.6175 24.99 33.625 59.53
+ Jake X 2234.99 2234.99 2299.115 2860.24 3792.14 4968.59
+ Gina X 2320.49 2234.99 2299.115 2860.24 3792.14 4968.59
+ Bob X 3399.99 2234.99 2299.115 2860.24 3792.14 4968.59
+ Liam X 4968.59 2234.99 2299.115 2860.24 3792.14 4968.59
+ Fred Y 539.99 539.99 2443.7 3328.27 3763.7 4319.99
+ Dave Y 3078.27 539.99 2443.7 3328.27 3763.7 4319.99
+ Alice Y 3578.27 539.99 2443.7 3328.27 3763.7 4319.99
+ Kim Y 4319.99 539.99 2443.7 3328.27 3763.7 4319.99
+ Cindy Z 699.1 699.1 1104.3225 1509.545 1914.7675 2319.99
+ Emma Z 2319.99 699.1 1104.3225 1509.545 1914.7675 2319.99
+}
+
+# Fuzzer find.
+do_execsql_test percentile-6.0 {
+ WITH RECURSIVE c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<12)
+ SELECT median(iif(n%2,0.1,1.0)) FROM c;
+} 0.55
+
finish_test
diff --git a/test/shell2.test b/test/shell2.test
index c6c27d216..ee5ae4bdd 100644
--- a/test/shell2.test
+++ b/test/shell2.test
@@ -217,6 +217,7 @@ do_test shell2-1.4.9 {
done
2}}
+ifcapable vtab {
# Verify that generate_series stays sane near 64-bit range boundaries.
# See overflow report at https://sqlite.org/forum/forumpost/5d34ce5280
do_test shell2-1.4.10 {
@@ -247,7 +248,9 @@ do_test shell2-1.4.10 {
0
1
2}}
+} ;# ifcapable vtab
+ifcapable vtab {
# Bug discovered while messing around, .import hangs with
# bit 7 set in column separator.
do_test shell2-1.4.11 {
@@ -262,6 +265,7 @@ do_test shell2-1.4.11 {
.import dummy.csv t
SELECT count(*) FROM t;}]]
} {0 1}
+} ;# ifcapable vtab
# Bug from forum post 7cbe081746dd3803
# Keywords as column names were producing an error message.
diff --git a/test/shell5.test b/test/shell5.test
index 31d5449fd..8eb905974 100644
--- a/test/shell5.test
+++ b/test/shell5.test
@@ -84,6 +84,14 @@ do_test shell5-1.4.1 {
.import FOO t1}]
} {1 {Error: cannot open "FOO"}}
+# the remainder of these test cases require virtual tables.
+#
+ifcapable !vtab {
+ puts "Skipping subsequent tests due to SQLITE_OMIT_VIRTUALTABLE"
+ finish_test
+ return
+}
+
# empty import file
do_test shell5-1.4.2 {
forcedelete shell5.csv
diff --git a/test/testrunner.tcl b/test/testrunner.tcl
index d3a2c1f4c..547dc0898 100644
--- a/test/testrunner.tcl
+++ b/test/testrunner.tcl
@@ -89,7 +89,9 @@ Other PERMUTATION arguments must be run using testfixture, not tclsh:
If no PATTERN arguments are present, all tests specified by the PERMUTATION
are run. Otherwise, each pattern is interpreted as a glob pattern. Only
those tcl tests for which the final component of the filename matches at
-least one specified pattern are run.
+least one specified pattern are run. The glob wildcard '*' is prepended
+to the pattern if it does not start with '^' and appended to every
+pattern that does not end with '$'.
If no PATTERN arguments are present, then various fuzztest, threadtest
and other tests are run as part of the "release" permutation. These are
@@ -277,10 +279,12 @@ set TRG(schema) {
priority INTEGER NOT NULL, -- higher priority jobs may run earlier
/* Fields updated as jobs run */
- starttime INTEGER,
- endtime INTEGER,
+ starttime INTEGER, -- Start time (milliseconds since 1970)
+ endtime INTEGER, -- End time
state TEXT CHECK( state IN ('','ready','running','done','failed','omit') ),
- output TEXT
+ ntest INT, -- Number of test cases run
+ nerr INT, -- Number of errors reported
+ output TEXT -- test output
);
CREATE TABLE config(
@@ -352,8 +356,8 @@ if {([llength $argv]==2 || [llength $argv]==1)
sqlite3 mydb $TRG(dbname)
if {[llength $argv]==2} {
set param [lindex $argv 1]
- if {[string is integer $param]==0 || $param<1 || $param>128} {
- puts stderr "parameter must be an integer between 1 and 128"
+ if {[string is integer $param]==0 || $param<0 || $param>128} {
+ puts stderr "parameter must be an integer between 0 and 128"
exit 1
}
@@ -389,14 +393,36 @@ if {[string compare -nocase script [lindex $argv 0]]==0} {
exit
}
+# Compute an elapse time string MM:SS or HH:MM:SS based on the
+# number of milliseconds in the argument.
+#
+proc elapsetime {ms} {
+ set s [expr {int(($ms+500.0)*0.001)}]
+ set hr [expr {$s/3600}]
+ set mn [expr {($s/60)%60}]
+ set sc [expr {$s%60}]
+ if {$hr>0} {
+ return [format %02d:%02d:%02d $hr $mn $sc]
+ } else {
+ return [format %02d:%02d $mn $sc]
+ }
+}
+
# Helper routine for show_status
#
proc display_job {jobdict {tm ""}} {
array set job $jobdict
- set dfname [format %-60s $job(displayname)]
+ if {[string length $job(displayname)]>65} {
+ set dfname [format %.65s... $job(displayname)]
+ } else {
+ set dfname [format %-68s $job(displayname)]
+ }
set dtm ""
if {$tm!=""} {
- set dtm [format %-10s "\[[expr {$tm-$job(starttime)}]ms\]"]
+ set dtm [expr {$tm-$job(starttime)}]
+ set dtm [format %8s [elapsetime $dtm]]
+ } else {
+ set dtm [format %8s ""]
}
puts " $dfname $dtm"
}
@@ -431,6 +457,11 @@ proc show_status {db cls} {
incr S($state) $cnt
incr total $cnt
}
+ set nt 0
+ set ne 0
+ $db eval {
+ SELECT sum(ntest) AS nt, sum(nerr) AS ne FROM jobs HAVING nt>0
+ } break
set fin [expr $S(done)+$S(failed)]
if {$cmdline!=""} {set cmdline " $cmdline"}
@@ -439,22 +470,25 @@ proc show_status {db cls} {
# overwrite.
puts -nonewline "\033\[H"
flush stdout
- set clreol "\033\[K"
- } else {
- set clreol ""
}
- set f ""
- if {$S(failed)>0} {
- set f "$S(failed) FAILED, "
- }
- puts "Command line: \[testrunner.tcl$cmdline\]$clreol"
- puts "Jobs: $nJob "
- puts "Summary: ${tm}ms, ($fin/$total) finished,\
- ${f}$S(running) running "
+ puts [format %-79.79s "Command: \[testrunner.tcl$cmdline\]"]
+ puts [format %-79.79s "Summary: [elapsetime $tm], $fin/$total jobs,\
+ $ne errors, $nt tests"]
set srcdir [file dirname [file dirname $TRG(info_script)]]
+ set line "Running: $S(running) (max: $nJob)"
+ if {$S(running)>0 && $fin>100 && $fin>0.05*$total} {
+ # Only estimate the time remaining after completing at least 100
+ # jobs amounting to 10% of the total. Never estimate less than
+ # 2% of the total time used so far.
+ set tmleft [expr {($tm/$fin)*($total-$fin)}]
+ if {$tmleft<0.02*$tm} {
+ set tmleft [expr {$tm*0.02}]
+ }
+ append line " est time left [elapsetime $tmleft]"
+ }
+ puts [format %-79.79s $line]
if {$S(running)>0} {
- puts "Running: "
$db eval {
SELECT * FROM jobs WHERE state='running' ORDER BY starttime
} job {
@@ -462,15 +496,30 @@ proc show_status {db cls} {
}
}
if {$S(failed)>0} {
- puts "Failures: "
+ # $toshow is the number of failures to report. In $cls mode,
+ # status tries to limit the number of failure reported so that
+ # the status display does not overflow a 24-line terminal. It will
+ # always show at least the most recent 4 failures, even if an overflow
+ # is needed. No limit is imposed for a status within $cls.
+ #
+ if {$cls && $S(failed)>18-$S(running)} {
+ set toshow [expr {18-$S(running)}]
+ if {$toshow<4} {set toshow 4}
+ set shown " (must recent $toshow shown)"
+ } else {
+ set toshow $S(failed)
+ set shown ""
+ }
+ puts [format %-79s "Failed: $S(failed) $shown"]
$db eval {
- SELECT * FROM jobs WHERE state='failed' ORDER BY starttime
+ SELECT * FROM jobs WHERE state='failed'
+ ORDER BY endtime DESC LIMIT $toshow
} job {
display_job [array get job]
}
set nOmit [$db one {SELECT count(*) FROM jobs WHERE state='omit'}]
if {$nOmit} {
- puts "$nOmit jobs omitted due to failures$clreol"
+ puts [format %-79s " ... $nOmit jobs omitted due to failures"]
}
}
if {$cls} {
@@ -842,7 +891,18 @@ proc add_tcl_jobs {build config patternlist {shelldepid ""}} {
if {[llength $patternlist]>0} {
set bMatch 0
foreach p $patternlist {
- if {[string match $p [file tail $f]]} {
+ set p [string trim $p *]
+ if {[string index $p 0]=="^"} {
+ set p [string range $p 1 end]
+ } else {
+ set p "*$p"
+ }
+ if {[string index $p end]=="\$"} {
+ set p [string range $p 0 end-1]
+ } else {
+ set p "$p*"
+ }
+ if {[string match $p "$config [file tail $f]"]} {
set bMatch 1
break
}
@@ -1068,7 +1128,7 @@ proc add_jobs_from_cmdline {patternlist} {
if {[regexp "\\y$b\\y" $TRG(omitconfig)]} continue
set bld [add_build_job $b $TRG(testfixture)]
foreach c [trd_configs $TRG(platform) $b] {
- add_tcl_jobs $bld $c $patternlist
+ add_tcl_jobs $bld $c $patternlist SHELL
}
if {$patternlist==""} {
@@ -1080,6 +1140,15 @@ proc add_jobs_from_cmdline {patternlist} {
}
}
}
+
+ if {[trdb one "SELECT EXISTS(SELECT 1
+ FROM jobs WHERE depid='SHELL')"]} {
+ set sbld [add_shell_build_job $b [lindex $bld 1] [lindex $bld 0]]
+ set sbldid [lindex $sbld 0]
+ trdb eval {
+ UPDATE jobs SET depid=$sbldid WHERE depid='SHELL'
+ }
+ }
}
}
@@ -1120,15 +1189,25 @@ proc make_new_testset {} {
}
proc mark_job_as_finished {jobid output state endtm} {
+ set ntest 1
+ set nerr 0
+ if {$endtm>0} {
+ if {[regexp {\y(\d+) errors out of (\d+) tests} $output all a b]} {
+ set nerr $a
+ set ntest $b
+ }
+ }
r_write_db {
if {$state=="failed"} {
set childstate omit
+ if {$nerr<=0} {set nerr 1}
} else {
set childstate ready
}
trdb eval {
UPDATE jobs
- SET output=$output, state=$state, endtime=$endtm
+ SET output=$output, state=$state, endtime=$endtm,
+ ntest=$ntest, nerr=$nerr
WHERE jobid=$jobid;
UPDATE jobs SET state=$childstate WHERE depid=$jobid;
}
@@ -1371,6 +1450,17 @@ proc run_testset {} {
puts "\nTest database is $TRG(dbname)"
puts "Test log is $TRG(logname)"
+ trdb eval {
+ SELECT sum(ntest) AS totaltest,
+ sum(nerr) AS totalerr
+ FROM jobs
+ } break
+ trdb eval {
+ SELECT max(endtime)-min(starttime) AS totaltime
+ FROM jobs WHERE endtime>0
+ } break;
+ set et [elapsetime $totaltime]
+ puts "$totalerr errors out of $totaltest tests in about $et"
}
# Handle the --buildonly option, if it was specified.
@@ -1401,7 +1491,14 @@ proc explain_layer {indent depid} {
puts "${indent}$displayname in $dirname"
explain_layer "${indent} " $jobid
} elseif {$showtests} {
- puts "${indent}[lindex $displayname end]"
+ set tail [lindex $displayname end]
+ set e1 [lindex $displayname 1]
+ if {[string match config=* $e1]} {
+ set cfg [string range $e1 7 end]
+ puts "${indent}($cfg) $tail"
+ } else {
+ puts "${indent}$tail"
+ }
}
}
}
@@ -1416,7 +1513,7 @@ if {$TRG(explain)} {
explain_tests
} else {
if {$TRG(nJob)>1} {
- puts "splitting work across $TRG(nJob) jobs"
+ puts "splitting work across $TRG(nJob) cores"
}
puts "built testset in [expr $tm/1000]ms.."
handle_buildonly
diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl
index af480fc4c..78f28bbf8 100644
--- a/test/testrunner_data.tcl
+++ b/test/testrunner_data.tcl
@@ -16,7 +16,6 @@ namespace eval trd {
set tcltest(linux.Have-Not) veryquick
set tcltest(linux.Secure-Delete) veryquick
set tcltest(linux.Unlock-Notify) veryquick
- set tcltest(linux.User-Auth) veryquick
set tcltest(linux.Update-Delete-Limit) veryquick
set tcltest(linux.Extra-Robustness) veryquick
set tcltest(linux.Device-Two) veryquick
@@ -89,6 +88,7 @@ namespace eval trd {
--disable-amalgamation --disable-shared
--enable-session
-DSQLITE_ENABLE_RBU
+ -DSQLITE_ENABLE_STMT_SCANSTATUS
}
# These two are used by [testrunner.tcl mdevtest] (All-O0) and
@@ -96,6 +96,7 @@ namespace eval trd {
#
set build(All-Debug) {
--enable-debug --enable-all
+ -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES
}
set build(All-O0) {
-O0 --enable-all
@@ -116,6 +117,7 @@ namespace eval trd {
}
set build(Stdcall) {
-DUSE_STDCALL=1
+ -DSQLITE_USE_ONLY_WIN32=1
-O2
}
@@ -139,10 +141,6 @@ namespace eval trd {
-DSQLITE_THREADSAFE
-DSQLITE_TCL_DEFAULT_FULLMUTEX=1
}
- set build(User-Auth) {
- -O2
- -DSQLITE_USER_AUTHENTICATION=1
- }
set build(Secure-Delete) {
-O2
-DSQLITE_SECURE_DELETE=1
diff --git a/test/windowE.test b/test/windowE.test
index 9128468b3..1cb67f56b 100644
--- a/test/windowE.test
+++ b/test/windowE.test
@@ -73,6 +73,38 @@ do_catchsql_test 2.2 {
WINDOW w1 AS (PARTITION BY x OVER w1);
} {1 {near "OVER": syntax error}}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 3.0 {
+ BEGIN TRANSACTION;
+ CREATE TABLE t2(c1 INT, c2 REAL);
+ INSERT INTO t2 VALUES
+ (447,0.0), (448,0.0), (449,0.0), (452,0.0), (453,0.0), (454,0.0), (455,0.0),
+ (456,0.0), (459,0.0), (460,0.0), (462,0.0), (463,0.0), (466,0.0), (467,0.0),
+ (468,0.0), (469,0.0), (470,0.0), (473,0.0), (474,0.0), (475,0.0), (476,0.0),
+ (477,0.0), (480,0.0), (481,0.0), (482,0.0), (483,0.0), (484,0.0), (487,0.0),
+ (488,0.0), (489,0.0), (490,0.0), (491,0.0), (494,0.0), (495,0.0), (496,0.0),
+ (497,0.0), (498,0.0), (501,0.0), (502,0.0), (503,0.0), (504,0.0), (505,0.0),
+ (508,0.0), (509,0.0), (510,0.0), (511,0.0), (512,0.0), (515,0.0), (516,0.0),
+ (517,0.0), (518,0.0), (519,0.0), (522,0.0), (523,0.0), (524,0.0), (525,0.0),
+ (526,0.0), (529,0.0), (530,0.0), (531,0.0), (532,0.0), (533,0.0), (536,0.0),
+ (537,1.0), (538,0.0), (539,0.0), (540,0.0), (543,0.0), (544,0.0);
+ COMMIT;
+}
+
+do_execsql_test 3.1 {
+ select c1, max(c2) over (order by c1 range 366.0 preceding) from t2;
+} {
+ 447 0.0 448 0.0 449 0.0 452 0.0 453 0.0 454 0.0 455 0.0 456 0.0 459 0.0
+ 460 0.0 462 0.0 463 0.0 466 0.0 467 0.0 468 0.0 469 0.0 470 0.0 473 0.0
+ 474 0.0 475 0.0 476 0.0 477 0.0 480 0.0 481 0.0 482 0.0 483 0.0 484 0.0
+ 487 0.0 488 0.0 489 0.0 490 0.0 491 0.0 494 0.0 495 0.0 496 0.0 497 0.0
+ 498 0.0 501 0.0 502 0.0 503 0.0 504 0.0 505 0.0 508 0.0 509 0.0 510 0.0
+ 511 0.0 512 0.0 515 0.0 516 0.0 517 0.0 518 0.0 519 0.0 522 0.0 523 0.0
+ 524 0.0 525 0.0 526 0.0 529 0.0 530 0.0 531 0.0 532 0.0 533 0.0 536 0.0
+ 537 1.0 538 1.0 539 1.0 540 1.0 543 1.0 544 1.0
+}
+
finish_test
diff --git a/tool/mkctimec.tcl b/tool/mkctimec.tcl
index 135164e3d..a7e897a59 100755
--- a/tool/mkctimec.tcl
+++ b/tool/mkctimec.tcl
@@ -159,6 +159,7 @@ set boolean_defnil_options {
SQLITE_ENABLE_MULTIPLEX
SQLITE_ENABLE_NORMALIZE
SQLITE_ENABLE_NULL_TRIM
+ SQLITE_ENABLE_ORDERED_SET_AGGREGATES
SQLITE_ENABLE_OFFSET_SQL_FUNC
SQLITE_ENABLE_OVERSIZE_CELL_CHECK
SQLITE_ENABLE_PREUPDATE_HOOK
@@ -448,6 +449,7 @@ if {[catch {set cfd [open $destfile w]}]!=0} {
puts stderr "File '$destfile' unwritable."
exit 1;
}
+fconfigure $cfd -translation binary
puts $cfd $::headWarning;
puts $cfd $::headCode;
diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c
index 5386a36c4..188c0a29a 100644
--- a/tool/mkkeywordhash.c
+++ b/tool/mkkeywordhash.c
@@ -164,6 +164,11 @@ struct Keyword {
#else
# define RETURNING 0x00400000
#endif
+#ifndef SQLITE_ENABLE_ORDERED_SET_AGGREGATES
+# define ORDERSET 0
+#else
+# define ORDERSET 0x00800000
+#endif
/*
@@ -316,6 +321,7 @@ static Keyword aKeywordTable[] = {
{ "WHERE", "TK_WHERE", ALWAYS, 10 },
{ "WINDOW", "TK_WINDOW", WINDOWFUNC, 3 },
{ "WITH", "TK_WITH", CTE, 4 },
+ { "WITHIN", "TK_WITHIN", ORDERSET, 1 },
{ "WITHOUT", "TK_WITHOUT", ALWAYS, 1 },
};
diff --git a/tool/omittest.tcl b/tool/omittest.tcl
index 8862c685f..e9033c0bd 100644
--- a/tool/omittest.tcl
+++ b/tool/omittest.tcl
@@ -1,370 +1,220 @@
-# Documentation for this script. This may be output to stderr
+#!/usr/bin/tclsh
+#
+# Documentation for this script. This may be output to
# if the script is invoked incorrectly.
+#
set ::USAGE_MESSAGE {
This Tcl script is used to test the various compile time options
-available for omitting code (the SQLITE_OMIT_xxx options). It
-should be invoked as follows:
-
- <script> ?test-symbol? ?-makefile PATH-TO-MAKEFILE? ?-skip_run?
-
-The default value for ::MAKEFILE is "../Makefile.linux.gcc".
-
-If -skip_run option is given then only the compile part is attempted.
-
-This script builds the testfixture program and runs the SQLite test suite
-once with each SQLITE_OMIT_ option defined and then once with all options
-defined together. Each run is performed in a seperate directory created
-as a sub-directory of the current directory by the script. The output
-of the build is saved in <sub-directory>/build.log. The output of the
-test-suite is saved in <sub-directory>/test.log.
-
-Almost any SQLite makefile (except those generated by configure - see below)
-should work. The following properties are required:
+available for building SQLite, especially options taht omit
+features (the SQLITE_OMIT_xxx options). It should be invoked as follows:
- * The makefile should support the "testfixture" target.
- * The makefile should support the "test" target.
- * The makefile should support the variable "OPTS" as a way to pass
- options from the make command line to lemon and the C compiler.
+ ./configure CFLAGS=-O0
+ tclsh test/omittest.tcl
-More precisely, the following two invocations must be supported:
-
- $::MAKEBIN -f $::MAKEFILE testfixture OPTS="-DSQLITE_OMIT_ALTERTABLE=1"
- $::MAKEBIN -f $::MAKEFILE test
-
-Makefiles generated by the sqlite configure program cannot be used as
-they do not respect the OPTS variable.
}
-
-# Build a testfixture executable and run quick.test using it. The first
-# parameter is the name of the directory to create and use to run the
-# test in. The second parameter is a list of OMIT symbols to define
-# when doing so. For example:
+# List of all options to be tested.
#
-# run_quick_test /tmp/testdir {SQLITE_OMIT_TRIGGER SQLITE_OMIT_VIEW}
-#
-#
-proc run_quick_test {dir omit_symbol_list} {
- # Compile the value of the OPTS Makefile variable.
- set opts ""
- if {$::tcl_platform(platform)=="windows"} {
- append opts "OPTS += -DSQLITE_OS_WIN=1\n"
- set target "testfixture.exe"
- } else {
- append opts "OPTS += -DSQLITE_OS_UNIX=1\n"
- }
- foreach sym $omit_symbol_list {
- append opts "OPTS += -D${sym}=1\n"
- }
-
- # Create the directory and do the build. If an error occurs return
- # early without attempting to run the test suite.
- file mkdir $dir
- puts -nonewline "Building $dir..."
- flush stdout
- catch {
- file copy -force ./config.h $dir
- file copy -force ./libtool $dir
- }
- set fd [open $::MAKEFILE]
- set mkfile [read $fd]
- close $fd
- regsub {\ninclude} $mkfile "\n$opts\ninclude" mkfile
- set fd [open $dir/makefile w]
- puts $fd $mkfile
- close $fd
-
- set rc [catch {
- exec $::MAKEBIN -C $dir -f makefile clean $::TARGET >& $dir/build.log
- }]
- if {$rc} {
- puts "No good. See $dir/build.log."
- return
- } else {
- puts "Ok"
- }
-
- # Create an empty file "$dir/sqlite3". This is to trick the makefile out
- # of trying to build the sqlite shell. The sqlite shell won't build
- # with some of the OMIT options (i.e OMIT_COMPLETE).
- set sqlite3_dummy $dir/sqlite3
- if {$::tcl_platform(platform)=="windows"} {
- append sqlite3_dummy ".exe"
- }
- if {![file exists $sqlite3_dummy]} {
- set wr [open $sqlite3_dummy w]
- puts $wr "dummy"
- close $wr
- }
-
- if {$::SKIP_RUN} {
- # puts "Skip testing $dir."
- } else {
- # Run the test suite.
- puts -nonewline "Testing $dir..."
- flush stdout
- set rc [catch {
- exec $::MAKEBIN -C $dir -f makefile test >& $dir/test.log
- }]
- if {$rc} {
- puts "No good. See $dir/test.log."
- } else {
- puts "Ok"
- }
- }
+set CompileOptionsToTest {
+ SQLITE_OMIT_ALTERTABLE
+ SQLITE_OMIT_ANALYZE
+ SQLITE_OMIT_ATTACH
+ SQLITE_OMIT_AUTHORIZATION
+ SQLITE_OMIT_AUTOINCREMENT
+ SQLITE_OMIT_AUTOINIT
+ SQLITE_OMIT_AUTOMATIC_INDEX
+ SQLITE_OMIT_AUTORESET
+ SQLITE_OMIT_AUTOVACUUM
+ SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS
+ SQLITE_OMIT_BETWEEN_OPTIMIZATION
+ SQLITE_OMIT_BLOB_LITERAL
+ SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA
+ SQLITE_OMIT_CAST
+ SQLITE_OMIT_CHECK
+ SQLITE_OMIT_COMPILEOPTION_DIAGS
+ SQLITE_OMIT_COMPLETE
+ SQLITE_OMIT_COMPOUND_SELECT
+ SQLITE_OMIT_CONFLICT_CLAUSE
+ SQLITE_OMIT_CTE
+ SQLITE_OMIT_DATETIME_FUNCS
+ SQLITE_OMIT_DECLTYPE
+ SQLITE_OMIT_DEPRECATED
+ SQLITE_OMIT_DESERIALIZE
+ SQLITE_OMIT_DISKIO
+ SQLITE_OMIT_EXPLAIN
+ SQLITE_OMIT_FLAG_PRAGMAS
+ SQLITE_OMIT_FLOATING_POINT
+ SQLITE_OMIT_FOREIGN_KEY
+ SQLITE_OMIT_GENERATED_COLUMNS
+ SQLITE_OMIT_GET_TABLE
+ SQLITE_OMIT_HEX_INTEGER
+ SQLITE_OMIT_INCRBLOB
+ SQLITE_OMIT_INTEGRITY_CHECK
+ SQLITE_OMIT_INTROSPECTION_PRAGMAS
+ SQLITE_OMIT_JSON
+ SQLITE_OMIT_LIKE_OPTIMIZATION
+ SQLITE_OMIT_LOAD_EXTENSION
+ SQLITE_OMIT_LOCALTIME
+ SQLITE_OMIT_LOOKASIDE
+ SQLITE_OMIT_MEMORYDB
+ SQLITE_OMIT_OR_OPTIMIZATION
+ SQLITE_OMIT_PAGER_PRAGMAS
+ SQLITE_OMIT_PARSER_TRACE
+ SQLITE_OMIT_POPEN
+ SQLITE_OMIT_PRAGMA
+ SQLITE_OMIT_PROGRESS_CALLBACK
+ SQLITE_OMIT_QUICKBALANCE
+ SQLITE_OMIT_RANDOMNESS
+ SQLITE_OMIT_REINDEX
+ SQLITE_OMIT_SCHEMA_PRAGMAS
+ SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
+ SQLITE_OMIT_SHARED_CACHE
+ SQLITE_OMIT_SHUTDOWN_DIRECTORIES
+ SQLITE_OMIT_SUBQUERY
+ SQLITE_OMIT_TCL_VARIABLE
+ SQLITE_OMIT_TEMPDB
+ SQLITE_OMIT_TEST_CONTROL
+ SQLITE_OMIT_TRACE
+ SQLITE_OMIT_TRIGGER
+ SQLITE_OMIT_TRUNCATE_OPTIMIZATION
+ SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ SQLITE_OMIT_UPSERT
+ SQLITE_OMIT_UTF
+ SQLITE_OMIT_VACUUM
+ SQLITE_OMIT_VIEW
+ SQLITE_OMIT_VIRTUALTABLE
+ SQLITE_OMIT_WAL
+ SQLITE_OMIT_WINDOWFUNC
+ SQLITE_OMIT_WSD
+ SQLITE_OMIT_XFER_OPT
+ SQLITE_ALLOW_ROWID_IN_VIEW
+ SQLITE_DISABLE_DIRSYNC
+ SQLITE_DISABLE_FTS
+ SQLITE_DISABLE_INTRINSIC
+ SQLITE_DISABLE_LFS
+ SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
+ SQLITE_DISABLE_SKIPAHEAD_DISTINCT
+ SQLITE_ENABLE_API_ARMOR
+ SQLITE_ENABLE_ATOMIC_WRITE
+ SQLITE_ENABLE_BATCH_ATOMIC_WRITE
+ SQLITE_ENABLE_BYTECODE_VTAB
+ SQLITE_ENABLE_CEROD
+ SQLITE_ENABLE_COLUMN_METADATA
+ SQLITE_ENABLE_COLUMN_USED_MASK
+ SQLITE_ENABLE_COMMENTS
+ SQLITE_ENABLE_CORRUPT_PGNO
+ SQLITE_ENABLE_COSTMULT
+ SQLITE_ENABLE_CURSOR_HINTS
+ SQLITE_ENABLE_DBPAGE_VTAB
+ SQLITE_ENABLE_DBSTAT_VTAB
+ SQLITE_ENABLE_EXPENSIVE_ASSERT
+ SQLITE_ENABLE_EXPLAIN_COMMENTS
+ SQLITE_ENABLE_FTS
+ SQLITE_ENABLE_GEOPOLY
+ SQLITE_ENABLE_HIDDEN_COLUMNS
+ SQLITE_ENABLE_ICU
+ SQLITE_ENABLE_ICU_COLLATIONS
+ SQLITE_ENABLE_INTERNAL_FUNCTIONS
+ SQLITE_ENABLE_IOTRACE
+ SQLITE_ENABLE_LOAD_EXTENSION
+ SQLITE_ENABLE_LOCKING_STYLE
+ SQLITE_ENABLE_MATH_FUNCTIONS
+ SQLITE_ENABLE_MEMORY_MANAGEMENT
+ SQLITE_ENABLE_MEMSYS
+ SQLITE_ENABLE_MODULE_COMMENTS
+ SQLITE_ENABLE_MULTIPLEX
+ SQLITE_ENABLE_MULTITHREADED_CHECKS
+ SQLITE_ENABLE_NORMALIZE
+ SQLITE_ENABLE_NULL_TRIM
+ SQLITE_ENABLE_OFFSET_SQL_FUNC
+ SQLITE_ENABLE_OVERSIZE_CELL_CHECK
+ SQLITE_ENABLE_PREUPDATE_HOOK
+ SQLITE_ENABLE_QPSG
+ SQLITE_ENABLE_RBU
+ SQLITE_ENABLE_RTREE
+ SQLITE_ENABLE_SELECTTRACE
+ SQLITE_ENABLE_SESSION
+ SQLITE_ENABLE_SETLK_TIMEOUT
+ SQLITE_ENABLE_SNAPSHOT
+ SQLITE_ENABLE_SORTER_MMAP
+ SQLITE_ENABLE_SORTER_REFERENCE
+ SQLITE_ENABLE_SORTER_REFERENCES
+ SQLITE_ENABLE_SQLLOG
+ SQLITE_ENABLE_STAT
+ SQLITE_ENABLE_STMT_SCANSTATUS
+ SQLITE_ENABLE_STMTVTAB
+ SQLITE_ENABLE_TREETRACE
+ SQLITE_ENABLE_UNKNOWN_FUNCTION
+ SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ SQLITE_ENABLE_UNLOCK_NOTIFY
+ SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+ SQLITE_ENABLE_URI_00_ERROR
+ SQLITE_ENABLE_VFSTRACE
+ SQLITE_ENABLE_WHERETRACE
+ SQLITE_ENABLE_ZIPVFS
}
-
-# This proc processes the command line options passed to this script.
-# Currently the only option supported is "-makefile", default
-# "../Makefile.linux-gcc". Set the ::MAKEFILE variable to the value of this
-# option.
+# Parse command-line options.
#
-proc process_options {argv} {
- set ::MAKEBIN make ;# Default value
- if {$::tcl_platform(platform)=="windows"} {
- set ::MAKEFILE ./Makefile ;# Default value on Windows
- } else {
- set ::MAKEFILE ./Makefile.linux-gcc ;# Default value
- }
- set ::SKIP_RUN 1 ;# Default to attempt test
- set ::TARGET testfixture ;# Default thing to build
-
- for {set i 0} {$i < [llength $argv]} {incr i} {
- switch -regexp -- [lindex $argv $i] {
- -{1,2}makefile {
- incr i
- set ::MAKEFILE [lindex $argv $i]
- }
-
- -{1,2}nmake {
- set ::MAKEBIN nmake
- set ::MAKEFILE ./Makefile.msc
- }
-
- -{1,2}target {
- incr i
- set ::TARGET [lindex $argv $i]
- }
-
- -{1,2}skip_run {
- set ::SKIP_RUN 1
- }
- -{1,2}run {
- set ::SKIP_RUN 0
- }
-
- -{1,2}help {
- puts $::USAGE_MESSAGE
- exit
- }
-
- -.* {
- puts stderr "Unknown option: [lindex $argv i]"
- puts stderr $::USAGE_MESSAGE
- exit 1
- }
-
- default {
- if {[info exists ::SYMBOL]} {
- puts stderr [string trim $::USAGE_MESSAGE]
- exit -1
- }
- set ::SYMBOL [lindex $argv $i]
- }
+for {set i 0} {$i<[llength $argv]} {incr i} {
+ set arg [lindex $argv $i]
+ switch -- $arg {
+ -start -
+ --start {
+ incr i
+ set startat [lindex $argv $i]
}
- set ::MAKEFILE [file normalize $::MAKEFILE]
}
}
-# Main routine.
+# Additional options required for some settings.
#
+set More(SQLITE_OMIT_DISKIO) {-DSQLITE_OMIT_WAL}
-proc main {argv} {
- # List of SQLITE_OMIT_XXX symbols supported by SQLite.
- set ::OMIT_SYMBOLS [list \
- SQLITE_OMIT_ALTERTABLE \
- SQLITE_OMIT_ANALYZE \
- SQLITE_OMIT_ATTACH \
- SQLITE_OMIT_AUTHORIZATION \
- SQLITE_OMIT_AUTOINCREMENT \
- SQLITE_OMIT_AUTOINIT \
- SQLITE_OMIT_AUTOMATIC_INDEX \
- SQLITE_OMIT_AUTORESET \
- SQLITE_OMIT_AUTOVACUUM \
- SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS \
- SQLITE_OMIT_BETWEEN_OPTIMIZATION \
- SQLITE_OMIT_BLOB_LITERAL \
- SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA \
- SQLITE_OMIT_CAST \
- SQLITE_OMIT_CHECK \
- SQLITE_OMIT_COMPILEOPTION_DIAGS \
- SQLITE_OMIT_COMPLETE \
- SQLITE_OMIT_COMPOUND_SELECT \
- SQLITE_OMIT_CONFLICT_CLAUSE \
- SQLITE_OMIT_CTE \
- SQLITE_OMIT_DATETIME_FUNCS \
- SQLITE_OMIT_DECLTYPE \
- SQLITE_OMIT_DEPRECATED \
- SQLITE_OMIT_DESERIALIZE \
- SQLITE_OMIT_DISKIO \
- SQLITE_OMIT_EXPLAIN \
- SQLITE_OMIT_FLAG_PRAGMAS \
- SQLITE_OMIT_FLOATING_POINT \
- SQLITE_OMIT_FOREIGN_KEY \
- SQLITE_OMIT_GENERATED_COLUMNS \
- SQLITE_OMIT_GET_TABLE \
- SQLITE_OMIT_HEX_INTEGER \
- SQLITE_OMIT_INCRBLOB \
- SQLITE_OMIT_INTEGRITY_CHECK \
- SQLITE_OMIT_INTROSPECTION_PRAGMAS \
- SQLITE_OMIT_JSON \
- SQLITE_OMIT_LIKE_OPTIMIZATION \
- SQLITE_OMIT_LOAD_EXTENSION \
- SQLITE_OMIT_LOCALTIME \
- SQLITE_OMIT_LOOKASIDE \
- SQLITE_OMIT_MEMORYDB \
- SQLITE_OMIT_OR_OPTIMIZATION \
- SQLITE_OMIT_PAGER_PRAGMAS \
- SQLITE_OMIT_PARSER_TRACE \
- SQLITE_OMIT_POPEN \
- SQLITE_OMIT_PRAGMA \
- SQLITE_OMIT_PROGRESS_CALLBACK \
- SQLITE_OMIT_QUICKBALANCE \
- SQLITE_OMIT_RANDOMNESS \
- SQLITE_OMIT_REINDEX \
- SQLITE_OMIT_SCHEMA_PRAGMAS \
- SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS \
- SQLITE_OMIT_SHARED_CACHE \
- SQLITE_OMIT_SHUTDOWN_DIRECTORIES \
- SQLITE_OMIT_SUBQUERY \
- SQLITE_OMIT_TCL_VARIABLE \
- SQLITE_OMIT_TEMPDB \
- SQLITE_OMIT_TEST_CONTROL \
- SQLITE_OMIT_TRACE \
- SQLITE_OMIT_TRIGGER \
- SQLITE_OMIT_TRUNCATE_OPTIMIZATION \
- SQLITE_OMIT_TWOSIZE_LOOKASIDE \
- SQLITE_OMIT_UPSERT \
- SQLITE_OMIT_UTF \
- SQLITE_OMIT_VACUUM \
- SQLITE_OMIT_VIEW \
- SQLITE_OMIT_VIRTUALTABLE \
- SQLITE_OMIT_WAL \
- SQLITE_OMIT_WINDOWFUNC \
- SQLITE_OMIT_WSD \
- SQLITE_OMIT_XFER_OPT \
- ]
-
- set ::ENABLE_SYMBOLS [list \
- SQLITE_ALLOW_ROWID_IN_VIEW \
- SQLITE_DISABLE_DIRSYNC \
- SQLITE_DISABLE_FTS \
- SQLITE_DISABLE_INTRINSIC \
- SQLITE_DISABLE_LFS \
- SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS \
- SQLITE_DISABLE_SKIPAHEAD_DISTINCT \
- SQLITE_ENABLE_API_ARMOR \
- SQLITE_ENABLE_ATOMIC_WRITE \
- SQLITE_ENABLE_BATCH_ATOMIC_WRITE \
- SQLITE_ENABLE_BYTECODE_VTAB \
- SQLITE_ENABLE_CEROD \
- SQLITE_ENABLE_COLUMN_METADATA \
- SQLITE_ENABLE_COLUMN_USED_MASK \
- SQLITE_ENABLE_COMMENTS \
- SQLITE_ENABLE_CORRUPT_PGNO \
- SQLITE_ENABLE_COSTMULT \
- SQLITE_ENABLE_CURSOR_HINTS \
- SQLITE_ENABLE_DBPAGE_VTAB \
- SQLITE_ENABLE_DBSTAT_VTAB \
- SQLITE_ENABLE_EXPENSIVE_ASSERT \
- SQLITE_ENABLE_EXPLAIN_COMMENTS \
- SQLITE_ENABLE_FTS \
- SQLITE_ENABLE_GEOPOLY \
- SQLITE_ENABLE_HIDDEN_COLUMNS \
- SQLITE_ENABLE_ICU \
- SQLITE_ENABLE_ICU_COLLATIONS \
- SQLITE_ENABLE_INTERNAL_FUNCTIONS \
- SQLITE_ENABLE_IOTRACE \
- SQLITE_ENABLE_LOAD_EXTENSION \
- SQLITE_ENABLE_LOCKING_STYLE \
- SQLITE_ENABLE_MATH_FUNCTIONS \
- SQLITE_ENABLE_MEMORY_MANAGEMENT \
- SQLITE_ENABLE_MEMSYS \
- SQLITE_ENABLE_MODULE_COMMENTS \
- SQLITE_ENABLE_MULTIPLEX \
- SQLITE_ENABLE_MULTITHREADED_CHECKS \
- SQLITE_ENABLE_NORMALIZE \
- SQLITE_ENABLE_NULL_TRIM \
- SQLITE_ENABLE_OFFSET_SQL_FUNC \
- SQLITE_ENABLE_OVERSIZE_CELL_CHECK \
- SQLITE_ENABLE_PREUPDATE_HOOK \
- SQLITE_ENABLE_QPSG \
- SQLITE_ENABLE_RBU \
- SQLITE_ENABLE_RTREE \
- SQLITE_ENABLE_SELECTTRACE \
- SQLITE_ENABLE_SESSION \
- SQLITE_ENABLE_SETLK_TIMEOUT \
- SQLITE_ENABLE_SNAPSHOT \
- SQLITE_ENABLE_SORTER_MMAP\
- SQLITE_ENABLE_SORTER_REFERENCE \
- SQLITE_ENABLE_SORTER_REFERENCES \
- SQLITE_ENABLE_SQLLOG\
- SQLITE_ENABLE_STAT \
- SQLITE_ENABLE_STMT_SCANSTATUS \
- SQLITE_ENABLE_STMTVTAB \
- SQLITE_ENABLE_TREETRACE \
- SQLITE_ENABLE_UNKNOWN_FUNCTION \
- SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
- SQLITE_ENABLE_UNLOCK_NOTIFY \
- SQLITE_ENABLE_UPDATE_DELETE_LIMIT \
- SQLITE_ENABLE_URI_00_ERROR \
- SQLITE_ENABLE_VFSTRACE \
- SQLITE_ENABLE_WHERETRACE \
- SQLITE_ENABLE_ZIPVFS \
- ]
-
- # Process any command line options.
- process_options $argv
-
- if {[info exists ::SYMBOL] } {
- set sym $::SYMBOL
+# Compile-time options for Mac only
+#
+set MacOnly(SQLITE_ENABLE_LOCKING_STYLE) 1
- if {[lsearch $::OMIT_SYMBOLS $sym]<0 && [lsearch $::ENABLE_SYMBOLS $sym]<0} {
- puts stderr "No such symbol: $sym"
- exit -1
+# Compile-time options that might fail, depending on what libraries
+# are installed. Failures on these tests issue a warning, but testing
+# continues.
+#
+set FailIsOk(SQLITE_ENABLE_ICU) 1
+set FailIsOk(SQLITE_ENABLE_ICU_COLLATIONS) 1
+
+file mkdir omittest
+foreach sym $CompileOptionsToTest {
+ if {[info exists startat]} {
+ if {$startat==$sym} {
+ unset startat
+ } else {
+ continue
}
-
- set dirname "test_[regsub -nocase {^x*SQLITE_} $sym {}]"
- run_quick_test $dirname $sym
+ }
+ if {[info exists MacOnly($sym)] && $tcl_platform(os)!="Darwin"} {
+ continue
+ }
+ set logfile "omittest/$sym.log"
+ if {[info exists More($sym)]} {
+ append opts "OPT_FEATURE_FLAGS=-D$sym $More($sym)"
} else {
- # First try a test with all OMIT symbols except SQLITE_OMIT_FLOATING_POINT
- # and SQLITE_OMIT_PRAGMA defined. The former doesn't work (causes segfaults)
- # and the latter is currently incompatible with the test suite (this should
- # be fixed, but it will be a lot of work).
- set allsyms [list]
- foreach s $::OMIT_SYMBOLS {
- if {$s!="SQLITE_OMIT_FLOATING_POINT" && $s!="SQLITE_OMIT_PRAGMA"} {
- lappend allsyms $s
- }
- }
- run_quick_test test_OMIT_EVERYTHING $allsyms
-
- # Now try one quick.test with each of the OMIT symbols defined. Included
- # are the OMIT_FLOATING_POINT and OMIT_PRAGMA symbols, even though we
- # know they will fail. It's good to be reminded of this from time to time.
- foreach sym $::OMIT_SYMBOLS {
- set dirname "test_[regsub -nocase {^x*SQLITE_} $sym {}]"
- run_quick_test $dirname $sym
- }
-
- # Try the ENABLE/DISABLE symbols one at a time.
- # We don't do them all at once since some are conflicting.
- foreach sym $::ENABLE_SYMBOLS {
- set dirname "test_[regsub -nocase {^x*SQLITE_} $sym {}]"
- run_quick_test $dirname $sym
+ set opts OPT_FEATURE_FLAGS=-D$sym
+ }
+ puts "make tidy sqlite3.lo $opts"
+ if {[catch {exec make tidy sqlite3.lo $opts >& $logfile}]} {
+ puts "BUILD FAILED: see $logfile for details"
+ if {[info exists FailIsOk($sym)]} {
+ set Failure($sym) 1
+ } else {
+ puts "Note: After fixes, continue the test using:\n"
+ puts " [info nameofexe] $argv0 --start $sym\n"
+ exit 1
}
}
}
-
-main $argv
+if {[llength [array names Failure]]>0} {
+ puts "BUILD FAILED on the following:"
+ foreach sym [array names Failure] {
+ puts " * $sym"
+ }
+}