diff options
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) @@ -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; } @@ -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 @@ -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" + } +} |