diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e7e8f8..1b2c410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # SQLCipher Change Log All notable changes to this project will be documented in this file. +## [4.4.1] - (October 2020 - [4.4.1 changes]) +- Updates baseline to upstream SQLite 3.33.0 +- Fixes double-free bug in cipher_default_plaintext_header_size +- Changes SQLCipher tests to use suite runner +- Improvement to cipher_integrity_check tests to minimize false negatives +- Deprecates PRAGMA cipher_store_pass + ## [4.4.0] - (May 2020 - [4.4.0 changes]) - Updates baseline to upstream SQLite 3.31.0 - Adjusts shell to report SQLCipher version alongside SQLite version @@ -164,7 +171,9 @@ All notable changes to this project will be documented in this file. ### Security - Change KDF iteration length from 4,000 to 64,000 -[unreleased]: https://github.com/sqlcipher/sqlcipher/compare/v4.4.0...prerelease +[unreleased]: https://github.com/sqlcipher/sqlcipher/compare/v4.4.1...prerelease +[4.4.1]: https://github.com/sqlcipher/sqlcipher/tree/v4.4.1 +[4.4.1 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.4.0...v4.4.1 [4.4.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.4.0 [4.4.0 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.3.0...v4.4.0 [4.3.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.3.0 diff --git a/Makefile.in b/Makefile.in index d8f5a62..3f810ab 100644 --- a/Makefile.in +++ b/Makefile.in @@ -211,8 +211,9 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ table.lo threads.lo tokenize.lo treeview.lo trigger.lo \ update.lo userauth.lo upsert.lo util.lo vacuum.lo \ vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \ - vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \ - window.lo utf.lo vtab.lo $(CRYPTOLIBOBJ) + vdbetrace.lo vdbevtab.lo \ + wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \ + window.lo utf.lo vtab.lo $(CRYPTOLIBOBJ) # Object files for the amalgamation. # @@ -318,6 +319,7 @@ SRC = \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbesort.c \ $(TOP)/src/vdbetrace.c \ + $(TOP)/src/vdbevtab.c \ $(TOP)/src/vdbeInt.h \ $(TOP)/src/vtab.c \ $(TOP)/src/vxworks.h \ @@ -463,6 +465,7 @@ TESTSRC += \ $(TOP)/ext/misc/carray.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/csv.c \ + $(TOP)/ext/misc/decimal.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/explain.c \ $(TOP)/ext/misc/fileio.c \ @@ -524,6 +527,7 @@ TESTSRC2 = \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ + $(TOP)/src/vdbevtab.c \ $(TOP)/src/where.c \ $(TOP)/src/wherecode.c \ $(TOP)/src/whereexpr.c \ @@ -629,6 +633,7 @@ SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB +SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC SHELL_OPT += -DSQLITE_ENABLE_DESERIALIZE FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 @@ -637,10 +642,12 @@ FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPT += -DSQLITE_PRINTF_PRECISION_LIMIT=1000 FUZZCHECK_OPT += -DSQLITE_ENABLE_DESERIALIZE FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS4 +FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS3_PARENTHESIS #FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS5 FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB +FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c DBFUZZ_OPT = @@ -710,6 +717,7 @@ DBFUZZ2_OPTS = \ -DSQLITE_ENABLE_DESERIALIZE \ -DSQLITE_DEBUG \ -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_RTREE \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_FTS5 @@ -1041,6 +1049,9 @@ vdbesort.lo: $(TOP)/src/vdbesort.c $(HDR) vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbetrace.c +vdbevtab.lo: $(TOP)/src/vdbevtab.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbevtab.c + vtab.lo: $(TOP)/src/vtab.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c @@ -1094,6 +1105,12 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE) sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h +sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION + echo '#ifndef SQLITE_RESOURCE_VERSION' >$@ + echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@ + cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@ + echo '#endif' >>sqlite3rc.h + keywordhash.h: $(TOP)/tool/mkkeywordhash.c $(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c ./mkkeywordhash$(BEXE) >keywordhash.h @@ -1102,10 +1119,13 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c SHELL_SRC = \ $(TOP)/src/shell.c.in \ $(TOP)/ext/misc/appendvfs.c \ - $(TOP)/ext/misc/shathree.c \ - $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/completion.c \ + $(TOP)/ext/misc/decimal.c \ + $(TOP)/ext/misc/fileio.c \ + $(TOP)/ext/misc/ieee754.c \ + $(TOP)/ext/misc/shathree.c \ $(TOP)/ext/misc/sqlar.c \ + $(TOP)/ext/misc/uint.c \ $(TOP)/ext/expert/sqlite3expert.c \ $(TOP)/ext/expert/sqlite3expert.h \ $(TOP)/ext/misc/zipfile.c \ @@ -1247,6 +1267,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB +TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DESERIALIZE TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlcipher.la @@ -1311,6 +1332,9 @@ valgrindtest: $(TESTPROGS) valgrindfuzz smoketest: $(TESTPROGS) fuzzcheck$(TEXE) ./testfixture$(TEXE) $(TOP)/test/main.test $(TESTOPTS) +shelltest: $(TESTPROGS) + ./testfixture$(TEXT) $(TOP)/test/permutations.test shell + sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in $(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c @@ -1413,10 +1437,10 @@ checksymbols: sqlite3.o # a tarball named for the version number. Ex: sqlite-autoconf-3110000.tar.gz. # The snapshot-tarball target builds a tarball named by the SHA1 hash # -amalgamation-tarball: sqlite3.c +amalgamation-tarball: sqlite3.c sqlite3rc.h TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal -snapshot-tarball: sqlite3.c +snapshot-tarball: sqlite3.c sqlite3rc.h TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot # The next two rules are used to support the "threadtest" target. Building diff --git a/Makefile.msc b/Makefile.msc index d59ca46..2261ee9 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -234,6 +234,15 @@ OSTRACE = 0 DEBUG = 0 !ENDIF +# <> +# Disable use of the --linemacros argument to the mksqlite3c.tcl tool, which +# is used to build the amalgamation. +# +!IFNDEF NO_LINEMACROS +NO_LINEMACROS = 0 +!ENDIF +# <> + # Enable use of available compiler optimizations? Normally, this should be # non-zero. Setting this to zero, thus disabling all compiler optimizations, # can be useful for testing. @@ -357,6 +366,7 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1 +OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1 !ENDIF OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1 @@ -775,7 +785,7 @@ MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c.tcl !ENDIF !IFNDEF MKSQLITE3C_ARGS -!IF $(DEBUG)>1 +!IF $(DEBUG)>1 && $(NO_LINEMACROS)==0 MKSQLITE3C_ARGS = --linemacros !ELSE MKSQLITE3C_ARGS = @@ -1246,7 +1256,8 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \ table.lo threads.lo tokenize.lo treeview.lo trigger.lo \ update.lo upsert.lo util.lo vacuum.lo \ vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \ - vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \ + vdbetrace.lo vdbevtab.lo wal.lo walker.lo where.lo wherecode.lo \ + whereexpr.lo \ window.lo utf.lo vtab.lo # <> @@ -1361,6 +1372,7 @@ SRC01 = \ $(TOP)\src\vdbemem.c \ $(TOP)\src\vdbesort.c \ $(TOP)\src\vdbetrace.c \ + $(TOP)\src\vdbevtab.c \ $(TOP)\src\vtab.c \ $(TOP)\src\wal.c \ $(TOP)\src\walker.c \ @@ -1491,7 +1503,7 @@ SRC12 = # All source code files. # -SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11) +SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11) $(SRC12) # Source code to the test files. # @@ -1556,6 +1568,7 @@ TESTEXT = \ $(TOP)\ext\misc\carray.c \ $(TOP)\ext\misc\closure.c \ $(TOP)\ext\misc\csv.c \ + $(TOP)\ext\misc\decimal.c \ $(TOP)\ext\misc\eval.c \ $(TOP)\ext\misc\explain.c \ $(TOP)\ext\misc\fileio.c \ @@ -1692,6 +1705,7 @@ FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS4 FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_RTREE FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB +FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c @@ -1841,15 +1855,16 @@ mptest: mptester.exe for %i in ($(SRC11)) do copy /Y %i tsrc for %i in ($(SRC12)) do copy /Y %i tsrc copy /Y fts5.c tsrc + copy /B tsrc\fts5.c +,, copy /Y fts5.h tsrc + copy /B tsrc\fts5.h +,, del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL $(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new move vdbe.new tsrc\vdbe.c echo > .target_source -sqlite3.c: .target_source sqlite3ext.h $(MKSQLITE3C_TOOL) +sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) $(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS) - copy $(TOP)\ext\session\sqlite3session.h . sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl @@ -1864,7 +1879,8 @@ sqlite3.lo: $(SQLITE3C) # Rules to build the LEMON compiler generator # lempar.c: $(TOP)\tool\lempar.c - copy $(TOP)\tool\lempar.c . + copy /Y $(TOP)\tool\lempar.c . + copy /B lempar.c +,, lemon.exe: $(TOP)\tool\lemon.c lempar.c $(BCC) $(NO_WARN) -Daccess=_access \ @@ -2117,6 +2133,9 @@ vdbesort.lo: $(TOP)\src\vdbesort.c $(HDR) vdbetrace.lo: $(TOP)\src\vdbetrace.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vdbetrace.c +vdbevtab.lo: $(TOP)\src\vdbevtab.c $(HDR) + $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vdbevtab.c + vtab.lo: $(TOP)\src\vtab.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vtab.c @@ -2161,7 +2180,8 @@ parse.h: parse.c parse.c: $(TOP)\src\parse.y lemon.exe del /Q parse.y parse.h parse.h.temp 2>NUL - copy $(TOP)\src\parse.y . + copy /Y $(TOP)\src\parse.y . + copy /B parse.y +,, .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S parse.y $(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION @@ -2174,8 +2194,13 @@ sqlite3ext.h: .target_source copy /Y sqlite3ext.h tsrc\sqlite3ext.h !ELSE copy /Y tsrc\sqlite3ext.h sqlite3ext.h + copy /B sqlite3ext.h +,, !ENDIF +sqlite3session.h: $(TOP)\ext\session\sqlite3session.h + copy /Y $(TOP)\ext\session\sqlite3session.h . + copy /B sqlite3session.h +,, + mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c $(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) \ $(TOP)\tool\mkkeywordhash.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) @@ -2187,9 +2212,12 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe SHELL_SRC = \ $(TOP)\src\shell.c.in \ $(TOP)\ext\misc\appendvfs.c \ - $(TOP)\ext\misc\shathree.c \ - $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\completion.c \ + $(TOP)\ext\misc\decimal.c \ + $(TOP)\ext\misc\fileio.c \ + $(TOP)\ext\misc\ieee754.c \ + $(TOP)\ext\misc\shathree.c \ + $(TOP)\ext\misc\uint.c \ $(TOP)\ext\expert\sqlite3expert.c \ $(TOP)\ext\expert\sqlite3expert.h \ $(TOP)\ext\misc\memtrace.c \ @@ -2320,7 +2348,8 @@ LSM1_SRC = \ $(TOP)\ext\lsm1\lsm_win32.c fts5parse.c: $(TOP)\ext\fts5\fts5parse.y lemon.exe - copy $(TOP)\ext\fts5\fts5parse.y . + copy /Y $(TOP)\ext\fts5\fts5parse.y . + copy /B fts5parse.y +,, del /Q fts5parse.h 2>NUL .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S fts5parse.y @@ -2328,11 +2357,13 @@ fts5parse.h: fts5parse.c fts5.c: $(FTS5_SRC) $(TCLSH_CMD) $(TOP)\ext\fts5\tool\mkfts5c.tcl - copy $(TOP)\ext\fts5\fts5.h . + copy /Y $(TOP)\ext\fts5\fts5.h . + copy /B fts5.h +,, lsm1.c: $(LSM1_SRC) $(TCLSH_CMD) $(TOP)\ext\lsm1\tool\mklsm1c.tcl - copy $(TOP)\ext\lsm1\lsm.h . + copy /Y $(TOP)\ext\lsm1\lsm.h . + copy /B lsm.h +,, fts5.lo: fts5.c $(HDR) $(EXTHDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c fts5.c @@ -2360,6 +2391,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1 +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_JSON1=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS) @@ -2442,6 +2474,9 @@ smoketest: $(TESTPROGS) @set PATH=$(LIBTCLPATH);$(PATH) .\testfixture.exe $(TOP)\test\main.test $(TESTOPTS) +shelltest: $(TESTPROGS) + .\testfixture.exe $(TOP)\test\permutations.test shell + sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(SQLITE_TCL_DEP) $(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in > $@ diff --git a/SQLCipher.podspec.json b/SQLCipher.podspec.json index f8db2e4..98d05a7 100644 --- a/SQLCipher.podspec.json +++ b/SQLCipher.podspec.json @@ -15,10 +15,10 @@ "requires_arc": false, "source": { "git": "https://github.com/sqlcipher/sqlcipher.git", - "tag": "v4.4.0" + "tag": "v4.4.1" }, "summary": "Full Database Encryption for SQLite.", - "version": "4.4.0", + "version": "4.4.1", "subspecs": [ { "compiler_flags": [ diff --git a/VERSION b/VERSION index d9351e5..949654d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.31.0 +3.33.0 diff --git a/aclocal.m4 b/aclocal.m4 index 79ce10f..8ce4d37 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -736,7 +736,6 @@ _LT_CONFIG_SAVE_COMMANDS([ cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. @@ -1048,8 +1047,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD - echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD - $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF @@ -1499,7 +1498,7 @@ need_locks=$enable_libtool_lock m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} -: ${AR_FLAGS=cru} +: ${AR_FLAGS=cr} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) @@ -2893,6 +2892,18 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) dynamic_linker='GNU/Linux ld.so' ;; +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + netbsd*) version_type=sunos need_lib_prefix=no @@ -3552,7 +3563,7 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; -netbsd*) +netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else @@ -4058,7 +4069,8 @@ _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm - if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD + if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" @@ -4430,7 +4442,7 @@ m4_if([$1], [CXX], [ ;; esac ;; - netbsd*) + netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise @@ -4698,6 +4710,12 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; + # flang / f18. f95 an alias for gfortran or flang on Debian + flang* | f18* | f95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) @@ -4942,6 +4960,9 @@ m4_if([$1], [CXX], [ ;; esac ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; @@ -5004,6 +5025,9 @@ dnl Note also adjust exclude_expsyms for C++ above. openbsd* | bitrig*) with_gnu_ld=no ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes @@ -5258,7 +5282,7 @@ _LT_EOF fi ;; - netbsd*) + netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= @@ -5779,6 +5803,7 @@ _LT_EOF if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi + _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' @@ -5800,7 +5825,7 @@ _LT_EOF esac ;; - netbsd*) + netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else @@ -6422,7 +6447,7 @@ if test yes != "$_lt_caught_CXX_error"; then # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no @@ -6797,7 +6822,7 @@ if test yes != "$_lt_caught_CXX_error"; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then @@ -6862,7 +6887,7 @@ if test yes != "$_lt_caught_CXX_error"; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then @@ -7201,7 +7226,7 @@ if test yes != "$_lt_caught_CXX_error"; then # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support @@ -7285,7 +7310,7 @@ if test yes != "$_lt_caught_CXX_error"; then # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. @@ -7296,7 +7321,7 @@ if test yes != "$_lt_caught_CXX_error"; then # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am index 20af743..694419b 100644 --- a/autoconf/Makefile.am +++ b/autoconf/Makefile.am @@ -13,7 +13,7 @@ sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_D include_HEADERS = sqlite3.h sqlite3ext.h -EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs Makefile.fallback +EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc sqlite3rc.h README.txt Replace.cs Makefile.fallback pkgconfigdir = ${libdir}/pkgconfig pkgconfig_DATA = sqlite3.pc diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc index 37a3c1b..746162a 100644 --- a/autoconf/Makefile.msc +++ b/autoconf/Makefile.msc @@ -196,6 +196,7 @@ OSTRACE = 0 DEBUG = 0 !ENDIF + # Enable use of available compiler optimizations? Normally, this should be # non-zero. Setting this to zero, thus disabling all compiler optimizations, # can be useful for testing. @@ -288,6 +289,7 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1 +OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1 !ENDIF OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1 diff --git a/autoconf/tea/configure.ac b/autoconf/tea/configure.ac index f6bea0f..dae94f5 100644 --- a/autoconf/tea/configure.ac +++ b/autoconf/tea/configure.ac @@ -19,7 +19,7 @@ dnl to configure the system for the local environment. # so you can encode the package version directly into the source files. #----------------------------------------------------------------------- -AC_INIT([sqlite], [3.31.0]) +AC_INIT([sqlite], [3.32.0]) #-------------------------------------------------------------------- # Call TEA_INIT as the first TEA_ macro to set up initial vars. diff --git a/configure b/configure index c90edcc..5852752 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlcipher 3.31.0. +# Generated by GNU Autoconf 2.69 for sqlcipher 3.33.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlcipher' PACKAGE_TARNAME='sqlcipher' -PACKAGE_VERSION='3.31.0' -PACKAGE_STRING='sqlcipher 3.31.0' +PACKAGE_VERSION='3.33.0' +PACKAGE_STRING='sqlcipher 3.33.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -729,6 +729,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -836,6 +837,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1088,6 +1090,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1225,7 +1236,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1338,7 +1349,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlcipher 3.31.0 to adapt to many kinds of systems. +\`configure' configures sqlcipher 3.33.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1378,6 +1389,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1403,7 +1415,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlcipher 3.31.0:";; + short | recursive ) echo "Configuration of sqlcipher 3.33.0:";; esac cat <<\_ACEOF @@ -1540,7 +1552,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlcipher configure 3.31.0 +sqlcipher configure 3.33.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1959,7 +1971,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlcipher $as_me 3.31.0, which was +It was created by sqlcipher $as_me 3.33.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -4446,7 +4458,7 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; -netbsd*) +netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else @@ -4809,7 +4821,7 @@ esac fi : ${AR=ar} -: ${AR_FLAGS=cru} +: ${AR_FLAGS=cr} @@ -5352,11 +5364,8 @@ _LT_EOF test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 - (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s "$nlist"; then + $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5 + if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" @@ -6575,8 +6584,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 - echo "$AR cru libconftest.a conftest.o" >&5 - $AR cru libconftest.a conftest.o 2>&5 + echo "$AR cr libconftest.a conftest.o" >&5 + $AR cr libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF @@ -7701,6 +7710,12 @@ lt_prog_compiler_static= lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; + # flang / f18. f95 an alias for gfortran or flang on Debian + flang* | f18* | f95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) @@ -8177,6 +8192,9 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie openbsd* | bitrig*) with_gnu_ld=no ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; esac ld_shlibs=yes @@ -8431,7 +8449,7 @@ _LT_EOF fi ;; - netbsd*) + netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= @@ -9101,6 +9119,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi + link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' @@ -9122,7 +9141,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; } esac ;; - netbsd*) + netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else @@ -10237,6 +10256,18 @@ fi dynamic_linker='GNU/Linux ld.so' ;; +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + netbsd*) version_type=sunos need_lib_prefix=no @@ -11366,7 +11397,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -11412,7 +11443,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -11436,7 +11467,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -11481,7 +11512,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -11505,7 +11536,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -12844,7 +12875,7 @@ if test "${enable_amalgamation+set}" = set; then : enableval=$enable_amalgamation; fi -if test "${enable_amalgamation}" == "no" ; then +if test "${enable_amalgamation}" = "no" ; then USE_AMALGAMATION=0 fi @@ -13195,7 +13226,7 @@ if test "${enable_update_limit+set}" = set; then : enableval=$enable_update_limit; fi -if test "${enable_udlimit}" = "yes" ; then +if test "${enable_update_limit}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT" fi @@ -13819,7 +13850,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlcipher $as_me 3.31.0, which was +This file was extended by sqlcipher $as_me 3.33.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13885,7 +13916,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlcipher config.status 3.31.0 +sqlcipher config.status 3.33.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -14869,7 +14900,6 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. diff --git a/configure.ac b/configure.ac index 25f6bed..e70bf12 100644 --- a/configure.ac +++ b/configure.ac @@ -624,7 +624,7 @@ AC_SUBST(TARGET_DEBUG) # See whether we should use the amalgamation to build AC_ARG_ENABLE(amalgamation, AC_HELP_STRING([--disable-amalgamation], [Disable the amalgamation and instead build all files separately])) -if test "${enable_amalgamation}" == "no" ; then +if test "${enable_amalgamation}" = "no" ; then USE_AMALGAMATION=0 fi AC_SUBST(USE_AMALGAMATION) @@ -706,7 +706,7 @@ fi # statements. AC_ARG_ENABLE(update-limit, AC_HELP_STRING([--enable-update-limit], [Enable the UPDATE/DELETE LIMIT clause])) -if test "${enable_udlimit}" = "yes" ; then +if test "${enable_update_limit}" = "yes" ; then OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT" fi diff --git a/doc/lemon.html b/doc/lemon.html index 056ae5f..714cbfa 100644 --- a/doc/lemon.html +++ b/doc/lemon.html @@ -104,9 +104,13 @@ Write all output files into directory. Normally, output files are written into the directory that contains the input grammar file.
  • -Dname Define C preprocessor macro name. This macro is usable by -"%ifdef" and -"%ifndef" lines +"%ifdef", +"%ifndef", and +"%if lines in the grammar file. +
  • -E +Run the "%if" preprocessor step only and print the revised grammar +file.
  • -g Do not generate a parser. Instead write the input grammar to standard output with all comments, actions, and other extraneous text removed. @@ -123,7 +127,7 @@ Suppress generation of the report file.
  • -r Do not sort or renumber the parser states as part of optimization.
  • -s -Show parser statistics before existing. +Show parser statistics before exiting.
  • -Tfile Use file as the template for the generated C-code parser implementation.
  • -x @@ -488,7 +492,7 @@ is an error.

    The precedence of a grammar rule is equal to the precedence of the left-most terminal symbol in the rule for which a precedence is defined. This is normally what you want, but in those cases where -you want to precedence of a grammar rule to be something different, +you want the precedence of a grammar rule to be something different, you can specify an alternative precedence symbol by putting the symbol in square braces after the period at the end of the rule and before any C-code. For example:

    @@ -555,9 +559,11 @@ other than that, the order of directives in Lemon is arbitrary.

  • %default_destructor
  • %default_type
  • %destructor +
  • %else
  • %endif
  • %extra_argument
  • %fallback +
  • %if
  • %ifdef
  • %ifndef
  • %include @@ -689,7 +695,7 @@ on Parse().

    The %extra_context directive

    -The %extra_context directive instructs Lemon to add a 2th parameter +The %extra_context directive instructs Lemon to add a 2nd parameter to the parameter list of the ParseAlloc() and ParseInif() functions. Lemon doesn't do anything itself with these extra argument, but it does store the value make it available to C-code action routines, destructors, @@ -699,9 +705,9 @@ and so forth. For example, if the grammar file contains:

    %extra_context { MyStruct *pAbc }

    -

    Then the ParseAlloc() and ParseInit() functions will have an 2th parameter +

    Then the ParseAlloc() and ParseInit() functions will have an 2nd parameter of type "MyStruct*" and all action routines will have access to -a variable named "pAbc" that is the value of that 2th parameter.

    +a variable named "pAbc" that is the value of that 2nd parameter.

    The %extra_argument directive works the same except that it is passed in on the Parse() routine instead of on ParseAlloc()/ParseInit(). @@ -737,10 +743,11 @@ arguments are tokens which fall back to the token identified by the first argument.

    -

    The %ifdef, %ifndef, and %endif directives

    +

    The %if directive and its friends

    -

    The %ifdef, %ifndef, and %endif directives -are similar to #ifdef, #ifndef, and #endif in the C-preprocessor, +

    The %if, %ifdef, %ifndef, %else, +and %endif directives +are similar to #if, #ifdef, #ifndef, #else, and #endif in the C-preprocessor, just not as general. Each of these directives must begin at the left margin. No whitespace is allowed between the "%" and the directive name.

    @@ -749,12 +756,22 @@ is allowed between the "%" and the directive name.

    "%endif" is ignored unless the "-DMACRO" command-line option is used. Grammar text betwen "%ifndef MACRO" and the next nested "%endif" is -included except when the "-DMACRO" command-line option is used.

    +included except when the "-DMACRO" command-line option is used.

    -

    Note that the argument to %ifdef and %ifndef must -be a single preprocessor symbol name, not a general expression. -There is no "%else" directive.

    +

    The text in between "%if CONDITIONAL" and its +corresponding %endif is included only if CONDITIONAL +is true. The CONDITION is one or more macro names, optionally connected +using the "||" and "&&" binary operators, the "!" unary operator, +and grouped using balanced parentheses. Each term is true if the +corresponding macro exists, and false if it does not exist.

    +

    An optional "%else" directive can occur anywhere in between a +%ifdef, %ifndef, or %if directive and +its corresponding %endif.

    + +

    Note that the argument to %ifdef and %ifndef is +intended to be a single preprocessor symbol name, not a general expression. +Use the "%if" directive for general expressions.

    The %include directive

    @@ -996,7 +1013,7 @@ on the parser's stack associated with terminal and non-terminal symbols. The values of all terminal symbols must be of the same type. This turns out to be the same data type as the 3rd parameter to the Parse() function generated by Lemon. Typically, you will -make the value of a terminal symbol by a pointer to some kind of +make the value of a terminal symbol be a pointer to some kind of token structure. Like this:

    diff --git a/doc/wal-lock.md b/doc/wal-lock.md
    new file mode 100644
    index 0000000..d74bb88
    --- /dev/null
    +++ b/doc/wal-lock.md
    @@ -0,0 +1,88 @@
    +# Wal-Mode Blocking Locks
    +
    +On some Unix-like systems, SQLite may be configured to use POSIX blocking locks
    +by:
    +
    +  * building the library with SQLITE\_ENABLE\_SETLK\_TIMEOUT defined, and 
    +  * configuring a timeout in ms using the sqlite3\_busy\_timeout() API.
    +
    +Blocking locks may be advantageous as (a) waiting database clients do not
    +need to continuously poll the database lock, and (b) using blocking locks
    +facilitates transfer of OS priority between processes when a high priority
    +process is blocked by a lower priority one.
    +
    +Only read/write clients use blocking locks. Clients that have read-only access
    +to the \*-shm file nevery use blocking locks.
    +
    +Threads or processes that access a single database at a time never deadlock as
    +a result of blocking database locks. But it is of course possible for threads
    +that lock multiple databases simultaneously to do so. In most cases the OS will
    +detect the deadlock and return an error.
    +
    +## Wal Recovery
    +
    +Wal database "recovery" is a process required when the number of connected
    +database clients changes from zero to one. In this case, a client is 
    +considered to connect to the database when it first reads data from it.
    +Before recovery commences, an exclusive WRITER lock is taken. 
    +
    +Without blocking locks, if two clients attempt recovery simultaneously, one
    +fails to obtain the WRITER lock and either invokes the busy-handler callback or
    +returns SQLITE\_BUSY to the user. With blocking locks configured, the second
    +client blocks on the WRITER lock.
    +
    +## Database Readers
    +
    +Usually, read-only are not blocked by any other database clients, so they 
    +have no need of blocking locks.
    +
    +If a read-only transaction is being opened on a snapshot, the CHECKPOINTER
    +lock is required briefly as part of opening the transaction (to check that a
    +checkpointer is not currently overwriting the snapshot being opened). A
    +blocking lock is used to obtain the CHECKPOINTER lock in this case. A snapshot
    +opener may therefore block on and transfer priority to a checkpointer in some
    +cases.
    +
    +## Database Writers
    +
    +A database writer must obtain the exclusive WRITER lock. It uses a blocking
    +lock to do so if any of the following are true:
    +
    +  * the transaction is an implicit one consisting of a single DML or DDL
    +    statement, or
    +  * the transaction is opened using BEGIN IMMEDIATE or BEGIN EXCLUSIVE, or
    +  * the first SQL statement executed following the BEGIN command is a DML or
    +    DDL statement (not a read-only statement like a SELECT).
    +
    +In other words, in all cases except when an open read-transaction is upgraded
    +to a write-transaction. In that case a non-blocking lock is used.
    +
    +## Database Checkpointers
    +
    +Database checkpointers takes the following locks, in order:
    +
    +  * The exclusive CHECKPOINTER lock.
    +  * The exclusive WRITER lock (FULL, RESTART and TRUNCATE only).
    +  * Exclusive lock on read-mark slots 1-N. These are immediately released after being taken.
    +  * Exclusive lock on read-mark 0.
    +  * Exclusive lock on read-mark slots 1-N again. These are immediately released
    +    after being taken (RESTART and TRUNCATE only).
    +
    +All of the above use blocking locks.
    +
    +## Summary
    +
    +With blocking locks configured, the only cases in which clients should see an
    +SQLITE\_BUSY error are:
    +
    +  * if the OS does not grant a blocking lock before the configured timeout
    +    expires, and
    +  * when an open read-transaction is upgraded to a write-transaction.
    +
    +In all other cases the blocking locks implementation should prevent clients
    +from having to handle SQLITE\_BUSY errors and facilitate appropriate transfer
    +of priorities between competing clients.
    +
    +Clients that lock multiple databases simultaneously must be wary of deadlock.
    +
    +
    diff --git a/ext/async/sqlite3async.c b/ext/async/sqlite3async.c
    index b6f4a4b..eed7c8d 100644
    --- a/ext/async/sqlite3async.c
    +++ b/ext/async/sqlite3async.c
    @@ -1704,4 +1704,3 @@ int sqlite3async_control(int op, ...){
     }
     
     #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ASYNCIO) */
    -
    diff --git a/ext/async/sqlite3async.h b/ext/async/sqlite3async.h
    index 5b20d71..13b23bc 100644
    --- a/ext/async/sqlite3async.h
    +++ b/ext/async/sqlite3async.h
    @@ -220,4 +220,3 @@ int sqlite3async_control(int op, ...);
     }  /* End of the 'extern "C"' block */
     #endif
     #endif        /* ifndef __SQLITEASYNC_H_ */
    -
    diff --git a/ext/expert/expert1.test b/ext/expert/expert1.test
    index a0a18f6..3e5d604 100644
    --- a/ext/expert/expert1.test
    +++ b/ext/expert/expert1.test
    @@ -37,6 +37,9 @@ proc squish {txt} {
     
     proc do_setup_rec_test {tn setup sql res} {
       reset_db
    +  if {[info exists ::set_main_db_name]} {
    +    dbconfig_maindbname_icecube db
    +  }
       db eval $setup
       uplevel [list do_rec_test $tn $sql $res]
     }
    @@ -76,6 +79,10 @@ foreach {tn setup} {
         }
       }
       3 {
    +    if {[info commands sqlite3_expert_new]==""} { continue }
    +    set ::set_main_db_name 1
    +  }
    +  4 {
         if {![file executable $CLI]} { continue }
     
         proc do_rec_test {tn sql res} {
    @@ -336,7 +343,7 @@ proc do_candidates_test {tn sql res} {
     
     
     reset_db
    -do_execsql_test 4.0 {
    +do_execsql_test 5.0 {
       CREATE TABLE t1(a, b);
       CREATE TABLE t2(c, d);
     
    @@ -346,7 +353,7 @@ do_execsql_test 4.0 {
       WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
       INSERT INTO t2 SELECT (i-1)/20, (i-1)/5 FROM s;
     }
    -do_candidates_test 4.1 {
    +do_candidates_test 5.1 {
       SELECT * FROM t1,t2 WHERE (b=? OR a=?) AND (c=? OR d=?)
     } {
       CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20 
    @@ -355,14 +362,14 @@ do_candidates_test 4.1 {
       CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
     }
     
    -do_candidates_test 4.2 {
    +do_candidates_test 5.2 {
       SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=?
     } {
       CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 17
       CREATE INDEX t2_idx_0001295b ON t2(c, d); -- stat1: 100 20 5
     }
     
    -do_execsql_test 4.3 {
    +do_execsql_test 5.3 {
       CREATE INDEX t1_idx_00000061 ON t1(a); -- stat1: 100 50 
       CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20 
       CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 16
    diff --git a/ext/expert/sqlite3expert.c b/ext/expert/sqlite3expert.c
    index e88fb7e..1dd0700 100644
    --- a/ext/expert/sqlite3expert.c
    +++ b/ext/expert/sqlite3expert.c
    @@ -1128,14 +1128,19 @@ int idxFindIndexes(
           /* int iParent = sqlite3_column_int(pExplain, 1); */
           /* int iNotUsed = sqlite3_column_int(pExplain, 2); */
           const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
    -      int nDetail = STRLEN(zDetail);
    +      int nDetail;
           int i;
     
    +      if( !zDetail ) continue;
    +      nDetail = STRLEN(zDetail);
    +
           for(i=0; ipTab;
       const char *zTab = pTab->zName;
       const char *zSql = 
    -    "SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_master "
    +    "SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema "
         "WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
         "ORDER BY type;";
       sqlite3_stmt *pSelect = 0;
    @@ -1318,12 +1323,12 @@ static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
       **   2) Create the equivalent virtual table in dbv.
       */
       rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
    -      "SELECT type, name, sql, 1 FROM sqlite_master "
    +      "SELECT type, name, sql, 1 FROM sqlite_schema "
           "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
           " UNION ALL "
    -      "SELECT type, name, sql, 2 FROM sqlite_master "
    +      "SELECT type, name, sql, 2 FROM sqlite_schema "
           "WHERE type = 'trigger'"
    -      "  AND tbl_name IN(SELECT name FROM sqlite_master WHERE type = 'view') "
    +      "  AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
           "ORDER BY 4, 1"
       );
       while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
    @@ -1493,7 +1498,7 @@ static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
       int rc = SQLITE_OK;
       const char *zMax = 
         "SELECT max(i.seqno) FROM "
    -    "  sqlite_master AS s, "
    +    "  sqlite_schema AS s, "
         "  pragma_index_list(s.name) AS l, "
         "  pragma_index_info(l.name) AS i "
         "WHERE s.type = 'table'";
    @@ -1646,7 +1651,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
     
       const char *zAllIndex =
         "SELECT s.rowid, s.name, l.name FROM "
    -    "  sqlite_master AS s, "
    +    "  sqlite_schema AS s, "
         "  pragma_index_list(s.name) AS l "
         "WHERE s.type = 'table'";
       const char *zIndexXInfo = 
    @@ -1720,7 +1725,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
       sqlite3_free(pCtx);
     
       if( rc==SQLITE_OK ){
    -    rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_master", 0, 0, 0);
    +    rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
       }
     
       sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
    @@ -1759,7 +1764,7 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
       if( rc==SQLITE_OK ){
         sqlite3_stmt *pSql;
         rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg, 
    -        "SELECT sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%%'"
    +        "SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
             " AND sql NOT LIKE 'CREATE VIRTUAL %%'"
         );
         while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
    @@ -1950,4 +1955,4 @@ void sqlite3_expert_destroy(sqlite3expert *p){
       }
     }
     
    -#endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */
    +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
    diff --git a/ext/fts3/README.content b/ext/fts3/README.content
    index ab98675..b6a7539 100644
    --- a/ext/fts3/README.content
    +++ b/ext/fts3/README.content
    @@ -174,5 +174,3 @@ EXTERNAL CONTENT FTS4 TABLES
       only be useful if the full-text index has somehow become corrupt. It is an
       error to attempt to rebuild the full-text index maintained by a contentless
       FTS4 table.
    -
    -
    diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c
    index 77738eb..79dc5c8 100644
    --- a/ext/fts3/fts3.c
    +++ b/ext/fts3/fts3.c
    @@ -962,6 +962,22 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
       return zRet;
     }
     
    +/*
    +** Buffer z contains a positive integer value encoded as utf-8 text.
    +** Decode this value and store it in *pnOut, returning the number of bytes
    +** consumed. If an overflow error occurs return a negative value.
    +*/
    +int sqlite3Fts3ReadInt(const char *z, int *pnOut){
    +  u64 iVal = 0;
    +  int i;
    +  for(i=0; z[i]>='0' && z[i]<='9'; i++){
    +    iVal = iVal*10 + (z[i] - '0');
    +    if( iVal>0x7FFFFFFF ) return -1;
    +  }
    +  *pnOut = (int)iVal;
    +  return i;
    +}
    +
     /*
     ** This function interprets the string at (*pp) as a non-negative integer
     ** value. It reads the integer and sets *pnOut to the value read, then 
    @@ -977,19 +993,17 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
     */
     static int fts3GobbleInt(const char **pp, int *pnOut){
       const int MAX_NPREFIX = 10000000;
    -  const char *p;                  /* Iterator pointer */
       int nInt = 0;                   /* Output value */
    -
    -  for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
    -    nInt = nInt * 10 + (p[0] - '0');
    -    if( nInt>MAX_NPREFIX ){
    -      nInt = 0;
    -      break;
    -    }
    +  int nByte;
    +  nByte = sqlite3Fts3ReadInt(*pp, &nInt);
    +  if( nInt>MAX_NPREFIX ){
    +    nInt = 0;
    +  }
    +  if( nByte==0 ){
    +    return SQLITE_ERROR;
       }
    -  if( p==*pp ) return SQLITE_ERROR;
       *pnOut = nInt;
    -  *pp = p;
    +  *pp += nByte;
       return SQLITE_OK;
     }
     
    @@ -1884,6 +1898,7 @@ static int fts3ScanInteriorNode(
       i64 nAlloc = 0;                 /* Size of allocated buffer */
       int isFirstTerm = 1;            /* True when processing first term on page */
       sqlite3_int64 iChild;           /* Block id of child node to descend to */
    +  int nBuffer = 0;                /* Total term size */
     
       /* Skip over the 'height' varint that occurs at the start of every 
       ** interior node. Then load the blockid of the left-child of the b-tree
    @@ -1908,12 +1923,15 @@ static int fts3ScanInteriorNode(
         int cmp;                      /* memcmp() result */
         int nSuffix;                  /* Size of term suffix */
         int nPrefix = 0;              /* Size of term prefix */
    -    int nBuffer;                  /* Total term size */
       
         /* Load the next term on the node into zBuffer. Use realloc() to expand
         ** the size of zBuffer if required.  */
         if( !isFirstTerm ){
           zCsr += fts3GetVarint32(zCsr, &nPrefix);
    +      if( nPrefix>nBuffer ){
    +        rc = FTS_CORRUPT_VTAB;
    +        goto finish_scan;
    +      }
         }
         isFirstTerm = 0;
         zCsr += fts3GetVarint32(zCsr, &nSuffix);
    @@ -2050,7 +2068,7 @@ static void fts3PutDeltaVarint(
       sqlite3_int64 *piPrev,          /* IN/OUT: Previous value written to list */
       sqlite3_int64 iVal              /* Write this value to the list */
     ){
    -  assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
    +  assert_fts3_nc( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
       *pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
       *piPrev = iVal;
     }
    @@ -2167,7 +2185,9 @@ static void fts3ReadNextPos(
       sqlite3_int64 *pi             /* IN/OUT: Value read from position-list */
     ){
       if( (**pp)&0xFE ){
    -    fts3GetDeltaVarint(pp, pi);
    +    int iVal;
    +    *pp += fts3GetVarint32((*pp), &iVal);
    +    *pi += iVal;
         *pi -= 2;
       }else{
         *pi = POSITION_LIST_END;
    @@ -2247,6 +2267,9 @@ static int fts3PoslistMerge(
           */
           fts3GetDeltaVarint(&p1, &i1);
           fts3GetDeltaVarint(&p2, &i2);
    +      if( i1<2 || i2<2 ){
    +        break;
    +      }
           do {
             fts3PutDeltaVarint(&p, &iPrev, (i1nColumn;
    -        /* fall-through */
    +        /* no break */ deliberate_fall_through
           }
     
         default:
    @@ -3699,9 +3722,13 @@ static void fts3SnippetFunc(
     
       switch( nVal ){
         case 6: nToken = sqlite3_value_int(apVal[5]);
    +            /* no break */ deliberate_fall_through
         case 5: iCol = sqlite3_value_int(apVal[4]);
    +            /* no break */ deliberate_fall_through
         case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
    +            /* no break */ deliberate_fall_through
         case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
    +            /* no break */ deliberate_fall_through
         case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
       }
       if( !zEllipsis || !zEnd || !zStart ){
    @@ -4500,7 +4527,7 @@ void sqlite3Fts3DoclistNext(
     
       assert( nDoclist>0 );
       assert( *pbEof==0 );
    -  assert( p || *piDocid==0 );
    +  assert_fts3_nc( p || *piDocid==0 );
       assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) );
     
       if( p==0 ){
    @@ -5150,7 +5177,7 @@ static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){
     **
     ** Parameter nNear is passed the NEAR distance of the expression (5 in
     ** the example above). When this function is called, *paPoslist points to
    -** the position list, and *pnToken is the number of phrase tokens in, the
    +** the position list, and *pnToken is the number of phrase tokens in the
     ** phrase on the other side of the NEAR operator to pPhrase. For example,
     ** if pPhrase refers to the "def ghi" phrase, then *paPoslist points to
     ** the position list associated with phrase "abc".
    @@ -5185,10 +5212,12 @@ static int fts3EvalNearTrim(
       );
       if( res ){
         nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
    -    assert( pPhrase->doclist.pList[nNew]=='\0' );
    -    assert( nNew<=pPhrase->doclist.nList && nNew>0 );
    -    memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
    -    pPhrase->doclist.nList = nNew;
    +    if( nNew>=0 ){
    +      assert( pPhrase->doclist.pList[nNew]=='\0' );
    +      assert( nNew<=pPhrase->doclist.nList && nNew>0 );
    +      memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
    +      pPhrase->doclist.nList = nNew;
    +    }
         *paPoslist = pPhrase->doclist.pList;
         *pnToken = pPhrase->nToken;
       }
    @@ -5297,6 +5326,7 @@ static void fts3EvalNextRow(
                     fts3EvalNextRow(pCsr, pLeft, pRc);
                   }
                 }
    +            pRight->bEof = pLeft->bEof = 1;
               }
             }
             break;
    @@ -5539,7 +5569,10 @@ static int fts3EvalTestExpr(
             }else
     #endif
             {
    -          bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
    +          bHit = ( 
    +              pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId
    +           && pExpr->pPhrase->doclist.nList>0
    +          );
             }
             break;
           }
    @@ -5802,7 +5835,8 @@ static int fts3EvalGatherStats(
           fts3EvalRestart(pCsr, pRoot, &rc);
           do {
             fts3EvalNextRow(pCsr, pRoot, &rc);
    -        assert( pRoot->bEof==0 );
    +        assert_fts3_nc( pRoot->bEof==0 );
    +        if( pRoot->bEof ) rc = FTS_CORRUPT_VTAB;
           }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
         }
       }
    diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h
    index 50370a9..b3e0ec8 100644
    --- a/ext/fts3/fts3Int.h
    +++ b/ext/fts3/fts3Int.h
    @@ -199,6 +199,8 @@ typedef sqlite3_int64 i64;        /* 8-byte signed integer */
     #define LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
     #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
     
    +#define deliberate_fall_through
    +
     #endif /* SQLITE_AMALGAMATION */
     
     #ifdef SQLITE_DEBUG
    @@ -591,6 +593,7 @@ int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
     int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
     void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
     int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
    +int sqlite3Fts3ReadInt(const char *z, int *pnOut);
     
     /* fts3_tokenizer.c */
     const char *sqlite3Fts3NextToken(const char *, int *);
    diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c
    index 5775fbc..e19137a 100644
    --- a/ext/fts3/fts3_expr.c
    +++ b/ext/fts3/fts3_expr.c
    @@ -446,10 +446,7 @@ static int getNextNode(
           if( pKey->eType==FTSQUERY_NEAR ){
             assert( nKey==4 );
             if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
    -          nNear = 0;
    -          for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
    -            nNear = nNear * 10 + (zInput[nKey] - '0');
    -          }
    +          nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
             }
           }
     
    diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c
    index 2b20ba1..ebc771f 100644
    --- a/ext/fts3/fts3_snippet.c
    +++ b/ext/fts3/fts3_snippet.c
    @@ -876,7 +876,7 @@ static int fts3ExprLHits(
         iStart = pExpr->iPhrase * ((p->nCol + 31) / 32);
       }
     
    -  while( 1 ){
    +  if( pIter ) while( 1 ){
         int nHit = fts3ColumnlistCount(&pIter);
         if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
           if( p->flag==FTS3_MATCHINFO_LHITS ){
    diff --git a/ext/fts3/fts3_tokenize_vtab.c b/ext/fts3/fts3_tokenize_vtab.c
    index 313a3c0..8bd2223 100644
    --- a/ext/fts3/fts3_tokenize_vtab.c
    +++ b/ext/fts3/fts3_tokenize_vtab.c
    @@ -188,7 +188,8 @@ static int fts3tokConnectMethod(
     
       assert( (rc==SQLITE_OK)==(pMod!=0) );
       if( rc==SQLITE_OK ){
    -    const char * const *azArg = (const char * const *)&azDequote[1];
    +    const char * const *azArg = 0;
    +    if( nDequote>1 ) azArg = (const char * const *)&azDequote[1];
         rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok);
       }
     
    @@ -350,7 +351,7 @@ static int fts3tokFilterMethod(
         if( pCsr->zInput==0 ){
           rc = SQLITE_NOMEM;
         }else{
    -      memcpy(pCsr->zInput, zByte, nByte);
    +      if( nByte>0 ) memcpy(pCsr->zInput, zByte, nByte);
           pCsr->zInput[nByte] = 0;
           rc = pTab->pMod->xOpen(pTab->pTok, pCsr->zInput, nByte, &pCsr->pCsr);
           if( rc==SQLITE_OK ){
    diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c
    index 197aebd..092cad9 100644
    --- a/ext/fts3/fts3_write.c
    +++ b/ext/fts3/fts3_write.c
    @@ -341,7 +341,9 @@ static int fts3SqlStmt(
     ** created by merging the oldest :2 segments from absolute level :1. See 
     ** function sqlite3Fts3Incrmerge() for details.  */
     /* 29 */ "SELECT 2 * total(1 + leaves_end_block - start_block) "
    -         "  FROM %Q.'%q_segdir' WHERE level = ? AND idx < ?",
    +         "  FROM (SELECT * FROM %Q.'%q_segdir' "
    +         "        WHERE level = ? ORDER BY idx ASC LIMIT ?"
    +         "  )",
     
     /* SQL_DELETE_SEGDIR_ENTRY
     **   Delete the %_segdir entry on absolute level :1 with index :2.  */
    @@ -1415,6 +1417,7 @@ static int fts3SegReaderNext(
       */
       if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode)
        || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
    +   || pReader->nDoclist==0
       ){
         return FTS_CORRUPT_VTAB;
       }
    @@ -2502,7 +2505,7 @@ static int fts3SegmentIsMaxLevel(Fts3Table *p, i64 iAbsLevel, int *pbMax){
       if( rc!=SQLITE_OK ) return rc;
       sqlite3_bind_int64(pStmt, 1, iAbsLevel+1);
       sqlite3_bind_int64(pStmt, 2, 
    -      ((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
    +      (((u64)iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
       );
     
       *pbMax = 0;
    @@ -2852,6 +2855,19 @@ int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
       return SQLITE_OK;
     }
     
    +static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){
    +  if( nReq>pCsr->nBuffer ){
    +    char *aNew;
    +    pCsr->nBuffer = nReq*2;
    +    aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
    +    if( !aNew ){
    +      return SQLITE_NOMEM;
    +    }
    +    pCsr->aBuffer = aNew;
    +  }
    +  return SQLITE_OK;
    +}
    +
     
     int sqlite3Fts3SegReaderStep(
       Fts3Table *p,                   /* Virtual table handle */
    @@ -2986,15 +3002,9 @@ int sqlite3Fts3SegReaderStep(
               }
     
               nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
    -          if( nDoclist+nByte>pCsr->nBuffer ){
    -            char *aNew;
    -            pCsr->nBuffer = (nDoclist+nByte)*2;
    -            aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
    -            if( !aNew ){
    -              return SQLITE_NOMEM;
    -            }
    -            pCsr->aBuffer = aNew;
    -          }
    +
    +          rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist);
    +          if( rc ) return rc;
     
               if( isFirst ){
                 char *a = &pCsr->aBuffer[nDoclist];
    @@ -3019,6 +3029,9 @@ int sqlite3Fts3SegReaderStep(
             fts3SegReaderSort(apSegment, nMerge, j, xCmp);
           }
           if( nDoclist>0 ){
    +        rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING);
    +        if( rc ) return rc;
    +        memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING);
             pCsr->aDoclist = pCsr->aBuffer;
             pCsr->nDoclist = nDoclist;
             rc = SQLITE_ROW;
    @@ -3068,11 +3081,11 @@ static void fts3ReadEndBlockField(
       if( zText ){
         int i;
         int iMul = 1;
    -    i64 iVal = 0;
    +    u64 iVal = 0;
         for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
           iVal = iVal*10 + (zText[i] - '0');
         }
    -    *piEndBlock = iVal;
    +    *piEndBlock = (i64)iVal;
         while( zText[i]==' ' ) i++;
         iVal = 0;
         if( zText[i]=='-' ){
    @@ -3082,7 +3095,7 @@ static void fts3ReadEndBlockField(
         for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
           iVal = iVal*10 + (zText[i] - '0');
         }
    -    *pnByte = (iVal * (i64)iMul);
    +    *pnByte = ((i64)iVal * (i64)iMul);
       }
     }
     
    @@ -4287,7 +4300,7 @@ static int fts3IncrmergeLoad(
           int i;
           int nHeight = (int)aRoot[0];
           NodeWriter *pNode;
    -      if( nHeight<1 || nHeight>FTS_MAX_APPENDABLE_HEIGHT ){
    +      if( nHeight<1 || nHeight>=FTS_MAX_APPENDABLE_HEIGHT ){
             sqlite3_reset(pSelect);
             return FTS_CORRUPT_VTAB;
           }
    @@ -4953,6 +4966,12 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
         ** Exit early in this case.  */
         if( nSeg<=0 ) break;
     
    +    assert( nMod<=0x7FFFFFFF );
    +    if( iAbsLevel<0 || iAbsLevel>(nMod<<32) ){
    +      rc = FTS_CORRUPT_VTAB;
    +      break;
    +    }
    +
         /* Open a cursor to iterate through the contents of the oldest nSeg 
         ** indexes of absolute level iAbsLevel. If this cursor is opened using 
         ** the 'hint' parameters, it is possible that there are less than nSeg
    diff --git a/ext/fts3/tool/fts3view.c b/ext/fts3/tool/fts3view.c
    index 37f9b73..9558cde 100644
    --- a/ext/fts3/tool/fts3view.c
    +++ b/ext/fts3/tool/fts3view.c
    @@ -93,7 +93,7 @@ static int runSql(sqlite3 *db, const char *zFormat, ...){
     static void showSchema(sqlite3 *db, const char *zTab){
       sqlite3_stmt *pStmt;
       pStmt = prepare(db,
    -            "SELECT sql FROM sqlite_master"
    +            "SELECT sql FROM sqlite_schema"
                 " WHERE name LIKE '%q%%'"
                 " ORDER BY 1",
                 zTab);
    @@ -831,7 +831,7 @@ int main(int argc, char **argv){
         sqlite3_stmt *pStmt;
         int cnt = 0;
         pStmt = prepare(db, "SELECT b.sql"
    -                        "  FROM sqlite_master a, sqlite_master b"
    +                        "  FROM sqlite_schema a, sqlite_schema b"
                             " WHERE a.name GLOB '*_segdir'"
                             "   AND b.name=substr(a.name,1,length(a.name)-7)"
                             " ORDER BY 1");
    diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
    index eb20af8..5e1155c 100644
    --- a/ext/fts5/fts5_index.c
    +++ b/ext/fts5/fts5_index.c
    @@ -2321,11 +2321,11 @@ static void fts5LeafSeek(
       }
     
      search_success:
    -  pIter->iLeafOffset = iOff + nNew;
    -  if( pIter->iLeafOffset>n || nNew<1 ){
    +  if( (i64)iOff+nNew>n || nNew<1 ){
         p->rc = FTS5_CORRUPT;
         return;
       }
    +  pIter->iLeafOffset = iOff + nNew;
       pIter->iTermLeafOffset = pIter->iLeafOffset;
       pIter->iTermLeafPgno = pIter->iLeafPgno;
     
    @@ -5732,8 +5732,8 @@ static int fts5QueryCksum(
     ** contain valid utf-8, return non-zero.
     */
     static int fts5TestUtf8(const char *z, int n){
    -  assert_nc( n>0 );
       int i = 0;
    +  assert_nc( n>0 );
       while( ibBusy ){
    +    pVTab->zErrMsg = sqlite3_mprintf(
    +       "recursive definition for %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
    +    );
    +    return SQLITE_ERROR;
    +  }
       zSql = sqlite3Fts5Mprintf(&rc,
           "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
           pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
    @@ -343,10 +350,12 @@ static int fts5VocabOpenMethod(
       assert( rc==SQLITE_OK || pStmt==0 );
       if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
     
    +  pTab->bBusy = 1;
       if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
         i64 iId = sqlite3_column_int64(pStmt, 0);
         pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId);
       }
    +  pTab->bBusy = 0;
     
       if( rc==SQLITE_OK ){
         if( pFts5==0 ){
    diff --git a/ext/fts5/test/fts5corrupt3.test b/ext/fts5/test/fts5corrupt3.test
    index 4e0ae64..25aa094 100644
    --- a/ext/fts5/test/fts5corrupt3.test
    +++ b/ext/fts5/test/fts5corrupt3.test
    @@ -10108,6 +10108,221 @@ do_catchsql_test 68.1 {
       INSERT INTO t1(t1) SELECT x FROM t2;
     } {1 {database disk image is malformed}}
     
    +#-------------------------------------------------------------------------
    +reset_db
    +do_test 69.0 {
    +  sqlite3 db {}
    +  db deserialize [decode_hexdb {
    +.open --hexdb
    +| size 32768 pagesize 4096 filename crash-31c462b8b665d0.db
    +| page 1 offset 0
    +|      0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00   SQLite format 3.
    +|     16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 08   .....@  ........
    +|     32: 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00   ................
    +|     96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36   ...............6
    +|    112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 00 00 00 00 00 00   ...k............
    +|   3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74   .........1tablet
    +|   3488: 32 74 32 08 43 52 45 41 54 45 20 54 41 42 4c 45   2t2.CREATE TABLE
    +|   3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61    t2(x)V.......ta
    +|   3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63   blet1_configt1_c
    +|   3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42   onfig.CREATE TAB
    +|   3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b   LE 't1_config'(k
    +|   3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29    PRIMARY KEY, v)
    +|   3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06    WITHOUT ROWID[.
    +|   3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64   ..!!...tablet1_d
    +|   3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65   ocsizet1_docsize
    +|   3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74   .CREATE TABLE 't
    +|   3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e   1_docsize'(id IN
    +|   3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45   TEGER PRIMARY KE
    +|   3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21   Y, sz BLOB)^...!
    +|   3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74   !...tablet1_cont
    +|   3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52   entt1_content.CR
    +|   3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63   EATE TABLE 't1_c
    +|   3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47   ontent'(id INTEG
    +|   3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20   ER PRIMARY KEY, 
    +|   3776: 63 39 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19   c9, c1, c2)i....
    +|   3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74   ...-tablet1_idxt
    +|   3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42   1_idx.CREATE TAB
    +|   3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69   LE 't1_idx'(segi
    +|   3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50   d, term, pgno, P
    +|   3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64   RIMARY KEY(segid
    +|   3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54   , term)) WITHOUT
    +|   3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74    ROWIDU........t
    +|   3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61   ablet1_datat1_da
    +|   3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20   ta.CREATE TABLE 
    +|   3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54   't1_data'(id INT
    +|   3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59   EGER PRIMARY KEY
    +|   3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06   , block BLOB)8..
    +|   3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52   ...._tablet1t1CR
    +|   4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42   EATE VIRTUAL TAB
    +|   4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35   LE t1 USING fts5
    +|   4032: 28 61 2c 62 2c 63 29 00 00 00 00 00 00 00 00 00   (a,b,c).........
    +| page 3 offset 8192
    +|      0: 0d 00 00 00 03 0c 94 00 0f e6 0f ef 0c 94 00 00   ................
    +|   3216: 00 00 00 00 86 4a 84 80 80 80 80 01 04 00 8d 18   .....J..........
    +|   3232: 00 00 03 2b 02 30 30 01 02 06 01 02 06 01 02 06   ...+.00.........
    +|   3248: 1f 02 03 01 02 03 01 02 03 01 08 32 30 31 36 30   ...........20160
    +|   3264: 36 30 39 01 02 07 01 02 07 01 02 07 01 01 34 01   609...........4.
    +|   3280: 02 05 01 02 05 01 02 05 01 01 35 01 02 04 01 02   ..........5.....
    +|   3296: 04 01 02 04 02 07 30 30 30 30 30 30 30 1c 02 04   ......0000000...
    +|   3312: 01 02 04 01 02 04 01 06 62 69 6e 61 72 79 03 06   ........binary..
    +|   3328: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01   ................
    +|   3344: 02 02 03 06 00 02 02 03 06 01 02 02 03 06 01 02   ................
    +|   3360: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02   ................
    +|   3376: 03 06 01 02 02 03 06 01 02 02 01 08 63 6f 6d 70   ............comp
    +|   3392: 69 6c 65 72 01 02 02 01 02 02 01 02 02 01 06 64   iler...........d
    +|   3408: 62 73 74 61 74 07 02 03 01 02 03 01 02 03 02 04   bstat...........
    +|   3424: 65 62 75 67 04 02 02 01 02 02 01 02 02 01 06 65   ebug...........e
    +|   3440: 6e 61 62 6c 65 07 02 02 01 02 02 01 02 02 01 02   nable...........
    +|   3456: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02   ................
    +|   3472: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01   ................
    +|   3488: 01 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02   ................
    +|   3504: 02 01 02 02 02 08 78 74 65 6e 73 69 6f 6e 1f 02   ......xtension..
    +|   3520: 04 01 02 04 01 02 04 01 04 66 74 73 34 1a 02 03   .........fts4...
    +|   3536: 01 02 03 01 02 03 04 01 35 0d 02 03 01 02 03 01   ........5.......
    +|   3552: 02 03 01 03 67 63 63 01 aa 03 01 02 03 01 02 03   ....gcc.........
    +|   3568: 02 06 65 6f 70 6f 6c 79 10 02 03 02 02 03 01 02   ..eopoly........
    +|   3584: 03 01 05 6a 73 6f 6e 31 13 02 03 01 02 03 01 02   ...json1........
    +|   3600: 03 01 04 6c 6f 61 64 1f 02 03 01 02 03 01 02 03   ...load.........
    +|   3616: 01 03 6d 61 78 1c 02 02 01 02 02 01 02 02 02 05   ..max...........
    +|   3632: 65 6d 6f 72 79 1c 02 03 01 02 03 01 02 03 04 04   emory...........
    +|   3648: 73 79 73 35 16 02 03 01 02 03 01 02 03 01 06 6e   sys5...........n
    +|   3664: 6f 63 61 73 65 02 06 01 02 02 03 06 01 02 02 03   ocase...........
    +|   3680: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06   ................
    +|   3696: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01   ................
    +|   3712: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02   ................
    +|   3728: 02 01 04 6f 6d 69 74 1f 02 02 01 02 02 01 02 01   ...omit.........
    +|   3744: ff ff ff ff ff ff ff ff f0 00 00 00 00 00 01 02   ................
    +|   3760: 58 81 96 4d 01 06 01 02 02 03 06 01 02 02 03 06   X..M............
    +|   3776: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01   ................
    +|   3792: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02   ................
    +|   3808: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02   ................
    +|   3824: 01 0a 74 68 72 65 61 64 73 61 66 65 22 02 02 01   ..threadsafe....
    +|   3840: 02 02 01 02 02 01 04 76 74 61 62 07 02 04 01 02   .......vtab.....
    +|   3856: 04 01 02 04 01 01 78 01 06 01 01 02 01 06 01 01   ......x.........
    +|   3872: 02 01 06 01 1e 02 01 06 01 01 02 01 06 01 01 02   ................
    +|   3888: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01   ................
    +|   3904: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06   ................
    +|   3920: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01   ................
    +|   3936: 00 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01   ................
    +|   3952: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02   ................
    +|   3968: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01   ................
    +|   3984: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06   ................
    +|   4000: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01   ................
    +|   4016: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01   ................
    +|   4032: 02 01 06 01 01 02 01 06 01 01 02 04 15 13 0c 0c   ................
    +|   4048: 12 44 13 11 0f 47 13 0f 0b 0e 11 10 0f 0e 10 0f   .D...G..........
    +|   4064: 44 0f 10 40 15 0f 07 01 03 00 14 24 5a 24 24 0f   D..@.......$Z$$.
    +|   4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01   ...$............
    +| page 4 offset 12288
    +|      0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
    +|   4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02   ................
    +| page 5 offset 16384
    +|      0: 0d 00 00 00 24 0c 0a 00 0f d8 0f af 0f 86 0f 74   ....$..........t
    +|     16: 0f 61 0f 4e 0f 2f 0f 0f 0e ef 0e d7 0e be 0e a5   .a.N./..........
    +|     32: 0e 8d 0e 74 0e 5b 0e 40 0e 24 0e 08 0d ef 0d d5   ...t.[.@.$......
    +|     48: 0d bb 0d a0 0d 84 0d 68 0d 4f 0d 00 00 00 00 00   .......h.O......
    +|   3072: 00 00 00 00 00 00 00 00 00 00 18 24 05 00 25 0f   ...........$..%.
    +|   3088: 19 54 48 52 45 41 44 53 41 46 45 3d 30 58 42 49   .THREADSAFE=0XBI
    +|   3104: 4e 41 52 59 18 23 05 00 25 0f 19 54 48 52 45 41   NARY.#..%..THREA
    +|   3120: 44 53 41 46 45 3d 30 58 4e 4f 43 41 53 45 17 22   DSAFE=0XNOCASE..
    +|   3136: 05 00 25 0f 17 54 48 52 45 41 44 53 41 46 45 3d   ..%..THREADSAFE=
    +|   3152: 30 58 52 54 52 49 4d 1f 21 05 00 33 0f 19 4f 4d   0XRTRIM.!..3..OM
    +|   3168: 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 4f   IT LOAD EXTENSIO
    +|   3184: 4e 58 42 49 4e 41 52 59 1f 20 05 00 33 d3 19 4f   NXBINARY. ..3..O
    +|   3200: 4d 49 54 28 2c 4f 41 44 b2 04 55 85 44 54 e5 34   MIT(,OAD..U.DT.4
    +|   3216: 94 f4 e5 84 e4 f4 34 15 34 51 e1 f0 50 03 30 f1   ......4.4Q..P.0.
    +|   3232: 74 f4 d4 95 42 04 c4 f4 14 42 04 55 85 44 54 e5   t...B....B.U.DT.
    +|   3248: 34 94 f4 e5 85 25 45 24 94 d1 f1 e0 50 03 30 f1   4....%E$....P.0.
    +|   3264: 94 d4 15 82 04 d4 54 d4 f5 25 93 d3 53 03 03 03   ......T..%..S...
    +|   3280: 03 03 03 05 84 24 94 e4 15 25 91 f1 d0 50 03 30   .....$...%...P.0
    +|   3296: f1 94 d4 15 82 04 d4 54 d4 f5 25 93 d3 53 03 03   .......T..%..S..
    +|   3312: 03 03 03 03 05 84 e4 f4 34 15 34 51 e1 c0 50 03   ........4.4Q..P.
    +|   3328: 30 f1 74 d4 15 82 04 d4 54 d4 f5 25 93 d3 53 03   0.t.....T..%..S.
    +|   3344: 03 03 03 03 03 05 85 25 45 24 94 d1 81 b0 50 02   .......%E$....P.
    +|   3360: 50 f1 94 54 e4 14 24 c4 52 05 25 45 24 54 55 84   P..T..$.R.%E$TU.
    +|   3376: 24 94 e4 15 25 91 81 a0 50 02 50 f1 94 54 e4 14   $...%...P.P..T..
    +|   3392: 24 c4 52 05 25 45 24 54 55 84 e4 f4 34 15 34 51   $.R.%E$TU...4.4Q
    +|   3408: 71 90 50 02 50 f1 74 54 e4 14 24 c4 52 05 25 45   q.P.P.tT..$.R.%E
    +|   3424: 24 54 55 85 25 45 24 94 d1 a1 80 50 02 90 f1 94   $TU.%E$....P....
    +|   3440: 54 e4 14 24 c4 52 04 d4 54 d5 35 95 33 55 84 24   T..$.R..T.5.3U.$
    +|   3456: 94 e4 15 25 91 a1 70 50 02 90 f1 94 54 e4 14 24   ...%..pP....T..$
    +|   3472: c4 52 04 d4 54 d5 35 95 33 55 84 e4 f4 34 15 34   .R..T.5.3U...4.4
    +|   3488: 51 91 60 50 02 90 f1 74 54 e4 14 24 c4 52 04 d4   Q.`P...tT..$.R..
    +|   3504: 54 d5 35 95 33 55 85 25 45 24 94 d1 81 50 50 02   T.5.3U.%E$...PP.
    +|   3520: 50 f1 94 54 e4 14 24 c4 52 04 a5 34 f4 e3 15 84   P..T..$.R..4....
    +|   3536: 24 94 e4 15 25 91 81 40 50 02 50 f1 94 54 e4 14   $...%..@P.P..T..
    +|   3552: 24 c4 52 04 a5 34 f4 e3 15 84 e4 f4 34 15 34 51   $.R..4......4.4Q
    +|   3568: 71 30 50 02 4f f1 74 54 e4 14 24 c4 52 04 a5 34   q0P.O.tT..$.R..4
    +|   3584: f4 e3 15 85 25 45 24 94 d1 a1 20 50 02 90 f1 94   ....%E$... P....
    +|   3600: 54 e4 14 24 c4 52 04 74 54 f5 04 f4 c5 95 84 24   T..$.R.tT......$
    +|   3616: 94 e4 15 25 91 a1 10 50 02 90 f1 94 54 e4 14 24   ...%...P....T..$
    +|   3632: c4 52 04 74 54 f5 04 f4 c5 95 84 e4 f4 34 15 34   .R.tT........4.4
    +|   3648: 51 91 00 50 02 90 f1 74 54 e4 14 24 c4 51 f4 74   Q..P...tT..$.Q.t
    +|   3664: 54 f5 04 f4 c5 95 85 25 45 24 94 d1 70 f0 50 02   T......%E$..p.P.
    +|   3680: 30 f1 94 54 e4 14 24 c5 20 46 54 53 35 58 42 49   0..T..$. FTS5XBI
    +|   3696: 4e 41 52 59 17 0e 05 00 23 0f 19 45 4e 41 42 4c   NARY....#..ENABL
    +|   3712: 45 20 46 54 53 35 58 4f 4f 43 41 53 45 16 0d 05   E FTS5XOOCASE...
    +|   3728: 00 23 0f 17 45 4e 41 42 4c 45 20 46 54 53 35 58   .#..ENABLE FTS5X
    +|   3744: 52 54 52 49 4d 17 0c 05 00 23 0f 19 45 4e 41 42   RTRIM....#..ENAB
    +|   3760: 4c 45 20 46 54 53 34 58 42 49 4e 41 52 59 97 0b   LE FTS4XBINARY..
    +|   3776: 05 00 23 0f 19 45 4e 41 42 4c 45 20 46 54 53 34   ..#..ENABLE FTS4
    +|   3792: 58 4e 4f 43 41 53 45 16 0a 05 00 23 0f 17 45 4e   XNOCASE....#..EN
    +|   3808: 41 42 4c 45 20 46 54 53 34 58 52 54 52 49 4d 1e   ABLE FTS4XRTRIM.
    +|   3824: 09 05 00 3e 5f 19 45 4e 41 42 4c 45 20 44 42 53   ...>_.ENABLE DBS
    +|   3840: 44 41 54 20 56 54 41 42 58 42 49 4e 41 52 59 1e   DAT VTABXBINARY.
    +|   3856: 08 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53   ...1..ENABLE DBS
    +|   3872: 54 41 54 20 56 54 41 42 58 4e 4f 43 4d e3 45 1d   TAT VTABXNOCM.E.
    +|   3888: 07 05 00 31 0f 17 45 4e 41 42 4c 45 20 44 42 53   ...1..ENABLE DBS
    +|   3904: 54 41 54 20 56 54 41 42 58 52 54 52 49 4d 11 06   TAT VTABXRTRIM..
    +|   3920: 05 00 17 0f 19 44 45 42 55 47 58 42 49 4e 41 52   .....DEBUGXBINAR
    +|   3936: 59 11 05 05 00 17 0f 19 44 45 42 55 47 58 4e 4f   Y.......DEBUGXNO
    +|   3952: 43 41 53 45 10 02 02 50 08 5f 17 44 45 42 55 47   CASE...P._.DEBUG
    +|   3968: 58 52 54 52 49 4d 27 03 05 00 44 0f 19 43 4f 4d   XRTRIM'...D..COM
    +|   3984: 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e 30 20   PILER=gcc-5.4.0 
    +|   4000: 32 30 31 36 30 36 30 39 58 42 49 4e 41 52 59 27   20160609XBINARY'
    +|   4016: 02 05 00 43 0f 19 43 4f 4d 50 49 4c 45 52 3d 67   ...C..COMPILER=g
    +|   4032: 63 63 2d 35 2e 34 2e 30 20 32 30 31 36 30 36 30   cc-5.4.0 2016060
    +|   4048: 39 58 4e 4f 43 41 53 45 26 01 05 00 43 c9 17 43   9XNOCASE&...C..C
    +|   4064: 4f 4d 50 49 4c 47 02 3d 67 63 63 2d 35 2e 34 2e   OMPILG.=gcc-5.4.
    +|   4080: 30 20 32 30 31 36 30 36 30 39 58 52 54 52 49 4d   0 20160609XRTRIM
    +| page 6 offset 20480
    +|      0: 0d 00 00 00 24 0e e0 00 0f f8 0f f0 0f e8 0f e0   ....$...........
    +|     16: 0f d8 0f d0 0f c8 0f c0 0f b8 0f b0 0f a8 0f a0   ................
    +|     32: 0f 98 0f 90 0f 88 0f 80 0f 78 0f 70 0f 68 0f 60   .........x.p.h.`
    +|     48: 0f 58 0f 50 0f 48 0f 40 0f 38 00 00 00 00 00 00   .X.P.H.@.8......
    +|   3808: 06 24 03 00 12 02 01 01 06 23 03 00 12 02 01 01   .$.......#......
    +|   3824: 06 22 03 00 12 02 01 01 06 21 03 00 12 03 01 01   .........!......
    +|   3840: 06 20 03 00 12 03 01 01 06 1f 03 00 12 03 01 01   . ..............
    +|   3856: 06 1e 03 00 12 03 01 01 06 1d 03 00 12 03 01 01   ................
    +|   3872: 06 1c 03 00 12 03 01 01 06 1b 03 00 12 02 01 01   ................
    +|   3888: 06 1a 03 00 12 02 01 01 06 19 03 00 12 02 01 01   ................
    +|   3904: 06 18 03 00 12 02 01 01 06 17 03 00 12 02 01 01   ................
    +|   3920: 06 16 03 00 12 02 01 01 06 15 03 00 12 02 01 01   ................
    +|   3936: 06 14 03 00 12 02 01 01 06 13 03 00 12 02 01 01   ................
    +|   3952: 06 12 03 00 12 02 01 01 06 11 03 00 12 02 01 01   ................
    +|   3968: 06 10 03 00 12 02 01 01 06 1f 03 00 12 02 01 01   ................
    +|   3984: 06 0e 03 00 12 02 01 01 06 0d 03 00 12 02 01 01   ................
    +|   4000: 06 0c 03 00 12 02 01 01 06 0b 03 00 12 02 01 01   ................
    +|   4016: 06 0a 03 00 12 02 01 01 06 09 03 00 12 03 01 01   ................
    +|   4032: 06 08 03 00 12 03 01 01 06 07 03 00 12 03 01 01   ................
    +|   4048: 06 06 03 00 12 01 01 01 06 05 03 00 12 01 01 01   ................
    +|   4064: 06 04 03 00 12 01 01 01 06 03 03 00 12 06 01 01   ................
    +|   4080: 06 02 03 00 12 06 01 01 06 01 03 00 12 06 01 01   ................
    +| page 7 offset 24576
    +|      0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00   ................
    +|   4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04   ........version.
    +| page 8 offset 28672
    +|      0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
    +|   4048: 00 00 00 00 00 00 11 03 02 2b 69 6e 74 65 67 72   .........+integr
    +|   4064: 69 74 79 2d 63 68 65 63 6b 09 00 00 00 00 00 00   ity-check.......
    +| end crash-31c462b8b665d0.db
    +}]} {}
    +
    +
    +do_catchsql_test 69.2 {
    +  SELECT * FROM t1 WHERE a MATCH 'fx*'
    +} {1 {database disk image is malformed}}
     
     sqlite3_fts5_may_be_corrupt 0
     finish_test
    diff --git a/ext/fts5/test/fts5matchinfo.test b/ext/fts5/test/fts5matchinfo.test
    index d8d8d84..5706933 100644
    --- a/ext/fts5/test/fts5matchinfo.test
    +++ b/ext/fts5/test/fts5matchinfo.test
    @@ -500,14 +500,18 @@ do_execsql_test 15.0 {
       INSERT INTO t1 VALUES('c', 'd');
     }
     
    +if {$tcl_platform(byteOrder)=="littleEndian"} {
    +  set res {X'02000000'}
    +} else {
    +  set res {X'00000002'}
    +}
     do_execsql_test 15.1 {
       SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
    -} {X'02000000'}
    -
    +} $res
     do_execsql_test 15.2 {
       DELETE FROM t1_content WHERE rowid=1;
       SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
    -} {X'02000000'}
    +} $res
     
     fts5_aux_test_functions db
     do_execsql_test 15.3 {
    @@ -517,4 +521,3 @@ do_execsql_test 15.3 {
     }
     
     finish_test
    -
    diff --git a/ext/fts5/test/fts5vocab.test b/ext/fts5/test/fts5vocab.test
    index a1bf2a4..c457c5c 100644
    --- a/ext/fts5/test/fts5vocab.test
    +++ b/ext/fts5/test/fts5vocab.test
    @@ -542,5 +542,16 @@ do_execsql_test 10.7.3 {
       SELECT * FROM t2 WHERE term=?;
     }
     
    -finish_test
    +# 2020-02-16  Detect recursively define fts5vocab() tables.
    +# Error found by dbsqlfuzz.
    +#
    +reset_db
    +do_execsql_test 11.100 {
    +  CREATE VIRTUAL TABLE t3 USING fts5vocab(rowid , 'col');
    +  CREATE VIRTUAL TABLE rowid USING fts5vocab(rowid , 'instance');
    +} {}
    +do_catchsql_test 11.110 {
    +  SELECT rowid+1,rowid, * FROM t3 WHERE null>rowid ;
    +} {1 {SQL logic error}}
     
    +finish_test
    diff --git a/ext/icu/README.txt b/ext/icu/README.txt
    index af75d22..be443f5 100644
    --- a/ext/icu/README.txt
    +++ b/ext/icu/README.txt
    @@ -116,7 +116,8 @@ SQLite. Documentation follows.
       and use it as a dynamically loadable SQLite extension. To do this
       using gcc on *nix:
     
    -    gcc -shared icu.c `icu-config --ldflags` -o libSqliteIcu.so
    +    gcc -fPIC -shared icu.c `pkg-config --libs --cflags icu-uc icu-io` \
    +        -o libSqliteIcu.so
     
       You may need to add "-I" flags so that gcc can find sqlite3ext.h
       and sqlite3.h. The resulting shared lib, libSqliteIcu.so, may be
    diff --git a/ext/icu/icu.c b/ext/icu/icu.c
    index 7fbe32a..92d7c54 100644
    --- a/ext/icu/icu.c
    +++ b/ext/icu/icu.c
    @@ -143,7 +143,7 @@ static int icuLikeCompare(
         **     3. uPattern is an unescaped escape character, or
         **     4. uPattern is to be handled as an ordinary character
         */
    -    if( !prevEscape && uPattern==MATCH_ALL ){
    +    if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
           /* Case 1. */
           uint8_t c;
     
    @@ -169,12 +169,12 @@ static int icuLikeCompare(
           }
           return 0;
     
    -    }else if( !prevEscape && uPattern==MATCH_ONE ){
    +    }else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
           /* Case 2. */
           if( *zString==0 ) return 0;
           SQLITE_ICU_SKIP_UTF8(zString);
     
    -    }else if( !prevEscape && uPattern==(uint32_t)uEsc){
    +    }else if( uPattern==(uint32_t)uEsc && !prevEscape ){
           /* Case 3. */
           prevEscape = 1;
     
    diff --git a/ext/icu/sqliteicu.h b/ext/icu/sqliteicu.h
    index 69b42f9..24a4d62 100644
    --- a/ext/icu/sqliteicu.h
    +++ b/ext/icu/sqliteicu.h
    @@ -24,4 +24,3 @@ int sqlite3IcuInit(sqlite3 *db);
     #ifdef __cplusplus
     }  /* extern "C" */
     #endif  /* __cplusplus */
    -
    diff --git a/ext/lsm1/lsm-test/lsmtest1.c b/ext/lsm1/lsm-test/lsmtest1.c
    index dcbc718..1ce2cc0 100644
    --- a/ext/lsm1/lsm-test/lsmtest1.c
    +++ b/ext/lsm1/lsm-test/lsmtest1.c
    @@ -654,5 +654,3 @@ void test_data_3(
         testFree(zName);
       }
     }
    -
    -
    diff --git a/ext/lsm1/lsm-test/lsmtest8.c b/ext/lsm1/lsm-test/lsmtest8.c
    index f734ac6..7efa0df 100644
    --- a/ext/lsm1/lsm-test/lsmtest8.c
    +++ b/ext/lsm1/lsm-test/lsmtest8.c
    @@ -322,5 +322,3 @@ void do_writer_crash_test(const char *zPattern, int *pRc){
       }
     
     }
    -
    -
    diff --git a/ext/lsm1/lsm-test/lsmtest9.c b/ext/lsm1/lsm-test/lsmtest9.c
    index 144cae7..b01de0d 100644
    --- a/ext/lsm1/lsm-test/lsmtest9.c
    +++ b/ext/lsm1/lsm-test/lsmtest9.c
    @@ -138,6 +138,3 @@ void test_data_4(
         testFree(zName);
       }
     }
    -
    -
    -
    diff --git a/ext/lsm1/lsm-test/lsmtest_bt.c b/ext/lsm1/lsm-test/lsmtest_bt.c
    index 5135dd0..8a4f54a 100644
    --- a/ext/lsm1/lsm-test/lsmtest_bt.c
    +++ b/ext/lsm1/lsm-test/lsmtest_bt.c
    @@ -69,7 +69,3 @@ int do_bt(int nArg, char **azArg){
       sqlite4_buffer_clear(&buf.output);
       return 0;
     }
    -
    -
    -
    -
    diff --git a/ext/lsm1/lsm-test/lsmtest_tdb.c b/ext/lsm1/lsm-test/lsmtest_tdb.c
    index 9c4f9df..8f63f64 100644
    --- a/ext/lsm1/lsm-test/lsmtest_tdb.c
    +++ b/ext/lsm1/lsm-test/lsmtest_tdb.c
    @@ -553,7 +553,7 @@ static int sql_begin(TestDb *pTestDb, int iLevel){
       /* If there are no transactions at all open, open a read transaction. */
       if( pDb->nOpenTrans==0 ){
         int rc = sqlite3_exec(pDb->db, 
    -        "BEGIN; SELECT * FROM sqlite_master LIMIT 1;" , 0, 0, 0
    +        "BEGIN; SELECT * FROM sqlite_schema LIMIT 1;" , 0, 0, 0
         );
         if( rc!=0 ) return rc;
         pDb->nOpenTrans = 1;
    diff --git a/ext/lsm1/lsm-test/lsmtest_tdb2.cc b/ext/lsm1/lsm-test/lsmtest_tdb2.cc
    index 307c2b5..86ebb49 100644
    --- a/ext/lsm1/lsm-test/lsmtest_tdb2.cc
    +++ b/ext/lsm1/lsm-test/lsmtest_tdb2.cc
    @@ -367,4 +367,3 @@ int test_mdb_scan(
     }
     
     #endif /* HAVE_MDB */
    -
    diff --git a/ext/lsm1/lsm-test/lsmtest_tdb4.c b/ext/lsm1/lsm-test/lsmtest_tdb4.c
    index c45b052..1f92928 100644
    --- a/ext/lsm1/lsm-test/lsmtest_tdb4.c
    +++ b/ext/lsm1/lsm-test/lsmtest_tdb4.c
    @@ -978,5 +978,3 @@ static int bgc_detach(BtDb *pDb){
     /*
     ** End of background checkpointer.
     *************************************************************************/
    -
    -
    diff --git a/ext/lsm1/lsm_unix.c b/ext/lsm1/lsm_unix.c
    index 2224077..88952d1 100644
    --- a/ext/lsm1/lsm_unix.c
    +++ b/ext/lsm1/lsm_unix.c
    @@ -228,6 +228,10 @@ static int lsmPosixOsRemap(
         }
     
         p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
    +    if( p->pMap==MAP_FAILED ){
    +      p->pMap = 0;
    +      return LSM_IOERR_BKPT;
    +    }
         p->nMap = iSz;
       }
     
    @@ -413,7 +417,10 @@ static int lsmPosixOsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
         p->apShm[iChunk] = mmap(0, LSM_SHM_CHUNK_SIZE, 
             PROT_READ|PROT_WRITE, MAP_SHARED, p->shmfd, iChunk*LSM_SHM_CHUNK_SIZE
         );
    -    if( p->apShm[iChunk]==0 ) return LSM_IOERR_BKPT;
    +    if( p->apShm[iChunk]==MAP_FAILED ){
    +      p->apShm[iChunk] = 0;
    +      return LSM_IOERR_BKPT;
    +    }
       }
     
       *ppShm = p->apShm[iChunk];
    diff --git a/ext/misc/appendvfs.c b/ext/misc/appendvfs.c
    index b224245..14260ef 100644
    --- a/ext/misc/appendvfs.c
    +++ b/ext/misc/appendvfs.c
    @@ -439,7 +439,7 @@ static int apndOpen(
       p = (ApndFile*)pFile;
       memset(p, 0, sizeof(*p));
       pSubFile = ORIGFILE(pFile);
    -  p->base.pMethods = &apnd_io_methods;
    +  pFile->pMethods = &apnd_io_methods;
       rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
       if( rc ) goto apnd_open_done;
       rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
    diff --git a/ext/misc/btreeinfo.c b/ext/misc/btreeinfo.c
    index 3d818f2..22f8268 100644
    --- a/ext/misc/btreeinfo.c
    +++ b/ext/misc/btreeinfo.c
    @@ -21,18 +21,18 @@
     **    name TEXT,                   -- Name of table or index for this btree.
     **    tbl_name TEXT,               -- Associated table
     **    rootpage INT,                -- The root page of the btree
    -**    sql TEXT,                    -- SQL for this btree - from sqlite_master
    +**    sql TEXT,                    -- SQL for this btree - from sqlite_schema
     **    hasRowid BOOLEAN,            -- True if the btree has a rowid
    -**    nEntry INT,                  -- Estimated number of enteries
    +**    nEntry INT,                  -- Estimated number of entries
     **    nPage INT,                   -- Estimated number of pages
     **    depth INT,                   -- Depth of the btree
     **    szPage INT,                  -- Size of each page in bytes
     **    zSchema TEXT HIDDEN          -- The schema to which this btree belongs
     ** );
     **
    -** The first 5 fields are taken directly from the sqlite_master table.
    +** The first 5 fields are taken directly from the sqlite_schema table.
     ** Considering only the first 5 fields, the only difference between 
    -** this virtual table and the sqlite_master table is that this virtual
    +** this virtual table and the sqlite_schema table is that this virtual
     ** table omits all entries that have a 0 or NULL rowid - in other words
     ** it omits triggers and views.
     **
    @@ -88,7 +88,7 @@ typedef struct BinfoCursor BinfoCursor;
     /* A cursor for the sqlite_btreeinfo table */
     struct BinfoCursor {
       sqlite3_vtab_cursor base;       /* Base class.  Must be first */
    -  sqlite3_stmt *pStmt;            /* Query against sqlite_master */
    +  sqlite3_stmt *pStmt;            /* Query against sqlite_schema */
       int rc;                         /* Result of previous sqlite_step() call */
       int hasRowid;                   /* hasRowid value.  Negative if unknown. */
       sqlite3_int64 nEntry;           /* nEntry value */
    @@ -242,10 +242,10 @@ static int binfoFilter(
         pCsr->zSchema = sqlite3_mprintf("main");
       }
       zSql = sqlite3_mprintf(
    -      "SELECT 0, 'table','sqlite_master','sqlite_master',1,NULL "
    +      "SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
           "UNION ALL "
           "SELECT rowid, type, name, tbl_name, rootpage, sql"
    -      " FROM \"%w\".sqlite_master WHERE rootpage>=1",
    +      " FROM \"%w\".sqlite_schema WHERE rootpage>=1",
            pCsr->zSchema);
       sqlite3_finalize(pCsr->pStmt);
       pCsr->pStmt = 0;
    diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c
    new file mode 100644
    index 0000000..64cd37f
    --- /dev/null
    +++ b/ext/misc/cksumvfs.c
    @@ -0,0 +1,797 @@
    +/*
    +** 2020-04-20
    +**
    +** 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 implements a VFS shim that writes a checksum on each page
    +** of an SQLite database file.  When reading pages, the checksum is verified
    +** and an error is raised if the checksum is incorrect.
    +**
    +** COMPILING
    +**
    +** This extension requires SQLite 3.32.0 or later.  It uses the
    +** sqlite3_database_file_object() interface which was added in
    +** version 3.32.0, so it will not link with an earlier version of
    +** SQLite.
    +**
    +** To build this extension as a separately loaded shared library or
    +** DLL, use compiler command-lines similar to the following:
    +**
    +**   (linux)    gcc -fPIC -shared cksumvfs.c -o cksumvfs.so
    +**   (mac)      clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib
    +**   (windows)  cl cksumvfs.c -link -dll -out:cksumvfs.dll
    +**
    +** You may want to add additional compiler options, of course,
    +** according to the needs of your project.
    +**
    +** If you want to statically link this extension with your product,
    +** then compile it like any other C-language module but add the
    +** "-DSQLITE_CKSUMVFS_STATIC" option so that this module knows that
    +** it is being statically linked rather than dynamically linked
    +**
    +** LOADING
    +**
    +** To load this extension as a shared library, you first have to
    +** bring up a dummy SQLite database connection to use as the argument
    +** to the sqlite3_load_extension() API call.  Then you invoke the
    +** sqlite3_load_extension() API and shutdown the dummy database
    +** connection.  All subsequent database connections that are opened
    +** will include this extension.  For example:
    +**
    +**     sqlite3 *db;
    +**     sqlite3_open(":memory:", &db);
    +**     sqlite3_load_extention(db, "./cksumvfs");
    +**     sqlite3_close(db);
    +**
    +** If this extension is compiled with -DSQLITE_CKSUMVFS_STATIC and
    +** statically linked against the application, initialize it using
    +** a single API call as follows:
    +**
    +**     sqlite3_cksumvfs_init();
    +**
    +** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new
    +** default VFS and it uses the prior default VFS as the next VFS
    +** down in the stack.  This is normally what you want.  However, it
    +** complex situations where multiple VFS shims are being loaded,
    +** it might be important to ensure that cksumvfs is loaded in the
    +** correct order so that it sequences itself into the default VFS
    +** Shim stack in the right order.
    +**
    +** USING
    +**
    +** Open database connections using the sqlite3_open() or 
    +** sqlite3_open_v2() interfaces, as normal.  Ordinary database files
    +** (without a checksum) will operate normally.  Databases with 
    +** checksums will return an SQLITE_IOERR_DATA error if a page is
    +** encountered that contains an invalid checksum.
    +**
    +** Checksumming only works on databases that have a reserve-bytes
    +** value of exactly 8.  The default value for reserve-bytes is 0.
    +** Hence, newly created database files will omit the checksum by
    +** default.  To create a database that includes a checksum, change
    +** the reserve-bytes value to 8 by runing:
    +**
    +**    int n = 8;
    +**    sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVED_BYTES, &n);
    +**
    +** If you do this immediately after creating a new database file,
    +** before anything else has been written into the file, then that
    +** might be all that you need to do.  Otherwise, the API call
    +** above should be followed by:
    +**
    +**    sqlite3_exec(db, "VACUUM", 0, 0, 0);
    +**
    +** It never hurts to run the VACUUM, even if you don't need it.
    +** If the database is in WAL mode, you should shutdown and
    +** reopen all database connections before continuing.
    +**
    +** From the CLI, use the ".filectrl reserve_bytes 8" command, 
    +** followed by "VACUUM;".
    +**
    +** Note that SQLite allows the number of reserve-bytes to be
    +** increased but not decreased.  So if a database file already
    +** has a reserve-bytes value greater than 8, there is no way to
    +** activate checksumming on that database, other than to dump
    +** and restore the database file.  Note also that other extensions
    +** might also make use of the reserve-bytes.  Checksumming will
    +** be incompatible with those other extensions.
    +**
    +** VERIFICATION OF CHECKSUMS
    +**
    +** If any checksum is incorrect, the "PRAGMA quick_check" command
    +** will find it.  To verify that checksums are actually enabled
    +** and running, use the following query:
    +**
    +**   SELECT count(*), verify_checksum(data)
    +**     FROM sqlite_dbpage
    +**    GROUP BY 2;
    +**
    +** There are three possible outputs form the verify_checksum()
    +** function: 1, 0, and NULL.  1 is returned if the checksum is
    +** correct.  0 is returned if the checksum is incorrect.  NULL
    +** is returned if the page is unreadable.  If checksumming is
    +** enabled, the read will fail if the checksum is wrong, so the
    +** usual result from verify_checksum() on a bad checksum is NULL.
    +**
    +** If everything is OK, the query above should return a single
    +** row where the second column is 1.  Any other result indicates
    +** either that there is a checksum error, or checksum validation
    +** is disabled.
    +**
    +** CONTROLLING CHECKSUM VERIFICATION
    +**
    +** The cksumvfs extension implements a new PRAGMA statement that can
    +** be used to disable, re-enable, or query the status of checksum
    +** verification:
    +**
    +**    PRAGMA checksum_verification;          -- query status
    +**    PRAGMA checksum_verification=OFF;      -- disable verification
    +**    PRAGMA checksum_verification=ON;       -- re-enable verification
    +**
    +** The "checksum_verification" pragma will return "1" (true) or "0"
    +** (false) if checksum verification is enabled or disabled, respectively.
    +** "Verification" in this context means the feature that causes
    +** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while
    +** reading.  Checksums are always kept up-to-date as long as the
    +** reserve-bytes value of the database is 8, regardless of the setting
    +** of this pragma.  Checksum verification can be disabled (for example)
    +** to do forensic analysis of a database that has previously reported
    +** a checksum error.
    +**
    +** The "checksum_verification" pragma will always respond with "0" if
    +** the database file does not have a reserve-bytes value of 8.  The
    +** pragma will return no rows at all if the cksumvfs extension is
    +** not loaded.
    +**
    +** IMPLEMENTATION NOTES
    +**
    +** The checksum is stored in the last 8 bytes of each page.  This
    +** module only operates if the "bytes of reserved space on each page"
    +** value at offset 20 the SQLite database header is exactly 8.  If
    +** the reserved-space value is not 8, this module is a no-op.
    +*/
    +#ifdef SQLITE_CKSUMVFS_STATIC
    +# include "sqlite3.h"
    +#else
    +# include "sqlite3ext.h"
    +  SQLITE_EXTENSION_INIT1
    +#endif
    +#include 
    +#include 
    +
    +
    +/*
    +** Forward declaration of objects used by this utility
    +*/
    +typedef struct sqlite3_vfs CksmVfs;
    +typedef struct CksmFile CksmFile;
    +
    +/*
    +** Useful datatype abbreviations
    +*/
    +#if !defined(SQLITE_CORE)
    +  typedef unsigned char u8;
    +  typedef unsigned int u32;
    +#endif
    +
    +/* Access to a lower-level VFS that (might) implement dynamic loading,
    +** access to randomness, etc.
    +*/
    +#define ORIGVFS(p)  ((sqlite3_vfs*)((p)->pAppData))
    +#define ORIGFILE(p) ((sqlite3_file*)(((CksmFile*)(p))+1))
    +
    +/* An open file */
    +struct CksmFile {
    +  sqlite3_file base;    /* IO methods */
    +  const char *zFName;   /* Original name of the file */
    +  char computeCksm;     /* True to compute checksums.
    +                        ** Always true if reserve size is 8. */
    +  char verifyCksm;      /* True to verify checksums */
    +  char isWal;           /* True if processing a WAL file */
    +  char inCkpt;          /* Currently doing a checkpoint */
    +  CksmFile *pPartner;   /* Ptr from WAL to main-db, or from main-db to WAL */
    +};
    +
    +/*
    +** Methods for CksmFile
    +*/
    +static int cksmClose(sqlite3_file*);
    +static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
    +static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
    +static int cksmTruncate(sqlite3_file*, sqlite3_int64 size);
    +static int cksmSync(sqlite3_file*, int flags);
    +static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize);
    +static int cksmLock(sqlite3_file*, int);
    +static int cksmUnlock(sqlite3_file*, int);
    +static int cksmCheckReservedLock(sqlite3_file*, int *pResOut);
    +static int cksmFileControl(sqlite3_file*, int op, void *pArg);
    +static int cksmSectorSize(sqlite3_file*);
    +static int cksmDeviceCharacteristics(sqlite3_file*);
    +static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
    +static int cksmShmLock(sqlite3_file*, int offset, int n, int flags);
    +static void cksmShmBarrier(sqlite3_file*);
    +static int cksmShmUnmap(sqlite3_file*, int deleteFlag);
    +static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
    +static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
    +
    +/*
    +** Methods for CksmVfs
    +*/
    +static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
    +static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir);
    +static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *);
    +static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
    +static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename);
    +static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
    +static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
    +static void cksmDlClose(sqlite3_vfs*, void*);
    +static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut);
    +static int cksmSleep(sqlite3_vfs*, int microseconds);
    +static int cksmCurrentTime(sqlite3_vfs*, double*);
    +static int cksmGetLastError(sqlite3_vfs*, int, char *);
    +static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
    +static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
    +static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z);
    +static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName);
    +
    +static sqlite3_vfs cksm_vfs = {
    +  3,                            /* iVersion (set when registered) */
    +  0,                            /* szOsFile (set when registered) */
    +  1024,                         /* mxPathname */
    +  0,                            /* pNext */
    +  "cksmvfs",                    /* zName */
    +  0,                            /* pAppData (set when registered) */ 
    +  cksmOpen,                     /* xOpen */
    +  cksmDelete,                   /* xDelete */
    +  cksmAccess,                   /* xAccess */
    +  cksmFullPathname,             /* xFullPathname */
    +  cksmDlOpen,                   /* xDlOpen */
    +  cksmDlError,                  /* xDlError */
    +  cksmDlSym,                    /* xDlSym */
    +  cksmDlClose,                  /* xDlClose */
    +  cksmRandomness,               /* xRandomness */
    +  cksmSleep,                    /* xSleep */
    +  cksmCurrentTime,              /* xCurrentTime */
    +  cksmGetLastError,             /* xGetLastError */
    +  cksmCurrentTimeInt64,         /* xCurrentTimeInt64 */
    +  cksmSetSystemCall,            /* xSetSystemCall */
    +  cksmGetSystemCall,            /* xGetSystemCall */
    +  cksmNextSystemCall            /* xNextSystemCall */
    +};
    +
    +static const sqlite3_io_methods cksm_io_methods = {
    +  3,                              /* iVersion */
    +  cksmClose,                      /* xClose */
    +  cksmRead,                       /* xRead */
    +  cksmWrite,                      /* xWrite */
    +  cksmTruncate,                   /* xTruncate */
    +  cksmSync,                       /* xSync */
    +  cksmFileSize,                   /* xFileSize */
    +  cksmLock,                       /* xLock */
    +  cksmUnlock,                     /* xUnlock */
    +  cksmCheckReservedLock,          /* xCheckReservedLock */
    +  cksmFileControl,                /* xFileControl */
    +  cksmSectorSize,                 /* xSectorSize */
    +  cksmDeviceCharacteristics,      /* xDeviceCharacteristics */
    +  cksmShmMap,                     /* xShmMap */
    +  cksmShmLock,                    /* xShmLock */
    +  cksmShmBarrier,                 /* xShmBarrier */
    +  cksmShmUnmap,                   /* xShmUnmap */
    +  cksmFetch,                      /* xFetch */
    +  cksmUnfetch                     /* xUnfetch */
    +};
    +
    +/* Do byte swapping on a unsigned 32-bit integer */
    +#define BYTESWAP32(x) ( \
    +    (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8)  \
    +  + (((x)&0x00FF0000)>>8)  + (((x)&0xFF000000)>>24) \
    +)
    +
    +/* Compute a checksum on a buffer */
    +static void cksmCompute(
    +  u8 *a,           /* Content to be checksummed */
    +  int nByte,       /* Bytes of content in a[].  Must be a multiple of 8. */
    +  u8 *aOut         /* OUT: Final 8-byte checksum value output */
    +){
    +  u32 s1 = 0, s2 = 0;
    +  u32 *aData = (u32*)a;
    +  u32 *aEnd = (u32*)&a[nByte];
    +  u32 x = 1;
    +
    +  assert( nByte>=8 );
    +  assert( (nByte&0x00000007)==0 );
    +  assert( nByte<=65536 );
    +
    +  if( 1 == *(u8*)&x ){
    +    /* Little-endian */
    +    do {
    +      s1 += *aData++ + s2;
    +      s2 += *aData++ + s1;
    +    }while( aData65536 || (nByte & (nByte-1))!=0 ) return;
    +  cksmCompute(data, nByte-8, cksum);
    +  sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
    +}
    +
    +/*
    +** Close a cksm-file.
    +*/
    +static int cksmClose(sqlite3_file *pFile){
    +  CksmFile *p = (CksmFile *)pFile;
    +  if( p->pPartner ){
    +    assert( p->pPartner->pPartner==p );
    +    p->pPartner->pPartner = 0;
    +    p->pPartner = 0;
    +  }
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xClose(pFile);
    +}
    +
    +/*
    +** Set the computeCkSm and verifyCksm flags, if they need to be
    +** changed.
    +*/
    +static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){
    +  if( hasCorrectReserveSize!=p->computeCksm ){
    +    p->computeCksm = p->verifyCksm = hasCorrectReserveSize;
    +    if( p->pPartner ){
    +      p->pPartner->verifyCksm = hasCorrectReserveSize;
    +      p->pPartner->computeCksm = hasCorrectReserveSize;
    +    }
    +  }
    +}
    +
    +/*
    +** Read data from a cksm-file.
    +*/
    +static int cksmRead(
    +  sqlite3_file *pFile, 
    +  void *zBuf, 
    +  int iAmt, 
    +  sqlite_int64 iOfst
    +){
    +  int rc;
    +  CksmFile *p = (CksmFile *)pFile;
    +  pFile = ORIGFILE(pFile);
    +  rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
    +  if( rc==SQLITE_OK ){
    +    if( iOfst==0 && iAmt>=100 && memcmp(zBuf,"SQLite format 3",16)==0 ){
    +      u8 *d = (u8*)zBuf;
    +      char hasCorrectReserveSize = (d[20]==8);
    +      cksmSetFlags(p, hasCorrectReserveSize);
    +    }
    +    /* Verify the checksum if
    +    **    (1) the size indicates that we are dealing with a complete
    +    **        database page
    +    **    (2) checksum verification is enabled
    +    **    (3) we are not in the middle of checkpoint
    +    */
    +    if( iAmt>=512           /* (1) */
    +     && p->verifyCksm       /* (2) */
    +     && !p->inCkpt          /* (3) */
    +    ){
    +      u8 cksum[8];
    +      cksmCompute((u8*)zBuf, iAmt-8, cksum);
    +      if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
    +        sqlite3_log(SQLITE_IOERR_DATA,
    +           "checksum fault offset %lld of \"%s\"",
    +           iOfst, p->zFName);
    +        rc = SQLITE_IOERR_DATA;
    +      }
    +    }
    +  }
    +  return rc;
    +}
    +
    +/*
    +** Write data to a cksm-file.
    +*/
    +static int cksmWrite(
    +  sqlite3_file *pFile,
    +  const void *zBuf,
    +  int iAmt,
    +  sqlite_int64 iOfst
    +){
    +  CksmFile *p = (CksmFile *)pFile;
    +  pFile = ORIGFILE(pFile);
    +  if( iOfst==0 && iAmt>=100 && memcmp(zBuf,"SQLite format 3",16)==0 ){
    +    u8 *d = (u8*)zBuf;
    +    char hasCorrectReserveSize = (d[20]==8);
    +    cksmSetFlags(p, hasCorrectReserveSize);
    +  }
    +  /* If the write size is appropriate for a database page and if
    +  ** checksums where ever enabled, then it will be safe to compute
    +  ** the checksums.  The reserve byte size might have increased, but
    +  ** it will never decrease.  And because it cannot decrease, the
    +  ** checksum will not overwrite anything.
    +  */
    +  if( iAmt>=512
    +   && p->computeCksm
    +   && !p->inCkpt
    +  ){
    +    cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8);
    +  }
    +  return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
    +}
    +
    +/*
    +** Truncate a cksm-file.
    +*/
    +static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xTruncate(pFile, size);
    +}
    +
    +/*
    +** Sync a cksm-file.
    +*/
    +static int cksmSync(sqlite3_file *pFile, int flags){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xSync(pFile, flags);
    +}
    +
    +/*
    +** Return the current file-size of a cksm-file.
    +*/
    +static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
    +  CksmFile *p = (CksmFile *)pFile;
    +  pFile = ORIGFILE(p);
    +  return pFile->pMethods->xFileSize(pFile, pSize);
    +}
    +
    +/*
    +** Lock a cksm-file.
    +*/
    +static int cksmLock(sqlite3_file *pFile, int eLock){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xLock(pFile, eLock);
    +}
    +
    +/*
    +** Unlock a cksm-file.
    +*/
    +static int cksmUnlock(sqlite3_file *pFile, int eLock){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xUnlock(pFile, eLock);
    +}
    +
    +/*
    +** Check if another file-handle holds a RESERVED lock on a cksm-file.
    +*/
    +static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
    +}
    +
    +/*
    +** File control method. For custom operations on a cksm-file.
    +*/
    +static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){
    +  int rc;
    +  CksmFile *p = (CksmFile*)pFile;
    +  pFile = ORIGFILE(pFile);
    +  if( op==SQLITE_FCNTL_PRAGMA ){
    +    char **azArg = (char**)pArg;
    +    assert( azArg[1]!=0 );
    +    if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){
    +      char *zArg = azArg[2];
    +      if( zArg!=0 ){
    +        if( (zArg[0]>='1' && zArg[0]<='9')
    +         || sqlite3_strlike("enable%",zArg,0)==0
    +         || sqlite3_stricmp("yes",zArg)==0
    +         || sqlite3_stricmp("on",zArg)==0
    +        ){
    +          p->verifyCksm = p->computeCksm;
    +        }else{
    +          p->verifyCksm = 0;
    +        }
    +        if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm;
    +      }
    +      azArg[0] = sqlite3_mprintf("%d",p->verifyCksm);
    +      return SQLITE_OK;
    +    }else if( p->computeCksm && azArg[2]!=0
    +           && sqlite3_stricmp(azArg[1], "page_size")==0 ){
    +      /* Do not allow page size changes on a checksum database */
    +      return SQLITE_OK;
    +    }
    +  }else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){
    +    p->inCkpt = op==SQLITE_FCNTL_CKPT_START;
    +    if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt;
    +  }
    +  rc = pFile->pMethods->xFileControl(pFile, op, pArg);
    +  if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
    +    *(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg);
    +  }
    +  return rc;
    +}
    +
    +/*
    +** Return the sector-size in bytes for a cksm-file.
    +*/
    +static int cksmSectorSize(sqlite3_file *pFile){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xSectorSize(pFile);
    +}
    +
    +/*
    +** Return the device characteristic flags supported by a cksm-file.
    +*/
    +static int cksmDeviceCharacteristics(sqlite3_file *pFile){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xDeviceCharacteristics(pFile);
    +}
    +
    +/* Create a shared memory file mapping */
    +static int cksmShmMap(
    +  sqlite3_file *pFile,
    +  int iPg,
    +  int pgsz,
    +  int bExtend,
    +  void volatile **pp
    +){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
    +}
    +
    +/* Perform locking on a shared-memory segment */
    +static int cksmShmLock(sqlite3_file *pFile, int offset, int n, int flags){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xShmLock(pFile,offset,n,flags);
    +}
    +
    +/* Memory barrier operation on shared memory */
    +static void cksmShmBarrier(sqlite3_file *pFile){
    +  pFile = ORIGFILE(pFile);
    +  pFile->pMethods->xShmBarrier(pFile);
    +}
    +
    +/* Unmap a shared memory segment */
    +static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
    +}
    +
    +/* Fetch a page of a memory-mapped file */
    +static int cksmFetch(
    +  sqlite3_file *pFile,
    +  sqlite3_int64 iOfst,
    +  int iAmt,
    +  void **pp
    +){
    +  CksmFile *p = (CksmFile *)pFile;
    +  if( p->computeCksm ){
    +    *pp = 0;
    +    return SQLITE_OK;
    +  }
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
    +}
    +
    +/* Release a memory-mapped page */
    +static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
    +  pFile = ORIGFILE(pFile);
    +  return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
    +}
    +
    +/*
    +** Open a cksm file handle.
    +*/
    +static int cksmOpen(
    +  sqlite3_vfs *pVfs,
    +  const char *zName,
    +  sqlite3_file *pFile,
    +  int flags,
    +  int *pOutFlags
    +){
    +  CksmFile *p;
    +  sqlite3_file *pSubFile;
    +  sqlite3_vfs *pSubVfs;
    +  int rc;
    +  pSubVfs = ORIGVFS(pVfs);
    +  if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
    +    return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
    +  }
    +  p = (CksmFile*)pFile;
    +  memset(p, 0, sizeof(*p));
    +  pSubFile = ORIGFILE(pFile);
    +  pFile->pMethods = &cksm_io_methods;
    +  rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
    +  if( rc ) goto cksm_open_done;
    +  if( flags & SQLITE_OPEN_WAL ){
    +    sqlite3_file *pDb = sqlite3_database_file_object(zName);
    +    p->pPartner = (CksmFile*)pDb;
    +    assert( p->pPartner->pPartner==0 );
    +    p->pPartner->pPartner = p;
    +    p->isWal = 1;
    +    p->computeCksm = p->pPartner->computeCksm;
    +  }else{
    +    p->isWal = 0;
    +    p->computeCksm = 0;
    +  }
    +  p->zFName = zName;
    +cksm_open_done:
    +  if( rc ) pFile->pMethods = 0;
    +  return rc;
    +}
    +
    +/*
    +** All other VFS methods are pass-thrus.
    +*/
    +static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
    +  return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
    +}
    +static int cksmAccess(
    +  sqlite3_vfs *pVfs, 
    +  const char *zPath, 
    +  int flags, 
    +  int *pResOut
    +){
    +  return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
    +}
    +static int cksmFullPathname(
    +  sqlite3_vfs *pVfs, 
    +  const char *zPath, 
    +  int nOut, 
    +  char *zOut
    +){
    +  return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
    +}
    +static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){
    +  return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
    +}
    +static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
    +  ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
    +}
    +static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
    +  return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
    +}
    +static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){
    +  ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
    +}
    +static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
    +  return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
    +}
    +static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){
    +  return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
    +}
    +static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
    +  return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
    +}
    +static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
    +  return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
    +}
    +static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
    +  return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
    +}
    +static int cksmSetSystemCall(
    +  sqlite3_vfs *pVfs,
    +  const char *zName,
    +  sqlite3_syscall_ptr pCall
    +){
    +  return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
    +}
    +static sqlite3_syscall_ptr cksmGetSystemCall(
    +  sqlite3_vfs *pVfs,
    +  const char *zName
    +){
    +  return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
    +}
    +static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
    +  return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
    +}
    +
    +/* Register the verify_checksum() SQL function.
    +*/
    +static int cksmRegisterFunc(
    +  sqlite3 *db, 
    +  char **pzErrMsg, 
    +  const sqlite3_api_routines *pApi
    +){
    +  int rc;
    +  if( db==0 ) return SQLITE_OK;
    +  rc = sqlite3_create_function(db, "verify_checksum", 1,
    +                   SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
    +                   0, cksmVerifyFunc, 0, 0);
    +  return rc;
    +}
    +
    +/*
    +** Register the cksum VFS as the default VFS for the system.
    +** Also make arrangements to automatically register the "verify_checksum()"
    +** SQL function on each new database connection.
    +*/
    +static int cksmRegisterVfs(void){
    +  int rc = SQLITE_OK;
    +  sqlite3_vfs *pOrig;
    +  if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK;
    +  pOrig = sqlite3_vfs_find(0);
    +  cksm_vfs.iVersion = pOrig->iVersion;
    +  cksm_vfs.pAppData = pOrig;
    +  cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile);
    +  rc = sqlite3_vfs_register(&cksm_vfs, 1);
    +  if( rc==SQLITE_OK ){
    +    rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc);
    +  }
    +  return rc;
    +}
    +
    +#if defined(SQLITE_CKSUMVFS_STATIC)
    +/* This variant of the initializer runs when the extension is
    +** statically linked.
    +*/
    +int sqlite3_register_cksumvfs(const char *NotUsed){
    +  (void)NotUsed;
    +  return cksmRegisterVfs();
    +}
    +#endif /* defined(SQLITE_CKSUMVFS_STATIC */
    +
    +#if !defined(SQLITE_CKSUMVFS_STATIC)
    +/* This variant of the initializer function is used when the
    +** extension is shared library to be loaded at run-time.
    +*/
    +#ifdef _WIN32
    +__declspec(dllexport)
    +#endif
    +/* 
    +** This routine is called by sqlite3_load_extension() when the
    +** extension is first loaded.
    +***/
    +int sqlite3_cksumvfs_init(
    +  sqlite3 *db, 
    +  char **pzErrMsg, 
    +  const sqlite3_api_routines *pApi
    +){
    +  int rc;
    +  SQLITE_EXTENSION_INIT2(pApi);
    +  (void)pzErrMsg; /* not used */
    +  rc = cksmRegisterFunc(db, 0, 0);
    +  if( rc==SQLITE_OK ){
    +    
    +  }
    +  if( rc==SQLITE_OK ){
    +    rc = cksmRegisterVfs();
    +  }
    +  if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
    +  return rc;
    +}
    +#endif /* !defined(SQLITE_CKSUMVFS_STATIC) */
    diff --git a/ext/misc/completion.c b/ext/misc/completion.c
    index b624b6d..d9e7b85 100644
    --- a/ext/misc/completion.c
    +++ b/ext/misc/completion.c
    @@ -226,7 +226,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
                 const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
                 zSql = sqlite3_mprintf(
                    "%z%s"
    -               "SELECT name FROM \"%w\".sqlite_master",
    +               "SELECT name FROM \"%w\".sqlite_schema",
                    zSql, zSep, zDb
                 );
                 if( zSql==0 ) return SQLITE_NOMEM;
    @@ -250,7 +250,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
                 const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
                 zSql = sqlite3_mprintf(
                    "%z%s"
    -               "SELECT pti.name FROM \"%w\".sqlite_master AS sm"
    +               "SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
                            " JOIN pragma_table_info(sm.name,%Q) AS pti"
                    " WHERE sm.type='table'",
                    zSql, zSep, zDb, zDb
    diff --git a/ext/misc/dbdump.c b/ext/misc/dbdump.c
    index 157e646..ecf7d81 100644
    --- a/ext/misc/dbdump.c
    +++ b/ext/misc/dbdump.c
    @@ -395,7 +395,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
       if( strcmp(zTable, "sqlite_sequence")==0 ){
         p->xCallback("DELETE FROM sqlite_sequence;\n", p->pArg);
       }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
    -    p->xCallback("ANALYZE sqlite_master;\n", p->pArg);
    +    p->xCallback("ANALYZE sqlite_schema;\n", p->pArg);
       }else if( strncmp(zTable, "sqlite_", 7)==0 ){
         return 0;
       }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
    @@ -404,7 +404,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
           p->writableSchema = 1;
         }
         output_formatted(p,
    -       "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
    +       "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)"
            "VALUES('table','%q','%q',0,'%q');",
            zTable, zTable, zSql);
         return 0;
    @@ -646,27 +646,27 @@ int sqlite3_db_dump(
       xCallback("PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n", pArg);
       if( zTable==0 ){
         run_schema_dump_query(&x,
    -      "SELECT name, type, sql FROM \"%w\".sqlite_master "
    +      "SELECT name, type, sql FROM \"%w\".sqlite_schema "
           "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'",
           zSchema
         );
         run_schema_dump_query(&x,
    -      "SELECT name, type, sql FROM \"%w\".sqlite_master "
    +      "SELECT name, type, sql FROM \"%w\".sqlite_schema "
           "WHERE name=='sqlite_sequence'", zSchema
         );
         output_sql_from_query(&x,
    -      "SELECT sql FROM sqlite_master "
    +      "SELECT sql FROM sqlite_schema "
           "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
         );
       }else{
         run_schema_dump_query(&x,
    -      "SELECT name, type, sql FROM \"%w\".sqlite_master "
    +      "SELECT name, type, sql FROM \"%w\".sqlite_schema "
           "WHERE tbl_name=%Q COLLATE nocase AND type=='table'"
           "  AND sql NOT NULL",
           zSchema, zTable
         );
         output_sql_from_query(&x,
    -      "SELECT sql FROM \"%w\".sqlite_master "
    +      "SELECT sql FROM \"%w\".sqlite_schema "
           "WHERE sql NOT NULL"
           "  AND type IN ('index','trigger','view')"
           "  AND tbl_name=%Q COLLATE nocase",
    diff --git a/ext/misc/decimal.c b/ext/misc/decimal.c
    new file mode 100644
    index 0000000..a8d68ac
    --- /dev/null
    +++ b/ext/misc/decimal.c
    @@ -0,0 +1,634 @@
    +/*
    +** 2020-06-22
    +**
    +** 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.
    +**
    +******************************************************************************
    +**
    +** Routines to implement arbitrary-precision decimal math.
    +**
    +** The focus here is on simplicity and correctness, not performance.
    +*/
    +#include "sqlite3ext.h"
    +SQLITE_EXTENSION_INIT1
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +/* Mark a function parameter as unused, to suppress nuisance compiler
    +** warnings. */
    +#ifndef UNUSED_PARAMETER
    +# define UNUSED_PARAMETER(X)  (void)(X)
    +#endif
    +
    +
    +/* A decimal object */
    +typedef struct Decimal Decimal;
    +struct Decimal {
    +  char sign;        /* 0 for positive, 1 for negative */
    +  char oom;         /* True if an OOM is encountered */
    +  char isNull;      /* True if holds a NULL rather than a number */
    +  char isInit;      /* True upon initialization */
    +  int nDigit;       /* Total number of digits */
    +  int nFrac;        /* Number of digits to the right of the decimal point */
    +  signed char *a;   /* Array of digits.  Most significant first. */
    +};
    +
    +/*
    +** Release memory held by a Decimal, but do not free the object itself.
    +*/
    +static void decimal_clear(Decimal *p){
    +  sqlite3_free(p->a);
    +}
    +
    +/*
    +** Destroy a Decimal object
    +*/
    +static void decimal_free(Decimal *p){
    +  if( p ){
    +    decimal_clear(p);
    +    sqlite3_free(p);
    +  }
    +}
    +
    +/*
    +** Allocate a new Decimal object.  Initialize it to the number given
    +** by the input string.
    +*/
    +static Decimal *decimal_new(
    +  sqlite3_context *pCtx,
    +  sqlite3_value *pIn,
    +  int nAlt,
    +  const unsigned char *zAlt
    +){
    +  Decimal *p;
    +  int n, i;
    +  const unsigned char *zIn;
    +  int iExp = 0;
    +  p = sqlite3_malloc( sizeof(*p) );
    +  if( p==0 ) goto new_no_mem;
    +  p->sign = 0;
    +  p->oom = 0;
    +  p->isInit = 1;
    +  p->isNull = 0;
    +  p->nDigit = 0;
    +  p->nFrac = 0;
    +  if( zAlt ){
    +    n = nAlt,
    +    zIn = zAlt;
    +  }else{
    +    if( sqlite3_value_type(pIn)==SQLITE_NULL ){
    +      p->a = 0;
    +      p->isNull = 1;
    +      return p;
    +    }
    +    n = sqlite3_value_bytes(pIn);
    +    zIn = sqlite3_value_text(pIn);
    +  }
    +  p->a = sqlite3_malloc64( n+1 );
    +  if( p->a==0 ) goto new_no_mem;
    +  for(i=0; isspace(zIn[i]); i++){}
    +  if( zIn[i]=='-' ){
    +    p->sign = 1;
    +    i++;
    +  }else if( zIn[i]=='+' ){
    +    i++;
    +  }
    +  while( i='0' && c<='9' ){
    +      p->a[p->nDigit++] = c - '0';
    +    }else if( c=='.' ){
    +      p->nFrac = p->nDigit + 1;
    +    }else if( c=='e' || c=='E' ){
    +      int j = i+1;
    +      int neg = 0;
    +      if( j>=n ) break;
    +      if( zIn[j]=='-' ){
    +        neg = 1;
    +        j++;
    +      }else if( zIn[j]=='+' ){
    +        j++;
    +      }
    +      while( j='0' && zIn[j]<='9' ){
    +          iExp = iExp*10 + zIn[j] - '0';
    +        }
    +        j++;
    +      }
    +      if( neg ) iExp = -iExp;
    +      break;
    +    }
    +    i++;
    +  }
    +  if( p->nFrac ){
    +    p->nFrac = p->nDigit - (p->nFrac - 1);
    +  }
    +  if( iExp>0 ){
    +    if( p->nFrac>0 ){
    +      if( iExp<=p->nFrac ){
    +        p->nFrac -= iExp;
    +        iExp = 0;
    +      }else{
    +        iExp -= p->nFrac;
    +        p->nFrac = 0;
    +      }
    +    }
    +    if( iExp>0 ){   
    +      p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
    +      if( p->a==0 ) goto new_no_mem;
    +      memset(p->a+p->nDigit, 0, iExp);
    +      p->nDigit += iExp;
    +    }
    +  }else if( iExp<0 ){
    +    int nExtra;
    +    iExp = -iExp;
    +    nExtra = p->nDigit - p->nFrac - 1;
    +    if( nExtra ){
    +      if( nExtra>=iExp ){
    +        p->nFrac += iExp;
    +        iExp  = 0;
    +      }else{
    +        iExp -= nExtra;
    +        p->nFrac = p->nDigit - 1;
    +      }
    +    }
    +    if( iExp>0 ){
    +      p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
    +      if( p->a==0 ) goto new_no_mem;
    +      memmove(p->a+iExp, p->a, p->nDigit);
    +      memset(p->a, 0, iExp);
    +      p->nDigit += iExp;
    +      p->nFrac += iExp;
    +    }
    +  }
    +  return p;
    +
    +new_no_mem:
    +  if( pCtx ) sqlite3_result_error_nomem(pCtx);
    +  sqlite3_free(p);
    +  return 0;
    +}
    +
    +/*
    +** Make the given Decimal the result.
    +*/
    +static void decimal_result(sqlite3_context *pCtx, Decimal *p){
    +  char *z;
    +  int i, j;
    +  int n;
    +  if( p==0 || p->oom ){
    +    sqlite3_result_error_nomem(pCtx);
    +    return;
    +  }
    +  if( p->isNull ){
    +    sqlite3_result_null(pCtx);
    +    return;
    +  }
    +  z = sqlite3_malloc( p->nDigit+4 );
    +  if( z==0 ){
    +    sqlite3_result_error_nomem(pCtx);
    +    return;
    +  }
    +  i = 0;
    +  if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){
    +    p->sign = 0;
    +  }
    +  if( p->sign ){
    +    z[0] = '-';
    +    i = 1;
    +  }
    +  n = p->nDigit - p->nFrac;
    +  if( n<=0 ){
    +    z[i++] = '0';
    +  }
    +  j = 0;
    +  while( n>1 && p->a[j]==0 ){
    +    j++;
    +    n--;
    +  }
    +  while( n>0  ){
    +    z[i++] = p->a[j] + '0';
    +    j++;
    +    n--;
    +  }
    +  if( p->nFrac ){
    +    z[i++] = '.';
    +    do{
    +      z[i++] = p->a[j] + '0';
    +      j++;
    +    }while( jnDigit );
    +  }
    +  z[i] = 0;
    +  sqlite3_result_text(pCtx, z, i, sqlite3_free);
    +}
    +
    +/*
    +** SQL Function:   decimal(X)
    +**
    +** Convert input X into decimal and then back into text
    +*/
    +static void decimalFunc(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *p = decimal_new(context, argv[0], 0, 0);
    +  UNUSED_PARAMETER(argc);
    +  decimal_result(context, p);
    +  decimal_free(p);
    +}
    +
    +/*
    +** Compare to Decimal objects.  Return negative, 0, or positive if the
    +** first object is less than, equal to, or greater than the second.
    +**
    +** Preconditions for this routine:
    +**
    +**    pA!=0
    +**    pA->isNull==0
    +**    pB!=0
    +**    pB->isNull==0
    +*/
    +static int decimal_cmp(const Decimal *pA, const Decimal *pB){
    +  int nASig, nBSig, rc, n;
    +  if( pA->sign!=pB->sign ){
    +    return pA->sign ? -1 : +1;
    +  }
    +  if( pA->sign ){
    +    const Decimal *pTemp = pA;
    +    pA = pB;
    +    pB = pTemp;
    +  }
    +  nASig = pA->nDigit - pA->nFrac;
    +  nBSig = pB->nDigit - pB->nFrac;
    +  if( nASig!=nBSig ){
    +    return nASig - nBSig;
    +  }
    +  n = pA->nDigit;
    +  if( n>pB->nDigit ) n = pB->nDigit;
    +  rc = memcmp(pA->a, pB->a, n);
    +  if( rc==0 ){
    +    rc = pA->nDigit - pB->nDigit;
    +  }
    +  return rc;
    +}
    +
    +/*
    +** SQL Function:   decimal_cmp(X, Y)
    +**
    +** Return negative, zero, or positive if X is less then, equal to, or
    +** greater than Y.
    +*/
    +static void decimalCmpFunc(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *pA = 0, *pB = 0;
    +  int rc;
    +
    +  UNUSED_PARAMETER(argc);
    +  pA = decimal_new(context, argv[0], 0, 0);
    +  if( pA==0 || pA->isNull ) goto cmp_done;
    +  pB = decimal_new(context, argv[1], 0, 0);
    +  if( pB==0 || pB->isNull ) goto cmp_done;
    +  rc = decimal_cmp(pA, pB);
    +  if( rc<0 ) rc = -1;
    +  else if( rc>0 ) rc = +1;
    +  sqlite3_result_int(context, rc);
    +cmp_done:
    +  decimal_free(pA);
    +  decimal_free(pB);
    +}
    +
    +/*
    +** Expand the Decimal so that it has a least nDigit digits and nFrac
    +** digits to the right of the decimal point.
    +*/
    +static void decimal_expand(Decimal *p, int nDigit, int nFrac){
    +  int nAddSig;
    +  int nAddFrac;
    +  if( p==0 ) return;
    +  nAddFrac = nFrac - p->nFrac;
    +  nAddSig = (nDigit - p->nDigit) - nAddFrac;
    +  if( nAddFrac==0 && nAddSig==0 ) return;
    +  p->a = sqlite3_realloc64(p->a, nDigit+1);
    +  if( p->a==0 ){
    +    p->oom = 1;
    +    return;
    +  }
    +  if( nAddSig ){
    +    memmove(p->a+nAddSig, p->a, p->nDigit);
    +    memset(p->a, 0, nAddSig);
    +    p->nDigit += nAddSig;
    +  }
    +  if( nAddFrac ){
    +    memset(p->a+p->nDigit, 0, nAddFrac);
    +    p->nDigit += nAddFrac;
    +    p->nFrac += nAddFrac;
    +  }
    +}
    +
    +/*
    +** Add the value pB into pA.
    +**
    +** Both pA and pB might become denormalized by this routine.
    +*/
    +static void decimal_add(Decimal *pA, Decimal *pB){
    +  int nSig, nFrac, nDigit;
    +  int i, rc;
    +  if( pA==0 ){
    +    return;
    +  }
    +  if( pA->oom || pB==0 || pB->oom ){
    +    pA->oom = 1;
    +    return;
    +  }
    +  if( pA->isNull || pB->isNull ){
    +    pA->isNull = 1;
    +    return;
    +  }
    +  nSig = pA->nDigit - pA->nFrac;
    +  if( nSig && pA->a[0]==0 ) nSig--;
    +  if( nSignDigit-pB->nFrac ){
    +    nSig = pB->nDigit - pB->nFrac;
    +  }
    +  nFrac = pA->nFrac;
    +  if( nFracnFrac ) nFrac = pB->nFrac;
    +  nDigit = nSig + nFrac + 1;
    +  decimal_expand(pA, nDigit, nFrac);
    +  decimal_expand(pB, nDigit, nFrac);
    +  if( pA->oom || pB->oom ){
    +    pA->oom = 1;
    +  }else{
    +    if( pA->sign==pB->sign ){
    +      int carry = 0;
    +      for(i=nDigit-1; i>=0; i--){
    +        int x = pA->a[i] + pB->a[i] + carry;
    +        if( x>=10 ){
    +          carry = 1;
    +          pA->a[i] = x - 10;
    +        }else{
    +          carry = 0;
    +          pA->a[i] = x;
    +        }
    +      }
    +    }else{
    +      signed char *aA, *aB;
    +      int borrow = 0;
    +      rc = memcmp(pA->a, pB->a, nDigit);
    +      if( rc<0 ){
    +        aA = pB->a;
    +        aB = pA->a;
    +        pA->sign = !pA->sign;
    +      }else{
    +        aA = pA->a;
    +        aB = pB->a;
    +      }
    +      for(i=nDigit-1; i>=0; i--){
    +        int x = aA[i] - aB[i] - borrow;
    +        if( x<0 ){
    +          pA->a[i] = x+10;
    +          borrow = 1;
    +        }else{
    +          pA->a[i] = x;
    +          borrow = 0;
    +        }
    +      }
    +    }
    +  }
    +}
    +
    +/*
    +** Compare text in decimal order.
    +*/
    +static int decimalCollFunc(
    +  void *notUsed,
    +  int nKey1, const void *pKey1,
    +  int nKey2, const void *pKey2
    +){
    +  const unsigned char *zA = (const unsigned char*)pKey1;
    +  const unsigned char *zB = (const unsigned char*)pKey2;
    +  Decimal *pA = decimal_new(0, 0, nKey1, zA);
    +  Decimal *pB = decimal_new(0, 0, nKey2, zB);
    +  int rc;
    +  UNUSED_PARAMETER(notUsed);
    +  if( pA==0 || pB==0 ){
    +    rc = 0;
    +  }else{
    +    rc = decimal_cmp(pA, pB);
    +  }
    +  decimal_free(pA);
    +  decimal_free(pB);
    +  return rc;
    +}
    +
    +
    +/*
    +** SQL Function:   decimal_add(X, Y)
    +**                 decimal_sub(X, Y)
    +**
    +** Return the sum or difference of X and Y.
    +*/
    +static void decimalAddFunc(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *pA = decimal_new(context, argv[0], 0, 0);
    +  Decimal *pB = decimal_new(context, argv[1], 0, 0);
    +  UNUSED_PARAMETER(argc);
    +  decimal_add(pA, pB);
    +  decimal_result(context, pA);
    +  decimal_free(pA);
    +  decimal_free(pB);
    +}
    +static void decimalSubFunc(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *pA = decimal_new(context, argv[0], 0, 0);
    +  Decimal *pB = decimal_new(context, argv[1], 0, 0);
    +  UNUSED_PARAMETER(argc);
    +  if( pB==0 ) return;
    +  pB->sign = !pB->sign;
    +  decimal_add(pA, pB);
    +  decimal_result(context, pA);
    +  decimal_free(pA);
    +  decimal_free(pB);
    +}
    +
    +/* Aggregate funcion:   decimal_sum(X)
    +**
    +** Works like sum() except that it uses decimal arithmetic for unlimited
    +** precision.
    +*/
    +static void decimalSumStep(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *p;
    +  Decimal *pArg;
    +  UNUSED_PARAMETER(argc);
    +  p = sqlite3_aggregate_context(context, sizeof(*p));
    +  if( p==0 ) return;
    +  if( !p->isInit ){
    +    p->isInit = 1;
    +    p->a = sqlite3_malloc(2);
    +    if( p->a==0 ){
    +      p->oom = 1;
    +    }else{
    +      p->a[0] = 0;
    +    }
    +    p->nDigit = 1;
    +    p->nFrac = 0;
    +  }
    +  if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
    +  pArg = decimal_new(context, argv[0], 0, 0);
    +  decimal_add(p, pArg);
    +  decimal_free(pArg);
    +}
    +static void decimalSumInverse(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *p;
    +  Decimal *pArg;
    +  UNUSED_PARAMETER(argc);
    +  p = sqlite3_aggregate_context(context, sizeof(*p));
    +  if( p==0 ) return;
    +  if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
    +  pArg = decimal_new(context, argv[0], 0, 0);
    +  if( pArg ) pArg->sign = !pArg->sign;
    +  decimal_add(p, pArg);
    +  decimal_free(pArg);
    +}
    +static void decimalSumValue(sqlite3_context *context){
    +  Decimal *p = sqlite3_aggregate_context(context, 0);
    +  if( p==0 ) return;
    +  decimal_result(context, p);
    +}
    +static void decimalSumFinalize(sqlite3_context *context){
    +  Decimal *p = sqlite3_aggregate_context(context, 0);
    +  if( p==0 ) return;
    +  decimal_result(context, p);
    +  decimal_clear(p);
    +}
    +
    +/*
    +** SQL Function:   decimal_mul(X, Y)
    +**
    +** Return the product of X and Y.
    +**
    +** All significant digits after the decimal point are retained.
    +** Trailing zeros after the decimal point are omitted as long as
    +** the number of digits after the decimal point is no less than
    +** either the number of digits in either input.
    +*/
    +static void decimalMulFunc(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  Decimal *pA = decimal_new(context, argv[0], 0, 0);
    +  Decimal *pB = decimal_new(context, argv[1], 0, 0);
    +  signed char *acc = 0;
    +  int i, j, k;
    +  int minFrac;
    +  UNUSED_PARAMETER(argc);
    +  if( pA==0 || pA->oom || pA->isNull
    +   || pB==0 || pB->oom || pB->isNull 
    +  ){
    +    goto mul_end;
    +  }
    +  acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
    +  if( acc==0 ){
    +    sqlite3_result_error_nomem(context);
    +    goto mul_end;
    +  }
    +  memset(acc, 0, pA->nDigit + pB->nDigit + 2);
    +  minFrac = pA->nFrac;
    +  if( pB->nFracnFrac;
    +  for(i=pA->nDigit-1; i>=0; i--){
    +    signed char f = pA->a[i];
    +    int carry = 0, x;
    +    for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){
    +      x = acc[k] + f*pB->a[j] + carry;
    +      acc[k] = x%10;
    +      carry = x/10;
    +    }
    +    x = acc[k] + carry;
    +    acc[k] = x%10;
    +    acc[k-1] += x/10;
    +  }
    +  sqlite3_free(pA->a);
    +  pA->a = acc;
    +  acc = 0;
    +  pA->nDigit += pB->nDigit + 2;
    +  pA->nFrac += pB->nFrac;
    +  pA->sign ^= pB->sign;
    +  while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){
    +    pA->nFrac--;
    +    pA->nDigit--;
    +  }
    +  decimal_result(context, pA);
    +
    +mul_end:
    +  sqlite3_free(acc);
    +  decimal_free(pA);
    +  decimal_free(pB);
    +}
    +
    +#ifdef _WIN32
    +__declspec(dllexport)
    +#endif
    +int sqlite3_decimal_init(
    +  sqlite3 *db, 
    +  char **pzErrMsg, 
    +  const sqlite3_api_routines *pApi
    +){
    +  int rc = SQLITE_OK;
    +  static const struct {
    +    const char *zFuncName;
    +    int nArg;
    +    void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
    +  } aFunc[] = {
    +    { "decimal",       1,   decimalFunc        },
    +    { "decimal_cmp",   2,   decimalCmpFunc     },
    +    { "decimal_add",   2,   decimalAddFunc     },
    +    { "decimal_sub",   2,   decimalSubFunc     },
    +    { "decimal_mul",   2,   decimalMulFunc     },
    +  };
    +  unsigned int i;
    +  (void)pzErrMsg;  /* Unused parameter */
    +
    +  SQLITE_EXTENSION_INIT2(pApi);
    +
    +  for(i=0; i=0 ){
     #if defined(_WIN32)
    +#if !SQLITE_OS_WINRT
         /* Windows */
         FILETIME lastAccess;
         FILETIME lastWrite;
    @@ -424,6 +425,7 @@ static int writeFile(
         }else{
           return 1;
         }
    +#endif
     #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
         /* Recent unix */
         struct timespec times[2];
    diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c
    index a67c918..121eb43 100644
    --- a/ext/misc/ieee754.c
    +++ b/ext/misc/ieee754.c
    @@ -26,16 +26,76 @@
     **
     ** Examples:
     **
    -**     ieee754(2.0)       ->     'ieee754(2,0)'
    -**     ieee754(45.25)     ->     'ieee754(181,-2)'
    -**     ieee754(2, 0)      ->     2.0
    -**     ieee754(181, -2)   ->     45.25
    +**     ieee754(2.0)             ->     'ieee754(2,0)'
    +**     ieee754(45.25)           ->     'ieee754(181,-2)'
    +**     ieee754(2, 0)            ->     2.0
    +**     ieee754(181, -2)         ->     45.25
    +**
    +** Two additional functions break apart the one-argument ieee754()
    +** result into separate integer values:
    +**
    +**     ieee754_mantissa(45.25)  ->     181
    +**     ieee754_exponent(45.25)  ->     -2
    +**
    +** These functions convert binary64 numbers into blobs and back again.
    +**
    +**     ieee754_from_blob(x'3ff0000000000000')  ->  1.0
    +**     ieee754_to_blob(1.0)                    ->  x'3ff0000000000000'
    +**
    +** In all single-argument functions, if the argument is an 8-byte blob
    +** then that blob is interpreted as a big-endian binary64 value.
    +**
    +**
    +** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES
    +** -----------------------------------------------
    +**
    +** This extension in combination with the separate 'decimal' extension
    +** can be used to compute the exact decimal representation of binary64
    +** values.  To begin, first compute a table of exponent values:
    +**
    +**    CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
    +**    WITH RECURSIVE c(x,v) AS (
    +**      VALUES(0,'1')
    +**      UNION ALL
    +**      SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971
    +**    ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
    +**    WITH RECURSIVE c(x,v) AS (
    +**      VALUES(-1,'0.5')
    +**      UNION ALL
    +**      SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075
    +**    ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
    +**
    +** Then, to compute the exact decimal representation of a floating
    +** point value (the value 47.49 is used in the example) do:
    +**
    +**    WITH c(n) AS (VALUES(47.49))
    +**          ---------------^^^^^---- Replace with whatever you want
    +**    SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
    +**      FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n);
    +**
    +** Here is a query to show various boundry values for the binary64
    +** number format:
    +**
    +**    WITH c(name,bin) AS (VALUES
    +**       ('minimum positive value',        x'0000000000000001'),
    +**       ('maximum subnormal value',       x'000fffffffffffff'),
    +**       ('mininum positive nornal value', x'0010000000000000'),
    +**       ('maximum value',                 x'7fefffffffffffff'))
    +**    SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
    +**      FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
    +**
     */
     #include "sqlite3ext.h"
     SQLITE_EXTENSION_INIT1
     #include 
     #include 
     
    +/* Mark a function parameter as unused, to suppress nuisance compiler
    +** warnings. */
    +#ifndef UNUSED_PARAMETER
    +# define UNUSED_PARAMETER(X)  (void)(X)
    +#endif
    +
     /*
     ** Implementation of the ieee754() function
     */
    @@ -51,8 +111,19 @@ static void ieee754func(
         int isNeg;
         char zResult[100];
         assert( sizeof(m)==sizeof(r) );
    -    if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
    -    r = sqlite3_value_double(argv[0]);
    +    if( sqlite3_value_type(argv[0])==SQLITE_BLOB
    +     && sqlite3_value_bytes(argv[0])==sizeof(r)
    +    ){
    +      const unsigned char *x = sqlite3_value_blob(argv[0]);
    +      unsigned int i;
    +      sqlite3_uint64 v = 0;
    +      for(i=0; i>52;
           m = a & ((((sqlite3_int64)1)<<52)-1);
    -      m |= ((sqlite3_int64)1)<<52;
    +      if( e==0 ){
    +        m <<= 1;
    +      }else{
    +        m |= ((sqlite3_int64)1)<<52;
    +      }
           while( e<1075 && m>0 && (m&1)==0 ){
             m >>= 1;
             e++;
           }
           if( isNeg ) m = -m;
         }
    -    sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
    -                     m, e-1075);
    -    sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
    -  }else if( argc==2 ){
    +    switch( *(int*)sqlite3_user_data(context) ){
    +      case 0:
    +        sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
    +                         m, e-1075);
    +        sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
    +        break;
    +      case 1:
    +        sqlite3_result_int64(context, m);
    +        break;
    +      case 2:
    +        sqlite3_result_int(context, e-1075);
    +        break;
    +    }
    +  }else{
         sqlite3_int64 m, e, a;
         double r;
         int isNeg = 0;
    @@ -86,7 +171,7 @@ static void ieee754func(
           isNeg = 1;
           m = -m;
           if( m<0 ) return;
    -    }else if( m==0 && e>1000 && e<1000 ){
    +    }else if( m==0 && e>-1000 && e<1000 ){
           sqlite3_result_double(context, 0.0);
           return;
         }
    @@ -99,8 +184,13 @@ static void ieee754func(
           e--;
         }
         e += 1075;
    -    if( e<0 ) e = m = 0;
    -    if( e>0x7ff ) e = 0x7ff;
    +    if( e<=0 ){
    +      /* Subnormal */
    +      m >>= 1-e;
    +      e = 0;
    +    }else if( e>0x7ff ){
    +      e = 0x7ff;
    +    }
         a = m & ((((sqlite3_int64)1)<<52)-1);
         a |= e<<52;
         if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
    @@ -109,6 +199,51 @@ static void ieee754func(
       }
     }
     
    +/*
    +** Functions to convert between blobs and floats.
    +*/
    +static void ieee754func_from_blob(
    +  sqlite3_context *context,
    +  int argc,
    +  sqlite3_value **argv
    +){
    +  UNUSED_PARAMETER(argc);
    +  if( sqlite3_value_type(argv[0])==SQLITE_BLOB
    +   && sqlite3_value_bytes(argv[0])==sizeof(double)
    +  ){
    +    double r;
    +    const unsigned char *x = sqlite3_value_blob(argv[0]);
    +    unsigned int i;
    +    sqlite3_uint64 v = 0;
    +    for(i=0; i>= 8;
    +    }
    +    sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT);
    +  }
    +}
    +
     
     #ifdef _WIN32
     __declspec(dllexport)
    @@ -118,16 +253,29 @@ int sqlite3_ieee_init(
       char **pzErrMsg, 
       const sqlite3_api_routines *pApi
     ){
    +  static const struct {
    +    char *zFName;
    +    int nArg;
    +    int iAux;
    +    void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
    +  } aFunc[] = {
    +    { "ieee754",           1,   0, ieee754func },
    +    { "ieee754",           2,   0, ieee754func },
    +    { "ieee754_mantissa",  1,   1, ieee754func },
    +    { "ieee754_exponent",  1,   2, ieee754func },
    +    { "ieee754_to_blob",   1,   0, ieee754func_to_blob },
    +    { "ieee754_from_blob", 1,   0, ieee754func_from_blob },
    +
    +  };
    +  unsigned int i;
       int rc = SQLITE_OK;
       SQLITE_EXTENSION_INIT2(pApi);
       (void)pzErrMsg;  /* Unused parameter */
    -  rc = sqlite3_create_function(db, "ieee754", 1, 
    -                               SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
    -                               ieee754func, 0, 0);
    -  if( rc==SQLITE_OK ){
    -    rc = sqlite3_create_function(db, "ieee754", 2,
    -                                 SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
    -                                 ieee754func, 0, 0);
    +  for(i=0; inUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
       memcpy(p->zBuf+p->nUsed, zIn, N);
       p->nUsed += N;
    @@ -459,7 +464,7 @@ static void jsonRenderNode(
             jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
             break;
           }
    -      /* Fall through into the next case */
    +      /* no break */ deliberate_fall_through
         }
         case JSON_REAL:
         case JSON_INT: {
    @@ -600,7 +605,7 @@ static void jsonReturn(
           sqlite3_result_int64(pCtx, i);
           int_done:
           break;
    -      int_as_real: /* fall through to real */;
    +      int_as_real: i=0; /* no break */ deliberate_fall_through
         }
         case JSON_REAL: {
           double r;
    @@ -2303,6 +2308,7 @@ static int jsonEachColumn(
           }
           /* For json_each() path and root are the same so fall through
           ** into the root case */
    +      /* no break */ deliberate_fall_through
         }
         default: {
           const char *zRoot = p->zRoot;
    diff --git a/ext/misc/mmapwarm.c b/ext/misc/mmapwarm.c
    index 970a873..5afa47b 100644
    --- a/ext/misc/mmapwarm.c
    +++ b/ext/misc/mmapwarm.c
    @@ -43,7 +43,7 @@ int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){
       if( 0==sqlite3_get_autocommit(db) ) return SQLITE_MISUSE;
     
       /* Open a read-only transaction on the file in question */
    -  zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_master", 
    +  zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_schema", 
           (zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "")
       );
       if( zSql==0 ) return SQLITE_NOMEM;
    diff --git a/ext/misc/normalize.c b/ext/misc/normalize.c
    index 5997ec1..08d7733 100644
    --- a/ext/misc/normalize.c
    +++ b/ext/misc/normalize.c
    @@ -286,6 +286,13 @@ static const unsigned char sqlite3CtypeMap[256] = {
     #define TK_VARIABLE TK_LITERAL
     #define TK_BLOB     TK_LITERAL
     
    +/* Disable nuisence warnings about case fall-through */
    +#if !defined(deliberate_fall_through) && defined(__GCC__) && __GCC__>=7
    +# define deliberate_fall_through __attribute__((fallthrough));
    +#else
    +# define deliberate_fall_through
    +#endif
    +
     /*
     ** Return the length (in bytes) of the token that begins at z[0]. 
     ** Store the token type in *tokenType before returning.
    @@ -436,6 +443,7 @@ static int sqlite3GetToken(const unsigned char *z, int *tokenType){
           }
           /* If the next character is a digit, this is a floating point
           ** number that begins with ".".  Fall thru into the next case */
    +      /* no break */ deliberate_fall_through
         }
         case CC_DIGIT: {
           *tokenType = TK_INTEGER;
    @@ -528,6 +536,7 @@ static int sqlite3GetToken(const unsigned char *z, int *tokenType){
           }
           /* If it is not a BLOB literal, then it must be an ID, since no
           ** SQL keywords start with the letter 'x'.  Fall through */
    +      /* no break */ deliberate_fall_through
         }
         case CC_ID: {
           i = 1;
    diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c
    index 4eb56b0..9fbf2ae 100644
    --- a/ext/misc/scrub.c
    +++ b/ext/misc/scrub.c
    @@ -166,7 +166,7 @@ static void scrubBackupOpenSrc(ScrubState *p){
                           sqlite3_errmsg(p->dbSrc));
         return;
       }
    -  p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_master; BEGIN;",
    +  p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_schema; BEGIN;",
                               0, 0, 0);
       if( p->rcErr ){
         scrubBackupErr(p,
    @@ -535,7 +535,7 @@ int sqlite3_scrub_backup(
       /* Copy all of the btrees */
       scrubBackupBtree(&s, 1, 0);
       pStmt = scrubBackupPrepare(&s, s.dbSrc,
    -       "SELECT rootpage FROM sqlite_master WHERE coalesce(rootpage,0)>0");
    +       "SELECT rootpage FROM sqlite_schema WHERE coalesce(rootpage,0)>0");
       if( pStmt==0 ) goto scrub_abort;
       while( sqlite3_step(pStmt)==SQLITE_ROW ){
         i = (u32)sqlite3_column_int(pStmt, 0);
    diff --git a/ext/misc/sha1.c b/ext/misc/sha1.c
    index 0050fdf..9fe6cae 100644
    --- a/ext/misc/sha1.c
    +++ b/ext/misc/sha1.c
    @@ -381,8 +381,9 @@ int sqlite3_sha_init(
       int rc = SQLITE_OK;
       SQLITE_EXTENSION_INIT2(pApi);
       (void)pzErrMsg;  /* Unused parameter */
    -  rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
    -                               sha1Func, 0, 0);
    +  rc = sqlite3_create_function(db, "sha1", 1, 
    +                       SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
    +                               0, sha1Func, 0, 0);
       if( rc==SQLITE_OK ){
         rc = sqlite3_create_function(db, "sha1_query", 1, 
                                      SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
    diff --git a/ext/misc/sqlar.c b/ext/misc/sqlar.c
    index 8b90f9d..47cb68f 100644
    --- a/ext/misc/sqlar.c
    +++ b/ext/misc/sqlar.c
    @@ -17,6 +17,7 @@
     #include "sqlite3ext.h"
     SQLITE_EXTENSION_INIT1
     #include 
    +#include 
     
     /*
     ** Implementation of the "sqlar_compress(X)" SQL function.
    diff --git a/ext/misc/stmt.c b/ext/misc/stmt.c
    index d2b33e7..876b0e5 100644
    --- a/ext/misc/stmt.c
    +++ b/ext/misc/stmt.c
    @@ -168,7 +168,8 @@ static int stmtColumn(
           sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
           break;
         }
    -    case STMT_COLUMN_MEM: {
    +    default: {
    +      assert( i==STMT_COLUMN_MEM );
           i = SQLITE_STMTSTATUS_MEMUSED + 
                 STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
           /* Fall thru */
    diff --git a/ext/misc/uint.c b/ext/misc/uint.c
    new file mode 100644
    index 0000000..286314f
    --- /dev/null
    +++ b/ext/misc/uint.c
    @@ -0,0 +1,92 @@
    +/*
    +** 2020-04-14
    +**
    +** The author disclaims copyright to this source code.  In place of
    +** a legal notice, here is a blessing:
    +**
    +**    May you do good and not evil.
    +**    May you find forgiveness for yourself and forgive others.
    +**    May you share freely, never taking more than you give.
    +**
    +******************************************************************************
    +**
    +** This SQLite extension implements the UINT collating sequence.
    +**
    +** UINT works like BINARY for text, except that embedded strings
    +** of digits compare in numeric order.
    +**
    +**     *   Leading zeros are handled properly, in the sense that
    +**         they do not mess of the maginitude comparison of embedded
    +**         strings of digits.  "x00123y" is equal to "x123y".
    +**
    +**     *   Only unsigned integers are recognized.  Plus and minus
    +**         signs are ignored.  Decimal points and exponential notation
    +**         are ignored.
    +**
    +**     *   Embedded integers can be of arbitrary length.  Comparison
    +**         is *not* limited integers that can be expressed as a
    +**         64-bit machine integer.
    +*/
    +#include "sqlite3ext.h"
    +SQLITE_EXTENSION_INIT1
    +#include 
    +#include 
    +#include 
    +
    +/*
    +** Compare text in lexicographic order, except strings of digits
    +** compare in numeric order.
    +*/
    +static int uintCollFunc(
    +  void *notUsed,
    +  int nKey1, const void *pKey1,
    +  int nKey2, const void *pKey2
    +){
    +  const unsigned char *zA = (const unsigned char*)pKey1;
    +  const unsigned char *zB = (const unsigned char*)pKey2;
    +  int i=0, j=0, x;
    +  (void)notUsed;
    +  while( idbRbu, &pIter->pTblIter, &p->zErrmsg, 
         sqlite3_mprintf(
           "SELECT rbu_target_name(name, type='view') AS target, name "
    -      "FROM sqlite_master "
    +      "FROM sqlite_schema "
           "WHERE type IN ('table', 'view') AND target IS NOT NULL "
           " %s "
           "ORDER BY name"
    @@ -984,7 +984,7 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
       if( rc==SQLITE_OK ){
         rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
             "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
    -        "  FROM main.sqlite_master "
    +        "  FROM main.sqlite_schema "
             "  WHERE type='index' AND tbl_name = ?"
         );
       }
    @@ -1156,12 +1156,12 @@ static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
     **
     ** ALGORITHM:
     **
    -**   if( no entry exists in sqlite_master ){
    +**   if( no entry exists in sqlite_schema ){
     **     return RBU_PK_NOTABLE
     **   }else if( sql for the entry starts with "CREATE VIRTUAL" ){
     **     return RBU_PK_VTAB
     **   }else if( "PRAGMA index_list()" for the table contains a "pk" index ){
    -**     if( the index that is the pk exists in sqlite_master ){
    +**     if( the index that is the pk exists in sqlite_schema ){
     **       *piPK = rootpage of that index.
     **       return RBU_PK_EXTERNAL
     **     }else{
    @@ -1181,9 +1181,9 @@ static void rbuTableType(
       int *piPk
     ){
       /*
    -  ** 0) SELECT count(*) FROM sqlite_master where name=%Q AND IsVirtual(%Q)
    +  ** 0) SELECT count(*) FROM sqlite_schema where name=%Q AND IsVirtual(%Q)
       ** 1) PRAGMA index_list = ?
    -  ** 2) SELECT count(*) FROM sqlite_master where name=%Q 
    +  ** 2) SELECT count(*) FROM sqlite_schema where name=%Q 
       ** 3) PRAGMA table_info = ?
       */
       sqlite3_stmt *aStmt[4] = {0, 0, 0, 0};
    @@ -1195,7 +1195,7 @@ static void rbuTableType(
       p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg, 
         sqlite3_mprintf(
               "SELECT (sql LIKE 'create virtual%%'), rootpage"
    -          "  FROM sqlite_master"
    +          "  FROM sqlite_schema"
               " WHERE name=%Q", zTab
       ));
       if( p->rc!=SQLITE_OK || sqlite3_step(aStmt[0])!=SQLITE_ROW ){
    @@ -1218,7 +1218,7 @@ static void rbuTableType(
         if( zOrig && zIdx && zOrig[0]=='p' ){
           p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg, 
               sqlite3_mprintf(
    -            "SELECT rootpage FROM sqlite_master WHERE name = %Q", zIdx
    +            "SELECT rootpage FROM sqlite_schema WHERE name = %Q", zIdx
           ));
           if( p->rc==SQLITE_OK ){
             if( sqlite3_step(aStmt[2])==SQLITE_ROW ){
    @@ -2038,7 +2038,7 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
         ** This is needed for the argument to "PRAGMA index_xinfo". Set
         ** zIdx to point to a nul-terminated string containing this name. */
         p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg, 
    -        "SELECT name FROM sqlite_master WHERE rootpage = ?"
    +        "SELECT name FROM sqlite_schema WHERE rootpage = ?"
         );
         if( p->rc==SQLITE_OK ){
           sqlite3_bind_int(pQuery, 1, tnum);
    @@ -2211,7 +2211,7 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
     
       if( rc==SQLITE_OK ){
         rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
    -        "SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?"
    +        "SELECT trim(sql) FROM sqlite_schema WHERE type='index' AND name=?"
         );
       }
       if( rc==SQLITE_OK ){
    @@ -2793,7 +2793,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
           int bOk = 0;
           sqlite3_stmt *pCnt = 0;
           p->rc = prepareAndCollectError(p->dbRbu, &pCnt, &p->zErrmsg,
    -          "SELECT count(*) FROM stat.sqlite_master"
    +          "SELECT count(*) FROM stat.sqlite_schema"
           );
           if( p->rc==SQLITE_OK 
            && sqlite3_step(pCnt)==SQLITE_ROW
    @@ -2897,7 +2897,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
       if( p->rc==SQLITE_OK ){
         p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
       }
    -  rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
    +  rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_schema");
     
       /* Mark the database file just opened as an RBU target database. If 
       ** this call returns SQLITE_NOTFOUND, then the RBU vfs is not in use.
    @@ -2990,7 +2990,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
       if( pState==0 ){
         p->eStage = 0;
         if( p->rc==SQLITE_OK ){
    -      p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_master", 0, 0, 0);
    +      p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_schema", 0, 0, 0);
         }
       }
     
    @@ -3581,7 +3581,7 @@ static void rbuCreateTargetSchema(sqlite3rbu *p){
       p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
       if( p->rc==SQLITE_OK ){
         p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg, 
    -      "SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
    +      "SELECT sql FROM sqlite_schema WHERE sql!='' AND rootpage!=0"
           " AND name!='sqlite_sequence' "
           " ORDER BY type DESC"
         );
    @@ -3596,13 +3596,13 @@ static void rbuCreateTargetSchema(sqlite3rbu *p){
     
       if( p->rc==SQLITE_OK ){
         p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg, 
    -        "SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL" 
    +        "SELECT * FROM sqlite_schema WHERE rootpage=0 OR rootpage IS NULL" 
         );
       }
     
       if( p->rc==SQLITE_OK ){
         p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg, 
    -        "INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
    +        "INSERT INTO sqlite_schema VALUES(?,?,?,?,?)"
         );
       }
     
    @@ -3865,7 +3865,7 @@ static void rbuIndexCntFunc(
       assert( nVal==1 );
       
       rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg, 
    -      sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
    +      sqlite3_mprintf("SELECT count(*) FROM sqlite_schema "
             "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
       );
       if( rc!=SQLITE_OK ){
    @@ -3916,7 +3916,7 @@ static void rbuInitPhaseOneSteps(sqlite3rbu *p){
         ** occurs, nPhaseOneStep will be left set to -1. */
         if( p->rc==SQLITE_OK ){
           p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
    -          "SELECT 1 FROM sqlite_master WHERE tbl_name = 'rbu_count'"
    +          "SELECT 1 FROM sqlite_schema WHERE tbl_name = 'rbu_count'"
           );
         }
         if( p->rc==SQLITE_OK ){
    diff --git a/ext/repair/checkindex.c b/ext/repair/checkindex.c
    index 58706c1..080a515 100644
    --- a/ext/repair/checkindex.c
    +++ b/ext/repair/checkindex.c
    @@ -473,7 +473,7 @@ static int cidxLookupIndex(
         
       /* Find the table for this index. */
       pFindTab = cidxPrepare(&rc, pCsr, 
    -      "SELECT tbl_name, sql FROM sqlite_master WHERE name=%Q AND type='index'",
    +      "SELECT tbl_name, sql FROM sqlite_schema WHERE name=%Q AND type='index'",
           zIdx
       );
       if( rc==SQLITE_OK && sqlite3_step(pFindTab)==SQLITE_ROW ){
    diff --git a/ext/rtree/geopoly.c b/ext/rtree/geopoly.c
    index 14facad..35294c8 100644
    --- a/ext/rtree/geopoly.c
    +++ b/ext/rtree/geopoly.c
    @@ -683,6 +683,8 @@ static GeoPoly *geopolyBBox(
           aCoord[2].f = mnY;
           aCoord[3].f = mxY;
         }
    +  }else{
    +    memset(aCoord, 0, sizeof(RtreeCoord)*4);
       }
       return pOut;
     }
    diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c
    index 1eef1a1..df0f6c2 100644
    --- a/ext/rtree/rtree.c
    +++ b/ext/rtree/rtree.c
    @@ -82,6 +82,7 @@ typedef unsigned int u32;
     #include 
     #include 
     #include 
    +#include 
     
     /*  The following macro is used to suppress compiler warnings.
     */
    @@ -419,6 +420,23 @@ struct RtreeMatchArg {
     # define testcase(X)
     #endif
     
    +/*
    +** Make sure that the compiler intrinsics we desire are enabled when
    +** compiling with an appropriate version of MSVC unless prevented by
    +** the SQLITE_DISABLE_INTRINSIC define.
    +*/
    +#if !defined(SQLITE_DISABLE_INTRINSIC)
    +#  if defined(_MSC_VER) && _MSC_VER>=1400
    +#    if !defined(_WIN32_WCE)
    +#      include 
    +#      pragma intrinsic(_byteswap_ulong)
    +#      pragma intrinsic(_byteswap_uint64)
    +#    else
    +#      include 
    +#    endif
    +#  endif
    +#endif
    +
     /*
     ** Macros to determine whether the machine is big or little endian,
     ** and whether or not that determination is run-time or compile-time.
    @@ -3746,8 +3764,10 @@ static int rtreeInit(
         }else if( pRtree->nAux>0 ){
           break;
         }else{
    +      static const char *azFormat[] = {",%.*s REAL", ",%.*s INT"};
           pRtree->nDim2++;
    -      sqlite3_str_appendf(pSql, ",%.*s NUM", rtreeTokenLength(zArg), zArg);
    +      sqlite3_str_appendf(pSql, azFormat[eCoordType],
    +                          rtreeTokenLength(zArg), zArg);
         }
       }
       sqlite3_str_appendf(pSql, ");");
    diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test
    index 447ef5d..c37a2a7 100644
    --- a/ext/rtree/rtree1.test
    +++ b/ext/rtree/rtree1.test
    @@ -716,6 +716,18 @@ do_execsql_test 18.0 {
       SELECT rt0.c1 > '-1' FROM rt0;
     } {9 1}
     
    -
     expand_all_sql db
    +
    +# 2020-02-28 ticket e63b4d1a65546532
    +reset_db
    +do_execsql_test 19.0 {
    +  CREATE VIRTUAL TABLE rt0 USING rtree(a,b,c);
    +  INSERT INTO rt0(a,b,c) VALUES(0,0.0,0.0);
    +  CREATE VIEW v0(x) AS SELECT DISTINCT rt0.b FROM rt0;
    +  SELECT v0.x FROM v0, rt0;
    +} {0.0}
    +do_execsql_test 19.1 {
    +  SELECT v0.x FROM v0, rt0 WHERE v0.x = rt0.b;
    +} {0.0}
    +
     finish_test
    diff --git a/ext/session/sessionH.test b/ext/session/sessionH.test
    index 3f5a28d..8ba2311 100644
    --- a/ext/session/sessionH.test
    +++ b/ext/session/sessionH.test
    @@ -34,5 +34,51 @@ do_test 1.0 {
       compare_db db db2
     } {}
     
    +#------------------------------------------------------------------------
    +db2 close
    +reset_db
    +
    +do_execsql_test 2.0 {
    +  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    +  INSERT INTO main.t1 VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);
    +}
    +
    +do_test 2.1 {
    +  sqlite3session S db main
    +  S attach *
    +  db eval {
    +    BEGIN;
    +      INSERT INTO t1 VALUES(10, 11, 12);
    +      DELETE FROM t1 WHERE a=1;
    +      UPDATE t1 SET b='five', c='six' WHERE a=4;
    +  }
    +
    +  set C [S changeset]
    +  db eval ROLLBACK
    +  S delete
    +  set {} {}
    +} {}
    +
    +do_execsql_test 2.2 {
    +  CREATE TEMP TABLE t1(a INTEGER PRIMARY KEY, b, c);
    +  INSERT INTO temp.t1 VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);
    +}
    +
    +set ::conflict [list]
    +proc xConflict {args} { lappend ::conflict $args ; return "" }
    +do_test 2.3 {
    +  sqlite3changeset_apply db $C xConflict
    +  set ::conflict
    +} {}
    +do_execsql_test 2.4 {
    +  SELECT * FROM main.t1;
    +  SELECT '****';
    +  SELECT * FROM temp.t1;
    +} {
    +  4 five six 7 8 9 10 11 12
    +  ****
    +  1 2 3 4 5 6 7 8 9
    +}
    +
     
     finish_test
    diff --git a/ext/session/session_common.tcl b/ext/session/session_common.tcl
    index ceffdad..c52ac45 100644
    --- a/ext/session/session_common.tcl
    +++ b/ext/session/session_common.tcl
    @@ -172,8 +172,8 @@ proc compare_db {db1 db2} {
         set data1 [$db1 eval $sql]
         set data2 [$db2 eval $sql]
         if {$data1 != $data2} { 
    -      puts "$data1"
    -      puts "$data2"
    +      puts "$db1: $data1"
    +      puts "$db2: $data2"
           error "table $tbl data mismatch" 
         }
       }
    diff --git a/ext/session/sessioninvert.test b/ext/session/sessioninvert.test
    index 49205f6..b7c157d 100644
    --- a/ext/session/sessioninvert.test
    +++ b/ext/session/sessioninvert.test
    @@ -155,5 +155,29 @@ do_test 3.2 {
       compare_db db db2
     } {}
     
    +#-------------------------------------------------------------------------
    +#
    +reset_db
    +do_execsql_test 4.0 {
    +  CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
    +  INSERT INTO t1 VALUES(1, 'one');
    +  INSERT INTO t1 VALUES(2, 'two');
    +  INSERT INTO t1 VALUES(3, 'three');
    +  INSERT INTO t1 VALUES(4, 'four');
    +}
    +
    +do_invert_test 4.1 {
    +  DELETE FROM t1;
    +  INSERT INTO t1 VALUES(1, 'two');
    +  INSERT INTO t1 VALUES(2, 'five');
    +  INSERT INTO t1 VALUES(3, 'one');
    +  INSERT INTO t1 VALUES(4, 'three');
    +} {
    +  {UPDATE t1 0 X. {i 1 t two} {{} {} t one}}
    +  {UPDATE t1 0 X. {i 2 t five} {{} {} t two}}
    +  {UPDATE t1 0 X. {i 3 t one} {{} {} t three}} 
    +  {UPDATE t1 0 X. {i 4 t three} {{} {} t four}}
    +}
    +
     
     finish_test
    diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c
    index a14172d..dae2604 100644
    --- a/ext/session/sqlite3session.c
    +++ b/ext/session/sqlite3session.c
    @@ -2956,8 +2956,13 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
       }
     
       p->apValue = (sqlite3_value**)p->tblhdr.aBuf;
    -  p->abPK = (u8*)&p->apValue[p->nCol*2];
    -  p->zTab = (char*)&p->abPK[p->nCol];
    +  if( p->apValue==0 ){
    +    p->abPK = 0;
    +    p->zTab = 0;
    +  }else{
    +    p->abPK = (u8*)&p->apValue[p->nCol*2];
    +    p->zTab = p->abPK ? (char*)&p->abPK[p->nCol] : 0;
    +  }
       return (p->rc = rc);
     }
     
    @@ -3479,6 +3484,7 @@ struct SessionApplyCtx {
       u8 *abPK;                       /* Boolean array - true if column is in PK */
       int bStat1;                     /* True if table is sqlite_stat1 */
       int bDeferConstraints;          /* True to defer constraints */
    +  int bInvertConstraints;         /* Invert when iterating constraints buffer */
       SessionBuffer constraints;      /* Deferred constraints are stored here */
       SessionBuffer rebase;           /* Rebase information (if any) here */
       u8 bRebaseStarted;              /* If table header is already in rebase */
    @@ -3513,7 +3519,7 @@ static int sessionDeleteRow(
       SessionBuffer buf = {0, 0, 0};
       int nPk = 0;
     
    -  sessionAppendStr(&buf, "DELETE FROM ", &rc);
    +  sessionAppendStr(&buf, "DELETE FROM main.", &rc);
       sessionAppendIdent(&buf, zTab, &rc);
       sessionAppendStr(&buf, " WHERE ", &rc);
     
    @@ -3596,7 +3602,7 @@ static int sessionUpdateRow(
       SessionBuffer buf = {0, 0, 0};
     
       /* Append "UPDATE tbl SET " */
    -  sessionAppendStr(&buf, "UPDATE ", &rc);
    +  sessionAppendStr(&buf, "UPDATE main.", &rc);
       sessionAppendIdent(&buf, zTab, &rc);
       sessionAppendStr(&buf, " SET ", &rc);
     
    @@ -4251,7 +4257,9 @@ static int sessionRetryConstraints(
         SessionBuffer cons = pApply->constraints;
         memset(&pApply->constraints, 0, sizeof(SessionBuffer));
     
    -    rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0);
    +    rc = sessionChangesetStart(
    +        &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints
    +    );
         if( rc==SQLITE_OK ){
           size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
           int rc2;
    @@ -4318,6 +4326,7 @@ static int sessionChangesetApply(
       pIter->in.bNoDiscard = 1;
       memset(&sApply, 0, sizeof(sApply));
       sApply.bRebase = (ppRebase && pnRebase);
    +  sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
       sqlite3_mutex_enter(sqlite3_db_mutex(db));
       if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
         rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
    diff --git a/main.mk b/main.mk
    index cbf14f0..693887b 100644
    --- a/main.mk
    +++ b/main.mk
    @@ -74,7 +74,8 @@ LIBOBJ+= vdbe.o parse.o \
              table.o threads.o tokenize.o treeview.o trigger.o \
              update.o upsert.o userauth.o util.o vacuum.o \
              vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
    -	 vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \
    +	 vdbetrace.o vdbevtab.o \
    +         wal.o walker.o where.o wherecode.o whereexpr.o \
              utf.o vtab.o window.o
     
     LIBOBJ += sqlite3session.o
    @@ -173,6 +174,7 @@ SRC = \
       $(TOP)/src/vdbemem.c \
       $(TOP)/src/vdbesort.c \
       $(TOP)/src/vdbetrace.c \
    +  $(TOP)/src/vdbevtab.c \
       $(TOP)/src/vdbeInt.h \
       $(TOP)/src/vtab.c \
       $(TOP)/src/vxworks.h \
    @@ -361,6 +363,7 @@ TESTSRC += \
       $(TOP)/ext/misc/carray.c \
       $(TOP)/ext/misc/closure.c \
       $(TOP)/ext/misc/csv.c \
    +  $(TOP)/ext/misc/decimal.c \
       $(TOP)/ext/misc/eval.c \
       $(TOP)/ext/misc/explain.c \
       $(TOP)/ext/misc/fileio.c \
    @@ -421,6 +424,7 @@ TESTSRC2 = \
       $(TOP)/src/vdbeaux.c \
       $(TOP)/src/vdbe.c \
       $(TOP)/src/vdbemem.c \
    +  $(TOP)/src/vdbevtab.c \
       $(TOP)/src/where.c \
       $(TOP)/src/wherecode.c \
       $(TOP)/src/whereexpr.c \
    @@ -526,6 +530,7 @@ SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
     SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
     SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
     SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
    +SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
     SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
     FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
     FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
    @@ -536,6 +541,7 @@ FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS4
     FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE
     FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
     FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
    +FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
     DBFUZZ_OPT =
     KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
     ST_OPT = -DSQLITE_THREADSAFE=0
    @@ -586,6 +592,7 @@ DBFUZZ2_OPTS = \
       -DSQLITE_ENABLE_DESERIALIZE \
       -DSQLITE_DEBUG \
       -DSQLITE_ENABLE_DBSTAT_VTAB \
    +  -DSQLITE_ENABLE_BYTECODE_VTAB \
       -DSQLITE_ENABLE_RTREE \
       -DSQLITE_ENABLE_FTS4 \
       -DSQLITE_ENABLE_FTS5
    @@ -721,6 +728,12 @@ parse.c:	$(TOP)/src/parse.y lemon
     sqlite3.h:	$(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h
     	tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
     
    +sqlite3rc.h:	$(TOP)/src/sqlite3.rc $(TOP)/VERSION
    +	echo '#ifndef SQLITE_RESOURCE_VERSION' >$@
    +	echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@
    +	cat $(TOP)/VERSION | tclsh $(TOP)/tool/replace.tcl exact . , >>$@
    +	echo '#endif' >>sqlite3rc.h
    +
     keywordhash.h:	$(TOP)/tool/mkkeywordhash.c
     	$(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c
     	./mkkeywordhash >keywordhash.h
    @@ -729,10 +742,13 @@ keywordhash.h:	$(TOP)/tool/mkkeywordhash.c
     SHELL_SRC = \
     	$(TOP)/src/shell.c.in \
             $(TOP)/ext/misc/appendvfs.c \
    -	$(TOP)/ext/misc/shathree.c \
    -	$(TOP)/ext/misc/fileio.c \
     	$(TOP)/ext/misc/completion.c \
    +        $(TOP)/ext/misc/decimal.c \
    +	$(TOP)/ext/misc/fileio.c \
    +        $(TOP)/ext/misc/ieee754.c \
    +	$(TOP)/ext/misc/shathree.c \
     	$(TOP)/ext/misc/sqlar.c \
    +        $(TOP)/ext/misc/uint.c \
     	$(TOP)/ext/expert/sqlite3expert.c \
     	$(TOP)/ext/expert/sqlite3expert.h \
     	$(TOP)/ext/misc/zipfile.c \
    @@ -894,6 +910,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
     TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
     TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
     TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
    +TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB
     TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit
     
     testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
    @@ -967,6 +984,9 @@ valgrindtest:	$(TESTPROGS) valgrindfuzz
     smoketest:	$(TESTPROGS) fuzzcheck$(EXE)
     	./testfixture$(EXE) $(TOP)/test/main.test $(TESTOPTS)
     
    +shelltest: $(TESTPROGS)
    +	./testfixture$(EXT) $(TOP)/test/permutations.test shell
    +
     # The next two rules are used to support the "threadtest" target. Building
     # threadtest runs a few thread-safety tests that are implemented in C. This
     # target is invoked by the releasetest.tcl script.
    @@ -1068,10 +1088,10 @@ checksymbols: sqlite3.o
     # a tarball named for the version number.  Ex:  sqlite-autoconf-3110000.tar.gz.
     # The snapshot-tarball target builds a tarball named by the SHA1 hash
     #
    -amalgamation-tarball: sqlite3.c
    +amalgamation-tarball: sqlite3.c sqlite3rc.h
     	TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal
     
    -snapshot-tarball: sqlite3.c
    +snapshot-tarball: sqlite3.c sqlite3rc.h
     	TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
     
     
    diff --git a/manifest b/manifest
    index 927e64b..5435b79 100644
    --- a/manifest
    +++ b/manifest
    @@ -1,28 +1,28 @@
    -C Version\s3.31.0
    -D 2020-01-22T18:38:59.573
    +C Version\s3.33.0
    +D 2020-08-14T13:23:32.827
     F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
     F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
     F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
    -F Makefile.in 9dfc7936f675785309b74d09202bb656732325e65df889e5aaa18cc8932e5b0c
    +F Makefile.in 19374a5db06c3199ec1bab71ab74a103d8abf21053c05e9389255dc58083f806
     F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
    -F Makefile.msc fab23c6b10cb6f06a7e9c407fc2e35cb184a6d653f1b793bda87fcee2eafa4f6
    +F Makefile.msc 48f5a3fc32672c09ad73795749f6253e406a31526935fbbffd8f021108d54574
     F README.md 1514a365ffca3c138e00c5cc839906108a01011a6b082bad19b09781e3aa498a
    -F VERSION 081500f0aeaadc989d85aafbc717af45512018aebc73d89e5c2368fe62a600ff
    +F VERSION 5db2ee2cfcc790af73775fa485c13b2e8ccaa5936c6e1f47aedeba7056041ca5
     F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
     F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
     F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
     F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
     F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
    -F autoconf/Makefile.am e14b629addaa1ce372b72043f28f40de2e32b7e211b6e0fc18dbb87989197e40
    +F autoconf/Makefile.am a8d1d24affe52ebf8d7ddcf91aa973fa0316618ab95bb68c87cabf8faf527dc8
     F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
    -F autoconf/Makefile.msc 1d1e4af61289c62b94aa65a93afcd3dfa4b53e4195908980e0b138203e71e1c9
    +F autoconf/Makefile.msc e0f1dafc48d000fd6ddfdb01815271528db55cbc7299ca888df5b93367f0d5a4
     F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
     F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
     F autoconf/configure.ac 3cd933b959fe514eebd1ca1717dfddbf2c9b825b6bc2c5f744deaf5d63af9288
     F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd
     F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
     F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
    -F autoconf/tea/configure.ac 6f379dfd46ace5caa97791acae07675eff62cada9800fc7bb36c789ecb7f17ac
    +F autoconf/tea/configure.ac ea61e07340d97e4a79a081f0b8977198a6073edd060738dbb3ae5cb8d5e96f1c
     F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb
     F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
     F autoconf/tea/pkgIndex.tcl.in 3ef61715cf1c7bdcff56947ffadb26bc991ca39d
    @@ -34,22 +34,23 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
     F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6
     F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
     F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559
    -F configure aae28230005acf833d466c8a160bd4c70cf42984f5a6756fa327e15e7f473477 x
    -F configure.ac 798a24cee2879325ca5b688a618199eb32cc77ed8136edbaa43d9137b470d54e
    +F configure a97f98dfff699495aef66ae3d9c424345778a663f583e0d6e7522670518f87c1 x
    +F configure.ac 40d01e89cb325c28b33f5957e61fede0bd17da2b5e37d9b223a90c8a318e88d4
     F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
     F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
    -F doc/lemon.html 24956ab2995e55fe171e55bdd04f22b553957dc8bb43501dbb9311e30187e0d3
    +F doc/lemon.html 1edc0f916e771212792d4d077aedc05168bf13fd65d64d41b2c13e46ac0063a8
     F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
     F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
     F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
    +F doc/wal-lock.md 781726aaba20bafeceb7ba9f91d5c98c6731691b30c954e37cf0b49a053d461d
     F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
     F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91
    -F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
    -F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
    +F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94
    +F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a
     F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
     F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
    -F ext/expert/expert1.test e2afc53a27610e8251e44c7f961806607a5490ff204b3db342740d558e052662
    -F ext/expert/sqlite3expert.c 3da865f2286433588260f41e796422c611bceaca3a0bbf9139a619cf7d062c19
    +F ext/expert/expert1.test 2e10ff875c31c9e6fc5e324767624181273859771fe34c5daeeadf3f2974a4f7
    +F ext/expert/sqlite3expert.c b5eae75862d34a204d16c45dcb813888b5f86bdc156c6136b0f79094c0da4f79
     F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
     F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
     F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
    @@ -78,33 +79,33 @@ F ext/fts2/fts2_tokenizer.c b529493d55e55497213c37e1f31680a77746be26
     F ext/fts2/fts2_tokenizer.h 27a1a99ca2d615cf7e142839b8d79e8751b4529e
     F ext/fts2/fts2_tokenizer1.c 07e223eecb483d448313b5f1553a4f299a7fb7a1
     F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
    -F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
    +F ext/fts3/README.content b9078d0843a094d86af0d48dffbff13c906702b4c3558012e67b9c7cc3bf59ee
     F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
     F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f39fa93a2e422d
     F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
    -F ext/fts3/fts3.c 52c09f459364732b5df73eff0373f991fd6af8f0f60fcdbb4b649205e88a7568
    +F ext/fts3/fts3.c 4809e0b05af4519ad8bfa13d684f7ad635d1390a758299d2302f7e85c48ec160
     F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
    -F ext/fts3/fts3Int.h f091030b976045e7df91af2337935952b477cdbd9f48058c44c965684484cb50
    +F ext/fts3/fts3Int.h 045179f538c478ced266ca14327269cde8ad8d573c5be902230a5ebaa5636c59
     F ext/fts3/fts3_aux.c 96708c8b3a7d9b8ca1b68ea2b7e503e283f20e95f145becadedfad096dbd0f34
    -F ext/fts3/fts3_expr.c b132af223e90e35b9f9efa9fe63d6ae737d34153a3b6066736086df8abc78a1f
    +F ext/fts3/fts3_expr.c f081e38da641724cd72c20e23b71db2bf4d0c9517c14637442f6910259f11a34
     F ext/fts3/fts3_hash.c 8b6e31bfb0844c27dc6092c2620bdb1fca17ed613072db057d96952c6bdb48b7
     F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
     F ext/fts3/fts3_icu.c 305ce7fb6036484085b5556a9c8e62acdc7763f0f4cdf5fd538212a9f3720116
     F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009
    -F ext/fts3/fts3_snippet.c 052b35ad746349ffb53820379bacdb23ff3ac60d3cc13d986e56d42822ef5a9a
    +F ext/fts3/fts3_snippet.c 86e7e947a176f0f005720b3ca17631aca2fd2f9daa6729d4adbf2d16ab1b9613
     F ext/fts3/fts3_term.c f45a1e7c6ef464abb1231245d123dae12266b69e05cc56e14045b76591ae92d1
     F ext/fts3/fts3_test.c 73b16e229e517c1b1f0fb8e1046182a4e5dbc8dbe6eea8a5d4353fcce7dbbf39
    -F ext/fts3/fts3_tokenize_vtab.c 1de9a61acfa2a0445ed989310c31839c57f6b6086dd9d5c97177ae734a17fd8b
    +F ext/fts3/fts3_tokenize_vtab.c 8d15b148e7d88a4280389a200b26e8d52abda4c4ec2e9a35e9d7a1fa50e5aa03
     F ext/fts3/fts3_tokenizer.c 6d8fc150c48238955d5182bf661498db0dd473c8a2a80e00c16994a646fa96e7
     F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
     F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
     F ext/fts3/fts3_unicode.c 4b9af6151c29b35ed09574937083cece7c31e911f69615e168a39677569b684d
     F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
    -F ext/fts3/fts3_write.c 6f9dd5d774003ea81b8b32daa7d0819f9aaf01bf2b5f33498a69aab096094ed3
    +F ext/fts3/fts3_write.c 723ed1b11ed46ad1b3a23c0d69fa39e77986783a82d5711bf87a5ce29e0a3b52
     F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
     F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
     F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
    -F ext/fts3/tool/fts3view.c 202801a2056995b763864d60c2dee744d46f1677
    +F ext/fts3/tool/fts3view.c 413c346399159df81f86c4928b7c4a455caab73bfbc8cd68f950f632e5751674
     F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
     F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
     F ext/fts3/unicode/mkunicode.tcl bf7fcaa6d68e6d38223467983785d054f1cff4d9e3905dd51f6ed8801bb590d5
    @@ -117,7 +118,7 @@ F ext/fts5/fts5_buffer.c 5a5fe0159752c0fb0a5a93c722e9db2662822709490769d482b76a6
     F ext/fts5/fts5_config.c b447948f35ad3354e8fe5e242e0a7e7b5b941555400b9404259944e3aa570037
     F ext/fts5/fts5_expr.c 2be456484786333d559dc2987a00f2750981fab91d52db8452a8046278c5f22e
     F ext/fts5/fts5_hash.c 1cc0095646f5f3b46721aa112fb4f9bf29ae175cb5338f89dcec66ed97acfe75
    -F ext/fts5/fts5_index.c e3573c88ce8238f7a5892c777a9ddf646c1355012780337d783fb2dfc3dca650
    +F ext/fts5/fts5_index.c de14c9a30f45e2b847ff9284b14776d9d07961e545e8f1546a6aa3f915af721f
     F ext/fts5/fts5_main.c e881a2ea0bf01b3a3ff0bc1b31373c58fd54b6c9f3c43ea3d431bea4e5d4025e
     F ext/fts5/fts5_storage.c 3ecda8edadc1f62a355d6789776be0da609f8658c50d72e422674093ab7e1528
     F ext/fts5/fts5_tcl.c 39bcbae507f594aad778172fa914cad0f585bf92fd3b078c686e249282db0d95
    @@ -126,7 +127,7 @@ F ext/fts5/fts5_test_tok.c f96c6e193c466711d6d7828d5f190407fe7ab897062d371426dd3
     F ext/fts5/fts5_tokenize.c 2e508c6a3bd8ee56c48e98a38052e1a650e49b32a484cce9b189984114bc3b88
     F ext/fts5/fts5_unicode2.c 8bd0cd07396b74c1a05590e4070d635bccfc849812c305619f109e6c0485e250
     F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d09885d1dadf80
    -F ext/fts5/fts5_vocab.c c3f12188570abb423303cd193b16dd19ba54e21c2e930e9b748d743de3b385f5
    +F ext/fts5/fts5_vocab.c 7a071833064dc8bca236c3c323e56aac36f583aa2c46ce916d52e31ce87462c9
     F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
     F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
     F ext/fts5/test/fts5_common.tcl b01c584144b5064f30e6c648145a2dd6bc440841
    @@ -158,7 +159,7 @@ F ext/fts5/test/fts5connect.test 08030168fc96fc278fa81f28654fb7e90566f33aff269c0
     F ext/fts5/test/fts5content.test 213506436fb2c87567b8e31f6d43ab30aab99354cec74ed679f22aad0cdbf283
     F ext/fts5/test/fts5corrupt.test 77ae6f41a7eba10620efb921cf7dbe218b0ef232b04519deb43581cb17a57ebe
     F ext/fts5/test/fts5corrupt2.test 7453752ba12ce91690c469a6449d412561cc604b1dec994e16ab132952e7805f
    -F ext/fts5/test/fts5corrupt3.test fab4ea761b2df254fb3909423989320772a3a757de4d151ddcfa2a40a3b93328
    +F ext/fts5/test/fts5corrupt3.test 7afe0fea5b2160798fdc3306395048768c6fc13acefc0e7129d4075b6e1bb224
     F ext/fts5/test/fts5corrupt4.test ea805c4d7c68b5f185b9db5d2060a7ae5875339738dd48203c92162f41e7ca91
     F ext/fts5/test/fts5delete.test cbf87e3b8867c4d5cfcaed975c7475fd3f99d072bce2075fcedf43d1f82af775
     F ext/fts5/test/fts5detail.test 31b240dbf6d44ac3507e2f8b65f29fdc12465ffd531212378c7ce1066766f54e
    @@ -187,7 +188,7 @@ F ext/fts5/test/fts5integrity.test 8ffabcd91b058d812aba3e3e0a06f76ce165ba402a18c
     F ext/fts5/test/fts5interrupt.test 09613247b273a99889808ef852898177e671406fe71fdde7ea00e78ea283d227
     F ext/fts5/test/fts5lastrowid.test be98fe3e03235296585b72daad7aed5717ba0062bae5e5c18dd6e04e194c6b28
     F ext/fts5/test/fts5leftjoin.test c0b4cafb9661379e576dc4405c0891d8fcc2782680740513c4d1fc114b43d4ad
    -F ext/fts5/test/fts5matchinfo.test 50d86da66ec5b27603dcd90ba0227f5d9deb10351cbc52974a88e24f6fc9b076
    +F ext/fts5/test/fts5matchinfo.test 10c9a6f7fe61fb132299c4183c012770b10c4d5c2f2edb6df0b6607f683d737a
     F ext/fts5/test/fts5merge.test e92a8db28b45931e7a9c7b1bbd36101692759d00274df74d83fd29d25d53b3a6
     F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2
     F ext/fts5/test/fts5misc.test 088ac5f0f5de1ad45b0f83197ab5263bcae8130156cdc901bff2375ff2b8af86
    @@ -223,40 +224,40 @@ F ext/fts5/test/fts5unicode4.test 6463301d669f963c83988017aa354108be0b947d325aef
     F ext/fts5/test/fts5unindexed.test 9021af86a0fb9fc616f7a69a996db0116e7936d0db63892db6bafabbec21af4d
     F ext/fts5/test/fts5update.test b8affd796e45c94a4d19ad5c26606ea06065a0f162a9562d9f005b5a80ccf0bc
     F ext/fts5/test/fts5version.test c8f2cc105f0abf0224965f93e584633dee3e06c91478bc67e468f7cfdf97fd6a
    -F ext/fts5/test/fts5vocab.test 648fb2fe86b55e08295e34504704718d92fba3e2cf3e1f5d72fa3682df4cd0f0
    +F ext/fts5/test/fts5vocab.test 7ed80d9af1ddaaa1637da05e406327b5aac250848bc604c1c1cc667908b87760
     F ext/fts5/test/fts5vocab2.test e0fdc3a3095f6eda68ac9bf9a443ff929a124d46f00af19933604085712e9d47
     F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85
     F ext/fts5/tool/fts5txt2db.tcl 526a9979c963f1c54fd50976a05a502e533a4c59
     F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093
     F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45
     F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
    -F ext/icu/README.txt a295e91db742b153e8dce8f7efd31d28ad1eea4df31ef4daa3eedc85be2f5138
    -F ext/icu/icu.c 7adfe8a72dd4f54b47684dc9b88523399c6ef119d733b73e17371445f7428dd1
    -F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
    +F ext/icu/README.txt 1c48ffaf7f255bd73d00a35f68f6de357c2a6594f16cb00506a151be23694706
    +F ext/icu/icu.c 91c021c7e3e8bbba286960810fa303295c622e323567b2e6def4ce58e4466e60
    +F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
     F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9
     F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013
     F ext/lsm1/lsm-test/README 87ea529d2abe615e856d4714bfe8bb185e6c2771b8612aa6298588b7b43e6f86
     F ext/lsm1/lsm-test/lsmtest.h cf58528ffe0cfe535e91b44584e2ec5fb1caacdabecef0d8dcf83bf83168bf28
    -F ext/lsm1/lsm-test/lsmtest1.c ae6ba48a0851b39be69a7d0eb220bfb9521a526e926223d5014bd385df10abb3
    +F ext/lsm1/lsm-test/lsmtest1.c 54374fe88cee888c52c31160013c26184288f47a45b23d4d85390aa539733aab
     F ext/lsm1/lsm-test/lsmtest2.c 188b09aec776516aeedcfd13b9c6faf85ba16b3671a0897a2c740ee00a5dc4f8
     F ext/lsm1/lsm-test/lsmtest3.c 9ab87528a36dbf4a61d7c8ad954f5ee368c0878c127b84b942b2e2abe522de26
     F ext/lsm1/lsm-test/lsmtest4.c d258d6a245db5d8eaede096e2368d23f859c5e92c80ab9122463f708514fe10c
     F ext/lsm1/lsm-test/lsmtest5.c 8d5242a0f870d65eeada191c8945781fed9cb8ece3886573790ebd373b62dac5
     F ext/lsm1/lsm-test/lsmtest6.c 869cb4a172cd07d1a75b3aeaecd61d0a477787b3b8668bad0d3ff0f43b642b7c
     F ext/lsm1/lsm-test/lsmtest7.c 7a917455a0f956a8ed3f44f5c9387ec0ea6627714874464cc3fa5c5a9cabb2f2
    -F ext/lsm1/lsm-test/lsmtest8.c 589b68c44531a0f04d5e879bb1e211be5f7100f48eed7e8631e07ed5cbd68f94
    -F ext/lsm1/lsm-test/lsmtest9.c dd1a0ebf41134933a744d1e00e60429a2a21fc50d587ae7dd6bdb6e96d805bdc
    -F ext/lsm1/lsm-test/lsmtest_bt.c d70d9a9be5eef9360af1251dd083948d74fd30137a08f61bef995f7ac04e037f
    +F ext/lsm1/lsm-test/lsmtest8.c 773f226163d0f0d62701e3764d0c35fd4365faca74098bd63648bc57d6f14402
    +F ext/lsm1/lsm-test/lsmtest9.c 0a168757b757b106191acf43143dbbb5b2d76e57a3c8fd3018cecbaee1080aba
    +F ext/lsm1/lsm-test/lsmtest_bt.c 79b24bfd37e05fd626c35ec23bc5bb62d8a403afd66c710335384884dc1366d7
     F ext/lsm1/lsm-test/lsmtest_datasource.c 5d770be191d0ca51315926723009b2c25c0b4b8136840494ef710ac324aa916c
     F ext/lsm1/lsm-test/lsmtest_func.c 159aa401bc8032bfa3d8cf2977bd687abebab880255895a5eb45770d626fa38d
     F ext/lsm1/lsm-test/lsmtest_io.c cf11b27b129c6bd5818fa1d440176502dc27229f0db892b4479118d61993ea20
     F ext/lsm1/lsm-test/lsmtest_main.c a9bc647738c0dcaebf205d6d194b3ce4a6ef3925801cd2d919f0a4ea33a15aeb
     F ext/lsm1/lsm-test/lsmtest_mem.c 4e63c764345ab1df59d4f13a77980c6f3643798210b10d6cdbd785b4b888fda5
    -F ext/lsm1/lsm-test/lsmtest_tdb.c 618a8619183fda4f5540fcde15f9068293c5e3180e1a246e34409b0c148758b3
    +F ext/lsm1/lsm-test/lsmtest_tdb.c 754b1ca8e1cfa7b29cbe2e4ab500f7eee0059033741b8d83267afe6f495a536d
     F ext/lsm1/lsm-test/lsmtest_tdb.h 8733eee249b12956a9df8322994b43d19bd8c02ad2e8b0bb5164db4d6ccc1735
    -F ext/lsm1/lsm-test/lsmtest_tdb2.cc 99ea7f2dd9c7536c8fb9bdd329e4cfeb76899f3ddf6f48bdd3926e016922b715
    +F ext/lsm1/lsm-test/lsmtest_tdb2.cc aebe50f2cb7a759214241938046fe5f00da66e4217637f946f436ca209776af9
     F ext/lsm1/lsm-test/lsmtest_tdb3.c 7a7ccae189f5bb25bcd1ec3bbd740529706eded7f6729a5a0a9eeaeb57785320
    -F ext/lsm1/lsm-test/lsmtest_tdb4.c 47e8bb5eba266472d690fb8264f1855ebdba0ae5a0e541e35fcda61ebf1d277f
    +F ext/lsm1/lsm-test/lsmtest_tdb4.c cbe230727b9413d244062943371af1421ace472ccb023b75af6540e0fa52b1bb
     F ext/lsm1/lsm-test/lsmtest_util.c 241622db5a332a09c8e6e7606b617d288a37b557f7d3bce0bb97809f67cc2806
     F ext/lsm1/lsm-test/lsmtest_win32.c 0e0a224674c4d3170631c41b026b56c7e1672b151f5261e1b4cc19068641da2d
     F ext/lsm1/lsm.h 0f6f64ff071471cb87bf98beb8386566f30ea001
    @@ -271,7 +272,7 @@ F ext/lsm1/lsm_shared.c 76adfc1ed9ffebaf92746dde4b370ccc48143ca8b05b563816eadd2a
     F ext/lsm1/lsm_sorted.c 6f7d8cf7a7d3d3f1ab5d9ba6347e8f39f3d73c00ec48afcd0c4bcbefd806f9b8
     F ext/lsm1/lsm_str.c 65e361b488c87b10bf3e5c0070b14ffc602cf84f094880bece77bbf6678bca82
     F ext/lsm1/lsm_tree.c 682679d7ef2b8b6f2fe77aeb532c8d29695bca671c220b0abac77069de5fb9fb
    -F ext/lsm1/lsm_unix.c 57361bcf5b1a1a028f5d66571ee490e9064d2cfb145a2cc9e5ddade467bb551b
    +F ext/lsm1/lsm_unix.c 11e0a5c19d754a4e1d93dfad06de8cc201f10f886b8e61a4c599ed34e334fc24
     F ext/lsm1/lsm_varint.c 43f954af668a66c7928b81597c14d6ad4be9fedbc276bbd80f52fa28a02fdb62
     F ext/lsm1/lsm_vtab.c 169bfe7ef8e6c9de9c77e17c4c50c9ae55fb0167d80be3d1be82c991184b6f35
     F ext/lsm1/lsm_win32.c 0a4acbd7e8d136dd3a5753f0a9e7a9802263a9d96cef3278cf120bcaa724db7c
    @@ -281,50 +282,53 @@ F ext/lsm1/tool/mklsm1c.tcl f31561bbee5349f0a554d1ad7236ac1991fc09176626f529f607
     F ext/misc/README.md d6dd0fe1d8af77040216798a6a2b0c46c73054d2f0ea544fbbcdccf6f238c240
     F ext/misc/amatch.c e3ad5532799cee9a97647f483f67f43b38796b84b5a8c60594fe782a4338f358
     F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
    -F ext/misc/appendvfs.c 3777f22ec1057dc4e5fd89f2fbddcc7a29fbeef1ad038c736c54411bb1967af7
    +F ext/misc/appendvfs.c 55121d311d408ba9c62c3cfa367408887638f02f9522dd9859891d0ee69a7eba
     F ext/misc/blobio.c a867c4c4617f6ec223a307ebfe0eabb45e0992f74dd47722b96f3e631c0edb2a
    -F ext/misc/btreeinfo.c 4f0ebf278f46e68e6306c667917766cebc5550fd35d5de17847988e22892d4d2
    +F ext/misc/btreeinfo.c d28ce349b40054eaa9473e835837bad7a71deec33ba13e39f963d50933bfa0f9
     F ext/misc/carray.c 91e9a7f512fda934894bed30464552fffa7d3073b5be04189ae0bd0c59f26bfd
    +F ext/misc/cksumvfs.c 0f022867786b615d7f68fb3ab3a16627fe6a730442abf804735e18a73f835a83
     F ext/misc/closure.c dbfd8543b2a017ae6b1a5843986b22ddf99ff126ec9634a2f4047cd14c85c243
    -F ext/misc/completion.c a0efe03edfdc4f717c61e6c9b0bfe2708ff7878010dae3174980a68fdf76aabc
    +F ext/misc/completion.c 6dafd7f4348eecc7be9e920d4b419d1fb2af75d938cd9c59a20cfe8beb2f22b9
     F ext/misc/compress.c 3354c77a7c8e86e07d849916000cdac451ed96500bfb5bd83b20eb61eee012c9
     F ext/misc/csv.c 3ed979c1eb35e35a98b30ef545a2facf62994594217681d9138b4b75faf6b0d7
     F ext/misc/dbdata.c e316fba936571584e55abd5b974a32a191727a6b746053a0c9d439bd2cf93940
    -F ext/misc/dbdump.c baf6e37447c9d6968417b1cd34cbedb0b0ab3f91b5329501d8a8d5be3287c336
    +F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f823e01
    +F ext/misc/decimal.c 3ddbf8162015be4d5ec2395dee4538f1e638bb517174bb148274b132df6e1d08
     F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1
    -F ext/misc/explain.c d5c12962d79913ef774b297006872af1fccda388f61a11d37758f9179a09551f
    -F ext/misc/fileio.c bfa11a207da4eed8e5f84a1e3954608492f25f8850f9f00d0d2076f4648d7608
    +F ext/misc/explain.c 0086fab288d4352ea638cf40ac382aad3b0dc5e845a1ea829a694c015fd970fe
    +F ext/misc/fileio.c 9b69e25da3b51d4a1d905a464ccb96709792ad627a742ba09215bc0d1447e7bd
     F ext/misc/fossildelta.c 1240b2d3e52eab1d50c160c7fe1902a9bd210e052dc209200a750bbf885402d5
     F ext/misc/fuzzer.c eae560134f66333e9e1ca4c8ffea75df42056e2ce8456734565dbe1c2a92bf3d
    -F ext/misc/ieee754.c eaffd9b364d7c8371727e9c43fc8bec38cdacc4d11fc26beffaa3ca05a0ea9d6
    -F ext/misc/json1.c 2d44e3fa37f958b42cbcd41651f9f0a0eaaf3bac3f1f4b8eb456431623cb3bd8
    +F ext/misc/ieee754.c 5c7ca326361c7368f95f5743972eade3b8b24f60359ed7cba4706668a5682896
    +F ext/misc/json1.c f31e89171f932d1821c91f10d2cb4979fc0447030030a8bce70420cd43d074c0
     F ext/misc/memstat.c 3017a0832c645c0f8c773435620d663855f04690172316bd127270d1a7523d4d
     F ext/misc/memtrace.c 7c0d115d2ef716ad0ba632c91e05bd119cb16c1aedf3bec9f06196ead2d5537b
     F ext/misc/memvfs.c ab36f49e02ebcdf85a1e08dc4d8599ea8f343e073ac9e0bca18a98b7e1ec9567
    -F ext/misc/mmapwarm.c 8c5fe90d807a23e44a8b93e96e8b812b19b300d5fd8c1d40a4fd1d8224e33f46
    +F ext/misc/mmapwarm.c 347caa99915fb254e8949ec131667b7fae99e2a9ce91bd468efb6dc372d9b7a9
     F ext/misc/nextchar.c 7877914c2a80c2f181dd04c3dbef550dfb54c93495dc03da2403b5dd58f34edd
     F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1ab
    -F ext/misc/normalize.c b4290464f542bae7a97b43f15bd197949b833ffd668b7c313631bd5d4610212c
    +F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f
     F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691
     F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196
     F ext/misc/regexp.c 246244c714267f303df76acf73dcf110cf2eaf076896aaaba8db6d6d21a129db
     F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
     F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
    -F ext/misc/scrub.c db9fff56fed322ca587d73727c6021b11ae79ce3f31b389e1d82891d144f22ad
    +F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
     F ext/misc/series.c 4057dda3579b38ff88b2d3b13b4dd92dbd9d6f90dac2b55c19b0a8ed87ee4959
    -F ext/misc/sha1.c 1190aec0d9d886d9f5ffdf891142a626812327d11472c0cade3489db3b7b140a
    +F ext/misc/sha1.c c8f2253c8792ffab9517695ea7d88c079f0395a5505eefef5c8198fe184ed5ac
     F ext/misc/shathree.c 135b7c145db4a09b1650c3e7aff9cb538763a9a361e834c015dd1aaf8d5c9a00
     F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
     F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
    -F ext/misc/sqlar.c c9e5d58544e1506135806a1e0f525f92d4bb6bb125348dce469d778fb334fbce
    -F ext/misc/stmt.c 8a8dc4675042e4551e4afe99b8d0cc7a4a2fc1a8dacc0a9ce1b1bbff145da93d
    +F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
    +F ext/misc/stmt.c 35063044a388ead95557e4b84b89c1b93accc2f1c6ddea3f9710e8486a7af94a
     F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
     F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
    +F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
     F ext/misc/unionvtab.c 36237f0607ca954ac13a4a0e2d2ac40c33bc6e032a5f55f431713061ef1625f9
     F ext/misc/urifuncs.c f71360d14fa9e7626b563f1f781c6148109462741c5235ac63ae0f8917b9c751
     F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505cf
     F ext/misc/vfslog.c 3b25c2f56ba60788db247287be6ab024b53c4afffd412b4876db563389be0d35
    -F ext/misc/vfsstat.c 77b5b4235c9f7f11eddf82487c0a422944ac2f132dafd5af3be7a68a057b1cdb
    +F ext/misc/vfsstat.c 389ea13983d3af926504c314f06a83cc858d5adc24b40af74aaed1fece00c118
     F ext/misc/vtablog.c 5538acd0c8ddaae372331bee11608d76973436b77d6a91e8635cfc9432fba5ae
     F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
     F ext/misc/wholenumber.c 520f34c3099e5b7d546f13708607dc2fa173c46b68952eecf0d19cd675fec85e
    @@ -370,12 +374,12 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
     F ext/rbu/rbuvacuum2.test b8e5b51dc8b2c0153373d024c0936be3f66f9234acbd6d0baab0869d56b14e6b
     F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
     F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
    -F ext/rbu/sqlite3rbu.c 77a47f3231f5f363b2c584dba3e310a7efdaf073ad8c18728ab846b38de2879c
    +F ext/rbu/sqlite3rbu.c 05c457c27e9340c944f34e850871a915a6b5ee1d823f7a0bb2b482ac6b1e1464
     F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
     F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
     F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
     F ext/repair/checkfreelist.c 0dbae18c1b552f58d64f8969e4fb1e7f11930c60a8c2a9a8d50b7f15bdfd54bd
    -F ext/repair/checkindex.c 7d28c01a2e012ac64257d230fc452b2cafb78311a91a343633d01d95220f66f3
    +F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890
     F ext/repair/sqlite3_checker.c.in 4a5a3af3f450fe503e5a2985e98516dc2a6b9ad247449e284c1cf140fc91720f
     F ext/repair/sqlite3_checker.tcl a9a2caa9660567257c177a91124d8c0dccdfa341e25c51e6da7f1fd9e601eafa
     F ext/repair/test/README.md 34b2f542cf5be7bffe479242b33ee3492cea30711e447cc4a1a86cb5915f419e
    @@ -383,10 +387,10 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
     F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c335096108c12c01bddbadcec
     F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
     F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
    -F ext/rtree/geopoly.c cac70b5502742bd0ba8877a1329a74e86a379c78567546a2a18cf5f9c3787f73
    -F ext/rtree/rtree.c 84b939a9a558edd0461bb976b98f60012e3e574b3b17a0f44533d6f2a9aa2f2e
    +F ext/rtree/geopoly.c f15cc6845d64a629035627d863cbe3eadc9cb30f9ca77bd823b0ca8a5a3f8b00
    +F ext/rtree/rtree.c f87ffcc91f49272862119cfdd256b02514351893786a710d88f85933790608d4
     F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
    -F ext/rtree/rtree1.test 4092a8bd2b5eafc4fafe4fe9024249c12b13e4bab23c2c3eaff57412fdf805fa
    +F ext/rtree/rtree1.test 00792b030a4e188ff1b22e8530e8aa0452bb5dd81c2b18cb004afc7dc63e040e
     F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
     F ext/rtree/rtree3.test 4ee5d7df86040efe3d8d84f141f2962a7745452200a7cba1db06f86d97050499
     F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd73452c4b
    @@ -433,18 +437,18 @@ F ext/session/sessionD.test 4f91d0ca8afc4c3969c72c9f0b5ea9527e21de29039937d0d973
     F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
     F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
     F ext/session/sessionG.test 3828b944cd1285f4379340fd36f8b64c464fc84df6ff3ccbc95578fd87140b9c
    -F ext/session/sessionH.test a417559f29a7e775950fc5fc82b3d01256a7cbe793ddf1180df234df823d56e2
    -F ext/session/session_common.tcl 29ec9910aca1e996ca1c8531b8cecabf96eb576aa53de65a8ff03d848b9a2a8b
    +F ext/session/sessionH.test b17afdbd3b8f17e9bab91e235acf167cf35485db2ab2df0ea8893fbb914741a4
    +F ext/session/session_common.tcl f613174665456b2d916ae8df3e5735092a1c1712f36f46840172e9a01e8cc53e
     F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
     F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28f0c1cc142c3ec
     F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
     F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
     F ext/session/sessionfault2.test dd593f80b6b4786f7adfe83c5939620bc505559770cc181332da26f29cddd7bb
    -F ext/session/sessioninvert.test ae1a003a9ab1f8d64227dbb5c3a4c97e65b561b01e7b2953cf48683fb2724169
    +F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2c52219819bc105a25
     F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2f05e82363b9142810
     F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5
     F ext/session/sessionwor.test 67b5ab91d4f93ce65ff1f58240ac5ddf73f8670facc1ffa49cef56293d52818d
    -F ext/session/sqlite3session.c a4dfb372f270df93422b0dc7666fd46849e6979b62a152f11287c21eed4ac21b
    +F ext/session/sqlite3session.c 2c76b8c3a5d6dab736686f8a48833b8bdac0871ecc6f447f9839d28bd4a63d6c
     F ext/session/sqlite3session.h a2db5b72b938d12c727b4b4ec632254ca493670a9c0de597af3271a7f774fc57
     F ext/session/test_session.c 98797aba475a799376c9a42214f2d1debf2d0c3cb657d9c8bbf4f70bf3fb4aec
     F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
    @@ -453,7 +457,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865
     F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
     F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
     F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
    -F main.mk 7ce055f3df31a4f7d21e38f493f907c21db1f673863a573e231f55e2ab005023
    +F main.mk b1cd0bc6aedad7ebb667b7f74f835f932f60ee33be2a5c3051fd93eb465f5c75
     F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
     F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
     F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
    @@ -465,90 +469,90 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
     F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
     F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
     F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
    -F src/alter.c f48a4423c8f198d7f1ae4940f74b606707d05384ac79fb219be8e3323af2a2de
    -F src/analyze.c b3ceec3fc052df8a96ca8a8c858d455dc5029ba681b4be98bb5c5a9162cfa58c
    -F src/attach.c df0ead9091042c68964856ecc08dba55d5403ad5f3ca865d9d396d71528c511a
    +F src/alter.c 805de23ddca536181f8f0439df989fdd4a2f76c40bc305ec9fe2f211f68c89e8
    +F src/analyze.c 5cffff3d355858cd22bfc6e20ac7203510d2e1cc935086eb06f4abb2f579f628
    +F src/attach.c 0b11e00c166b622c84ec176773b1d691c61ad07d247809e3e1635d4e99e71d30
     F src/auth.c a3d5bfdba83d25abed1013a8c7a5f204e2e29b0c25242a56bc02bb0c07bf1e06
    -F src/backup.c f70077d40c08b7787bfe934e4d1da8030cb0cc57d46b345fba2294b7d1be23ab
    +F src/backup.c b1c90cd4110248c8e1273ff4578d3a84c0c34725e1b96dacd4a6294a908702de
     F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
     F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
    -F src/btree.c 7af5ff0f88ba856c2681f6eeb457590b24f787e994f18cbdb44c2de2d33f757e
    -F src/btree.h 6111552f19ed7a40f029cf4b33badc6fef9880314fffd80a945f0b7f43ab7471
    -F src/btreeInt.h 6794084fad08c9750b45145743c0e3e5c27c94dee89f26dd8df7073314934fd2
    -F src/build.c 2394d2c853088106dfc1cf485d609f20e6421d7c84892b795824e454f78e50ad
    -F src/callback.c c547d00963ae28100117b4fb1f0f32242109b5804374ee3bfe01138a54da7f76
    +F src/btree.c 1439fd9b45d4d1883c53752daef42af489adaa1a1508fa39dedbc9c80ea21a2f
    +F src/btree.h 7af72bbb4863c331c8f6753277ab40ee67d2a2125a63256d5c25489722ec162b
    +F src/btreeInt.h 83166f6daeb91062b6ae9ee6247b3ad07e40eba58f3c05ba9e8dedad4ab1ea38
    +F src/build.c dbdaee54ffef924a070eb6202017e10d6be56baab953ef0a8e714a6def683198
    +F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
     F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
    -F src/ctime.c 1b0724e66f95f33b160b1af85caaf9cceb325d22abf39bd24df4f54a73982251
    -F src/date.c 6c408fdd2e9ddf6e8431aba76315a2d061bea2cec8fbb75e25d7c1ba08274712
    +F src/ctime.c e98518d2d3d4029a13c805e07313fb60c877be56db76e90dd5f3af73085d0ce6
    +F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
     F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
    -F src/dbstat.c 0f55297469d4244ab7df395849e1af98eb5e95816af7c661e7d2d8402dea23da
    -F src/delete.c a5c59b9c0251cf7682bc52af0d64f09b1aefc6781a63592c8f1136f7b73c66e4
    -F src/expr.c 003c59158b33d7f3b198122cb0d1e13c06517cc3932e56b42283eb0e96696d66
    +F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
    +F src/delete.c 410c771c25afc113c273d9efad6ab6881bda28c75a1838b9d2c52ba20d1dc704
    +F src/expr.c 58c06940d964c2cf455b979cf66a648499d294a5ee6dadcaeaed447257c1dc75
     F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
    -F src/fkey.c 92a248ec0fa4ed8ab60c98d9b188ce173aaf218f32e7737ba77deb2a684f9847
    -F src/func.c 92249abc3fd7e52b249ca8eb2d15a617f70819d2fa4c777a4a33552b89bfb322
    -F src/global.c 59601d885a0dbbfbd22ed2d030424a5e7f1b9809a17ca46686058bbc4a55e980
    +F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
    +F src/func.c 2333eb4277f55a5efdc12ef754e7d7ec9105d257b2fd00301d23ce1e8fa67dc0
    +F src/global.c 943256ac44f333039d35a9830c18d075a81fa6b6bf2af05771494a9acfb9a40b
     F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
     F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
     F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
     F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
    -F src/insert.c 2fe4d7f67078a68650f16e4efe73207899e21702e6b9d2e8ad1894c76dcad352
    +F src/insert.c 957254a2d0542597455d0d4c640e4e3f3eea8c6d78f04582df03dfc626f07925
     F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
    -F src/loadext.c 8cd803f1747c03a50b32fe87ebfb5851998d0cdafefe02737daa95e0616b42bb
    -F src/main.c 430db07f140a2455784b504af1a56fe49134a79dd479a203370490031708d48f
    -F src/malloc.c eaa4dc9602ce28b077f7de2eb275db2be270c5cc56d7fec5466301bd9b80e2f5
    +F src/loadext.c 436af4968c6954d304fce9efa12719367bd8f37b19b93b71d6ad607e85adbb47
    +F src/main.c 09580279145f27f3db206ef44dcb3a8875a42644230f79c7e54aff35e71668f0
    +F src/malloc.c 22d5bdd9fe88ae4fad1b91a1b9735104b82853ffef868f1f05517d60dc1875f5
     F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
     F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
    -F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
    -F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a
    +F src/mem2.c b93b8762ab999a29ae7751532dadf0a1ac78040308a5fb1d17fcc365171d67eb
    +F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
     F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
    -F src/memdb.c 02a5fcec19b9d40dd449ca802dc1b2e8f93f255fbf2a886277a3c3800d8d35db
    -F src/memjournal.c 7561c01c90958f3ba9bc6cb2d857123d932bdfa5539ea34427a0957b2e35154d
    +F src/memdb.c d58e398e315e88f95f8d07d17e80ab11259ebd6d1a10397434329eeabd1985e3
    +F src/memjournal.c 90b2ca7e2f465d57c16b69d15a9f3e3294af61088eb4938f2f7664d5ac50f813
     F src/msvc.h 3a15918220367a8876be3fa4f2abe423a861491e84b864fb2b7426bf022a28f8
    -F src/mutex.c bae36f8af32c22ad80bbf0ccebec63c252b6a2b86e4d3e42672ff287ebf4a604
    +F src/mutex.c 5e3409715552348732e97b9194abe92fdfcd934cfb681df4ba0ab87ac6c18d25
     F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a
     F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
    -F src/mutex_unix.c aaf9ebc3f89df28483c52208497a99a02cc3650011422fc9d4c57e4392f7fe58
    -F src/mutex_w32.c 7670d770c94bbfe8289bec9d7f1394c5a00a57c37f892aab6b6612d085255235
    -F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
    -F src/os.c 669cc3839cc35d20f81faf0be1ab6d4581cea35e9d8f3a9d48a98d6571f7c285
    +F src/mutex_unix.c dd2b3f1cc1863079bc1349ac0fec395a500090c4fe4e11ab775310a49f2f956d
    +F src/mutex_w32.c caa50e1c0258ac4443f52e00fe8aaea73b6d0728bd8856bedfff822cae418541
    +F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6
    +F src/os.c 80e4cf3e5da06be03ca641661e331ce60eeeeabf0d7354dbb1c0e166d0eedbbe
     F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
     F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
     F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
    -F src/os_unix.c ad7640c04eed946052a3b12856362a773d0a717696707313037186df0e2b59f2
    -F src/os_win.c 035a813cbd17f355bdcad7ab894af214a9c13a1db8aeac902365350b98cd45a7
    +F src/os_unix.c 9b1b860163fd2d4d7679b5260d384d1a9f88ef917a90f28963eca8acd472d8c8
    +F src/os_win.c a2149ff0a85c1c3f9cc102a46c673ce87e992396ba3411bfb53db66813b32f1d
     F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
    -F src/pager.c 302a18da8b247881808cd65009e1ac7c8b6e5cefb22ed9a1c330ed47b73e94ab
    -F src/pager.h 71fe1d5016ec54d0cc5d344cd474e563450b438c59f535e8c1ec8a13b1373f14
    -F src/parse.y c8d2de64db469fd56e0fa24da46cd8ec8523eb98626567d2708df371b47fdc3f
    +F src/pager.c 3700a1c55427a3d4168ad1f1b8a8b0cb9ace1d107e4506e30a8f1e66d8a1195e
    +F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
    +F src/parse.y 5bdb760a29c0b25caf7e80e82210b81cd2ea3066d5199ca29e6eac40b34bc184
     F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
     F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
     F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
    -F src/pragma.c 0d49d43b22d66397aa026db505457f6683d8a66cd0a4f9db2e6776156bda716c
    -F src/pragma.h 9f86a3a3a0099e651189521c8ad03768df598974e7bbdc21c7f9bb6125592fbd
    -F src/prepare.c 6049beb71385f017af6fc320d2c75a4e50b75e280c54232442b785fbb83df057
    -F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
    +F src/pragma.c bdb600be936f66b9fe69d26dfbba4528beaaf4f95c479c85b328a92484e0bf71
    +F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
    +F src/prepare.c 3d5a761d026052bc888d1b803a06dd2bfe245e8e836d4689f927003549148b0f
    +F src/printf.c 9efcd4e984f22bcccb1ded37a1178cac98f6e3a0534e1e0629f64899971f8838
     F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
    -F src/resolve.c f0781c9e180028b279bc4ff079ad54f4727223d470c8d2343643fcaf79b67740
    -F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
    -F src/select.c 3f7aecf64b08b018b89e4fe16ea621cc9a0e3f3801e9e5638cfe1a6035fa1581
    -F src/shell.c.in c2e20c43a44fb5588a6c27ce60589538fbf4794fd7686f5b2598eca22eaae1fa
    -F src/sqlite.h.in 75d0304247a2154122d6d06f12219c1e29291d72304f0eeef4c1ec6b1409b443
    +F src/resolve.c d74715aceed2a8f493ba244d535646fa93132042a4400a29dfd26ec841514048
    +F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
    +F src/select.c 510fdf819f218be3dac2683d3eaaf64e5080f548061a4dd12205590beda976bb
    +F src/shell.c.in b9b819feede7b85585ab0826490a352e04e2ee46e8132c92597d29972b2be1d7
    +F src/sqlite.h.in d2c03414a8ee5d4a6855c04dd7cd5998e45139b0fe66b65bae86d4223edd091f
     F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
    -F src/sqlite3ext.h b0f776a0d042b23b6bcbb6b0943e8a3768c7f0b438a275e7168f0204e223a4db
    -F src/sqliteInt.h 7a29ba700a51eeb925731749a570cf3859f6a58ed94797ecf47508875b0ba279
    -F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
    -F src/status.c 9ff2210207c6c3b4d9631a8241a7d45ab1b26a0e9c84cb07a9b5ce2de9a3b278
    -F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
    -F src/tclsqlite.c 97590069efaba5a4928ecffb606e3771dd93ee8e6bf248a62a6507c37a2b2e46
    -F src/test1.c 4d0ab2f67053a4fff87d1d3586ecc0e5322a1fc45dd4119ab11dc96de44f17a1
    +F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
    +F src/sqliteInt.h a1aa5457ca881cbf5adb55933bf81d7d4889375afb9a0a5df382a451c058087d
    +F src/sqliteLimit.h 95cb8479ca459496d9c1c6a9f76b38aee12203a56ce1092fe13e50ae2454c032
    +F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
    +F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
    +F src/tclsqlite.c 986b6391f02cd9b53c1d688be55899f6ffddeb8e8014cd83c1b73ff912579a71
    +F src/test1.c 24b9cd0863ecc4d3920f4999c40e876c2bd92f3cc5879c48b99abe02c546ed18
     F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
     F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
    -F src/test4.c 405834f6a93ec395cc4c9bb8ecebf7c3d8079e7ca16ae65e82d01afd229694bb
    +F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
     F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
     F src/test6.c e8d839fbc552ce044bec8234561a2d5b8819b48e29548ad0ba400471697946a8
     F src/test7.c 5612e9aecf934d6df7bba6ce861fdf5ba5456010
    -F src/test8.c 3f7d0cc4e12e06832ba3db4455cb16867ccadafa602eb6ff5fcf097bffce56ed
    +F src/test8.c 7fb971777c2c79c734bb52757191d68d4af659b8de9b4a071be3f527a9d19a02
     F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5
     F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a
     F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871
    @@ -556,7 +560,7 @@ F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0
     F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857
     F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
     F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
    -F src/test_config.c e25826d693039cdd45963de378cbf39e3af0e8aa7a8a6fc159876f4e7b5a4f8c
    +F src/test_config.c 5ea19bf0972a9d91728518b4d30e91477acce80496003ecbef3a7fb18d0bd081
     F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
     F src/test_demovfs.c 86142ba864d4297d54c5b2e972e74f3141ae4b30f05b3a95824184ed2d3d7f91
     F src/test_devsym.c 6109b45c3db3ef7b002320947ed448c027356ab8b885156ff535fd8684d4a571
    @@ -568,25 +572,25 @@ F src/test_intarray.c 39b4181662a0f33a427748d87218e7578d913e683dc27eab7098bb4161
     F src/test_intarray.h d57ae92f420cda25e22790dac474d60961bd0c500cbaa3338a05152d4a669ef7
     F src/test_journal.c a0b9709b2f12b1ec819eea8a1176f283bca6d688a6d4a502bd6fd79786f4e287
     F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd
    -F src/test_malloc.c dec0aa821b230773aeb3dd11d652c1193f7cedb18a20b25659bc672288115242
    +F src/test_malloc.c 21121ea85b49ec0bdb69995847cef9036ef9beca3ce63bbb776e4ea2ecc44b97
     F src/test_md5.c 7268e1e8c399d4a5e181b64ac20e1e6f3bc4dd9fc87abac02db145a3d951fa8c
    -F src/test_multiplex.c e054459f7633f3ff8ce1245da724f9a8be189e4e
    +F src/test_multiplex.c 2ccf35551c094e353af20b0cdfac053a37bf3e96e10e7cf9f4abd1d279890a78
     F src/test_multiplex.h 5436d03f2d0501d04f3ed50a75819e190495b635
    -F src/test_mutex.c 7f4337ba23ee6b1d2ec81c189653608cb069926a
    +F src/test_mutex.c abf486e91bd65e2448027d4bb505e7cce6ba110e1afb9bd348d1996961cadf0d
     F src/test_onefile.c f31e52e891c5fef6709b9fcef54ce660648a34172423a9cbdf4cbce3ba0049f4
    -F src/test_osinst.c 98ef31ff03d55497829ca0f6c74a9f4e1aa48690
    +F src/test_osinst.c d341f9d7613e007c8c3f7eba6cd307230047506aa8f97858c1fd21f5069616bd
     F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
     F src/test_quota.c 6cb9297115b551f433a9ad1741817a9831abed99
     F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d
     F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b
    -F src/test_schema.c f575932cb6274d12147a77e13ea4b49d52408513
    +F src/test_schema.c f5d6067dfc2f2845c4dd56df63e66ee826fb23877855c785f75cc2ca83fd0c1b
     F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
    -F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
    +F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a
     F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
     F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
     F src/test_tclsh.c eeafce33ad2136d57e5dec10f1e9a4347447eb72ffd504a1c7b9c6bfe2e71578
     F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
    -F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
    +F src/test_thread.c 269ea9e1fa5828dba550eb26f619aa18aedbc29fd92f8a5f6b93521fbb74a61c
     F src/test_vdbecov.c f60c6f135ec42c0de013a1d5136777aa328a776d33277f92abac648930453d43
     F src/test_vfs.c 36822d696789535bdd0260f07d2c9a46546082fea8bb1d0a7354c7f9366e37ea
     F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
    @@ -595,53 +599,54 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9
     F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
     F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
     F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
    -F src/tokenize.c 7b17f6e2f20f6cbcb0b215025a86b7457c38451fc7622f705e553d7a488c572d
    -F src/treeview.c 438c1000587b33faba35e87596bebcf7f40638d98f33781cdd9e04711b18b09c
    -F src/trigger.c a40d50e88bd3355f1d2a73f0a3b2d6b42eae26ca4219001b82ef0d064439badc
    -F src/update.c 9ad19af96aff95dc02a923a99f97c1bc0b909009a29a2914b796f786b9ac0c60
    +F src/tokenize.c 4dc01b267593537e2a0d0efe9f80dabe24c5b6f7627bc6971c487fa6a1dacbbf
    +F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
    +F src/trigger.c ef67bde309a831515dc3c2173d792574309f2f42d45f8c078743fae9f7f98c75
    +F src/update.c fb15bec5b54fd098f4b84f6abc83c7103b45ba8484011fff8edf5ae31656eab6
     F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
    -F src/utf.c 736ff76753236ffbc8b5b939f5e0607f28aeaa7c780b3a56b419228f0a81c87b
    -F src/util.c d035b09df9cecbc0e8f07c34b815acbf0d43c8adc8d2c540e3dc92eecb27855a
    -F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf
    -F src/vdbe.c e3dd230ece613409507523e68436764cc20638bb77ba2f416097de5b37235ce1
    -F src/vdbe.h defd693289c7bb8d325f109be9490c77138061211a116827da7244b6015a4934
    -F src/vdbeInt.h 30d3e8b991547cdf39025e416a0a737b0416d46747af70ae058f60e2e0466fe7
    -F src/vdbeapi.c 1252d80c548711e47a6d84dae88ed4e95d3fbb4e7bd0eaa1347299af7efddf02
    -F src/vdbeaux.c ff690e6c9314ef281de7c06f8c8c33393f0afca80aabb1fe69836dcf2d60b0bf
    +F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
    +F src/util.c c8bf30c4356b091bcc3b624d0e24b2b4d11b8be4d6c90d8e0705971e15cc819b
    +F src/vacuum.c 1c4f8e2f39d950037f4cf946b6858c993d3a54c3101f78e05c76460a073afcf0
    +F src/vdbe.c e9f7f818f128c8600058c0eabb6b3975974c95153a104d340f419adabbc15b9f
    +F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
    +F src/vdbeInt.h 762abffb7709f19c2cb74af1bba73a900f762e64f80d69c31c9ae89ed1066b60
    +F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
    +F src/vdbeaux.c 73854da7a9a4f12db72a855758214173c82f46a14be6cb19e63677ba02c97cae
     F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
    -F src/vdbemem.c 6200af702c87105d5b00d8ac5f5fa2c6d8f796aa974dbe2d15dcd95379ba1fa7
    -F src/vdbesort.c a3be032cc3fee0e3af31773af4a7a6f931b7230a34f53282ccf1d9a2a72343be
    +F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
    +F src/vdbesort.c 2be76d26998ce2b3324cdcc9f6443728e54b6c7677c553ad909c7d7cfab587df
     F src/vdbetrace.c fa3bf238002f0bbbdfb66cc8afb0cea284ff9f148d6439bc1f6f2b4c3b7143f0
    -F src/vtab.c 7b704a90515a239c6cdba6a66b1bb3a385e62326cceb5ecb05ec7a091d6b8515
    +F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
    +F src/vtab.c 5f5fc793092f53bbdfde296c50f563fb7bda58cf48e9cf6a8bdfbc5abd409845
     F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
    -F src/wal.c dbc77159e6734c2d64343cb8624ad245d89dd79a5010750fce8118b3fa7be2e8
    -F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
    -F src/walker.c a137468bf36c92e64d2275caa80c83902e3a0fc59273591b96c6416d3253d05d
    -F src/where.c 2005d0511e05e5f7b6fb3be514b44f264f23d45f3b0cc5e150c63e3006a003e5
    -F src/whereInt.h 9157228db086f436a574589f8cc5749bd971e94017c552305ad9ec472ed2e098
    -F src/wherecode.c ec8870d6fe79668dd12d7edc65ae9771828d6cdfe478348c8abd872a89fdbadd
    -F src/whereexpr.c 4b34be1434183e7bb8a05d4bf42bd53ea53021b0b060936fbd12062b4ff6b396
    -F src/window.c f8ba2ee12a19b51d3ba42c16277c74185ee9215306bc0d5a03974ade8b5bc98f
    +F src/wal.c 69e770e96fd56cc21608992bf2c6f1f3dc5cf2572d0495c6a643b06c3a679f14
    +F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
    +F src/walker.c 3df26a33dc4f54e8771600fb7fdebe1ece0896c2ad68c30ab40b017aa4395049
    +F src/where.c 2ea911238674e9baaeddf105dddabed92692a01996073c4d4983f9a7efe481f9
    +F src/whereInt.h 6b874aa15f94e43a2cec1080be64d955b04deeafeac90ffb5d6975c0d511be3c
    +F src/wherecode.c 8064fe5c042824853a9b1fda670054a51a49033a6c79059988c97751ccf8088e
    +F src/whereexpr.c 264d58971eaf8256eb5b0917bcd7fc7a1f1109fdda183a8382308a1b18a2dce7
    +F src/window.c edd6f5e25a1e8f2b6f5305b7f5f7da7bb35f07f0d432b255b1d4c2fcab4205aa
     F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
     F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
     F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
     F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
    -F test/aggnested.test 12106f0748e8e9bfc1a8e6840e203e051eae06a26ed13fc9fd5db108a8d6db54
    +F test/aggnested.test 2f65ec8132e0ca896de550b9908094d49ad65a99116a9d79deeb6017604ad4f6
     F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
     F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13
    -F test/alter.test 77f0092d137dd9470fc683b64ed92868e188462e713e52f48deae8902ea60b96
    +F test/alter.test 25e109787dc5e631e117eb6e1c57f96a572bb51228db3b4f8b5f41d665e2ccaa
     F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807687
    -F test/alter3.test 9351a9f0c59ff9dddecccaaa2f777ffee5369870c63d30d3a74add815254ec0f
    -F test/alter4.test 74b22251c5e9c48093cfc4921ed9c11b59df84634aeeb00e501773320beb8424
    +F test/alter3.test e487958dec7932453e0b83baf21d6b1e71d5e7d9a55bc20eadfa62a51ddffc29
    +F test/alter4.test dfd6086faf461b27ca2d2999848dcd207edf23352fc1592d0005c0844f3f08cf
     F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
     F test/alterauth2.test c0a1ddf5b93d93cb0d15ba7acaf0c5c6fb515bbe861ede75b2d3fabad33b6499
     F test/altercol.test 1d6a6fe698b81e626baea4881f5717f9bc53d7d07f1cd23ee7ad1b931f117ddf
     F test/alterlegacy.test 82022721ce0de29cedc9a7af63bc9fcc078b0ee000f8283b4b6ea9c3eab2f44b
     F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
     F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b
    -F test/altertab.test bd61e5b73d495ec4707133db91b07f09d57e339d988de5ec5a76d34a2198e8f2
    +F test/altertab.test b8b2104212e8ea87c75c3cbe3cb78ed7236a6c828ee2e59ed09d3dbe9812d002
     F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b
    -F test/altertab3.test 155b8dc225ce484454a7fb4c8ba745680b6fa0fc3e08919cbbc19f9309d128ff
    +F test/altertab3.test d0d51e652aaa11e37de1f1215181d88334fefcb185f3b9bd91e06e98260c4694
     F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
     F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7
     F test/analyze3.test 01f0b122e3e54ad2544f14f7cc7dcb4c2cb8753cad5e88c6b8d49615b3fd6a2b
    @@ -655,6 +660,7 @@ F test/analyzeC.test 489fe2ea3be3f17548e8dd895f1b41c9669b52de1b0861f5bffe6eec46e
     F test/analyzeD.test e50cd0b3e6063216cc0c88a1776e8645dc0bd65a6bb275769cbee33b7fd8d90c
     F test/analyzeE.test 8684e8ac5722fb97c251887ad97e5d496a98af1d
     F test/analyzeF.test 9e1a0537949eb5483642b1140a5c39e5b4025939024b935398471fa552f4dabb
    +F test/analyzeG.test a48c0f324dd14de9a40d52abe5ca2637f682b9a791d2523dd619f6efa14e345b
     F test/analyzer1.test 459fa02c445ddbf0101a3bad47b34290a35f2e49
     F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
     F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
    @@ -664,8 +670,8 @@ F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0
     F test/atof1.test 1ccfc96a6888566597b83d882c81b3c04258dc39317e8c1cec89ba481eaa2fba
     F test/atomic.test 065a453dde33c77ff586d91ccaa6ed419829d492dbb1a5694b8a09f3f9d7d061
     F test/atomic2.test b6863b4aa552543874f80b42fb3063f1c8c2e3d8e56b6562f00a3cc347b5c1da
    -F test/atrc.c ec92d56d8fbed9eb3e11aaf1ab98cf7dd59e69dae31f128013f1d97e54e7dfed
    -F test/attach.test 21bce8681f780a8d631a5ec7ecd0d849bfe84611257b038ae4ffeccc609d8a4e
    +F test/atrc.c c388fac43dbba05c804432a7135ae688b32e8f25818e9994ffba4b64cf60c27c
    +F test/attach.test d42862c72fef3d54367d962d41dcfb5363442a4a1bd898c22ae950cea1aa0dd3
     F test/attach2.test 256bd240da1835fb8408dd59fb7ef71f8358c7a756c46662434d11d07ba3a0ce
     F test/attach3.test c59d92791070c59272e00183b7353eeb94915976
     F test/attach4.test aa05b1d8218b24eba5a7cccf4f224f514ba57ba705c9267f09d2bb63fed0eea1
    @@ -699,6 +705,7 @@ F test/bestindex3.test 7622e792ff2da16d262d3cea6ad914591ac4806d57ed128e6c940b792
     F test/bestindex4.test 038e3d0789332f3f1d61474f9bbc9c6d08c6bd1783a978f31f38ad82688de601
     F test/bestindex5.test 67c1166131bb59f9e47c00118f7d432ca5491e6cae6ca3f87ca9db20103a78f9
     F test/bestindex6.test d856a9bb63d927493575823eed44053bc36251e241aa364e54d0f2a2d302e1d4
    +F test/bestindex7.test f36ada201973d3022cf7afffffe08de9e58341996020e7a2df9a1d2f2be20132
     F test/between.test 68137a6e941c221417c15b6fe2d55f27bb1b6ab48bdf9e2aa51efdd85bc53802
     F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
     F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
    @@ -721,6 +728,7 @@ F test/btree01.test e08b3613540145b353f20c81cb18ead54ff12e0f
     F test/btree02.test 7555a5440453d900410160a52554fe6478af4faf53098f7235f1f443d5a1d6cc
     F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3
     F test/busy.test 510dc6daaad18bcbbc085bcc6217d6dc418def5e73f72ce1475eea0cb7834727
    +F test/busy2.test 2499cb62c9e58e18335892602c158cb35639c411803adca6423401b31e46c503
     F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de
     F test/cacheflush.test af25bb1509df04c1da10e38d8f322d66eceedf61
     F test/cachespill.test 895997f84a25b323b166aecb69baab2d6380ea98f9e0bcc688c4493c535cfab9
    @@ -732,7 +740,7 @@ F test/capi3d.test aba917805573a03deed961a21f07a5a84505ad0a616f7e3fc1508844a15bc
     F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
     F test/cast.test 2906ccab6a3ebd147ffa63304b635be903ce58264110d0a0eb4fd9939422bb53
     F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
    -F test/check.test b21a76546c2115af2674280566a8eba577e72adfec330c3d9a8a466d41f8eb0d
    +F test/check.test 9776af795bb5d0f197b7f8fe248052e3ca2ed35304d863efcdbf8b1316e11f65
     F test/checkfault.test da6cb3d50247169efcb20bdf57863a3ccfa1d27d9e55cd324f0680096970f014
     F test/chunksize.test 427d87791743486cbf0c3b8c625002f3255cb3a89c6eba655a98923b1387b760
     F test/close.test eccbad8ecd611d974cbf47278c3d4e5874faf02d811338d5d348af42d56d647c
    @@ -757,7 +765,7 @@ F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb54
     F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4
     F test/corrupt.test d7cb0300e4a297147b6a05e92a1684bc8973635c3bcaa3d66e983c9cbdbf47a3
     F test/corrupt2.test bb50042cf9a1f1023d73af325d47eb02a6bb11e3c52f8812644b220c5d4bca35
    -F test/corrupt3.test f95d7bf78109e0b84eb285a787ce91a3fd6a2dd7d0cb55882abff3bdc081a57e
    +F test/corrupt3.test 2520432b1fbf99994841e69804a3c59fb828183f4d09b85a1631bc7adca17e31
     F test/corrupt4.test 8d1d86b850fcc43e417450454f2044e52d55778a
     F test/corrupt5.test 387be3250795e2a86e6234745558b80efb248a357d0cd8e53bce75c7463f545d
     F test/corrupt6.test fc6a891716139665dae0073b6945e3670bf92568
    @@ -775,10 +783,10 @@ F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454
     F test/corruptI.test a17bbf54fdde78d43cf3cc34b0057719fd4a173a3d824285b67dc5257c064c7b
     F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
     F test/corruptK.test 5b4212fe346699831c5ad559a62c54e11c0611bdde1ea8423a091f9c01aa32af
    -F test/corruptL.test 4f28fbef85a6f27489542bb915ab7938dcd68f896e8f62a7d23de02b32489e5d
    +F test/corruptL.test 22589f503602cc5984e80f27f46c4de2134f24f1515ba2440513c377cb692258
     F test/corruptM.test 7d574320e08c1b36caa3e47262061f186367d593a7e305d35f15289cc2c3e067
    -F test/cost.test 51f4fcaae6e78ad5a57096831259ed6c760e2ac6876836e91c00030fad385b34
    -F test/count.test cb2e0f934c6eb33670044520748d2ecccd46259c
    +F test/cost.test 1d156ce9858780a966c062694687afe0343a0ed12d081d071fb57027e726bafc
    +F test/count.test e0699a15712bc2a4679d60e408921c2cce7f6365a30340e790c98e0f334a9c77
     F test/countofview.test e17d6e6688cf74f22783c9ec6e788c0790ee4fbbaee713affd00b1ac0bb39b86
     F test/coveridxscan.test 5ec98719a2e2914e8908dc75f7247d9b54a26df04625f846ac7900d5483f7296
     F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
    @@ -792,23 +800,24 @@ F test/crash8.test 64366e459c28dd62edfb7ad87253a409c7533b92d16fcc479a6a8131bdcc3
     F test/crashM.test d95f59046fa749b0d0822edf18a717788c8f318d
     F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
     F test/createtab.test 85cdfdae5c3de331cd888d6c66e1aba575b47c2e3c3cc4a1d6f54140699f5165
    -F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
    +F test/cse.test 00b3aea44b16828833c94fbe92475fd6977583fcb064ae0bc590986812b38d0c
     F test/csv01.test c9c3af0d58c34e9ac970c5875a77939edb958762c8aafb95409e19a3f088b6cd
     F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3
    -F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
    +F test/cursorhint.test 0175e4404181ace3ceca8b114eb0a98eae600d565aa4e2705abbe6614c7fe201
     F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee9ae9c42f
     F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8
     F test/date.test 9b73bbeb1b82d9c1f44dec5cf563bf7da58d2373
     F test/date2.test 5ef8265c71460cda6b1698bf18f4bb0ffb40ac08c5092f6afe84d398c2feb5be
     F test/dbdata.test 042f49acff3438f940eeba5868d3af080ae64ddf26ae78f80c92bec3ca7d8603
     F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e
    -F test/dbfuzz001.test 42aad1dcef6219fbee86a9b7d08832c9bbb2e41508f6f128ae91745927276292
    +F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b321e30
     F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee
    -F test/dbfuzz2.c c2c9cb40082a77b7e95ffb8b2da1e93322efadfb1c8c1e0001c95a0af1e156c2
    +F test/dbfuzz2.c 40cc4600947f30600f0ab365a2714ec76a899c9adb2c0ccd63ba583b2f71390e
     F test/dbpage.test 650234ba683b9d82b899c6c51439819787e7609f17a0cc40e0080a7b6443bc38
     F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759
     F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
    -F test/default.test 3e46c421eebefd2787c2f96673efabf792d360f3a1d5073918cbe450ce672a62
    +F test/decimal.test fcf403fd5585f47342234e153c4a4338cd737b8e0884ac66fc484df47dbcf1a7
    +F test/default.test 9687cfb16717e4b8238c191697c98be88c0b16e568dd5368cd9284154097ef50
     F test/delete.test 31832b0c45ecb51a54348c68db173be462985901e6ed7f403d6d7a8f70ab4ef0
     F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
     F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
    @@ -818,7 +827,7 @@ F test/descidx1.test edc8adee58d491b06c7157c50364eaf1c3605c9c19f8093cb1ea2b6184f
     F test/descidx2.test a0ba347037ff3b811f4c6ceca5fd0f9d5d72e74e59f2d9de346a9d2f6ad78298
     F test/descidx3.test 953c831df7ea219c73826dfbf2f6ee02d95040725aa88ccb4fa43d1a1999b926
     F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
    -F test/distinct.test 8b6c652f0b2d477f0830884736f2a1cd2e8f7fc10a04aa6d571a401fa13ed88b
    +F test/distinct.test e7d0cf371944dd0cbedff86420744e2f1ea2b528156451c97eb6ff41a99b9236
     F test/distinct2.test 11b0594c932098e969d084ba45ab81d5040f4d4e766db65d49146705a305ed98
     F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
     F test/e_blobbytes.test 439a945953b35cb6948a552edaec4dc31fd70a05
    @@ -826,12 +835,12 @@ F test/e_blobclose.test 4b3c8c60c2171164d472059c73e9f3c1844bb66d
     F test/e_blobopen.test e95e1d40f995056f6f322cd5e1a1b83a27e1a145
     F test/e_blobwrite.test f87ff598b67af5b3ec002a8d83e804dc8d23808e88cf0080c176612fc9ffce14
     F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
    -F test/e_createtable.test 1c602347e73ab80b11b9fa083f47155861aaafcff8054aac9e0b76d0df33b0a7
    +F test/e_createtable.test ea27082d6f84df61e1d9e383f3fd79220418856a4a8afc41af75d458b8e7ac33
     F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
    -F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
    -F test/e_dropview.test 21ce09c361227ddbc9819a5608ee2700c276bdd5
    -F test/e_expr.test 328d2d7c84f8e53e942a13eac771b337bcdfcf4c3569324001868b5639f3c857
    -F test/e_fkey.test 2febb2084aef9b0186782421c07bc9d377abf067c9cb4efd49d9647ae31f5afe
    +F test/e_droptrigger.test 235c610f8bf8ec44513e222b9085c7e49fad65ad0c1975ac2577109dd06fd8fa
    +F test/e_dropview.test 74e405df7fa0f762e0c9445b166fe03955856532e2bb234c372f7c51228d75e7
    +F test/e_expr.test 62000e6675d5bcf4b09276fe011a27779629ff8f6678ba5937fb6f1b78d645ff
    +F test/e_fkey.test 0b458b85f192cdb9e9933d5891848bb19bcc44d3f49faf111a375f2844a164d3
     F test/e_fts3.test 17ba7c373aba4d4f5696ba147ee23fd1a1ef70782af050e03e262ca187c5ee07
     F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e
     F test/e_reindex.test 2b0e29344497d9a8a999453a003cb476b6b1d2eef2d6c120f83c2d3a429f3164
    @@ -847,7 +856,7 @@ F test/e_walauto.test 248af31e73c98df23476a22bdb815524c9dc3ba8
     F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0
     F test/e_walhook.test 01b494287ba9e60b70f6ebf3c6c62e0ffe01788e344a4846b08e5de0b344cb66
     F test/emptytable.test a38110becbdfa6325cd65cb588dca658cd885f62
    -F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
    +F test/enc.test 9a7be5479da985381d740b15f432800f65e2c87029ee57a318f42cb2eb43763a
     F test/enc2.test 848bf05f15b011719f478dddb7b5e9aea35e39e457493cba4c4eef75d849a5ec
     F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6
     F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
    @@ -865,20 +874,20 @@ F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
     F test/fallocate.test 37a62e396a68eeede8f8d2ecf23573a80faceb630788d314d0a073d862616717
     F test/filectrl.test 6e871c2d35dead1d9a88e176e8d2ca094fec6bb3
     F test/filefmt.test f393e80c4b8d493b7a7f8f3809a8425bbf4292af1f5140f01cb1427798a2bbd4
    -F test/filter1.test 8a6f047a000ef391db2ca17b6beecc0006f4e0f9ca8bbe272b2443c7316e66b1
    +F test/filter1.test 6c483ecf7886c8843a8612c021aa23f33c581f584151f251842b3a3592c95ac8
     F test/filter2.tcl 44e525497ce07382915f01bd29ffd0fa49dab3adb87253b5e5103ba8f93393e8
     F test/filter2.test 485cf95d1f6d6ceee5632201ca52a71868599836f430cdee42e5f7f14666e30a
     F test/filterfault.test c08fb491d698e8df6c122c98f7db1c65ffcfcad2c1ab0e07fa8a5be1b34eaa8b
     F test/fkey1.test d11dbb8a93ead9b5c46ae5d02da016d61245d47662fb2d844c99214f6163f768
    -F test/fkey2.test 65c86b11127c11f80c0f450b3480321e0f087edea3031b9daa1978e3c020c91b
    +F test/fkey2.test b1b6a8c5556dc0ccf31291b1fed8aa57e404b38f3236110e19ab4dc6aa93edf2
     F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
     F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
    -F test/fkey5.test 24dd28eb3d9f1b5a174f47e9899ace5facb08373a4223593c8c631e6cf9f7d5a
    +F test/fkey5.test 6727452e163a427147e84e739da18713da553d79f9783559b04fdcd36d5c7421
     F test/fkey6.test d078a1e323a740062bed38df32b8a736fd320dc0
     F test/fkey7.test 64fb28da03da5dfe3cdef5967aa7e832c2507bf7fb8f0780cacbca1f2338d031
     F test/fkey8.test 48ef829d63f5f7b37aabd4df9363ac05f65539d1da8c4a44251631769d920579
     F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
    -F test/fordelete.test eb93a2f34137bb87bdab88fcab06c0bd92719aff
    +F test/fordelete.test ba98f14446b310f9c9d935b97ec748753d0144a28b356ba30d1f4f6958fdde5c
     F test/format4.test eeae341953db8b6bda7f549044797c3278a6cc345d11ada81471671b654f8ef4
     F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
     F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7
    @@ -940,11 +949,12 @@ F test/fts3b.test c15c4a9d04e210d0be67e54ce6a87b927168fbf9c1e3faec8c1a732c366fd4
     F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
     F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
     F test/fts3conf.test c84bbaec81281c1788aa545ac6e78a6bd6cde2bdbbce2da261690e3659f5a76b
    -F test/fts3corrupt.test ce7f7b5eaeee5f1804584d061b978d85e64abf2af9adaa7577589fac6f7eae01
    -F test/fts3corrupt2.test bf55c3fa0b0dc8ea1c0fe5543623bd27714585da6a129038fd6999fe3b0d25f3
    +F test/fts3corrupt.test 79a32ffdcd5254e2f7fa121d9656e61949ad049c3c6554229911b7ceac37c9c6
    +F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
     F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cdb764b491f
    -F test/fts3corrupt4.test e407c7b4f4cd3335080833aff3a8855d520e531b79f84dcc77be4623af2342d4
    +F test/fts3corrupt4.test e4662d37f02248301d8b58778eac380663e09a17a38dd5d6bb5ea4c927b9a575
     F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
    +F test/fts3corrupt6.test b6c55218b704b0cef224b284c756f9c55d0afd0b3c3837618bffeaa8c31e0d8e
     F test/fts3cov.test 7eacdbefd756cfa4dc2241974e3db2834e9b372ca215880e00032222f32194cf
     F test/fts3d.test 2bd8c97bcb9975f2334147173b4872505b6a41359a4f9068960a36afe07a679f
     F test/fts3defer.test f4c20e4c7153d20a98ee49ee5f3faef624fefc9a067f8d8d629db380c4d9f1de
    @@ -964,7 +974,8 @@ F test/fts3fuzz001.test e3c7b0ce9b04cc02281dcc96812a277f02df03cd7dc082055d87e11e
     F test/fts3join.test 949b4f5ae3ae9cc2423cb865d711e32476bdb205ab2be923fdf48246e4a44166
     F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6
     F test/fts3matchinfo.test aa66cc50615578b30f6df9984819ae5b702511cf8a94251ec7c594096a703a4a
    -F test/fts3misc.test c47d2c1ea1351c51c32c688545b02c8180a3f22156d1aedc206a8c09b9d95905
    +F test/fts3matchinfo2.test 00144e841704b8debfcdf6097969cd9f2a1cf759e2203cda42583648f2e6bf58
    +F test/fts3misc.test 9ec15e7c0b5831a6353bd4c46bf3acdf1360eda5d9f396f667db4d05bcf92ecf
     F test/fts3near.test 7e3354d46f155a822b59c0e957fd2a70c1d7e905
     F test/fts3offsets.test b85fd382abdc78ebce721d8117bd552dfb75094c
     F test/fts3prefix.test fa794eaab0bdae466494947b0b153d7844478ab2
    @@ -973,12 +984,13 @@ F test/fts3query.test ca033ff2ebcc22c69d89032fb0bc1850997d31e7e60ecd26440796ba16
     F test/fts3rank.test cd99bc83a3c923c8d52afd90d86979cf05fc41849f892faeac3988055ef37b99
     F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
     F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e
    -F test/fts3snippet.test d9b9f4b717584040fb56df1dacab53acd474958e9c1d00b073d10726695cea0c
    +F test/fts3snippet.test 0887196d67cffbe365edde535b95ecc642a532ce8551ccd9a73aab5999c3ffae
    +F test/fts3snippet2.test 2dabb5889eda4c9980aad325e688b470781f97ce7c0fca0db125616fae0a2cdd
     F test/fts3sort.test ed34c716a11cc2009a35210e84ad5f9c102362ca
     F test/fts3tok1.test a663f4cac22a9505400bc22aacb818d7055240409c28729669ea7d4cc2120d15
     F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d
     F test/fts3varint.test 0b84a3fd4eba8a39f3687523804d18f3b322e6d4539a55bf342079c3614f2ada
    -F test/fts4aa.test 4338ea7a67f7e19269bf6e6fb4a291352aa32296e7daed87f9823d57016a1ef7
    +F test/fts4aa.test 0e6bfd6a81695a39b23e448dda25d864e63dda75bde6949c45ddc95426c6c3f5
     F test/fts4check.test 6259f856604445d7b684c9b306b2efb6346834c3f50e8fc4a59a2ca6d5319ad0
     F test/fts4content.test 73bbb123420d2c46ef2fb3b24761e9acdb78b0877179d3a5d7d57aada08066f6
     F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01
    @@ -992,6 +1004,7 @@ F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
     F test/fts4merge3.test 8d9ccb4a3d41c4c617a149d6c4b13ad02de797d0
     F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b
     F test/fts4merge5.test 69932d85cda8a1c4dcfb742865900ed8fbda51724b8cf9a45bbe226dfd06c596
    +F test/fts4min.test 1c11e4bde16674a0c795953509cbc3731a7d9cbd1ddc7f35467bf39d632d749f
     F test/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309
     F test/fts4onepass.test d69ddc4ee3415e40b0c5d1d0408488a87614d4f63ba9c44f3e52db541d6b7cc7
     F test/fts4opt.test 0fd0cc84000743ff2a883b9b84b4a5be07249f0ba790c8848a757164cdd46b2a
    @@ -999,12 +1012,13 @@ F test/fts4record.test a48508f69a84c9287c8019d3a1ae712f5730d8335ffaf8e2101e691d0
     F test/fts4rename.test 15fd9985c2bce6dea20da2245b22029ec89bd4710ed317c4c53abbe3cfd0c880
     F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f757380429
     F test/fts4unicode.test ceca76422abc251818cb25dabe33d3c3970da5f7c90e1540f190824e6b3a7c95
    +F test/fts4upfrom.test 8df5acb6e10ad73f393d1add082b042ab1db72567888847d098152121e507b34
     F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
    -F test/func.test b7f1a706d1bb8de103a24bd0c30c9e3dc3eedf0df24aabc54b0a4f6e08742622
    +F test/func.test f673822636fb8ed618dd2b80230d16e495d19c8f2e2e7d6c22e93e2b3de097ad
     F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
     F test/func3.test 2bb0f31ab7baaed690b962a88544d7be6b34fa389364bc36a44e441ed3e3f1e6
    -F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f
    -F test/func5.test cdd224400bc3e48d891827cc913a57051a426fa4
    +F test/func4.test a94858a8c1f10a408b0b3db439c990b59dbb2349411d503de011ac4e2b5f27a6
    +F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a
     F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c
     F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa
     F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
    @@ -1013,35 +1027,35 @@ F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c
     F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
     F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
     F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
    -F test/fuzzcheck.c a9746aa49843827f960bc875cc70e04b0cfcd3e10e6676e3abc402ad190e165f
    +F test/fuzzcheck.c 656ee850f331872a784e7d6a10649efe2af123bdaacb728b5a03e4faee8b959c
     F test/fuzzdata1.db d36e88741b4f23bcbaaf55b006290669d03c6c891cf13c7b3a53bc1b097b693f
     F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
     F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
     F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e42ed2
     F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5
     F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7
    -F test/fuzzdata7.db e7a86fd83dda151d160445d542e32e5c6019c541b3a74c2a525b6ac640639711
    -F test/fuzzdata8.db 8bd41f8e1b9c61af011bf5cf16a85fb7f718fdd3348e476b78ba36adab872653
    +F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2
    +F test/fuzzdata8.db 99f99201280962430f3287e879e050ba88fe458d05cbf28e37ecab369ffe2e86
     F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
     F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
     F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
     F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c
    -F test/gencol1.test e89eafdf03245e2609ddf6e9b0add37a17ce229095836c409131764c3a5282a5
    +F test/gencol1.test b05e6c5edb9b10d48efb634ed07342441bddc89d225043e17095c36e567521a0
     F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
     F test/having.test e4098a4b8962f9596035c3b87a8928a10648acc509f1bb8d6f96413bbf79a1b3
     F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
     F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
    -F test/hook.test 1604b3b2f5931430087540404555c1b6be3618600b81558657c66b533ed70b13
    +F test/hook.test e97382e68e4379838e888756d653afd159f5f14780315ff97b70360d3d8485bc
     F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
    -F test/icu.test 41aa8847745a879b897a7febea0f8f9efc8e67fe8bf680589b6e07c7b0a1569a
    -F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
    +F test/icu.test 716a6b89fbabe5cc63e0cd4c260befb08fd7b9d761f04d43669233292f0753b1
    +F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
     F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
    -F test/in.test 3e9bd58597a444123a40a9ac94cae0fec8897e17e9f519b02fc370bcf5ba5175
    +F test/in.test ae4ba0fe3232fdd84ef1090a68c5cd6ccd93f1f8774d5c967dd0c1b301492eed
     F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
     F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
     F test/in4.test 65460600d48933adba4283c6ebd089aae173d16136ab9d01f74c89089090c5a5
     F test/in5.test b32ce7f4a93f44c5dee94af16886d922cc16ebe33c8e1765c73d4049d0f4b40f
    -F test/in6.test 62d943a02f722948f4410ee0b53c3cb39acd7c41afb083df8d7004238fe90a20
    +F test/in6.test 8562d0945195cab3cc4ab3794e9118e72cb44c43f785c2b04d48a9d06ca6b4ec
     F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
     F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f
     F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
    @@ -1059,10 +1073,10 @@ F test/index3.test 51685f39345462b84fcf77eb8537af847fdf438cc96b05c45d6aaca4e473a
     F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
     F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
     F test/index6.test f172653b35b20233e59200e8b92a76db61bf7285437bf777b93b306ba26a47e7
    -F test/index7.test 1d764c0cca45f5a76150b08e127ccc8d52492cfa788b5fafed4be784a351b020
    +F test/index7.test b8a0ba2110fd517bb48c4e76d26d60f1ab2ed9e257b18d71f820d7e71e9f8570
     F test/index8.test bc2e3db70e8e62459aaa1bd7e4a9b39664f8f9d7
     F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
    -F test/indexedby.test a52c8c6abfae4fbfb51d99440de4ca1840dbacc606b05e29328a2a8ba7cd914e
    +F test/indexedby.test f54aac21c06948872010a956fd02de5178c362c7785a9887cf0b8616be17883b
     F test/indexexpr1.test 284e119999d132cc8bf37735a928c9859b28e8e295d02b7a6a4f93977c7f9ba5
     F test/indexexpr2.test dba11dbb0a58fcba4cd694f46b4004976123b81b0501f525d43c9be59f0207b1
     F test/indexfault.test 98d78a8ff1f5335628b62f886a1cb7c7dac1ef6d48fa39c51ec871c87dce9811
    @@ -1087,9 +1101,9 @@ F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
     F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
     F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
     F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
    -F test/istrue.test 75327829744e65cc8700e69340b8e6c192e10e39dfae7ccb0e970d3c4f49090a
    +F test/istrue.test 06f92ea38750fa74df7dbbe6920205251c2310861fbbe23a3adfa918a2e2ba74
     F test/join.test bca044589e94bb466e4c1e91fb6fecdc3f3326ca6b3f590f555f1958156eb321
    -F test/join2.test 659bc6193f5c3fe20fa444dd2c91713db8c33e376b098b860644e175e87b8dbc
    +F test/join2.test 7d24d095ab88d3910228d53a3b548b7baf2e0e7d8aac6731a273e300e1b34b61
     F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
     F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
     F test/join5.test 3a96dc62f0b45402d7207e22d1993fe0c2fce1c57644a11439891dd62b990eb7
    @@ -1106,13 +1120,13 @@ F test/json103.test aff6b7a4c17d5a20b487a7bc1a274bfdc63b829413bdfb83bedac42ec7f6
     F test/json104.test 317f4ec4b2d87afbba4d2460cf5be297aea76f2285eb618d276dbcd40a50950f
     F test/json105.test 45f7d6a9a54c85f8a9589b68d3e7a1f42d02f2359911a8cdbad1f9988f571173
     F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
    -F test/kvtest.c 94da54bb66aae7a54e47cf7e4ea4acecc0f217560f79ad3abfcc0361d6d557ba
    +F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b
     F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
     F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
     F test/lemon-test01.y 58b764610fd934e189ffbb0bbfa33d171b9cb06019b55bdc04d090d6767e11d7
    -F test/like.test 3d702d79bf871fa32985b1ce334294c587e3948d3ab972001e811a58577e8b3c
    +F test/like.test 47b81d5de2ff19d996d49a65d50ec9754246aacbe0e950b48d186d9d8171eaf0
     F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
    -F test/like3.test 4f940ad275c006319950054a7a65661f476772171b82b6fdf795e4dda36f246f
    +F test/like3.test 03d1bdf848483b78d2cfd1db283d75c4ec2e37c8b8eccc006813f3978d78fbbd
     F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
     F test/limit2.test 9409b033284642a859fafc95f29a5a6a557bd57c1f0d7c3f554bd64ed69df77e
     F test/loadext.test faa4f6eed07a5aac35d57fdd7bc07f8fc82464cfd327567c10cf0ba3c86cde04
    @@ -1169,8 +1183,8 @@ F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
     F test/misc4.test 10cd6addb2fa9093df4751a1b92b50440175dd5468a6ec84d0386e78f087db0e
     F test/misc5.test c4aeaa0fa28faa08f2485309c38db4719e6cd1364215d5687a5b96d340a3fa58
     F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
    -F test/misc7.test 4f21954012e4eb0a923c54a311f38c81bf6798ccdd7b51584db46d4007f63daa
    -F test/misc8.test 8fb0f31d7a8aed484d759773ab8ad12ec746a477f4a67394a4af0e677494c3ca
    +F test/misc7.test 7b4c88c1d5ea8c8b9d537d212c08a0343d345fdd5c789598692c1c0e60fbda69
    +F test/misc8.test 8782708f4c8a459591c3e8fe1215bd2048bffb4024b3df249e9b9ed407dc61ed
     F test/misuse.test 9e7f78402005e833af71dcab32d048003869eca5abcaccc985d4f8dc1d86bcc7
     F test/mjournal.test 28a08d5cb5fb5b5702a46e19176e45e964e0800d1f894677169e79f34030e152
     F test/mmap1.test fb04e0c10492455007624ade884ca0c8852ff3e4e11d95408f9709ca2ef7f626
    @@ -1183,7 +1197,7 @@ F test/multiplex.test dc0d67b66f84b484a83cb8bbdf3f0a7f49562ccd
     F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
     F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
     F test/multiplex4.test e8ae4c4bd70606a5727743241f13b5701990abe4
    -F test/mutex1.test ea2cc74d97f077b9e74c84cbd024f14d79a8126f
    +F test/mutex1.test 177db2e4edb530f2ff21edc52ac79a412dbe63e4c47c3ae9504d3fb4f1ce81fa
     F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
     F test/nan.test 437d40e6d0778b050d7750726c0cbd2c9936b81962926e8f8c48ca698f00f4d1
     F test/nockpt.test 8c43b25af63b0bd620cf1b003529e37b6f1dc53bd22690e96a1bd73f78dde53a
    @@ -1194,19 +1208,19 @@ F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
     F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
     F test/notnull.test a37b663d5bb728d66fc182016613fb8e4a0a4bbf3d75b8876a7527f7d4ed3f18
     F test/null.test 0dcce4f04284ec66108c503327ad6d224c0752b3
    -F test/nulls1.test fe4153fd59a3786b4b9f3663a3e65a3aa43a318c7b4d7dcb3f6c3ed5652c9095
    +F test/nulls1.test 82c5bc33148405f21205865abf13c786084438d573a4ac4e87e11b6091cde526
     F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
     F test/numindex1.test 20a5450d4b056e48cd5db30e659f13347a099823
     F test/offset1.test f06b83657bcf26f9ce805e67450e189e282143b2
     F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
    -F test/optfuzz-db01.c a0c256905c8ac79f9a5de2f374a3d9f757bef0dca2a238dc7c10cc8a38031834
    +F test/optfuzz-db01.c 9f2fa80b8f84ebbf1f2e8b13421a4e0477fe300f6686fbd76cac1d2db66e0fdc
     F test/optfuzz-db01.txt 21f6bdeadc701cf11528276e2a55c70bfcb846ba42df327f979bd9e7b6ce7041
     F test/optfuzz.c 50e330304eb1992e15ddd11f3daaad9bcc0d9aaad09cb2bcc77f9515df2e88b1
     F test/orderby1.test 6bf0ce45cbfb1cf4779dd418ac5e8cf66abfa04de2c1d2edf1e0e85f1520d8f3
     F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
     F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
     F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
    -F test/orderby5.test 5f4d6cb93cc2f6d3f4228354310a2ce1fbd95d5bbffcba8c6482eeb62a466407
    +F test/orderby5.test bd7d9e3380e87e5dcf6ea817ebaab6d15da213c7804b38767e1b3e695e85650b
     F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
     F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
     F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
    @@ -1216,7 +1230,7 @@ F test/oserror.test 1fc9746b83d778e70d115049747ba19c7fba154afce7cc165b09feb6ca6a
     F test/ossfuzz.c 9636dad2092a05a32110df0ca06713038dd0c43dd89a77dabe4b8b0d71096715
     F test/ossshell.c f125c5bd16e537a2549aa579b328dd1c59905e7ab1338dfc210e755bb7b69f17
     F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f
    -F test/pager1.test 1e9ee778bdeaf4f7f09997d029cdaca6a42dfc2092edafe4f5e590acbf1eab13
    +F test/pager1.test 4fba160bf450cea19f6bf1d6483ef467545bac6405570e176c83c2c4b5d6d0d5
     F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
     F test/pager3.test 4e9a83d6ca0838d7c602c9eb93d1357562d9059c1e02ffb138a8271020838370
     F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e
    @@ -1229,16 +1243,16 @@ F test/parser1.test 6ccdf5e459a5dc4673d3273dc311a7e9742ca952dd0551a6a6320d27035c
     F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
     F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
     F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
    -F test/permutations.test 8587800fe1a0eb01456a3f4500b821e54e3347e78acf11dbf05f4990530f6cee
    -F test/pg_common.tcl 222a1bad1c41c308fa366313cd7b51b3be7e9b21c8736a421b974ac941693b54
    -F test/pragma.test 59becdfd720b80d463ab750f69f7118fde10dfd556aa5d554f3bf6b7e5ea7533
    +F test/permutations.test 661a4325a5717957a77836910ee164ba26594a502d7a3df0e1ae7b9cba829c5d
    +F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
    +F test/pragma.test 50b91bedea9324d3ab48e793f908ee7d2c7dcf84bfa2281e792838be59641ec8
     F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
     F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31
    -F test/pragma4.test 10c624e45a83c0096a82a7579a5ff658542391d3b77355192da6572c8c17c00b
    +F test/pragma4.test ca5e4dfc46adfe490f75d73734f70349d95a199e6510973899e502eef2c8b1f8
     F test/pragma5.test 7b33fc43e2e41abf17f35fb73f71b49671a380ea92a6c94b6ce530a25f8d9102
     F test/pragmafault.test 275edaf3161771d37de60e5c2b412627ac94cef11739236bec12ed1258b240f8
     F test/prefixes.test b524a1c44bffec225b9aec98bd728480352aa8532ac4c15771fb85e8beef65d9
    -F test/printf.test 0300699733e53101b2ce48800518427249edd4053bb50fa0621c6607482f0fdb
    +F test/printf.test 390d0d7fcffc3c4ea3c1bb4cbb267444e32b33b048ae21895f23a291844fe1da
     F test/printf2.test 30b5dd0b4b992dc5626496846ecce17ff592cacbcb11c3e589f3ac4d7e129dae
     F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
     F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
    @@ -1300,14 +1314,14 @@ F test/securedel.test 2f70b2449186a1921bd01ec9da407fbfa98c3a7a5521854c300c194b2f
     F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
     F test/select1.test 009a6d8eacd9684d046302b8d13b50846a87e39d6f08e92178aa13e95ea29a2d
     F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
    -F test/select3.test 3905450067c28766bc83ee397f6d87342de868baa60f2bcfd00f286dfbd62cb9
    -F test/select4.test 5389d9895968d1196c457d59b3ee6515d771d328
    +F test/select3.test c49fbb758903f3718e2de5aa4655eda4838131cbea24a86db908f8b6889aa68c
    +F test/select4.test e8a2502e3623f3058871030599a48abb35789d2244d5b380ecf3696873fdd4a4
     F test/select5.test df9ec0d218cedceb4fe7b63262025b547b50a55e59148c6f40b60ca25f1d4546
     F test/select6.test 319d45e414cdd321bf17cfacedaf19e3935ad64dac357c53f1492338c6e9b801
     F test/select7.test f659f231489349e8c5734e610803d7654207318f
     F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
     F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
    -F test/selectA.test b8a590f6493cad5b0bb4dfe1709bf7dcda0b6c40bb4caf32d1e36a89eebc8fc5
    +F test/selectA.test 68de52409e45a3313d00b8461b48bef4fb729faf36ade9067a994eae55cc86f4
     F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
     F test/selectC.test e25243f8ca503e06f252eb0218976d07cfeceac3
     F test/selectD.test fc20452847a01775710090383cfb4423275d2f745fed61f34fbf37573ac0d214
    @@ -1317,7 +1331,7 @@ F test/selectG.test 089f7d3d7e6db91566f00b036cb353107a2cca6220eb1cb264085a836dae
     F test/server1.test c2b00864514a68a0e6fd518659dc95d0050307a357a08969872bef027d785dc4
     F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be
     F test/sessionfuzz-data1.db 1f8d5def831f19b1c74571037f0d53a588ea49a6c4ca2a028fc0c27ef896dbcb
    -F test/sessionfuzz.c be9c4d4afd359ce80024d8b541b9b8a880510aef5cf263df56fc0e9b947727f1
    +F test/sessionfuzz.c f74c4e806bab5a093fb9c11b6123d17a6e0cf73fb7a0f49b12f5a75bf0b7b1a8
     F test/shared.test 1826673eb5eb745fb91a3bdac99a7737057742ae38dcb0fe076a384d6727578b
     F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
     F test/shared3.test ab693f9b6e156b8bfb2a0ad94f29fe69602a5d38
    @@ -1330,11 +1344,11 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
     F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
     F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
     F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
    -F test/shell1.test 3c9707dce15e8fdca529503378660f099777d3ddcedccf801a37589a405c5942
    +F test/shell1.test 1305efc92f132a1fcf97c00ac02b1b192865b4d33d44cab499e0bdcff71ce045
     F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
     F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
     F test/shell4.test 1c6aef11daaa2d6830acaba3ac9cbec93fbc1c3d5530743a637f39b3987d08ce
    -F test/shell5.test 23939a4c51f0421330ea61dbd3c74f9c215f5f8d3d1a94846da6ffc777a35458
    +F test/shell5.test 84a30b55722a95a5b72989e691c469a999ca7591e7aa00b7fabc783ea5c9a6fe
     F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
     F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
     F test/shell8.test 96be02ea0c21f05b24c1883d7b711a1fa8525a68ab7b636aacf6057876941013
    @@ -1371,7 +1385,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
     F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
     F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
     F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
    -F test/speedtest1.c f3bfe3c6a87cbd88e4c4e38005d972bcc1019d1b2fe9569425f86629b11f6c31
    +F test/speedtest1.c ac0e6ebadb97b54b7ac45288d1beea633a219d5fb98aa3395cb8364c31c985cb
     F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
     F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
     F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
    @@ -1379,7 +1393,7 @@ F test/spellfix4.test 51c7c26514ade169855c66bcf130bd5acfb4d7fd090cc624645ab275ae
     F test/sqldiff1.test 28cd737cf1b0078b1ec1bbf425e674c47785835e
     F test/sqllimits1.test 264f4b0f941800ba139d25e33ee919c5d95fea06dfbe8ac291d6811a30984ca5
     F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a
    -F test/stat.test 423257dc36e5865fb9dd1d9051ac985763b6fba1daec134932f37772d5ed1e64
    +F test/stat.test 15a3106eddedfc882f64bc09f237b4169be4b92dd57c93031b8ff8b13af3e7c5
     F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
     F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75
     F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5
    @@ -1412,7 +1426,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
     F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
     F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
     F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
    -F test/tester.tcl abba168acd7f01dbfa3ffdbf402d151eb97e8a824d9208e845ab34c194441483
    +F test/tester.tcl 6417cbb60c4169804e2e1b36ce1a840c9f33d0b0d97956e058f3cc49ed3904f0
     F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
     F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
     F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
    @@ -1594,11 +1608,12 @@ F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
     F test/triggerE.test ede2e4bce4ba802337bd69d39447fa04a938e06d84a8bfc53c76850fc36ed86d
     F test/triggerF.test 5d76f0a8c428ff87a4d5ed52da06f6096a2c787a1e21b846111dfac4123de3ad
     F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505233e9
    +F test/triggerupfrom.test d25961fa70a99b6736193da7b49a36d8c1d28d56188f0be6406d4366315cd6e4
     F test/trustschema1.test 4e970aef0bfe0cee139703cc7209d0e0f07725d999b180ba50770f49edef1494
     F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1
     F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a
     F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
    -F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f
    +F test/tt3_stress.c f9a769ca8b026ecc76ee93ca8c9700a5619f8e51c581107c4053ba6ac97f616f
     F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
     F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
     F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
    @@ -1611,6 +1626,11 @@ F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
     F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
     F test/update.test e906ca7cb1dc6f52af1ea243e08f727edfa79f924c2691f2f9e72481f847310d
     F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
    +F test/upfrom1.tcl 8859d9d437f03b44174c4524a7a734a391fd4526fcff65be08285dafc9dc9041
    +F test/upfrom1.test d18f69f7c691bc791e7f31bf0e354eeff04cf2f44edc32d6b1928bad71697073
    +F test/upfrom2.test 6ebd3be8c3fac984e89a177d823686f04605b512fc167392bce6d8ba2ba63325
    +F test/upfrom3.test 7dab379d128e8dd7beb2055b295fb113c7ba93e8c2038f5ddb7a4a10f0ebb348
    +F test/upfromfault.test 70ecf8eb85559727a487283f69374e3ae39879e994d8a2437c49d7c05ecb70c9
     F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
     F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
     F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
    @@ -1652,8 +1672,8 @@ F test/vtab_alter.test 736e66fb5ec7b4fee58229aa3ada2f27ec58bc58c00edae4836890c37
     F test/vtab_err.test dcc8b7b9cb67522b3fe7a272c73856829dae4ab7fdb30399aea1b6981bda2b65
     F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
     F test/vtabdrop.test 65d4cf6722972e5499bdaf0c0d70ee3b8133944a4e4bc31862563f32a7edca12
    -F test/wal.test cdf0ca6cc0447520d19ef1c83287824ebeb3e82d75af856511ba96841a79fc9b
    -F test/wal2.test 537f59e5c5932e3b45bf3591ae3e48a2601360c2e52821b633e222fe6ebd5b09
    +F test/wal.test 16180bc4becda176428ad02eaea437b4b8f5ae099314de443a4e12b2dcc007a2
    +F test/wal2.test 31f6e2c404b9f2cdf9ca19b105a1742fdc19653c2c936da39e3658c617524046
     F test/wal3.test 2a93004bc0fb2b5c29888964024695bade278ab2
     F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
     F test/wal5.test 9c11da7aeccd83a46d79a556ad11a18d3cb15aa9
    @@ -1678,18 +1698,19 @@ F test/walmode.test cd6e7cff618eaaa5910ce57c3657aa50110397f86213886a2400afb9bfec
     F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
     F test/waloverwrite.test dad2f26567f1b45174e54fbf9a8dc1cb876a7f03
     F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
    -F test/walprotocol.test a112aba0b79e3adeaa485fed09484b32c654e97df58e454aa8489ac2cd57bf84
    +F test/walprotocol.test 1b3f922125e341703f6e946d77fdc564d38fb3e07a9385cfdc6c99cac1ecf878
     F test/walprotocol2.test 7d3b6b4bf0b12f8007121b1e6ef714bc99101fb3b48e46371df1db868eebc131
     F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
     F test/walro2.test 0e79dd15cbdb4f482c01ea248373669c732414a726b357d04846a816afafb768
     F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68
    +F test/walsetlk.test 11f7fe792fdce54cf09874dab824e0627f2eedecfb9f7983e325606ec5184e0c
     F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
     F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
     F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
    -F test/walvfs.test ca81c9f427e0e5434076dfa948fd1d8e6d5ddd192b2fb6991635d81da5f3f5d4
    +F test/walvfs.test a2913001a83b19c1d20220e556cee14d87d47ecb6949b5e0a2e9e2590abecf1e
     F test/wapp.tcl b440cd8cf57953d3a49e7ee81e6a18f18efdaf113b69f7d8482b0710a64566ec
    -F test/wapptest.tcl 3cca775aede0591756a1fc0da55bbb3715d8c363873fd2cfdd4d555b0a4af57d x
    -F test/where.test 19c709c9f0f6ed12c23f909f6592aa55fba34269d5a2898537aa27a22b9ce650
    +F test/wapptest.tcl 899594e25684861d5b0c0880fb012364def50ef8097041b8ddf74be5ba7fa270 x
    +F test/where.test f5e62453537e5b335b69f3b09f8a02ce3328289fad5d866e25371284b837d78d
     F test/where2.test 478d2170637b9211f593120648858593bf2445a1
     F test/where3.test 2341a294e17193a6b1699ea7f192124a5286ca6acfcc3f4b06d16c931fbcda2c
     F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8
    @@ -1697,46 +1718,46 @@ F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
     F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
     F test/where7.test 75722434c486ac9e74718caa6cce234f45ba34c0b6c0f9555b29eb8bb5f6ade1
     F test/where8.test 461ca40265ed996a6305da99bb024b0e41602bb586acf544c08f95922358e49f
    -F test/where9.test 2c554b97bbdb2fdf26c57099f60db8a52bfcf7c147f2c256f9798fa0e267ca85
    +F test/where9.test 8e3e0ff42cc17156f52361a1c012281550d0d632912fec92d1d6df74db7a8e6d
     F test/whereA.test 6c6a420ca7d313242f9b1bd471dc80e4d0f8323700ba9c78df0bb843d4daa3b4
     F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
     F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
    -F test/whereD.test 711d4df58d6d4fb9b3f5ce040b818564198be002
    +F test/whereD.test c1c335e914e28b122e000e9310f02d2be83e1c9dbca2e29f46bd732703944d1b
     F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
     F test/whereF.test 3d9412b1199d3e2bed34fcb76b4c48d0bf4df95d27e3f8dd27b6f8b4716d0d89
    -F test/whereG.test c9378b285828754377ef47fbece7264018c0a3743e7eb686e89917bb9df10885
    +F test/whereG.test 9363b2a97d914cb1b81aff5069ef0cf2a071a67e2b604eac6fe9c0114017d9aa
     F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
     F test/whereI.test a2874062140ed4aba9ffae76e6190a3df6fc73d1373fdfa8fd632945082a5364
     F test/whereJ.test 88287550f6ee604422403b053455b1ad894eeaa5c35d348532dfa1439286cb9a
     F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
    -F test/whereL.test 976f100f412ce2f39bf923eb57794cdc39fc0a3fec8fab025d51706a7cc4845b
    +F test/whereL.test e05cedc9389c6f09ad55bd5999a3fddccebec90672fb989433c145dcdaf26996
     F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
     F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
     F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
    -F test/wherelimit2.test 9bf0aa56cca40ea0e4c5e2915341355a2bbc0859ec4ce1589197fe2a9d94635f
    +F test/wherelimit2.test 657a3f24aadee62d058c5091ea682dc4af4b95ffe32f137155be49799a58e721
     F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
     F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
    -F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
    +F test/win32longpath.test 4baffc3acb2e5188a5e3a895b2b543ed09e62f7c72d713c1feebf76222fe9976
     F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
    -F test/window1.test cec56b9a0a2e7ca4bd63b30590c7b049dce9acfd87478e2597e13b67152bd821
    +F test/window1.test e52b81fff0c3cb122a1240f336688eb81bea2967a99c4ddb78969adec7aadc2a
     F test/window2.tcl 492c125fa550cda1dd3555768a2303b3effbeceee215293adf8871efc25f1476
     F test/window2.test e466a88bd626d66edc3d352d7d7e1d5531e0079b549ba44efb029d1fbff9fd3c
     F test/window3.tcl acea6e86a4324a210fd608d06741010ca83ded9fde438341cb978c49928faf03
     F test/window3.test e9959a993c8a71e96433be8daaa1827d78b8921e4f12debd7bdbeb3c856ef3cb
    -F test/window4.tcl d732df0e81beedc0ba8a563ade68611d322d27303ad0c0c8e4444107c39e84ec
    -F test/window4.test 807f3e6b15f9338e5b9742b87c5c7ca825b42b9657fde6096e890119370848e0
    +F test/window4.tcl 6f85307eb67242b654d051f7da32a996a66aee039a09c5ae358541aa61720742
    +F test/window4.test fbead87f681400ac07ef3555e0488b544a47d35491f8bf09a7474b6f76ce9b4e
     F test/window5.test d328dd18221217c49c144181975eea17339eaeaf0e9aa558cee3afb84652821e
     F test/window6.test f8d674254b23289cc17c84d79dec7eda7caa1dfb7836c43122cfdf3640d1df32
     F test/window7.tcl 6a1210f05d40ec89c22960213a22cd3f98d4e2f2eb20646c83c8c30d4d76108f
     F test/window7.test 1d31276961ae7801edc72173edaf7593e3cbc79c06d1f1f09e20d8418af403cd
     F test/window8.tcl f2711aa3571e4e6b0dad98db8d95fd6cb8d9db0c92bbdf535f153b07606a1ce2
     F test/window8.test c4331b27a6f66d69fa8f8bab10cc731db1a81d293ae108a68f7c3487fa94e65b
    -F test/window9.test b63f6f74d730547e63e78946f951f5d1a7d4e99f91f6d5906305469043d92a15
    +F test/window9.test 4d8c875b73febdbac9b8f2b52ec132b98f48261cdafd6b08db62bc6d8ff913fc
     F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
     F test/windowB.test 7a983ea1cc1cf72be7f378e4b32f6cb2d73014c5cd8b25aaee825164cd4269e5
     F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
     F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
    -F test/windowfault.test 8e3b69abe0eea9595ba3940afd9c63644e11966ed8815734b67f1479a8e9891a
    +F test/windowfault.test 72375ae71031eabf96bc88d0af128c8628a091ddc99b5a394e848b3df5fc17ad
     F test/with1.test 584580a5ae79868a91873863f8cb2d00040006dc1e4c332ef1d8642f2815dc6e
     F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
     F test/with3.test 13b3336739da648a9e4dfa11bb04e73a920c97620041007c5f75d5d14084c346
    @@ -1744,7 +1765,7 @@ F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f1982
     F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
     F test/without_rowid1.test 9cfb83705c506e3849fa7efc88a3c9a15f9a50bf9b1516b41757a7cef9bba8c3
     F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
    -F test/without_rowid3.test 392e6e12f275d11d931a8bc4580e573342f391639c87ffb631010a7b3cedfdc0
    +F test/without_rowid3.test 96426a6c9a2a5cf62bbe55ea1ad038eaaf4bf743f40a1ad517233b8e5a3d4339
     F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
     F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a
     F test/without_rowid6.test 8463b20098e9f75a501a9f17dfb42fffc79068eac0b2775fe56ef2281d2df45e
    @@ -1763,43 +1784,44 @@ F tool/build-all-msvc.bat c12328d06c45fec8baada5949e3d5af54bf8c887 x
     F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
     F tool/cg_anno.tcl c1f875f5a4c9caca3d59937b16aff716f8b1883935f1b4c9ae23124705bc8099 x
     F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
    -F tool/dbhash.c 19560c9a2aa2b269b6a5108259b93d26d12f8f0877c31fe9f8f61dfbd219ba63
    +F tool/dbhash.c 5da0c61032d23d74f2ab84ffc5740f0e8abec94f2c45c0b4306be7eb3ae96df0
     F tool/dbtotxt.c b2221864a20fb391c46bd31bc1fbdc4a96f5c8a89bef58f421eb9b9c36b1702c
     F tool/dbtotxt.md c9a57af8739957ef36d2cfad5c4b1443ff3688ed33e4901ee200c8b651f43f3c
    +F tool/enlargedb.c 3e8b2612b985cfa7e3e8800031ee191b43ae80de96abb5abbd5eada62651ee21
     F tool/extract-sqlite3h.tcl 069ceab0cee26cba99952bfa08c0b23e35941c837acabe143f0c355d96c9e2eb x
     F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
    -F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
    +F tool/fast_vacuum.c c129ae2924a48310c7b766810391da9e8fda532b9f6bd3f9a9e3a799a1b42af9
     F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
     F tool/fuzzershell.c e1d90a03ca790d7c331c2aae08ca46ff435f1ae1faa6cb9cc48f4687c18fdc6e
     F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
     F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a5a4f
     F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
    -F tool/index_usage.c 9ec344d29cbeb03fdc0fce668eedfb7495792170de933adf95cf8d6904a166ad
    +F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
     F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
    -F tool/lemon.c a361b85fa230560b783006ac002a6a8bad214c3b9d7fa48980aecc2b691ddcad
    +F tool/lemon.c 600a58b9d1b8ec5419373982428e927ca208826edacb91ca42ab94514d006039
     F tool/lempar.c e8899b28488f060d0ff931539ea6311b16b22dce068c086c788a06d5e8d01ab7
     F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
     F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
     F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
     F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
    -F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
    +F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
     F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
     F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
    -F tool/mkkeywordhash.c 27ffc6f6e7e3ecbfc5bca1f1f11a09fc5badf6d67557a5fb2d3b069dbed90617
    +F tool/mkkeywordhash.c 11a3f3af8e787d0c5ca459ed66fe80fd09e661876506e7b978ec08c19477bdc2
     F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
     F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
     F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
     F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
    -F tool/mkpragmatab.tcl ca12b1c718ececdab2d3aacb437bc3c81ebf68467f19d7974e17f18844a3a48f
    +F tool/mkpragmatab.tcl ae5585ae76ca26e4d6ccd5ea9cdebaf5efefb318bf989497a0e846cd711d9ab1
     F tool/mkshellc.tcl 70a9978e363b0f3280ca9ce1c46d72563ff479c1930a12a7375e3881b7325712
     F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
     F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
     F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
    -F tool/mksqlite3c.tcl 5fed3d75069d8f66f202d3b5200b0cea4aa7108481acd06732a06fdd42eb83a2
    -F tool/mksqlite3h.tcl 080873e3856eceb9d289a08a00c4b30f875ea3feadcbece796bd509b1532792c
    +F tool/mksqlite3c.tcl f4ef476510eca4124c874a72029f1e01bc54a896b1724e8f9eef0d8bfae0e84c
    +F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf
     F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
     F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
    -F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
    +F tool/offsets.c 8ed2b344d33f06e71366a9b93ccedaa38c096cc1dbd4c3c26ad08c6115285845
     F tool/omittest.tcl 6616fbf384f0f630113eab27d41d4530435dd94e2883307759988b45f0604a3b
     F tool/opcodesum.tcl 740ed206ba8c5040018988129abbf3089a0ccf4a
     F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
    @@ -1807,22 +1829,22 @@ F tool/replace.tcl 60f91e8dd06ab81f74d213ecbd9c9945f32ac048
     F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
     F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
     F tool/run-speed-test.sh f95d19fd669b68c4c38b6b475242841d47c66076
    -F tool/showdb.c 9b2dbb4b7a00afaf8fc1719f0d775775effa5b135ac1a2c23f1c3f5d670c4e15
    +F tool/showdb.c 49e810f5c414c792b5bf38cd5557ca9639713ebfef32aaff32faf7cb7ccce513
     F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
    -F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
    +F tool/showlocks.c 9cc5e66d4ebbf2d194f39db2527ece92077e86ae627ddd233ee48e16e8142564
     F tool/showshm.c a0ab6ec32dd1f11218ca2a4018f8fb875b59414801ab8ceed8b2e69b7b45a809
     F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c2a1
     F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847
     F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
    -F tool/spaceanal.tcl 4bfd19aad7eb3ce0372ef0255f58035e0bba4ff5e9acfd763a10c6fb365c8dec
    -F tool/speed-check.sh 2b042d703a9472f08c3b13be27afac658426f8e4fc87cd2d575953fda86f08d1
    +F tool/spaceanal.tcl a95036b36622e25cffd65a55b22d6af53dfbbff0de02d45dd0059bb3c9978609
    +F tool/speed-check.sh 8ba7c7c0dba37e664679974f5954f2282275271a5b92f890756e282df0bfc458
     F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
     F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
     F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
     F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
     F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
     F tool/split-sqlite3c.tcl 3efcd4240b738f6bb2b5af0aea7e1e0ef9bc1c61654f645076cec883030b710c
    -F tool/sqldiff.c 7b9b7238284f02131dbb8f21a4e862409bff728045c5473139d28c67ac87580e
    +F tool/sqldiff.c 5046b8e227213ad016b336eb5a933e252e1f0fd1e07060c5755e259a30891287
     F tool/sqlite3_analyzer.c.in 7eeaae8b0d7577662acaabbb11107af0659d1b41bc1dfdd4d91422de27127968
     F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
     F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
    @@ -1837,7 +1859,7 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
     F tool/warnings.sh 09311479bdc290e20ec8e35a3d1b14b096bbd96222277cfd6274c3a99b3d012f
     F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
     F vsixtest/App.xaml b76d3b48860e7454775c47ea38ffea9c4abe3e85
    -F vsixtest/App.xaml.cpp c465147f50871165c60ca16955219f6c5812d6d8
    +F vsixtest/App.xaml.cpp 41158ee43269820136fa3bba00c0bd91b26cc38b650ee392aec2a8d823e54318
     F vsixtest/App.xaml.h 4a9768e2983d05600ad1e1c2f1b00a132967da9f
     F vsixtest/Assets/LockScreenLogo.scale-200.png e820c9a3deb909197081b0bf3216c06e13905f0a
     F vsixtest/Assets/SplashScreen.scale-200.png cab70988ca71bebec7bfeb3b6dbafe17b9ab0b4a
    @@ -1857,11 +1879,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
     F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
     F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
     F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
    -P 4daf94d83319231e42243625c804d5db2d14f10fa5ea1a1f358c3603c47b955b
    -R 5a98666c879217368b8f21c26d062d92
    +P b050976079ba4a22d4dfeadb81f40cf71da6588c95bf2b634d88b416de5accd7
    +R 799818c17104aa7af945cf9f5cad3762
     T +bgcolor * #d0c0ff
     T +sym-release *
    -T +sym-version-3.31.0 *
    +T +sym-version-3.33.0 *
     U drh
    -Z 51c630793e0eec473e1274a795f77643
    +Z 652aa26f564c1138ae3a9226a8f034b3
     # Remove this line to create a well-formed manifest.
    diff --git a/manifest.uuid b/manifest.uuid
    index b885b0a..4969ea1 100644
    --- a/manifest.uuid
    +++ b/manifest.uuid
    @@ -1 +1 @@
    -f6affdd41608946fcfcea914ece149038a8b25a62bbe719ed2561c649b86d824
    +fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f
    diff --git a/src/alter.c b/src/alter.c
    index ee193d1..c146c2f 100644
    --- a/src/alter.c
    +++ b/src/alter.c
    @@ -52,22 +52,22 @@ static int isAlterableTable(Parse *pParse, Table *pTab){
     static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
       sqlite3NestedParse(pParse, 
           "SELECT 1 "
    -      "FROM \"%w\".%s "
    +      "FROM \"%w\"." DFLT_SCHEMA_TABLE " "
           "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
           " AND sql NOT LIKE 'create virtual%%'"
           " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
    -      zDb, MASTER_NAME, 
    +      zDb,
           zDb, bTemp
       );
     
       if( bTemp==0 ){
         sqlite3NestedParse(pParse, 
             "SELECT 1 "
    -        "FROM temp.%s "
    +        "FROM temp." DFLT_SCHEMA_TABLE " "
             "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
             " AND sql NOT LIKE 'create virtual%%'"
             " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
    -        MASTER_NAME, zDb 
    +        zDb 
         );
       }
     }
    @@ -123,7 +123,10 @@ void sqlite3AlterRenameTable(
       /* Check that a table or index named 'zName' does not already exist
       ** in database iDb. If so, this is an error.
       */
    -  if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){
    +  if( sqlite3FindTable(db, zName, zDb)
    +   || sqlite3FindIndex(db, zName, zDb)
    +   || sqlite3IsShadowTableOf(db, pTab, zName)
    +  ){
         sqlite3ErrorMsg(pParse, 
             "there is already another table or index with this name: %s", zName);
         goto exit_rename_table;
    @@ -182,17 +185,17 @@ void sqlite3AlterRenameTable(
       /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
       ** the schema to use the new table name.  */
       sqlite3NestedParse(pParse, 
    -      "UPDATE \"%w\".%s SET "
    +      "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
           "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
           "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
           "AND   name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
    -      , zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
    +      , zDb, zDb, zTabName, zName, (iDb==1), zTabName
       );
     
    -  /* Update the tbl_name and name columns of the sqlite_master table
    +  /* Update the tbl_name and name columns of the sqlite_schema table
       ** as required.  */
       sqlite3NestedParse(pParse,
    -      "UPDATE %Q.%s SET "
    +      "UPDATE %Q." DFLT_SCHEMA_TABLE " SET "
               "tbl_name = %Q, "
               "name = CASE "
                 "WHEN type='table' THEN %Q "
    @@ -202,7 +205,7 @@ void sqlite3AlterRenameTable(
                 "ELSE name END "
           "WHERE tbl_name=%Q COLLATE nocase AND "
               "(type='table' OR type='index' OR type='trigger');", 
    -      zDb, MASTER_NAME, 
    +      zDb,
           zName, zName, zName, 
           nTabName, zTabName
       );
    @@ -223,7 +226,7 @@ void sqlite3AlterRenameTable(
       ** as required.  */
       if( iDb!=1 ){
         sqlite3NestedParse(pParse, 
    -        "UPDATE sqlite_temp_master SET "
    +        "UPDATE sqlite_temp_schema SET "
                 "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
                 "tbl_name = "
                   "CASE WHEN tbl_name=%Q COLLATE nocase AND "
    @@ -255,6 +258,22 @@ exit_rename_table:
       db->mDbFlags = savedDbFlags;
     }
     
    +/*
    +** Write code that will raise an error if the table described by
    +** zDb and zTab is not empty.
    +*/
    +static void sqlite3ErrorIfNotEmpty(
    +  Parse *pParse,        /* Parsing context */
    +  const char *zDb,      /* Schema holding the table */
    +  const char *zTab,     /* Table to check for empty */
    +  const char *zErr      /* Error message text */
    +){
    +  sqlite3NestedParse(pParse,
    +     "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
    +     zErr, zDb, zTab
    +  );
    +}
    +
     /*
     ** This function is called after an "ALTER TABLE ... ADD" statement
     ** has been parsed. Argument pColDef contains the text of the new
    @@ -307,7 +326,8 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
         return;
       }
       if( pNew->pIndex ){
    -    sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
    +    sqlite3ErrorMsg(pParse,
    +         "Cannot add a UNIQUE column");
         return;
       }
       if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
    @@ -320,16 +340,15 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
           pDflt = 0;
         }
         if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
    -      sqlite3ErrorMsg(pParse, 
    +      sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
               "Cannot add a REFERENCES column with non-NULL default value");
    -      return;
         }
         if( pCol->notNull && !pDflt ){
    -      sqlite3ErrorMsg(pParse, 
    +      sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
               "Cannot add a NOT NULL column with default value NULL");
    -      return;
         }
     
    +
         /* Ensure the default expression is something that sqlite3ValueFromExpr()
         ** can handle (i.e. not CURRENT_TIME etc.)
         */
    @@ -343,14 +362,13 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
             return;
           }
           if( !pVal ){
    -        sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default");
    -        return;
    +        sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
    +           "Cannot add a column with non-constant default");
           }
           sqlite3ValueFree(pVal);
         }
       }else if( pCol->colFlags & COLFLAG_STORED ){
    -    sqlite3ErrorMsg(pParse, "cannot add a STORED column");
    -    return;
    +    sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
       }
     
     
    @@ -364,10 +382,10 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
         }
         db->mDbFlags |= DBFLAG_PreferBuiltin;
         sqlite3NestedParse(pParse, 
    -        "UPDATE \"%w\".%s SET "
    +        "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
               "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
             "WHERE type = 'table' AND name = %Q", 
    -      zDb, MASTER_NAME, pNew->addColOffset, zCol, pNew->addColOffset+1,
    +      zDb, pNew->addColOffset, zCol, pNew->addColOffset+1,
           zTab
         );
         sqlite3DbFree(db, zCol);
    @@ -469,6 +487,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
       for(i=0; inCol; i++){
         Column *pCol = &pNew->aCol[i];
         pCol->zName = sqlite3DbStrDup(db, pCol->zName);
    +    pCol->hName = sqlite3StrIHash(pCol->zName);
         pCol->zColl = 0;
         pCol->pDflt = 0;
       }
    @@ -568,7 +587,7 @@ void sqlite3AlterRenameColumn(
     
       /* Do the rename operation using a recursive UPDATE statement that
       ** uses the sqlite_rename_column() SQL function to compute the new
    -  ** CREATE statement text for the sqlite_master table.
    +  ** CREATE statement text for the sqlite_schema table.
       */
       sqlite3MayAbort(pParse);
       zNew = sqlite3NameFromToken(db, pNew);
    @@ -576,21 +595,20 @@ void sqlite3AlterRenameColumn(
       assert( pNew->n>0 );
       bQuote = sqlite3Isquote(pNew->z[0]);
       sqlite3NestedParse(pParse, 
    -      "UPDATE \"%w\".%s SET "
    +      "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
           "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
           "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
           " AND (type != 'index' OR tbl_name = %Q)"
           " AND sql NOT LIKE 'create virtual%%'",
    -      zDb, MASTER_NAME, 
    +      zDb,
           zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
           pTab->zName
       );
     
       sqlite3NestedParse(pParse, 
    -      "UPDATE temp.%s SET "
    +      "UPDATE temp." DFLT_SCHEMA_TABLE " SET "
           "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) "
           "WHERE type IN ('trigger', 'view')",
    -      MASTER_NAME, 
           zDb, pTab->zName, iCol, zNew, bQuote
       );
     
    @@ -697,7 +715,7 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
       RenameToken *pNew;
       assert( pPtr || pParse->db->mallocFailed );
       renameTokenCheckAll(pParse, pPtr);
    -  if( pParse->eParseMode!=PARSE_MODE_UNMAP ){
    +  if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
         pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
         if( pNew ){
           pNew->p = pPtr;
    @@ -755,6 +773,21 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){
       }
     }
     
    +/*
    +** Unmap all tokens in the IdList object passed as the second argument.
    +*/
    +static void unmapColumnIdlistNames(
    +  Parse *pParse,
    +  IdList *pIdList
    +){
    +  if( pIdList ){
    +    int ii;
    +    for(ii=0; iinId; ii++){
    +      sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName);
    +    }
    +  }
    +}
    +
     /*
     ** Walker callback used by sqlite3RenameExprUnmap().
     */
    @@ -776,6 +809,7 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
         for(i=0; inSrc; i++){
           sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
           if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
    +      unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
         }
       }
     
    @@ -984,6 +1018,7 @@ static void renameColumnIdlistNames(
       }
     }
     
    +
     /*
     ** Parse the SQL statement zSql using Parse object (*p). The Parse object
     ** is initialized by this function before it is used.
    @@ -1125,7 +1160,7 @@ static int renameEditSql(
     ** successful. Otherwise, return an SQLite error code and leave an error
     ** message in the Parse object.
     */
    -static int renameResolveTrigger(Parse *pParse, const char *zDb){
    +static int renameResolveTrigger(Parse *pParse){
       sqlite3 *db = pParse->db;
       Trigger *pNew = pParse->pNewTrigger;
       TriggerStep *pStep;
    @@ -1156,17 +1191,22 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
           if( pParse->nErr ) rc = pParse->rc;
         }
         if( rc==SQLITE_OK && pStep->zTarget ){
    -      Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
    -      if( pTarget==0 ){
    -        rc = SQLITE_ERROR;
    -      }else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){
    -        SrcList sSrc;
    -        memset(&sSrc, 0, sizeof(sSrc));
    -        sSrc.nSrc = 1;
    -        sSrc.a[0].zName = pStep->zTarget;
    -        sSrc.a[0].pTab = pTarget;
    -        sNC.pSrcList = &sSrc;
    -        if( pStep->pWhere ){
    +      SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
    +      if( pSrc ){
    +        int i;
    +        for(i=0; inSrc && rc==SQLITE_OK; i++){
    +          struct SrcList_item *p = &pSrc->a[i];
    +          p->pTab = sqlite3LocateTableItem(pParse, 0, p);
    +          p->iCursor = pParse->nTab++;
    +          if( p->pTab==0 ){
    +            rc = SQLITE_ERROR;
    +          }else{
    +            p->pTab->nTabRef++;
    +            rc = sqlite3ViewGetColumnNames(pParse, p->pTab);
    +          }
    +        }
    +        sNC.pSrcList = pSrc;
    +        if( rc==SQLITE_OK && pStep->pWhere ){
               rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
             }
             if( rc==SQLITE_OK ){
    @@ -1176,7 +1216,7 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
             if( pStep->pUpsert ){
               Upsert *pUpsert = pStep->pUpsert;
               assert( rc==SQLITE_OK );
    -          pUpsert->pUpsertSrc = &sSrc;
    +          pUpsert->pUpsertSrc = pSrc;
               sNC.uNC.pUpsert = pUpsert;
               sNC.ncFlags = NC_UUpsert;
               rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
    @@ -1193,6 +1233,9 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
               sNC.ncFlags = 0;
             }
             sNC.pSrcList = 0;
    +        sqlite3SrcListDelete(db, pSrc);
    +      }else{
    +        rc = SQLITE_NOMEM;
           }
         }
       }
    @@ -1379,7 +1422,7 @@ static void renameColumnFunc(
       }else{
         /* A trigger */
         TriggerStep *pStep;
    -    rc = renameResolveTrigger(&sParse, (bTemp ? 0 : zDb));
    +    rc = renameResolveTrigger(&sParse);
         if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
     
         for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
    @@ -1582,7 +1625,7 @@ static void renameTableFunc(
             }
     
             if( isLegacy==0 ){
    -          rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
    +          rc = renameResolveTrigger(&sParse);
               if( rc==SQLITE_OK ){
                 renameWalkTrigger(&sWalker, pTrigger);
                 for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
    @@ -1669,7 +1712,7 @@ static void renameTableTest(
     
           else if( sParse.pNewTrigger ){
             if( isLegacy==0 ){
    -          rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
    +          rc = renameResolveTrigger(&sParse);
             }
             if( rc==SQLITE_OK ){
               int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema);
    diff --git a/src/analyze.c b/src/analyze.c
    index 2a071ef..9a9de99 100644
    --- a/src/analyze.c
    +++ b/src/analyze.c
    @@ -186,8 +186,13 @@ static void openStatTable(
       sqlite3 *db = pParse->db;
       Db *pDb;
       Vdbe *v = sqlite3GetVdbe(pParse);
    -  int aRoot[ArraySize(aTable)];
    +  u32 aRoot[ArraySize(aTable)];
       u8 aCreateTbl[ArraySize(aTable)];
    +#ifdef SQLITE_ENABLE_STAT4
    +  const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1;
    +#else
    +  const int nToOpen = 1;
    +#endif
     
       if( v==0 ) return;
       assert( sqlite3BtreeHoldsAllMutexes(db) );
    @@ -200,8 +205,9 @@ static void openStatTable(
       for(i=0; izDbSName))==0 ){
    -      if( aTable[i].zCols ){
    +      if( iregRoot. This is important 
    @@ -209,7 +215,7 @@ static void openStatTable(
             sqlite3NestedParse(pParse,
                 "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
             );
    -        aRoot[i] = pParse->regRoot;
    +        aRoot[i] = (u32)pParse->regRoot;
             aCreateTbl[i] = OPFLAG_P2ISREG;
           }
         }else{
    @@ -217,7 +223,6 @@ static void openStatTable(
           ** associated with the table zWhere. If zWhere is NULL, delete the
           ** entire contents of the table. */
           aRoot[i] = pStat->tnum;
    -      aCreateTbl[i] = 0;
           sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
           if( zWhere ){
             sqlite3NestedParse(pParse,
    @@ -230,15 +235,15 @@ static void openStatTable(
     #endif
           }else{
             /* The sqlite_stat[134] table already exists.  Delete all rows. */
    -        sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
    +        sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb);
           }
         }
       }
     
       /* Open the sqlite_stat[134] tables for writing. */
    -  for(i=0; aTable[i].zCols; i++){
    +  for(i=0; inRowid ){
         sqlite3DbFree(db, p->u.aRowid);
    @@ -305,7 +315,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
     /* Initialize the BLOB value of a ROWID
     */
     #ifdef SQLITE_ENABLE_STAT4
    -static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
    +static void sampleSetRowid(sqlite3 *db, StatSample *p, int n, const u8 *pData){
       assert( db!=0 );
       if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
       p->u.aRowid = sqlite3DbMallocRawNN(db, n);
    @@ -321,7 +331,7 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
     /* Initialize the INTEGER value of a ROWID.
     */
     #ifdef SQLITE_ENABLE_STAT4
    -static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
    +static void sampleSetRowidInt64(sqlite3 *db, StatSample *p, i64 iRowid){
       assert( db!=0 );
       if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
       p->nRowid = 0;
    @@ -334,7 +344,7 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
     ** Copy the contents of object (*pFrom) into (*pTo).
     */
     #ifdef SQLITE_ENABLE_STAT4
    -static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
    +static void sampleCopy(StatAccum *p, StatSample *pTo, StatSample *pFrom){
       pTo->isPSample = pFrom->isPSample;
       pTo->iCol = pFrom->iCol;
       pTo->iHash = pFrom->iHash;
    @@ -350,40 +360,41 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
     #endif
     
     /*
    -** Reclaim all memory of a Stat4Accum structure.
    +** Reclaim all memory of a StatAccum structure.
     */
    -static void stat4Destructor(void *pOld){
    -  Stat4Accum *p = (Stat4Accum*)pOld;
    +static void statAccumDestructor(void *pOld){
    +  StatAccum *p = (StatAccum*)pOld;
     #ifdef SQLITE_ENABLE_STAT4
    -  int i;
    -  for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i);
    -  for(i=0; imxSample; i++) sampleClear(p->db, p->a+i);
    -  sampleClear(p->db, &p->current);
    +  if( p->mxSample ){
    +    int i;
    +    for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i);
    +    for(i=0; imxSample; i++) sampleClear(p->db, p->a+i);
    +    sampleClear(p->db, &p->current);
    +  }
     #endif
       sqlite3DbFree(p->db, p);
     }
     
     /*
    -** Implementation of the stat_init(N,K,C) SQL function. The three parameters
    +** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters
     ** are:
     **     N:    The number of columns in the index including the rowid/pk (note 1)
     **     K:    The number of columns in the index excluding the rowid/pk.
    -**     C:    The number of rows in the index (note 2)
    +**     C:    Estimated number of rows in the index
    +**     L:    A limit on the number of rows to scan, or 0 for no-limit 
     **
     ** Note 1:  In the special case of the covering index that implements a
     ** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
     ** total number of columns in the table.
     **
    -** Note 2:  C is only used for STAT4.
    -**
     ** For indexes on ordinary rowid tables, N==K+1.  But for indexes on
     ** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
     ** PRIMARY KEY of the table.  The covering index that implements the
     ** original WITHOUT ROWID table as N==K as a special case.
     **
    -** This routine allocates the Stat4Accum object in heap memory. The return 
    -** value is a pointer to the Stat4Accum object.  The datatype of the
    -** return value is BLOB, but it is really just a pointer to the Stat4Accum
    +** This routine allocates the StatAccum object in heap memory. The return 
    +** value is a pointer to the StatAccum object.  The datatype of the
    +** return value is BLOB, but it is really just a pointer to the StatAccum
     ** object.
     */
     static void statInit(
    @@ -391,14 +402,15 @@ static void statInit(
       int argc,
       sqlite3_value **argv
     ){
    -  Stat4Accum *p;
    +  StatAccum *p;
       int nCol;                       /* Number of columns in index being sampled */
       int nKeyCol;                    /* Number of key columns */
       int nColUp;                     /* nCol rounded up for alignment */
       int n;                          /* Bytes of space to allocate */
    -  sqlite3 *db;                    /* Database connection */
    +  sqlite3 *db = sqlite3_context_db_handle(context);   /* Database connection */
     #ifdef SQLITE_ENABLE_STAT4
    -  int mxSample = SQLITE_STAT4_SAMPLES;
    +  /* Maximum number of samples.  0 if STAT4 data is not collected */
    +  int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0;
     #endif
     
       /* Decode the three function arguments */
    @@ -410,16 +422,17 @@ static void statInit(
       assert( nKeyCol<=nCol );
       assert( nKeyCol>0 );
     
    -  /* Allocate the space required for the Stat4Accum object */
    +  /* Allocate the space required for the StatAccum object */
       n = sizeof(*p) 
    -    + sizeof(tRowcnt)*nColUp                  /* Stat4Accum.anEq */
    -    + sizeof(tRowcnt)*nColUp                  /* Stat4Accum.anDLt */
    +    + sizeof(tRowcnt)*nColUp                  /* StatAccum.anEq */
    +    + sizeof(tRowcnt)*nColUp;                 /* StatAccum.anDLt */
     #ifdef SQLITE_ENABLE_STAT4
    -    + sizeof(tRowcnt)*nColUp                  /* Stat4Accum.anLt */
    -    + sizeof(Stat4Sample)*(nCol+mxSample)     /* Stat4Accum.aBest[], a[] */
    -    + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
    +  if( mxSample ){
    +    n += sizeof(tRowcnt)*nColUp                  /* StatAccum.anLt */
    +      + sizeof(StatSample)*(nCol+mxSample)       /* StatAccum.aBest[], a[] */
    +      + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
    +  }
     #endif
    -  ;
       db = sqlite3_context_db_handle(context);
       p = sqlite3DbMallocZero(db, n);
       if( p==0 ){
    @@ -428,25 +441,28 @@ static void statInit(
       }
     
       p->db = db;
    +  p->nEst = sqlite3_value_int64(argv[2]);
       p->nRow = 0;
    +  p->nLimit = sqlite3_value_int64(argv[3]);
       p->nCol = nCol;
       p->nKeyCol = nKeyCol;
    +  p->nSkipAhead = 0;
       p->current.anDLt = (tRowcnt*)&p[1];
       p->current.anEq = &p->current.anDLt[nColUp];
     
     #ifdef SQLITE_ENABLE_STAT4
    -  {
    +  p->mxSample = p->nLimit==0 ? mxSample : 0;
    +  if( mxSample ){
         u8 *pSpace;                     /* Allocated space not yet assigned */
         int i;                          /* Used to iterate through p->aSample[] */
     
         p->iGet = -1;
    -    p->mxSample = mxSample;
    -    p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
    +    p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
         p->current.anLt = &p->current.anEq[nColUp];
         p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
       
    -    /* Set up the Stat4Accum.a[] and aBest[] arrays */
    -    p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
    +    /* Set up the StatAccum.a[] and aBest[] arrays */
    +    p->a = (struct StatSample*)&p->current.anLt[nColUp];
         p->aBest = &p->a[mxSample];
         pSpace = (u8*)(&p->a[mxSample+nCol]);
         for(i=0; i<(mxSample+nCol); i++){
    @@ -466,10 +482,10 @@ static void statInit(
       ** only the pointer (the 2nd parameter) matters.  The size of the object
       ** (given by the 3rd parameter) is never used and can be any positive
       ** value. */
    -  sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
    +  sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
     }
     static const FuncDef statInitFuncdef = {
    -  2+IsStat4,       /* nArg */
    +  4,               /* nArg */
       SQLITE_UTF8,     /* funcFlags */
       0,               /* pUserData */
       0,               /* pNext */
    @@ -493,9 +509,9 @@ static const FuncDef statInitFuncdef = {
     ** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid. 
     */
     static int sampleIsBetterPost(
    -  Stat4Accum *pAccum, 
    -  Stat4Sample *pNew, 
    -  Stat4Sample *pOld
    +  StatAccum *pAccum, 
    +  StatSample *pNew, 
    +  StatSample *pOld
     ){
       int nCol = pAccum->nCol;
       int i;
    @@ -517,9 +533,9 @@ static int sampleIsBetterPost(
     ** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid. 
     */
     static int sampleIsBetter(
    -  Stat4Accum *pAccum, 
    -  Stat4Sample *pNew, 
    -  Stat4Sample *pOld
    +  StatAccum *pAccum, 
    +  StatSample *pNew, 
    +  StatSample *pOld
     ){
       tRowcnt nEqNew = pNew->anEq[pNew->iCol];
       tRowcnt nEqOld = pOld->anEq[pOld->iCol];
    @@ -539,21 +555,21 @@ static int sampleIsBetter(
     ** Copy the contents of sample *pNew into the p->a[] array. If necessary,
     ** remove the least desirable sample from p->a[] to make room.
     */
    -static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
    -  Stat4Sample *pSample = 0;
    +static void sampleInsert(StatAccum *p, StatSample *pNew, int nEqZero){
    +  StatSample *pSample = 0;
       int i;
     
       assert( IsStat4 || nEqZero==0 );
     
    -  /* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0
    -  ** values in the anEq[] array of any sample in Stat4Accum.a[]. In
    +  /* StatAccum.nMaxEqZero is set to the maximum number of leading 0
    +  ** values in the anEq[] array of any sample in StatAccum.a[]. In
       ** other words, if nMaxEqZero is n, then it is guaranteed that there
    -  ** are no samples with Stat4Sample.anEq[m]==0 for (m>=n). */
    +  ** are no samples with StatSample.anEq[m]==0 for (m>=n). */
       if( nEqZero>p->nMaxEqZero ){
         p->nMaxEqZero = nEqZero;
       }
       if( pNew->isPSample==0 ){
    -    Stat4Sample *pUpgrade = 0;
    +    StatSample *pUpgrade = 0;
         assert( pNew->anEq[pNew->iCol]>0 );
     
         /* This sample is being added because the prefix that ends in column 
    @@ -562,7 +578,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
         ** this one. Instead, upgrade the priority of the highest priority
         ** existing sample that shares this prefix.  */
         for(i=p->nSample-1; i>=0; i--){
    -      Stat4Sample *pOld = &p->a[i];
    +      StatSample *pOld = &p->a[i];
           if( pOld->anEq[pNew->iCol]==0 ){
             if( pOld->isPSample ) return;
             assert( pOld->iCol>pNew->iCol );
    @@ -581,7 +597,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
     
       /* If necessary, remove sample iMin to make room for the new sample. */
       if( p->nSample>=p->mxSample ){
    -    Stat4Sample *pMin = &p->a[p->iMin];
    +    StatSample *pMin = &p->a[p->iMin];
         tRowcnt *anEq = pMin->anEq;
         tRowcnt *anLt = pMin->anLt;
         tRowcnt *anDLt = pMin->anDLt;
    @@ -624,20 +640,20 @@ find_new_min:
     }
     #endif /* SQLITE_ENABLE_STAT4 */
     
    +#ifdef SQLITE_ENABLE_STAT4
     /*
     ** Field iChng of the index being scanned has changed. So at this point
     ** p->current contains a sample that reflects the previous row of the
     ** index. The value of anEq[iChng] and subsequent anEq[] elements are
     ** correct at this point.
     */
    -static void samplePushPrevious(Stat4Accum *p, int iChng){
    -#ifdef SQLITE_ENABLE_STAT4
    +static void samplePushPrevious(StatAccum *p, int iChng){
       int i;
     
       /* Check if any samples from the aBest[] array should be pushed
       ** into IndexSample.a[] at this point.  */
       for(i=(p->nCol-2); i>=iChng; i--){
    -    Stat4Sample *pBest = &p->aBest[i];
    +    StatSample *pBest = &p->aBest[i];
         pBest->anEq[i] = p->current.anEq[i];
         if( p->nSamplemxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){
           sampleInsert(p, pBest, i);
    @@ -661,27 +677,25 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
         }
         p->nMaxEqZero = iChng;
       }
    -#endif
    -
    -#ifndef SQLITE_ENABLE_STAT4
    -  UNUSED_PARAMETER( p );
    -  UNUSED_PARAMETER( iChng );
    -#endif
     }
    +#endif /* SQLITE_ENABLE_STAT4 */
     
     /*
     ** Implementation of the stat_push SQL function:  stat_push(P,C,R)
     ** Arguments:
     **
    -**    P     Pointer to the Stat4Accum object created by stat_init()
    +**    P     Pointer to the StatAccum object created by stat_init()
     **    C     Index of left-most column to differ from previous row
     **    R     Rowid for the current row.  Might be a key record for
     **          WITHOUT ROWID tables.
     **
    -** This SQL function always returns NULL.  It's purpose it to accumulate
    -** statistical data and/or samples in the Stat4Accum object about the
    -** index being analyzed.  The stat_get() SQL function will later be used to
    -** extract relevant information for constructing the sqlite_statN tables.
    +** The purpose of this routine is to collect statistical data and/or
    +** samples from the index being analyzed into the StatAccum object.
    +** The stat_get() SQL function will be used afterwards to
    +** retrieve the information gathered.
    +**
    +** This SQL function usually returns NULL, but might return an integer
    +** if it wants the byte-code to do special processing.
     **
     ** The R parameter is only used for STAT4
     */
    @@ -693,7 +707,7 @@ static void statPush(
       int i;
     
       /* The three function arguments */
    -  Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
    +  StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
       int iChng = sqlite3_value_int(argv[1]);
     
       UNUSED_PARAMETER( argc );
    @@ -706,7 +720,9 @@ static void statPush(
         for(i=0; inCol; i++) p->current.anEq[i] = 1;
       }else{
         /* Second and subsequent calls get processed here */
    -    samplePushPrevious(p, iChng);
    +#ifdef SQLITE_ENABLE_STAT4
    +    if( p->mxSample ) samplePushPrevious(p, iChng);
    +#endif
     
         /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
         ** to the current row of the index. */
    @@ -716,26 +732,25 @@ static void statPush(
         for(i=iChng; inCol; i++){
           p->current.anDLt[i]++;
     #ifdef SQLITE_ENABLE_STAT4
    -      p->current.anLt[i] += p->current.anEq[i];
    +      if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
     #endif
           p->current.anEq[i] = 1;
         }
       }
    +
       p->nRow++;
     #ifdef SQLITE_ENABLE_STAT4
    -  if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
    -    sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
    -  }else{
    -    sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
    -                                       sqlite3_value_blob(argv[2]));
    -  }
    -  p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
    -#endif
    -
    -#ifdef SQLITE_ENABLE_STAT4
    -  {
    -    tRowcnt nLt = p->current.anLt[p->nCol-1];
    +  if( p->mxSample ){
    +    tRowcnt nLt;
    +    if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
    +      sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
    +    }else{
    +      sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
    +                                         sqlite3_value_blob(argv[2]));
    +    }
    +    p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
     
    +    nLt = p->current.anLt[p->nCol-1];
         /* Check if this is to be a periodic sample. If so, add it. */
         if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
           p->current.isPSample = 1;
    @@ -751,9 +766,14 @@ static void statPush(
             sampleCopy(p, &p->aBest[i], &p->current);
           }
         }
    -  }
    +  }else
     #endif
    +  if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){
    +    p->nSkipAhead++;
    +    sqlite3_result_int(context, p->current.anDLt[0]>0);
    +  }
     }
    +
     static const FuncDef statPushFuncdef = {
       2+IsStat4,       /* nArg */
       SQLITE_UTF8,     /* funcFlags */
    @@ -775,15 +795,15 @@ static const FuncDef statPushFuncdef = {
     /*
     ** Implementation of the stat_get(P,J) SQL function.  This routine is
     ** used to query statistical information that has been gathered into
    -** the Stat4Accum object by prior calls to stat_push().  The P parameter
    -** has type BLOB but it is really just a pointer to the Stat4Accum object.
    +** the StatAccum object by prior calls to stat_push().  The P parameter
    +** has type BLOB but it is really just a pointer to the StatAccum object.
     ** The content to returned is determined by the parameter J
     ** which is one of the STAT_GET_xxxx values defined above.
     **
     ** The stat_get(P,J) function is not available to generic SQL.  It is
     ** inserted as part of a manually constructed bytecode program.  (See
     ** the callStatGet() routine below.)  It is guaranteed that the P
    -** parameter will always be a poiner to a Stat4Accum object, never a
    +** parameter will always be a pointer to a StatAccum object, never a
     ** NULL.
     **
     ** If STAT4 is not enabled, then J is always
    @@ -796,7 +816,7 @@ static void statGet(
       int argc,
       sqlite3_value **argv
     ){
    -  Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
    +  StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
     #ifdef SQLITE_ENABLE_STAT4
       /* STAT4 has a parameter on this routine. */
       int eCall = sqlite3_value_int(argv[1]);
    @@ -805,6 +825,7 @@ static void statGet(
            || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
            || eCall==STAT_GET_NDLT 
       );
    +  assert( eCall==STAT_GET_STAT1 || p->mxSample );
       if( eCall==STAT_GET_STAT1 )
     #else
       assert( argc==1 );
    @@ -817,7 +838,7 @@ static void statGet(
         ** the index. The first integer in the list is the total number of 
         ** entries in the index. There is one additional integer in the list 
         ** for each indexed column. This additional integer is an estimate of
    -    ** the number of rows matched by a stabbing query on the index using
    +    ** the number of rows matched by a equality query on the index using
         ** a key with the corresponding number of fields. In other words,
         ** if the index is on columns (a,b) and the sqlite_stat1 value is 
         ** "100 10 2", then SQLite estimates that:
    @@ -840,7 +861,8 @@ static void statGet(
           return;
         }
     
    -    sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
    +    sqlite3_snprintf(24, zRet, "%llu", 
    +        p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
         z = zRet + sqlite3Strlen30(zRet);
         for(i=0; inKeyCol; i++){
           u64 nDistinct = p->current.anDLt[i] + 1;
    @@ -860,7 +882,7 @@ static void statGet(
           p->iGet = 0;
         }
         if( p->iGetnSample ){
    -      Stat4Sample *pS = p->a + p->iGet;
    +      StatSample *pS = p->a + p->iGet;
           if( pS->nRowid==0 ){
             sqlite3_result_int64(context, pS->u.iRowid);
           }else{
    @@ -916,19 +938,43 @@ static const FuncDef statGetFuncdef = {
       {0}
     };
     
    -static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){
    +static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){
     #ifdef SQLITE_ENABLE_STAT4
    -  sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1);
    +  sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1);
     #elif SQLITE_DEBUG
       assert( iParam==STAT_GET_STAT1 );
     #else
       UNUSED_PARAMETER( iParam );
     #endif
    -  assert( regOut!=regStat4 && regOut!=regStat4+1 );
    -  sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4,
    +  assert( regOut!=regStat && regOut!=regStat+1 );
    +  sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4,
                                  &statGetFuncdef, 0);
     }
     
    +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
    +/* Add a comment to the most recent VDBE opcode that is the name
    +** of the k-th column of the pIdx index.
    +*/
    +static void analyzeVdbeCommentIndexWithColumnName(
    +  Vdbe *v,         /* Prepared statement under construction */
    +  Index *pIdx,     /* Index whose column is being loaded */
    +  int k            /* Which column index */
    +){
    +  int i;           /* Index of column in the table */
    +  assert( k>=0 && knColumn );
    +  i = pIdx->aiColumn[k];
    +  if( NEVER(i==XN_ROWID) ){
    +    VdbeComment((v,"%s.rowid",pIdx->zName));
    +  }else if( i==XN_EXPR ){
    +    VdbeComment((v,"%s.expr(%d)",pIdx->zName, k));
    +  }else{
    +    VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zName));
    +  }
    +}
    +#else
    +# define analyzeVdbeCommentIndexWithColumnName(a,b,c)
    +#endif /* SQLITE_DEBUG */
    +
     /*
     ** Generate code to do an analysis of all indices associated with
     ** a single table.
    @@ -951,12 +997,11 @@ static void analyzeOneTable(
       int iDb;                     /* Index of database containing pTab */
       u8 needTableCnt = 1;         /* True to count the table */
       int regNewRowid = iMem++;    /* Rowid for the inserted record */
    -  int regStat4 = iMem++;       /* Register to hold Stat4Accum object */
    +  int regStat = iMem++;        /* Register to hold StatAccum object */
       int regChng = iMem++;        /* Index of changed index field */
    -#ifdef SQLITE_ENABLE_STAT4
       int regRowid = iMem++;       /* Rowid argument passed to stat_push() */
    -#endif
       int regTemp = iMem++;        /* Temporary use register */
    +  int regTemp2 = iMem++;       /* Second temporary use register */
       int regTabname = iMem++;     /* Register containing table name */
       int regIdxname = iMem++;     /* Register containing index name */
       int regStat1 = iMem++;       /* Value for the stat column of sqlite_stat1 */
    @@ -1084,17 +1129,26 @@ static void analyzeOneTable(
         **    (1) the number of columns in the index including the rowid
         **        (or for a WITHOUT ROWID table, the number of PK columns),
         **    (2) the number of columns in the key without the rowid/pk
    -    **    (3) the number of rows in the index,
    -    **
    -    **
    -    ** The third argument is only used for STAT4
    +    **    (3) estimated number of rows in the index,
         */
    +    sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
    +    assert( regRowid==regStat+2 );
    +    sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
     #ifdef SQLITE_ENABLE_STAT4
    -    sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
    +    if( OptimizationEnabled(db, SQLITE_Stat4) ){
    +      sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
    +      addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
    +      VdbeCoverage(v);
    +    }else
     #endif
    -    sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
    -    sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
    -    sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4,
    +    {
    +      addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
    +      VdbeCoverage(v);
    +      sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
    +    }
    +    assert( regTemp2==regStat+4 );
    +    sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
    +    sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
                                    &statInitFuncdef, 0);
     
         /* Implementation of the following:
    @@ -1105,8 +1159,6 @@ static void analyzeOneTable(
         **   goto next_push_0;
         **
         */
    -    addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
    -    VdbeCoverage(v);
         sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
         addrNextRow = sqlite3VdbeCurrentAddr(v);
     
    @@ -1139,6 +1191,7 @@ static void analyzeOneTable(
             char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
             sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
             sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
    +        analyzeVdbeCommentIndexWithColumnName(v,pIdx,i);
             aGotoChng[i] = 
             sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
             sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
    @@ -1159,6 +1212,7 @@ static void analyzeOneTable(
           for(i=0; ipTable);
    -      int j, k, regKey;
    -      regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
    -      for(j=0; jnKeyCol; j++){
    -        k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
    -        assert( k>=0 && knColumn );
    -        sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
    -        VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
    +    if( OptimizationEnabled(db, SQLITE_Stat4) ){
    +      assert( regRowid==(regStat+2) );
    +      if( HasRowid(pTab) ){
    +        sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
    +      }else{
    +        Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
    +        int j, k, regKey;
    +        regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
    +        for(j=0; jnKeyCol; j++){
    +          k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
    +          assert( k>=0 && knColumn );
    +          sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
    +          analyzeVdbeCommentIndexWithColumnName(v,pIdx,k);
    +        }
    +        sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
    +        sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
           }
    -      sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
    -      sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
         }
     #endif
    -    assert( regChng==(regStat4+1) );
    -    sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4,
    -                               &statPushFuncdef, 0);
    -    sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
    +    assert( regChng==(regStat+1) );
    +    {
    +      sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4,
    +                                 &statPushFuncdef, 0);
    +      if( db->nAnalysisLimit ){
    +        int j1, j2, j3;
    +        j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v);
    +        j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v);
    +        j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1);
    +        VdbeCoverage(v);
    +        sqlite3VdbeJumpHere(v, j1);
    +        sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
    +        sqlite3VdbeJumpHere(v, j2);
    +        sqlite3VdbeJumpHere(v, j3);
    +      }else{
    +        sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
    +      }
    +    }
     
         /* Add the entry to the stat1 table. */
    -    callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1);
    +    callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
         assert( "BBB"[0]==SQLITE_AFF_TEXT );
         sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
         sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
    @@ -1207,7 +1277,7 @@ static void analyzeOneTable(
     
         /* Add the entries to the stat4 table. */
     #ifdef SQLITE_ENABLE_STAT4
    -    {
    +    if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){
           int regEq = regStat1;
           int regLt = regStat1+1;
           int regDLt = regStat1+2;
    @@ -1221,12 +1291,12 @@ static void analyzeOneTable(
           pParse->nMem = MAX(pParse->nMem, regCol+nCol);
     
           addrNext = sqlite3VdbeCurrentAddr(v);
    -      callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid);
    +      callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
           addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
           VdbeCoverage(v);
    -      callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq);
    -      callStatGet(pParse, regStat4, STAT_GET_NLT, regLt);
    -      callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt);
    +      callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
    +      callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
    +      callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt);
           sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
           VdbeCoverage(v);
           for(i=0; iaDb[iDb].zDbSName, zName)==0
    +   || (iDb==0 && sqlite3StrICmp("main", zName)==0)
    +  );
    +}
    +
     /*
     ** An SQL user-function registered to do the work of an ATTACH statement. The
     ** three arguments to the function come directly from an attach statement:
    @@ -117,9 +128,8 @@ static void attachFunc(
           goto attach_error;
         }
         for(i=0; inDb; i++){
    -      char *z = db->aDb[i].zDbSName;
    -      assert( z && zName );
    -      if( sqlite3StrICmp(z, zName)==0 ){
    +      assert( zName );
    +      if( sqlite3DbIsNamed(db, i, zName) ){
             zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
             goto attach_error;
           }
    @@ -188,7 +198,7 @@ static void attachFunc(
         rc = SQLITE_NOMEM_BKPT;
       }
     
    -
    +/* BEGIN SQLCIPHER */
     #ifdef SQLITE_HAS_CODEC
       if( rc==SQLITE_OK ){
         extern int sqlite3CodecAttach(sqlite3*, int, const void*, int);
    @@ -215,7 +225,7 @@ static void attachFunc(
             ** use the key from the main database. */
             if( sqlite3CodecQueryParameters(db, zName, zPath)==0 ){
               sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
    -          if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
    +          if( nKey || sqlite3BtreeGetRequestedReserve(db->aDb[0].pBt)>0 ){
                 rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
               }
             }
    @@ -223,7 +233,8 @@ static void attachFunc(
         }
       }
     #endif
    -  sqlite3_free( zPath );
    +/* END SQLCIPHER */
    +  sqlite3_free_filename( zPath );
     
       /* If the file was opened successfully, read the schema for the new database.
       ** If this fails, or if opening the file failed, then close the file and 
    @@ -308,7 +319,7 @@ static void detachFunc(
       for(i=0; inDb; i++){
         pDb = &db->aDb[i];
         if( pDb->pBt==0 ) continue;
    -    if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
    +    if( sqlite3DbIsNamed(db, i, zName) ) break;
       }
     
       if( i>=db->nDb ){
    @@ -499,20 +510,21 @@ int sqlite3FixSrcList(
       SrcList *pList       /* The Source list to check and modify */
     ){
       int i;
    -  const char *zDb;
       struct SrcList_item *pItem;
    +  sqlite3 *db = pFix->pParse->db;
    +  int iDb = sqlite3FindDbName(db, pFix->zDb);
     
       if( NEVER(pList==0) ) return 0;
    -  zDb = pFix->zDb;
    +
       for(i=0, pItem=pList->a; inSrc; i++, pItem++){
         if( pFix->bTemp==0 ){
    -      if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
    +      if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
             sqlite3ErrorMsg(pFix->pParse,
                 "%s %T cannot reference objects in database %s",
                 pFix->zType, pFix->pName, pItem->zDatabase);
             return 1;
           }
    -      sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
    +      sqlite3DbFree(db, pItem->zDatabase);
           pItem->zDatabase = 0;
           pItem->pSchema = pFix->pSchema;
           pItem->fg.fromDDL = 1;
    @@ -624,6 +636,9 @@ int sqlite3FixTriggerStep(
         if( sqlite3FixExprList(pFix, pStep->pExprList) ){
           return 1;
         }
    +    if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){
    +      return 1;
    +    }
     #ifndef SQLITE_OMIT_UPSERT
         if( pStep->pUpsert ){
           Upsert *pUp = pStep->pUpsert;
    diff --git a/src/backup.c b/src/backup.c
    index 8035a65..99ad72e 100644
    --- a/src/backup.c
    +++ b/src/backup.c
    @@ -112,7 +112,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
     */
     static int setDestPgsz(sqlite3_backup *p){
       int rc;
    -  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
    +  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
       return rc;
     }
     
    @@ -256,13 +256,15 @@ static int backupOnePage(
       int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
       const int nCopy = MIN(nSrcPgsz, nDestPgsz);
       const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
    +/* BEGIN SQLCIPHER */
     #ifdef SQLITE_HAS_CODEC
       /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
       ** guaranteed that the shared-mutex is held by this thread, handle
       ** p->pSrc may not actually be the owner.  */
       int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
    -  int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest);
    +  int nDestReserve = sqlite3BtreeGetRequestedReserve(p->pDest);
     #endif
    +/* END SQLCIPHER */
       int rc = SQLITE_OK;
       i64 iOff;
     
    @@ -279,6 +281,7 @@ static int backupOnePage(
         rc = SQLITE_READONLY;
       }
     
    +/* BEGIN SQLCIPHER */
     #ifdef SQLITE_HAS_CODEC
       /* Backup is not possible if the page size of the destination is changing
       ** and a codec is in use.
    @@ -298,6 +301,7 @@ static int backupOnePage(
         if( rc==SQLITE_OK && newPgsz!=(u32)nSrcPgsz ) rc = SQLITE_READONLY;
       }
     #endif
    +/* END SQLCIPHER */
     
       /* This loop runs once for each destination page spanned by the source 
       ** page. For each iteration, variable iOff is set to the byte offset
    @@ -794,9 +798,11 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
       b.pDest = pTo;
       b.iNext = 1;
     
    +/* BEGIN SQLCIPHER */
     #ifdef SQLITE_HAS_CODEC
       sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom));
     #endif
    +/* END SQLCIPHER */
     
       /* 0x7FFFFFFF is the hard limit for the number of pages in a database
       ** file. By passing this as the number of pages to copy to
    diff --git a/src/btree.c b/src/btree.c
    index be5d639..919d3b8 100644
    --- a/src/btree.c
    +++ b/src/btree.c
    @@ -69,7 +69,7 @@ int sqlite3BtreeTrace=1;  /* True to enable tracing */
     ** but the test harness needs to access it so we make it global for 
     ** test builds.
     **
    -** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER.
    +** Access to this variable is protected by SQLITE_MUTEX_STATIC_MAIN.
     */
     #ifdef SQLITE_TEST
     BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
    @@ -200,16 +200,18 @@ static int hasSharedCacheTableLock(
       ** table.  */
       if( isIndex ){
         HashElem *p;
    +    int bSeen = 0;
         for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){
           Index *pIdx = (Index *)sqliteHashData(p);
           if( pIdx->tnum==(int)iRoot ){
    -        if( iTab ){
    +        if( bSeen ){
               /* Two or more indexes share the same root page.  There must
               ** be imposter tables.  So just return true.  The assert is not
               ** useful in that case. */
               return 1;
             }
             iTab = pIdx->pTable->tnum;
    +        bSeen = 1;
           }
         }
       }else{
    @@ -355,7 +357,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
     
       /* A connection with the read-uncommitted flag set will never try to
       ** obtain a read-lock using this function. The only read-lock obtained
    -  ** by a connection in read-uncommitted mode is on the sqlite_master 
    +  ** by a connection in read-uncommitted mode is on the sqlite_schema 
       ** table, and that lock is obtained in BtreeBeginTrans().  */
       assert( 0==(p->db->flags&SQLITE_ReadUncommit) || eLock==WRITE_LOCK );
     
    @@ -611,7 +613,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
     */
     static int btreeGetHasContent(BtShared *pBt, Pgno pgno){
       Bitvec *p = pBt->pHasContent;
    -  return (p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno)));
    +  return p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTestNotNull(p, pgno));
     }
     
     /*
    @@ -991,7 +993,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
       if( *pRC ) return;
     
       assert( sqlite3_mutex_held(pBt->mutex) );
    -  /* The master-journal page number must never be used as a pointer map page */
    +  /* The super-journal page number must never be used as a pointer map page */
       assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
     
       assert( pBt->autoVacuum );
    @@ -1449,7 +1451,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
             int sz2 = 0;
             int sz = get2byte(&data[iFree+2]);
             int top = get2byte(&data[hdr+5]);
    -        if( NEVER(top>=iFree) ){
    +        if( top>=iFree ){
               return SQLITE_CORRUPT_PAGE(pPage);
             }
             if( iFree2 ){
    @@ -1751,7 +1753,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
           nFrag = iFreeBlk - iEnd;
           if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
           iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
    -      if( NEVER(iEnd > pPage->pBt->usableSize) ){
    +      if( iEnd > pPage->pBt->usableSize ){
             return SQLITE_CORRUPT_PAGE(pPage);
           }
           iSize = iEnd - iStart;
    @@ -1780,7 +1782,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
         ** so just extend the cell content area rather than create another
         ** freelist entry */
         if( iStartnPage & 0x80000000)==0 || CORRUPT_DB );
       return pBt->nPage;
     }
    -u32 sqlite3BtreeLastPage(Btree *p){
    +Pgno sqlite3BtreeLastPage(Btree *p){
       assert( sqlite3BtreeHoldsMutex(p) );
    -  return btreePagecount(p->pBt) & 0x7fffffff;
    +  return btreePagecount(p->pBt);
     }
     
     /*
    @@ -2304,8 +2305,7 @@ static int btreeInvokeBusyHandler(void *pArg){
       BtShared *pBt = (BtShared*)pArg;
       assert( pBt->db );
       assert( sqlite3_mutex_held(pBt->db->mutex) );
    -  return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
    -                                  sqlite3PagerFile(pBt->pPager));
    +  return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
     }
     
     /*
    @@ -2421,7 +2421,7 @@ int sqlite3BtreeOpen(
     #if SQLITE_THREADSAFE
           mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
           sqlite3_mutex_enter(mutexOpen);
    -      mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
    +      mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
           sqlite3_mutex_enter(mutexShared);
     #endif
           for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
    @@ -2540,7 +2540,7 @@ int sqlite3BtreeOpen(
         pBt->nRef = 1;
         if( p->sharable ){
           MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
    -      MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
    +      MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);)
           if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
             pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
             if( pBt->mutex==0 ){
    @@ -2629,13 +2629,13 @@ btree_open_out:
     */
     static int removeFromSharingList(BtShared *pBt){
     #ifndef SQLITE_OMIT_SHARED_CACHE
    -  MUTEX_LOGIC( sqlite3_mutex *pMaster; )
    +  MUTEX_LOGIC( sqlite3_mutex *pMainMtx; )
       BtShared *pList;
       int removed = 0;
     
       assert( sqlite3_mutex_notheld(pBt->mutex) );
    -  MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
    -  sqlite3_mutex_enter(pMaster);
    +  MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
    +  sqlite3_mutex_enter(pMainMtx);
       pBt->nRef--;
       if( pBt->nRef<=0 ){
         if( GLOBAL(BtShared*,sqlite3SharedCacheList)==pBt ){
    @@ -2654,7 +2654,7 @@ static int removeFromSharingList(BtShared *pBt){
         }
         removed = 1;
       }
    -  sqlite3_mutex_leave(pMaster);
    +  sqlite3_mutex_leave(pMainMtx);
       return removed;
     #else
       return 1;
    @@ -2856,19 +2856,17 @@ int sqlite3BtreeSetPagerFlags(
     */
     int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
       int rc = SQLITE_OK;
    +  int x;
       BtShared *pBt = p->pBt;
    -  assert( nReserve>=-1 && nReserve<=255 );
    +  assert( nReserve>=0 && nReserve<=255 );
       sqlite3BtreeEnter(p);
    -#if SQLITE_HAS_CODEC
    -  if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve;
    -#endif
    +  pBt->nReserveWanted = nReserve;
    +  x = pBt->pageSize - pBt->usableSize;
    +  if( nReservebtsFlags & BTS_PAGESIZE_FIXED ){
         sqlite3BtreeLeave(p);
         return SQLITE_READONLY;
       }
    -  if( nReserve<0 ){
    -    nReserve = pBt->pageSize - pBt->usableSize;
    -  }
       assert( nReserve>=0 && nReserve<=255 );
       if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
             ((pageSize-1)&pageSize)==0 ){
    @@ -2914,19 +2912,17 @@ int sqlite3BtreeGetReserveNoMutex(Btree *p){
     ** are intentually left unused.  This is the "reserved" space that is
     ** sometimes used by extensions.
     **
    -** If SQLITE_HAS_MUTEX is defined then the number returned is the
    -** greater of the current reserved space and the maximum requested
    -** reserve space.
    +** The value returned is the larger of the current reserve size and
    +** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES.
    +** The amount of reserve can only grow - never shrink.
     */
    -int sqlite3BtreeGetOptimalReserve(Btree *p){
    -  int n;
    +int sqlite3BtreeGetRequestedReserve(Btree *p){
    +  int n1, n2;
       sqlite3BtreeEnter(p);
    -  n = sqlite3BtreeGetReserveNoMutex(p);
    -#ifdef SQLITE_HAS_CODEC
    -  if( npBt->optimalReserve ) n = p->pBt->optimalReserve;
    -#endif
    +  n1 = (int)p->pBt->nReserveWanted;
    +  n2 = sqlite3BtreeGetReserveNoMutex(p);
       sqlite3BtreeLeave(p);
    -  return n;
    +  return n1>n2 ? n1 : n2;
     }
     
     
    @@ -2935,8 +2931,8 @@ int sqlite3BtreeGetOptimalReserve(Btree *p){
     ** No changes are made if mxPage is 0 or negative.
     ** Regardless of the value of mxPage, return the maximum page count.
     */
    -int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
    -  int n;
    +Pgno sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){
    +  Pgno n;
       sqlite3BtreeEnter(p);
       n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
       sqlite3BtreeLeave(p);
    @@ -3376,6 +3372,7 @@ int sqlite3BtreeNewDb(Btree *p){
     */
     int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
       BtShared *pBt = p->pBt;
    +  Pager *pPager = pBt->pPager;
       int rc = SQLITE_OK;
     
       sqlite3BtreeEnter(p);
    @@ -3391,7 +3388,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
       assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
     
       if( (p->db->flags & SQLITE_ResetDatabase) 
    -   && sqlite3PagerIsreadonly(pBt->pPager)==0 
    +   && sqlite3PagerIsreadonly(pPager)==0 
       ){
         pBt->btsFlags &= ~BTS_READ_ONLY;
       }
    @@ -3433,12 +3430,24 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
       /* Any read-only or read-write transaction implies a read-lock on 
       ** page 1. So if some other shared-cache client already has a write-lock 
       ** on page 1, the transaction cannot be opened. */
    -  rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
    +  rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
       if( SQLITE_OK!=rc ) goto trans_begun;
     
       pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
       if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
       do {
    +    sqlite3PagerWalDb(pPager, p->db);
    +
    +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    +    /* If transitioning from no transaction directly to a write transaction,
    +    ** block for the WRITER lock first if possible. */
    +    if( pBt->pPage1==0 && wrflag ){
    +      assert( pBt->inTransaction==TRANS_NONE );
    +      rc = sqlite3PagerWalWriteLock(pPager, 1);
    +      if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break;
    +    }
    +#endif
    +
         /* Call lockBtree() until either pBt->pPage1 is populated or
         ** lockBtree() returns something other than SQLITE_OK. lockBtree()
         ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
    @@ -3452,7 +3461,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
           if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
             rc = SQLITE_READONLY;
           }else{
    -        rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
    +        rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
             if( rc==SQLITE_OK ){
               rc = newDatabase(pBt);
             }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
    @@ -3465,11 +3474,15 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
         }
       
         if( rc!=SQLITE_OK ){
    +      (void)sqlite3PagerWalWriteLock(pPager, 0);
           unlockBtreeIfUnused(pBt);
         }
       }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
               btreeInvokeBusyHandler(pBt) );
    -  sqlite3PagerResetLockTimeout(pBt->pPager);
    +  sqlite3PagerWalDb(pPager, 0);
    +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    +  if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
    +#endif
     
       if( rc==SQLITE_OK ){
         if( p->inTrans==TRANS_NONE ){
    @@ -3521,7 +3534,7 @@ trans_begun:
           ** open savepoints. If the second parameter is greater than 0 and
           ** the sub-journal is not already open, then it will be opened here.
           */
    -      rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
    +      rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
         }
       }
     
    @@ -3872,7 +3885,7 @@ int sqlite3BtreeIncrVacuum(Btree *p){
         Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
         Pgno nFin = finalDbSize(pBt, nOrig, nFree);
     
    -    if( nOrig=nOrig ){
           rc = SQLITE_CORRUPT_BKPT;
         }else if( nFree>0 ){
           rc = saveAllCursors(pBt, 0, 0);
    @@ -3969,18 +3982,18 @@ static int autoVacuumCommit(BtShared *pBt){
     **
     ** This call is a no-op if no write-transaction is currently active on pBt.
     **
    -** Otherwise, sync the database file for the btree pBt. zMaster points to
    -** the name of a master journal file that should be written into the
    -** individual journal file, or is NULL, indicating no master journal file 
    +** Otherwise, sync the database file for the btree pBt. zSuperJrnl points to
    +** the name of a super-journal file that should be written into the
    +** individual journal file, or is NULL, indicating no super-journal file 
     ** (single database transaction).
     **
    -** When this is called, the master journal should already have been
    +** When this is called, the super-journal should already have been
     ** created, populated with this journal pointer and synced to disk.
     **
     ** Once this is routine has returned, the only thing required to commit
     ** the write-transaction for this database file is to delete the journal.
     */
    -int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
    +int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){
       int rc = SQLITE_OK;
       if( p->inTrans==TRANS_WRITE ){
         BtShared *pBt = p->pBt;
    @@ -3997,7 +4010,7 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
           sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
         }
     #endif
    -    rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
    +    rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0);
         sqlite3BtreeLeave(p);
       }
       return rc;
    @@ -4060,7 +4073,7 @@ static void btreeEndTransaction(Btree *p){
     ** the upper layer will attempt a rollback. However, if the second argument
     ** is non-zero then this b-tree transaction is part of a multi-file 
     ** transaction. In this case, the transaction has already been committed 
    -** (by deleting a master journal file) and the caller will ignore this 
    +** (by deleting a super-journal file) and the caller will ignore this 
     ** functions return code. So, even if an error occurs in the pager layer,
     ** reset the b-tree objects internal state to indicate that the write
     ** transaction has been closed. This is quite safe, as the pager will have
    @@ -4358,7 +4371,7 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
     */
     static int btreeCursor(
       Btree *p,                              /* The btree */
    -  int iTable,                            /* Root page of table to open */
    +  Pgno iTable,                           /* Root page of table to open */
       int wrFlag,                            /* 1 to write. 0 read-only */
       struct KeyInfo *pKeyInfo,              /* First arg to comparison function */
       BtCursor *pCur                         /* Space for new cursor */
    @@ -4401,7 +4414,7 @@ static int btreeCursor(
     
       /* Now that no other errors can occur, finish filling in the BtCursor
       ** variables and link the cursor into the BtShared list.  */
    -  pCur->pgnoRoot = (Pgno)iTable;
    +  pCur->pgnoRoot = iTable;
       pCur->iPage = -1;
       pCur->pKeyInfo = pKeyInfo;
       pCur->pBtree = p;
    @@ -4411,7 +4424,7 @@ static int btreeCursor(
       /* If there are two or more cursors on the same btree, then all such
       ** cursors *must* have the BTCF_Multiple flag set. */
       for(pX=pBt->pCursor; pX; pX=pX->pNext){
    -    if( pX->pgnoRoot==(Pgno)iTable ){
    +    if( pX->pgnoRoot==iTable ){
           pX->curFlags |= BTCF_Multiple;
           pCur->curFlags |= BTCF_Multiple;
         }
    @@ -4423,7 +4436,7 @@ static int btreeCursor(
     }
     static int btreeCursorWithLock(
       Btree *p,                              /* The btree */
    -  int iTable,                            /* Root page of table to open */
    +  Pgno iTable,                           /* Root page of table to open */
       int wrFlag,                            /* 1 to write. 0 read-only */
       struct KeyInfo *pKeyInfo,              /* First arg to comparison function */
       BtCursor *pCur                         /* Space for new cursor */
    @@ -4436,7 +4449,7 @@ static int btreeCursorWithLock(
     }
     int sqlite3BtreeCursor(
       Btree *p,                                   /* The btree */
    -  int iTable,                                 /* Root page of table to open */
    +  Pgno iTable,                                /* Root page of table to open */
       int wrFlag,                                 /* 1 to write. 0 read-only */
       struct KeyInfo *pKeyInfo,                   /* First arg to xCompare() */
       BtCursor *pCur                              /* Write new cursor here */
    @@ -4822,7 +4835,7 @@ static int accessPayload(
         Pgno nextPage;
     
         nextPage = get4byte(&aPayload[pCur->info.nLocal]);
    -
    + 
         /* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
         **
         ** The aOverflow[] array is sized at one entry for each overflow page
    @@ -4861,6 +4874,7 @@ static int accessPayload(
         assert( rc==SQLITE_OK && amt>0 );
         while( nextPage ){
           /* If required, populate the overflow page-list cache. */
    +      if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT;
           assert( pCur->aOverflow[iIdx]==0
                   || pCur->aOverflow[iIdx]==nextPage
                   || CORRUPT_DB );
    @@ -6276,6 +6290,10 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
         u32 nLeaf;                /* Initial number of leaf cells on trunk page */
     
         iTrunk = get4byte(&pPage1->aData[32]);
    +    if( iTrunk>btreePagecount(pBt) ){
    +      rc = SQLITE_CORRUPT_BKPT;
    +      goto freepage_out;
    +    }
         rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
         if( rc!=SQLITE_OK ){
           goto freepage_out;
    @@ -7157,7 +7175,7 @@ static int editPage(
       assert( nCell>=0 );
       if( iOldnCell ) return SQLITE_CORRUPT_BKPT;
    +    if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT;
         memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
         nCell -= nShift;
       }
    @@ -8758,7 +8776,11 @@ int sqlite3BtreeInsert(
       assert( pPage->intKey || pX->nKey>=0 );
       assert( pPage->leaf || !pPage->intKey );
       if( pPage->nFree<0 ){
    -    rc = btreeComputeFreeSpace(pPage);
    +    if( pCur->eState>CURSOR_INVALID ){
    +      rc = SQLITE_CORRUPT_BKPT;
    +    }else{
    +      rc = btreeComputeFreeSpace(pPage);
    +    }
         if( rc ) return rc;
       }
     
    @@ -9076,7 +9098,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
     **     BTREE_INTKEY|BTREE_LEAFDATA     Used for SQL tables with rowid keys
     **     BTREE_ZERODATA                  Used for SQL indices
     */
    -static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
    +static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
       BtShared *pBt = p->pBt;
       MemPage *pRoot;
       Pgno pgnoRoot;
    @@ -9109,6 +9131,9 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
         ** created so far, so the new root-page is (meta[3]+1).
         */
         sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
    +    if( pgnoRoot>btreePagecount(pBt) ){
    +      return SQLITE_CORRUPT_BKPT;
    +    }
         pgnoRoot++;
     
         /* The new root-page may not be allocated on a pointer-map page, or the
    @@ -9118,8 +9143,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
             pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
           pgnoRoot++;
         }
    -    assert( pgnoRoot>=3 || CORRUPT_DB );
    -    testcase( pgnoRoot<3 );
    +    assert( pgnoRoot>=3 );
     
         /* Allocate a page. The page that currently resides at pgnoRoot will
         ** be moved to the allocated page (unless the allocated page happens
    @@ -9216,10 +9240,10 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
       zeroPage(pRoot, ptfFlags);
       sqlite3PagerUnref(pRoot->pDbPage);
       assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 );
    -  *piTable = (int)pgnoRoot;
    +  *piTable = pgnoRoot;
       return SQLITE_OK;
     }
    -int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
    +int sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){
       int rc;
       sqlite3BtreeEnter(p);
       rc = btreeCreateTable(p, piTable, flags);
    @@ -9465,7 +9489,7 @@ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
     
       sqlite3BtreeEnter(p);
       assert( p->inTrans>TRANS_NONE );
    -  assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) );
    +  assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) );
       assert( pBt->pPage1 );
       assert( idx>=0 && idx<=15 );
     
    @@ -9514,7 +9538,6 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
       return rc;
     }
     
    -#ifndef SQLITE_OMIT_BTREECOUNT
     /*
     ** The first argument, pCur, is a cursor opened on some b-tree. Count the
     ** number of entries in the b-tree and write the result to *pnEntry.
    @@ -9536,7 +9559,7 @@ int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){
       /* Unless an error occurs, the following loop runs one iteration for each
       ** page in the B-Tree structure (not including overflow pages). 
       */
    -  while( rc==SQLITE_OK && !db->u1.isInterrupted ){
    +  while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){
         int iIdx;                          /* Index of child node in parent */
         MemPage *pPage;                    /* Current page of the b-tree */
     
    @@ -9587,7 +9610,6 @@ int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){
       /* An error has occurred. Return an error code. */
       return rc;
     }
    -#endif
     
     /*
     ** Return the pager associated with a BTree.  This routine is used for
    @@ -9620,7 +9642,7 @@ static void checkAppendMsg(
       sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
       va_end(ap);
       if( pCheck->errMsg.accError==SQLITE_NOMEM ){
    -    pCheck->mallocFailed = 1;
    +    pCheck->bOomFault = 1;
       }
     }
     #endif /* SQLITE_OMIT_INTEGRITY_CHECK */
    @@ -9662,7 +9684,7 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage){
         checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
         return 1;
       }
    -  if( pCheck->db->u1.isInterrupted ) return 1;
    +  if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1;
       setPageReferenced(pCheck, iPage);
       return 0;
     }
    @@ -9685,7 +9707,7 @@ static void checkPtrmap(
     
       rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
       if( rc!=SQLITE_OK ){
    -    if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
    +    if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1;
         checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
         return;
       }
    @@ -9705,7 +9727,7 @@ static void checkPtrmap(
     static void checkList(
       IntegrityCk *pCheck,  /* Integrity checking context */
       int isFreeList,       /* True for a freelist.  False for overflow page list */
    -  int iPage,            /* Page number for first page in the list */
    +  Pgno iPage,           /* Page number for first page in the list */
       u32 N                 /* Expected number of pages in the list */
     ){
       int i;
    @@ -9837,7 +9859,7 @@ static int btreeHeapPull(u32 *aHeap, u32 *pOut){
     */
     static int checkTreePage(
       IntegrityCk *pCheck,  /* Context for the sanity check */
    -  int iPage,            /* Page number of the page to check */
    +  Pgno iPage,           /* Page number of the page to check */
       i64 *piMinKey,        /* Write minimum integer primary key here */
       i64 maxKey            /* Error if integer primary key greater than this */
     ){
    @@ -9873,9 +9895,9 @@ static int checkTreePage(
       usableSize = pBt->usableSize;
       if( iPage==0 ) return 0;
       if( checkRef(pCheck, iPage) ) return 0;
    -  pCheck->zPfx = "Page %d: ";
    +  pCheck->zPfx = "Page %u: ";
       pCheck->v1 = iPage;
    -  if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
    +  if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
         checkAppendMsg(pCheck,
            "unable to get the page. error code=%d", rc);
         goto end_of_check;
    @@ -9900,7 +9922,7 @@ static int checkTreePage(
       hdr = pPage->hdrOffset;
     
       /* Set up for cell analysis */
    -  pCheck->zPfx = "On tree page %d cell %d: ";
    +  pCheck->zPfx = "On tree page %u cell %d: ";
       contentOffset = get2byteNotZero(&data[hdr+5]);
       assert( contentOffset<=usableSize );  /* Enforced by btreeInitPage() */
     
    @@ -9920,7 +9942,7 @@ static int checkTreePage(
         pgno = get4byte(&data[hdr+8]);
     #ifndef SQLITE_OMIT_AUTOVACUUM
         if( pBt->autoVacuum ){
    -      pCheck->zPfx = "On page %d at right child: ";
    +      pCheck->zPfx = "On page %u at right child: ";
           checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
         }
     #endif
    @@ -10061,7 +10083,7 @@ static int checkTreePage(
         while( btreeHeapPull(heap,&x) ){
           if( (prev&0xffff)>=(x>>16) ){
             checkAppendMsg(pCheck,
    -          "Multiple uses for byte %u of page %d", x>>16, iPage);
    +          "Multiple uses for byte %u of page %u", x>>16, iPage);
             break;
           }else{
             nFrag += (x>>16) - (prev&0xffff) - 1;
    @@ -10076,7 +10098,7 @@ static int checkTreePage(
         */
         if( heap[0]==0 && nFrag!=data[hdr+7] ){
           checkAppendMsg(pCheck,
    -          "Fragmentation of %d bytes reported as %d on page %d",
    +          "Fragmentation of %d bytes reported as %d on page %u",
               nFrag, data[hdr+7], iPage);
         }
       }
    @@ -10104,11 +10126,20 @@ end_of_check:
     ** allocation errors,  an error message held in memory obtained from
     ** malloc is returned if *pnErr is non-zero.  If *pnErr==0 then NULL is
     ** returned.  If a memory allocation error occurs, NULL is returned.
    +**
    +** If the first entry in aRoot[] is 0, that indicates that the list of
    +** root pages is incomplete.  This is a "partial integrity-check".  This
    +** happens when performing an integrity check on a single table.  The
    +** zero is skipped, of course.  But in addition, the freelist checks
    +** and the checks to make sure every page is referenced are also skipped,
    +** since obviously it is not possible to know which pages are covered by
    +** the unverified btrees.  Except, if aRoot[1] is 1, then the freelist
    +** checks are still performed.
     */
     char *sqlite3BtreeIntegrityCheck(
       sqlite3 *db,  /* Database connection that is running the check */
       Btree *p,     /* The btree to be checked */
    -  int *aRoot,   /* An array of root pages numbers for individual trees */
    +  Pgno *aRoot,  /* An array of root pages numbers for individual trees */
       int nRoot,    /* Number of entries in aRoot[] */
       int mxErr,    /* Stop reporting errors after this many */
       int *pnErr    /* Write number of errors seen to this variable */
    @@ -10118,7 +10149,17 @@ char *sqlite3BtreeIntegrityCheck(
       BtShared *pBt = p->pBt;
       u64 savedDbFlags = pBt->db->flags;
       char zErr[100];
    +  int bPartial = 0;            /* True if not checking all btrees */
    +  int bCkFreelist = 1;         /* True to scan the freelist */
       VVA_ONLY( int nRef );
    +  assert( nRoot>0 );
    +
    +  /* aRoot[0]==0 means this is a partial check */
    +  if( aRoot[0]==0 ){
    +    assert( nRoot>1 );
    +    bPartial = 1;
    +    if( aRoot[1]!=1 ) bCkFreelist = 0;
    +  }
     
       sqlite3BtreeEnter(p);
       assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
    @@ -10130,7 +10171,7 @@ char *sqlite3BtreeIntegrityCheck(
       sCheck.nPage = btreePagecount(sCheck.pBt);
       sCheck.mxErr = mxErr;
       sCheck.nErr = 0;
    -  sCheck.mallocFailed = 0;
    +  sCheck.bOomFault = 0;
       sCheck.zPfx = 0;
       sCheck.v1 = 0;
       sCheck.v2 = 0;
    @@ -10144,12 +10185,12 @@ char *sqlite3BtreeIntegrityCheck(
     
       sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
       if( !sCheck.aPgRef ){
    -    sCheck.mallocFailed = 1;
    +    sCheck.bOomFault = 1;
         goto integrity_ck_cleanup;
       }
       sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
       if( sCheck.heap==0 ){
    -    sCheck.mallocFailed = 1;
    +    sCheck.bOomFault = 1;
         goto integrity_ck_cleanup;
       }
     
    @@ -10158,29 +10199,33 @@ char *sqlite3BtreeIntegrityCheck(
     
       /* Check the integrity of the freelist
       */
    -  sCheck.zPfx = "Main freelist: ";
    -  checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
    -            get4byte(&pBt->pPage1->aData[36]));
    -  sCheck.zPfx = 0;
    +  if( bCkFreelist ){
    +    sCheck.zPfx = "Main freelist: ";
    +    checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
    +              get4byte(&pBt->pPage1->aData[36]));
    +    sCheck.zPfx = 0;
    +  }
     
       /* Check all the tables.
       */
     #ifndef SQLITE_OMIT_AUTOVACUUM
    -  if( pBt->autoVacuum ){
    -    int mx = 0;
    -    int mxInHdr;
    -    for(i=0; (int)ipPage1->aData[52]);
    -    if( mx!=mxInHdr ){
    +  if( !bPartial ){
    +    if( pBt->autoVacuum ){
    +      Pgno mx = 0;
    +      Pgno mxInHdr;
    +      for(i=0; (int)ipPage1->aData[52]);
    +      if( mx!=mxInHdr ){
    +        checkAppendMsg(&sCheck,
    +          "max rootpage (%d) disagrees with header (%d)",
    +          mx, mxInHdr
    +        );
    +      }
    +    }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
           checkAppendMsg(&sCheck,
    -        "max rootpage (%d) disagrees with header (%d)",
    -        mx, mxInHdr
    +        "incremental_vacuum enabled with a max rootpage of zero"
           );
         }
    -  }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
    -    checkAppendMsg(&sCheck,
    -      "incremental_vacuum enabled with a max rootpage of zero"
    -    );
       }
     #endif
       testcase( pBt->db->flags & SQLITE_CellSizeCk );
    @@ -10189,7 +10234,7 @@ char *sqlite3BtreeIntegrityCheck(
         i64 notUsed;
         if( aRoot[i]==0 ) continue;
     #ifndef SQLITE_OMIT_AUTOVACUUM
    -    if( pBt->autoVacuum && aRoot[i]>1 ){
    +    if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
           checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
         }
     #endif
    @@ -10199,24 +10244,26 @@ char *sqlite3BtreeIntegrityCheck(
     
       /* Make sure every page in the file is referenced
       */
    -  for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
    +  if( !bPartial ){
    +    for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
     #ifdef SQLITE_OMIT_AUTOVACUUM
    -    if( getPageReferenced(&sCheck, i)==0 ){
    -      checkAppendMsg(&sCheck, "Page %d is never used", i);
    -    }
    +      if( getPageReferenced(&sCheck, i)==0 ){
    +        checkAppendMsg(&sCheck, "Page %d is never used", i);
    +      }
     #else
    -    /* If the database supports auto-vacuum, make sure no tables contain
    -    ** references to pointer-map pages.
    -    */
    -    if( getPageReferenced(&sCheck, i)==0 && 
    -       (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
    -      checkAppendMsg(&sCheck, "Page %d is never used", i);
    -    }
    -    if( getPageReferenced(&sCheck, i)!=0 && 
    -       (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
    -      checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
    -    }
    +      /* If the database supports auto-vacuum, make sure no tables contain
    +      ** references to pointer-map pages.
    +      */
    +      if( getPageReferenced(&sCheck, i)==0 && 
    +         (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
    +        checkAppendMsg(&sCheck, "Page %d is never used", i);
    +      }
    +      if( getPageReferenced(&sCheck, i)!=0 && 
    +         (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
    +        checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
    +      }
     #endif
    +    }
       }
     
       /* Clean  up and report errors.
    @@ -10224,7 +10271,7 @@ char *sqlite3BtreeIntegrityCheck(
     integrity_ck_cleanup:
       sqlite3PageFree(sCheck.heap);
       sqlite3_free(sCheck.aPgRef);
    -  if( sCheck.mallocFailed ){
    +  if( sCheck.bOomFault ){
         sqlite3_str_reset(&sCheck.errMsg);
         sCheck.nErr++;
       }
    @@ -10344,13 +10391,13 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
     /*
     ** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared 
     ** btree as the argument handle holds an exclusive lock on the 
    -** sqlite_master table. Otherwise SQLITE_OK.
    +** sqlite_schema table. Otherwise SQLITE_OK.
     */
     int sqlite3BtreeSchemaLocked(Btree *p){
       int rc;
       assert( sqlite3_mutex_held(p->db->mutex) );
       sqlite3BtreeEnter(p);
    -  rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
    +  rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
       assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE );
       sqlite3BtreeLeave(p);
       return rc;
    diff --git a/src/btree.h b/src/btree.h
    index 4bd41f7..5dd5287 100644
    --- a/src/btree.h
    +++ b/src/btree.h
    @@ -71,20 +71,20 @@ int sqlite3BtreeSetSpillSize(Btree*,int);
     int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
     int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
     int sqlite3BtreeGetPageSize(Btree*);
    -int sqlite3BtreeMaxPageCount(Btree*,int);
    -u32 sqlite3BtreeLastPage(Btree*);
    +Pgno sqlite3BtreeMaxPageCount(Btree*,Pgno);
    +Pgno sqlite3BtreeLastPage(Btree*);
     int sqlite3BtreeSecureDelete(Btree*,int);
    -int sqlite3BtreeGetOptimalReserve(Btree*);
    +int sqlite3BtreeGetRequestedReserve(Btree*);
     int sqlite3BtreeGetReserveNoMutex(Btree *p);
     int sqlite3BtreeSetAutoVacuum(Btree *, int);
     int sqlite3BtreeGetAutoVacuum(Btree *);
     int sqlite3BtreeBeginTrans(Btree*,int,int*);
    -int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
    +int sqlite3BtreeCommitPhaseOne(Btree*, const char*);
     int sqlite3BtreeCommitPhaseTwo(Btree*, int);
     int sqlite3BtreeCommit(Btree*);
     int sqlite3BtreeRollback(Btree*,int,int);
     int sqlite3BtreeBeginStmt(Btree*,int);
    -int sqlite3BtreeCreateTable(Btree*, int*, int flags);
    +int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags);
     int sqlite3BtreeIsInTrans(Btree*);
     int sqlite3BtreeIsInReadTrans(Btree*);
     int sqlite3BtreeIsInBackup(Btree*);
    @@ -225,7 +225,7 @@ int sqlite3BtreeNewDb(Btree *p);
     
     int sqlite3BtreeCursor(
       Btree*,                              /* BTree containing table to open */
    -  int iTable,                          /* Index of root page */
    +  Pgno iTable,                         /* Index of root page */
       int wrFlag,                          /* 1 for writing.  0 for read-only */
       struct KeyInfo*,                     /* First argument to compare function */
       BtCursor *pCursor                    /* Space to write cursor structure */
    @@ -316,7 +316,7 @@ const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
     u32 sqlite3BtreePayloadSize(BtCursor*);
     sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*);
     
    -char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,int*aRoot,int nRoot,int,int*);
    +char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*);
     struct Pager *sqlite3BtreePager(Btree*);
     i64 sqlite3BtreeRowCountEst(BtCursor*);
     
    @@ -336,9 +336,7 @@ int sqlite3BtreeCursorIsValid(BtCursor*);
     #endif
     int sqlite3BtreeCursorIsValidNN(BtCursor*);
     
    -#ifndef SQLITE_OMIT_BTREECOUNT
     int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
    -#endif
     
     #ifdef SQLITE_TEST
     int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
    diff --git a/src/btreeInt.h b/src/btreeInt.h
    index 7687a0f..c6dfa27 100644
    --- a/src/btreeInt.h
    +++ b/src/btreeInt.h
    @@ -381,7 +381,7 @@ struct Btree {
     **
     ** Fields in this structure are accessed under the BtShared.mutex
     ** mutex, except for nRef and pNext which are accessed under the
    -** global SQLITE_MUTEX_STATIC_MASTER mutex.  The pPager field
    +** global SQLITE_MUTEX_STATIC_MAIN mutex.  The pPager field
     ** may not be modified once it is initially set as long as nRef>0.
     ** The pSchema field may be set once under BtShared.mutex and
     ** thereafter is unchanged as long as nRef>0.
    @@ -417,9 +417,7 @@ struct BtShared {
     #endif
       u8 inTransaction;     /* Transaction state */
       u8 max1bytePayload;   /* Maximum first byte of cell for a 1-byte payload */
    -#ifdef SQLITE_HAS_CODEC
    -  u8 optimalReserve;    /* Desired amount of reserved space per page */
    -#endif
    +  u8 nReserveWanted;    /* Desired number of extra bytes per page */
       u16 btsFlags;         /* Boolean parameters.  See BTS_* macros below */
       u16 maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
       u16 minLocal;         /* Minimum local payload in non-LEAFDATA tables */
    @@ -681,9 +679,10 @@ struct IntegrityCk {
       Pgno nPage;       /* Number of pages in the database */
       int mxErr;        /* Stop accumulating errors when this reaches zero */
       int nErr;         /* Number of messages written to zErrMsg so far */
    -  int mallocFailed; /* A memory allocation error has occurred */
    +  int bOomFault;    /* A memory allocation error has occurred */
       const char *zPfx; /* Error message prefix */
    -  int v1, v2;       /* Values for up to two %d fields in zPfx */
    +  Pgno v1;          /* Value for first %u substitution in zPfx */
    +  int v2;           /* Value for second %d substitution in zPfx */
       StrAccum errMsg;  /* Accumulate the error message text here */
       u32 *heap;        /* Min-heap used for analyzing cell coverage */
       sqlite3 *db;      /* Database connection running the check */
    diff --git a/src/build.c b/src/build.c
    index a7dc36f..aa0f919 100644
    --- a/src/build.c
    +++ b/src/build.c
    @@ -31,7 +31,7 @@
     */
     struct TableLock {
       int iDb;               /* The database containing the table to be locked */
    -  int iTab;              /* The root page of the table to be locked */
    +  Pgno iTab;             /* The root page of the table to be locked */
       u8 isWriteLock;        /* True for write lock.  False for a read lock */
       const char *zLockName; /* Name of the table */
     };
    @@ -49,7 +49,7 @@ struct TableLock {
     void sqlite3TableLock(
       Parse *pParse,     /* Parsing context */
       int iDb,           /* Index of the database containing the table to lock */
    -  int iTab,          /* Root page number of the table to be locked */
    +  Pgno iTab,         /* Root page number of the table to be locked */
       u8 isWriteLock,    /* True for a write lock */
       const char *zName  /* Name of the table to be locked */
     ){
    @@ -207,12 +207,21 @@ void sqlite3FinishCoding(Parse *pParse){
           */
           sqlite3AutoincrementBegin(pParse);
     
    -      /* Code constant expressions that where factored out of inner loops */
    +      /* Code constant expressions that where factored out of inner loops.
    +      **
    +      ** The pConstExpr list might also contain expressions that we simply
    +      ** want to keep around until the Parse object is deleted.  Such
    +      ** expressions have iConstExprReg==0.  Do not generate code for
    +      ** those expressions, of course.
    +      */
           if( pParse->pConstExpr ){
             ExprList *pEL = pParse->pConstExpr;
             pParse->okConstFactor = 0;
             for(i=0; inExpr; i++){
    -          sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
    +          int iReg = pEL->a[i].u.iConstExprReg;
    +          if( iReg>0 ){
    +            sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
    +          }
             }
           }
     
    @@ -244,7 +253,7 @@ void sqlite3FinishCoding(Parse *pParse){
     ** outermost parser.
     **
     ** Not everything is nestable.  This facility is designed to permit
    -** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER.  Use
    +** INSERT, UPDATE, and DELETE operations against the schema table.  Use
     ** care if you decide to try to use this routine for some other purposes.
     */
     void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
    @@ -312,22 +321,59 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
         return 0;
       }
     #endif
    -  while(1){
    -    for(i=OMIT_TEMPDB; inDb; i++){
    -      int j = (i<2) ? i^1 : i;   /* Search TEMP before MAIN */
    -      if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
    -        assert( sqlite3SchemaMutexHeld(db, j, 0) );
    -        p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
    -        if( p ) return p;
    +  if( zDatabase ){
    +    for(i=0; inDb; i++){
    +      if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break;
    +    }
    +    if( i>=db->nDb ){
    +      /* No match against the official names.  But always match "main"
    +      ** to schema 0 as a legacy fallback. */
    +      if( sqlite3StrICmp(zDatabase,"main")==0 ){
    +        i = 0;
    +      }else{
    +        return 0;
    +      }
    +    }
    +    p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName);
    +    if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
    +      if( i==1 ){
    +        if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0
    +         || sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0
    +         || sqlite3StrICmp(zName+7, &DFLT_SCHEMA_TABLE[7])==0
    +        ){
    +          p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, 
    +                              DFLT_TEMP_SCHEMA_TABLE);
    +        }
    +      }else{
    +        if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){
    +          p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash,
    +                              DFLT_SCHEMA_TABLE);
    +        }
    +      }
    +    }
    +  }else{
    +    /* Match against TEMP first */
    +    p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, zName);
    +    if( p ) return p;
    +    /* The main database is second */
    +    p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, zName);
    +    if( p ) return p;
    +    /* Attached databases are in order of attachment */
    +    for(i=2; inDb; i++){
    +      assert( sqlite3SchemaMutexHeld(db, i, 0) );
    +      p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName);
    +      if( p ) break;
    +    }
    +    if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
    +      if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){
    +        p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, DFLT_SCHEMA_TABLE);
    +      }else if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 ){
    +        p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, 
    +                            DFLT_TEMP_SCHEMA_TABLE);
           }
         }
    -    /* Not found.  If the name we were looking for was temp.sqlite_master
    -    ** then change the name to sqlite_temp_master and try again. */
    -    if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break;
    -    if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break;
    -    zName = TEMP_MASTER_NAME;
       }
    -  return 0;
    +  return p;
     }
     
     /*
    @@ -437,7 +483,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
         int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */
         Schema *pSchema = db->aDb[j].pSchema;
         assert( pSchema );
    -    if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
    +    if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue;
         assert( sqlite3SchemaMutexHeld(db, j, 0) );
         p = sqlite3HashFind(&pSchema->idxHash, zName);
         if( p ) break;
    @@ -590,6 +636,7 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
       assert( pTable!=0 );
       if( (pCol = pTable->aCol)!=0 ){
         for(i=0; inCol; i++, pCol++){
    +      assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) );
           sqlite3DbFree(db, pCol->zName);
           sqlite3ExprDelete(db, pCol->pDflt);
           sqlite3DbFree(db, pCol->zColl);
    @@ -716,13 +763,13 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
     }
     
     /*
    -** Open the sqlite_master table stored in database number iDb for
    +** Open the sqlite_schema table stored in database number iDb for
     ** writing. The table is opened using cursor 0.
     */
    -void sqlite3OpenMasterTable(Parse *p, int iDb){
    +void sqlite3OpenSchemaTable(Parse *p, int iDb){
       Vdbe *v = sqlite3GetVdbe(p);
    -  sqlite3TableLock(p, iDb, MASTER_ROOT, 1, MASTER_NAME);
    -  sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5);
    +  sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, DFLT_SCHEMA_TABLE);
    +  sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5);
       if( p->nTab==0 ){
         p->nTab = 1;
       }
    @@ -830,7 +877,7 @@ int sqlite3WritableSchema(sqlite3 *db){
     ** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
     ** is reserved for internal use.
     **
    -** When parsing the sqlite_master table, this routine also checks to
    +** When parsing the sqlite_schema table, this routine also checks to
     ** make sure the "type", "name", and "tbl_name" columns are consistent
     ** with the SQL.
     */
    @@ -841,7 +888,10 @@ int sqlite3CheckObjectName(
       const char *zTblName      /* Parent table name for triggers and indexes */
     ){
       sqlite3 *db = pParse->db;
    -  if( sqlite3WritableSchema(db) || db->init.imposterTable ){
    +  if( sqlite3WritableSchema(db)
    +   || db->init.imposterTable
    +   || !sqlite3Config.bExtraSchemaChecks
    +  ){
         /* Skip these error checks for writable_schema=ON */
         return SQLITE_OK;
       }
    @@ -850,10 +900,8 @@ int sqlite3CheckObjectName(
          || sqlite3_stricmp(zName, db->init.azInit[1])
          || sqlite3_stricmp(zTblName, db->init.azInit[2])
         ){
    -      if( sqlite3Config.bExtraSchemaChecks ){
    -        sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
    -        return SQLITE_ERROR;
    -      }
    +      sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
    +      return SQLITE_ERROR;
         }
       }else{
         if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7))
    @@ -1002,7 +1050,7 @@ void sqlite3StartTable(
       Token *pName;    /* Unqualified name of the table to create */
     
       if( db->init.busy && db->init.newTnum==1 ){
    -    /* Special case:  Parsing the sqlite_master or sqlite_temp_master schema */
    +    /* Special case:  Parsing the sqlite_schema or sqlite_temp_schema schema */
         iDb = db->init.iDb;
         zName = sqlite3DbStrDup(db, SCHEMA_TABLE(iDb));
         pName = pName1;
    @@ -1108,7 +1156,7 @@ void sqlite3StartTable(
     #endif
     
       /* Begin generating the code that will insert the table record into
    -  ** the SQLITE_MASTER table.  Note in particular that we must go ahead
    +  ** the schema table.  Note in particular that we must go ahead
       ** and allocate the record number for the table entry now.  Before any
       ** PRIMARY KEY or UNIQUE keywords are parsed.  Those keywords will cause
       ** indices to be created and the table record must come before the 
    @@ -1144,7 +1192,7 @@ void sqlite3StartTable(
         sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, ENC(db));
         sqlite3VdbeJumpHere(v, addr1);
     
    -    /* This just creates a place-holder record in the sqlite_master table.
    +    /* This just creates a place-holder record in the sqlite_schema table.
         ** The record created does not contain anything yet.  It will be replaced
         ** by the real entry in code generated at sqlite3EndTable().
         **
    @@ -1162,7 +1210,7 @@ void sqlite3StartTable(
           pParse->addrCrTab =
              sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
         }
    -    sqlite3OpenMasterTable(pParse, iDb);
    +    sqlite3OpenSchemaTable(pParse, iDb);
         sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
         sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
         sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
    @@ -1238,6 +1286,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
       pCol = &p->aCol[p->nCol];
       memset(pCol, 0, sizeof(p->aCol[0]));
       pCol->zName = z;
    +  pCol->hName = sqlite3StrIHash(z);
       sqlite3ColumnPropertiesFromName(p, pCol);
      
       if( pType->n==0 ){
    @@ -1971,9 +2020,9 @@ static void recomputeColumnsNotIndexed(Index *pIdx){
     **     (1)  Set all columns of the PRIMARY KEY schema object to be NOT NULL.
     **     (2)  Convert P3 parameter of the OP_CreateBtree from BTREE_INTKEY 
     **          into BTREE_BLOBKEY.
    -**     (3)  Bypass the creation of the sqlite_master table entry
    +**     (3)  Bypass the creation of the sqlite_schema table entry
     **          for the PRIMARY KEY as the primary key index is now
    -**          identified by the sqlite_master table entry of the table itself.
    +**          identified by the sqlite_schema table entry of the table itself.
     **     (4)  Set the Index.tnum of the PRIMARY KEY Index object in the
     **          schema to the rootpage from the main table.
     **     (5)  Add all table columns to the PRIMARY KEY Index object
    @@ -2060,13 +2109,13 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
       if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
       nPk = pPk->nColumn = pPk->nKeyCol;
     
    -  /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
    +  /* Bypass the creation of the PRIMARY KEY btree and the sqlite_schema
       ** table entry. This is only required if currently generating VDBE
       ** code for a CREATE TABLE (not when parsing one as part of reading
       ** a database schema).  */
       if( v && pPk->tnum>0 ){
         assert( db->init.busy==0 );
    -    sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto);
    +    sqlite3VdbeChangeOpcode(v, (int)pPk->tnum, OP_Goto);
       }
     
       /* The root page of the PRIMARY KEY is the table root page */
    @@ -2129,6 +2178,28 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
       recomputeColumnsNotIndexed(pPk);
     }
     
    +
    +#ifndef SQLITE_OMIT_VIRTUALTABLE
    +/*
    +** Return true if pTab is a virtual table and zName is a shadow table name
    +** for that virtual table.
    +*/
    +int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char *zName){
    +  int nName;                    /* Length of zName */
    +  Module *pMod;                 /* Module for the virtual table */
    +
    +  if( !IsVirtual(pTab) ) return 0;
    +  nName = sqlite3Strlen30(pTab->zName);
    +  if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0;
    +  if( zName[nName]!='_' ) return 0;
    +  pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
    +  if( pMod==0 ) return 0;
    +  if( pMod->pModule->iVersion<3 ) return 0;
    +  if( pMod->pModule->xShadowName==0 ) return 0;
    +  return pMod->pModule->xShadowName(zName+nName+1);
    +}
    +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
    +
     #ifndef SQLITE_OMIT_VIRTUALTABLE
     /*
     ** Return true if zName is a shadow table name in the current database
    @@ -2140,8 +2211,6 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
     int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
       char *zTail;                  /* Pointer to the last "_" in zName */
       Table *pTab;                  /* Table that zName is a shadow of */
    -  Module *pMod;                 /* Module for the virtual table */
    -
       zTail = strrchr(zName, '_');
       if( zTail==0 ) return 0;
       *zTail = 0;
    @@ -2149,14 +2218,37 @@ int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
       *zTail = '_';
       if( pTab==0 ) return 0;
       if( !IsVirtual(pTab) ) return 0;
    -  pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
    -  if( pMod==0 ) return 0;
    -  if( pMod->pModule->iVersion<3 ) return 0;
    -  if( pMod->pModule->xShadowName==0 ) return 0;
    -  return pMod->pModule->xShadowName(zTail+1);
    +  return sqlite3IsShadowTableOf(db, pTab, zName);
     }
     #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
     
    +
    +#ifdef SQLITE_DEBUG
    +/*
    +** Mark all nodes of an expression as EP_Immutable, indicating that
    +** they should not be changed.  Expressions attached to a table or
    +** index definition are tagged this way to help ensure that we do
    +** not pass them into code generator routines by mistake.
    +*/
    +static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){
    +  ExprSetVVAProperty(pExpr, EP_Immutable);
    +  return WRC_Continue;
    +}
    +static void markExprListImmutable(ExprList *pList){
    +  if( pList ){
    +    Walker w;
    +    memset(&w, 0, sizeof(w));
    +    w.xExprCallback = markImmutableExprStep;
    +    w.xSelectCallback = sqlite3SelectWalkNoop;
    +    w.xSelectCallback2 = 0;
    +    sqlite3WalkExprList(&w, pList);
    +  }
    +}
    +#else
    +#define markExprListImmutable(X)  /* no-op */
    +#endif /* SQLITE_DEBUG */
    +
    +
     /*
     ** This routine is called to report the final ")" that terminates
     ** a CREATE TABLE statement.
    @@ -2165,12 +2257,12 @@ int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
     ** is added to the internal hash tables, assuming no errors have
     ** occurred.
     **
    -** An entry for the table is made in the master table on disk, unless
    +** An entry for the table is made in the schema table on disk, unless
     ** this is a temporary table or db->init.busy==1.  When db->init.busy==1
    -** it means we are reading the sqlite_master table because we just
    -** connected to the database or because the sqlite_master table has
    +** it means we are reading the sqlite_schema table because we just
    +** connected to the database or because the sqlite_schema table has
     ** recently changed, so the entry for this table already exists in
    -** the sqlite_master table.  We do not want to create it again.
    +** the sqlite_schema table.  We do not want to create it again.
     **
     ** If the pSelect argument is not NULL, it means that this routine
     ** was called to create a table generated from a 
    @@ -2201,12 +2293,12 @@ void sqlite3EndTable(
       }
     
       /* If the db->init.busy is 1 it means we are reading the SQL off the
    -  ** "sqlite_master" or "sqlite_temp_master" table on the disk.
    +  ** "sqlite_schema" or "sqlite_temp_schema" table on the disk.
       ** So do not write to the disk again.  Extract the root page number
       ** for the table from the db->init.newTnum field.  (The page number
       ** should have been put there by the sqliteOpenCb routine.)
       **
    -  ** If the root page number is 1, that means this is the sqlite_master
    +  ** If the root page number is 1, that means this is the sqlite_schema
       ** table itself.  So mark it read-only.
       */
       if( db->init.busy ){
    @@ -2249,6 +2341,8 @@ void sqlite3EndTable(
           ** actually be used if PRAGMA writable_schema=ON is set. */
           sqlite3ExprListDelete(db, p->pCheck);
           p->pCheck = 0;
    +    }else{
    +      markExprListImmutable(p->pCheck);
         }
       }
     #endif /* !defined(SQLITE_OMIT_CHECK) */
    @@ -2291,7 +2385,7 @@ void sqlite3EndTable(
       }
     
       /* If not initializing, then create a record for the new table
    -  ** in the SQLITE_MASTER table of the database.
    +  ** in the schema table of the database.
       **
       ** If this is a TEMPORARY table, write the entry into the auxiliary
       ** file instead of into the main database file.
    @@ -2393,14 +2487,14 @@ void sqlite3EndTable(
         }
     
         /* A slot for the record has already been allocated in the 
    -    ** SQLITE_MASTER table.  We just need to update that slot with all
    +    ** schema table.  We just need to update that slot with all
         ** the information we've collected.
         */
         sqlite3NestedParse(pParse,
    -      "UPDATE %Q.%s "
    -         "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
    -       "WHERE rowid=#%d",
    -      db->aDb[iDb].zDbSName, MASTER_NAME,
    +      "UPDATE %Q." DFLT_SCHEMA_TABLE
    +      " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q"
    +      " WHERE rowid=#%d",
    +      db->aDb[iDb].zDbSName,
           zType,
           p->zName,
           p->zName,
    @@ -2528,7 +2622,7 @@ void sqlite3CreateView(
       sEnd.z = &z[n-1];
       sEnd.n = 1;
     
    -  /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
    +  /* Use sqlite3EndTable() to add the view to the schema table */
       sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
     
     create_view_fail:
    @@ -2609,10 +2703,8 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
       assert( pTable->pSelect );
       pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
       if( pSel ){
    -#ifndef SQLITE_OMIT_ALTERTABLE
         u8 eParseMode = pParse->eParseMode;
         pParse->eParseMode = PARSE_MODE_NORMAL;
    -#endif
         n = pParse->nTab;
         sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
         pTable->nCol = -1;
    @@ -2660,9 +2752,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
         sqlite3DeleteTable(db, pSelTab);
         sqlite3SelectDelete(db, pSel);
         EnableLookaside;
    -#ifndef SQLITE_OMIT_ALTERTABLE
         pParse->eParseMode = eParseMode;
    -#endif
       } else {
         nErr++;
       }
    @@ -2717,7 +2807,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
     ** in order to be certain that we got the right one.
     */
     #ifndef SQLITE_OMIT_AUTOVACUUM
    -void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
    +void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){
       HashElem *pElem;
       Hash *pHash;
       Db *pDb;
    @@ -2743,7 +2833,7 @@ void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
     
     /*
     ** Write code to erase the table with root-page iTable from database iDb.
    -** Also write code to modify the sqlite_master table and internal schema
    +** Also write code to modify the sqlite_schema table and internal schema
     ** if a root-page of another table is moved by the btree-layer whilst
     ** erasing iTable (this can happen with an auto-vacuum database).
     */ 
    @@ -2756,7 +2846,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
     #ifndef SQLITE_OMIT_AUTOVACUUM
       /* OP_Destroy stores an in integer r1. If this integer
       ** is non-zero, then it is the root page number of a table moved to
    -  ** location iTable. The following code modifies the sqlite_master table to
    +  ** location iTable. The following code modifies the sqlite_schema table to
       ** reflect this.
       **
       ** The "#NNN" in the SQL is a special constant that means whatever value
    @@ -2764,15 +2854,16 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
       ** token for additional information.
       */
       sqlite3NestedParse(pParse, 
    -     "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
    -     pParse->db->aDb[iDb].zDbSName, MASTER_NAME, iTable, r1, r1);
    +     "UPDATE %Q." DFLT_SCHEMA_TABLE
    +     " SET rootpage=%d WHERE #%d AND rootpage=#%d",
    +     pParse->db->aDb[iDb].zDbSName, iTable, r1, r1);
     #endif
       sqlite3ReleaseTempReg(pParse, r1);
     }
     
     /*
     ** Write VDBE code to erase table pTab and all associated indices on disk.
    -** Code to update the sqlite_master tables and internal schema definitions
    +** Code to update the sqlite_schema tables and internal schema definitions
     ** in case a root-page belonging to another table is moved by the btree layer
     ** is also added (this can happen with an auto-vacuum database).
     */
    @@ -2793,18 +2884,18 @@ static void destroyTable(Parse *pParse, Table *pTab){
       ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
       ** a free-list page.
       */
    -  int iTab = pTab->tnum;
    -  int iDestroyed = 0;
    +  Pgno iTab = pTab->tnum;
    +  Pgno iDestroyed = 0;
     
       while( 1 ){
         Index *pIdx;
    -    int iLargest = 0;
    +    Pgno iLargest = 0;
     
         if( iDestroyed==0 || iTabpIndex; pIdx; pIdx=pIdx->pNext){
    -      int iIdx = pIdx->tnum;
    +      Pgno iIdx = pIdx->tnum;
           assert( pIdx->pSchema==pTab->pSchema );
           if( (iDestroyed==0 || (iIdxiLargest ){
             iLargest = iIdx;
    @@ -2865,8 +2956,8 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
     #endif
     
       /* Drop all triggers associated with the table being dropped. Code
    -  ** is generated to remove entries from sqlite_master and/or
    -  ** sqlite_temp_master if required.
    +  ** is generated to remove entries from sqlite_schema and/or
    +  ** sqlite_temp_schema if required.
       */
       pTrigger = sqlite3TriggerList(pParse, pTab);
       while( pTrigger ){
    @@ -2890,16 +2981,17 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
       }
     #endif
     
    -  /* Drop all SQLITE_MASTER table and index entries that refer to the
    -  ** table. The program name loops through the master table and deletes
    +  /* Drop all entries in the schema table that refer to the
    +  ** table. The program name loops through the schema table and deletes
       ** every row that refers to a table of the same name as the one being
       ** dropped. Triggers are handled separately because a trigger can be
       ** created in the temp database that refers to a table in another
       ** database.
       */
       sqlite3NestedParse(pParse, 
    -      "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
    -      pDb->zDbSName, MASTER_NAME, pTab->zName);
    +      "DELETE FROM %Q." DFLT_SCHEMA_TABLE
    +      " WHERE tbl_name=%Q and type!='trigger'",
    +      pDb->zDbSName, pTab->zName);
       if( !isView && !IsVirtual(pTab) ){
         destroyTable(pParse, pTab);
       }
    @@ -3035,7 +3127,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
       }
     #endif
     
    -  /* Generate code to remove the table from the master table
    +  /* Generate code to remove the table from the schema table
       ** on disk.
       */
       v = sqlite3GetVdbe(pParse);
    @@ -3226,7 +3318,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
       int iSorter;                   /* Cursor opened by OpenSorter (if in use) */
       int addr1;                     /* Address of top of loop */
       int addr2;                     /* Address to jump to for next iteration */
    -  int tnum;                      /* Root page of index */
    +  Pgno tnum;                     /* Root page of index */
       int iPartIdxLabel;             /* Jump to this label to skip a row */
       Vdbe *v;                       /* Generate code into this virtual machine */
       KeyInfo *pKey;                 /* KeyInfo for index */
    @@ -3247,7 +3339,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
       v = sqlite3GetVdbe(pParse);
       if( v==0 ) return;
       if( memRootPage>=0 ){
    -    tnum = memRootPage;
    +    tnum = (Pgno)memRootPage;
       }else{
         tnum = pIndex->tnum;
       }
    @@ -3272,7 +3364,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
       sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v);
       sqlite3VdbeJumpHere(v, addr1);
       if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
    -  sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, 
    +  sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, (int)tnum, iDb, 
                         (char *)pKey, P4_KEYINFO);
       sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
     
    @@ -3489,10 +3581,7 @@ void sqlite3CreateIndex(
     #if SQLITE_USER_AUTHENTICATION
            && sqlite3UserAuthTable(pTab->zName)==0
     #endif
    -#ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX
    -       && sqlite3StrICmp(&pTab->zName[7],"master")!=0
    -#endif
    - ){
    +  ){
         sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
         goto exit_create_index;
       }
    @@ -3514,7 +3603,7 @@ void sqlite3CreateIndex(
       ** index or table with the same name.  
       **
       ** Exception:  If we are reading the names of permanent indices from the
    -  ** sqlite_master table (because some other process changed the schema) and
    +  ** sqlite_schema table (because some other process changed the schema) and
       ** one of the index names collides with the name of a temporary table or
       ** index, then we will continue to process this index.
       **
    @@ -3858,8 +3947,8 @@ void sqlite3CreateIndex(
         /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
         ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
         ** emit code to allocate the index rootpage on disk and make an entry for
    -    ** the index in the sqlite_master table and populate the index with
    -    ** content.  But, do not do this if we are simply reading the sqlite_master
    +    ** the index in the sqlite_schema table and populate the index with
    +    ** content.  But, do not do this if we are simply reading the sqlite_schema
         ** table to parse the schema, or if this index is the PRIMARY KEY index
         ** of a WITHOUT ROWID table.
         **
    @@ -3884,7 +3973,7 @@ void sqlite3CreateIndex(
           ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In 
           ** that case the convertToWithoutRowidTable() routine will replace
           ** the Noop with a Goto to jump over the VDBE code generated below. */
    -      pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
    +      pIndex->tnum = (Pgno)sqlite3VdbeAddOp0(v, OP_Noop);
           sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY);
     
           /* Gather the complete text of the CREATE INDEX statement into
    @@ -3903,11 +3992,11 @@ void sqlite3CreateIndex(
             zStmt = 0;
           }
     
    -      /* Add an entry in sqlite_master for this index
    +      /* Add an entry in sqlite_schema for this index
           */
           sqlite3NestedParse(pParse, 
    -          "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
    -          db->aDb[iDb].zDbSName, MASTER_NAME,
    +          "INSERT INTO %Q." DFLT_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);",
    +          db->aDb[iDb].zDbSName,
               pIndex->zName,
               pTab->zName,
               iMem,
    @@ -3926,7 +4015,7 @@ void sqlite3CreateIndex(
             sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
           }
     
    -      sqlite3VdbeJumpHere(v, pIndex->tnum);
    +      sqlite3VdbeJumpHere(v, (int)pIndex->tnum);
         }
       }
       if( db->init.busy || pTblName==0 ){
    @@ -3983,9 +4072,10 @@ exit_create_index:
     ** are based on typical values found in actual indices.
     */
     void sqlite3DefaultRowEst(Index *pIdx){
    -  /*                10,  9,  8,  7,  6 */
    -  LogEst aVal[] = { 33, 32, 30, 28, 26 };
    +               /*                10,  9,  8,  7,  6 */
    +  static const LogEst aVal[] = { 33, 32, 30, 28, 26 };
       LogEst *a = pIdx->aiRowLogEst;
    +  LogEst x;
       int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
       int i;
     
    @@ -3994,10 +4084,21 @@ void sqlite3DefaultRowEst(Index *pIdx){
     
       /* Set the first entry (number of rows in the index) to the estimated 
       ** number of rows in the table, or half the number of rows in the table
    -  ** for a partial index.   But do not let the estimate drop below 10. */
    -  a[0] = pIdx->pTable->nRowLogEst;
    -  if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10;  assert( 10==sqlite3LogEst(2) );
    -  if( a[0]<33 ) a[0] = 33;                  assert( 33==sqlite3LogEst(10) );
    +  ** for a partial index.
    +  **
    +  ** 2020-05-27:  If some of the stat data is coming from the sqlite_stat1
    +  ** table but other parts we are having to guess at, then do not let the
    +  ** estimated number of rows in the table be less than 1000 (LogEst 99).
    +  ** Failure to do this can cause the indexes for which we do not have
    +  ** stat1 data to be ignored by the query planner.
    +  */
    +  x = pIdx->pTable->nRowLogEst;
    +  assert( 99==sqlite3LogEst(1000) );
    +  if( x<99 ){
    +    pIdx->pTable->nRowLogEst = x = 99;
    +  }
    +  if( pIdx->pPartIdxWhere!=0 ) x -= 10;  assert( 10==sqlite3LogEst(2) );
    +  a[0] = x;
     
       /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
       ** 6 and each subsequent value (if any) is 5.  */
    @@ -4060,13 +4161,13 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
       }
     #endif
     
    -  /* Generate code to remove the index and from the master table */
    +  /* Generate code to remove the index and from the schema table */
       v = sqlite3GetVdbe(pParse);
       if( v ){
         sqlite3BeginWriteOperation(pParse, 1, iDb);
         sqlite3NestedParse(pParse,
    -       "DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
    -       db->aDb[iDb].zDbSName, MASTER_NAME, pIndex->zName
    +       "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='index'",
    +       db->aDb[iDb].zDbSName, pIndex->zName
         );
         sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
         sqlite3ChangeCookie(pParse, iDb);
    @@ -4465,6 +4566,26 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
       }
     }
     
    +/*
    +** Append the contents of SrcList p2 to SrcList p1 and return the resulting
    +** SrcList. Or, if an error occurs, return NULL. In all cases, p1 and p2
    +** are deleted by this function.
    +*/ 
    +SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
    +  assert( p1 && p1->nSrc==1 );
    +  if( p2 ){
    +    SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1);
    +    if( pNew==0 ){
    +      sqlite3SrcListDelete(pParse->db, p2);
    +    }else{
    +      p1 = pNew;
    +      memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item));
    +      sqlite3DbFree(pParse->db, p2);
    +    }
    +  }
    +  return p1;
    +}
    +
     /*
     ** Add the list of function arguments to the SrcList entry for a
     ** table-valued-function.
    @@ -4600,7 +4721,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){
         }
         db->aDb[1].pBt = pBt;
         assert( db->aDb[1].pSchema );
    -    if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
    +    if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){
           sqlite3OomFault(db);
           return 1;
         }
    @@ -4711,7 +4832,7 @@ void sqlite3HaltConstraint(
       u8 p5Errmsg       /* P5_ErrMsg type */
     ){
       Vdbe *v = sqlite3GetVdbe(pParse);
    -  assert( (errCode&0xff)==SQLITE_CONSTRAINT );
    +  assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
       if( onError==OE_Abort ){
         sqlite3MayAbort(pParse);
       }
    diff --git a/src/callback.c b/src/callback.c
    index 3d99190..421cef2 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -163,17 +163,30 @@ CollSeq *sqlite3FindCollSeq(
       int create            /* True to create CollSeq if doesn't already exist */
     ){
       CollSeq *pColl;
    +  assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
    +  assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
       if( zName ){
         pColl = findCollSeqEntry(db, zName, create);
    +    if( pColl ) pColl += enc-1;
       }else{
         pColl = db->pDfltColl;
       }
    -  assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
    -  assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
    -  if( pColl ) pColl += enc-1;
       return pColl;
     }
     
    +/*
    +** Change the text encoding for a database connection. This means that
    +** the pDfltColl must change as well.
    +*/
    +void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){
    +  assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
    +  db->enc = enc;
    +  /* EVIDENCE-OF: R-08308-17224 The default collating function for all
    +  ** strings is BINARY. 
    +  */
    +  db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0);
    +}
    +
     /*
     ** This function is responsible for invoking the collation factory callback
     ** or substituting a collation sequence of a different encoding when the
    diff --git a/src/crypto.c b/src/crypto.c
    index ea6dc54..3c433a6 100644
    --- a/src/crypto.c
    +++ b/src/crypto.c
    @@ -1,4 +1,4 @@
    -/* 
    +/*
     ** SQLCipher
     ** http://sqlcipher.net
     ** 
    @@ -119,7 +119,10 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
       } else
       if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && zRight ) {
         if(ctx) {
    +      char *deprecation = "PRAGMA cipher_store_pass is deprecated, please remove from use";
           sqlcipher_codec_set_store_pass(ctx, sqlite3GetBoolean(zRight, 1));
    +      codec_vdbe_return_string(pParse, "cipher_store_pass", deprecation, P4_TRANSIENT);
    +      sqlite3_log(SQLITE_WARNING, deprecation);
         }
       } else
       if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) {
    @@ -318,7 +321,6 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
         } else {
           char *size = sqlite3_mprintf("%d", sqlcipher_get_default_plaintext_header_size());
           codec_vdbe_return_string(pParse, "cipher_default_plaintext_header_size", size, P4_DYNAMIC);
    -      sqlite3_free(size);
         }
       }else
       if( sqlite3StrICmp(zLeft,"cipher_salt")==0 ){
    @@ -1026,7 +1028,6 @@ void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **ar
       int saved_nChange = db->nChange;      /* Saved value of db->nChange */
       int saved_nTotalChange = db->nTotalChange; /* Saved value of db->nTotalChange */
       u8 saved_mTrace = db->mTrace;        /* Saved value of db->mTrace */
    -  int (*saved_xTrace)(u32,void*,void*,void*) = db->xTrace; /* Saved db->xTrace */
       int rc = SQLITE_OK;     /* Return code from service routines */
       char *zSql = NULL;         /* SQL statements */
       char *pzErrMsg = NULL;
    @@ -1053,7 +1054,6 @@ void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **ar
       db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; 
       db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
       db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows); 
    -  db->xTrace = 0;
       db->mTrace = 0;
     
       /* Query the schema of the main database. Create a mirror schema
    @@ -1133,7 +1133,6 @@ end_of_export:
       db->mDbFlags = saved_mDbFlags;
       db->nChange = saved_nChange;
       db->nTotalChange = saved_nTotalChange;
    -  db->xTrace = saved_xTrace;
       db->mTrace = saved_mTrace;
     
       if(zSql) sqlite3_free(zSql);
    diff --git a/src/crypto.h b/src/crypto.h
    index b833a3d..f0fab61 100644
    --- a/src/crypto.h
    +++ b/src/crypto.h
    @@ -59,7 +59,7 @@ void sqlite3pager_reset(Pager *pPager);
     #define CIPHER_STR(s) #s
     
     #ifndef CIPHER_VERSION_NUMBER
    -#define CIPHER_VERSION_NUMBER 4.4.0
    +#define CIPHER_VERSION_NUMBER 4.4.1
     #endif
     
     #ifndef CIPHER_VERSION_BUILD
    diff --git a/src/crypto_impl.c b/src/crypto_impl.c
    index a6ce8ac..71d9eb8 100644
    --- a/src/crypto_impl.c
    +++ b/src/crypto_impl.c
    @@ -1419,7 +1419,7 @@ migrate:
       pDb = &(db->aDb[db->nDb-1]);
       pSrc = pDb->pBt;
     
    -  nRes = sqlite3BtreeGetOptimalReserve(pSrc); 
    +  nRes = sqlite3BtreeGetRequestedReserve(pSrc);
       /* unset the BTS_PAGESIZE_FIXED flag to avoid SQLITE_READONLY */
       pDest->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; 
       rc = sqlite3BtreeSetPageSize(pDest, default_page_size, nRes, 0);
    diff --git a/src/ctime.c b/src/ctime.c
    index 013a0c2..a02c243 100644
    --- a/src/ctime.c
    +++ b/src/ctime.c
    @@ -193,6 +193,9 @@ static const char * const sqlite3azCompileOpt[] = {
     #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
       "ENABLE_BATCH_ATOMIC_WRITE",
     #endif
    +#if SQLITE_ENABLE_BYTECODE_VTAB
    +  "ENABLE_BYTECODE_VTAB",
    +#endif
     #if SQLITE_ENABLE_CEROD
       "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
     #endif
    @@ -355,9 +358,11 @@ static const char * const sqlite3azCompileOpt[] = {
     #if SQLITE_FTS5_NO_WITHOUT_ROWID
       "FTS5_NO_WITHOUT_ROWID",
     #endif
    +/* BEGIN SQLCIPHER */
     #if SQLITE_HAS_CODEC
       "HAS_CODEC",
     #endif
    +/* END SQLCIPHER */
     #if HAVE_ISNAN || SQLITE_HAVE_ISNAN
       "HAVE_ISNAN",
     #endif
    @@ -514,9 +519,6 @@ static const char * const sqlite3azCompileOpt[] = {
     #if SQLITE_OMIT_BLOB_LITERAL
       "OMIT_BLOB_LITERAL",
     #endif
    -#if SQLITE_OMIT_BTREECOUNT
    -  "OMIT_BTREECOUNT",
    -#endif
     #if SQLITE_OMIT_CAST
       "OMIT_CAST",
     #endif
    diff --git a/src/date.c b/src/date.c
    index 6a8defc..1db26b1 100644
    --- a/src/date.c
    +++ b/src/date.c
    @@ -515,7 +515,7 @@ static int osLocaltime(time_t *t, struct tm *pTm){
     #if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S
       struct tm *pX;
     #if SQLITE_THREADSAFE>0
    -  sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
    +  sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
     #endif
       sqlite3_mutex_enter(mutex);
       pX = localtime(t);
    @@ -621,12 +621,12 @@ static const struct {
       double rLimit;      /* Maximum NNN value for this transform */
       double rXform;      /* Constant used for this transform */
     } aXformType[] = {
    -  { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
    -  { 0, 6, "minute", 7737817680.0,   86400000.0/(24.0*60.0)      },
    -  { 0, 4, "hour",   128963628.0,    86400000.0/24.0             },
    -  { 0, 3, "day",    5373485.0,      86400000.0                  },
    -  { 1, 5, "month",  176546.0,       30.0*86400000.0             },
    -  { 2, 4, "year",   14713.0,        365.0*86400000.0            },
    +  { 0, 6, "second", 464269060800.0, 1000.0         },
    +  { 0, 6, "minute", 7737817680.0,   60000.0        },
    +  { 0, 4, "hour",   128963628.0,    3600000.0      },
    +  { 0, 3, "day",    5373485.0,      86400000.0     },
    +  { 1, 5, "month",  176546.0,       2592000000.0   },
    +  { 2, 4, "year",   14713.0,        31536000000.0  },
     };
     
     /*
    @@ -1112,8 +1112,8 @@ static void strftimeFunc(
             case 'm':  sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break;
             case 'M':  sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break;
             case 's': {
    -          sqlite3_snprintf(30,&z[j],"%lld",
    -                           (i64)(x.iJD/1000 - 21086676*(i64)10000));
    +          i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000);
    +          sqlite3Int64ToText(iS, &z[j]);
               j += sqlite3Strlen30(&z[j]);
               break;
             }
    @@ -1211,10 +1211,10 @@ static void currentTimeFunc(
     #if HAVE_GMTIME_R
       pTm = gmtime_r(&t, &sNow);
     #else
    -  sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
    +  sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
       pTm = gmtime(&t);
       if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
    -  sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
    +  sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
     #endif
       if( pTm ){
         strftime(zBuf, 20, zFormat, &sNow);
    diff --git a/src/dbstat.c b/src/dbstat.c
    index 2fea48c..78173c3 100644
    --- a/src/dbstat.c
    +++ b/src/dbstat.c
    @@ -238,6 +238,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
       i = 0;
       if( iSchema>=0 ){
         pIdxInfo->aConstraintUsage[iSchema].argvIndex = ++i;
    +    pIdxInfo->aConstraintUsage[iSchema].omit = 1;
         pIdxInfo->idxNum |= 0x01;
       }
       if( iName>=0 ){
    @@ -452,7 +453,9 @@ static int statDecodePage(Btree *pBt, StatPage *p){
             if( nPayload>(u32)nLocal ){
               int j;
               int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
    -          if( iOff+nLocal>nUsable ) goto statPageIsCorrupt;
    +          if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){
    +            goto statPageIsCorrupt;
    +          }
               pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
               pCell->nOvfl = nOvfl;
               pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
    @@ -721,10 +724,10 @@ static int statFilter(
       pSql = sqlite3_str_new(pTab->db);
       sqlite3_str_appendf(pSql,
           "SELECT * FROM ("
    -        "SELECT 'sqlite_master' AS name,1 AS rootpage,'table' AS type"
    +        "SELECT 'sqlite_schema' AS name,1 AS rootpage,'table' AS type"
             " UNION ALL "
             "SELECT name,rootpage,type"
    -        " FROM \"%w\".sqlite_master WHERE rootpage!=0)",
    +        " FROM \"%w\".sqlite_schema WHERE rootpage!=0)",
           pTab->db->aDb[pCsr->iDb].zDbSName);
       if( zName ){
         sqlite3_str_appendf(pSql, "WHERE name=%Q", zName);
    diff --git a/src/delete.c b/src/delete.c
    index 0a83f1b..ae2f85b 100644
    --- a/src/delete.c
    +++ b/src/delete.c
    @@ -31,7 +31,7 @@
     Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
       struct SrcList_item *pItem = pSrc->a;
       Table *pTab;
    -  assert( pItem && pSrc->nSrc==1 );
    +  assert( pItem && pSrc->nSrc>=1 );
       pTab = sqlite3LocateTableItem(pParse, 0, pItem);
       sqlite3DeleteTable(pParse->db, pItem->pTab);
       pItem->pTab = pTab;
    @@ -51,7 +51,7 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
     **   1) It is a virtual table and no implementation of the xUpdate method
     **      has been provided
     **
    -**   2) It is a system table (i.e. sqlite_master), this call is not
    +**   2) It is a system table (i.e. sqlite_schema), this call is not
     **      part of a nested parse and writable_schema pragma has not 
     **      been specified
     **
    @@ -533,7 +533,9 @@ void sqlite3DeleteFrom(
                                      iTabCur, aToOpen, &iDataCur, &iIdxCur);
           assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
           assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
    -      if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
    +      if( eOnePass==ONEPASS_MULTI ){
    +        sqlite3VdbeJumpHereOrPopInst(v, iAddrOnce);
    +      }
         }
       
         /* Set up a loop over the rowids/primary-keys that were found in the
    @@ -856,6 +858,7 @@ void sqlite3GenerateRowIndexDelete(
             &iPartIdxLabel, pPrior, r1);
         sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
             pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
    +    sqlite3VdbeChangeP5(v, 1);  /* Cause IdxDelete to error if no entry found */
         sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
         pPrior = pIdx;
       }
    diff --git a/src/expr.c b/src/expr.c
    index d82ef8b..b64ea28 100644
    --- a/src/expr.c
    +++ b/src/expr.c
    @@ -42,16 +42,19 @@ char sqlite3TableColumnAffinity(Table *pTab, int iCol){
     ** SELECT a AS b FROM t1 WHERE b;
     ** SELECT * FROM t1 WHERE (select a from t1);
     */
    -char sqlite3ExprAffinity(Expr *pExpr){
    +char sqlite3ExprAffinity(const Expr *pExpr){
       int op;
       while( ExprHasProperty(pExpr, EP_Skip) ){
    -    assert( pExpr->op==TK_COLLATE );
    +    assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
         pExpr = pExpr->pLeft;
         assert( pExpr!=0 );
       }
       op = pExpr->op;
       if( op==TK_SELECT ){
         assert( pExpr->flags&EP_xIsSelect );
    +    assert( pExpr->x.pSelect!=0 );
    +    assert( pExpr->x.pSelect->pEList!=0 );
    +    assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
         return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
       }
       if( op==TK_REGISTER ) op = pExpr->op2;
    @@ -112,7 +115,7 @@ Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
     */
     Expr *sqlite3ExprSkipCollate(Expr *pExpr){
       while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
    -    assert( pExpr->op==TK_COLLATE );
    +    assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
         pExpr = pExpr->pLeft;
       }   
       return pExpr;
    @@ -131,7 +134,7 @@ Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
           assert( pExpr->op==TK_FUNCTION );
           pExpr = pExpr->x.pList->a[0].pExpr;
         }else{
    -      assert( pExpr->op==TK_COLLATE );
    +      assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
           pExpr = pExpr->pLeft;
         }
       }   
    @@ -152,10 +155,10 @@ Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
     ** COLLATE operators take first precedence.  Left operands take
     ** precedence over right operands.
     */
    -CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
    +CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
       sqlite3 *db = pParse->db;
       CollSeq *pColl = 0;
    -  Expr *p = pExpr;
    +  const Expr *p = pExpr;
       while( p ){
         int op = p->op;
         if( op==TK_REGISTER ) op = p->op2;
    @@ -195,7 +198,7 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
              && ALWAYS(!ExprHasProperty(p, EP_xIsSelect))
             ){
               int i;
    -          for(i=0; ix.pList->nExpr; i++){
    +          for(i=0; ALWAYS(ix.pList->nExpr); i++){
                 if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){
                   pNext = p->x.pList->a[i].pExpr;
                   break;
    @@ -224,7 +227,7 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
     ** The sqlite3ExprCollSeq() routine works the same except that it
     ** returns NULL if there is no defined collation.
     */
    -CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
    +CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr){
       CollSeq *p = sqlite3ExprCollSeq(pParse, pExpr);
       if( p==0 ) p = pParse->db->pDfltColl;
       assert( p!=0 );
    @@ -234,7 +237,7 @@ CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
     /*
     ** Return TRUE if the two expressions have equivalent collating sequences.
     */
    -int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
    +int sqlite3ExprCollSeqMatch(Parse *pParse, const Expr *pE1, const Expr *pE2){
       CollSeq *pColl1 = sqlite3ExprNNCollSeq(pParse, pE1);
       CollSeq *pColl2 = sqlite3ExprNNCollSeq(pParse, pE2);
       return sqlite3StrICmp(pColl1->zName, pColl2->zName)==0;
    @@ -245,7 +248,7 @@ int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
     ** type affinity of the other operand.  This routine returns the
     ** type affinity that should be used for the comparison operator.
     */
    -char sqlite3CompareAffinity(Expr *pExpr, char aff2){
    +char sqlite3CompareAffinity(const Expr *pExpr, char aff2){
       char aff1 = sqlite3ExprAffinity(pExpr);
       if( aff1>SQLITE_AFF_NONE && aff2>SQLITE_AFF_NONE ){
         /* Both sides of the comparison are columns. If one has numeric
    @@ -267,7 +270,7 @@ char sqlite3CompareAffinity(Expr *pExpr, char aff2){
     ** pExpr is a comparison operator.  Return the type affinity that should
     ** be applied to both operands prior to doing the comparison.
     */
    -static char comparisonAffinity(Expr *pExpr){
    +static char comparisonAffinity(const Expr *pExpr){
       char aff;
       assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT ||
               pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE ||
    @@ -290,7 +293,7 @@ static char comparisonAffinity(Expr *pExpr){
     ** if the index with affinity idx_affinity may be used to implement
     ** the comparison in pExpr.
     */
    -int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
    +int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity){
       char aff = comparisonAffinity(pExpr);
       if( affpRight, p->pLeft);
       }else{
    @@ -594,6 +601,7 @@ static void codeVectorCompare(
       int addrDone = sqlite3VdbeMakeLabel(pParse);
       int isCommuted = ExprHasProperty(pExpr,EP_Commuted);
     
    +  assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
       if( pParse->nErr ) return;
       if( nLeft!=sqlite3ExprVectorSize(pRight) ){
         sqlite3ErrorMsg(pParse, "row value misused");
    @@ -1206,7 +1214,7 @@ static int dupedExprStructSize(Expr *p, int flags){
         assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
         assert( !ExprHasProperty(p, EP_FromJoin) ); 
         assert( !ExprHasProperty(p, EP_MemToken) );
    -    assert( !ExprHasProperty(p, EP_NoReduce) );
    +    assert( !ExprHasVVAProperty(p, EP_NoReduce) );
         if( p->pLeft || p->x.pList ){
           nSize = EXPR_REDUCEDSIZE | EP_Reduced;
         }else{
    @@ -1311,6 +1319,10 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
         pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
         pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
         pNew->flags |= staticFlag;
    +    ExprClearVVAProperties(pNew);
    +    if( dupFlags ){
    +      ExprSetVVAProperty(pNew, EP_Immutable);
    +    }
     
         /* Copy the p->u.zToken string, if any. */
         if( nToken ){
    @@ -1778,6 +1790,7 @@ void sqlite3ExprListSetName(
       int dequote             /* True to cause the name to be dequoted */
     ){
       assert( pList!=0 || pParse->db->mallocFailed!=0 );
    +  assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 );
       if( pList ){
         struct ExprList_item *pItem;
         assert( pList->nExpr>0 );
    @@ -1785,9 +1798,14 @@ void sqlite3ExprListSetName(
         assert( pItem->zEName==0 );
         assert( pItem->eEName==ENAME_NAME );
         pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
    -    if( dequote ) sqlite3Dequote(pItem->zEName);
    -    if( IN_RENAME_OBJECT ){
    -      sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
    +    if( dequote ){
    +      /* If dequote==0, then pName->z does not point to part of a DDL
    +      ** statement handled by the parser. And so no token need be added
    +      ** to the token-map.  */
    +      sqlite3Dequote(pItem->zEName);
    +      if( IN_RENAME_OBJECT ){
    +        sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
    +      }
         }
       }
     }
    @@ -1973,10 +1991,10 @@ Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
     **
     ** The sqlite3ExprIsConstantOrFunction() is used for evaluating DEFAULT
     ** expressions in a CREATE TABLE statement.  The Walker.eCode value is 5
    -** when parsing an existing schema out of the sqlite_master table and 4
    +** when parsing an existing schema out of the sqlite_schema table and 4
     ** when processing a new CREATE TABLE statement.  A bound parameter raises
     ** an error for new statements, but is silently converted
    -** to NULL for existing schemas.  This allows sqlite_master tables that 
    +** to NULL for existing schemas.  This allows sqlite_schema tables that 
     ** contain a bound parameter because they were generated by older versions
     ** of SQLite to be parsed by newer versions of SQLite without raising a
     ** malformed schema error.
    @@ -2011,7 +2029,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
           if( sqlite3ExprIdToTrueFalse(pExpr) ){
             return WRC_Prune;
           }
    -      /* Fall thru */
    +      /* no break */ deliberate_fall_through
         case TK_COLUMN:
         case TK_AGG_FUNCTION:
         case TK_AGG_COLUMN:
    @@ -2025,18 +2043,20 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
           if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
             return WRC_Continue;
           }
    -      /* Fall through */
    +      /* no break */ deliberate_fall_through
         case TK_IF_NULL_ROW:
         case TK_REGISTER:
    +    case TK_DOT:
           testcase( pExpr->op==TK_REGISTER );
           testcase( pExpr->op==TK_IF_NULL_ROW );
    +      testcase( pExpr->op==TK_DOT );
           pWalker->eCode = 0;
           return WRC_Abort;
         case TK_VARIABLE:
           if( pWalker->eCode==5 ){
             /* Silently convert bound parameters that appear inside of CREATE
             ** statements into a NULL when parsing the CREATE statement text out
    -        ** of the sqlite_master table */
    +        ** of the sqlite_schema table */
             pExpr->op = TK_NULL;
           }else if( pWalker->eCode==4 ){
             /* A bound parameter in a CREATE statement that originates from
    @@ -2044,7 +2064,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
             pWalker->eCode = 0;
             return WRC_Abort;
           }
    -      /* Fall through */
    +      /* no break */ deliberate_fall_through
         default:
           testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
           testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
    @@ -2087,7 +2107,7 @@ int sqlite3ExprIsConstant(Expr *p){
     **
     ** When this routine returns true, it indicates that the expression
     ** can be added to the pParse->pConstExpr list and evaluated once when
    -** the prepared statement starts up.  See sqlite3ExprCodeAtInit().
    +** the prepared statement starts up.  See sqlite3ExprCodeRunJustOnce().
     */
     int sqlite3ExprIsConstantNotJoin(Expr *p){
       return exprIsConst(p, 2, 0);
    @@ -2169,12 +2189,12 @@ int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprList *pGroupBy){
     ** the expression is constant or a function call with constant arguments.
     ** Return and 0 if there are any variables.
     **
    -** isInit is true when parsing from sqlite_master.  isInit is false when
    +** isInit is true when parsing from sqlite_schema.  isInit is false when
     ** processing a new CREATE TABLE statement.  When isInit is true, parameters
     ** (such as ? or $abc) in the expression are converted into NULL.  When
     ** isInit is false, parameters raise an error.  Parameters should not be
     ** allowed in a CREATE TABLE statement, but some legacy versions of SQLite
    -** allowed it, so we need to support it when reading sqlite_master for
    +** allowed it, so we need to support it when reading sqlite_schema for
     ** backwards compatibility.
     **
     ** If isInit is true, set EP_FromDDL on every TK_FUNCTION node.
    @@ -2537,7 +2557,7 @@ int sqlite3FindInIndex(
       if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
         sqlite3 *db = pParse->db;              /* Database connection */
         Table *pTab;                           /* Table . */
    -    i16 iDb;                               /* Database idx for pTab */
    +    int iDb;                               /* Database idx for pTab */
         ExprList *pEList = p->pEList;
         int nExpr = pEList->nExpr;
     
    @@ -2548,6 +2568,7 @@ int sqlite3FindInIndex(
     
         /* Code an OP_Transaction and OP_TableLock for 
    . */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDb>=0 && iDbtnum, 0, pTab->zName); @@ -2850,6 +2871,7 @@ void sqlite3CodeRhsOfIN( /* Begin coding the subroutine */ ExprSetProperty(pExpr, EP_Subrtn); + assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) ); pExpr->y.sub.regReturn = ++pParse->nMem; pExpr->y.sub.iAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; @@ -2931,6 +2953,8 @@ void sqlite3CodeRhsOfIN( affinity = sqlite3ExprAffinity(pLeft); if( affinity<=SQLITE_AFF_NONE ){ affinity = SQLITE_AFF_BLOB; + }else if( affinity==SQLITE_AFF_REAL ){ + affinity = SQLITE_AFF_NUMERIC; } if( pKeyInfo ){ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); @@ -3169,7 +3193,9 @@ static void sqlite3ExprCodeIN( int destNotNull; /* Jump here if a comparison is not true in step 6 */ int addrTop; /* Top of the step-6 loop */ int iTab = 0; /* Index to use */ + u8 okConstFactor = pParse->okConstFactor; + assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); pLeft = pExpr->pLeft; if( sqlite3ExprCheckIN(pParse, pExpr) ) return; zAff = exprINAffinity(pParse, pExpr); @@ -3212,8 +3238,14 @@ static void sqlite3ExprCodeIN( ** so that the fields are in the same order as an existing index. The ** aiMap[] array contains a mapping from the original LHS field order to ** the field order that matches the RHS index. - */ + ** + ** Avoid factoring the LHS of the IN(...) expression out of the loop, + ** even if it is constant, as OP_Affinity may be used on the register + ** by code generated below. */ + assert( pParse->okConstFactor==okConstFactor ); + pParse->okConstFactor = 0; rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy); + pParse->okConstFactor = okConstFactor; for(i=0; ipLeft)==SQLITE_AFF_REAL; for(ii=0; iinExpr; ii++){ - if( bLhsReal ){ - r2 = regToFree = sqlite3GetTempReg(pParse); - sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2); - sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC); - }else{ - r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); - } + r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree); if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){ sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull); } @@ -3496,7 +3520,7 @@ void sqlite3ExprCodeGeneratedColumn( }else{ iAddr = 0; } - sqlite3ExprCode(pParse, pCol->pDflt, regOut); + sqlite3ExprCodeCopy(pParse, pCol->pDflt, regOut); if( pCol->affinity>=SQLITE_AFF_TEXT ){ sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1); } @@ -3637,6 +3661,16 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ return iResult; } +/* +** If the last opcode is a OP_Copy, then set the do-not-merge flag (p5) +** so that a subsequent copy will not be merged into this one. +*/ +static void setDoNotMergeFlagOnCopy(Vdbe *v){ + if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){ + sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */ + } +} + /* ** Generate code to implement special SQL functions that are implemented ** in-line rather than by using the usual callbacks. @@ -3668,12 +3702,17 @@ static int exprCodeInlineFunction( VdbeCoverage(v); sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target); } - if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){ - sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */ - } + setDoNotMergeFlagOnCopy(v); sqlite3VdbeResolveLabel(v, endCoalesce); break; } + case INLINEFUNC_iif: { + Expr caseExpr; + memset(&caseExpr, 0, sizeof(caseExpr)); + caseExpr.op = TK_CASE; + caseExpr.x.pList = pFarg; + return sqlite3ExprCodeTarget(pParse, &caseExpr, target); + } default: { /* The UNLIKELY() function is a no-op. The result is the value @@ -3763,30 +3802,41 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ int p5 = 0; assert( target>0 && target<=pParse->nMem ); - if( v==0 ){ - assert( pParse->db->mallocFailed ); - return 0; - } + assert( v!=0 ); expr_code_doover: if( pExpr==0 ){ op = TK_NULL; }else{ + assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); op = pExpr->op; } switch( op ){ case TK_AGG_COLUMN: { AggInfo *pAggInfo = pExpr->pAggInfo; - struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg]; + struct AggInfo_col *pCol; + assert( pAggInfo!=0 ); + assert( pExpr->iAgg>=0 && pExpr->iAggnColumn ); + pCol = &pAggInfo->aCol[pExpr->iAgg]; if( !pAggInfo->directMode ){ assert( pCol->iMem>0 ); return pCol->iMem; }else if( pAggInfo->useSortingIdx ){ + Table *pTab = pCol->pTab; sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab, pCol->iSorterColumn, target); + if( pCol->iColumn<0 ){ + VdbeComment((v,"%s.rowid",pTab->zName)); + }else{ + VdbeComment((v,"%s.%s",pTab->zName,pTab->aCol[pCol->iColumn].zName)); + if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, target); + } + } return target; } /* Otherwise, fall thru into the TK_COLUMN case */ + /* no break */ deliberate_fall_through } case TK_COLUMN: { int iTab = pExpr->iTable; @@ -3809,10 +3859,6 @@ expr_code_doover: static const char zAff[] = "B\000C\000D\000E"; assert( SQLITE_AFF_BLOB=='A' ); assert( SQLITE_AFF_TEXT=='B' ); - if( iReg!=target ){ - sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target); - iReg = target; - } sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0, &zAff[(aff-'B')*2], P4_STATIC); } @@ -4026,6 +4072,7 @@ expr_code_doover: tempX.op = TK_INTEGER; tempX.flags = EP_IntValue|EP_TokenOnly; tempX.u.iValue = 0; + ExprClearVVAProperties(&tempX); r1 = sqlite3ExprCodeTemp(pParse, &tempX, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free2); sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target); @@ -4071,7 +4118,10 @@ expr_code_doover: } case TK_AGG_FUNCTION: { AggInfo *pInfo = pExpr->pAggInfo; - if( pInfo==0 ){ + if( pInfo==0 + || NEVER(pExpr->iAgg<0) + || NEVER(pExpr->iAgg>=pInfo->nFunc) + ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken); }else{ @@ -4097,16 +4147,13 @@ expr_code_doover: #endif if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ - /* SQL functions can be expensive. So try to move constant functions - ** out of the inner loop, even if that means an extra OP_Copy. */ - return sqlite3ExprCodeAtInit(pParse, pExpr, -1); + /* SQL functions can be expensive. So try to avoid running them + ** multiple times if we know they always give the same result */ + return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); } assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - if( ExprHasProperty(pExpr, EP_TokenOnly) ){ - pFarg = 0; - }else{ - pFarg = pExpr->x.pList; - } + assert( !ExprHasProperty(pExpr, EP_TokenOnly) ); + pFarg = pExpr->x.pList; nFarg = pFarg ? pFarg->nExpr : 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); zId = pExpr->u.zToken; @@ -4222,7 +4269,9 @@ expr_code_doover: int nCol; testcase( op==TK_EXISTS ); testcase( op==TK_SELECT ); - if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ + if( pParse->db->mallocFailed ){ + return 0; + }else if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){ sqlite3SubselectError(pParse, nCol, 1); }else{ return sqlite3CodeSubselect(pParse, pExpr); @@ -4441,6 +4490,7 @@ expr_code_doover: sqlite3VdbeAddOp2(v, OP_Null, 0, target); } sqlite3ExprDelete(db, pDel); + setDoNotMergeFlagOnCopy(v); sqlite3VdbeResolveLabel(v, endLabel); break; } @@ -4451,7 +4501,7 @@ expr_code_doover: || pExpr->affExpr==OE_Fail || pExpr->affExpr==OE_Ignore ); - if( !pParse->pTriggerTab ){ + if( !pParse->pTriggerTab && !pParse->nested ){ sqlite3ErrorMsg(pParse, "RAISE() may only be used within a trigger-program"); return 0; @@ -4465,8 +4515,9 @@ expr_code_doover: v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); VdbeCoverage(v); }else{ - sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, - pExpr->affExpr, pExpr->u.zToken, 0, 0); + sqlite3HaltConstraint(pParse, + pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR, + pExpr->affExpr, pExpr->u.zToken, 0, 0); } break; @@ -4479,15 +4530,23 @@ expr_code_doover: } /* -** Factor out the code of the given expression to initialization time. +** Generate code that will evaluate expression pExpr just one time +** per prepared statement execution. +** +** If the expression uses functions (that might throw an exception) then +** guard them with an OP_Once opcode to ensure that the code is only executed +** once. If no functions are involved, then factor the code out and put it at +** the end of the prepared statement in the initialization section. ** ** If regDest>=0 then the result is always stored in that register and the ** result is not reusable. If regDest<0 then this routine is free to ** store the value whereever it wants. The register where the expression -** is stored is returned. When regDest<0, two identical expressions will -** code to the same register. +** is stored is returned. When regDest<0, two identical expressions might +** code to the same register, if they do not contain function calls and hence +** are factored out into the initialization section at the end of the +** prepared statement. */ -int sqlite3ExprCodeAtInit( +int sqlite3ExprCodeRunJustOnce( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The expression to code when the VDBE initializes */ int regDest /* Store the value in this register */ @@ -4505,14 +4564,29 @@ int sqlite3ExprCodeAtInit( } } pExpr = sqlite3ExprDup(pParse->db, pExpr, 0); - p = sqlite3ExprListAppend(pParse, p, pExpr); - if( p ){ - struct ExprList_item *pItem = &p->a[p->nExpr-1]; - pItem->reusable = regDest<0; - if( regDest<0 ) regDest = ++pParse->nMem; - pItem->u.iConstExprReg = regDest; + if( pExpr!=0 && ExprHasProperty(pExpr, EP_HasFunc) ){ + Vdbe *v = pParse->pVdbe; + int addr; + assert( v ); + addr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + pParse->okConstFactor = 0; + if( !pParse->db->mallocFailed ){ + if( regDest<0 ) regDest = ++pParse->nMem; + sqlite3ExprCode(pParse, pExpr, regDest); + } + pParse->okConstFactor = 1; + sqlite3ExprDelete(pParse->db, pExpr); + sqlite3VdbeJumpHere(v, addr); + }else{ + p = sqlite3ExprListAppend(pParse, p, pExpr); + if( p ){ + struct ExprList_item *pItem = &p->a[p->nExpr-1]; + pItem->reusable = regDest<0; + if( regDest<0 ) regDest = ++pParse->nMem; + pItem->u.iConstExprReg = regDest; + } + pParse->pConstExpr = p; } - pParse->pConstExpr = p; return regDest; } @@ -4537,7 +4611,7 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ && sqlite3ExprIsConstantNotJoin(pExpr) ){ *pReg = 0; - r2 = sqlite3ExprCodeAtInit(pParse, pExpr, -1); + r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); }else{ int r1 = sqlite3GetTempReg(pParse); r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); @@ -4559,10 +4633,12 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; + assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) ); assert( target>0 && target<=pParse->nMem ); - inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); assert( pParse->pVdbe!=0 || pParse->db->mallocFailed ); - if( inReg!=target && pParse->pVdbe ){ + if( pParse->pVdbe==0 ) return; + inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); + if( inReg!=target ){ u8 op; if( ExprHasProperty(pExpr,EP_Subquery) ){ op = OP_Copy; @@ -4593,9 +4669,9 @@ void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){ */ void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){ - sqlite3ExprCodeAtInit(pParse, pExpr, target); + sqlite3ExprCodeRunJustOnce(pParse, pExpr, target); }else{ - sqlite3ExprCode(pParse, pExpr, target); + sqlite3ExprCodeCopy(pParse, pExpr, target); } } @@ -4653,7 +4729,7 @@ int sqlite3ExprCodeExprList( }else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstantNotJoin(pExpr) ){ - sqlite3ExprCodeAtInit(pParse, pExpr, target+i); + sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i); }else{ int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); if( inReg!=target+i ){ @@ -4776,6 +4852,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 ); if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ if( NEVER(pExpr==0) ) return; /* No way this can happen */ + assert( !ExprHasVVAProperty(pExpr, EP_Immutable) ); op = pExpr->op; switch( op ){ case TK_AND: @@ -4825,7 +4902,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ testcase( op==TK_ISNOT ); op = (op==TK_IS) ? TK_EQ : TK_NE; jumpIfNull = SQLITE_NULLEQ; - /* Fall thru */ + /* no break */ deliberate_fall_through case TK_LT: case TK_LE: case TK_GT: @@ -4917,6 +4994,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 ); if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ if( pExpr==0 ) return; + assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); /* The value of pExpr->op and op are related as follows: ** @@ -5000,7 +5078,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ testcase( pExpr->op==TK_ISNOT ); op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; jumpIfNull = SQLITE_NULLEQ; - /* Fall thru */ + /* no break */ deliberate_fall_through case TK_LT: case TK_LE: case TK_GT: @@ -5200,7 +5278,7 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ } if( (pA->flags & (EP_Distinct|EP_Commuted)) != (pB->flags & (EP_Distinct|EP_Commuted)) ) return 2; - if( (combinedFlags & EP_TokenOnly)==0 ){ + if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){ if( combinedFlags & EP_xIsSelect ) return 2; if( (combinedFlags & EP_FixedCol)==0 && sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2; @@ -5208,24 +5286,10 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){ if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; if( pA->op!=TK_STRING && pA->op!=TK_TRUEFALSE - && (combinedFlags & EP_Reduced)==0 + && ALWAYS((combinedFlags & EP_Reduced)==0) ){ if( pA->iColumn!=pB->iColumn ) return 2; - if( pA->op2!=pB->op2 ){ - if( pA->op==TK_TRUTH ) return 2; - if( pA->op==TK_FUNCTION && iTab<0 ){ - /* Ex: CREATE TABLE t1(a CHECK( aop2!=pB->op2 && pA->op==TK_TRUTH ) return 2; if( pA->op!=TK_IN && pA->iTable!=pB->iTable && pA->iTable!=iTab ){ return 2; } @@ -5326,13 +5390,13 @@ static int exprImpliesNotNull( case TK_RSHIFT: case TK_CONCAT: seenNot = 1; - /* Fall thru */ + /* no break */ deliberate_fall_through case TK_STAR: case TK_REM: case TK_BITAND: case TK_SLASH: { if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1; - /* Fall thru into the next case */ + /* no break */ deliberate_fall_through } case TK_SPAN: case TK_COLLATE: @@ -5463,19 +5527,26 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ case TK_LT: case TK_LE: case TK_GT: - case TK_GE: + case TK_GE: { + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pRight; testcase( pExpr->op==TK_EQ ); testcase( pExpr->op==TK_NE ); testcase( pExpr->op==TK_LT ); testcase( pExpr->op==TK_LE ); testcase( pExpr->op==TK_GT ); testcase( pExpr->op==TK_GE ); - if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab)) - || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab)) + /* The y.pTab=0 assignment in wherecode.c always happens after the + ** impliesNotNullRow() test */ + if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0) + && IsVirtual(pLeft->y.pTab)) + || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) + && IsVirtual(pRight->y.pTab)) ){ - return WRC_Prune; + return WRC_Prune; } - + /* no break */ deliberate_fall_through + } default: return WRC_Continue; } @@ -5587,10 +5658,25 @@ int sqlite3ExprCoveredByIndex( */ struct SrcCount { SrcList *pSrc; /* One particular FROM clause in a nested query */ + int iSrcInner; /* Smallest cursor number in this context */ int nThis; /* Number of references to columns in pSrcList */ int nOther; /* Number of references to columns in other FROM clauses */ }; +/* +** xSelect callback for sqlite3FunctionUsesThisSrc(). If this is the first +** SELECT with a FROM clause encountered during this iteration, set +** SrcCount.iSrcInner to the cursor number of the leftmost object in +** the FROM cause. +*/ +static int selectSrcCount(Walker *pWalker, Select *pSel){ + struct SrcCount *p = pWalker->u.pSrcCount; + if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){ + pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor; + } + return WRC_Continue; +} + /* ** Count the number of references to columns. */ @@ -5611,7 +5697,7 @@ static int exprSrcCount(Walker *pWalker, Expr *pExpr){ } if( inThis++; - }else if( nSrc==0 || pExpr->iTablea[0].iCursor ){ + }else if( pExpr->iTableiSrcInner ){ /* In a well-formed parse tree (no name resolution errors), ** TK_COLUMN nodes with smaller Expr.iTable values are in an ** outer context. Those are the only ones to count as "other" */ @@ -5633,9 +5719,10 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ assert( pExpr->op==TK_AGG_FUNCTION ); memset(&w, 0, sizeof(w)); w.xExprCallback = exprSrcCount; - w.xSelectCallback = sqlite3SelectWalkNoop; + w.xSelectCallback = selectSrcCount; w.u.pSrcCount = &cnt; cnt.pSrc = pSrcList; + cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF; cnt.nThis = 0; cnt.nOther = 0; sqlite3WalkExprList(&w, pExpr->x.pList); @@ -5647,6 +5734,64 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ return cnt.nThis>0 || cnt.nOther==0; } +/* +** This is a Walker expression node callback. +** +** For Expr nodes that contain pAggInfo pointers, make sure the AggInfo +** object that is referenced does not refer directly to the Expr. If +** it does, make a copy. This is done because the pExpr argument is +** subject to change. +** +** The copy is stored on pParse->pConstExpr with a register number of 0. +** This will cause the expression to be deleted automatically when the +** Parse object is destroyed, but the zero register number means that it +** will not generate any code in the preamble. +*/ +static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ + if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) + && pExpr->pAggInfo!=0 + ){ + AggInfo *pAggInfo = pExpr->pAggInfo; + int iAgg = pExpr->iAgg; + Parse *pParse = pWalker->pParse; + sqlite3 *db = pParse->db; + assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION ); + if( pExpr->op==TK_AGG_COLUMN ){ + assert( iAgg>=0 && iAggnColumn ); + if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){ + pExpr = sqlite3ExprDup(db, pExpr, 0); + if( pExpr ){ + pAggInfo->aCol[iAgg].pCExpr = pExpr; + pParse->pConstExpr = + sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + } + } + }else{ + assert( iAgg>=0 && iAggnFunc ); + if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ + pExpr = sqlite3ExprDup(db, pExpr, 0); + if( pExpr ){ + pAggInfo->aFunc[iAgg].pFExpr = pExpr; + pParse->pConstExpr = + sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); + } + } + } + } + return WRC_Continue; +} + +/* +** Initialize a Walker object so that will persist AggInfo entries referenced +** by the tree that is walked. +*/ +void sqlite3AggInfoPersistWalkerInit(Walker *pWalker, Parse *pParse){ + memset(pWalker, 0, sizeof(*pWalker)); + pWalker->pParse = pParse; + pWalker->xExprCallback = agginfoPersistExprCb; + pWalker->xSelectCallback = sqlite3SelectWalkNoop; +} + /* ** Add a new element to the pAggInfo->aCol[] array. Return the index of ** the new element. Return a negative number if malloc fails. @@ -5677,7 +5822,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){ &i ); return i; -} +} /* ** This is the xExprCallback for a tree walker. It is used to @@ -5728,7 +5873,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ pCol->iColumn = pExpr->iColumn; pCol->iMem = ++pParse->nMem; pCol->iSorterColumn = -1; - pCol->pExpr = pExpr; + pCol->pCExpr = pExpr; if( pAggInfo->pGroupBy ){ int j, n; ExprList *pGB = pAggInfo->pGroupBy; @@ -5771,7 +5916,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; inFunc; i++, pItem++){ - if( sqlite3ExprCompare(0, pItem->pExpr, pExpr, -1)==0 ){ + if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ break; } } @@ -5783,7 +5928,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ if( i>=0 ){ assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); pItem = &pAggInfo->aFunc[i]; - pItem->pExpr = pExpr; + pItem->pFExpr = pExpr; pItem->iMem = ++pParse->nMem; assert( !ExprHasProperty(pExpr, EP_IntValue) ); pItem->pFunc = sqlite3FindFunction(pParse->db, @@ -5810,15 +5955,6 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ } return WRC_Continue; } -static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ - UNUSED_PARAMETER(pSelect); - pWalker->walkerDepth++; - return WRC_Continue; -} -static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){ - UNUSED_PARAMETER(pSelect); - pWalker->walkerDepth--; -} /* ** Analyze the pExpr expression looking for aggregate functions and @@ -5832,8 +5968,8 @@ static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){ void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ Walker w; w.xExprCallback = analyzeAggregate; - w.xSelectCallback = analyzeAggregatesInSelect; - w.xSelectCallback2 = analyzeAggregatesInSelectEnd; + w.xSelectCallback = sqlite3WalkerDepthIncrease; + w.xSelectCallback2 = sqlite3WalkerDepthDecrease; w.walkerDepth = 0; w.u.pNC = pNC; w.pParse = 0; diff --git a/src/fkey.c b/src/fkey.c index 9698d34..959e994 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -658,7 +658,7 @@ static void fkScanChildren( /* Clean up the WHERE clause constructed above. */ sqlite3ExprDelete(db, pWhere); if( iFkIfZero ){ - sqlite3VdbeJumpHere(v, iFkIfZero); + sqlite3VdbeJumpHereOrPopInst(v, iFkIfZero); } } @@ -1359,6 +1359,7 @@ static Trigger *fkActionTrigger( pStep->op = TK_DELETE; break; } + /* no break */ deliberate_fall_through default: pStep->op = TK_UPDATE; } diff --git a/src/func.c b/src/func.c index ff0bcb5..039bc48 100644 --- a/src/func.c +++ b/src/func.c @@ -853,6 +853,7 @@ static void likeFunc( int nPat; sqlite3 *db = sqlite3_context_db_handle(context); struct compareInfo *pInfo = sqlite3_user_data(context); + struct compareInfo backupInfo; #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS if( sqlite3_value_type(argv[0])==SQLITE_BLOB @@ -888,6 +889,12 @@ static void likeFunc( return; } escape = sqlite3Utf8Read(&zEsc); + if( escape==pInfo->matchAll || escape==pInfo->matchOne ){ + memcpy(&backupInfo, pInfo, sizeof(backupInfo)); + pInfo = &backupInfo; + if( escape==pInfo->matchAll ) pInfo->matchAll = 0; + if( escape==pInfo->matchOne ) pInfo->matchOne = 0; + } }else{ escape = pInfo->matchSet; } @@ -1276,7 +1283,7 @@ static void replaceFunc( ** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */ u8 *zOld; zOld = zOut; - zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1)); + zOut = sqlite3Realloc(zOut, (int)nOut + (nOut - nStr - 1)); if( zOut==0 ){ sqlite3_result_error_nomem(context); sqlite3_free(zOld); @@ -1877,19 +1884,12 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); nExpr = pExpr->x.pList->nExpr; pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0); +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + if( pDef==0 ) return 0; +#endif if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){ return 0; } - if( nExpr<3 ){ - aWc[3] = 0; - }else{ - Expr *pEscape = pExpr->x.pList->a[2].pExpr; - char *zEscape; - if( pEscape->op!=TK_STRING ) return 0; - zEscape = pEscape->u.zToken; - if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; - aWc[3] = zEscape[0]; - } /* The memcpy() statement assumes that the wildcard characters are ** the first three statements in the compareInfo structure. The @@ -1899,6 +1899,20 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll ); assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne ); assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet ); + + if( nExpr<3 ){ + aWc[3] = 0; + }else{ + Expr *pEscape = pExpr->x.pList->a[2].pExpr; + char *zEscape; + if( pEscape->op!=TK_STRING ) return 0; + zEscape = pEscape->u.zToken; + if( zEscape[0]==0 || zEscape[1]!=0 ) return 0; + if( zEscape[0]==aWc[0] ) return 0; + if( zEscape[0]==aWc[1] ) return 0; + aWc[3] = zEscape[0]; + } + *pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0; return 1; } @@ -1979,7 +1993,7 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(hex, 1, 0, 0, hexFunc ), - INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE), + INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), @@ -2019,7 +2033,8 @@ void sqlite3RegisterBuiltinFunctions(void){ #endif FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), - INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE), + INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), + INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), }; #ifndef SQLITE_OMIT_ALTERTABLE sqlite3AlterFunctions(); diff --git a/src/global.c b/src/global.c index a2c51f4..a89d2ca 100644 --- a/src/global.c +++ b/src/global.c @@ -140,11 +140,13 @@ const unsigned char sqlite3CtypeMap[256] = { ** enabled. */ #ifndef SQLITE_USE_URI +/* BEGIN SQLCIPHER */ # ifdef SQLITE_HAS_CODEC # define SQLITE_USE_URI 1 # else # define SQLITE_USE_URI 0 # endif +/* END SQLCIPHER */ #endif /* EVIDENCE-OF: R-38720-18127 The default setting is determined by the @@ -307,6 +309,11 @@ sqlite3_uint64 sqlite3NProfileCnt = 0; int sqlite3PendingByte = 0x40000000; #endif +/* +** Flags for select tracing and the ".selecttrace" macro of the CLI +*/ +u32 sqlite3_unsupported_selecttrace = 0; + #include "opcodes.h" /* ** Properties of opcodes. The OPFLG_INITIALIZER macro is diff --git a/src/insert.c b/src/insert.c index 93f22a8..789b3b6 100644 --- a/src/insert.c +++ b/src/insert.c @@ -180,7 +180,7 @@ static int readsTable(Parse *p, int iDb, Table *pTab){ assert( pOp!=0 ); if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ Index *pIndex; - int tnum = pOp->p2; + Pgno tnum = pOp->p2; if( tnum==pTab->tnum ){ return 1; } @@ -1606,13 +1606,13 @@ void sqlite3GenerateConstraintChecks( VdbeCoverage(v); assert( (pCol->colFlags & COLFLAG_GENERATED)==0 ); nSeenReplace++; - sqlite3ExprCode(pParse, pCol->pDflt, iReg); + sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg); sqlite3VdbeJumpHere(v, addr1); break; } case OE_Abort: sqlite3MayAbort(pParse); - /* Fall through */ + /* no break */ deliberate_fall_through case OE_Rollback: case OE_Fail: { char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, @@ -1661,6 +1661,7 @@ void sqlite3GenerateConstraintChecks( onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; inExpr; i++){ int allOk; + Expr *pCopy; Expr *pExpr = pCheck->a[i].pExpr; if( aiChng && !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng) @@ -1669,9 +1670,17 @@ void sqlite3GenerateConstraintChecks( ** updated so there is no point it verifying the check constraint */ continue; } + if( bAffinityDone==0 ){ + sqlite3TableAffinity(v, pTab, regNewData+1); + bAffinityDone = 1; + } allOk = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeVerifyAbortable(v, onError); - sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); + pCopy = sqlite3ExprDup(db, pExpr, 0); + if( !db->mallocFailed ){ + sqlite3ExprIfTrue(pParse, pCopy, allOk, SQLITE_JUMPIFNULL); + } + sqlite3ExprDelete(db, pCopy); if( onError==OE_Ignore ){ sqlite3VdbeGoto(v, ignoreDest); }else{ @@ -1831,7 +1840,7 @@ void sqlite3GenerateConstraintChecks( switch( onError ){ default: { onError = OE_Abort; - /* Fall thru into the next case */ + /* no break */ deliberate_fall_through } case OE_Rollback: case OE_Abort: @@ -1892,7 +1901,7 @@ void sqlite3GenerateConstraintChecks( #ifndef SQLITE_OMIT_UPSERT case OE_Update: { sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur); - /* Fall through */ + /* no break */ deliberate_fall_through } #endif case OE_Ignore: { @@ -1935,7 +1944,7 @@ void sqlite3GenerateConstraintChecks( sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } - VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName)); + VdbeNoopComment((v, "prep index %s", pIdx->zName)); iThisCur = iIdxCur+ix; @@ -2113,7 +2122,7 @@ void sqlite3GenerateConstraintChecks( #ifndef SQLITE_OMIT_UPSERT case OE_Update: { sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix); - /* Fall through */ + /* no break */ deliberate_fall_through } #endif case OE_Ignore: { @@ -2170,12 +2179,14 @@ void sqlite3GenerateConstraintChecks( x = *sqlite3VdbeGetOp(v, addrConflictCk); if( x.opcode!=OP_IdxRowid ){ int p2; /* New P2 value for copied conflict check opcode */ + const char *zP4; if( sqlite3OpcodeProperty[x.opcode]&OPFLG_JUMP ){ p2 = lblRecheckOk; }else{ p2 = x.p2; } - sqlite3VdbeAddOp4(v, x.opcode, x.p1, p2, x.p3, x.p4.z, x.p4type); + zP4 = x.p4type==P4_INT32 ? SQLITE_INT_TO_PTR(x.p4.i) : x.p4.z; + sqlite3VdbeAddOp4(v, x.opcode, x.p1, p2, x.p3, zP4, x.p4type); sqlite3VdbeChangeP5(v, x.p5); VdbeCoverageIf(v, p2!=x.p2); } @@ -2599,7 +2610,7 @@ static int xferOptimization( return 0; /* FROM clause does not contain a real table */ } if( pSrc->tnum==pDest->tnum && pSrc->pSchema==pDest->pSchema ){ - testcase( pSrc!=pDest ); /* Possible due to bad sqlite_master.rootpage */ + testcase( pSrc!=pDest ); /* Possible due to bad sqlite_schema.rootpage */ return 0; /* tab1 and tab2 may not be the same table */ } if( HasRowid(pDest)!=HasRowid(pSrc) ){ @@ -2783,14 +2794,13 @@ static int xferOptimization( addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( (pDest->tabFlags & TF_Autoincrement)==0 ); } - sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); if( db->mDbFlags & DBFLAG_Vacuum ){ sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); - insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID| - OPFLAG_APPEND|OPFLAG_USESEEKRESULT; + insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT; }else{ insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND; } + sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid, (char*)pDest, P4_TABLE); sqlite3VdbeChangeP5(v, insFlags); @@ -2815,7 +2825,6 @@ static int xferOptimization( sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); if( db->mDbFlags & DBFLAG_Vacuum ){ /* This INSERT command is part of a VACUUM operation, which guarantees ** that the destination table is empty. If all indexed columns use @@ -2839,10 +2848,10 @@ static int xferOptimization( idxInsFlags = OPFLAG_USESEEKRESULT; sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest); } - } - if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ + }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){ idxInsFlags |= OPFLAG_NCHANGE; } + sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData); sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); diff --git a/src/loadext.c b/src/loadext.c index 245e9b0..7007e31 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -474,8 +474,20 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_filename_database, sqlite3_filename_journal, sqlite3_filename_wal, + /* Version 3.32.0 and later */ + sqlite3_create_filename, + sqlite3_free_filename, + sqlite3_database_file_object, }; +/* True if x is the directory separator character +*/ +#if SQLITE_OS_WIN +# define DirSep(X) ((X)=='/'||(X)=='\\') +#else +# define DirSep(X) ((X)=='/') +#endif + /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a @@ -577,7 +589,7 @@ static int sqlite3LoadExtension( return SQLITE_NOMEM_BKPT; } memcpy(zAltEntry, "sqlite3_", 8); - for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){} + for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} iFile++; if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ @@ -677,7 +689,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ ** The following object holds the list of automatically loaded ** extensions. ** -** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER +** This list is shared across threads. The SQLITE_MUTEX_STATIC_MAIN ** mutex must be held while accessing this list. */ typedef struct sqlite3AutoExtList sqlite3AutoExtList; @@ -719,7 +731,7 @@ int sqlite3_auto_extension( { u32 i; #if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); #endif wsdAutoextInit; sqlite3_mutex_enter(mutex); @@ -757,7 +769,7 @@ int sqlite3_cancel_auto_extension( void (*xInit)(void) ){ #if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); #endif int i; int n = 0; @@ -784,7 +796,7 @@ void sqlite3_reset_auto_extension(void){ #endif { #if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); #endif wsdAutoextInit; sqlite3_mutex_enter(mutex); @@ -814,7 +826,7 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){ for(i=0; go; i++){ char *zErrmsg; #if SQLITE_THREADSAFE - sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); #endif #ifdef SQLITE_OMIT_LOAD_EXTENSION const sqlite3_api_routines *pThunk = 0; diff --git a/src/main.c b/src/main.c index 7e94ad3..14db1bf 100644 --- a/src/main.c +++ b/src/main.c @@ -25,19 +25,82 @@ #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) # include "sqliteicu.h" #endif + +/* +** This is an extension initializer that is a no-op and always +** succeeds, except that it fails if the fault-simulation is set +** to 500. +*/ +static int sqlite3TestExtInit(sqlite3 *db){ + (void)db; + return sqlite3FaultSim(500); +} + + +/* +** Forward declarations of external module initializer functions +** for modules that need them. +*/ +#ifdef SQLITE_ENABLE_FTS1 +int sqlite3Fts1Init(sqlite3*); +#endif +#ifdef SQLITE_ENABLE_FTS2 +int sqlite3Fts2Init(sqlite3*); +#endif +#ifdef SQLITE_ENABLE_FTS5 +int sqlite3Fts5Init(sqlite3*); +#endif #ifdef SQLITE_ENABLE_JSON1 int sqlite3Json1Init(sqlite3*); #endif #ifdef SQLITE_ENABLE_STMTVTAB int sqlite3StmtVtabInit(sqlite3*); #endif -#ifdef SQLITE_ENABLE_FTS5 -int sqlite3Fts5Init(sqlite3*); + +/* +** An array of pointers to extension initializer functions for +** built-in extensions. +*/ +static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { +#ifdef SQLITE_ENABLE_FTS1 + sqlite3Fts1Init, #endif +#ifdef SQLITE_ENABLE_FTS2 + sqlite3Fts2Init, +#endif +#ifdef SQLITE_ENABLE_FTS3 + sqlite3Fts3Init, +#endif +#ifdef SQLITE_ENABLE_FTS5 + sqlite3Fts5Init, +#endif +#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) + sqlite3IcuInit, +#endif +#ifdef SQLITE_ENABLE_RTREE + sqlite3RtreeInit, +#endif +#ifdef SQLITE_ENABLE_DBPAGE_VTAB + sqlite3DbpageRegister, +#endif +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + sqlite3DbstatRegister, +#endif + sqlite3TestExtInit, +#ifdef SQLITE_ENABLE_JSON1 + sqlite3Json1Init, +#endif +#ifdef SQLITE_ENABLE_STMTVTAB + sqlite3StmtVtabInit, +#endif +#ifdef SQLITE_ENABLE_BYTECODE_VTAB + sqlite3VdbeBytecodeVtabInit, +#endif +}; #ifndef SQLITE_AMALGAMATION /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant -** contains the text of SQLITE_VERSION macro. +** contains the text of SQLITE_VERSION macro. */ const char sqlite3_version[] = SQLITE_VERSION; #endif @@ -138,7 +201,7 @@ char *sqlite3_data_directory = 0; ** without blocking. */ int sqlite3_initialize(void){ - MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ + MUTEX_LOGIC( sqlite3_mutex *pMainMtx; ) /* The main static mutex */ int rc; /* Result code */ #ifdef SQLITE_EXTRA_INIT int bRunExtraInit = 0; /* Extra initialization needed */ @@ -161,7 +224,10 @@ int sqlite3_initialize(void){ ** must be complete. So isInit must not be set until the very end ** of this routine. */ - if( sqlite3GlobalConfig.isInit ) return SQLITE_OK; + if( sqlite3GlobalConfig.isInit ){ + sqlite3MemoryBarrier(); + return SQLITE_OK; + } /* Make sure the mutex subsystem is initialized. If unable to ** initialize the mutex subsystem, return early with the error. @@ -175,13 +241,13 @@ int sqlite3_initialize(void){ if( rc ) return rc; /* Initialize the malloc() system and the recursive pInitMutex mutex. - ** This operation is protected by the STATIC_MASTER mutex. Note that + ** This operation is protected by the STATIC_MAIN mutex. Note that ** MutexAlloc() is called for a static mutex prior to initializing the ** malloc subsystem - this implies that the allocation of a static ** mutex must not require support from the malloc subsystem. */ - MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) - sqlite3_mutex_enter(pMaster); + MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) + sqlite3_mutex_enter(pMainMtx); sqlite3GlobalConfig.isMutexInit = 1; if( !sqlite3GlobalConfig.isMallocInit ){ rc = sqlite3MallocInit(); @@ -199,7 +265,7 @@ int sqlite3_initialize(void){ if( rc==SQLITE_OK ){ sqlite3GlobalConfig.nRefInitMutex++; } - sqlite3_mutex_leave(pMaster); + sqlite3_mutex_leave(pMainMtx); /* If rc is not SQLITE_OK at this point, then either the malloc ** subsystem could not be initialized or the system failed to allocate @@ -247,6 +313,7 @@ int sqlite3_initialize(void){ if( rc==SQLITE_OK ){ sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); + sqlite3MemoryBarrier(); sqlite3GlobalConfig.isInit = 1; #ifdef SQLITE_EXTRA_INIT bRunExtraInit = 1; @@ -259,14 +326,14 @@ int sqlite3_initialize(void){ /* Go back under the static mutex and clean up the recursive ** mutex to prevent a resource leak. */ - sqlite3_mutex_enter(pMaster); + sqlite3_mutex_enter(pMainMtx); sqlite3GlobalConfig.nRefInitMutex--; if( sqlite3GlobalConfig.nRefInitMutex<=0 ){ assert( sqlite3GlobalConfig.nRefInitMutex==0 ); sqlite3_mutex_free(sqlite3GlobalConfig.pInitMutex); sqlite3GlobalConfig.pInitMutex = 0; } - sqlite3_mutex_leave(pMaster); + sqlite3_mutex_leave(pMainMtx); /* The following is just a sanity check to make sure SQLite has ** been compiled correctly. It is important to run this code, but @@ -1135,7 +1202,7 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ } sqlite3_mutex_enter(db->mutex); if( db->mTrace & SQLITE_TRACE_CLOSE ){ - db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); + db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); } /* Force xDisconnect calls on all virtual tables */ @@ -1548,8 +1615,7 @@ const char *sqlite3ErrStr(int rc){ */ static int sqliteDefaultBusyCallback( void *ptr, /* Database connection */ - int count, /* Number of times table has been busy */ - sqlite3_file *pFile /* The file on which the lock occurred */ + int count /* Number of times table has been busy */ ){ #if SQLITE_OS_WIN || HAVE_USLEEP /* This case is for systems that have support for sleeping for fractions of @@ -1563,19 +1629,6 @@ static int sqliteDefaultBusyCallback( int tmout = db->busyTimeout; int delay, prior; -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT - if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){ - if( count ){ - tmout = 0; - sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout); - return 0; - }else{ - return 1; - } - } -#else - UNUSED_PARAMETER(pFile); -#endif assert( count>=0 ); if( count < NDELAY ){ delay = delays[count]; @@ -1595,7 +1648,6 @@ static int sqliteDefaultBusyCallback( ** must be done in increments of whole seconds */ sqlite3 *db = (sqlite3 *)ptr; int tmout = ((sqlite3 *)ptr)->busyTimeout; - UNUSED_PARAMETER(pFile); if( (count+1)*1000 > tmout ){ return 0; } @@ -1613,19 +1665,10 @@ static int sqliteDefaultBusyCallback( ** If this routine returns non-zero, the lock is retried. If it ** returns 0, the operation aborts with an SQLITE_BUSY error. */ -int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){ +int sqlite3InvokeBusyHandler(BusyHandler *p){ int rc; if( p->xBusyHandler==0 || p->nBusy<0 ) return 0; - if( p->bExtraFileArg ){ - /* Add an extra parameter with the pFile pointer to the end of the - ** callback argument list */ - int (*xTra)(void*,int,sqlite3_file*); - xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler; - rc = xTra(p->pBusyArg, p->nBusy, pFile); - }else{ - /* Legacy style busy handler callback */ - rc = p->xBusyHandler(p->pBusyArg, p->nBusy); - } + rc = p->xBusyHandler(p->pBusyArg, p->nBusy); if( rc==0 ){ p->nBusy = -1; }else{ @@ -1650,7 +1693,6 @@ int sqlite3_busy_handler( db->busyHandler.xBusyHandler = xBusy; db->busyHandler.pBusyArg = pArg; db->busyHandler.nBusy = 0; - db->busyHandler.bExtraFileArg = 0; db->busyTimeout = 0; sqlite3_mutex_leave(db->mutex); return SQLITE_OK; @@ -1701,7 +1743,6 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){ sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, (void*)db); db->busyTimeout = ms; - db->busyHandler.bExtraFileArg = 1; }else{ sqlite3_busy_handler(db, 0, 0); } @@ -1718,7 +1759,7 @@ void sqlite3_interrupt(sqlite3 *db){ return; } #endif - db->u1.isInterrupted = 1; + AtomicStore(&db->u1.isInterrupted, 1); } @@ -2050,7 +2091,7 @@ void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){ sqlite3_mutex_enter(db->mutex); pOld = db->pTraceArg; db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0; - db->xTrace = (int(*)(u32,void*,void*,void*))xTrace; + db->trace.xLegacy = xTrace; db->pTraceArg = pArg; sqlite3_mutex_leave(db->mutex); return pOld; @@ -2074,7 +2115,7 @@ int sqlite3_trace_v2( if( mTrace==0 ) xTrace = 0; if( xTrace==0 ) mTrace = 0; db->mTrace = mTrace; - db->xTrace = xTrace; + db->trace.xV2 = xTrace; db->pTraceArg = pArg; sqlite3_mutex_leave(db->mutex); return SQLITE_OK; @@ -2340,7 +2381,7 @@ int sqlite3_wal_checkpoint_v2( /* If there are no active statements, clear the interrupt flag at this ** point. */ if( db->nVdbeActive==0 ){ - db->u1.isInterrupted = 0; + AtomicStore(&db->u1.isInterrupted, 0); } sqlite3_mutex_leave(db->mutex); @@ -2750,9 +2791,11 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ ** ** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to ** the VFS that should be used to open the database file. *pzFile is set to -** point to a buffer containing the name of the file to open. It is the -** responsibility of the caller to eventually call sqlite3_free() to release -** this buffer. +** point to a buffer containing the name of the file to open. The value +** stored in *pzFile is a database name acceptable to sqlite3_uri_parameter() +** and is in the same format as names created using sqlite3_create_filename(). +** The caller must invoke sqlite3_free_filename() (not sqlite3_free()!) on +** the value returned in *pzFile to avoid a memory leak. ** ** If an error occurs, then an SQLite error code is returned and *pzErrMsg ** may be set to point to a buffer containing an English language error @@ -2784,7 +2827,7 @@ int sqlite3ParseUri( int eState; /* Parser state when parsing URI */ int iIn; /* Input character index */ int iOut = 0; /* Output character index */ - u64 nByte = nUri+2; /* Bytes of space to allocate */ + u64 nByte = nUri+8; /* Bytes of space to allocate */ /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen ** method that there may be extra parameters following the file-name. */ @@ -2794,6 +2837,9 @@ int sqlite3ParseUri( zFile = sqlite3_malloc64(nByte); if( !zFile ) return SQLITE_NOMEM_BKPT; + memset(zFile, 0, 4); /* 4-byte of 0x00 is the start of DB name marker */ + zFile += 4; + iIn = 5; #ifdef SQLITE_ALLOW_URI_AUTHORITY if( strncmp(zUri+5, "///", 3)==0 ){ @@ -2883,8 +2929,7 @@ int sqlite3ParseUri( zFile[iOut++] = c; } if( eState==1 ) zFile[iOut++] = '\0'; - zFile[iOut++] = '\0'; - zFile[iOut++] = '\0'; + memset(zFile+iOut, 0, 4); /* end-of-options + empty journal filenames */ /* Check if there were any options specified that should be interpreted ** here. Options that are interpreted here include "vfs" and those that @@ -2964,13 +3009,14 @@ int sqlite3ParseUri( } }else{ - zFile = sqlite3_malloc64(nUri+2); + zFile = sqlite3_malloc64(nUri+8); if( !zFile ) return SQLITE_NOMEM_BKPT; + memset(zFile, 0, 4); + zFile += 4; if( nUri ){ memcpy(zFile, zUri, nUri); } - zFile[nUri] = '\0'; - zFile[nUri+1] = '\0'; + memset(zFile+nUri, 0, 4); flags &= ~SQLITE_OPEN_URI; } @@ -2981,7 +3027,7 @@ int sqlite3ParseUri( } parse_uri_out: if( rc!=SQLITE_OK ){ - sqlite3_free(zFile); + sqlite3_free_filename(zFile); zFile = 0; } *pFlags = flags; @@ -2989,6 +3035,22 @@ int sqlite3ParseUri( return rc; } +/* +** This routine does the core work of extracting URI parameters from a +** database filename for the sqlite3_uri_parameter() interface. +*/ +static const char *uriParameter(const char *zFilename, const char *zParam){ + zFilename += sqlite3Strlen30(zFilename) + 1; + while( zFilename[0] ){ + int x = strcmp(zFilename, zParam); + zFilename += sqlite3Strlen30(zFilename) + 1; + if( x==0 ) return zFilename; + zFilename += sqlite3Strlen30(zFilename) + 1; + } + return 0; +} + +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) /* ** Process URI filename query parameters relevant to the SQLite Encryption @@ -3001,7 +3063,9 @@ int sqlite3CodecQueryParameters( const char *zUri /* URI filename */ ){ const char *zKey; - if( (zKey = sqlite3_uri_parameter(zUri, "hexkey"))!=0 && zKey[0] ){ + if( zUri==0 ){ + return 0; + }else if( (zKey = uriParameter(zUri, "hexkey"))!=0 && zKey[0] ){ u8 iByte; int i; char zDecoded[40]; @@ -3011,10 +3075,10 @@ int sqlite3CodecQueryParameters( } sqlite3_key_v2(db, zDb, zDecoded, i/2); return 1; - }else if( (zKey = sqlite3_uri_parameter(zUri, "key"))!=0 ){ + }else if( (zKey = uriParameter(zUri, "key"))!=0 ){ sqlite3_key_v2(db, zDb, zKey, sqlite3Strlen30(zKey)); return 1; - }else if( (zKey = sqlite3_uri_parameter(zUri, "textkey"))!=0 ){ + }else if( (zKey = uriParameter(zUri, "textkey"))!=0 ){ sqlite3_key_v2(db, zDb, zKey, -1); return 1; }else{ @@ -3022,6 +3086,7 @@ int sqlite3CodecQueryParameters( } } #endif +/* END SQLCIPHER */ /* @@ -3040,6 +3105,7 @@ static int openDatabase( int isThreadsafe; /* True for threadsafe connections */ char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ + int i; /* Loop counter */ #ifdef SQLITE_ENABLE_API_ARMOR if( ppDb==0 ) return SQLITE_MISUSE_BKPT; @@ -3083,7 +3149,7 @@ static int openDatabase( SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | - SQLITE_OPEN_MASTER_JOURNAL | + SQLITE_OPEN_SUPER_JOURNAL | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_WAL @@ -3187,6 +3253,9 @@ static int openDatabase( #endif #if defined(SQLITE_DEFAULT_DEFENSIVE) | SQLITE_Defensive +#endif +#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE) + | SQLITE_LegacyAlter #endif ; sqlite3HashInit(&db->aCollSeq); @@ -3209,11 +3278,6 @@ static int openDatabase( if( db->mallocFailed ){ goto opendb_out; } - /* EVIDENCE-OF: R-08308-17224 The default collating function for all - ** strings is BINARY. - */ - db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0); - assert( db->pDfltColl!=0 ); /* Parse the filename/URI argument ** @@ -3235,7 +3299,7 @@ static int openDatabase( testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ if( ((1<<(flags&7)) & 0x46)==0 ){ - rc = SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ + rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */ }else{ rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); } @@ -3258,7 +3322,9 @@ static int openDatabase( } sqlite3BtreeEnter(db->aDb[0].pBt); db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); - if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db); + if( !db->mallocFailed ){ + sqlite3SetTextEncoding(db, SCHEMA_ENC(db)); + } sqlite3BtreeLeave(db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); @@ -3283,14 +3349,11 @@ static int openDatabase( sqlite3RegisterPerConnectionBuiltinFunctions(db); rc = sqlite3_errcode(db); -#ifdef SQLITE_ENABLE_FTS5 - /* Register any built-in FTS5 module before loading the automatic - ** extensions. This allows automatic extensions to register FTS5 - ** tokenizers and auxiliary functions. */ - if( !db->mallocFailed && rc==SQLITE_OK ){ - rc = sqlite3Fts5Init(db); + + /* Load compiled-in extensions */ + for(i=0; rc==SQLITE_OK && imallocFailed ){ - extern int sqlite3Fts1Init(sqlite3*); - rc = sqlite3Fts1Init(db); - } -#endif - -#ifdef SQLITE_ENABLE_FTS2 - if( !db->mallocFailed && rc==SQLITE_OK ){ - extern int sqlite3Fts2Init(sqlite3*); - rc = sqlite3Fts2Init(db); - } -#endif - -#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */ - if( !db->mallocFailed && rc==SQLITE_OK ){ - rc = sqlite3Fts3Init(db); - } -#endif - -#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) - if( !db->mallocFailed && rc==SQLITE_OK ){ - rc = sqlite3IcuInit(db); - } -#endif - -#ifdef SQLITE_ENABLE_RTREE - if( !db->mallocFailed && rc==SQLITE_OK){ - rc = sqlite3RtreeInit(db); - } -#endif - -#ifdef SQLITE_ENABLE_DBPAGE_VTAB - if( !db->mallocFailed && rc==SQLITE_OK){ - rc = sqlite3DbpageRegister(db); - } -#endif - -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - if( !db->mallocFailed && rc==SQLITE_OK){ - rc = sqlite3DbstatRegister(db); - } -#endif - -#ifdef SQLITE_ENABLE_JSON1 - if( !db->mallocFailed && rc==SQLITE_OK){ - rc = sqlite3Json1Init(db); - } -#endif - -#ifdef SQLITE_ENABLE_STMTVTAB - if( !db->mallocFailed && rc==SQLITE_OK){ - rc = sqlite3StmtVtabInit(db); - } -#endif - #ifdef SQLCIPHER_EXT if( !db->mallocFailed && rc==SQLITE_OK ){ extern int sqlcipherVtabInit(sqlite3 *); @@ -3413,10 +3420,12 @@ opendb_out: sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); } #endif +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) if( rc==SQLITE_OK ) sqlite3CodecQueryParameters(db, 0, zOpen); #endif - sqlite3_free(zOpen); +/* END SQLCIPHER */ + sqlite3_free_filename(zOpen); return rc & 0xff; } @@ -3643,13 +3652,15 @@ int sqlite3CantopenError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return sqlite3ReportError(SQLITE_CANTOPEN, lineno, "cannot open file"); } -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO) int sqlite3CorruptPgnoError(int lineno, Pgno pgno){ char zMsg[100]; sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno); testcase( sqlite3GlobalConfig.xLog!=0 ); return sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg); } +#endif +#ifdef SQLITE_DEBUG int sqlite3NomemError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return sqlite3ReportError(SQLITE_NOMEM, lineno, "OOM"); @@ -3852,6 +3863,13 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ }else if( op==SQLITE_FCNTL_DATA_VERSION ){ *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); rc = SQLITE_OK; + }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){ + int iNew = *(int*)pArg; + *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree); + if( iNew>=0 && iNew<=255 ){ + sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); + } + rc = SQLITE_OK; }else{ rc = sqlite3OsFileControl(fd, op, pArg); } @@ -4068,20 +4086,6 @@ int sqlite3_test_control(int op, ...){ break; } - /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) - ** - ** Set the nReserve size to N for the main database on the database - ** connection db. - */ - case SQLITE_TESTCTRL_RESERVE: { - sqlite3 *db = va_arg(ap, sqlite3*); - int x = va_arg(ap,int); - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); - sqlite3_mutex_leave(db->mutex); - break; - } - /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N) ** ** Enable or disable various optimizations for testing purposes. The @@ -4134,8 +4138,14 @@ int sqlite3_test_control(int op, ...){ /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int); ** ** Set or clear a flag that causes SQLite to verify that type, name, - ** and tbl_name fields of the sqlite_master table. This is normally + ** and tbl_name fields of the sqlite_schema table. This is normally ** on, but it is sometimes useful to turn it off for testing. + ** + ** 2020-07-22: Disabling EXTRA_SCHEMA_CHECKS also disables the + ** verification of rootpage numbers when parsing the schema. This + ** is useful to make it easier to reach strange internal error states + ** during testing. The EXTRA_SCHEMA_CHECKS setting is always enabled + ** in production. */ case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: { sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int); @@ -4250,6 +4260,83 @@ int sqlite3_test_control(int op, ...){ return rc; } +/* +** The Pager stores the Database filename, Journal filename, and WAL filename +** consecutively in memory, in that order. The database filename is prefixed +** by four zero bytes. Locate the start of the database filename by searching +** backwards for the first byte following four consecutive zero bytes. +** +** This only works if the filename passed in was obtained from the Pager. +*/ +static const char *databaseName(const char *zName){ + while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ + zName--; + } + return zName; +} + +/* +** Append text z[] to the end of p[]. Return a pointer to the first +** character after then zero terminator on the new text in p[]. +*/ +static char *appendText(char *p, const char *z){ + size_t n = strlen(z); + memcpy(p, z, n+1); + return p+n+1; +} + +/* +** Allocate memory to hold names for a database, journal file, WAL file, +** and query parameters. The pointer returned is valid for use by +** sqlite3_filename_database() and sqlite3_uri_parameter() and related +** functions. +** +** Memory layout must be compatible with that generated by the pager +** and expected by sqlite3_uri_parameter() and databaseName(). +*/ +char *sqlite3_create_filename( + const char *zDatabase, + const char *zJournal, + const char *zWal, + int nParam, + const char **azParam +){ + sqlite3_int64 nByte; + int i; + char *pResult, *p; + nByte = strlen(zDatabase) + strlen(zJournal) + strlen(zWal) + 10; + for(i=0; i0 ){ zFilename += sqlite3Strlen30(zFilename) + 1; @@ -4311,25 +4393,6 @@ sqlite3_int64 sqlite3_uri_int64( return bDflt; } -/* -** The Pager stores the Journal filename, WAL filename, and Database filename -** consecutively in memory, in that order, with prefixes \000\001\000, -** \002\000, and \003\000, in that order. Thus the three names look like query -** parameters if you start at the first prefix. -** -** This routine backs up a filename to the start of the first prefix. -** -** This only works if the filenamed passed in was obtained from the Pager. -*/ -static const char *startOfNameList(const char *zName){ - while( zName[0]!='\001' || zName[1]!=0 ){ - zName -= 3; - while( zName[0]!='\000' ){ zName--; } - zName++; - } - return zName-1; -} - /* ** Translate a filename that was handed to a VFS routine into the corresponding ** database, journal, or WAL file. @@ -4341,14 +4404,25 @@ static const char *startOfNameList(const char *zName){ ** corruption. */ const char *sqlite3_filename_database(const char *zFilename){ - return sqlite3_uri_parameter(zFilename - 3, "\003"); + return databaseName(zFilename); } const char *sqlite3_filename_journal(const char *zFilename){ - const char *z = sqlite3_uri_parameter(startOfNameList(zFilename), "\001"); - return ALWAYS(z) && z[0] ? z : 0; + zFilename = databaseName(zFilename); + zFilename += sqlite3Strlen30(zFilename) + 1; + while( zFilename[0] ){ + zFilename += sqlite3Strlen30(zFilename) + 1; + zFilename += sqlite3Strlen30(zFilename) + 1; + } + return zFilename + 1; } const char *sqlite3_filename_wal(const char *zFilename){ - return sqlite3_uri_parameter(startOfNameList(zFilename), "\002"); +#ifdef SQLITE_OMIT_WAL + return 0; +#else + zFilename = sqlite3_filename_journal(zFilename); + zFilename += sqlite3Strlen30(zFilename) + 1; + return zFilename; +#endif } /* diff --git a/src/malloc.c b/src/malloc.c index 9a2dc64..ce3b007 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -111,7 +111,7 @@ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ } mem0.alarmThreshold = n; nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - mem0.nearlyFull = (n>0 && n<=nUsed); + AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed); sqlite3_mutex_leave(mem0.mutex); excess = sqlite3_memory_used() - n; if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); @@ -190,7 +190,7 @@ int sqlite3MallocInit(void){ ** sqlite3_soft_heap_limit(). */ int sqlite3HeapNearlyFull(void){ - return mem0.nearlyFull; + return AtomicLoad(&mem0.nearlyFull); } /* @@ -254,7 +254,7 @@ static void mallocWithAlarm(int n, void **pp){ if( mem0.alarmThreshold>0 ){ sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.alarmThreshold - nFull ){ - mem0.nearlyFull = 1; + AtomicStore(&mem0.nearlyFull, 1); sqlite3MallocAlarm(nFull); if( mem0.hardLimit ){ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); @@ -264,7 +264,7 @@ static void mallocWithAlarm(int n, void **pp){ } } }else{ - mem0.nearlyFull = 0; + AtomicStore(&mem0.nearlyFull, 0); } } p = sqlite3GlobalConfig.m.xMalloc(nFull); @@ -493,10 +493,12 @@ void *sqlite3Realloc(void *pOld, u64 nBytes){ sqlite3MallocAlarm(nDiff); } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( pNew==0 && mem0.alarmThreshold>0 ){ sqlite3MallocAlarm((int)nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } +#endif if( pNew ){ nNew = sqlite3MallocSize(pNew); sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); @@ -681,7 +683,7 @@ static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); - pNew = sqlite3_realloc64(p, n); + pNew = sqlite3Realloc(p, n); if( !pNew ){ sqlite3OomFault(db); } @@ -728,11 +730,9 @@ char *sqlite3DbStrDup(sqlite3 *db, const char *z){ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){ char *zNew; assert( db!=0 ); - if( z==0 ){ - return 0; - } + assert( z!=0 || n==0 ); assert( (n&0x7fffffff)==n ); - zNew = sqlite3DbMallocRawNN(db, n+1); + zNew = z ? sqlite3DbMallocRawNN(db, n+1) : 0; if( zNew ){ memcpy(zNew, z, (size_t)n); zNew[n] = 0; @@ -771,7 +771,7 @@ void sqlite3OomFault(sqlite3 *db){ if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ db->mallocFailed = 1; if( db->nVdbeExec>0 ){ - db->u1.isInterrupted = 1; + AtomicStore(&db->u1.isInterrupted, 1); } DisableLookaside; if( db->pParse ){ @@ -790,7 +790,7 @@ void sqlite3OomFault(sqlite3 *db){ void sqlite3OomClear(sqlite3 *db){ if( db->mallocFailed && db->nVdbeExec==0 ){ db->mallocFailed = 0; - db->u1.isInterrupted = 0; + AtomicStore(&db->u1.isInterrupted, 0); assert( db->lookaside.bDisable>0 ); EnableLookaside; } diff --git a/src/mem2.c b/src/mem2.c index 51ea297..ac03150 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -379,7 +379,7 @@ void sqlite3MemSetDefault(void){ ** Set the "type" of an allocation. */ void sqlite3MemdebugSetType(void *p, u8 eType){ - if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ + if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); @@ -398,7 +398,7 @@ void sqlite3MemdebugSetType(void *p, u8 eType){ */ int sqlite3MemdebugHasType(void *p, u8 eType){ int rc = 1; - if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ + if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ @@ -420,7 +420,7 @@ int sqlite3MemdebugHasType(void *p, u8 eType){ */ int sqlite3MemdebugNoType(void *p, u8 eType){ int rc = 1; - if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ + if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){ struct MemBlockHdr *pHdr; pHdr = sqlite3MemsysGetHeader(p); assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */ diff --git a/src/mem3.c b/src/mem3.c index 2de028d..16463d6 100644 --- a/src/mem3.c +++ b/src/mem3.c @@ -118,16 +118,16 @@ static SQLITE_WSD struct Mem3Global { /* ** The minimum amount of free space that we have seen. */ - u32 mnMaster; + u32 mnKeyBlk; /* - ** iMaster is the index of the master chunk. Most new allocations - ** occur off of this chunk. szMaster is the size (in Mem3Blocks) - ** of the current master. iMaster is 0 if there is not master chunk. - ** The master chunk is not in either the aiHash[] or aiSmall[]. + ** iKeyBlk is the index of the key chunk. Most new allocations + ** occur off of this chunk. szKeyBlk is the size (in Mem3Blocks) + ** of the current key chunk. iKeyBlk is 0 if there is no key chunk. + ** The key chunk is not in either the aiHash[] or aiSmall[]. */ - u32 iMaster; - u32 szMaster; + u32 iKeyBlk; + u32 szKeyBlk; /* ** Array of lists of free blocks according to the block size @@ -263,34 +263,34 @@ static void *memsys3Checkout(u32 i, u32 nBlock){ } /* -** Carve a piece off of the end of the mem3.iMaster free chunk. -** Return a pointer to the new allocation. Or, if the master chunk +** Carve a piece off of the end of the mem3.iKeyBlk free chunk. +** Return a pointer to the new allocation. Or, if the key chunk ** is not large enough, return 0. */ -static void *memsys3FromMaster(u32 nBlock){ +static void *memsys3FromKeyBlk(u32 nBlock){ assert( sqlite3_mutex_held(mem3.mutex) ); - assert( mem3.szMaster>=nBlock ); - if( nBlock>=mem3.szMaster-1 ){ - /* Use the entire master */ - void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster); - mem3.iMaster = 0; - mem3.szMaster = 0; - mem3.mnMaster = 0; + assert( mem3.szKeyBlk>=nBlock ); + if( nBlock>=mem3.szKeyBlk-1 ){ + /* Use the entire key chunk */ + void *p = memsys3Checkout(mem3.iKeyBlk, mem3.szKeyBlk); + mem3.iKeyBlk = 0; + mem3.szKeyBlk = 0; + mem3.mnKeyBlk = 0; return p; }else{ - /* Split the master block. Return the tail. */ + /* Split the key block. Return the tail. */ u32 newi, x; - newi = mem3.iMaster + mem3.szMaster - nBlock; - assert( newi > mem3.iMaster+1 ); - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock; - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2; + newi = mem3.iKeyBlk + mem3.szKeyBlk - nBlock; + assert( newi > mem3.iKeyBlk+1 ); + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = nBlock; + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x |= 2; mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1; - mem3.szMaster -= nBlock; - mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster; - x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; - mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; - if( mem3.szMaster < mem3.mnMaster ){ - mem3.mnMaster = mem3.szMaster; + mem3.szKeyBlk -= nBlock; + mem3.aPool[newi-1].u.hdr.prevSize = mem3.szKeyBlk; + x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; + mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; + if( mem3.szKeyBlk < mem3.mnKeyBlk ){ + mem3.mnKeyBlk = mem3.szKeyBlk; } return (void*)&mem3.aPool[newi]; } @@ -304,13 +304,13 @@ static void *memsys3FromMaster(u32 nBlock){ ** This routine examines all entries on the given list and tries ** to coalesce each entries with adjacent free chunks. ** -** If it sees a chunk that is larger than mem3.iMaster, it replaces -** the current mem3.iMaster with the new larger chunk. In order for -** this mem3.iMaster replacement to work, the master chunk must be +** If it sees a chunk that is larger than mem3.iKeyBlk, it replaces +** the current mem3.iKeyBlk with the new larger chunk. In order for +** this mem3.iKeyBlk replacement to work, the key chunk must be ** linked into the hash tables. That is not the normal state of -** affairs, of course. The calling routine must link the master +** affairs, of course. The calling routine must link the key ** chunk before invoking this routine, then must unlink the (possibly -** changed) master chunk once this routine has finished. +** changed) key chunk once this routine has finished. */ static void memsys3Merge(u32 *pRoot){ u32 iNext, prev, size, i, x; @@ -337,9 +337,9 @@ static void memsys3Merge(u32 *pRoot){ }else{ size /= 4; } - if( size>mem3.szMaster ){ - mem3.iMaster = i; - mem3.szMaster = size; + if( size>mem3.szKeyBlk ){ + mem3.iKeyBlk = i; + mem3.szKeyBlk = size; } } } @@ -388,26 +388,26 @@ static void *memsys3MallocUnsafe(int nByte){ /* STEP 2: ** Try to satisfy the allocation by carving a piece off of the end - ** of the master chunk. This step usually works if step 1 fails. + ** of the key chunk. This step usually works if step 1 fails. */ - if( mem3.szMaster>=nBlock ){ - return memsys3FromMaster(nBlock); + if( mem3.szKeyBlk>=nBlock ){ + return memsys3FromKeyBlk(nBlock); } /* STEP 3: ** Loop through the entire memory pool. Coalesce adjacent free - ** chunks. Recompute the master chunk as the largest free chunk. + ** chunks. Recompute the key chunk as the largest free chunk. ** Then try again to satisfy the allocation by carving a piece off - ** of the end of the master chunk. This step happens very + ** of the end of the key chunk. This step happens very ** rarely (we hope!) */ for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){ memsys3OutOfMemory(toFree); - if( mem3.iMaster ){ - memsys3Link(mem3.iMaster); - mem3.iMaster = 0; - mem3.szMaster = 0; + if( mem3.iKeyBlk ){ + memsys3Link(mem3.iKeyBlk); + mem3.iKeyBlk = 0; + mem3.szKeyBlk = 0; } for(i=0; i=nBlock ){ - return memsys3FromMaster(nBlock); + if( mem3.szKeyBlk ){ + memsys3Unlink(mem3.iKeyBlk); + if( mem3.szKeyBlk>=nBlock ){ + return memsys3FromKeyBlk(nBlock); } } } @@ -448,23 +448,23 @@ static void memsys3FreeUnsafe(void *pOld){ mem3.aPool[i+size-1].u.hdr.size4x &= ~2; memsys3Link(i); - /* Try to expand the master using the newly freed chunk */ - if( mem3.iMaster ){ - while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){ - size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize; - mem3.iMaster -= size; - mem3.szMaster += size; - memsys3Unlink(mem3.iMaster); - x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; - mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster; + /* Try to expand the key using the newly freed chunk */ + if( mem3.iKeyBlk ){ + while( (mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x&2)==0 ){ + size = mem3.aPool[mem3.iKeyBlk-1].u.hdr.prevSize; + mem3.iKeyBlk -= size; + mem3.szKeyBlk += size; + memsys3Unlink(mem3.iKeyBlk); + x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; + mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk; } - x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; - while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){ - memsys3Unlink(mem3.iMaster+mem3.szMaster); - mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4; - mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; - mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster; + x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2; + while( (mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x&1)==0 ){ + memsys3Unlink(mem3.iKeyBlk+mem3.szKeyBlk); + mem3.szKeyBlk += mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x/4; + mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x; + mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk; } } } @@ -560,11 +560,11 @@ static int memsys3Init(void *NotUsed){ mem3.aPool = (Mem3Block *)sqlite3GlobalConfig.pHeap; mem3.nPool = (sqlite3GlobalConfig.nHeap / sizeof(Mem3Block)) - 2; - /* Initialize the master block. */ - mem3.szMaster = mem3.nPool; - mem3.mnMaster = mem3.szMaster; - mem3.iMaster = 1; - mem3.aPool[0].u.hdr.size4x = (mem3.szMaster<<2) + 2; + /* Initialize the key block. */ + mem3.szKeyBlk = mem3.nPool; + mem3.mnKeyBlk = mem3.szKeyBlk; + mem3.iKeyBlk = 1; + mem3.aPool[0].u.hdr.size4x = (mem3.szKeyBlk<<2) + 2; mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool; mem3.aPool[mem3.nPool].u.hdr.size4x = 1; @@ -624,7 +624,7 @@ void sqlite3Memsys3Dump(const char *zFilename){ fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8); }else{ fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8, - i==mem3.iMaster ? " **master**" : ""); + i==mem3.iKeyBlk ? " **key**" : ""); } } for(i=0; ip->szMax ) newSz = p->szMax; - pNew = sqlite3_realloc64(p->aData, newSz); + pNew = sqlite3Realloc(p->aData, newSz); if( pNew==0 ) return SQLITE_NOMEM; p->aData = pNew; p->szAlloc = newSz; @@ -339,12 +339,12 @@ static int memdbOpen( p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ *pOutFlags = flags | SQLITE_OPEN_MEMORY; - p->base.pMethods = &memdb_io_methods; + pFile->pMethods = &memdb_io_methods; p->szMax = sqlite3GlobalConfig.mxMemdbSize; return SQLITE_OK; } -#if 0 /* Only used to delete rollback journals, master journals, and WAL +#if 0 /* Only used to delete rollback journals, super-journals, and WAL ** files, none of which exist in memdb. So this routine is never used */ /* ** Delete the file located at zPath. If the dirSync argument is true, @@ -613,10 +613,11 @@ int sqlite3MemdbInit(void){ sqlite3_vfs *pLower = sqlite3_vfs_find(0); int sz = pLower->szOsFile; memdb_vfs.pAppData = pLower; - /* In all known configurations of SQLite, the size of a default - ** sqlite3_file is greater than the size of a memdb sqlite3_file. - ** Should that ever change, remove the following NEVER() */ - if( NEVER(sznChunkSize) ); } - p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods; + pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods; p->nSpill = nSpill; p->flags = flags; p->zJournal = zName; @@ -392,7 +392,7 @@ void sqlite3MemJournalOpen(sqlite3_file *pJfd){ int sqlite3JournalCreate(sqlite3_file *pJfd){ int rc = SQLITE_OK; MemJournal *p = (MemJournal*)pJfd; - if( p->pMethod==&MemJournalMethods && ( + if( pJfd->pMethods==&MemJournalMethods && ( #ifdef SQLITE_ENABLE_ATOMIC_WRITE p->nSpill>0 #else diff --git a/src/mutex.c b/src/mutex.c index 3ad7b89..13a9fca 100644 --- a/src/mutex.c +++ b/src/mutex.c @@ -254,6 +254,7 @@ int sqlite3MutexInit(void){ GLOBAL(int, mutexIsInit) = 1; #endif + sqlite3MemoryBarrier(); return rc; } diff --git a/src/mutex_unix.c b/src/mutex_unix.c index 9282d28..2afadde 100644 --- a/src/mutex_unix.c +++ b/src/mutex_unix.c @@ -112,7 +112,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; } **
      **
    • SQLITE_MUTEX_FAST **
    • SQLITE_MUTEX_RECURSIVE -**
    • SQLITE_MUTEX_STATIC_MASTER +**
    • SQLITE_MUTEX_STATIC_MAIN **
    • SQLITE_MUTEX_STATIC_MEM **
    • SQLITE_MUTEX_STATIC_OPEN **
    • SQLITE_MUTEX_STATIC_PRNG diff --git a/src/mutex_w32.c b/src/mutex_w32.c index 8a8ae28..09deda4 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -171,7 +171,7 @@ static int winMutexEnd(void){ **
        **
      • SQLITE_MUTEX_FAST **
      • SQLITE_MUTEX_RECURSIVE -**
      • SQLITE_MUTEX_STATIC_MASTER +**
      • SQLITE_MUTEX_STATIC_MAIN **
      • SQLITE_MUTEX_STATIC_MEM **
      • SQLITE_MUTEX_STATIC_OPEN **
      • SQLITE_MUTEX_STATIC_PRNG diff --git a/src/notify.c b/src/notify.c index 8137226..4960ab7 100644 --- a/src/notify.c +++ b/src/notify.c @@ -29,12 +29,12 @@ */ #define assertMutexHeld() \ - assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ) + assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) ) /* ** Head of a linked list of all sqlite3 objects created by this process ** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection -** is not NULL. This variable may only accessed while the STATIC_MASTER +** is not NULL. This variable may only accessed while the STATIC_MAIN ** mutex is held. */ static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0; @@ -108,20 +108,20 @@ static void addToBlockedList(sqlite3 *db){ } /* -** Obtain the STATIC_MASTER mutex. +** Obtain the STATIC_MAIN mutex. */ static void enterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); checkListProperties(0); } /* -** Release the STATIC_MASTER mutex. +** Release the STATIC_MAIN mutex. */ static void leaveMutex(void){ assertMutexHeld(); checkListProperties(0); - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); } /* @@ -232,7 +232,7 @@ void sqlite3ConnectionUnlocked(sqlite3 *db){ void *aStatic[16]; /* Starter space for aArg[]. No malloc required */ aArg = aStatic; - enterMutex(); /* Enter STATIC_MASTER mutex */ + enterMutex(); /* Enter STATIC_MAIN mutex */ /* This loop runs once for each entry in the blocked-connections list. */ for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){ @@ -315,7 +315,7 @@ void sqlite3ConnectionUnlocked(sqlite3 *db){ xUnlockNotify(aArg, nArg); } sqlite3_free(aDyn); - leaveMutex(); /* Leave STATIC_MASTER mutex */ + leaveMutex(); /* Leave STATIC_MAIN mutex */ } /* diff --git a/src/os.c b/src/os.c index b729e95..a1a276f 100644 --- a/src/os.c +++ b/src/os.c @@ -353,7 +353,7 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){ if( rc ) return 0; #endif #if SQLITE_THREADSAFE - mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); #endif sqlite3_mutex_enter(mutex); for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){ @@ -368,7 +368,7 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){ ** Unlink a VFS from the linked list */ static void vfsUnlink(sqlite3_vfs *pVfs){ - assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ); + assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) ); if( pVfs==0 ){ /* No-op */ }else if( vfsList==pVfs ){ @@ -399,7 +399,7 @@ int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ if( pVfs==0 ) return SQLITE_MISUSE_BKPT; #endif - MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) + MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) sqlite3_mutex_enter(mutex); vfsUnlink(pVfs); if( makeDflt || vfsList==0 ){ @@ -423,7 +423,7 @@ int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ int rc = sqlite3_initialize(); if( rc ) return rc; #endif - MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) + MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) sqlite3_mutex_enter(mutex); vfsUnlink(pVfs); sqlite3_mutex_leave(mutex); diff --git a/src/os_unix.c b/src/os_unix.c index 07ae4bc..fc54153 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -689,7 +689,7 @@ static int robust_open(const char *z, int f, mode_t m){ sqlite3_log(SQLITE_WARNING, "attempt to open \"%s\" as file descriptor %d", z, fd); fd = -1; - if( osOpen("/dev/null", f, m)<0 ) break; + if( osOpen("/dev/null", O_RDONLY, m)<0 ) break; } if( fd>=0 ){ if( m!=0 ){ @@ -1565,8 +1565,9 @@ static int osSetPosixAdvisoryLock( struct flock *pLock, /* The description of the lock */ unixFile *pFile /* Structure holding timeout value */ ){ + int tm = pFile->iBusyTimeout; int rc = osFcntl(h,F_SETLK,pLock); - while( rc<0 && pFile->iBusyTimeout>0 ){ + while( rc<0 && tm>0 ){ /* On systems that support some kind of blocking file lock with a timeout, ** make appropriate changes here to invoke that blocking file lock. On ** generic posix, however, there is no such API. So we simply try the @@ -1574,7 +1575,7 @@ static int osSetPosixAdvisoryLock( ** the lock is obtained. */ usleep(1000); rc = osFcntl(h,F_SETLK,pLock); - pFile->iBusyTimeout--; + tm--; } return rc; } @@ -3339,7 +3340,7 @@ static int unixRead( assert( offset>=0 ); assert( amt>0 ); - /* If this is a database file (not a journal, master-journal or temp + /* If this is a database file (not a journal, super-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 assert( pFile->pPreallocatedUnused==0 @@ -3452,7 +3453,7 @@ static int unixWrite( assert( id ); assert( amt>0 ); - /* If this is a database file (not a journal, master-journal or temp + /* If this is a database file (not a journal, super-journal or temp ** file), the bytes in the locking range should never be read or written. */ #if 0 assert( pFile->pPreallocatedUnused==0 @@ -3685,7 +3686,7 @@ static int openDirectory(const char *zFilename, int *pFd){ if( zDirname[0]!='/' ) zDirname[0] = '.'; zDirname[1] = 0; } - fd = robust_open(zDirname, O_RDONLY|O_BINARY|O_NOFOLLOW, 0); + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); } @@ -3995,7 +3996,9 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT case SQLITE_FCNTL_LOCK_TIMEOUT: { + int iOld = pFile->iBusyTimeout; pFile->iBusyTimeout = *(int*)pArg; + *(int*)pArg = iOld; return SQLITE_OK; } #endif @@ -4314,13 +4317,20 @@ static int unixShmSystemLock( assert( n>=1 && n<=SQLITE_SHM_NLOCK ); if( pShmNode->hShm>=0 ){ + int res; /* Initialize the locking parameters */ f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; - rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); + if( res==-1 ){ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); +#else + rc = SQLITE_BUSY; +#endif + } } /* Update the global lock state and do debug tracing */ @@ -4817,6 +4827,28 @@ static int unixShmLock( assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 ); assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 ); + /* Check that, if this to be a blocking lock, no locks that occur later + ** in the following list than the lock being obtained are already held: + ** + ** 1. Checkpointer lock (ofst==1). + ** 2. Write lock (ofst==0). + ** 3. Read locks (ofst>=3 && ofstiBusyTimeout==0 || ( + (ofst!=2) /* not RECOVER */ + && (ofst!=1 || (p->exclMask|p->sharedMask)==0) + && (ofst!=0 || (p->exclMask|p->sharedMask)<3) + && (ofst<3 || (p->exclMask|p->sharedMask)<(1<1 || mask==(1<pShmMutex); @@ -5660,7 +5692,7 @@ static int fillInUnixFile( if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ - pNew->pMethod = pLockingStyle; + pId->pMethods = pLockingStyle; OpenCounter(+1); verifyDbFile(pNew); } @@ -5741,7 +5773,7 @@ static int proxyTransformUnixFile(unixFile*, const char*); /* ** Search for an unused file descriptor that was opened on the database -** file (not a journal or master-journal file) identified by pathname +** file (not a journal or super-journal file) identified by pathname ** zPath with SQLITE_OPEN_XXX flags matching those passed as the second ** argument to this function. ** @@ -5875,7 +5907,7 @@ static int findCreateFileMode( while( zPath[nDb]!='-' ){ /* In normal operation, the journal file name will always contain ** a '-' character. However in 8+3 filename mode, or if a corrupt - ** rollback journal specifies a master journal with a goofy name, then + ** rollback journal specifies a super-journal with a goofy name, then ** the '-' might be missing. */ if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; nDb--; @@ -5948,12 +5980,12 @@ static int unixOpen( struct statfs fsInfo; #endif - /* If creating a master or main-file journal, this function will open + /* If creating a super- or main-file journal, this function will open ** a file-descriptor on the directory too. The first time unixSync() ** is called the directory file descriptor will be fsync()ed and close()d. */ int isNewJrnl = (isCreate && ( - eType==SQLITE_OPEN_MASTER_JOURNAL + eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_WAL )); @@ -5976,17 +6008,17 @@ static int unixOpen( assert(isExclusive==0 || isCreate); assert(isDelete==0 || isCreate); - /* The main DB, main journal, WAL file and master journal are never + /* The main DB, main journal, WAL file and super-journal are never ** automatically deleted. Nor are they ever temporary files. */ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); /* Assert that the upper layer has set one of the "file-type" flags. */ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); @@ -6179,7 +6211,7 @@ static int unixOpen( #endif assert( zPath==0 || zPath[0]=='/' - || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL + || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); diff --git a/src/os_win.c b/src/os_win.c index 32758ab..1f22fd9 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -1290,17 +1290,17 @@ int sqlite3_win32_compact_heap(LPUINT pnLargest){ */ int sqlite3_win32_reset_heap(){ int rc; - MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ + MUTEX_LOGIC( sqlite3_mutex *pMainMtx; ) /* The main static mutex */ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ - MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) + MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); ) MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); ) - sqlite3_mutex_enter(pMaster); + sqlite3_mutex_enter(pMainMtx); sqlite3_mutex_enter(pMem); winMemAssertMagic(); if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){ /* ** At this point, there should be no outstanding memory allocations on - ** the heap. Also, since both the master and memsys locks are currently + ** the heap. Also, since both the main and memsys locks are currently ** being held by us, no other function (i.e. from another thread) should ** be able to even access the heap. Attempt to destroy and recreate our ** isolated Win32 native heap now. @@ -1323,7 +1323,7 @@ int sqlite3_win32_reset_heap(){ rc = SQLITE_BUSY; } sqlite3_mutex_leave(pMem); - sqlite3_mutex_leave(pMaster); + sqlite3_mutex_leave(pMainMtx); return rc; } #endif /* SQLITE_WIN32_MALLOC */ @@ -3502,6 +3502,7 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ /* Forward references to VFS helper methods used for temporary files */ static int winGetTempname(sqlite3_vfs *, char **); static int winIsDir(const void *); +static BOOL winIsLongPathPrefix(const char *); static BOOL winIsDriveLetterAndColon(const char *); /* @@ -5022,7 +5023,7 @@ static int winOpen( #ifndef NDEBUG int isOpenJournal = (isCreate && ( - eType==SQLITE_OPEN_MASTER_JOURNAL + eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_WAL )); @@ -5043,17 +5044,17 @@ static int winOpen( assert(isExclusive==0 || isCreate); assert(isDelete==0 || isCreate); - /* The main DB, main journal, WAL file and master journal are never + /* The main DB, main journal, WAL file and super-journal are never ** automatically deleted. Nor are they ever temporary files. */ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); - assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); /* Assert that the upper layer has set one of the "file-type" flags. */ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL - || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); @@ -5265,13 +5266,15 @@ static int winOpen( } sqlite3_free(zTmpname); - pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod; + id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; pFile->pVfs = pVfs; pFile->h = h; if( isReadonly ){ pFile->ctrlFlags |= WINFILE_RDONLY; } - if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ + if( (flags & SQLITE_OPEN_MAIN_DB) + && sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) + ){ pFile->ctrlFlags |= WINFILE_PSOW; } pFile->lastErrno = NO_ERROR; @@ -5481,6 +5484,17 @@ static int winAccess( return SQLITE_OK; } +/* +** Returns non-zero if the specified path name starts with the "long path" +** prefix. +*/ +static BOOL winIsLongPathPrefix( + const char *zPathname +){ + return ( zPathname[0]=='\\' && zPathname[1]=='\\' + && zPathname[2]=='?' && zPathname[3]=='\\' ); +} + /* ** Returns non-zero if the specified path name starts with a drive letter ** followed by a colon character. @@ -5545,10 +5559,11 @@ static int winFullPathname( char *zOut; #endif - /* If this path name begins with "/X:", where "X" is any alphabetic - ** character, discard the initial "/" from the pathname. + /* If this path name begins with "/X:" or "\\?\", where "X" is any + ** alphabetic character, discard the initial "/" from the pathname. */ - if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ + if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1) + || winIsLongPathPrefix(zRelative+1)) ){ zRelative++; } diff --git a/src/pager.c b/src/pager.c index 99cbf9a..c484502 100644 --- a/src/pager.c +++ b/src/pager.c @@ -70,8 +70,8 @@ ** (5) All writes to the database file are synced prior to the rollback journal ** being deleted, truncated, or zeroed. ** -** (6) If a master journal file is used, then all writes to the database file -** are synced prior to the master journal being deleted. +** (6) If a super-journal file is used, then all writes to the database file +** are synced prior to the super-journal being deleted. ** ** Definition: Two databases (or the same database at two points it time) ** are said to be "logically equivalent" if they give the same answer to @@ -409,6 +409,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ /* ** A macro used for invoking the codec if there is one */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC # define CODEC1(P,D,N,X,E) \ if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; } @@ -419,6 +420,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ # define CODEC1(P,D,N,X,E) /* NO-OP */ # define CODEC2(P,D,N,X,E,O) O=(char*)D #endif +/* END SQLCIPHER */ /* ** The maximum allowed sector size. 64KiB. If the xSectorsize() method @@ -502,29 +504,29 @@ struct PagerSavepoint { ** need only update the change-counter once, for the first transaction ** committed. ** -** setMaster +** setSuper ** ** When PagerCommitPhaseOne() is called to commit a transaction, it may -** (or may not) specify a master-journal name to be written into the +** (or may not) specify a super-journal name to be written into the ** journal file before it is synced to disk. ** -** Whether or not a journal file contains a master-journal pointer affects +** Whether or not a journal file contains a super-journal pointer affects ** the way in which the journal file is finalized after the transaction is ** committed or rolled back when running in "journal_mode=PERSIST" mode. -** If a journal file does not contain a master-journal pointer, it is +** If a journal file does not contain a super-journal pointer, it is ** finalized by overwriting the first journal header with zeroes. If -** it does contain a master-journal pointer the journal file is finalized +** it does contain a super-journal pointer the journal file is finalized ** by truncating it to zero bytes, just as if the connection were ** running in "journal_mode=truncate" mode. ** -** Journal files that contain master journal pointers cannot be finalized +** Journal files that contain super-journal pointers cannot be finalized ** simply by overwriting the first journal-header with zeroes, as the -** master journal pointer could interfere with hot-journal rollback of any +** super-journal pointer could interfere with hot-journal rollback of any ** subsequently interrupted transaction that reuses the journal file. ** ** The flag is cleared as soon as the journal file is finalized (either ** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the -** journal file from being successfully finalized, the setMaster flag +** journal file from being successfully finalized, the setSuper flag ** is cleared anyway (and the pager will move to ERROR state). ** ** doNotSpill @@ -656,7 +658,7 @@ struct Pager { u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */ u8 eLock; /* Current lock held on database file */ u8 changeCountDone; /* Set after incrementing the change-counter */ - u8 setMaster; /* True if a m-j name has been written to jrnl */ + u8 setSuper; /* Super-jrnl name is written into jrnl */ u8 doNotSpill; /* Do not spill the cache when non-zero */ u8 subjInMemory; /* True to use in-memory sub-journals */ u8 bUseFetch; /* True to use xFetch() */ @@ -705,12 +707,14 @@ struct Pager { #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */ void (*xCodecFree)(void*); /* Destructor for the codec */ void *pCodec; /* First argument to xCodec... methods */ #endif +/* END SQLCIPHER */ char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ #ifndef SQLITE_OMIT_WAL @@ -806,11 +810,6 @@ static const unsigned char aJournalMagic[] = { # define USEFETCH(x) 0 #endif -/* -** The maximum legal page number is (2^31 - 1). -*/ -#define PAGER_MAX_PGNO 2147483647 - /* ** The argument to this macro is a file descriptor (type sqlite3_file*). ** Return 0 if it is not open, or non-zero (but not 1) if it is. @@ -837,9 +836,11 @@ static const unsigned char aJournalMagic[] = { int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( pPager->xCodec!=0 ) return 0; #endif +/* END SQLCIPHER */ #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; @@ -943,7 +944,7 @@ static int assert_pager_state(Pager *p){ assert( pPager->dbSize==pPager->dbOrigSize ); assert( pPager->dbOrigSize==pPager->dbFileSize ); assert( pPager->dbOrigSize==pPager->dbHintSize ); - assert( pPager->setMaster==0 ); + assert( pPager->setSuper==0 ); break; case PAGER_WRITER_CACHEMOD: @@ -1074,9 +1075,11 @@ static void setGetterMethod(Pager *pPager){ pPager->xGet = getPageError; #if SQLITE_MAX_MMAP_SIZE>0 }else if( USEFETCH(pPager) +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC && pPager->xCodec==0 #endif +/* END SQLCIPHER */ ){ pPager->xGet = getPageMMap; #endif /* SQLITE_MAX_MMAP_SIZE>0 */ @@ -1301,66 +1304,66 @@ static void checkPage(PgHdr *pPg){ /* ** When this is called the journal file for pager pPager must be open. -** This function attempts to read a master journal file name from the +** This function attempts to read a super-journal file name from the ** end of the file and, if successful, copies it into memory supplied -** by the caller. See comments above writeMasterJournal() for the format -** used to store a master journal file name at the end of a journal file. +** by the caller. See comments above writeSuperJournal() for the format +** used to store a super-journal file name at the end of a journal file. ** -** zMaster must point to a buffer of at least nMaster bytes allocated by +** zSuper must point to a buffer of at least nSuper bytes allocated by ** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is -** enough space to write the master journal name). If the master journal -** name in the journal is longer than nMaster bytes (including a -** nul-terminator), then this is handled as if no master journal name +** enough space to write the super-journal name). If the super-journal +** name in the journal is longer than nSuper bytes (including a +** nul-terminator), then this is handled as if no super-journal name ** were present in the journal. ** -** If a master journal file name is present at the end of the journal -** file, then it is copied into the buffer pointed to by zMaster. A -** nul-terminator byte is appended to the buffer following the master -** journal file name. +** If a super-journal file name is present at the end of the journal +** file, then it is copied into the buffer pointed to by zSuper. A +** nul-terminator byte is appended to the buffer following the +** super-journal file name. ** -** If it is determined that no master journal file name is present -** zMaster[0] is set to 0 and SQLITE_OK returned. +** If it is determined that no super-journal file name is present +** zSuper[0] is set to 0 and SQLITE_OK returned. ** ** If an error occurs while reading from the journal file, an SQLite ** error code is returned. */ -static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){ +static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u32 nSuper){ int rc; /* Return code */ - u32 len; /* Length in bytes of master journal name */ + u32 len; /* Length in bytes of super-journal name */ i64 szJ; /* Total size in bytes of journal file pJrnl */ u32 cksum; /* MJ checksum value read from journal */ u32 u; /* Unsigned loop counter */ unsigned char aMagic[8]; /* A buffer to hold the magic header */ - zMaster[0] = '\0'; + zSuper[0] = '\0'; if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ)) || szJ<16 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) - || len>=nMaster + || len>=nSuper || len>szJ-16 || len==0 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) || memcmp(aMagic, aJournalMagic, 8) - || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len)) + || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zSuper, len, szJ-16-len)) ){ return rc; } - /* See if the checksum matches the master journal name */ + /* See if the checksum matches the super-journal name */ for(u=0; usetMaster==0 ); + assert( pPager->setSuper==0 ); assert( !pagerUseWal(pPager) ); - if( !zMaster + if( !zSuper || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || !isOpen(pPager->jfd) ){ return SQLITE_OK; } - pPager->setMaster = 1; + pPager->setSuper = 1; assert( pPager->journalHdr <= pPager->journalOff ); - /* Calculate the length in bytes and the checksum of zMaster */ - for(nMaster=0; zMaster[nMaster]; nMaster++){ - cksum += zMaster[nMaster]; + /* Calculate the length in bytes and the checksum of zSuper */ + for(nSuper=0; zSuper[nSuper]; nSuper++){ + cksum += zSuper[nSuper]; } /* If in full-sync mode, advance to the next disk sector before writing - ** the master journal name. This is in case the previous page written to + ** the super-journal name. This is in case the previous page written to ** the journal has already been synced. */ if( pPager->fullSync ){ @@ -1739,25 +1742,25 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ } iHdrOff = pPager->journalOff; - /* Write the master journal data to the end of the journal file. If + /* Write the super-journal data to the end of the journal file. If ** an error occurs, return the error code to the caller. */ if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager)))) - || (0 != (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster))) - || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum))) + || (0 != (rc = sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4))) + || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper))) + || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum))) || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, - iHdrOff+4+nMaster+8))) + iHdrOff+4+nSuper+8))) ){ return rc; } - pPager->journalOff += (nMaster+20); + pPager->journalOff += (nSuper+20); /* If the pager is in peristent-journal mode, then the physical - ** journal-file may extend past the end of the master-journal name + ** journal-file may extend past the end of the super-journal name ** and 8 bytes of magic data just written to the file. This is ** dangerous because the code to rollback a hot-journal file - ** will not be able to find the master-journal name to determine + ** will not be able to find the super-journal name to determine ** whether or not the journal is hot. ** ** Easiest thing to do in this scenario is to truncate the journal @@ -1918,7 +1921,7 @@ static void pager_unlock(Pager *pPager){ pPager->journalOff = 0; pPager->journalHdr = 0; - pPager->setMaster = 0; + pPager->setSuper = 0; } /* @@ -2034,7 +2037,7 @@ static int pagerFlushOnCommit(Pager *pPager, int bCommit){ ** to the first error encountered (the journal finalization one) is ** returned. */ -static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ +static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ int rc = SQLITE_OK; /* Error code from journal finalization operation */ int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ @@ -2086,7 +2089,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) ){ - rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile); + rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); pPager->journalOff = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if @@ -2159,7 +2162,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ rc2 = pagerUnlockDb(pPager, SHARED_LOCK); } pPager->eState = PAGER_READER; - pPager->setMaster = 0; + pPager->setSuper = 0; return (rc==SQLITE_OK?rc2:rc); } @@ -2229,6 +2232,7 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ ** Report the current page size and number of reserved bytes back ** to the codec. */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC static void pagerReportSize(Pager *pPager){ if( pPager->xCodecSizeChng ){ @@ -2239,7 +2243,9 @@ static void pagerReportSize(Pager *pPager){ #else # define pagerReportSize(X) /* No-op if we do not support a codec */ #endif +/* END SQLCIPHER */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC /* ** Make sure the number of reserved bits is the same in the destination @@ -2253,6 +2259,7 @@ void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){ } } #endif +/* END SQLCIPHER */ /* ** Read a single page from either the journal file (if isMainJrnl==1) or @@ -2305,11 +2312,13 @@ static int pager_playback_one_page( char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ int isSynced; /* True if journal page is synced */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC /* The jrnlEnc flag is true if Journal pages should be passed through ** the codec. It is false for pure in-memory journals. */ const int jrnlEnc = (isMainJrnl || pPager->subjInMemory==0); #endif +/* END SQLCIPHER */ assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ @@ -2440,6 +2449,7 @@ static int pager_playback_one_page( ** is if the data was just read from an in-memory sub-journal. In that ** case it must be encrypted here before it is copied into the database ** file. */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( !jrnlEnc ){ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); @@ -2447,12 +2457,14 @@ static int pager_playback_one_page( CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); }else #endif +/* END SQLCIPHER */ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } if( pPager->pBackup ){ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( jrnlEnc ){ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); @@ -2460,6 +2472,7 @@ static int pager_playback_one_page( CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); }else #endif +/* END SQLCIPHER */ sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); } }else if( !isMainJrnl && pPg==0 ){ @@ -2512,45 +2525,47 @@ static int pager_playback_one_page( } /* Decode the page just read from disk */ +/* BEGIN SQLCIPHER */ #if SQLITE_HAS_CODEC if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } #endif +/* END SQLCIPHER */ sqlite3PcacheRelease(pPg); } return rc; } /* -** Parameter zMaster is the name of a master journal file. A single journal -** file that referred to the master journal file has just been rolled back. -** This routine checks if it is possible to delete the master journal file, +** Parameter zSuper is the name of a super-journal file. A single journal +** file that referred to the super-journal file has just been rolled back. +** This routine checks if it is possible to delete the super-journal file, ** and does so if it is. ** -** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not +** Argument zSuper may point to Pager.pTmpSpace. So that buffer is not ** available for use within this function. ** -** When a master journal file is created, it is populated with the names +** When a super-journal file is created, it is populated with the names ** of all of its child journals, one after another, formatted as utf-8 ** encoded text. The end of each child journal file is marked with a -** nul-terminator byte (0x00). i.e. the entire contents of a master journal +** nul-terminator byte (0x00). i.e. the entire contents of a super-journal ** file for a transaction involving two databases might be: ** ** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00" ** -** A master journal file may only be deleted once all of its child +** A super-journal file may only be deleted once all of its child ** journals have been rolled back. ** -** This function reads the contents of the master-journal file into +** This function reads the contents of the super-journal file into ** memory and loops through each of the child journal names. For ** each child journal, it checks if: ** ** * if the child journal exists, and if so -** * if the child journal contains a reference to master journal -** file zMaster +** * if the child journal contains a reference to super-journal +** file zSuper ** ** If a child journal can be found that matches both of the criteria ** above, this function returns without doing anything. Otherwise, if -** no such child journal can be found, file zMaster is deleted from +** no such child journal can be found, file zSuper is deleted from ** the file-system using sqlite3OsDelete(). ** ** If an IO error within this function, an error code is returned. This @@ -2559,96 +2574,100 @@ static int pager_playback_one_page( ** occur, SQLITE_OK is returned. ** ** TODO: This function allocates a single block of memory to load -** the entire contents of the master journal file. This could be +** the entire contents of the super-journal file. This could be ** a couple of kilobytes or so - potentially larger than the page ** size. */ -static int pager_delmaster(Pager *pPager, const char *zMaster){ +static int pager_delsuper(Pager *pPager, const char *zSuper){ sqlite3_vfs *pVfs = pPager->pVfs; int rc; /* Return code */ - sqlite3_file *pMaster; /* Malloc'd master-journal file descriptor */ + sqlite3_file *pSuper; /* Malloc'd super-journal file descriptor */ sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ - char *zMasterJournal = 0; /* Contents of master journal file */ - i64 nMasterJournal; /* Size of master journal file */ + char *zSuperJournal = 0; /* Contents of super-journal file */ + i64 nSuperJournal; /* Size of super-journal file */ char *zJournal; /* Pointer to one journal within MJ file */ - char *zMasterPtr; /* Space to hold MJ filename from a journal file */ - int nMasterPtr; /* Amount of space allocated to zMasterPtr[] */ + char *zSuperPtr; /* Space to hold super-journal filename */ + int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ - /* Allocate space for both the pJournal and pMaster file descriptors. - ** If successful, open the master journal file for reading. + /* Allocate space for both the pJournal and pSuper file descriptors. + ** If successful, open the super-journal file for reading. */ - pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); - pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile); - if( !pMaster ){ + pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); + if( !pSuper ){ rc = SQLITE_NOMEM_BKPT; + pJournal = 0; }else{ - const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); - rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); + const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); + rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); + pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); } - if( rc!=SQLITE_OK ) goto delmaster_out; + if( rc!=SQLITE_OK ) goto delsuper_out; - /* Load the entire master journal file into space obtained from - ** sqlite3_malloc() and pointed to by zMasterJournal. Also obtain - ** sufficient space (in zMasterPtr) to hold the names of master - ** journal files extracted from regular rollback-journals. + /* Load the entire super-journal file into space obtained from + ** sqlite3_malloc() and pointed to by zSuperJournal. Also obtain + ** sufficient space (in zSuperPtr) to hold the names of super-journal + ** files extracted from regular rollback-journals. */ - rc = sqlite3OsFileSize(pMaster, &nMasterJournal); - if( rc!=SQLITE_OK ) goto delmaster_out; - nMasterPtr = pVfs->mxPathname+1; - zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 2); - if( !zMasterJournal ){ + rc = sqlite3OsFileSize(pSuper, &nSuperJournal); + if( rc!=SQLITE_OK ) goto delsuper_out; + nSuperPtr = pVfs->mxPathname+1; + zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); + if( !zSuperJournal ){ rc = SQLITE_NOMEM_BKPT; - goto delmaster_out; + goto delsuper_out; } - zMasterPtr = &zMasterJournal[nMasterJournal+2]; - rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0); - if( rc!=SQLITE_OK ) goto delmaster_out; - zMasterJournal[nMasterJournal] = 0; - zMasterJournal[nMasterJournal+1] = 0; + zSuperPtr = &zSuperJournal[nSuperJournal+2]; + rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); + if( rc!=SQLITE_OK ) goto delsuper_out; + zSuperJournal[nSuperJournal] = 0; + zSuperJournal[nSuperJournal+1] = 0; - zJournal = zMasterJournal; - while( (zJournal-zMasterJournal)pageSize; @@ -2839,8 +2858,8 @@ static int pager_playback(Pager *pPager, int isHot){ goto end_playback; } - /* Read the master journal name from the journal, if it is present. - ** If a master journal file name is specified, but the file is not + /* Read the super-journal name from the journal, if it is present. + ** If a super-journal file name is specified, but the file is not ** present on disk, then the journal is not hot and does not need to be ** played back. ** @@ -2850,12 +2869,12 @@ static int pager_playback(Pager *pPager, int isHot){ ** mxPathname is 512, which is the same as the minimum allowable value ** for pageSize. */ - zMaster = pPager->pTmpSpace; - rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); - if( rc==SQLITE_OK && zMaster[0] ){ - rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); + zSuper = pPager->pTmpSpace; + rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); + if( rc==SQLITE_OK && zSuper[0] ){ + rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); } - zMaster = 0; + zSuper = 0; if( rc!=SQLITE_OK || !res ){ goto end_playback; } @@ -2982,8 +3001,8 @@ end_playback: pPager->changeCountDone = pPager->tempFile; if( rc==SQLITE_OK ){ - zMaster = pPager->pTmpSpace; - rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); + zSuper = pPager->pTmpSpace; + rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK @@ -2992,14 +3011,14 @@ end_playback: rc = sqlite3PagerSync(pPager, 0); } if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0); + rc = pager_end_transaction(pPager, zSuper[0]!='\0', 0); testcase( rc!=SQLITE_OK ); } - if( rc==SQLITE_OK && zMaster[0] && res ){ - /* If there was a master journal and this routine will return success, - ** see if it is possible to delete the master journal. + if( rc==SQLITE_OK && zSuper[0] && res ){ + /* If there was a super-journal and this routine will return success, + ** see if it is possible to delete the super-journal. */ - rc = pager_delmaster(pPager, zMaster); + rc = pager_delsuper(pPager, zSuper); testcase( rc!=SQLITE_OK ); } if( isHot && nPlayback ){ @@ -3380,7 +3399,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){ /* ** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback -** the entire master journal file. The case pSavepoint==NULL occurs when +** the entire super-journal file. The case pSavepoint==NULL occurs when ** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction ** savepoint. ** @@ -3844,7 +3863,7 @@ void *sqlite3PagerTempSpace(Pager *pPager){ ** ** Regardless of mxPage, return the current maximum page count. */ -int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ +Pgno sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){ if( mxPage>0 ){ pPager->mxPgno = mxPage; } @@ -4216,9 +4235,11 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ sqlite3PageFree(pTmp); sqlite3PcacheClose(pPager->pPCache); +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); #endif +/* END SQLCIPHER */ assert( !pPager->aSavepoint && !pPager->pInJournal ); assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); @@ -4561,11 +4582,13 @@ static int subjournalPage(PgHdr *pPg){ i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; +/* BEGIN SQLCIPHER */ #if SQLITE_HAS_CODEC if( !pPager->subjInMemory ){ CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); }else #endif +/* END SQLCIPHER */ pData2 = pData; PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); @@ -4838,30 +4861,55 @@ int sqlite3PagerOpen( ** Database file handle (pVfs->szOsFile bytes) ** Sub-journal file handle (journalFileSize bytes) ** Main journal file handle (journalFileSize bytes) - ** \0\1\0 journal prefix (3 bytes) - ** Journal filename (nPathname+8+1 bytes) - ** \2\0 WAL prefix (2 bytes) - ** WAL filename (nPathname+4+1 bytes) - ** \3\0 database prefix (2 bytes) + ** Ptr back to the Pager (sizeof(Pager*) bytes) + ** \0\0\0\0 database prefix (4 bytes) ** Database file name (nPathname+1 bytes) ** URI query parameters (nUriByte bytes) - ** \0\0 terminator (2 bytes) + ** Journal filename (nPathname+8+1 bytes) + ** WAL filename (nPathname+4+1 bytes) + ** \0\0\0 terminator (3 bytes) + ** + ** Some 3rd-party software, over which we have no control, depends on + ** the specific order of the filenames and the \0 separators between them + ** so that it can (for example) find the database filename given the WAL + ** filename without using the sqlite3_filename_database() API. This is a + ** misuse of SQLite and a bug in the 3rd-party software, but the 3rd-party + ** software is in widespread use, so we try to avoid changing the filename + ** order and formatting if possible. In particular, the details of the + ** filename format expected by 3rd-party software should be as follows: + ** + ** - Main Database Path + ** - \0 + ** - Multiple URI components consisting of: + ** - Key + ** - \0 + ** - Value + ** - \0 + ** - \0 + ** - Journal Path + ** - \0 + ** - WAL Path (zWALName) + ** - \0 + ** + ** The sqlite3_create_filename() interface and the databaseFilename() utility + ** that is used by sqlite3_filename_database() and kin also depend on the + ** specific formatting and order of the various filenames, so if the format + ** changes here, be sure to change it there as well. */ pPtr = (u8 *)sqlite3MallocZero( ROUND8(sizeof(*pPager)) + /* Pager structure */ ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ - 3 + /* Journal prefix */ - nPathname + 8 + 1 + /* Journal filename */ -#ifndef SQLITE_OMIT_WAL - 2 + /* WAL prefix */ - nPathname + 4 + 1 + /* WAL filename */ -#endif - 2 + /* Database prefix */ + sizeof(pPager) + /* Space to hold a pointer */ + 4 + /* Database prefix */ nPathname + 1 + /* database filename */ nUriByte + /* query parameters */ - 2 /* Terminator */ + nPathname + 8 + 1 + /* Journal filename */ +#ifndef SQLITE_OMIT_WAL + nPathname + 4 + 1 + /* WAL filename */ +#endif + 3 /* Terminator */ ); assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); if( !pPtr ){ @@ -4874,10 +4922,22 @@ int sqlite3PagerOpen( pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize; assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); + memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager); + + /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ + pPtr += 4; /* Skip zero prefix */ + pPager->zFilename = (char*)pPtr; + if( nPathname>0 ){ + memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1; + if( zUri ){ + memcpy(pPtr, zUri, nUriByte); pPtr += nUriByte; + }else{ + pPtr++; + } + } /* Fill in Pager.zJournal */ - pPtr[1] = '\001'; pPtr += 3; if( nPathname>0 ){ pPager->zJournal = (char*)pPtr; memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; @@ -4888,12 +4948,10 @@ int sqlite3PagerOpen( #endif }else{ pPager->zJournal = 0; - pPtr++; } #ifndef SQLITE_OMIT_WAL /* Fill in Pager.zWal */ - pPtr[0] = '\002'; pPtr[1] = 0; pPtr += 2; if( nPathname>0 ){ pPager->zWal = (char*)pPtr; memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; @@ -4904,21 +4962,9 @@ int sqlite3PagerOpen( #endif }else{ pPager->zWal = 0; - pPtr++; } #endif - /* Fill in the Pager.zFilename and pPager.zQueryParam fields */ - pPtr[0] = '\003'; pPtr[1] = 0; pPtr += 2; - pPager->zFilename = (char*)pPtr; - if( nPathname>0 ){ - memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1; - if( zUri ){ - memcpy(pPtr, zUri, nUriByte); /* pPtr += nUriByte; // not needed */ - } - /* Double-zero terminator implied by the sqlite3MallocZero */ - } - if( nPathname ) sqlite3DbFree(0, zPathname); pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; @@ -5077,6 +5123,19 @@ act_like_temp_file: return SQLITE_OK; } +/* +** Return the sqlite3_file for the main database given the name +** of the corresonding WAL or Journal name as passed into +** xOpen. +*/ +sqlite3_file *sqlite3_database_file_object(const char *zName){ + Pager *pPager; + while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ + zName--; + } + pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); + return pPager->fd; +} /* @@ -5097,8 +5156,8 @@ act_like_temp_file: ** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK ** is returned. ** -** This routine does not check if there is a master journal filename -** at the end of the file. If there is, and that master journal file +** This routine does not check if there is a super-journal filename +** at the end of the file. If there is, and that super-journal file ** does not exist, then the journal file is not really hot. In this ** case this routine will return a false-positive. The pager_playback() ** routine will discover that the journal file is not really hot and @@ -5547,7 +5606,7 @@ static int getPageNormal( if( pPg->pPager && !noContent ){ /* In this case the pcache already contains an initialized copy of ** the page. Return without further ado. */ - assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); + assert( pgno!=PAGER_MJ_PGNO(pPager) ); pPager->aStat[PAGER_STAT_HIT]++; return SQLITE_OK; @@ -5555,10 +5614,10 @@ static int getPageNormal( /* The pager cache has created a new page. Its content needs to ** be initialized. But first some error checks: ** - ** (1) The maximum page number is 2^31 + ** (*) obsolete. Was: maximum page number is 2^31 ** (2) Never try to fetch the locking page */ - if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ + if( pgno==PAGER_MJ_PGNO(pPager) ){ rc = SQLITE_CORRUPT_BKPT; goto pager_acquire_err; } @@ -5632,9 +5691,11 @@ static int getPageMMap( ); assert( USEFETCH(pPager) ); +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC assert( pPager->xCodec==0 ); #endif +/* END SQLCIPHER */ /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here ** allows the compiler optimizer to reuse the results of the "pgno>1" @@ -5765,7 +5826,6 @@ void sqlite3PagerUnrefPageOne(DbPage *pPg){ assert( pPg->pgno==1 ); assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */ pPager = pPg->pPager; - sqlite3PagerResetLockTimeout(pPager); sqlite3PcacheRelease(pPg); pagerUnlockIfUnused(pPager); } @@ -5847,7 +5907,7 @@ static int pager_open_journal(Pager *pPager){ /* TODO: Check if all of these are really required. */ pPager->nRec = 0; pPager->journalOff = 0; - pPager->setMaster = 0; + pPager->setSuper = 0; pPager->journalHdr = 0; rc = writeJournalHdr(pPager); } @@ -6359,9 +6419,9 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ ** If successful, or if called on a pager for which it is a no-op, this ** function returns SQLITE_OK. Otherwise, an IO error code is returned. */ -int sqlite3PagerSync(Pager *pPager, const char *zMaster){ +int sqlite3PagerSync(Pager *pPager, const char *zSuper){ int rc = SQLITE_OK; - void *pArg = (void*)zMaster; + void *pArg = (void*)zSuper; rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg); if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; if( rc==SQLITE_OK && !pPager->noSync ){ @@ -6399,10 +6459,10 @@ int sqlite3PagerExclusiveLock(Pager *pPager){ } /* -** Sync the database file for the pager pPager. zMaster points to the name -** of a master journal file that should be written into the individual -** journal file. zMaster may be NULL, which is interpreted as no master -** journal (a single database transaction). +** Sync the database file for the pager pPager. zSuper points to the name +** of a super-journal file that should be written into the individual +** journal file. zSuper may be NULL, which is interpreted as no +** super-journal (a single database transaction). ** ** This routine ensures that: ** @@ -6414,9 +6474,9 @@ int sqlite3PagerExclusiveLock(Pager *pPager){ ** ** The only thing that remains to commit the transaction is to finalize ** (delete, truncate or zero the first part of) the journal file (or -** delete the master journal file if specified). +** delete the super-journal file if specified). ** -** Note that if zMaster==NULL, this does not overwrite a previous value +** Note that if zSuper==NULL, this does not overwrite a previous value ** passed to an sqlite3PagerCommitPhaseOne() call. ** ** If the final parameter - noSync - is true, then the database file itself @@ -6426,7 +6486,7 @@ int sqlite3PagerExclusiveLock(Pager *pPager){ */ int sqlite3PagerCommitPhaseOne( Pager *pPager, /* Pager object */ - const char *zMaster, /* If not NULL, the master journal name */ + const char *zSuper, /* If not NULL, the super-journal name */ int noSync /* True to omit the xSync on the db file */ ){ int rc = SQLITE_OK; /* Return code */ @@ -6444,8 +6504,8 @@ int sqlite3PagerCommitPhaseOne( /* Provide the ability to easily simulate an I/O error during testing */ if( sqlite3FaultSim(400) ) return SQLITE_IOERR; - PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", - pPager->zFilename, zMaster, pPager->dbSize)); + PAGERTRACE(("DATABASE SYNC: File=%s zSuper=%s nSize=%d\n", + pPager->zFilename, zSuper, pPager->dbSize)); /* If no database changes have been made, return early. */ if( pPager->eStatefd; - int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */ + int bBatch = zSuper==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */ && (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC) && !pPager->noSync && sqlite3JournalIsInMemory(pPager->jfd); @@ -6522,7 +6582,7 @@ int sqlite3PagerCommitPhaseOne( || pPager->journalMode==PAGER_JOURNALMODE_OFF || pPager->journalMode==PAGER_JOURNALMODE_WAL ); - if( !zMaster && isOpen(pPager->jfd) + if( !zSuper && isOpen(pPager->jfd) && pPager->journalOff==jrnlBufferSize(pPager) && pPager->dbSize>=pPager->dbOrigSize && (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty) @@ -6543,7 +6603,7 @@ int sqlite3PagerCommitPhaseOne( } #else /* SQLITE_ENABLE_ATOMIC_WRITE */ #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE - if( zMaster ){ + if( zSuper ){ rc = sqlite3JournalCreate(pPager->jfd); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; assert( bBatch==0 ); @@ -6553,11 +6613,11 @@ int sqlite3PagerCommitPhaseOne( #endif /* !SQLITE_ENABLE_ATOMIC_WRITE */ if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - /* Write the master journal name into the journal file. If a master - ** journal file name has already been written to the journal file, - ** or if zMaster is NULL (no master journal), then this call is a no-op. + /* Write the super-journal name into the journal file. If a + ** super-journal file name has already been written to the journal file, + ** or if zSuper is NULL (no super-journal), then this call is a no-op. */ - rc = writeMasterJournal(pPager, zMaster); + rc = writeSuperJournal(pPager, zSuper); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; /* Sync the journal file and write all dirty pages to the database. @@ -6625,7 +6685,7 @@ int sqlite3PagerCommitPhaseOne( /* Finally, sync the database file. */ if( !noSync ){ - rc = sqlite3PagerSync(pPager, zMaster); + rc = sqlite3PagerSync(pPager, zSuper); } IOTRACE(("DBSYNC %p\n", pPager)) } @@ -6690,7 +6750,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ } PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - rc = pager_end_transaction(pPager, pPager->setMaster, 1); + rc = pager_end_transaction(pPager, pPager->setSuper, 1); return pager_error(pPager, rc); } @@ -6735,7 +6795,7 @@ int sqlite3PagerRollback(Pager *pPager){ if( pagerUseWal(pPager) ){ int rc2; rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); - rc2 = pager_end_transaction(pPager, pPager->setMaster, 0); + rc2 = pager_end_transaction(pPager, pPager->setSuper, 0); if( rc==SQLITE_OK ) rc = rc2; }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ int eState = pPager->eState; @@ -7038,8 +7098,8 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ ** sqlite3_uri_parameter() and sqlite3_filename_database() and friends. */ const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){ - static const char zFake[] = { 0x00, 0x01, 0x00, 0x00, 0x00 }; - return (nullIfMemDb && pPager->memDb) ? &zFake[3] : pPager->zFilename; + static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + return (nullIfMemDb && pPager->memDb) ? &zFake[4] : pPager->zFilename; } /* @@ -7058,16 +7118,6 @@ sqlite3_file *sqlite3PagerFile(Pager *pPager){ return pPager->fd; } -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -/* -** Reset the lock timeout for pager. -*/ -void sqlite3PagerResetLockTimeout(Pager *pPager){ - int x = 0; - sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x); -} -#endif - /* ** Return the file handle for the journal file (if it exists). ** This will be either the rollback journal or the WAL file. @@ -7087,6 +7137,7 @@ const char *sqlite3PagerJournalname(Pager *pPager){ return pPager->zJournal; } +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC /* ** Set or retrieve the codec for this pager @@ -7134,6 +7185,7 @@ int sqlite3PagerState(Pager *pPager){ return pPager->eState; } #endif /* SQLITE_HAS_CODEC */ +/* END SQLCIPHER */ #ifndef SQLITE_OMIT_AUTOVACUUM /* @@ -7529,7 +7581,6 @@ int sqlite3PagerCheckpoint( pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, pnLog, pnCkpt ); - sqlite3PagerResetLockTimeout(pPager); } return rc; } @@ -7694,7 +7745,31 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ return rc; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +/* +** If pager pPager is a wal-mode database not in exclusive locking mode, +** invoke the sqlite3WalWriteLock() function on the associated Wal object +** with the same db and bLock parameters as were passed to this function. +** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. +*/ +int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){ + int rc = SQLITE_OK; + if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){ + rc = sqlite3WalWriteLock(pPager->pWal, bLock); + } + return rc; +} +/* +** Set the database handle used by the wal layer to determine if +** blocking locks are required. +*/ +void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){ + if( pagerUseWal(pPager) ){ + sqlite3WalDb(pPager->pWal, db); + } +} +#endif #ifdef SQLITE_ENABLE_SNAPSHOT /* @@ -7714,7 +7789,10 @@ int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot){ ** read transaction is opened, attempt to read from the snapshot it ** identifies. If this is not a WAL database, return an error. */ -int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){ +int sqlite3PagerSnapshotOpen( + Pager *pPager, + sqlite3_snapshot *pSnapshot +){ int rc = SQLITE_OK; if( pPager->pWal ){ sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot); diff --git a/src/pager.h b/src/pager.h index 9042789..4aecfae 100644 --- a/src/pager.h +++ b/src/pager.h @@ -46,8 +46,8 @@ typedef struct PgHdr DbPage; ** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is ** reserved for working around a windows/posix incompatibility). It is ** used in the journal to signify that the remainder of the journal file -** is devoted to storing a master journal name - there are no more pages to -** roll back. See comments for function writeMasterJournal() in pager.c +** is devoted to storing a super-journal name - there are no more pages to +** roll back. See comments for function writeSuperJournal() in pager.c ** for details. */ #define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) @@ -128,10 +128,12 @@ int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC void sqlite3PagerAlignReserve(Pager*,Pager*); #endif -int sqlite3PagerMaxPageCount(Pager*, int); +/* END SQLCIPHER */ +Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); void sqlite3PagerSetCachesize(Pager*, int); int sqlite3PagerSetSpillsize(Pager*, int); void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); @@ -164,9 +166,9 @@ void *sqlite3PagerGetExtra(DbPage *); /* Functions used to manage pager transactions and savepoints. */ void sqlite3PagerPagecount(Pager*, int*); int sqlite3PagerBegin(Pager*, int exFlag, int); -int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); +int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int); int sqlite3PagerExclusiveLock(Pager*); -int sqlite3PagerSync(Pager *pPager, const char *zMaster); +int sqlite3PagerSync(Pager *pPager, const char *zSuper); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); @@ -180,14 +182,22 @@ int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); # ifdef SQLITE_ENABLE_SNAPSHOT - int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); - int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); + int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); + int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); int sqlite3PagerSnapshotRecover(Pager *pPager); int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); void sqlite3PagerSnapshotUnlock(Pager *pPager); # endif #endif +#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) + int sqlite3PagerWalWriteLock(Pager*, int); + void sqlite3PagerWalDb(Pager*, sqlite3*); +#else +# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK +# define sqlite3PagerWalDb(x,y) +#endif + #ifdef SQLITE_DIRECT_OVERFLOW_READ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); #endif @@ -213,20 +223,17 @@ int sqlite3PagerIsMemdb(Pager*); void sqlite3PagerCacheStat(Pager *, int, int, int *); void sqlite3PagerClearCache(Pager*); int sqlite3SectorSize(sqlite3_file *); -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT -void sqlite3PagerResetLockTimeout(Pager *pPager); -#else -# define sqlite3PagerResetLockTimeout(X) -#endif /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); void sqlite3PagerRekey(DbPage*, Pgno, u16); +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) void *sqlite3PagerCodec(DbPage *); #endif +/* END SQLCIPHER */ /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) diff --git a/src/parse.y b/src/parse.y index 5876a1a..44e0b4f 100644 --- a/src/parse.y +++ b/src/parse.y @@ -111,6 +111,27 @@ static void disableLookaside(Parse *pParse){ DisableLookaside; } +#if !defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) \ + && defined(SQLITE_UDL_CAPABLE_PARSER) +/* +** Issue an error message if an ORDER BY or LIMIT clause occurs on an +** UPDATE or DELETE statement. +*/ +static void updateDeleteLimitError( + Parse *pParse, + ExprList *pOrderBy, + Expr *pLimit +){ + if( pOrderBy ){ + sqlite3ErrorMsg(pParse, "syntax error near \"ORDER BY\""); + }else{ + sqlite3ErrorMsg(pParse, "syntax error near \"LIMIT\""); + } + sqlite3ExprListDelete(pParse->db, pOrderBy); + sqlite3ExprDelete(pParse->db, pLimit); +} +#endif /* SQLITE_ENABLE_UPDATE_DELETE_LIMIT */ + } // end %include // Input is a single SQL command @@ -448,7 +469,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). { //////////////////////// The SELECT statement ///////////////////////////////// // cmd ::= select(X). { - SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0}; + SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; sqlite3Select(pParse, X, &dest); sqlite3SelectDelete(pParse->db, X); } @@ -637,7 +658,7 @@ as(X) ::= . {X.n = 0; X.z = 0;} // A complete FROM clause. // -from(A) ::= . {A = sqlite3DbMallocZero(pParse->db, sizeof(*A));} +from(A) ::= . {A = 0;} from(A) ::= FROM seltablist(X). { A = X; sqlite3SrcListShiftJoinType(A); @@ -840,18 +861,20 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y). /////////////////////////// The DELETE statement ///////////////////////////// // -%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT +%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W) orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); #ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT - sqlite3ExprListDelete(pParse->db, O); O = 0; - sqlite3ExprDelete(pParse->db, L); L = 0; + if( O || L ){ + updateDeleteLimitError(pParse,O,L); + O = 0; + L = 0; + } #endif sqlite3DeleteFrom(pParse,X,W,O,L); } -%endif -%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT +%else cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3DeleteFrom(pParse,X,W,0,0); @@ -866,23 +889,33 @@ where_opt(A) ::= WHERE expr(X). {A = X;} ////////////////////////// The UPDATE command //////////////////////////////// // -%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) +%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER +cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) where_opt(W) orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); + X = sqlite3SrcListAppendList(pParse, X, F); sqlite3ExprListCheckLength(pParse,Y,"set list"); +#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + if( O || L ){ + updateDeleteLimitError(pParse,O,L); + O = 0; + L = 0; + } +#endif sqlite3Update(pParse,X,Y,W,R,O,L,0); } -%endif -%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) - where_opt(W). { +%else +cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) + where_opt(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); + X = sqlite3SrcListAppendList(pParse, X, F); sqlite3Update(pParse,X,Y,W,R,0,0,0); } %endif + + %type setlist {ExprList*} %destructor setlist {sqlite3ExprListDelete(pParse->db, $$);} @@ -965,6 +998,7 @@ idlist(A) ::= nm(Y). p->op = (u8)op; p->affExpr = 0; p->flags = EP_Leaf; + ExprClearVVAProperties(p); p->iAgg = -1; p->pLeft = p->pRight = 0; p->x.pList = 0; @@ -1193,6 +1227,13 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { */ sqlite3ExprUnmapAndDelete(pParse, A); A = sqlite3Expr(pParse->db, TK_INTEGER, N ? "1" : "0"); + }else if( Y->nExpr==1 && sqlite3ExprIsConstant(Y->a[0].pExpr) ){ + Expr *pRHS = Y->a[0].pExpr; + Y->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, Y); + pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); + A = sqlite3PExpr(pParse, TK_EQ, A, pRHS); + if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0); }else{ A = sqlite3PExpr(pParse, TK_IN, A, 0); if( A ){ @@ -1307,7 +1348,7 @@ uniqueflag(A) ::= . {A = OE_None;} // // IMPORTANT COMPATIBILITY NOTE: Some prior versions of SQLite accepted // COLLATE clauses and ASC or DESC keywords on ID lists in inappropriate -// places - places that might have been stored in the sqlite_master schema. +// places - places that might have been stored in the sqlite_schema table. // Those extra features were ignored. But because they might be in some // (busted) old databases, we need to continue parsing them when loading // historical schemas. @@ -1362,16 +1403,14 @@ cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);} ///////////////////////////// The VACUUM command ///////////////////////////// // -%ifndef SQLITE_OMIT_VACUUM -%ifndef SQLITE_OMIT_ATTACH +%if !SQLITE_OMIT_VACUUM && !SQLITE_OMIT_ATTACH %type vinto {Expr*} %destructor vinto {sqlite3ExprDelete(pParse->db, $$);} cmd ::= VACUUM vinto(Y). {sqlite3Vacuum(pParse,0,Y);} cmd ::= VACUUM nm(X) vinto(Y). {sqlite3Vacuum(pParse,&X,Y);} vinto(A) ::= INTO expr(X). {A = X;} vinto(A) ::= . {A = 0;} -%endif SQLITE_OMIT_ATTACH -%endif SQLITE_OMIT_VACUUM +%endif ///////////////////////////// The PRAGMA command ///////////////////////////// // @@ -1478,8 +1517,8 @@ tridxby ::= NOT INDEXED. { %destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);} // UPDATE trigger_cmd(A) ::= - UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z) scanpt(E). - {A = sqlite3TriggerUpdateStep(pParse, &X, Y, Z, R, B.z, E);} + UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) from(F) where_opt(Z) scanpt(E). + {A = sqlite3TriggerUpdateStep(pParse, &X, F, Y, Z, R, B.z, E);} // INSERT trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO diff --git a/src/pragma.c b/src/pragma.c index 331dfdb..8fcbb4d 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -570,7 +570,7 @@ void sqlite3Pragma( ** buffer that the pager module resizes using sqlite3_realloc(). */ db->nextPagesize = sqlite3Atoi(zRight); - if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){ + if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){ sqlite3OomFault(db); } } @@ -626,13 +626,19 @@ void sqlite3Pragma( */ case PragTyp_PAGE_COUNT: { int iReg; + i64 x = 0; sqlite3CodeVerifySchema(pParse, iDb); iReg = ++pParse->nMem; if( sqlite3Tolower(zLeft[0])=='p' ){ sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); }else{ - sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, - sqlite3AbsInt32(sqlite3Atoi(zRight))); + if( zRight && sqlite3DecOrHexToI64(zRight,&x)==0 ){ + if( x<0 ) x = 0; + else if( x>0xfffffffe ) x = 0xfffffffe; + }else{ + x = 0; + } + sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, (int)x); } sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); break; @@ -1153,15 +1159,14 @@ void sqlite3Pragma( */ case PragTyp_TABLE_INFO: if( zRight ){ Table *pTab; + sqlite3CodeVerifyNamedSchema(pParse, zDb); pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb); if( pTab ){ - int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int i, k; int nHidden = 0; Column *pCol; Index *pPk = sqlite3PrimaryKeyIndex(pTab); pParse->nMem = 7; - sqlite3CodeVerifySchema(pParse, iTabDb); sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ int isHidden = 0; @@ -1418,7 +1423,6 @@ void sqlite3Pragma( regRow = ++pParse->nMem; k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ - int iTabDb; if( zRight ){ pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); k = 0; @@ -1427,23 +1431,24 @@ void sqlite3Pragma( k = sqliteHashNext(k); } if( pTab==0 || pTab->pFKey==0 ) continue; - iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); - sqlite3CodeVerifySchema(pParse, iTabDb); - sqlite3TableLock(pParse, iTabDb, pTab->tnum, 0, pTab->zName); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zDbSName; + sqlite3CodeVerifySchema(pParse, iDb); + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; - sqlite3OpenTable(pParse, 0, iTabDb, pTab, OP_OpenRead); + sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regResult, pTab->zName); for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlite3FindTable(db, pFK->zTo, zDb); if( pParent==0 ) continue; pIdx = 0; - sqlite3TableLock(pParse, iTabDb, pParent->tnum, 0, pParent->zName); + sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName); x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); if( x==0 ){ if( pIdx==0 ){ - sqlite3OpenTable(pParse, i, iTabDb, pParent, OP_OpenRead); + sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); }else{ - sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iTabDb); + sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); } }else{ @@ -1536,9 +1541,22 @@ void sqlite3Pragma( ** integrity_check designed to detect most database corruption ** without the overhead of cross-checking indexes. Quick_check ** is linear time wherease integrity_check is O(NlogN). + ** + ** The maximum nubmer of errors is 100 by default. A different default + ** can be specified using a numeric parameter N. + ** + ** Or, the parameter N can be the name of a table. In that case, only + ** the one table named is verified. The freelist is only verified if + ** the named table is "sqlite_schema" (or one of its aliases). + ** + ** All schemas are checked by default. To check just a single + ** schema, use the form: + ** + ** PRAGMA schema.integrity_check; */ case PragTyp_INTEGRITY_CHECK: { int i, j, addr, mxErr; + Table *pObjTab = 0; /* Check only this one table, if not NULL */ int isQuick = (sqlite3Tolower(zLeft[0])=='q'); @@ -1561,9 +1579,13 @@ void sqlite3Pragma( /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ - sqlite3GetInt32(zRight, &mxErr); - if( mxErr<=0 ){ - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; + if( sqlite3GetInt32(zRight, &mxErr) ){ + if( mxErr<=0 ){ + mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; + } + }else{ + pObjTab = sqlite3LocateTable(pParse, 0, zRight, + iDb>=0 ? db->aDb[iDb].zDbSName : 0); } } sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ @@ -1592,15 +1614,21 @@ void sqlite3Pragma( Table *pTab = sqliteHashData(x); /* Current table */ Index *pIdx; /* An index on pTab */ int nIdx; /* Number of indexes on pTab */ + if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } if( nIdx>mxIdx ) mxIdx = nIdx; } + if( cnt==0 ) continue; + if( pObjTab ) cnt++; aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); if( aRoot==0 ) break; - for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ + cnt = 0; + if( pObjTab ) aRoot[++cnt] = 0; + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx; + if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ aRoot[++cnt] = pIdx->tnum; @@ -1634,6 +1662,7 @@ void sqlite3Pragma( int r1 = -1; if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ + if( pObjTab && pObjTab!=pTab ) continue; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, 1, 0, &iDataCur, &iIdxCur); @@ -1744,7 +1773,6 @@ void sqlite3Pragma( } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); -#ifndef SQLITE_OMIT_BTREECOUNT if( !isQuick ){ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ @@ -1758,7 +1786,6 @@ void sqlite3Pragma( sqlite3VdbeJumpHere(v, addr); } } -#endif /* SQLITE_OMIT_BTREECOUNT */ } } { @@ -1839,21 +1866,12 @@ void sqlite3Pragma( ** will be overwritten when the schema is next loaded. If it does not ** already exists, it will be created to use the new encoding value. */ - int canChangeEnc = 1; /* True if allowed to change the encoding */ - int i; /* For looping over all attached databases */ - for(i=0; inDb; i++){ - if( db->aDb[i].pBt!=0 - && DbHasProperty(db,i,DB_SchemaLoaded) - && !DbHasProperty(db,i,DB_Empty) - ){ - canChangeEnc = 0; - } - } - if( canChangeEnc ){ + if( (db->mDbFlags & DBFLAG_EncodingFixed)==0 ){ for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ - SCHEMA_ENC(db) = ENC(db) = - pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; + u8 enc = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; + SCHEMA_ENC(db) = enc; + sqlite3SetTextEncoding(db, enc); break; } } @@ -1916,6 +1934,7 @@ void sqlite3Pragma( aOp[1].p1 = iDb; aOp[1].p2 = iCookie; aOp[1].p3 = sqlite3Atoi(zRight); + aOp[1].p5 = 1; }else{ /* Read the specified cookie value */ static const VdbeOpList readCookie[] = { @@ -2202,6 +2221,25 @@ void sqlite3Pragma( break; } + /* + ** PRAGMA analysis_limit + ** PRAGMA analysis_limit = N + ** + ** Configure the maximum number of rows that ANALYZE will examine + ** in each index that it looks at. Return the new limit. + */ + case PragTyp_ANALYSIS_LIMIT: { + sqlite3_int64 N; + if( zRight + && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK + && N>=0 + ){ + db->nAnalysisLimit = (int)(N&0x7fffffff); + } + returnSingleInt(v, db->nAnalysisLimit); + break; + } + #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases @@ -2230,6 +2268,7 @@ void sqlite3Pragma( } #endif +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC /* Pragma iArg ** ---------- ------ @@ -2271,18 +2310,12 @@ void sqlite3Pragma( break; } #endif -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) +/* END SQLCIPHER */ +#if defined(SQLITE_ENABLE_CEROD) case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){ -#ifdef SQLITE_HAS_CODEC - if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ - sqlite3_activate_see(&zRight[4]); - } -#endif -#ifdef SQLITE_ENABLE_CEROD if( sqlite3StrNICmp(zRight, "cerod-", 6)==0 ){ sqlite3_activate_cerod(&zRight[6]); } -#endif } break; #endif diff --git a/src/pragma.h b/src/pragma.h index 3edf5c1..20ec13b 100644 --- a/src/pragma.h +++ b/src/pragma.h @@ -5,49 +5,50 @@ */ /* The various pragma types */ -#define PragTyp_HEADER_VALUE 0 -#define PragTyp_AUTO_VACUUM 1 -#define PragTyp_FLAG 2 -#define PragTyp_BUSY_TIMEOUT 3 -#define PragTyp_CACHE_SIZE 4 -#define PragTyp_CACHE_SPILL 5 -#define PragTyp_CASE_SENSITIVE_LIKE 6 -#define PragTyp_COLLATION_LIST 7 -#define PragTyp_COMPILE_OPTIONS 8 -#define PragTyp_DATA_STORE_DIRECTORY 9 -#define PragTyp_DATABASE_LIST 10 -#define PragTyp_DEFAULT_CACHE_SIZE 11 -#define PragTyp_ENCODING 12 -#define PragTyp_FOREIGN_KEY_CHECK 13 -#define PragTyp_FOREIGN_KEY_LIST 14 -#define PragTyp_FUNCTION_LIST 15 -#define PragTyp_HARD_HEAP_LIMIT 16 -#define PragTyp_INCREMENTAL_VACUUM 17 -#define PragTyp_INDEX_INFO 18 -#define PragTyp_INDEX_LIST 19 -#define PragTyp_INTEGRITY_CHECK 20 -#define PragTyp_JOURNAL_MODE 21 -#define PragTyp_JOURNAL_SIZE_LIMIT 22 -#define PragTyp_LOCK_PROXY_FILE 23 -#define PragTyp_LOCKING_MODE 24 -#define PragTyp_PAGE_COUNT 25 -#define PragTyp_MMAP_SIZE 26 -#define PragTyp_MODULE_LIST 27 -#define PragTyp_OPTIMIZE 28 -#define PragTyp_PAGE_SIZE 29 -#define PragTyp_PRAGMA_LIST 30 -#define PragTyp_SECURE_DELETE 31 -#define PragTyp_SHRINK_MEMORY 32 -#define PragTyp_SOFT_HEAP_LIMIT 33 -#define PragTyp_SYNCHRONOUS 34 -#define PragTyp_TABLE_INFO 35 -#define PragTyp_TEMP_STORE 36 -#define PragTyp_TEMP_STORE_DIRECTORY 37 -#define PragTyp_THREADS 38 -#define PragTyp_WAL_AUTOCHECKPOINT 39 -#define PragTyp_WAL_CHECKPOINT 40 -#define PragTyp_ACTIVATE_EXTENSIONS 41 -#define PragTyp_KEY 42 +#define PragTyp_KEY 255 +#define PragTyp_ACTIVATE_EXTENSIONS 0 +#define PragTyp_ANALYSIS_LIMIT 1 +#define PragTyp_HEADER_VALUE 2 +#define PragTyp_AUTO_VACUUM 3 +#define PragTyp_FLAG 4 +#define PragTyp_BUSY_TIMEOUT 5 +#define PragTyp_CACHE_SIZE 6 +#define PragTyp_CACHE_SPILL 7 +#define PragTyp_CASE_SENSITIVE_LIKE 8 +#define PragTyp_COLLATION_LIST 9 +#define PragTyp_COMPILE_OPTIONS 10 +#define PragTyp_DATA_STORE_DIRECTORY 11 +#define PragTyp_DATABASE_LIST 12 +#define PragTyp_DEFAULT_CACHE_SIZE 13 +#define PragTyp_ENCODING 14 +#define PragTyp_FOREIGN_KEY_CHECK 15 +#define PragTyp_FOREIGN_KEY_LIST 16 +#define PragTyp_FUNCTION_LIST 17 +#define PragTyp_HARD_HEAP_LIMIT 18 +#define PragTyp_INCREMENTAL_VACUUM 19 +#define PragTyp_INDEX_INFO 20 +#define PragTyp_INDEX_LIST 21 +#define PragTyp_INTEGRITY_CHECK 22 +#define PragTyp_JOURNAL_MODE 23 +#define PragTyp_JOURNAL_SIZE_LIMIT 24 +#define PragTyp_LOCK_PROXY_FILE 25 +#define PragTyp_LOCKING_MODE 26 +#define PragTyp_PAGE_COUNT 27 +#define PragTyp_MMAP_SIZE 28 +#define PragTyp_MODULE_LIST 29 +#define PragTyp_OPTIMIZE 30 +#define PragTyp_PAGE_SIZE 31 +#define PragTyp_PRAGMA_LIST 32 +#define PragTyp_SECURE_DELETE 33 +#define PragTyp_SHRINK_MEMORY 34 +#define PragTyp_SOFT_HEAP_LIMIT 35 +#define PragTyp_SYNCHRONOUS 36 +#define PragTyp_TABLE_INFO 37 +#define PragTyp_TEMP_STORE 38 +#define PragTyp_TEMP_STORE_DIRECTORY 39 +#define PragTyp_THREADS 40 +#define PragTyp_WAL_AUTOCHECKPOINT 41 +#define PragTyp_WAL_CHECKPOINT 42 #define PragTyp_LOCK_STATUS 43 #define PragTyp_STATS 44 @@ -133,13 +134,18 @@ typedef struct PragmaName { u64 iArg; /* Extra argument */ } PragmaName; static const PragmaName aPragmaName[] = { -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) +#if defined(SQLITE_ENABLE_CEROD) {/* zName: */ "activate_extensions", /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS, /* ePragFlg: */ 0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif + {/* zName: */ "analysis_limit", + /* ePragTyp: */ PragTyp_ANALYSIS_LIMIT, + /* ePragFlg: */ PragFlg_Result0, + /* ColNames: */ 0, 0, + /* iArg: */ 0 }, #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) {/* zName: */ "application_id", /* ePragTyp: */ PragTyp_HEADER_VALUE, @@ -276,7 +282,7 @@ static const PragmaName aPragmaName[] = { #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) {/* zName: */ "foreign_key_check", /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK, - /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0, + /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt, /* ColNames: */ 37, 4, /* iArg: */ 0 }, #endif @@ -329,6 +335,7 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) {/* zName: */ "hexkey", /* ePragTyp: */ PragTyp_KEY, @@ -341,6 +348,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 3 }, #endif +/* END SQLCIPHER */ #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_CHECK) {/* zName: */ "ignore_check_constraints", @@ -393,6 +401,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) {/* zName: */ "key", /* ePragTyp: */ PragTyp_KEY, @@ -400,6 +409,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif +/* END SQLCIPHER */ #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "legacy_alter_table", /* ePragTyp: */ PragTyp_FLAG, @@ -508,6 +518,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ SQLITE_RecTriggers }, #endif +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) {/* zName: */ "rekey", /* ePragTyp: */ PragTyp_KEY, @@ -515,6 +526,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 1 }, #endif +/* END SQLCIPHER */ #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "reverse_unordered_selects", /* ePragTyp: */ PragTyp_FLAG, @@ -600,6 +612,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) {/* zName: */ "textkey", /* ePragTyp: */ PragTyp_KEY, @@ -612,6 +625,7 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 5 }, #endif +/* END SQLCIPHER */ {/* zName: */ "threads", /* ePragTyp: */ PragTyp_THREADS, /* ePragFlg: */ PragFlg_Result0, @@ -680,4 +694,4 @@ static const PragmaName aPragmaName[] = { /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 66 on by default, 82 total. */ +/* Number of pragmas: 67 on by default, 77 total. */ diff --git a/src/prepare.c b/src/prepare.c index 2d928f1..8e2186b 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -91,7 +91,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ assert( argc==5 ); UNUSED_PARAMETER2(NotUsed, argc); assert( sqlite3_mutex_held(db->mutex) ); - DbClearProperty(db, iDb, DB_Empty); + db->mDbFlags |= DBFLAG_EncodingFixed; pData->nInitRow++; if( db->mallocFailed ){ corruptSchema(pData, argv[1], 0); @@ -115,7 +115,13 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ assert( db->init.busy ); db->init.iDb = iDb; - db->init.newTnum = sqlite3Atoi(argv[3]); + if( sqlite3GetUInt32(argv[3], &db->init.newTnum)==0 + || (db->init.newTnum>pData->mxPage && pData->mxPage>0) + ){ + if( sqlite3Config.bExtraSchemaChecks ){ + corruptSchema(pData, argv[1], "invalid rootpage"); + } + } db->init.orphanTrigger = 0; db->init.azInit = argv; pStmt = 0; @@ -148,12 +154,17 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ */ Index *pIndex; pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); - if( pIndex==0 - || sqlite3GetInt32(argv[3],&pIndex->tnum)==0 + if( pIndex==0 ){ + corruptSchema(pData, argv[1], "orphan index"); + }else + if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 || pIndex->tnum<2 + || pIndex->tnum>pData->mxPage || sqlite3IndexHasDuplicateRootPage(pIndex) ){ - corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index"); + if( sqlite3Config.bExtraSchemaChecks ){ + corruptSchema(pData, argv[1], "invalid rootpage"); + } } } return 0; @@ -177,8 +188,9 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ char const *azArg[6]; int meta[5]; InitData initData; - const char *zMasterName; + const char *zSchemaTabName; int openedTransaction = 0; + int mask = ((db->mDbFlags & DBFLAG_EncodingFixed) | ~DBFLAG_EncodingFixed); assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ); assert( iDb>=0 && iDbnDb ); @@ -188,13 +200,13 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ db->init.busy = 1; - /* Construct the in-memory representation schema tables (sqlite_master or - ** sqlite_temp_master) by invoking the parser directly. The appropriate + /* Construct the in-memory representation schema tables (sqlite_schema or + ** sqlite_temp_schema) by invoking the parser directly. The appropriate ** table name will be inserted automatically by the parser so we can just ** use the abbreviation "x" here. The parser will also automatically tag ** the schema table as read-only. */ azArg[0] = "table"; - azArg[1] = zMasterName = SCHEMA_TABLE(iDb); + azArg[1] = zSchemaTabName = SCHEMA_TABLE(iDb); azArg[2] = azArg[1]; azArg[3] = "1"; azArg[4] = "CREATE TABLE x(type text,name text,tbl_name text," @@ -206,7 +218,9 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ initData.pzErrMsg = pzErrMsg; initData.mInitFlags = mFlags; initData.nInitRow = 0; + initData.mxPage = 0; sqlite3InitCallback(&initData, 5, (char **)azArg, 0); + db->mDbFlags &= mask; if( initData.rc ){ rc = initData.rc; goto error_out; @@ -266,27 +280,25 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ ** as sqlite3.enc. */ if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */ - if( iDb==0 ){ -#ifndef SQLITE_OMIT_UTF16 + if( iDb==0 && (db->mDbFlags & DBFLAG_EncodingFixed)==0 ){ u8 encoding; +#ifndef SQLITE_OMIT_UTF16 /* If opening the main database, set ENC(db). */ encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; if( encoding==0 ) encoding = SQLITE_UTF8; - ENC(db) = encoding; #else - ENC(db) = SQLITE_UTF8; + encoding = SQLITE_UTF8; #endif + sqlite3SetTextEncoding(db, encoding); }else{ /* If opening an attached database, the encoding much match ENC(db) */ - if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){ + if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){ sqlite3SetString(pzErrMsg, db, "attached databases must use the same" " text encoding as main database"); rc = SQLITE_ERROR; goto initone_error_out; } } - }else{ - DbSetProperty(db, iDb, DB_Empty); } pDb->pSchema->enc = ENC(db); @@ -329,11 +341,12 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ /* Read the schema information out of the schema tables */ assert( db->init.busy ); + initData.mxPage = sqlite3BtreeLastPage(pDb->pBt); { char *zSql; zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\".%s ORDER BY rowid", - db->aDb[iDb].zDbSName, zMasterName); + db->aDb[iDb].zDbSName, zSchemaTabName); #ifndef SQLITE_OMIT_AUTHORIZATION { sqlite3_xauth xAuth; @@ -363,7 +376,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ ** current sqlite3_prepare() operation will fail, but the following one ** will attempt to compile the supplied statement against whatever subset ** of the schema was loaded before the error occurred. The primary - ** purpose of this is to allow access to the sqlite_master table + ** purpose of this is to allow access to the sqlite_schema table ** even when its contents have been corrupted. */ DbSetProperty(db, iDb, DB_SchemaLoaded); @@ -398,8 +411,7 @@ error_out: ** error occurs, write an error message into *pzErrMsg. ** ** After a database is initialized, the DB_SchemaLoaded bit is set -** bit is set in the flags field of the Db structure. If the database -** file was of zero-length, then the DB_Empty flag is also set. +** bit is set in the flags field of the Db structure. */ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ int i, rc; @@ -505,17 +517,18 @@ static void schemaIsValid(Parse *pParse){ ** attached database is returned. */ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ - int i = -1000000; + int i = -32768; - /* If pSchema is NULL, then return -1000000. This happens when code in + /* If pSchema is NULL, then return -32768. This happens when code in ** expr.c is trying to resolve a reference to a transient table (i.e. one ** created by a sub-select). In this case the return value of this ** function should never be used. ** - ** We return -1000000 instead of the more usual -1 simply because using - ** -1000000 as the incorrect index into db->aDb[] is much + ** We return -32768 instead of the more usual -1 simply because using + ** -32768 as the incorrect index into db->aDb[] is much ** more likely to cause a segfault than -1 (of course there are assert() - ** statements too, but it never hurts to play the odds). + ** statements too, but it never hurts to play the odds) and + ** -32768 will still fit into a 16-bit signed integer. */ assert( sqlite3_mutex_held(db->mutex) ); if( pSchema ){ @@ -530,11 +543,26 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ return i; } +/* +** Deallocate a single AggInfo object +*/ +static void agginfoFree(sqlite3 *db, AggInfo *p){ + sqlite3DbFree(db, p->aCol); + sqlite3DbFree(db, p->aFunc); + sqlite3DbFree(db, p); +} + /* ** Free all memory allocations in the pParse object */ void sqlite3ParserReset(Parse *pParse){ sqlite3 *db = pParse->db; + AggInfo *pThis = pParse->pAggList; + while( pThis ){ + AggInfo *pNext = pThis->pNext; + agginfoFree(db, pThis); + pThis = pNext; + } sqlite3DbFree(db, pParse->aLabel); sqlite3ExprListDelete(db, pParse->pConstExpr); if( db ){ @@ -728,7 +756,7 @@ static int sqlite3LockAndPrepare( ** ** If the statement is successfully recompiled, return SQLITE_OK. Otherwise, ** if the statement cannot be recompiled because another connection has -** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error +** locked the sqlite3_schema table, return SQLITE_LOCKED. If any other error ** occurs, return SQLITE_SCHEMA. */ int sqlite3Reprepare(Vdbe *p){ diff --git a/src/printf.c b/src/printf.c index fc77f68..2f99208 100644 --- a/src/printf.c +++ b/src/printf.c @@ -194,6 +194,13 @@ static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ #endif #define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */ +/* +** Hard limit on the precision of floating-point conversions. +*/ +#ifndef SQLITE_PRINTF_PRECISION_LIMIT +# define SQLITE_FP_PRECISION_LIMIT 100000000 +#endif + /* ** Render a string given by "fmt" into the StrAccum object. */ @@ -394,15 +401,17 @@ void sqlite3_str_vappendf( ** xtype The class of the conversion. ** infop Pointer to the appropriate info struct. */ + assert( width>=0 ); + assert( precision>=(-1) ); switch( xtype ){ case etPOINTER: flag_long = sizeof(char*)==sizeof(i64) ? 2 : sizeof(char*)==sizeof(long int) ? 1 : 0; - /* Fall through into the next case */ + /* no break */ deliberate_fall_through case etORDINAL: case etRADIX: cThousand = 0; - /* Fall through into the next case */ + /* no break */ deliberate_fall_through case etDECIMAL: if( infop->flags & FLAG_SIGNED ){ i64 v; @@ -515,6 +524,11 @@ void sqlite3_str_vappendf( length = 0; #else if( precision<0 ) precision = 6; /* Set default precision */ +#ifdef SQLITE_FP_PRECISION_LIMIT + if( precision>SQLITE_FP_PRECISION_LIMIT ){ + precision = SQLITE_FP_PRECISION_LIMIT; + } +#endif if( realvalue<0.0 ){ realvalue = -realvalue; prefix = '-'; @@ -797,7 +811,7 @@ void sqlite3_str_vappendf( } isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); - /* For %q, %Q, and %w, the precision is the number of byte (or + /* For %q, %Q, and %w, the precision is the number of bytes (or ** characters if the ! flags is present) to use from the input. ** Because of the extra quoting characters inserted, the number ** of output characters may be larger than the precision. @@ -924,7 +938,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ if( p->db ){ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); }else{ - zNew = sqlite3_realloc64(zOld, p->nAlloc); + zNew = sqlite3Realloc(zOld, p->nAlloc); } if( zNew ){ assert( p->zText!=0 || p->nChar==0 ); @@ -1266,7 +1280,7 @@ void sqlite3_log(int iErrCode, const char *zFormat, ...){ void sqlite3DebugPrintf(const char *zFormat, ...){ va_list ap; StrAccum acc; - char zBuf[500]; + char zBuf[SQLITE_PRINT_BUF_SIZE*10]; sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); va_start(ap,zFormat); sqlite3_str_vappendf(&acc, zFormat, ap); diff --git a/src/resolve.c b/src/resolve.c index 119a07f..fcb6f15 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -16,6 +16,11 @@ */ #include "sqliteInt.h" +/* +** Magic table number to mean the EXCLUDED table in an UPSERT statement. +*/ +#define EXCLUDED_TABLE_NUMBER 2 + /* ** Walk the expression tree pExpr and increase the aggregate function ** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. @@ -24,6 +29,8 @@ ** ** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..) ** is a helper function - a callback for the tree walker. +** +** See also the sqlite3WindowExtraAggFuncDepth() routine in window.c */ static int incrAggDepth(Walker *pWalker, Expr *pExpr){ if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.n; @@ -140,7 +147,7 @@ int sqlite3MatchEName( ){ int n; const char *zSpan; - if( NEVER(pItem->eEName!=ENAME_TAB) ) return 0; + if( pItem->eEName!=ENAME_TAB ) return 0; zSpan = pItem->zEName; for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ @@ -175,6 +182,31 @@ static int areDoubleQuotedStringsEnabled(sqlite3 *db, NameContext *pTopNC){ } } +/* +** The argument is guaranteed to be a non-NULL Expr node of type TK_COLUMN. +** return the appropriate colUsed mask. +*/ +Bitmask sqlite3ExprColUsed(Expr *pExpr){ + int n; + Table *pExTab; + + n = pExpr->iColumn; + pExTab = pExpr->y.pTab; + assert( pExTab!=0 ); + if( (pExTab->tabFlags & TF_HasGenerated)!=0 + && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 + ){ + testcase( pExTab->nCol==BMS-1 ); + testcase( pExTab->nCol==BMS ); + return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1; + }else{ + testcase( n==BMS-1 ); + testcase( n==BMS ); + if( n>=BMS ) n = BMS-1; + return ((Bitmask)1)<nDb && sqlite3StrICmp("main", zDb)==0 ){ + /* This branch is taken when the main database has been renamed + ** using SQLITE_DBCONFIG_MAINDBNAME. */ + pSchema = db->aDb[0].pSchema; + zDb = db->aDb[0].zDbSName; + } } } @@ -263,6 +301,7 @@ static int lookupName( if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ + u8 hCol; pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); assert( pTab->nCol>0 ); @@ -296,8 +335,9 @@ static int lookupName( if( 0==(cntTab++) ){ pMatch = pItem; } + hCol = sqlite3StrIHash(zCol); for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ /* If there has been exactly one prior match and this match ** is for the right-hand table of a NATURAL JOIN or is in a ** USING clause, then skip this match. @@ -351,17 +391,18 @@ static int lookupName( Upsert *pUpsert = pNC->uNC.pUpsert; if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ pTab = pUpsert->pUpsertSrc->a[0].pTab; - pExpr->iTable = 2; + pExpr->iTable = EXCLUDED_TABLE_NUMBER; } } #endif /* SQLITE_OMIT_UPSERT */ if( pTab ){ int iCol; + u8 hCol = sqlite3StrIHash(zCol); pSchema = pTab->pSchema; cntTab++; for(iCol=0, pCol=pTab->aCol; iColnCol; iCol++, pCol++){ - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ + if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){ if( iCol==pTab->iPKey ){ iCol = -1; } @@ -375,14 +416,15 @@ static int lookupName( if( iColnCol ){ cnt++; #ifndef SQLITE_OMIT_UPSERT - if( pExpr->iTable==2 ){ + if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){ testcase( iCol==(-1) ); if( IN_RENAME_OBJECT ){ pExpr->iColumn = iCol; pExpr->y.pTab = pTab; eNewExprOp = TK_COLUMN; }else{ - pExpr->iTable = pNC->uNC.pUpsert->regData + iCol; + pExpr->iTable = pNC->uNC.pUpsert->regData + + sqlite3TableColumnToStorage(pTab, iCol); eNewExprOp = TK_REGISTER; ExprSetProperty(pExpr, EP_Alias); } @@ -571,22 +613,7 @@ static int lookupName( ** of the table. */ if( pExpr->iColumn>=0 && pMatch!=0 ){ - int n = pExpr->iColumn; - Table *pExTab = pExpr->y.pTab; - assert( pExTab!=0 ); - assert( pMatch->iCursor==pExpr->iTable ); - if( (pExTab->tabFlags & TF_HasGenerated)!=0 - && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 - ){ - testcase( pExTab->nCol==BMS-1 ); - testcase( pExTab->nCol==BMS ); - pMatch->colUsed = pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1; - }else{ - testcase( n==BMS-1 ); - testcase( n==BMS ); - if( n>=BMS ) n = BMS-1; - pMatch->colUsed |= ((Bitmask)1)<colUsed |= sqlite3ExprColUsed(pExpr); } /* Clean up and return @@ -729,26 +756,23 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif switch( pExpr->op ){ -#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) /* The special operator TK_ROW means use the rowid for the first ** column in the FROM clause. This is used by the LIMIT and ORDER BY - ** clause processing on UPDATE and DELETE statements. + ** clause processing on UPDATE and DELETE statements, and by + ** UPDATE ... FROM statement processing. */ case TK_ROW: { SrcList *pSrcList = pNC->pSrcList; struct SrcList_item *pItem; - assert( pSrcList && pSrcList->nSrc==1 ); + assert( pSrcList && pSrcList->nSrc>=1 ); pItem = pSrcList->a; - assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 ); pExpr->op = TK_COLUMN; pExpr->y.pTab = pItem->pTab; pExpr->iTable = pItem->iCursor; - pExpr->iColumn = -1; + pExpr->iColumn--; pExpr->affExpr = SQLITE_AFF_INTEGER; break; } -#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) - && !defined(SQLITE_OMIT_SUBQUERY) */ /* A column name: ID ** Or table name and column name: ID.ID @@ -1051,7 +1075,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( !ExprHasProperty(pExpr, EP_Reduced) ); /* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE", ** and "x IS NOT FALSE". */ - if( pRight->op==TK_ID ){ + if( pRight && pRight->op==TK_ID ){ int rc = resolveExprStep(pWalker, pRight); if( rc==WRC_Abort ) return WRC_Abort; if( pRight->op==TK_TRUEFALSE ){ @@ -1060,7 +1084,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } } - /* Fall thru */ + /* no break */ deliberate_fall_through } case TK_BETWEEN: case TK_EQ: @@ -1177,7 +1201,7 @@ static int resolveOrderByTermToExprList( nc.nErr = 0; db = pParse->db; savedSuppErr = db->suppressErr; - db->suppressErr = 1; + if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1; rc = sqlite3ResolveExprNames(&nc, pE); db->suppressErr = savedSuppErr; if( rc ) return 0; @@ -1812,11 +1836,41 @@ int sqlite3ResolveExprListNames( ExprList *pList /* The expression list to be analyzed. */ ){ int i; - if( pList ){ - for(i=0; inExpr; i++){ - if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort; + int savedHasAgg = 0; + Walker w; + if( pList==0 ) return WRC_Continue; + w.pParse = pNC->pParse; + w.xExprCallback = resolveExprStep; + w.xSelectCallback = resolveSelectStep; + w.xSelectCallback2 = 0; + w.u.pNC = pNC; + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + for(i=0; inExpr; i++){ + Expr *pExpr = pList->a[i].pExpr; + if( pExpr==0 ) continue; +#if SQLITE_MAX_EXPR_DEPTH>0 + w.pParse->nHeight += pExpr->nHeight; + if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){ + return WRC_Abort; } +#endif + sqlite3WalkExpr(&w, pExpr); +#if SQLITE_MAX_EXPR_DEPTH>0 + w.pParse->nHeight -= pExpr->nHeight; +#endif + assert( EP_Agg==NC_HasAgg ); + assert( EP_Win==NC_HasWin ); + testcase( pNC->ncFlags & NC_HasAgg ); + testcase( pNC->ncFlags & NC_HasWin ); + if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){ + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); + savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + } + if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort; } + pNC->ncFlags |= savedHasAgg; return WRC_Continue; } diff --git a/src/rowset.c b/src/rowset.c index 703cf49..0562320 100644 --- a/src/rowset.c +++ b/src/rowset.c @@ -177,7 +177,7 @@ void sqlite3RowSetDelete(void *pArg){ /* ** Allocate a new RowSetEntry object that is associated with the ** given RowSet. Return a pointer to the new and completely uninitialized -** objected. +** object. ** ** In an OOM situation, the RowSet.db->mallocFailed flag is set and this ** routine returns NULL. @@ -453,7 +453,7 @@ int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){ if( p ){ struct RowSetEntry **ppPrevTree = &pRowSet->pForest; if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/ - /* Only sort the current set of entiries if they need it */ + /* Only sort the current set of entries if they need it */ p = rowSetEntrySort(p); } for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ diff --git a/src/select.c b/src/select.c index 595b6eb..8b1fae7 100644 --- a/src/select.c +++ b/src/select.c @@ -14,20 +14,6 @@ */ #include "sqliteInt.h" -/* -** Trace output macros -*/ -#if SELECTTRACE_ENABLED -/***/ int sqlite3SelectTrace = 0; -# define SELECTTRACE(K,P,S,X) \ - if(sqlite3SelectTrace&(K)) \ - sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ - sqlite3DebugPrintf X -#else -# define SELECTTRACE(K,P,S,X) -#endif - - /* ** An instance of the following object is used to record information about ** how to process the DISTINCT keyword, to simplify passing that information @@ -103,7 +89,6 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){ if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){ sqlite3WindowListDelete(db, p->pWinDefn); } - assert( p->pWin==0 ); #endif if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); if( bFree ) sqlite3DbFreeNN(db, p); @@ -118,6 +103,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){ void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){ pDest->eDest = (u8)eDest; pDest->iSDParm = iParm; + pDest->iSDParm2 = 0; pDest->zAffSdst = 0; pDest->iSdst = 0; pDest->nSdst = 0; @@ -139,9 +125,9 @@ Select *sqlite3SelectNew( u32 selFlags, /* Flag parameters, such as SF_Distinct */ Expr *pLimit /* LIMIT value. NULL means not used */ ){ - Select *pNew; + Select *pNew, *pAllocated; Select standin; - pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) ); + pAllocated = pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) ); if( pNew==0 ){ assert( pParse->db->mallocFailed ); pNew = &standin; @@ -175,12 +161,11 @@ Select *sqlite3SelectNew( #endif if( pParse->db->mallocFailed ) { clearSelect(pParse->db, pNew, pNew!=&standin); - pNew = 0; + pAllocated = 0; }else{ assert( pNew->pSrc!=0 || pParse->nErr>0 ); } - assert( pNew!=&standin ); - return pNew; + return pAllocated; } @@ -191,21 +176,6 @@ void sqlite3SelectDelete(sqlite3 *db, Select *p){ if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); } -/* -** Delete all the substructure for p, but keep p allocated. Redefine -** p to be a single SELECT where every column of the result set has a -** value of NULL. -*/ -void sqlite3SelectReset(Parse *pParse, Select *p){ - if( ALWAYS(p) ){ - clearSelect(pParse->db, p, 0); - memset(&p->iLimit, 0, sizeof(Select) - offsetof(Select,iLimit)); - p->pEList = sqlite3ExprListAppend(pParse, 0, - sqlite3ExprAlloc(pParse->db,TK_NULL,0,0)); - p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(SrcList)); - } -} - /* ** Return a pointer to the right-most SELECT statement in a compound. */ @@ -294,8 +264,10 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ */ static int columnIndex(Table *pTab, const char *zCol){ int i; - for(i=0; inCol; i++){ - if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i; + u8 h = sqlite3StrIHash(zCol); + Column *pCol; + for(pCol=pTab->aCol, i=0; inCol; pCol++, i++){ + if( pCol->hName==h && sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; } return -1; } @@ -1006,7 +978,8 @@ static void selectInnerLoop( testcase( eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); assert( eDest==SRT_Set || eDest==SRT_Mem - || eDest==SRT_Coroutine || eDest==SRT_Output ); + || eDest==SRT_Coroutine || eDest==SRT_Output + || eDest==SRT_Upfrom ); } sRowLoadInfo.regResult = regResult; sRowLoadInfo.ecelFlags = ecelFlags; @@ -1155,6 +1128,30 @@ static void selectInnerLoop( break; } + case SRT_Upfrom: { + if( pSort ){ + pushOntoSorter( + pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); + }else{ + int i2 = pDest->iSDParm2; + int r1 = sqlite3GetTempReg(pParse); + + /* If the UPDATE FROM join is an aggregate that matches no rows, it + ** might still be trying to return one row, because that is what + ** aggregates do. Don't record that empty row in the output table. */ + sqlite3VdbeAddOp2(v, OP_IsNull, regResult, iBreak); VdbeCoverage(v); + + sqlite3VdbeAddOp3(v, OP_MakeRecord, + regResult+(i2<0), nResultCol-(i2<0), r1); + if( i2<0 ){ + sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult); + }else{ + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2); + } + } + break; + } + #ifndef SQLITE_OMIT_SUBQUERY /* If we are creating a set for an "expr IN (SELECT ...)" construct, ** then there should be a single item on the stack. Write this @@ -1179,6 +1176,7 @@ static void selectInnerLoop( break; } + /* If any row exist in the result set, record that fact and abort. */ case SRT_Exists: { @@ -1586,6 +1584,17 @@ static void generateSortTail( break; } #endif + case SRT_Upfrom: { + int i2 = pDest->iSDParm2; + int r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1); + if( i2<0 ){ + sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regRow); + }else{ + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2); + } + break; + } default: { assert( eDest==SRT_Output || eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); @@ -2024,6 +2033,7 @@ int sqlite3ColumnsFromExprList( if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt); } pCol->zName = zName; + pCol->hName = sqlite3StrIHash(zName); sqlite3ColumnPropertiesFromName(0, pCol); if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){ sqlite3OomFault(db); @@ -2717,9 +2727,7 @@ static int multiSelect( selectOpName(p->op))); rc = sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); - /* Query flattening in sqlite3Select() might refill p->pOrderBy. - ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */ - sqlite3ExprListDelete(db, p->pOrderBy); + assert( p->pOrderBy==0 ); pDelete = p->pPrior; p->pPrior = pPrior; p->pOrderBy = 0; @@ -2806,6 +2814,7 @@ static int multiSelect( /* Generate code to take the intersection of the two temporary ** tables. */ + if( rc ) break; assert( p->pEList ); iBreak = sqlite3VdbeMakeLabel(pParse); iCont = sqlite3VdbeMakeLabel(pParse); @@ -3172,7 +3181,7 @@ static int multiSelectOrderBy( sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ - int *aPermute; /* Mapping from ORDER BY terms to result set columns */ + u32 *aPermute; /* Mapping from ORDER BY terms to result set columns */ assert( p->pOrderBy!=0 ); assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ @@ -3221,7 +3230,7 @@ static int multiSelectOrderBy( ** to the right and the left are evaluated, they use the correct ** collation. */ - aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1)); + aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); if( aPermute ){ struct ExprList_item *pItem; aPermute[0] = nOrderBy; @@ -3476,7 +3485,10 @@ static Expr *substExpr( ){ pExpr->iRightJoinTable = pSubst->iNewTable; } - if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){ + if( pExpr->op==TK_COLUMN + && pExpr->iTable==pSubst->iTable + && !ExprHasProperty(pExpr, EP_FixedCol) + ){ if( pExpr->iColumn<0 ){ pExpr->op = TK_NULL; }else{ @@ -3494,6 +3506,7 @@ static Expr *substExpr( ifNullRow.op = TK_IF_NULL_ROW; ifNullRow.pLeft = pCopy; ifNullRow.iTable = pSubst->iNewTable; + ifNullRow.flags = EP_Skip; pCopy = &ifNullRow; } testcase( ExprHasProperty(pCopy, EP_Subquery) ); @@ -3580,6 +3593,38 @@ static void substSelect( } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ +#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) +/* +** pSelect is a SELECT statement and pSrcItem is one item in the FROM +** clause of that SELECT. +** +** This routine scans the entire SELECT statement and recomputes the +** pSrcItem->colUsed mask. +*/ +static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){ + struct SrcList_item *pItem; + if( pExpr->op!=TK_COLUMN ) return WRC_Continue; + pItem = pWalker->u.pSrcItem; + if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue; + if( pExpr->iColumn<0 ) return WRC_Continue; + pItem->colUsed |= sqlite3ExprColUsed(pExpr); + return WRC_Continue; +} +static void recomputeColumnsUsed( + Select *pSelect, /* The complete SELECT statement */ + struct SrcList_item *pSrcItem /* Which FROM clause item to recompute */ +){ + Walker w; + if( NEVER(pSrcItem->pTab==0) ) return; + memset(&w, 0, sizeof(w)); + w.xExprCallback = recomputeColumnsUsedExpr; + w.xSelectCallback = sqlite3SelectWalkNoop; + w.u.pSrcItem = pSrcItem; + pSrcItem->colUsed = 0; + sqlite3WalkSelect(&w, pSelect); +} +#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ + #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* ** This routine attempts to flatten subqueries as a performance optimization. @@ -3755,6 +3800,7 @@ static int flattenSubquery( Expr *pWhere; /* The WHERE clause */ struct SrcList_item *pSubitem; /* The subquery */ sqlite3 *db = pParse->db; + Walker w; /* Walker to persist agginfo data */ /* Check to see if flattening is permitted. Return 0 if not. */ @@ -4068,7 +4114,7 @@ static int flattenSubquery( ** We look at every expression in the outer query and every place we see ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". */ - if( pSub->pOrderBy ){ + if( pSub->pOrderBy && (pParent->selFlags & SF_NoopOrderBy)==0 ){ /* At this point, any non-zero iOrderByCol values indicate that the ** ORDER BY column expression is identical to the iOrderByCol'th ** expression returned by SELECT statement pSub. Since these values @@ -4092,7 +4138,13 @@ static int flattenSubquery( if( isLeftJoin>0 ){ sqlite3SetJoinExpr(pWhere, iNewParent); } - pParent->pWhere = sqlite3ExprAnd(pParse, pWhere, pParent->pWhere); + if( pWhere ){ + if( pParent->pWhere ){ + pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere); + }else{ + pParent->pWhere = pWhere; + } + } if( db->mallocFailed==0 ){ SubstContext x; x.pParse = pParse; @@ -4118,15 +4170,23 @@ static int flattenSubquery( pParent->pLimit = pSub->pLimit; pSub->pLimit = 0; } + + /* Recompute the SrcList_item.colUsed masks for the flattened + ** tables. */ + for(i=0; ia[i+iFrom]); + } } /* Finially, delete what is left of the subquery and return ** success. */ + sqlite3AggInfoPersistWalkerInit(&w, pParse); + sqlite3WalkSelect(&w,pSub1); sqlite3SelectDelete(db, pSub1); #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x100 ){ + if( sqlite3_unsupported_selecttrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After flattening:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -4166,9 +4226,8 @@ static void constInsert( assert( pColumn->op==TK_COLUMN ); assert( sqlite3ExprIsConstant(pValue) ); - if( !ExprHasProperty(pValue, EP_FixedCol) && sqlite3ExprAffinity(pValue)!=0 ){ - return; - } + if( ExprHasProperty(pColumn, EP_FixedCol) ) return; + if( sqlite3ExprAffinity(pValue)!=0 ) return; if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pConst->pParse,pExpr)) ){ return; } @@ -4191,9 +4250,6 @@ static void constInsert( if( pConst->apExpr==0 ){ pConst->nConst = 0; }else{ - if( ExprHasProperty(pValue, EP_FixedCol) ){ - pValue = pValue->pLeft; - } pConst->apExpr[pConst->nConst*2-2] = pColumn; pConst->apExpr[pConst->nConst*2-1] = pValue; } @@ -4387,11 +4443,14 @@ static int pushDownWhereTerms( ){ Expr *pNew; int nChng = 0; + Select *pSel; if( pWhere==0 ) return 0; if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */ #ifndef SQLITE_OMIT_WINDOWFUNC - if( pSubq->pWin ) return 0; /* restriction (6) */ + for(pSel=pSubq; pSel; pSel=pSel->pPrior){ + if( pSel->pWin ) return 0; /* restriction (6) */ + } #endif #ifdef SQLITE_DEBUG @@ -4469,7 +4528,7 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */ const char *zFunc; /* Name of aggregate function pFunc */ ExprList *pOrderBy; - u8 sortFlags; + u8 sortFlags = 0; assert( *ppMinMax==0 ); assert( pFunc->op==TK_AGG_FUNCTION ); @@ -4480,7 +4539,9 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){ zFunc = pFunc->u.zToken; if( sqlite3StrICmp(zFunc, "min")==0 ){ eRet = WHERE_ORDERBY_MIN; - sortFlags = KEYINFO_ORDER_BIGNULL; + if( sqlite3ExprCanBeNull(pEList->a[0].pExpr) ){ + sortFlags = KEYINFO_ORDER_BIGNULL; + } }else if( sqlite3StrICmp(zFunc, "max")==0 ){ eRet = WHERE_ORDERBY_MAX; sortFlags = KEYINFO_ORDER_DESC; @@ -4589,6 +4650,14 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ for(pX=p; pX && (pX->op==TK_ALL || pX->op==TK_SELECT); pX=pX->pPrior){} if( pX==0 ) return WRC_Continue; a = p->pOrderBy->a; +#ifndef SQLITE_OMIT_WINDOWFUNC + /* If iOrderByCol is already non-zero, then it has already been matched + ** to a result column of the SELECT statement. This occurs when the + ** SELECT is rewritten for window-functions processing and then passed + ** to sqlite3SelectPrep() and similar a second time. The rewriting done + ** by this function is not required in this case. */ + if( a[0].u.x.iOrderByCol ) return WRC_Continue; +#endif for(i=p->pOrderBy->nExpr-1; i>=0; i--){ if( a[i].pExpr->flags & EP_Collate ) break; } @@ -4940,8 +5009,8 @@ static int selectExpander(Walker *pWalker, Select *p){ for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab; assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); - if( pFrom->fg.isRecursive ) continue; - assert( pFrom->pTab==0 ); + if( pFrom->pTab ) continue; + assert( pFrom->fg.isRecursive==0 ); #ifndef SQLITE_OMIT_CTE if( withExpand(pWalker, pFrom) ) return WRC_Abort; if( pFrom->pTab ) {} else @@ -5148,7 +5217,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); sqlite3TokenInit(&sColname, zColname); sqlite3ExprListSetName(pParse, pNew, &sColname, 0); - if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ + if( pNew && (p->selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; sqlite3DbFree(db, pX->zEName); if( pSub ){ @@ -5188,29 +5257,6 @@ static int selectExpander(Walker *pWalker, Select *p){ return WRC_Continue; } -/* -** No-op routine for the parse-tree walker. -** -** When this routine is the Walker.xExprCallback then expression trees -** are walked without any actions being taken at each node. Presumably, -** when this routine is used for Walker.xExprCallback then -** Walker.xSelectCallback is set to do something useful for every -** subquery in the parser tree. -*/ -int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return WRC_Continue; -} - -/* -** No-op routine for the parse-tree walker for SELECT statements. -** subquery in the parser tree. -*/ -int sqlite3SelectWalkNoop(Walker *NotUsed, Select *NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); - return WRC_Continue; -} - #if SQLITE_DEBUG /* ** Always assert. This xSelectCallback2 implementation proves that the @@ -5352,6 +5398,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ struct AggInfo_func *pFunc; int nReg = pAggInfo->nFunc + pAggInfo->nColumn; if( nReg==0 ) return; + if( pParse->nErr || pParse->db->mallocFailed ) return; #ifdef SQLITE_DEBUG /* Verify that all AggInfo registers are within the range specified by ** AggInfo.mnReg..AggInfo.mxReg */ @@ -5368,7 +5415,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg); for(pFunc=pAggInfo->aFunc, i=0; inFunc; i++, pFunc++){ if( pFunc->iDistinct>=0 ){ - Expr *pE = pFunc->pExpr; + Expr *pE = pFunc->pFExpr; assert( !ExprHasProperty(pE, EP_xIsSelect) ); if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){ sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one " @@ -5392,8 +5439,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ - ExprList *pList = pF->pExpr->x.pList; - assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); + ExprList *pList = pF->pFExpr->x.pList; + assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0); sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); } @@ -5422,22 +5469,26 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ int nArg; int addrNext = 0; int regAgg; - ExprList *pList = pF->pExpr->x.pList; - assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); - assert( !IsWindowFunc(pF->pExpr) ); - if( ExprHasProperty(pF->pExpr, EP_WinFunc) ){ - Expr *pFilter = pF->pExpr->y.pWin->pFilter; + ExprList *pList = pF->pFExpr->x.pList; + assert( !ExprHasProperty(pF->pFExpr, EP_xIsSelect) ); + assert( !IsWindowFunc(pF->pFExpr) ); + if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ + Expr *pFilter = pF->pFExpr->y.pWin->pFilter; if( pAggInfo->nAccumulator && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) + && regAcc ){ + /* If regAcc==0, there there exists some min() or max() function + ** without a FILTER clause that will ensure the magnet registers + ** are populated. */ if( regHit==0 ) regHit = ++pParse->nMem; - /* If this is the first row of the group (regAcc==0), clear the + /* If this is the first row of the group (regAcc contains 0), clear the ** "magnet" register regHit so that the accumulator registers ** are populated if the FILTER clause jumps over the the ** invocation of min() or max() altogether. Or, if this is not - ** the first row (regAcc==1), set the magnet register so that the - ** accumulators are not populated unless the min()/max() is invoked and - ** indicates that they should be. */ + ** the first row (regAcc contains 1), set the magnet register so that + ** the accumulators are not populated unless the min()/max() is invoked + ** and indicates that they should be. */ sqlite3VdbeAddOp2(v, OP_Copy, regAcc, regHit); } addrNext = sqlite3VdbeMakeLabel(pParse); @@ -5488,12 +5539,12 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); } for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ - sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); + sqlite3ExprCode(pParse, pC->pCExpr, pC->iMem); } pAggInfo->directMode = 0; if( addrHitTest ){ - sqlite3VdbeJumpHere(v, addrHitTest); + sqlite3VdbeJumpHereOrPopInst(v, addrHitTest); } } @@ -5573,7 +5624,7 @@ static void havingToWhere(Parse *pParse, Select *p){ sWalker.u.pSelect = p; sqlite3WalkExpr(&sWalker, p->pHaving); #if SELECTTRACE_ENABLED - if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){ + if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -5695,7 +5746,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ p->selFlags &= ~SF_Aggregate; #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x400 ){ + if( sqlite3_unsupported_selecttrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -5731,10 +5782,10 @@ int sqlite3Select( Expr *pWhere; /* The WHERE clause. May be NULL */ ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */ Expr *pHaving; /* The HAVING clause. May be NULL */ + AggInfo *pAggInfo = 0; /* Aggregate information */ int rc = 1; /* Value to return from this function */ DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */ SortCtx sSort; /* Info on how to code the ORDER BY clause */ - AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 *db; /* The database connection */ ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */ @@ -5746,10 +5797,9 @@ int sqlite3Select( return 1; } if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; - memset(&sAggInfo, 0, sizeof(sAggInfo)); #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); - if( sqlite3SelectTrace & 0x100 ){ + if( sqlite3_unsupported_selecttrace & 0x100 ){ sqlite3TreeViewSelect(0, p, 0); } #endif @@ -5768,6 +5818,7 @@ int sqlite3Select( sqlite3ExprListDelete(db, p->pOrderBy); p->pOrderBy = 0; p->selFlags &= ~SF_Distinct; + p->selFlags |= SF_NoopOrderBy; } sqlite3SelectPrep(pParse, p, 0); if( pParse->nErr || db->mallocFailed ){ @@ -5775,12 +5826,30 @@ int sqlite3Select( } assert( p->pEList!=0 ); #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x104 ){ + if( sqlite3_unsupported_selecttrace & 0x104 ){ SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif + /* If the SF_UpdateFrom flag is set, then this function is being called + ** as part of populating the temp table for an UPDATE...FROM statement. + ** In this case, it is an error if the target object (pSrc->a[0]) name + ** or alias is duplicated within FROM clause (pSrc->a[1..n]). */ + if( p->selFlags & SF_UpdateFrom ){ + struct SrcList_item *p0 = &p->pSrc->a[0]; + for(i=1; ipSrc->nSrc; i++){ + struct SrcList_item *p1 = &p->pSrc->a[i]; + if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ + sqlite3ErrorMsg(pParse, + "target object/alias may not appear in FROM clause: %s", + p0->zAlias ? p0->zAlias : p0->pTab->zName + ); + goto select_end; + } + } + } + if( pDest->eDest==SRT_Output ){ generateColumnNames(pParse, p); } @@ -5792,7 +5861,7 @@ int sqlite3Select( goto select_end; } #if SELECTTRACE_ENABLED - if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){ + if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){ SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -5803,7 +5872,7 @@ int sqlite3Select( memset(&sSort, 0, sizeof(sSort)); sSort.pOrderBy = p->pOrderBy; - /* Try to various optimizations (flattening subqueries, and strength + /* Try to do various optimizations (flattening subqueries, and strength ** reduction of join operators) in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) @@ -5812,6 +5881,11 @@ int sqlite3Select( Select *pSub = pItem->pSelect; Table *pTab = pItem->pTab; + /* The expander should have already created transient Table objects + ** even for FROM clause elements such as subqueries that do not correspond + ** to a real table */ + assert( pTab!=0 ); + /* Convert LEFT JOIN into JOIN if there are terms of the right table ** of the LEFT JOIN used in the WHERE clause. */ @@ -5894,7 +5968,7 @@ int sqlite3Select( rc = multiSelect(pParse, p, pDest); #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); - if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif @@ -5913,7 +5987,7 @@ int sqlite3Select( && propagateConstants(pParse, p) ){ #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x100 ){ + if( sqlite3_unsupported_selecttrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -6001,7 +6075,7 @@ int sqlite3Select( (pItem->fg.jointype & JT_OUTER)!=0) ){ #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x100 ){ + if( sqlite3_unsupported_selecttrace & 0x100 ){ SELECTTRACE(0x100,pParse,p, ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); sqlite3TreeViewSelect(0, p, 0); @@ -6101,7 +6175,7 @@ int sqlite3Select( sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x400 ){ + if( sqlite3_unsupported_selecttrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -6137,7 +6211,7 @@ int sqlite3Select( assert( sDistinct.isTnct ); #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x400 ){ + if( sqlite3_unsupported_selecttrace & 0x400 ){ SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); sqlite3TreeViewSelect(0, p, 0); } @@ -6203,7 +6277,7 @@ int sqlite3Select( u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0) | (p->selFlags & SF_FixedLimit); #ifndef SQLITE_OMIT_WINDOWFUNC - Window *pWin = p->pWin; /* Master window object (or NULL) */ + Window *pWin = p->pWin; /* Main window object (or NULL) */ if( pWin ){ sqlite3WindowCodeInit(pParse, p); } @@ -6336,14 +6410,21 @@ int sqlite3Select( ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the ** SELECT statement. */ + pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); + if( pAggInfo==0 ){ + goto select_end; + } + pAggInfo->pNext = pParse->pAggList; + pParse->pAggList = pAggInfo; + pAggInfo->selId = p->selId; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; - sNC.uNC.pAggInfo = &sAggInfo; + sNC.uNC.pAggInfo = pAggInfo; VVA_ONLY( sNC.ncFlags = NC_UAggInfo; ) - sAggInfo.mnReg = pParse->nMem+1; - sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; - sAggInfo.pGroupBy = pGroupBy; + pAggInfo->mnReg = pParse->nMem+1; + pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; + pAggInfo->pGroupBy = pGroupBy; sqlite3ExprAnalyzeAggList(&sNC, pEList); sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy); if( pHaving ){ @@ -6356,14 +6437,14 @@ int sqlite3Select( } sqlite3ExprAnalyzeAggregates(&sNC, pHaving); } - sAggInfo.nAccumulator = sAggInfo.nColumn; - if( p->pGroupBy==0 && p->pHaving==0 && sAggInfo.nFunc==1 ){ - minMaxFlag = minMaxQuery(db, sAggInfo.aFunc[0].pExpr, &pMinMaxOrderBy); + pAggInfo->nAccumulator = pAggInfo->nColumn; + if( p->pGroupBy==0 && p->pHaving==0 && pAggInfo->nFunc==1 ){ + minMaxFlag = minMaxQuery(db, pAggInfo->aFunc[0].pFExpr, &pMinMaxOrderBy); }else{ minMaxFlag = WHERE_ORDERBY_NORMAL; } - for(i=0; inFunc; i++){ + Expr *pExpr = pAggInfo->aFunc[i].pFExpr; assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); sNC.ncFlags |= NC_InAggFunc; sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); @@ -6375,22 +6456,22 @@ int sqlite3Select( #endif sNC.ncFlags &= ~NC_InAggFunc; } - sAggInfo.mxReg = pParse->nMem; + pAggInfo->mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; #if SELECTTRACE_ENABLED - if( sqlite3SelectTrace & 0x400 ){ + if( sqlite3_unsupported_selecttrace & 0x400 ){ int ii; - SELECTTRACE(0x400,pParse,p,("After aggregate analysis:\n")); + SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); sqlite3TreeViewSelect(0, p, 0); - for(ii=0; iinColumn; ii++){ sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", - ii, sAggInfo.aCol[ii].iMem); - sqlite3TreeViewExpr(0, sAggInfo.aCol[ii].pExpr, 0); + ii, pAggInfo->aCol[ii].iMem); + sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0); } - for(ii=0; iinFunc; ii++){ sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", - ii, sAggInfo.aFunc[ii].iMem); - sqlite3TreeViewExpr(0, sAggInfo.aFunc[ii].pExpr, 0); + ii, pAggInfo->aFunc[ii].iMem); + sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0); } } #endif @@ -6415,10 +6496,11 @@ int sqlite3Select( ** that we do not need it after all, the OP_SorterOpen instruction ** will be converted into a Noop. */ - sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn); + pAggInfo->sortingIdx = pParse->nTab++; + pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pGroupBy, + 0, pAggInfo->nColumn); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, - sAggInfo.sortingIdx, sAggInfo.nSortingColumn, + pAggInfo->sortingIdx, pAggInfo->nSortingColumn, 0, (char*)pKeyInfo, P4_KEYINFO); /* Initialize memory locations used by GROUP BY aggregate processing @@ -6473,8 +6555,8 @@ int sqlite3Select( nGroupBy = pGroupBy->nExpr; nCol = nGroupBy; j = nGroupBy; - for(i=0; i=j ){ + for(i=0; inColumn; i++){ + if( pAggInfo->aCol[i].iSorterColumn>=j ){ nCol++; j++; } @@ -6482,8 +6564,8 @@ int sqlite3Select( regBase = sqlite3GetTempRange(pParse, nCol); sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0); j = nGroupBy; - for(i=0; inColumn; i++){ + struct AggInfo_col *pCol = &pAggInfo->aCol[i]; if( pCol->iSorterColumn>=j ){ int r1 = j + regBase; sqlite3ExprCodeGetColumnOfTable(v, @@ -6493,16 +6575,16 @@ int sqlite3Select( } regRecord = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); - sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord); + sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3ReleaseTempRange(pParse, regBase, nCol); sqlite3WhereEnd(pWInfo); - sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++; + pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; sortOut = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); - sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd); + sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd); VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); - sAggInfo.useSortingIdx = 1; + pAggInfo->useSortingIdx = 1; } /* If the index or temporary table used by the GROUP BY sort @@ -6526,14 +6608,14 @@ int sqlite3Select( */ addrTopOfLoop = sqlite3VdbeCurrentAddr(v); if( groupBySort ){ - sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx, + sqlite3VdbeAddOp3(v, OP_SorterData, pAggInfo->sortingIdx, sortOut, sortPTab); } for(j=0; jnExpr; j++){ if( groupBySort ){ sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); }else{ - sAggInfo.directMode = 1; + pAggInfo->directMode = 1; sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); } } @@ -6563,14 +6645,14 @@ int sqlite3Select( ** the current row */ sqlite3VdbeJumpHere(v, addr1); - updateAccumulator(pParse, iUseFlag, &sAggInfo); + updateAccumulator(pParse, iUseFlag, pAggInfo); sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); VdbeComment((v, "indicate data in accumulator")); /* End of the loop */ if( groupBySort ){ - sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop); + sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop); VdbeCoverage(v); }else{ sqlite3WhereEnd(pWInfo); @@ -6603,7 +6685,7 @@ int sqlite3Select( VdbeCoverage(v); VdbeComment((v, "Groupby result generator entry point")); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); - finalizeAggFunctions(pParse, &sAggInfo); + finalizeAggFunctions(pParse, pAggInfo); sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, @@ -6614,16 +6696,15 @@ int sqlite3Select( /* Generate a subroutine that will reset the group-by accumulator */ sqlite3VdbeResolveLabel(v, addrReset); - resetAccumulator(pParse, &sAggInfo); + resetAccumulator(pParse, pAggInfo); sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag); VdbeComment((v, "indicate accumulator empty")); sqlite3VdbeAddOp1(v, OP_Return, regReset); } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { -#ifndef SQLITE_OMIT_BTREECOUNT Table *pTab; - if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ + if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ /* If isSimpleCount() returns a pointer to a Table structure, then ** the SQL statement is of the form: ** @@ -6642,7 +6723,7 @@ int sqlite3Select( Index *pIdx; /* Iterator variable */ KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ Index *pBest = 0; /* Best index found so far */ - int iRoot = pTab->tnum; /* Root page of scanned b-tree */ + Pgno iRoot = pTab->tnum; /* Root page of scanned b-tree */ sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); @@ -6657,13 +6738,15 @@ int sqlite3Select( ** passed to keep OP_OpenRead happy. */ if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->bUnordered==0 - && pIdx->szIdxRowszTabRow - && pIdx->pPartIdxWhere==0 - && (!pBest || pIdx->szIdxRowszIdxRow) - ){ - pBest = pIdx; + if( !p->pSrc->a[0].fg.notIndexed ){ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->bUnordered==0 + && pIdx->szIdxRowszTabRow + && pIdx->pPartIdxWhere==0 + && (!pBest || pIdx->szIdxRowszIdxRow) + ){ + pBest = pIdx; + } } } if( pBest ){ @@ -6672,17 +6755,16 @@ int sqlite3Select( } /* Open a read-only cursor, execute the OP_Count, close the cursor. */ - sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1); + sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, (int)iRoot, iDb, 1); if( pKeyInfo ){ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } - sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); + sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); - }else -#endif /* SQLITE_OMIT_BTREECOUNT */ - { + }else{ int regAcc = 0; /* "populate accumulators" flag */ + int addrSkip; /* If there are accumulator registers but no min() or max() functions ** without FILTER clauses, allocate register regAcc. Register regAcc @@ -6693,12 +6775,16 @@ int sqlite3Select( ** first row visited by the aggregate, so that they are updated at ** least once even if the FILTER clause means the min() or max() ** function visits zero rows. */ - if( sAggInfo.nAccumulator ){ - for(i=0; ifuncFlags&SQLITE_FUNC_NEEDCOLL ) break; + if( pAggInfo->nAccumulator ){ + for(i=0; inFunc; i++){ + if( ExprHasProperty(pAggInfo->aFunc[i].pFExpr, EP_WinFunc) ){ + continue; + } + if( pAggInfo->aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ){ + break; + } } - if( i==sAggInfo.nFunc ){ + if( i==pAggInfo->nFunc ){ regAcc = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc); } @@ -6709,7 +6795,7 @@ int sqlite3Select( ** of output. */ assert( p->pGroupBy==0 ); - resetAccumulator(pParse, &sAggInfo); + resetAccumulator(pParse, pAggInfo); /* If this query is a candidate for the min/max optimization, then ** minMaxFlag will have been previously set to either @@ -6725,15 +6811,14 @@ int sqlite3Select( if( pWInfo==0 ){ goto select_end; } - updateAccumulator(pParse, regAcc, &sAggInfo); + updateAccumulator(pParse, regAcc, pAggInfo); if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); - if( sqlite3WhereIsOrdered(pWInfo)>0 ){ - sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo)); - VdbeComment((v, "%s() by index", - (minMaxFlag==WHERE_ORDERBY_MIN?"min":"max"))); + addrSkip = sqlite3WhereOrderByLimitOptLabel(pWInfo); + if( addrSkip!=sqlite3WhereContinueLabel(pWInfo) ){ + sqlite3VdbeGoto(v, addrSkip); } sqlite3WhereEnd(pWInfo); - finalizeAggFunctions(pParse, &sAggInfo); + finalizeAggFunctions(pParse, pAggInfo); } sSort.pOrderBy = 0; @@ -6772,11 +6857,28 @@ int sqlite3Select( */ select_end: sqlite3ExprListDelete(db, pMinMaxOrderBy); - sqlite3DbFree(db, sAggInfo.aCol); - sqlite3DbFree(db, sAggInfo.aFunc); +#ifdef SQLITE_DEBUG + if( pAggInfo && !db->mallocFailed ){ + for(i=0; inColumn; i++){ + Expr *pExpr = pAggInfo->aCol[i].pCExpr; + assert( pExpr!=0 || db->mallocFailed ); + if( pExpr==0 ) continue; + assert( pExpr->pAggInfo==pAggInfo ); + assert( pExpr->iAgg==i ); + } + for(i=0; inFunc; i++){ + Expr *pExpr = pAggInfo->aFunc[i].pFExpr; + assert( pExpr!=0 || db->mallocFailed ); + if( pExpr==0 ) continue; + assert( pExpr->pAggInfo==pAggInfo ); + assert( pExpr->iAgg==i ); + } + } +#endif + #if SELECTTRACE_ENABLED SELECTTRACE(0x1,pParse,p,("end processing\n")); - if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ + if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif diff --git a/src/shell.c.in b/src/shell.c.in index 2993f5b..738c90d 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -17,6 +17,14 @@ #define _CRT_SECURE_NO_WARNINGS #endif +/* +** Determine if we are dealing with WinRT, which provides only a subset of +** the full Win32 API. +*/ +#if !defined(SQLITE_OS_WINRT) +# define SQLITE_OS_WINRT 0 +#endif + /* ** Warning pragmas copied from msvc.h in the core. */ @@ -129,22 +137,26 @@ typedef unsigned char u8; #if defined(_WIN32) || defined(WIN32) -# include -# include -# define isatty(h) _isatty(h) -# ifndef access -# define access(f,m) _access((f),(m)) +# if SQLITE_OS_WINRT +# define SQLITE_OMIT_POPEN 1 +# else +# include +# include +# define isatty(h) _isatty(h) +# ifndef access +# define access(f,m) _access((f),(m)) +# endif +# ifndef unlink +# define unlink _unlink +# endif +# ifndef strdup +# define strdup _strdup +# endif +# undef popen +# define popen _popen +# undef pclose +# define pclose _pclose # endif -# ifndef unlink -# define unlink _unlink -# endif -# ifndef strdup -# define strdup _strdup -# endif -# undef popen -# define popen _popen -# undef pclose -# define pclose _pclose #else /* Make sure isatty() has a prototype. */ extern int isatty(int); @@ -173,6 +185,9 @@ typedef unsigned char u8; #define ToLower(X) (char)tolower((unsigned char)X) #if defined(_WIN32) || defined(WIN32) +#if SQLITE_OS_WINRT +#include +#endif #include /* string conversion routines only needed on Win32 */ @@ -188,7 +203,7 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); ** rendering quoted strings that contain \n characters). The following ** routines take care of that. */ -#if defined(_WIN32) || defined(WIN32) +#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT static void setBinaryMode(FILE *file, int isOutput){ if( isOutput ) fflush(file); _setmode(_fileno(file), _O_BINARY); @@ -292,6 +307,7 @@ static int hasTimer(void){ if( getProcessTimesAddr ){ return 1; } else { +#if !SQLITE_OS_WINRT /* GetProcessTimes() isn't supported in WIN95 and some other Windows ** versions. See if the version we are running on has it, and if it ** does, save off a pointer to it and the current process handle. @@ -308,6 +324,7 @@ static int hasTimer(void){ FreeLibrary(hinstLib); } } +#endif } return 0; } @@ -397,6 +414,15 @@ static sqlite3 *globalDb = 0; */ static volatile int seenInterrupt = 0; +#ifdef SQLITE_DEBUG +/* +** Out-of-memory simulator variables +*/ +static unsigned int oomCounter = 0; /* Simulate OOM when equals 1 */ +static unsigned int oomRepeat = 0; /* Number of OOMs in a row */ +static void*(*defaultMalloc)(int) = 0; /* The low-level malloc routine */ +#endif /* SQLITE_DEBUG */ + /* ** This is the name of our program. It is set in main(), used ** in a number of other places, mostly for error messages. @@ -448,6 +474,49 @@ static void shell_out_of_memory(void){ exit(1); } +#ifdef SQLITE_DEBUG +/* This routine is called when a simulated OOM occurs. It is broken +** out as a separate routine to make it easy to set a breakpoint on +** the OOM +*/ +void shellOomFault(void){ + if( oomRepeat>0 ){ + oomRepeat--; + }else{ + oomCounter--; + } +} +#endif /* SQLITE_DEBUG */ + +#ifdef SQLITE_DEBUG +/* This routine is a replacement malloc() that is used to simulate +** Out-Of-Memory (OOM) errors for testing purposes. +*/ +static void *oomMalloc(int nByte){ + if( oomCounter ){ + if( oomCounter==1 ){ + shellOomFault(); + return 0; + }else{ + oomCounter--; + } + } + return defaultMalloc(nByte); +} +#endif /* SQLITE_DEBUG */ + +#ifdef SQLITE_DEBUG +/* Register the OOM simulator. This must occur before any memory +** allocations */ +static void registerOomSimulator(void){ + sqlite3_mem_methods mem; + sqlite3_config(SQLITE_CONFIG_GETMALLOC, &mem); + defaultMalloc = mem.xMalloc; + mem.xMalloc = oomMalloc; + sqlite3_config(SQLITE_CONFIG_MALLOC, &mem); +} +#endif + /* ** Write I/O traces to the following stream. */ @@ -554,6 +623,21 @@ static int strlenChar(const char *z){ return n; } +/* +** Return true if zFile does not exist or if it is not an ordinary file. +*/ +#ifdef _WIN32 +# define notNormalFile(X) 0 +#else +static int notNormalFile(const char *zFile){ + struct stat x; + int rc; + memset(&x, 0, sizeof(x)); + rc = stat(zFile, &x); + return rc || !S_ISREG(x.st_mode); +} +#endif + /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer @@ -879,7 +963,7 @@ static void shellModuleSchema( ** CREATE VIRTUAL TABLE ** ** This UDF is used by the .schema command to insert the schema name of -** attached databases into the middle of the sqlite_master.sql field. +** attached databases into the middle of the sqlite_schema.sql field. */ static void shellAddSchemaName( sqlite3_context *pCtx, @@ -954,6 +1038,9 @@ INCLUDE ../ext/misc/fileio.c INCLUDE ../ext/misc/completion.c INCLUDE ../ext/misc/appendvfs.c INCLUDE ../ext/misc/memtrace.c +INCLUDE ../ext/misc/uint.c +INCLUDE ../ext/misc/decimal.c +INCLUDE ../ext/misc/ieee754.c #ifdef SQLITE_HAVE_ZLIB INCLUDE ../ext/misc/zipfile.c INCLUDE ../ext/misc/sqlar.c @@ -978,18 +1065,6 @@ struct OpenSession { }; #endif -/* -** Shell output mode information from before ".explain on", -** saved so that it can be restored by ".explain off" -*/ -typedef struct SavedModeInfo SavedModeInfo; -struct SavedModeInfo { - int valid; /* Is there legit data in here? */ - int mode; /* Mode prior to ".explain on" */ - int showHeader; /* The ".header" setting prior to ".explain on" */ - int colWidth[100]; /* Column widths prior to ".explain on" */ -}; - typedef struct ExpertInfo ExpertInfo; struct ExpertInfo { sqlite3expert *pExpert; @@ -1050,6 +1125,7 @@ struct ShellState { unsigned mxProgress; /* Maximum progress callbacks before failing */ unsigned flgProgress; /* Flags for the progress callback */ unsigned shellFlgs; /* Various flags */ + unsigned priorShFlgs; /* Saved copy of flags */ sqlite3_int64 szMax; /* --maxsize argument to .open */ char *zDestTable; /* Name of destination table when MODE_Insert */ char *zTempFile; /* Temporary file that might need deleting */ @@ -1058,8 +1134,9 @@ struct ShellState { char rowSeparator[20]; /* Row separator character for MODE_Ascii */ char colSepPrior[20]; /* Saved column separator */ char rowSepPrior[20]; /* Saved row separator */ - int colWidth[100]; /* Requested width of each column when in column mode*/ - int actualWidth[100]; /* Actual width of each column */ + int *colWidth; /* Requested width of each column in columnar modes */ + int *actualWidth; /* Actual width of each column */ + int nWidth; /* Number of slots in colWidth[] and actualWidth[] */ char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ char outfile[FILENAME_MAX]; /* Filename for *out */ @@ -1120,6 +1197,7 @@ struct ShellState { #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo or --echo setting */ +#define SHFLG_HeaderSet 0x00000080 /* .header has been used */ /* ** Macros for testing and setting shellFlgs @@ -1144,6 +1222,10 @@ struct ShellState { #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ #define MODE_Pretty 11 /* Pretty-print schemas */ #define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */ +#define MODE_Json 13 /* Output JSON */ +#define MODE_Markdown 14 /* Markdown formatting */ +#define MODE_Table 15 /* MySQL-style table formatting */ +#define MODE_Box 16 /* Unicode box-drawing characters */ static const char *modeDescr[] = { "line", @@ -1158,7 +1240,11 @@ static const char *modeDescr[] = { "explain", "ascii", "prettyprint", - "eqp" + "eqp", + "json", + "markdown", + "table", + "box" }; /* @@ -1350,11 +1436,13 @@ edit_func_end: */ static void outputModePush(ShellState *p){ p->modePrior = p->mode; + p->priorShFlgs = p->shellFlgs; memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); } static void outputModePop(ShellState *p){ p->mode = p->modePrior; + p->shellFlgs = p->priorShFlgs; memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); } @@ -1524,6 +1612,40 @@ static void output_c_string(FILE *out, const char *z){ fputc('"', out); } +/* +** Output the given string as a quoted according to JSON quoting rules. +*/ +static void output_json_string(FILE *out, const char *z, int n){ + unsigned int c; + if( n<0 ) n = (int)strlen(z); + fputc('"', out); + while( n-- ){ + c = *(z++); + if( c=='\\' || c=='"' ){ + fputc('\\', out); + fputc(c, out); + }else if( c<=0x1f ){ + fputc('\\', out); + if( c=='\b' ){ + fputc('b', out); + }else if( c=='\f' ){ + fputc('f', out); + }else if( c=='\n' ){ + fputc('n', out); + }else if( c=='\r' ){ + fputc('r', out); + }else if( c=='\t' ){ + fputc('t', out); + }else{ + raw_printf(out, "u%04x",c); + } + }else{ + fputc(c, out); + } + } + fputc('"', out); +} + /* ** Output the given string with characters that are special to ** HTML escaped. @@ -1833,6 +1955,40 @@ static int progress_handler(void *pClientData) { } #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ +/* +** Print N dashes +*/ +static void print_dashes(FILE *out, int N){ + const char zDash[] = "--------------------------------------------------"; + const int nDash = sizeof(zDash) - 1; + while( N>nDash ){ + fputs(zDash, out); + N -= nDash; + } + raw_printf(out, "%.*s", N, zDash); +} + +/* +** Print a markdown or table-style row separator using ascii-art +*/ +static void print_row_separator( + ShellState *p, + int nArg, + const char *zSep +){ + int i; + if( nArg>0 ){ + fputs(zSep, p->out); + print_dashes(p->out, p->actualWidth[0]+2); + for(i=1; iout); + print_dashes(p->out, p->actualWidth[i]+2); + } + fputs(zSep, p->out); + } + fputs("\n", p->out); +} + /* ** This is the callback routine that the shell ** invokes for each row of a query result. @@ -1842,7 +1998,7 @@ static int shell_callback( int nArg, /* Number of result columns */ char **azArg, /* Text of each result column */ char **azCol, /* Column names */ - int *aiType /* Column types */ + int *aiType /* Column types. Might be NULL */ ){ int i; ShellState *p = (ShellState*)pArg; @@ -1863,71 +2019,27 @@ static int shell_callback( } break; } - case MODE_Explain: - case MODE_Column: { - static const int aExplainWidths[] = {4, 13, 4, 4, 4, 13, 2, 13}; - const int *colWidth; - int showHdr; - char *rowSep; - int nWidth; - if( p->cMode==MODE_Column ){ - colWidth = p->colWidth; - nWidth = ArraySize(p->colWidth); - showHdr = p->showHeader; - rowSep = p->rowSeparator; - }else{ - colWidth = aExplainWidths; - nWidth = ArraySize(aExplainWidths); - showHdr = 1; - rowSep = SEP_Row; + case MODE_Explain: { + static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; + if( nArg>ArraySize(aExplainWidth) ){ + nArg = ArraySize(aExplainWidth); } if( p->cnt++==0 ){ for(i=0; inullValue); - if( wactualWidth) ){ - p->actualWidth[i] = w; - } - if( showHdr ){ - utf8_width_print(p->out, w, azCol[i]); - utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); - } + int w = aExplainWidth[i]; + utf8_width_print(p->out, w, azCol[i]); + fputs(i==nArg-1 ? "\n" : " ", p->out); } - if( showHdr ){ - for(i=0; iactualWidth) ){ - w = p->actualWidth[i]; - if( w<0 ) w = -w; - }else{ - w = 10; - } - utf8_printf(p->out,"%-*.*s%s",w,w, - "----------------------------------------------------------" - "----------------------------------------------------------", - i==nArg-1 ? rowSep : " "); - } + for(i=0; iout, w); + fputs(i==nArg-1 ? "\n" : " ", p->out); } } if( azArg==0 ) break; for(i=0; iactualWidth) ){ - w = p->actualWidth[i]; - }else{ - w = 10; - } - if( p->cMode==MODE_Explain && azArg[i] && strlenChar(azArg[i])>w ){ + int w = aExplainWidth[i]; + if( azArg[i] && strlenChar(azArg[i])>w ){ w = strlenChar(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ @@ -1937,7 +2049,7 @@ static int shell_callback( p->iIndent++; } utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); - utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : " "); + fputs(i==nArg-1 ? "\n" : " ", p->out); } break; } @@ -2141,18 +2253,60 @@ static int shell_callback( raw_printf(p->out,");\n"); break; } + case MODE_Json: { + if( azArg==0 ) break; + if( p->cnt==0 ){ + fputs("[{", p->out); + }else{ + fputs(",\n{", p->out); + } + p->cnt++; + for(i=0; iout, azCol[i], -1); + putc(':', p->out); + if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ + fputs("null",p->out); + }else if( aiType && aiType[i]==SQLITE_FLOAT ){ + char z[50]; + double r = sqlite3_column_double(p->pStmt, i); + sqlite3_uint64 ur; + memcpy(&ur,&r,sizeof(r)); + if( ur==0x7ff0000000000000LL ){ + raw_printf(p->out, "1e999"); + }else if( ur==0xfff0000000000000LL ){ + raw_printf(p->out, "-1e999"); + }else{ + sqlite3_snprintf(50,z,"%!.20g", r); + raw_printf(p->out, "%s", z); + } + }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ + const void *pBlob = sqlite3_column_blob(p->pStmt, i); + int nBlob = sqlite3_column_bytes(p->pStmt, i); + output_json_string(p->out, pBlob, nBlob); + }else if( aiType && aiType[i]==SQLITE_TEXT ){ + output_json_string(p->out, azArg[i], -1); + }else{ + utf8_printf(p->out,"%s", azArg[i]); + } + if( iout); + } + } + putc('}', p->out); + break; + } case MODE_Quote: { if( azArg==0 ) break; if( p->cnt==0 && p->showHeader ){ for(i=0; i0 ) raw_printf(p->out, ","); + if( i>0 ) fputs(p->colSeparator, p->out); output_quoted_string(p->out, azCol[i]); } - raw_printf(p->out,"\n"); + fputs(p->rowSeparator, p->out); } p->cnt++; for(i=0; i0 ) raw_printf(p->out, ","); + if( i>0 ) fputs(p->colSeparator, p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ utf8_printf(p->out,"NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ @@ -2174,7 +2328,7 @@ static int shell_callback( output_quoted_string(p->out, azArg[i]); } } - raw_printf(p->out,"\n"); + fputs(p->rowSeparator, p->out); break; } case MODE_Ascii: { @@ -2247,16 +2401,16 @@ static void createSelftestTable(ShellState *p){ "INSERT INTO [_shell$self]\n" " SELECT 'run',\n" " 'SELECT hex(sha3_query(''SELECT type,name,tbl_name,sql " - "FROM sqlite_master ORDER BY 2'',224))',\n" + "FROM sqlite_schema ORDER BY 2'',224))',\n" " hex(sha3_query('SELECT type,name,tbl_name,sql " - "FROM sqlite_master ORDER BY 2',224));\n" + "FROM sqlite_schema ORDER BY 2',224));\n" "INSERT INTO [_shell$self]\n" " SELECT 'run'," " 'SELECT hex(sha3_query(''SELECT * FROM \"' ||" " printf('%w',name) || '\" NOT INDEXED'',224))',\n" " hex(sha3_query(printf('SELECT * FROM \"%w\" NOT INDEXED',name),224))\n" " FROM (\n" - " SELECT name FROM sqlite_master\n" + " SELECT name FROM sqlite_schema\n" " WHERE type='table'\n" " AND name<>'selftest'\n" " AND coalesce(rootpage,0)>0\n" @@ -2319,8 +2473,7 @@ static void set_table_name(ShellState *p, const char *zName){ */ static int run_table_dump_query( ShellState *p, /* Query context */ - const char *zSelect, /* SELECT statement to extract content */ - const char *zFirstRow /* Print before first row, if not NULL */ + const char *zSelect /* SELECT statement to extract content */ ){ sqlite3_stmt *pSelect; int rc; @@ -2337,10 +2490,6 @@ static int run_table_dump_query( rc = sqlite3_step(pSelect); nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ - if( zFirstRow ){ - utf8_printf(p->out, "%s", zFirstRow); - zFirstRow = 0; - } z = (const char*)sqlite3_column_text(pSelect, 0); utf8_printf(p->out, "%s", z); for(i=1; inDash ){ + utf8_printf(out, zDash); + N -= nDash; + } + utf8_printf(out, "%.*s", N, zDash); +} + +/* +** Draw a horizontal separator for a MODE_Box table. +*/ +static void print_box_row_separator( + ShellState *p, + int nArg, + const char *zSep1, + const char *zSep2, + const char *zSep3 +){ + int i; + if( nArg>0 ){ + utf8_printf(p->out, "%s", zSep1); + print_box_line(p->out, p->actualWidth[0]+2); + for(i=1; iout, "%s", zSep2); + print_box_line(p->out, p->actualWidth[i]+2); + } + utf8_printf(p->out, "%s", zSep3); + } + fputs("\n", p->out); +} + + + +/* +** Run a prepared statement and output the result in one of the +** table-oriented formats: MODE_Column, MODE_Markdown, MODE_Table, +** or MODE_Box. +** +** This is different from ordinary exec_prepared_stmt() in that +** it has to run the entire query and gather the results into memory +** first, in order to determine column widths, before providing +** any output. +*/ +static void exec_prepared_stmt_columnar( + ShellState *p, /* Pointer to ShellState */ + sqlite3_stmt *pStmt /* Statment to run */ +){ + sqlite3_int64 nRow = 0; + int nColumn = 0; + char **azData = 0; + sqlite3_int64 nAlloc = 0; + const char *z; + int rc; + sqlite3_int64 i, nData; + int j, nTotal, w, n; + const char *colSep = 0; + const char *rowSep = 0; + + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ) return; + nColumn = sqlite3_column_count(pStmt); + nAlloc = nColumn*4; + azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); + if( azData==0 ) shell_out_of_memory(); + for(i=0; i= nAlloc ){ + nAlloc *= 2; + azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); + if( azData==0 ) shell_out_of_memory(); + } + nRow++; + for(i=0; ip->nWidth ){ + p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); + if( p->colWidth==0 ) shell_out_of_memory(); + for(i=p->nWidth; icolWidth[i] = 0; + p->nWidth = nColumn; + p->actualWidth = &p->colWidth[nColumn]; + } + memset(p->actualWidth, 0, nColumn*sizeof(int)); + for(i=0; icolWidth[i]; + if( w<0 ) w = -w; + p->actualWidth[i] = w; + } + nTotal = nColumn*(nRow+1); + for(i=0; inullValue; + n = strlenChar(z); + j = i%nColumn; + if( n>p->actualWidth[j] ) p->actualWidth[j] = n; + } + if( seenInterrupt ) goto columnar_end; + switch( p->cMode ){ + case MODE_Column: { + colSep = " "; + rowSep = "\n"; + if( p->showHeader ){ + for(i=0; iactualWidth[i]; + if( p->colWidth[i]<0 ) w = -w; + utf8_width_print(p->out, w, azData[i]); + fputs(i==nColumn-1?"\n":" ", p->out); + } + for(i=0; iout, p->actualWidth[i]); + fputs(i==nColumn-1?"\n":" ", p->out); + } + } + break; + } + case MODE_Table: { + colSep = " | "; + rowSep = " |\n"; + print_row_separator(p, nColumn, "+"); + fputs("| ", p->out); + for(i=0; iactualWidth[i]; + n = strlenChar(azData[i]); + utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + fputs(i==nColumn-1?" |\n":" | ", p->out); + } + print_row_separator(p, nColumn, "+"); + break; + } + case MODE_Markdown: { + colSep = " | "; + rowSep = " |\n"; + fputs("| ", p->out); + for(i=0; iactualWidth[i]; + n = strlenChar(azData[i]); + utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + fputs(i==nColumn-1?" |\n":" | ", p->out); + } + print_row_separator(p, nColumn, "|"); + break; + } + case MODE_Box: { + colSep = " " BOX_13 " "; + rowSep = " " BOX_13 "\n"; + print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); + utf8_printf(p->out, BOX_13 " "); + for(i=0; iactualWidth[i]; + n = strlenChar(azData[i]); + utf8_printf(p->out, "%*s%s%*s%s", + (w-n)/2, "", azData[i], (w-n+1)/2, "", + i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); + } + print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); + break; + } + } + for(i=nColumn, j=0; icMode!=MODE_Column ){ + utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| "); + } + z = azData[i]; + if( z==0 ) z = p->nullValue; + w = p->actualWidth[j]; + if( p->colWidth[j]<0 ) w = -w; + utf8_width_print(p->out, w, z); + if( j==nColumn-1 ){ + utf8_printf(p->out, "%s", rowSep); + j = -1; + if( seenInterrupt ) goto columnar_end; + }else{ + utf8_printf(p->out, "%s", colSep); + } + } + if( p->cMode==MODE_Table ){ + print_row_separator(p, nColumn, "+"); + }else if( p->cMode==MODE_Box ){ + print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14); + } +columnar_end: + if( seenInterrupt ){ + utf8_printf(p->out, "Interrupt\n"); + } + nData = (nRow+1)*nColumn; + for(i=0; icMode==MODE_Column + || pArg->cMode==MODE_Table + || pArg->cMode==MODE_Box + || pArg->cMode==MODE_Markdown + ){ + exec_prepared_stmt_columnar(pArg, pStmt); + return; + } + /* perform the first step. this will tell us if we ** have a result set or not and how wide it is. */ @@ -2891,6 +3275,9 @@ static void exec_prepared_stmt( } } while( SQLITE_ROW == rc ); sqlite3_free(pData); + if( pArg->cMode==MODE_Json ){ + fputs("]\n", pArg->out); + } } } } @@ -3103,6 +3490,7 @@ static int shell_exec( const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); int iEqpId = sqlite3_column_int(pExplain, 0); int iParentId = sqlite3_column_int(pExplain, 1); + if( zEQPLine==0 ) zEQPLine = ""; if( zEQPLine[0]=='-' ) eqp_render(pArg); eqp_append(pArg, iEqpId, iParentId, zEQPLine); } @@ -3340,7 +3728,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ if( strcmp(zTable, "sqlite_sequence")==0 ){ raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ - raw_printf(p->out, "ANALYZE sqlite_master;\n"); + raw_printf(p->out, "ANALYZE sqlite_schema;\n"); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ @@ -3350,7 +3738,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ p->writableSchema = 1; } zIns = sqlite3_mprintf( - "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" + "INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); utf8_printf(p->out, "%s\n", zIns); @@ -3513,11 +3901,12 @@ static const char *(azHelp[]) = { ".databases List names and files of attached databases", ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", ".dbinfo ?DB? Show status information about the database", - ".dump ?TABLE? ... Render all database content as SQL", + ".dump ?TABLE? Render database content as SQL", " Options:", " --preserve-rowids Include ROWID values in the output", " --newlines Allow unescaped newline characters in output", " TABLE is a LIKE pattern for the tables to dump", + " Additional LIKE patterns can be given in subsequent arguments", ".echo on|off Turn command echo on or off", ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", " Other Modes:", @@ -3527,15 +3916,29 @@ static const char *(azHelp[]) = { #endif " trigger Like \"full\" but also show trigger bytecode", ".excel Display the output of next command in spreadsheet", + " --bom Put a UTF8 byte-order mark on intermediate file", ".exit ?CODE? Exit this program with return-code CODE", ".expert EXPERIMENTAL. Suggest indexes for queries", ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", ".filectrl CMD ... Run various sqlite3_file_control() operations", - " Run \".filectrl\" with no arguments for details", + " --schema SCHEMA Use SCHEMA instead of \"main\"", + " --help Show CMD details", ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", ".headers on|off Turn display of headers on or off", ".help ?-all? ?PATTERN? Show help text for PATTERN", ".import FILE TABLE Import data from FILE into TABLE", + " Options:", + " --ascii Use \\037 and \\036 as column and row separators", + " --csv Use , and \\n as column and row separators", + " --skip N Skip the first N rows of input", + " -v \"Verbose\" - increase auxiliary output", + " Notes:", + " * If TABLE does not exist, it is created. The first row of input", + " determines the column names.", + " * If neither --csv or --ascii are used, the input mode is derived", + " from the \".mode\" output mode", + " * If FILE begins with \"|\" then it is a command that generates the", + " input text.", #ifndef SQLITE_OMIT_TEST_CONTROL ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif @@ -3555,22 +3958,29 @@ static const char *(azHelp[]) = { ".log FILE|off Turn logging on or off. FILE can be stderr/stdout", ".mode MODE ?TABLE? Set output mode", " MODE is one of:", - " ascii Columns/rows delimited by 0x1F and 0x1E", - " csv Comma-separated values", - " column Left-aligned columns. (See .width)", - " html HTML
    code", - " insert SQL insert statements for TABLE", - " line One value per line", - " list Values delimited by \"|\"", - " quote Escape answers as for SQL", - " tabs Tab-separated values", - " tcl TCL list elements", + " ascii Columns/rows delimited by 0x1F and 0x1E", + " box Tables using unicode box-drawing characters", + " csv Comma-separated values", + " column Output in columns. (See .width)", + " html HTML
    code", + " insert SQL insert statements for TABLE", + " json Results in a JSON array", + " line One value per line", + " list Values delimited by \"|\"", + " markdown Markdown table format", + " quote Escape answers as for SQL", + " table ASCII-art table", + " tabs Tab-separated values", + " tcl TCL list elements", ".nullvalue STRING Use STRING in place of NULL values", - ".once (-e|-x|FILE) Output for the next SQL command only to FILE", + ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", - " Other options:", - " -e Invoke system text editor", - " -x Open in a spreadsheet", + " --bom Put a UTF8 byte-order mark at the beginning", + " -e Send output to the system text editor", + " -x Send output as CSV to a spreadsheet (same as \".excel\")", +#ifdef SQLITE_DEBUG + ".oom ?--repeat M? ?N? Simulate an OOM error on the N-th allocation", +#endif ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", @@ -3584,7 +3994,11 @@ static const char *(azHelp[]) = { " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", ".output ?FILE? Send output to FILE or stdout if FILE is omitted", - " If FILE begins with '|' then open it as a pipe.", + " If FILE begins with '|' then open it as a pipe.", + " Options:", + " --bom Prefix output with a UTF8 byte-order mark", + " -e Send output to the system text editor", + " -x Send output as CSV to a spreadsheet", ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", " init Initialize the TEMP table that holds bindings", @@ -3639,7 +4053,7 @@ static const char *(azHelp[]) = { #endif ".sha3sum ... Compute a SHA3 hash of database content", " Options:", - " --schema Also hash the sqlite_master table", + " --schema Also hash the sqlite_schema table", " --sha3-224 Use the sha3-224 algorithm", " --sha3-256 Use the sha3-256 algorithm (default)", " --sha3-384 Use the sha3-384 algorithm", @@ -3682,7 +4096,7 @@ static const char *(azHelp[]) = { ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", - ".width NUM1 NUM2 ... Set column widths for \"column\" mode", + ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", }; @@ -3704,6 +4118,7 @@ static int showHelp(FILE *out, const char *zPattern){ || zPattern[0]=='0' || strcmp(zPattern,"-a")==0 || strcmp(zPattern,"-all")==0 + || strcmp(zPattern,"--all")==0 ){ /* Show all commands, but only one line per command */ if( zPattern==0 ) zPattern = ""; @@ -4185,6 +4600,9 @@ static void open_db(ShellState *p, int openFlags){ sqlite3_fileio_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); sqlite3_completion_init(p->db, 0, 0); + sqlite3_uint_init(p->db, 0, 0); + sqlite3_decimal_init(p->db, 0, 0); + sqlite3_ieee_init(p->db, 0, 0); #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) sqlite3_dbdata_init(p->db, 0, 0); #endif @@ -4517,16 +4935,29 @@ typedef struct ImportCtx ImportCtx; struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ + int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ + int nRow; /* Number of rows imported */ + int nErr; /* Number of errors encountered */ int bNotFirst; /* True if one or more bytes already read */ int cTerm; /* Character that terminated the most recent field */ int cColSep; /* The column separator character. (Usually ",") */ int cRowSep; /* The row separator character. (Usually "\n") */ }; +/* Clean up resourced used by an ImportCtx */ +static void import_cleanup(ImportCtx *p){ + if( p->in!=0 && p->xCloser!=0 ){ + p->xCloser(p->in); + p->in = 0; + } + sqlite3_free(p->z); + p->z = 0; +} + /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ @@ -4775,7 +5206,7 @@ end_data_xfer: ** Try to transfer all rows of the schema that match zWhere. For ** each row, invoke xForEach() on the object defined by that row. ** If an error is encountered while moving forward through the -** sqlite_master table, try again moving backwards. +** sqlite_schema table, try again moving backwards. */ static void tryToCloneSchema( ShellState *p, @@ -4790,7 +5221,7 @@ static void tryToCloneSchema( const unsigned char *zSql; char *zErrMsg = 0; - zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" + zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" " WHERE %s", zWhere); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ @@ -4817,7 +5248,7 @@ static void tryToCloneSchema( if( rc!=SQLITE_DONE ){ sqlite3_finalize(pQuery); sqlite3_free(zQuery); - zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" + zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" " WHERE %s ORDER BY rowid DESC", zWhere); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ @@ -4902,11 +5333,15 @@ static void output_reset(ShellState *p){ zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); if( system(zCmd) ){ utf8_printf(stderr, "Failed: [%s]\n", zCmd); + }else{ + /* Give the start/open/xdg-open command some time to get + ** going before we continue, and potential delete the + ** p->zTempFile data file out from under it */ + sqlite3_sleep(2000); } sqlite3_free(zCmd); outputModePop(p); p->doXdgOpen = 0; - sqlite3_sleep(100); } #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */ } @@ -4982,12 +5417,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", -1, &pStmt, 0); if( rc ){ - if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){ - utf8_printf(stderr, "the \".dbinfo\" command requires the " - "-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n"); - }else{ - utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); - } + utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); return 1; } @@ -5022,11 +5452,11 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ raw_printf(p->out, "\n"); } if( zDb==0 ){ - zSchemaTab = sqlite3_mprintf("main.sqlite_master"); + zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); }else if( strcmp(zDb,"temp")==0 ){ - zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_master"); + zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema"); }else{ - zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_master", zDb); + zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); } for(i=0; idb, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); } if( p->zTempFile==0 ){ + /* If p->db is an in-memory database then the TEMPFILENAME file-control + ** will not work and we will need to fallback to guessing */ + char *zTemp; sqlite3_uint64 r; sqlite3_randomness(sizeof(r), &r); - p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix); + zTemp = getenv("TEMP"); + if( zTemp==0 ) zTemp = getenv("TMP"); + if( zTemp==0 ){ +#ifdef _WIN32 + zTemp = "\\tmp"; +#else + zTemp = "/tmp"; +#endif + } + p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix); }else{ p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); } @@ -5336,7 +5778,7 @@ static int lintFkeyIndexes( " || ');'" ", " " f.[table] " - "FROM sqlite_master AS s, pragma_foreign_key_list(s.name) AS f " + "FROM sqlite_schema AS s, pragma_foreign_key_list(s.name) AS f " "LEFT JOIN pragma_table_info AS p ON (pk-1=seq AND p.arg=f.[table]) " "GROUP BY s.name, f.id " "ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)" @@ -6411,7 +6853,7 @@ static RecoverTable *recoverNewTable( shellPreparePrintf(dbtmp, &rc, &pStmt, "SELECT (" " SELECT substr(data,1,1)==X'0D' FROM sqlite_dbpage WHERE pgno=rootpage" - ") FROM sqlite_master WHERE name = %Q", zName + ") FROM sqlite_schema WHERE name = %Q", zName ); if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ bSqlIntkey = sqlite3_column_int(pStmt, 0); @@ -6483,7 +6925,7 @@ static RecoverTable *recoverNewTable( /* ** This function is called to search the schema recovered from the -** sqlite_master table of the (possibly) corrupt database as part +** sqlite_schema table of the (possibly) corrupt database as part ** of a ".recover" command. Specifically, for a table with root page ** iRoot and at least nCol columns. Additionally, if bIntkey is 0, the ** table must be a WITHOUT ROWID table, or if non-zero, not one of @@ -6746,7 +7188,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ ");" /* Extract data from page 1 and any linked pages into table - ** recovery.schema. With the same schema as an sqlite_master table. */ + ** recovery.schema. With the same schema as an sqlite_schema table. */ "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" "INSERT INTO recovery.schema SELECT " " max(CASE WHEN field=0 THEN value ELSE NULL END)," @@ -6898,7 +7340,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ if( sqlite3_strnicmp(zSql, "create virt", 11)==0 ){ const char *zName = (const char*)sqlite3_column_text(pStmt, 1); char *zPrint = shellMPrintf(&rc, - "INSERT INTO sqlite_master VALUES('table', %Q, %Q, 0, %Q)", + "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", zName, zName, zSql ); raw_printf(pState->out, "%s;\n", zPrint); @@ -7222,7 +7664,8 @@ static int do_meta_command(char *zLine, ShellState *p){ #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ - const char *zLike = 0; + char *zLike = 0; + char *zSql; int i; int savedShowHeader = p->showHeader; int savedShellFlags = p->shellFlgs; @@ -7236,6 +7679,7 @@ static int do_meta_command(char *zLine, ShellState *p){ raw_printf(stderr, "The --preserve-rowids option is not compatible" " with SQLITE_OMIT_VIRTUALTABLE\n"); rc = 1; + sqlite3_free(zLike); goto meta_command_exit; #else ShellSetFlag(p, SHFLG_PreserveRowid); @@ -7247,15 +7691,14 @@ static int do_meta_command(char *zLine, ShellState *p){ { raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); rc = 1; + sqlite3_free(zLike); goto meta_command_exit; } }else if( zLike ){ - raw_printf(stderr, "Usage: .dump ?--preserve-rowids? " - "?--newlines? ?LIKE-PATTERN?\n"); - rc = 1; - goto meta_command_exit; + zLike = sqlite3_mprintf("%z OR name LIKE %Q ESCAPE '\\'", + zLike, azArg[i]); }else{ - zLike = azArg[i]; + zLike = sqlite3_mprintf("name LIKE %Q ESCAPE '\\'", azArg[i]); } } @@ -7269,39 +7712,29 @@ static int do_meta_command(char *zLine, ShellState *p){ p->writableSchema = 0; p->showHeader = 0; /* Set writable_schema=ON since doing so forces SQLite to initialize - ** as much of the schema as it can even if the sqlite_master table is + ** as much of the schema as it can even if the sqlite_schema table is ** corrupt. */ sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); p->nErr = 0; - if( zLike==0 ){ - run_schema_dump_query(p, - "SELECT name, type, sql FROM sqlite_master " - "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" - ); - run_schema_dump_query(p, - "SELECT name, type, sql FROM sqlite_master " - "WHERE name=='sqlite_sequence'" - ); - run_table_dump_query(p, - "SELECT sql FROM sqlite_master " - "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 - ); - }else{ - char *zSql; - zSql = sqlite3_mprintf( - "SELECT name, type, sql FROM sqlite_master " - "WHERE tbl_name LIKE %Q AND type=='table'" - " AND sql NOT NULL", zLike); - run_schema_dump_query(p,zSql); - sqlite3_free(zSql); - zSql = sqlite3_mprintf( - "SELECT sql FROM sqlite_master " - "WHERE sql NOT NULL" - " AND type IN ('index','trigger','view')" - " AND tbl_name LIKE %Q", zLike); - run_table_dump_query(p, zSql, 0); - sqlite3_free(zSql); - } + if( zLike==0 ) zLike = sqlite3_mprintf("true"); + zSql = sqlite3_mprintf( + "SELECT name, type, sql FROM sqlite_schema " + "WHERE (%s) AND type=='table'" + " AND sql NOT NULL" + " ORDER BY tbl_name='sqlite_sequence', rowid", + zLike + ); + run_schema_dump_query(p,zSql); + sqlite3_free(zSql); + zSql = sqlite3_mprintf( + "SELECT sql FROM sqlite_schema " + "WHERE (%s) AND sql NOT NULL" + " AND type IN ('index','trigger','view')", + zLike + ); + run_table_dump_query(p, zSql); + sqlite3_free(zSql); + sqlite3_free(zLike); if( p->writableSchema ){ raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; @@ -7341,7 +7774,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->autoEQP = AUTOEQP_full; p->autoEQPtrace = 1; open_db(p, 0); - sqlite3_exec(p->db, "SELECT name FROM sqlite_master LIMIT 1", 0, 0, 0); + sqlite3_exec(p->db, "SELECT name FROM sqlite_schema LIMIT 1", 0, 0, 0); sqlite3_exec(p->db, "PRAGMA vdbe_trace=ON;", 0, 0, 0); #endif }else{ @@ -7404,6 +7837,7 @@ static int do_meta_command(char *zLine, ShellState *p){ { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" }, { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" }, + { "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" }, }; int filectrl = -1; int iCtrl = -1; @@ -7411,10 +7845,21 @@ static int do_meta_command(char *zLine, ShellState *p){ int isOk = 0; /* 0: usage 1: %lld 2: no-result */ int n2, i; const char *zCmd = 0; + const char *zSchema = 0; open_db(p, 0); zCmd = nArg>=2 ? azArg[1] : "help"; + if( zCmd[0]=='-' + && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0) + && nArg>=4 + ){ + zSchema = azArg[2]; + for(i=3; idb, 0, SQLITE_FCNTL_SIZE_LIMIT, &iRes); + sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes); isOk = 1; break; } @@ -7465,7 +7910,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int x; if( nArg!=3 ) break; x = (int)integerValue(azArg[2]); - sqlite3_file_control(p->db, 0, filectrl, &x); + sqlite3_file_control(p->db, zSchema, filectrl, &x); isOk = 2; break; } @@ -7474,7 +7919,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int x; if( nArg!=2 && nArg!=3 ) break; x = nArg==3 ? booleanValue(azArg[2]) : -1; - sqlite3_file_control(p->db, 0, filectrl, &x); + sqlite3_file_control(p->db, zSchema, filectrl, &x); iRes = x; isOk = 1; break; @@ -7482,7 +7927,7 @@ static int do_meta_command(char *zLine, ShellState *p){ case SQLITE_FCNTL_HAS_MOVED: { int x; if( nArg!=2 ) break; - sqlite3_file_control(p->db, 0, filectrl, &x); + sqlite3_file_control(p->db, zSchema, filectrl, &x); iRes = x; isOk = 1; break; @@ -7490,7 +7935,7 @@ static int do_meta_command(char *zLine, ShellState *p){ case SQLITE_FCNTL_TEMPFILENAME: { char *z = 0; if( nArg!=2 ) break; - sqlite3_file_control(p->db, 0, filectrl, &z); + sqlite3_file_control(p->db, zSchema, filectrl, &z); if( z ){ utf8_printf(p->out, "%s\n", z); sqlite3_free(z); @@ -7498,6 +7943,18 @@ static int do_meta_command(char *zLine, ShellState *p){ isOk = 2; break; } + case SQLITE_FCNTL_RESERVE_BYTES: { + int x; + if( nArg>=3 ){ + x = atoi(azArg[2]); + sqlite3_file_control(p->db, zSchema, filectrl, &x); + } + x = -1; + sqlite3_file_control(p->db, zSchema, filectrl, &x); + utf8_printf(p->out,"%d\n", x); + isOk = 2; + break; + } } } if( isOk==0 && iCtrl>=0 ){ @@ -7530,8 +7987,8 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_exec(p->db, "SELECT sql FROM" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" - " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " + " FROM sqlite_schema UNION ALL" + " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " "ORDER BY rowid", callback, &data, &zErrMsg @@ -7539,7 +7996,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc==SQLITE_OK ){ sqlite3_stmt *pStmt; rc = sqlite3_prepare_v2(p->db, - "SELECT rowid FROM sqlite_master" + "SELECT rowid FROM sqlite_schema" " WHERE name GLOB 'sqlite_stat[134]'", -1, &pStmt, 0); doStats = sqlite3_step(pStmt)==SQLITE_ROW; @@ -7548,21 +8005,22 @@ static int do_meta_command(char *zLine, ShellState *p){ if( doStats==0 ){ raw_printf(p->out, "/* No STAT tables available */\n"); }else{ - raw_printf(p->out, "ANALYZE sqlite_master;\n"); - sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", + raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_schema'", callback, &data, &zErrMsg); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg); - raw_printf(p->out, "ANALYZE sqlite_master;\n"); + raw_printf(p->out, "ANALYZE sqlite_schema;\n"); } }else if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ if( nArg==2 ){ p->showHeader = booleanValue(azArg[1]); + p->shellFlgs |= SHFLG_HeaderSet; }else{ raw_printf(stderr, "Usage: .headers on|off\n"); rc = 1; @@ -7581,8 +8039,8 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='i' && strncmp(azArg[0], "import", n)==0 ){ - char *zTable; /* Insert data into this table */ - char *zFile; /* Name of file to extra content from */ + char *zTable = 0; /* Insert data into this table */ + char *zFile = 0; /* Name of file to extra content from */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ @@ -7592,75 +8050,139 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zSql; /* An SQL statement */ ImportCtx sCtx; /* Reader context */ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ - int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close file */ + int eVerbose = 0; /* Larger for more console output */ + int nSkip = 0; /* Initial lines to skip */ + int useOutputMode = 1; /* Use output mode to determine separators */ - if( nArg!=3 ){ - raw_printf(stderr, "Usage: .import FILE TABLE\n"); + memset(&sCtx, 0, sizeof(sCtx)); + if( p->mode==MODE_Ascii ){ + xRead = ascii_read_one_field; + }else{ + xRead = csv_read_one_field; + } + for(i=1; iout, "ERROR: extra argument: \"%s\". Usage:\n", z); + showHelp(p->out, "import"); + rc = 1; + goto meta_command_exit; + } + }else if( strcmp(z,"-v")==0 ){ + eVerbose++; + }else if( strcmp(z,"-skip")==0 && iout, "ERROR: unknown option: \"%s\". Usage:\n", z); + showHelp(p->out, "import"); + rc = 1; + goto meta_command_exit; + } + } + if( zTable==0 ){ + utf8_printf(p->out, "ERROR: missing %s argument. Usage:\n", + zFile==0 ? "FILE" : "TABLE"); + showHelp(p->out, "import"); + rc = 1; goto meta_command_exit; } - zFile = azArg[1]; - zTable = azArg[2]; seenInterrupt = 0; - memset(&sCtx, 0, sizeof(sCtx)); open_db(p, 0); - nSep = strlen30(p->colSeparator); - if( nSep==0 ){ - raw_printf(stderr, - "Error: non-null column separator required for import\n"); - return 1; - } - if( nSep>1 ){ - raw_printf(stderr, "Error: multi-character column separators not allowed" - " for import\n"); - return 1; - } - nSep = strlen30(p->rowSeparator); - if( nSep==0 ){ - raw_printf(stderr, "Error: non-null row separator required for import\n"); - return 1; - } - if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ - /* When importing CSV (only), if the row separator is set to the - ** default output row separator, change it to the default input - ** row separator. This avoids having to maintain different input - ** and output row separators. */ - sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); + if( useOutputMode ){ + /* If neither the --csv or --ascii options are specified, then set + ** the column and row separator characters from the output mode. */ + nSep = strlen30(p->colSeparator); + if( nSep==0 ){ + raw_printf(stderr, + "Error: non-null column separator required for import\n"); + rc = 1; + goto meta_command_exit; + } + if( nSep>1 ){ + raw_printf(stderr, + "Error: multi-character column separators not allowed" + " for import\n"); + rc = 1; + goto meta_command_exit; + } nSep = strlen30(p->rowSeparator); - } - if( nSep>1 ){ - raw_printf(stderr, "Error: multi-character row separators not allowed" - " for import\n"); - return 1; + if( nSep==0 ){ + raw_printf(stderr, + "Error: non-null row separator required for import\n"); + rc = 1; + goto meta_command_exit; + } + if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator,SEP_CrLf)==0 ){ + /* When importing CSV (only), if the row separator is set to the + ** default output row separator, change it to the default input + ** row separator. This avoids having to maintain different input + ** and output row separators. */ + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); + nSep = strlen30(p->rowSeparator); + } + if( nSep>1 ){ + raw_printf(stderr, "Error: multi-character row separators not allowed" + " for import\n"); + rc = 1; + goto meta_command_exit; + } + sCtx.cColSep = p->colSeparator[0]; + sCtx.cRowSep = p->rowSeparator[0]; } sCtx.zFile = zFile; sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); - return 1; + rc = 1; + goto meta_command_exit; #else sCtx.in = popen(sCtx.zFile+1, "r"); sCtx.zFile = ""; - xCloser = pclose; + sCtx.xCloser = pclose; #endif }else{ sCtx.in = fopen(sCtx.zFile, "rb"); - xCloser = fclose; - } - if( p->mode==MODE_Ascii ){ - xRead = ascii_read_one_field; - }else{ - xRead = csv_read_one_field; + sCtx.xCloser = fclose; } if( sCtx.in==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); - return 1; + rc = 1; + goto meta_command_exit; + } + if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ + char zSep[2]; + zSep[1] = 0; + zSep[0] = sCtx.cColSep; + utf8_printf(p->out, "Column separator "); + output_c_string(p->out, zSep); + utf8_printf(p->out, ", row separator "); + zSep[0] = sCtx.cRowSep; + output_c_string(p->out, zSep); + utf8_printf(p->out, "\n"); + } + while( (nSkip--)>0 ){ + while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} } - sCtx.cColSep = p->colSeparator[0]; - sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ - xCloser(sCtx.in); + import_cleanup(&sCtx); shell_out_of_memory(); } nByte = strlen30(zSql); @@ -7676,20 +8198,23 @@ static int do_meta_command(char *zLine, ShellState *p){ } if( cSep=='(' ){ sqlite3_free(zCreate); - sqlite3_free(sCtx.z); - xCloser(sCtx.in); + import_cleanup(&sCtx); utf8_printf(stderr,"%s: empty file\n", sCtx.zFile); - return 1; + rc = 1; + goto meta_command_exit; } zCreate = sqlite3_mprintf("%z\n)", zCreate); + if( eVerbose>=1 ){ + utf8_printf(p->out, "%s\n", zCreate); + } rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc ){ utf8_printf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(p->db)); - sqlite3_free(sCtx.z); - xCloser(sCtx.in); - return 1; + import_cleanup(&sCtx); + rc = 1; + goto meta_command_exit; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); } @@ -7697,8 +8222,9 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc ){ if (pStmt) sqlite3_finalize(pStmt); utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db)); - xCloser(sCtx.in); - return 1; + import_cleanup(&sCtx); + rc = 1; + goto meta_command_exit; } nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); @@ -7706,7 +8232,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCol==0 ) return 0; /* no columns, no error */ zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ - xCloser(sCtx.in); + import_cleanup(&sCtx); shell_out_of_memory(); } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); @@ -7717,13 +8243,17 @@ static int do_meta_command(char *zLine, ShellState *p){ } zSql[j++] = ')'; zSql[j] = 0; + if( eVerbose>=2 ){ + utf8_printf(p->out, "Insert using: %s\n", zSql); + } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); if (pStmt) sqlite3_finalize(pStmt); - xCloser(sCtx.in); - return 1; + import_cleanup(&sCtx); + rc = 1; + goto meta_command_exit; } needCommit = sqlite3_get_autocommit(p->db); if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); @@ -7766,14 +8296,21 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc!=SQLITE_OK ){ utf8_printf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(p->db)); + sCtx.nErr++; + }else{ + sCtx.nRow++; } } }while( sCtx.cTerm!=EOF ); - xCloser(sCtx.in); - sqlite3_free(sCtx.z); + import_cleanup(&sCtx); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); + if( eVerbose>0 ){ + utf8_printf(p->out, + "Added %d rows with %d errors using %d lines of input\n", + sCtx.nRow, sCtx.nErr, sCtx.nLine-1); + } }else #ifndef SQLITE_UNTESTABLE @@ -7804,10 +8341,10 @@ static int do_meta_command(char *zLine, ShellState *p){ goto meta_command_exit; } zSql = sqlite3_mprintf( - "SELECT rootpage, 0 FROM sqlite_master" + "SELECT rootpage, 0 FROM sqlite_schema" " WHERE name='%q' AND type='index'" "UNION ALL " - "SELECT rootpage, 1 FROM sqlite_master" + "SELECT rootpage, 1 FROM sqlite_schema" " WHERE name='%q' AND type='table'" " AND sql LIKE '%%without%%rowid%%'", azArg[1], azArg[1] @@ -8005,6 +8542,9 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ p->mode = MODE_Column; + if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){ + p->showHeader = 1; + } sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ p->mode = MODE_List; @@ -8028,15 +8568,26 @@ static int do_meta_command(char *zLine, ShellState *p){ set_table_name(p, nArg>=3 ? azArg[2] : "table"); }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){ p->mode = MODE_Quote; + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ p->mode = MODE_Ascii; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); + }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){ + p->mode = MODE_Markdown; + }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){ + p->mode = MODE_Table; + }else if( c2=='b' && strncmp(azArg[1],"box",n2)==0 ){ + p->mode = MODE_Box; + }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){ + p->mode = MODE_Json; }else if( nArg==1 ){ raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); }else{ raw_printf(stderr, "Error: mode should be one of: " - "ascii column csv html insert line list quote tabs tcl\n"); + "ascii box column csv html insert json line list markdown " + "quote table tabs tcl\n"); rc = 1; } p->cMode = p->mode; @@ -8052,6 +8603,34 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else +#ifdef SQLITE_DEBUG + if( c=='o' && strcmp(azArg[0],"oom")==0 ){ + int i; + for(i=1; iout, "missing argument on \"%s\"\n", azArg[i]); + rc = 1; + }else{ + oomRepeat = (int)integerValue(azArg[++i]); + } + }else if( IsDigit(z[0]) ){ + oomCounter = (int)integerValue(azArg[i]); + }else{ + raw_printf(p->out, "unknown argument: \"%s\"\n", azArg[i]); + raw_printf(p->out, "Usage: .oom [--repeat N] [M]\n"); + rc = 1; + } + } + if( rc==0 ){ + raw_printf(p->out, "oomCounter = %d\n", oomCounter); + raw_printf(p->out, "oomRepeat = %d\n", oomRepeat); + } + }else +#endif /* SQLITE_DEBUG */ + if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){ char *zNewFilename; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ @@ -8119,42 +8698,66 @@ static int do_meta_command(char *zLine, ShellState *p){ && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) ){ - const char *zFile = nArg>=2 ? azArg[1] : "stdout"; + const char *zFile = 0; int bTxtMode = 0; - if( azArg[0][0]=='e' ){ - /* Transform the ".excel" command into ".once -x" */ - nArg = 2; - azArg[0] = "once"; - zFile = azArg[1] = "-x"; - n = 4; + int i; + int eMode = 0; + int bBOM = 0; + int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ + + if( c=='e' ){ + eMode = 'x'; + bOnce = 2; + }else if( strncmp(azArg[0],"once",n)==0 ){ + bOnce = 1; } - if( nArg>2 ){ - utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]); - rc = 1; - goto meta_command_exit; - } - if( n>1 && strncmp(azArg[0], "once", n)==0 ){ - if( nArg<2 ){ - raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n"); + for(i=1; iout, "ERROR: unknown option: \"%s\". Usage:\n", + azArg[i]); + showHelp(p->out, azArg[0]); + rc = 1; + goto meta_command_exit; + } + }else if( zFile==0 ){ + zFile = z; + }else{ + utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n", + azArg[i]); + showHelp(p->out, azArg[0]); rc = 1; goto meta_command_exit; } + } + if( zFile==0 ) zFile = "stdout"; + if( bOnce ){ p->outCount = 2; }else{ p->outCount = 0; } output_reset(p); - if( zFile[0]=='-' && zFile[1]=='-' ) zFile++; #ifndef SQLITE_NOHAVE_SYSTEM - if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){ + if( eMode=='e' || eMode=='x' ){ p->doXdgOpen = 1; outputModePush(p); - if( zFile[1]=='x' ){ + if( eMode=='x' ){ + /* spreadsheet mode. Output as CSV. */ newTempFile(p, "csv"); + ShellClearFlag(p, SHFLG_Echo); p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else{ + /* text editor mode */ newTempFile(p, "txt"); bTxtMode = 1; } @@ -8173,6 +8776,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->out = stdout; rc = 1; }else{ + if( bBOM ) fprintf(p->out,"\357\273\277"); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif @@ -8185,6 +8789,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->out = stdout; rc = 1; } else { + if( bBOM ) fprintf(p->out,"\357\273\277"); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } @@ -8370,8 +8975,9 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; goto meta_command_exit; } - p->in = fopen(azArg[1], "rb"); - if( p->in==0 ){ + if( notNormalFile(azArg[1]) + || (p->in = fopen(azArg[1], "rb"))==0 + ){ utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ @@ -8474,8 +9080,11 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( zName!=0 ){ - int isMaster = sqlite3_strlike(zName, "sqlite_master", '\\')==0; - if( isMaster || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 ){ + int isSchema = sqlite3_strlike(zName, "sqlite_master", '\\')==0 + || sqlite3_strlike(zName, "sqlite_schema", '\\')==0 + || sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0 + || sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0; + if( isSchema ){ char *new_argv[2], *new_colv[2]; new_argv[0] = sqlite3_mprintf( "CREATE TABLE %s (\n" @@ -8484,7 +9093,7 @@ static int do_meta_command(char *zLine, ShellState *p){ " tbl_name text,\n" " rootpage integer,\n" " sql text\n" - ")", isMaster ? "sqlite_master" : "sqlite_temp_master"); + ")", zName); new_argv[1] = 0; new_colv[0] = "sql"; new_colv[1] = 0; @@ -8522,7 +9131,7 @@ static int do_meta_command(char *zLine, ShellState *p){ appendText(&sSelect, zDb, '\''); appendText(&sSelect, " AS sname FROM ", 0); appendText(&sSelect, zDb, quoteChar(zDb)); - appendText(&sSelect, ".sqlite_master", 0); + appendText(&sSelect, ".sqlite_schema", 0); } sqlite3_finalize(pStmt); #ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS @@ -8574,7 +9183,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ - sqlite3SelectTrace = (int)integerValue(azArg[1]); + sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; }else #endif @@ -8966,12 +9575,12 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( bSchema ){ - zSql = "SELECT lower(name) FROM sqlite_master" + zSql = "SELECT lower(name) FROM sqlite_schema" " WHERE type='table' AND coalesce(rootpage,0)>1" - " UNION ALL SELECT 'sqlite_master'" + " UNION ALL SELECT 'sqlite_schema'" " ORDER BY 1 collate nocase"; }else{ - zSql = "SELECT lower(name) FROM sqlite_master" + zSql = "SELECT lower(name) FROM sqlite_schema" " WHERE type='table' AND coalesce(rootpage,0)>1" " AND name NOT LIKE 'sqlite_%'" " ORDER BY 1 collate nocase"; @@ -8988,8 +9597,8 @@ static int do_meta_command(char *zLine, ShellState *p){ appendText(&sQuery,"SELECT * FROM ", 0); appendText(&sQuery,zTab,'"'); appendText(&sQuery," NOT INDEXED;", 0); - }else if( strcmp(zTab, "sqlite_master")==0 ){ - appendText(&sQuery,"SELECT type,name,tbl_name,sql FROM sqlite_master" + }else if( strcmp(zTab, "sqlite_schema")==0 ){ + appendText(&sQuery,"SELECT type,name,tbl_name,sql FROM sqlite_schema" " ORDER BY name;", 0); }else if( strcmp(zTab, "sqlite_sequence")==0 ){ appendText(&sQuery,"SELECT name,seq FROM sqlite_sequence" @@ -9083,7 +9692,7 @@ static int do_meta_command(char *zLine, ShellState *p){ raw_printf(p->out, "\n"); utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]); utf8_printf(p->out, "%12.12s: ", "width"); - for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { + for (i=0;inWidth;i++) { raw_printf(p->out, "%d ", p->colWidth[i]); } raw_printf(p->out, "\n"); @@ -9140,7 +9749,7 @@ static int do_meta_command(char *zLine, ShellState *p){ appendText(&s, "||'.'||name FROM ", 0); } appendText(&s, zDbName, '"'); - appendText(&s, ".sqlite_master ", 0); + appendText(&s, ".sqlite_schema ", 0); if( c=='t' ){ appendText(&s," WHERE type IN ('table','view')" " AND name NOT LIKE 'sqlite_%'" @@ -9248,7 +9857,6 @@ static int do_meta_command(char *zLine, ShellState *p){ { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, - { "reserve", SQLITE_TESTCTRL_RESERVE, "BYTES-OF-RESERVE"}, }; int testctrl = -1; int iCtrl = -1; @@ -9301,7 +9909,6 @@ static int do_meta_command(char *zLine, ShellState *p){ /* sqlite3_test_control(int, db, int) */ case SQLITE_TESTCTRL_OPTIMIZATIONS: - case SQLITE_TESTCTRL_RESERVE: if( nArg==3 ){ int opt = (int)strtol(azArg[2], 0, 0); rc2 = sqlite3_test_control(testctrl, p->db, opt); @@ -9644,7 +10251,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ int j; assert( nArg<=ArraySize(azArg) ); - for(j=1; jcolWidth); j++){ + p->nWidth = nArg-1; + p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2); + if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory(); + if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth]; + for(j=1; jcolWidth[j-1] = (int)integerValue(azArg[j]); } }else @@ -9989,6 +10600,7 @@ static const char zOptions[] = " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" + " -box set output mode to 'box'\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" @@ -10004,9 +10616,11 @@ static const char zOptions[] = " -help show this message\n" " -html set output mode to HTML\n" " -interactive force interactive I/O\n" + " -json set output mode to 'json'\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" + " -markdown set output mode to 'markdown'\n" #if defined(SQLITE_ENABLE_DESERIALIZE) " -maxsize N maximum size for a --deserialize database\n" #endif @@ -10026,6 +10640,7 @@ static const char zOptions[] = " -sorterref SIZE sorter references threshold size\n" #endif " -stats print memory stats before each finalize\n" + " -table set output mode to 'table'\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE @@ -10083,14 +10698,18 @@ static void main_init(ShellState *data) { */ #ifdef _WIN32 static void printBold(const char *zText){ +#if !SQLITE_OS_WINRT HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; GetConsoleScreenBufferInfo(out, &defaultScreenInfo); SetConsoleTextAttribute(out, FOREGROUND_RED|FOREGROUND_INTENSITY ); +#endif printf("%s", zText); +#if !SQLITE_OS_WINRT SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); +#endif } #else static void printBold(const char *zText){ @@ -10145,6 +10764,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); +#ifdef SQLITE_DEBUG + registerOomSimulator(); +#endif + #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ if( isatty(0) && isatty(2) ){ @@ -10154,7 +10777,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ fgetc(stdin); }else{ #if defined(_WIN32) || defined(WIN32) +#if SQLITE_OS_WINRT + __debugbreak(); +#else DebugBreak(); +#endif #elif defined(SIGTRAP) raise(SIGTRAP); #endif @@ -10415,6 +11042,14 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; + }else if( strcmp(z,"-json")==0 ){ + data.mode = MODE_Json; + }else if( strcmp(z,"-markdown")==0 ){ + data.mode = MODE_Markdown; + }else if( strcmp(z,"-table")==0 ){ + data.mode = MODE_Table; + }else if( strcmp(z,"-box")==0 ){ + data.mode = MODE_Box; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); @@ -10656,6 +11291,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ for(i=0; i[[SQLITE_FCNTL_LOCK_TIMEOUT]] -** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain -** a file lock using the xLock or xShmLock methods of the VFS to wait -** for up to M milliseconds before failing, where M is the single -** unsigned integer parameter. +** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS +** to block for up to M milliseconds before failing when attempting to +** obtain a file lock using the xLock or xShmLock methods of the VFS. +** The parameter is a pointer to a 32-bit signed integer that contains +** the value that M is to be set to. Before returning, the 32-bit signed +** integer is overwritten with the previous value of M. ** **
  • [[SQLITE_FCNTL_DATA_VERSION]] ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to @@ -1112,6 +1116,11 @@ struct sqlite3_io_methods { ** happen either internally or externally and that are associated with ** a particular attached database. ** +**
  • [[SQLITE_FCNTL_CKPT_START]] +** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint +** in wal mode before the client starts to copy pages from the wal +** file to the database file. +** **
  • [[SQLITE_FCNTL_CKPT_DONE]] ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint ** in wal mode after the client has finished copying pages from the wal @@ -1155,6 +1164,8 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_DATA_VERSION 35 #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 +#define SQLITE_FCNTL_RESERVE_BYTES 38 +#define SQLITE_FCNTL_CKPT_START 39 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -1259,7 +1270,7 @@ typedef struct sqlite3_api_routines sqlite3_api_routines; **
  • [SQLITE_OPEN_TEMP_JOURNAL] **
  • [SQLITE_OPEN_TRANSIENT_DB] **
  • [SQLITE_OPEN_SUBJOURNAL] -**
  • [SQLITE_OPEN_MASTER_JOURNAL] +**
  • [SQLITE_OPEN_SUPER_JOURNAL] **
  • [SQLITE_OPEN_WAL] ** )^ ** @@ -1637,7 +1648,7 @@ int sqlite3_db_config(sqlite3*, int op, ...); ** by xInit. The pAppData pointer is used as the only parameter to ** xInit and xShutdown. ** -** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes +** SQLite holds the [SQLITE_MUTEX_STATIC_MAIN] mutex when it invokes ** the xInit method, so the xInit method need not be threadsafe. The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. For all other methods, SQLite @@ -2275,8 +2286,7 @@ struct sqlite3_mem_methods { ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] **
    SQLITE_DBCONFIG_TRUSTED_SCHEMA **
    The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to -** assume that database schemas (the contents of the [sqlite_master] tables) -** are untainted by malicious content. +** assume that database schemas are untainted by malicious content. ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite ** takes additional defensive steps to protect the application from harm ** including: @@ -3533,8 +3543,19 @@ int sqlite3_open_v2( ** that check if a database file was a URI that contained a specific query ** parameter, and if so obtains the value of that query parameter. ** -** If F is the database filename pointer passed into the xOpen() method of -** a VFS implementation or it is the return value of [sqlite3_db_filename()] +** The first parameter to these interfaces (hereafter referred to +** as F) must be one of: +**
      +**
    • A database filename pointer created by the SQLite core and +** passed into the xOpen() method of a VFS implemention, or +**
    • A filename obtained from [sqlite3_db_filename()], or +**
    • A new filename constructed using [sqlite3_create_filename()]. +**
    +** If the F parameter is not one of the above, then the behavior is +** undefined and probably undesirable. Older versions of SQLite were +** more tolerant of invalid F parameters than newer versions. +** +** If F is a suitable filename (as described in the previous paragraph) ** and if P is the name of the query parameter, then ** sqlite3_uri_parameter(F,P) returns the value of the P ** parameter if it exists or a NULL pointer if P does not appear as a @@ -3617,6 +3638,78 @@ const char *sqlite3_filename_database(const char*); const char *sqlite3_filename_journal(const char*); const char *sqlite3_filename_wal(const char*); +/* +** CAPI3REF: Database File Corresponding To A Journal +** +** ^If X is the name of a rollback or WAL-mode journal file that is +** passed into the xOpen method of [sqlite3_vfs], then +** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file] +** object that represents the main database file. +** +** This routine is intended for use in custom [VFS] implementations +** only. It is not a general-purpose interface. +** The argument sqlite3_file_object(X) must be a filename pointer that +** has been passed into [sqlite3_vfs].xOpen method where the +** flags parameter to xOpen contains one of the bits +** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use +** of this routine results in undefined and probably undesirable +** behavior. +*/ +sqlite3_file *sqlite3_database_file_object(const char*); + +/* +** CAPI3REF: Create and Destroy VFS Filenames +** +** These interfces are provided for use by [VFS shim] implementations and +** are not useful outside of that context. +** +** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of +** database filename D with corresponding journal file J and WAL file W and +** with N URI parameters key/values pairs in the array P. The result from +** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that +** is safe to pass to routines like: +**
      +**
    • [sqlite3_uri_parameter()], +**
    • [sqlite3_uri_boolean()], +**
    • [sqlite3_uri_int64()], +**
    • [sqlite3_uri_key()], +**
    • [sqlite3_filename_database()], +**
    • [sqlite3_filename_journal()], or +**
    • [sqlite3_filename_wal()]. +**
    +** If a memory allocation error occurs, sqlite3_create_filename() might +** return a NULL pointer. The memory obtained from sqlite3_create_filename(X) +** must be released by a corresponding call to sqlite3_free_filename(Y). +** +** The P parameter in sqlite3_create_filename(D,J,W,N,P) should be an array +** of 2*N pointers to strings. Each pair of pointers in this array corresponds +** to a key and value for a query parameter. The P parameter may be a NULL +** pointer if N is zero. None of the 2*N pointers in the P array may be +** NULL pointers and key pointers should not be empty strings. +** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may +** be NULL pointers, though they can be empty strings. +** +** The sqlite3_free_filename(Y) routine releases a memory allocation +** previously obtained from sqlite3_create_filename(). Invoking +** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op. +** +** If the Y parameter to sqlite3_free_filename(Y) is anything other +** than a NULL pointer or a pointer previously acquired from +** sqlite3_create_filename(), then bad things such as heap +** corruption or segfaults may occur. The value Y should be +** used again after sqlite3_free_filename(Y) has been called. This means +** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y, +** then the corresponding [sqlite3_module.xClose() method should also be +** invoked prior to calling sqlite3_free_filename(Y). +*/ +char *sqlite3_create_filename( + const char *zDatabase, + const char *zJournal, + const char *zWal, + int nParam, + const char **azParam +); +void sqlite3_free_filename(char*); /* ** CAPI3REF: Error Codes And Messages @@ -4199,12 +4292,30 @@ typedef struct sqlite3_context sqlite3_context; ** [sqlite3_bind_parameter_index()] API if desired. ^The index ** for "?NNN" parameters is the value of NNN. ** ^The NNN value must be between 1 and the [sqlite3_limit()] -** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). +** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 32766). ** ** ^The third argument is the value to bind to the parameter. ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter ** is ignored and the end result is the same as sqlite3_bind_null(). +** ^If the third parameter to sqlite3_bind_text() is not NULL, then +** it should be a pointer to well-formed UTF8 text. +** ^If the third parameter to sqlite3_bind_text16() is not NULL, then +** it should be a pointer to well-formed UTF16 text. +** ^If the third parameter to sqlite3_bind_text64() is not NULL, then +** it should be a pointer to a well-formed unicode string that is +** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 +** otherwise. +** +** [[byte-order determination rules]] ^The byte-order of +** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) +** found in first character, which is removed, or in the absence of a BOM +** the byte order is the native byte order of the host +** machine for sqlite3_bind_text16() or the byte order specified in +** the 6th parameter for sqlite3_bind_text64().)^ +** ^If UTF16 input text contains invalid unicode +** characters, then SQLite might change those invalid characters +** into the unicode replacement character: U+FFFD. ** ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the @@ -4218,7 +4329,7 @@ typedef struct sqlite3_context sqlite3_context; ** or sqlite3_bind_text16() or sqlite3_bind_text64() then ** that parameter must be the byte offset ** where the NUL terminator would occur assuming the string were NUL -** terminated. If any NUL characters occur at byte offsets less than +** terminated. If any NUL characters occurs at byte offsets less than ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. @@ -5386,7 +5497,7 @@ void sqlite3_value_free(sqlite3_value*); ** ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is ** determined by the N parameter on first successful call. Changing the -** value of N in any subsequents call to sqlite3_aggregate_context() within +** value of N in any subsequent call to sqlite3_aggregate_context() within ** the same aggregate function instance will not resize the memory ** allocation.)^ Within the xFinal callback, it is customary to set ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no @@ -5543,8 +5654,9 @@ typedef void (*sqlite3_destructor_type)(void*); ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() ** as the text of an error message. ^SQLite interprets the error ** message string from sqlite3_result_error() as UTF-8. ^SQLite -** interprets the string from sqlite3_result_error16() as UTF-16 in native -** byte order. ^If the third parameter to sqlite3_result_error() +** interprets the string from sqlite3_result_error16() as UTF-16 using +** the same [byte-order determination rules] as [sqlite3_bind_text16()]. +** ^If the third parameter to sqlite3_result_error() ** or sqlite3_result_error16() is negative then SQLite takes as the error ** message all text up through the first zero character. ** ^If the third parameter to sqlite3_result_error() or @@ -5612,6 +5724,25 @@ typedef void (*sqlite3_destructor_type)(void*); ** then SQLite makes a copy of the result into space obtained ** from [sqlite3_malloc()] before it returns. ** +** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and +** sqlite3_result_text16be() routines, and for sqlite3_result_text64() +** when the encoding is not UTF8, if the input UTF16 begins with a +** byte-order mark (BOM, U+FEFF) then the BOM is removed from the +** string and the rest of the string is interpreted according to the +** byte-order specified by the BOM. ^The byte-order specified by +** the BOM at the beginning of the text overrides the byte-order +** specified by the interface procedure. ^So, for example, if +** sqlite3_result_text16le() is invoked with text that begins +** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the +** first two bytes of input are skipped and the remaining input +** is interpreted as UTF16BE text. +** +** ^For UTF16 input text to the sqlite3_result_text16(), +** sqlite3_result_text16be(), sqlite3_result_text16le(), and +** sqlite3_result_text64() routines, if the text contains invalid +** UTF16 characters, the invalid characters might be converted +** into the unicode replacement character, U+FFFD. +** ** ^The sqlite3_result_value() interface sets the result of ** the application-defined function to be a copy of the ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The @@ -5817,6 +5948,7 @@ int sqlite3_collation_needed16( void(*)(void*,sqlite3*,int eTextRep,const void*) ); +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC /* ** Specify the key for an encrypted database. This routine should be @@ -5843,22 +5975,6 @@ int sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ -/* BEGIN SQLCIPHER - SQLCipher usage note: - - If the current database is plaintext SQLCipher will NOT encrypt it. - If the current database is encrypted and pNew==0 or nNew==0, SQLCipher - will NOT decrypt it. - - This routine will ONLY work on an already encrypted database in order - to change the key. - - Conversion from plaintext-to-encrypted or encrypted-to-plaintext should - use an ATTACHed database and the sqlcipher_export() convenience function - as per the SQLCipher Documentation. - - END SQLCIPHER -*/ int sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ @@ -5877,6 +5993,7 @@ void sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif +/* BEGIN SQLCIPHER */ #ifdef SQLITE_ENABLE_CEROD /* @@ -6206,7 +6323,7 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ^In the case of an update, this is the [rowid] after the update takes place. ** ** ^(The update hook is not invoked when internal system tables are -** modified (i.e. sqlite_master and sqlite_sequence).)^ +** modified (i.e. sqlite_sequence).)^ ** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. ** ** ^In the current implementation, the update hook @@ -7308,7 +7425,7 @@ int sqlite3_vfs_unregister(sqlite3_vfs*); **
      **
    • SQLITE_MUTEX_FAST **
    • SQLITE_MUTEX_RECURSIVE -**
    • SQLITE_MUTEX_STATIC_MASTER +**
    • SQLITE_MUTEX_STATIC_MAIN **
    • SQLITE_MUTEX_STATIC_MEM **
    • SQLITE_MUTEX_STATIC_OPEN **
    • SQLITE_MUTEX_STATIC_PRNG @@ -7510,7 +7627,7 @@ int sqlite3_mutex_notheld(sqlite3_mutex*); */ #define SQLITE_MUTEX_FAST 0 #define SQLITE_MUTEX_RECURSIVE 1 -#define SQLITE_MUTEX_STATIC_MASTER 2 +#define SQLITE_MUTEX_STATIC_MAIN 2 #define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ #define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ @@ -7525,6 +7642,10 @@ int sqlite3_mutex_notheld(sqlite3_mutex*); #define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ #define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ +/* Legacy compatibility: */ +#define SQLITE_MUTEX_STATIC_MASTER 2 + + /* ** CAPI3REF: Retrieve the mutex for a database connection ** METHOD: sqlite3 @@ -7620,7 +7741,7 @@ int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 -#define SQLITE_TESTCTRL_RESERVE 14 +#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ @@ -9320,7 +9441,7 @@ int sqlite3_db_cacheflush(sqlite3*); ** ** ^The preupdate hook only fires for changes to real database tables; the ** preupdate hook is not invoked for changes to [virtual tables] or to -** system tables like sqlite_master or sqlite_stat1. +** system tables like sqlite_sequence or sqlite_stat1. ** ** ^The second parameter to the preupdate callback is a pointer to ** the [database connection] that registered the preupdate hook. diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index b5258e0..78c19a0 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -330,6 +330,11 @@ struct sqlite3_api_routines { const char *(*filename_database)(const char*); const char *(*filename_journal)(const char*); const char *(*filename_wal)(const char*); + /* Version 3.32.0 and later */ + char *(*create_filename)(const char*,const char*,const char*, + int,const char**); + void (*free_filename)(char*); + sqlite3_file *(*database_file_object)(const char*); }; /* @@ -620,16 +625,20 @@ typedef int (*sqlite3_loadext_entry)( /* Version 3.26.0 and later */ #define sqlite3_normalized_sql sqlite3_api->normalized_sql /* Version 3.28.0 and later */ -#define sqlite3_stmt_isexplain sqlite3_api->isexplain -#define sqlite3_value_frombind sqlite3_api->frombind +#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain +#define sqlite3_value_frombind sqlite3_api->value_frombind /* Version 3.30.0 and later */ #define sqlite3_drop_modules sqlite3_api->drop_modules -/* Version 3.31.0 andn later */ +/* Version 3.31.0 and later */ #define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64 #define sqlite3_uri_key sqlite3_api->uri_key #define sqlite3_filename_database sqlite3_api->filename_database #define sqlite3_filename_journal sqlite3_api->filename_journal #define sqlite3_filename_wal sqlite3_api->filename_wal +/* Version 3.32.0 and later */ +#define sqlite3_create_filename sqlite3_api->create_filename +#define sqlite3_free_filename sqlite3_api->free_filename +#define sqlite3_database_file_object sqlite3_api->database_file_object #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 53e7095..2d6edf8 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -128,6 +128,15 @@ # define _BSD_SOURCE #endif +/* +** Macro to disable warnings about missing "break" at the end of a "case". +*/ +#if GCC_VERSION>=7000000 +# define deliberate_fall_through __attribute__((fallthrough)); +#else +# define deliberate_fall_through +#endif + /* ** For MinGW, check to see if we can include the header file containing its ** version information, among other things. Normally, this internal MinGW @@ -186,6 +195,21 @@ #pragma warn -spa /* Suspicious pointer arithmetic */ #endif +/* +** WAL mode depends on atomic aligned 32-bit loads and stores in a few +** places. The following macros try to make this explicit. +*/ +#ifndef __has_extension +# define __has_extension(x) 0 /* compatibility with non-clang compilers */ +#endif +#if GCC_VERSION>=4007000 || __has_extension(c_atomic) +# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) +# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) +#else +# define AtomicLoad(PTR) (*(PTR)) +# define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) +#endif + /* ** Include standard header files as necessary */ @@ -887,6 +911,7 @@ typedef INT16_TYPE LogEst; ** compilers. */ #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) +#define LARGEST_UINT64 (0xffffffff|(((u64)0xffffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) /* @@ -964,6 +989,16 @@ typedef INT16_TYPE LogEst; #else # define SELECTTRACE_ENABLED 0 #endif +#if defined(SQLITE_ENABLE_SELECTTRACE) +# define SELECTTRACE_ENABLED 1 +# define SELECTTRACE(K,P,S,X) \ + if(sqlite3_unsupported_selecttrace&(K)) \ + sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ + sqlite3DebugPrintf X +#else +# define SELECTTRACE(K,P,S,X) +# define SELECTTRACE_ENABLED 0 +#endif /* ** An instance of the following structure is used to store the busy-handler @@ -979,26 +1014,27 @@ struct BusyHandler { int (*xBusyHandler)(void *,int); /* The busy callback */ void *pBusyArg; /* First arg to busy callback */ int nBusy; /* Incremented with each busy call */ - u8 bExtraFileArg; /* Include sqlite3_file as callback arg */ }; /* -** Name of the master database table. The master database table -** is a special table that holds the names and attributes of all -** user tables and indices. +** Name of table that holds the database schema. */ -#define MASTER_NAME "sqlite_master" -#define TEMP_MASTER_NAME "sqlite_temp_master" +#define DFLT_SCHEMA_TABLE "sqlite_master" +#define DFLT_TEMP_SCHEMA_TABLE "sqlite_temp_master" +#define ALT_SCHEMA_TABLE "sqlite_schema" +#define ALT_TEMP_SCHEMA_TABLE "sqlite_temp_schema" + /* -** The root-page of the master database table. +** The root-page of the schema table. */ -#define MASTER_ROOT 1 +#define SCHEMA_ROOT 1 /* -** The name of the schema table. +** The name of the schema table. The name is different for TEMP. */ -#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) +#define SCHEMA_TABLE(x) \ + ((!OMIT_TEMPDB)&&(x==1)?DFLT_TEMP_SCHEMA_TABLE:DFLT_SCHEMA_TABLE) /* ** A convenience macro that returns the number of elements in @@ -1019,7 +1055,7 @@ struct BusyHandler { ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. */ -#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) +#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault) /* ** When SQLITE_OMIT_WSD is defined, it means that the target platform does @@ -1159,9 +1195,9 @@ typedef int VList; ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ +#include "pager.h" #include "btree.h" #include "vdbe.h" -#include "pager.h" #include "pcache.h" #include "os.h" #include "mutex.h" @@ -1264,7 +1300,6 @@ struct Schema { */ #define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ #define DB_UnresetViews 0x0002 /* Some views have defined column names */ -#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ #define DB_ResetWanted 0x0008 /* Reset the schema when nSchemaLock==0 */ /* @@ -1422,7 +1457,7 @@ void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); struct sqlite3 { sqlite3_vfs *pVfs; /* OS Interface */ struct Vdbe *pVdbe; /* List of active virtual machines */ - CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ + CollSeq *pDfltColl; /* BINARY collseq for the database encoding */ sqlite3_mutex *mutex; /* Connection mutex */ Db *aDb; /* All backends */ int nDb; /* Number of backends currently in use */ @@ -1456,7 +1491,7 @@ struct sqlite3 { int aLimit[SQLITE_N_LIMIT]; /* Limits */ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ struct sqlite3InitInfo { /* Information used during initialization */ - int newTnum; /* Rootpage of table being initialized */ + Pgno newTnum; /* Rootpage of table being initialized */ u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ @@ -1471,7 +1506,10 @@ struct sqlite3 { int nVDestroy; /* Number of active OP_VDestroy operations */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ - int (*xTrace)(u32,void*,void*,void*); /* Trace function */ + union { + void (*xLegacy)(void*,const char*); /* Legacy trace function */ + int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ + } trace; void *pTraceArg; /* Argument to the trace function */ #ifndef SQLITE_OMIT_DEPRECATED void (*xProfile)(void*,const char*,u64); /* Profiling function */ @@ -1525,6 +1563,7 @@ struct sqlite3 { BusyHandler busyHandler; /* Busy callback */ Db aDbStatic[2]; /* Static space for the 2 default backends */ Savepoint *pSavepoint; /* List of active savepoints */ + int nAnalysisLimit; /* Number of index rows to ANALYZE */ int busyTimeout; /* Busy handler timeout, in msec */ int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ @@ -1532,7 +1571,7 @@ struct sqlite3 { i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - /* The following variables are all protected by the STATIC_MASTER + /* The following variables are all protected by the STATIC_MAIN ** mutex, not by sqlite3.mutex. They are used by code in notify.c. ** ** When X.pUnlockConnection==Y, that means that X is waiting for Y to @@ -1574,7 +1613,7 @@ struct sqlite3 { ** SQLITE_CkptFullFSync == PAGER_CKPT_FULLFSYNC ** SQLITE_CacheSpill == PAGER_CACHE_SPILL */ -#define SQLITE_WriteSchema 0x00000001 /* OK to update SQLITE_MASTER */ +#define SQLITE_WriteSchema 0x00000001 /* OK to update SQLITE_SCHEMA */ #define SQLITE_LegacyFileFmt 0x00000002 /* Create new databases in format 1 */ #define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */ #define SQLITE_FullFSync 0x00000008 /* Use full fsync on the backend */ @@ -1631,6 +1670,7 @@ struct sqlite3 { #define DBFLAG_VacuumInto 0x0008 /* Currently running VACUUM INTO */ #define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */ #define DBFLAG_InternalFunc 0x0020 /* Allow use of internal functions */ +#define DBFLAG_EncodingFixed 0x0040 /* No longer possible to change enc. */ /* ** Bits of the sqlite3.dbOptFlags field that are used by the @@ -1749,7 +1789,7 @@ struct FuncDestructor { #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ -#define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */ +/* 0x0200 -- available for reuse */ #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ #define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */ #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ @@ -1770,6 +1810,7 @@ struct FuncDestructor { #define INLINEFUNC_expr_implies_expr 2 #define INLINEFUNC_expr_compare 3 #define INLINEFUNC_affinity 4 +#define INLINEFUNC_iif 5 #define INLINEFUNC_unlikely 99 /* Default case */ /* @@ -1934,6 +1975,7 @@ struct Column { u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ char affinity; /* One of the SQLITE_AFF_... values */ u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */ + u8 hName; /* Column name hash for faster lookup */ u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ }; @@ -2091,7 +2133,7 @@ struct Table { char *zColAff; /* String defining the affinity of each column */ ExprList *pCheck; /* All CHECK constraints */ /* ... also used as column name list in a VIEW */ - int tnum; /* Root BTree page for this table */ + Pgno tnum; /* Root BTree page for this table */ u32 nTabRef; /* Number of pointers to this Table */ u32 tabFlags; /* Mask of TF_* values */ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ @@ -2153,8 +2195,11 @@ struct Table { */ #ifndef SQLITE_OMIT_VIRTUALTABLE # define IsVirtual(X) ((X)->nModuleArg) +# define ExprIsVtab(X) \ + ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg) #else # define IsVirtual(X) 0 +# define ExprIsVtab(X) 0 #endif /* @@ -2362,7 +2407,7 @@ struct UnpackedRecord { ** element. ** ** While parsing a CREATE TABLE or CREATE INDEX statement in order to -** generate VDBE code (as opposed to parsing one read from an sqlite_master +** generate VDBE code (as opposed to parsing one read from an sqlite_schema ** table as part of parsing an existing database schema), transient instances ** of this structure may be created. In this case the Index.tnum variable is ** used to store the address of a VDBE instruction, not a database page @@ -2381,7 +2426,7 @@ struct Index { const char **azColl; /* Array of collation sequence names for index */ Expr *pPartIdxWhere; /* WHERE clause for partial indices */ ExprList *aColExpr; /* Column expressions */ - int tnum; /* DB Page containing root of this index */ + Pgno tnum; /* DB Page containing root of this index */ LogEst szIdxRow; /* Estimated average row size in bytes */ u16 nKeyCol; /* Number of columns forming the key */ u16 nColumn; /* Number of columns stored in the index */ @@ -2466,7 +2511,7 @@ struct Token { ** code for a SELECT that contains aggregate functions. ** ** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a -** pointer to this structure. The Expr.iColumn field is the index in +** pointer to this structure. The Expr.iAgg field is the index in ** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate ** code for that node. ** @@ -2486,23 +2531,25 @@ struct AggInfo { ExprList *pGroupBy; /* The group by clause */ struct AggInfo_col { /* For each column used in source tables */ Table *pTab; /* Source table */ + Expr *pCExpr; /* The original expression */ int iTable; /* Cursor number of the source table */ - int iColumn; /* Column number within the source table */ - int iSorterColumn; /* Column number in the sorting index */ int iMem; /* Memory location that acts as accumulator */ - Expr *pExpr; /* The original expression */ + i16 iColumn; /* Column number within the source table */ + i16 iSorterColumn; /* Column number in the sorting index */ } *aCol; int nColumn; /* Number of used entries in aCol[] */ int nAccumulator; /* Number of columns that show through to the output. ** Additional columns are used only as parameters to ** aggregate functions */ struct AggInfo_func { /* For each aggregate function */ - Expr *pExpr; /* Expression encoding the function */ + Expr *pFExpr; /* Expression encoding the function */ FuncDef *pFunc; /* The aggregate function implementation */ int iMem; /* Memory location that acts as accumulator */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ + u32 selId; /* Select to which this AggInfo belongs */ + AggInfo *pNext; /* Next in list of them all */ }; /* @@ -2512,10 +2559,10 @@ struct AggInfo { ** it uses less memory in the Expr object, which is a big memory user ** in systems with lots of prepared statements. And few applications ** need more than about 10 or 20 variables. But some extreme users want -** to have prepared statements with over 32767 variables, and for them +** to have prepared statements with over 32766 variables, and for them ** the option is available (at compile-time). */ -#if SQLITE_MAX_VARIABLE_NUMBER<=32767 +#if SQLITE_MAX_VARIABLE_NUMBER<32767 typedef i16 ynVar; #else typedef int ynVar; @@ -2591,6 +2638,9 @@ struct Expr { ** TK_COLUMN: the value of p5 for OP_Column ** TK_AGG_FUNCTION: nesting depth ** TK_FUNCTION: NC_SelfRef flag if needs OP_PureFunc */ +#ifdef SQLITE_DEBUG + u8 vvaFlags; /* Verification flags. */ +#endif u32 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ @@ -2665,7 +2715,7 @@ struct Expr { #define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ -#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ + /* 0x020000 // available for reuse */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ #define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ @@ -2678,7 +2728,8 @@ struct Expr { #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ -#define EP_FromDDL 0x40000000 /* Originates from sqlite_master */ +#define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */ + /* 0x80000000 // Available */ /* ** The EP_Propagate mask is a set of properties that automatically propagate @@ -2697,14 +2748,24 @@ struct Expr { #define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue) #define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse) + +/* Flags for use with Expr.vvaFlags +*/ +#define EP_NoReduce 0x01 /* Cannot EXPRDUP_REDUCE this Expr */ +#define EP_Immutable 0x02 /* Do not change this Expr node */ + /* The ExprSetVVAProperty() macro is used for Verification, Validation, ** and Accreditation only. It works like ExprSetProperty() during VVA ** processes but is a no-op for delivery. */ #ifdef SQLITE_DEBUG -# define ExprSetVVAProperty(E,P) (E)->flags|=(P) +# define ExprSetVVAProperty(E,P) (E)->vvaFlags|=(P) +# define ExprHasVVAProperty(E,P) (((E)->vvaFlags&(P))!=0) +# define ExprClearVVAProperties(E) (E)->vvaFlags = 0 #else # define ExprSetVVAProperty(E,P) +# define ExprHasVVAProperty(E,P) 0 +# define ExprClearVVAProperties(E) #endif /* @@ -2847,7 +2908,7 @@ struct SrcList { unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ - unsigned fromDDL :1; /* Comes from sqlite_master */ + unsigned fromDDL :1; /* Comes from sqlite_schema */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ @@ -2968,7 +3029,7 @@ struct NameContext { #define NC_HasWin 0x08000 /* One or more window functions seen */ #define NC_IsDDL 0x10000 /* Resolving names in a CREATE statement */ #define NC_InAggFunc 0x20000 /* True if analyzing arguments to an agg func */ -#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_master */ +#define NC_FromDDL 0x40000 /* SQL text comes from sqlite_schema */ /* ** An instance of the following object describes a single ON CONFLICT @@ -3071,6 +3132,8 @@ struct Select { #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ +#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ +#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */ /* ** The results of a SELECT can be distributed in several ways, as defined @@ -3135,6 +3198,14 @@ struct Select { ** SRT_DistQueue Store results in priority queue pDest->iSDParm only if ** the same record has never been stored before. The ** index at pDest->iSDParm+1 hold all prior stores. +** +** SRT_Upfrom Store results in the temporary table already opened by +** pDest->iSDParm. If (pDest->iSDParm<0), then the temp +** table is an intkey table - in this case the first +** column returned by the SELECT is used as the integer +** key. If (pDest->iSDParm>0), then the table is an index +** table. (pDest->iSDParm) is the number of key columns in +** each index record in this case. */ #define SRT_Union 1 /* Store result as keys in an index */ #define SRT_Except 2 /* Remove result from a UNION index */ @@ -3154,14 +3225,16 @@ struct Select { #define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */ #define SRT_Coroutine 13 /* Generate a single row of result */ #define SRT_Table 14 /* Store result as data with an automatic rowid */ +#define SRT_Upfrom 15 /* Store result as data with rowid */ /* ** An instance of this object describes where to put of the results of ** a SELECT statement. */ struct SelectDest { - u8 eDest; /* How to dispose of the results. On of SRT_* above. */ + u8 eDest; /* How to dispose of the results. One of SRT_* above. */ int iSDParm; /* A parameter used by the eDest disposal method */ + int iSDParm2; /* A second parameter for the eDest disposal method */ int iSdst; /* Base register where results are written */ int nSdst; /* Number of registers allocated */ char *zAffSdst; /* Affinity used when eDest==SRT_Set */ @@ -3287,6 +3360,7 @@ struct Parse { Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ Parse *pParentParse; /* Parent parser if this parser is nested */ + AggInfo *pAggList; /* List of all AggInfo objects */ int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ @@ -3316,9 +3390,7 @@ struct Parse { ynVar nVar; /* Number of '?' variables seen in the SQL so far */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ -#if !(defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)) u8 eParseMode; /* PARSE_MODE_XXX constant */ -#endif #ifndef SQLITE_OMIT_VIRTUALTABLE int nVtabLock; /* Number of virtual tables to lock */ #endif @@ -3506,6 +3578,7 @@ struct TriggerStep { Trigger *pTrig; /* The trigger that this step is a part of */ Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ + SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ ExprList *pExprList; /* SET clause for UPDATE */ IdList *pIdList; /* Column names for INSERT */ @@ -3561,6 +3634,7 @@ typedef struct { int rc; /* Result code stored here */ u32 mInitFlags; /* Flags controlling error messages */ u32 nInitRow; /* Number of rows processed */ + Pgno mxPage; /* Maximum page number. 0 for no limit. */ } InitData; /* @@ -3678,6 +3752,7 @@ struct Walker { struct WhereConst *pConst; /* WHERE clause constants */ struct RenameCtx *pRename; /* RENAME COLUMN context */ struct Table *pTab; /* Table of generated column */ + struct SrcList_item *pSrcItem; /* A single FROM clause item */ } u; }; @@ -3690,6 +3765,9 @@ int sqlite3WalkSelectFrom(Walker*, Select*); int sqlite3ExprWalkNoop(Walker*, Expr*); int sqlite3SelectWalkNoop(Walker*, Select*); int sqlite3SelectWalkFail(Walker*, Select*); +int sqlite3WalkerDepthIncrease(Walker*,Select*); +void sqlite3WalkerDepthDecrease(Walker*,Select*); + #ifdef SQLITE_DEBUG void sqlite3SelectWalkAssert2(Walker*, Select*); #endif @@ -3834,13 +3912,16 @@ int sqlite3CantopenError(int); #ifdef SQLITE_DEBUG int sqlite3NomemError(int); int sqlite3IoerrnomemError(int); - int sqlite3CorruptPgnoError(int,Pgno); # define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__) # define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__) -# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P)) #else # define SQLITE_NOMEM_BKPT SQLITE_NOMEM # define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM +#endif +#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO) + int sqlite3CorruptPgnoError(int,Pgno); +# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P)) +#else # define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptError(__LINE__) #endif @@ -4084,7 +4165,7 @@ void sqlite3DeleteColumnNames(sqlite3*,Table*); int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char); Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); -void sqlite3OpenMasterTable(Parse *, int); +void sqlite3OpenSchemaTable(Parse *, int); Index *sqlite3PrimaryKeyIndex(Table*); i16 sqlite3TableColumnToIndex(Index*, i16); #ifdef SQLITE_OMIT_GENERATED_COLUMNS @@ -4110,11 +4191,13 @@ void sqlite3AddGenerated(Parse*,Expr*,Token*); void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC int sqlite3CodecQueryParameters(sqlite3*,const char*,const char*); #else # define sqlite3CodecQueryParameters(A,B,C) 0 #endif +/* END SQLCIPHER */ Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE @@ -4171,6 +4254,7 @@ void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); IdList *sqlite3IdListAppend(Parse*, IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); +SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, Expr*, IdList*); @@ -4189,7 +4273,6 @@ int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,u32,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); -void sqlite3SelectReset(Parse*, Select*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, int); void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); @@ -4223,7 +4306,7 @@ void sqlite3ExprCodeGeneratedColumn(Parse*, Column*, int); #endif void sqlite3ExprCodeCopy(Parse*, Expr*, int); void sqlite3ExprCodeFactorable(Parse*, Expr*, int); -int sqlite3ExprCodeAtInit(Parse*, Expr*, int); +int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); int sqlite3ExprCodeTarget(Parse*, Expr*, int); int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); @@ -4250,6 +4333,7 @@ int sqlite3ExprCompareSkip(Expr*, Expr*, int); int sqlite3ExprListCompare(ExprList*, ExprList*, int); int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); int sqlite3ExprImpliesNonNullRow(Expr*,int); +void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); @@ -4340,13 +4424,14 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*, Select*,u8,Upsert*, const char*,const char*); - TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,ExprList*, Expr*, u8, - const char*,const char*); + TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,SrcList*,ExprList*, + Expr*, u8, const char*,const char*); TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*, const char*,const char*); void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int); + SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*); # define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) # define sqlite3IsToplevel(p) ((p)->pToplevel==0) #else @@ -4360,6 +4445,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); # define sqlite3ParseToplevel(p) p # define sqlite3IsToplevel(p) 1 # define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0 +# define sqlite3TriggerStepSrc(A,B) 0 #endif int sqlite3JoinType(Parse*, Token*, Token*, Token*); @@ -4378,6 +4464,7 @@ void sqlite3DeferForeignKey(Parse*, int); # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif +int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName); void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); void sqlite3Detach(Parse*, Expr*); void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); @@ -4387,8 +4474,10 @@ int sqlite3FixExpr(DbFixer*, Expr*); int sqlite3FixExprList(DbFixer*, ExprList*); int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); int sqlite3RealSameAsInt(double,sqlite3_int64); +void sqlite3Int64ToText(i64,char*); int sqlite3AtoF(const char *z, double*, int, u8); int sqlite3GetInt32(const char *, int*); +int sqlite3GetUInt32(const char*, u32*); int sqlite3Atoi(const char*); #ifndef SQLITE_OMIT_UTF16 int sqlite3Utf16ByteLen(const void *pData, int nChar); @@ -4426,6 +4515,8 @@ int sqlite3VarintLen(u64 v); */ #define getVarint32(A,B) \ (u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite3GetVarint32((A),(u32 *)&(B))) +#define getVarint32NR(A,B) \ + B=(u32)*(A);if(B>=0x80)sqlite3GetVarint32((A),(u32*)&(B)) #define putVarint32(A,B) \ (u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1:\ sqlite3PutVarint((A),(B))) @@ -4435,10 +4526,10 @@ int sqlite3VarintLen(u64 v); const char *sqlite3IndexAffinityStr(sqlite3*, Index*); void sqlite3TableAffinity(Vdbe*, Table*, int); -char sqlite3CompareAffinity(Expr *pExpr, char aff2); -int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); +char sqlite3CompareAffinity(const Expr *pExpr, char aff2); +int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); char sqlite3TableColumnAffinity(Table*,int); -char sqlite3ExprAffinity(Expr *pExpr); +char sqlite3ExprAffinity(const Expr *pExpr); int sqlite3Atoi64(const char*, i64*, int, u8); int sqlite3DecOrHexToI64(const char*, i64*); void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); @@ -4461,9 +4552,10 @@ int sqlite3ReadSchema(Parse *pParse); CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); int sqlite3IsBinary(const CollSeq*); CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); -CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); -CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr); -int sqlite3ExprCollSeqMatch(Parse*,Expr*,Expr*); +void sqlite3SetTextEncoding(sqlite3 *db, u8); +CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr); +CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr); +int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*); Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); Expr *sqlite3ExprSkipCollate(Expr*); @@ -4505,14 +4597,15 @@ extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; extern SQLITE_WSD struct Sqlite3Config sqlite3Config; extern FuncDefHash sqlite3BuiltinFunctions; +extern u32 sqlite3_unsupported_selecttrace; #ifndef SQLITE_OMIT_WSD extern int sqlite3PendingByte; #endif -#endif +#endif /* SQLITE_AMALGAMATION */ #ifdef VDBE_PROFILE extern sqlite3_uint64 sqlite3NProfileCnt; #endif -void sqlite3RootPageMoved(sqlite3*, int, int, int); +void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno); void sqlite3Reindex(Parse*, Token*, Token*); void sqlite3AlterFunctions(void); void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); @@ -4530,6 +4623,8 @@ int sqlite3MatchEName( const char*, const char* ); +Bitmask sqlite3ExprColUsed(Expr*); +u8 sqlite3StrIHash(const char*); int sqlite3ResolveExprNames(NameContext*, Expr*); int sqlite3ResolveExprListNames(NameContext*, ExprList*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); @@ -4545,7 +4640,7 @@ void sqlite3RenameExprlistUnmap(Parse*, ExprList*); CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); char sqlite3AffinityType(const char*, Column*); void sqlite3Analyze(Parse*, Token*, Token*); -int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*); +int sqlite3InvokeBusyHandler(BusyHandler*); int sqlite3FindDb(sqlite3*, Token*); int sqlite3FindDbName(sqlite3 *, const char *); int sqlite3AnalysisLoad(sqlite3*,int iDB); @@ -4624,7 +4719,7 @@ void sqlite3AutoLoadExtensions(sqlite3*); #endif #ifndef SQLITE_OMIT_SHARED_CACHE - void sqlite3TableLock(Parse *, int, int, u8, const char *); + void sqlite3TableLock(Parse *, int, Pgno, u8, const char *); #else #define sqlite3TableLock(v,w,x,y,z) #endif @@ -4670,8 +4765,10 @@ void sqlite3AutoLoadExtensions(sqlite3*); int sqlite3ReadOnlyShadowTables(sqlite3 *db); #ifndef SQLITE_OMIT_VIRTUALTABLE int sqlite3ShadowTableName(sqlite3 *db, const char *zName); + int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*); #else # define sqlite3ShadowTableName(A,B) 0 +# define sqlite3IsShadowTableOf(A,B,C) 0 #endif int sqlite3VtabEponymousTableInit(Parse*,Module*); void sqlite3VtabEponymousTableClear(sqlite3*,Module*); @@ -4694,8 +4791,8 @@ char *sqlite3Normalize(Vdbe*, const char*); #endif int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); -CollSeq *sqlite3ExprCompareCollSeq(Parse*,Expr*); -CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); +CollSeq *sqlite3ExprCompareCollSeq(Parse*,const Expr*); +CollSeq *sqlite3BinaryCompareCollSeq(Parse *, const Expr*, const Expr*); int sqlite3TempInMemory(const sqlite3*); const char *sqlite3JournalModename(int); #ifndef SQLITE_OMIT_WAL diff --git a/src/sqliteLimit.h b/src/sqliteLimit.h index 28e7a41..a730257 100644 --- a/src/sqliteLimit.h +++ b/src/sqliteLimit.h @@ -131,9 +131,12 @@ /* ** The maximum value of a ?nnn wildcard that the parser will accept. +** If the value exceeds 32767 then extra space is required for the Expr +** structure. But otherwise, we believe that the number can be as large +** as a signed 32-bit integer can hold. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER -# define SQLITE_MAX_VARIABLE_NUMBER 999 +# define SQLITE_MAX_VARIABLE_NUMBER 32766 #endif /* Maximum page size. The upper bound on this value is 65536. This a limit diff --git a/src/status.c b/src/status.c index c42bcc2..f0e307c 100644 --- a/src/status.c +++ b/src/status.c @@ -352,7 +352,7 @@ int sqlite3_db_status( */ case SQLITE_DBSTATUS_CACHE_SPILL: op = SQLITE_DBSTATUS_CACHE_WRITE+1; - /* Fall through into the next case */ + /* no break */ deliberate_fall_through case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{ diff --git a/src/table.c b/src/table.c index c79255f..db60a82 100644 --- a/src/table.c +++ b/src/table.c @@ -56,7 +56,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ if( p->nData + need > p->nAlloc ){ char **azNew; p->nAlloc = p->nAlloc*2 + need; - azNew = sqlite3_realloc64( p->azResult, sizeof(char*)*p->nAlloc ); + azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc ); if( azNew==0 ) goto malloc_failed; p->azResult = azNew; } @@ -165,7 +165,7 @@ int sqlite3_get_table( } if( res.nAlloc>res.nData ){ char **azNew; - azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData ); + azNew = sqlite3Realloc( res.azResult, sizeof(char*)*res.nData ); if( azNew==0 ){ sqlite3_free_table(&res.azResult[1]); db->errCode = SQLITE_NOMEM; diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 2cae580..2d35d0d 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -3094,14 +3094,17 @@ deserialize_error: ** Change the encryption key on the currently open database. */ case DB_REKEY: { +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) int nKey; void *pKey; #endif +/* END SQLCIPHER */ if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "KEY"); return TCL_ERROR; } +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); rc = sqlite3_rekey(pDb->db, pKey, nKey); @@ -3110,6 +3113,7 @@ deserialize_error: rc = TCL_ERROR; } #endif +/* END SQLCIPHER */ break; } @@ -3678,9 +3682,11 @@ static int sqliteCmdUsage( "HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" " ?-nofollow BOOLEAN?" " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) " ?-key CODECKEY?" #endif +/* END SQLCIPHER */ ); return TCL_ERROR; } @@ -3714,11 +3720,14 @@ static int SQLITE_TCLAPI DbMain( const char *zFile = 0; const char *zVfs = 0; int flags; + int bTranslateFileName = 1; Tcl_DString translatedFilename; +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) void *pKey = 0; int nKey = 0; #endif +/* BEGIN SQLCIPHER */ int rc; /* In normal use, each TCL interpreter runs in a single thread. So @@ -3745,11 +3754,13 @@ static int SQLITE_TCLAPI DbMain( return TCL_OK; } if( strcmp(zArg,"-has-codec")==0 ){ +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) Tcl_AppendResult(interp,"1",(char*)0); #else Tcl_AppendResult(interp,"0",(char*)0); #endif +/* END SQLCIPHER */ return TCL_OK; } if( zArg[0]=='-' ) return sqliteCmdUsage(interp, objv); @@ -3764,9 +3775,11 @@ static int SQLITE_TCLAPI DbMain( if( i==objc-1 ) return sqliteCmdUsage(interp, objv); i++; if( strcmp(zArg,"-key")==0 ){ +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) pKey = Tcl_GetByteArrayFromObj(objv[i], &nKey); #endif +/* END SQLCIPHER */ }else if( strcmp(zArg, "-vfs")==0 ){ zVfs = Tcl_GetString(objv[i]); }else if( strcmp(zArg, "-readonly")==0 ){ @@ -3821,6 +3834,10 @@ static int SQLITE_TCLAPI DbMain( }else{ flags &= ~SQLITE_OPEN_URI; } + }else if( strcmp(zArg, "-translatefilename")==0 ){ + if( Tcl_GetBooleanFromObj(interp, objv[i], &bTranslateFileName) ){ + return TCL_ERROR; + } }else{ Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0); return TCL_ERROR; @@ -3830,9 +3847,13 @@ static int SQLITE_TCLAPI DbMain( p = (SqliteDb*)Tcl_Alloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); if( zFile==0 ) zFile = ""; - zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); + if( bTranslateFileName ){ + zFile = Tcl_TranslateFileName(interp, zFile, &translatedFilename); + } rc = sqlite3_open_v2(zFile, &p->db, flags, zVfs); - Tcl_DStringFree(&translatedFilename); + if( bTranslateFileName ){ + Tcl_DStringFree(&translatedFilename); + } if( p->db ){ if( SQLITE_OK!=sqlite3_errcode(p->db) ){ zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db)); @@ -3842,11 +3863,13 @@ static int SQLITE_TCLAPI DbMain( }else{ zErrMsg = sqlite3_mprintf("%s", sqlite3_errstr(rc)); } +/* BEGIN SQLCIPHER */ #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) if( p->db ){ sqlite3_key(p->db, pKey, nKey); } #endif +/* END SQLCIPHER */ if( p->db==0 ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); Tcl_Free((char*)p); diff --git a/src/test1.c b/src/test1.c index 5b07aef..c3954e9 100644 --- a/src/test1.c +++ b/src/test1.c @@ -4658,7 +4658,7 @@ static int SQLITE_TCLAPI test_open_v2( { "SQLITE_OPEN_MAIN_JOURNAL", SQLITE_OPEN_MAIN_JOURNAL }, { "SQLITE_OPEN_TEMP_JOURNAL", SQLITE_OPEN_TEMP_JOURNAL }, { "SQLITE_OPEN_SUBJOURNAL", SQLITE_OPEN_SUBJOURNAL }, - { "SQLITE_OPEN_MASTER_JOURNAL", SQLITE_OPEN_MASTER_JOURNAL }, + { "SQLITE_OPEN_SUPER_JOURNAL", SQLITE_OPEN_SUPER_JOURNAL }, { "SQLITE_OPEN_NOMUTEX", SQLITE_OPEN_NOMUTEX }, { "SQLITE_OPEN_FULLMUTEX", SQLITE_OPEN_FULLMUTEX }, { "SQLITE_OPEN_SHAREDCACHE", SQLITE_OPEN_SHAREDCACHE }, @@ -6465,6 +6465,30 @@ static int SQLITE_TCLAPI prng_seed( return TCL_OK; } +/* +** tclcmd: extra_schema_checks BOOLEAN +** +** Enable or disable schema checks when parsing the sqlite_schema file. +** This is always enabled in production, but it is sometimes useful to +** disable the checks in order to make some internal error states reachable +** for testing. +*/ +static int SQLITE_TCLAPI extra_schema_checks( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int i = 0; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); + return TCL_ERROR; + } + if( Tcl_GetBooleanFromObj(interp,objv[1],&i) ) return TCL_ERROR; + sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, i); + return TCL_OK; +} + /* ** tclcmd: database_may_be_corrupt ** @@ -7285,6 +7309,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_explain_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_decimal_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); @@ -7310,6 +7335,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( { "carray", sqlite3_carray_init }, { "closure", sqlite3_closure_init }, { "csv", sqlite3_csv_init }, + { "decimal", sqlite3_decimal_init }, { "eval", sqlite3_eval_init }, { "explain", sqlite3_explain_init }, { "fileio", sqlite3_fileio_init }, @@ -7804,6 +7830,41 @@ static int SQLITE_TCLAPI test_mmap_warm( } } +/* +** Usage: test_write_db DB OFFSET DATA +** +** Obtain the sqlite3_file* object for the database file for the "main" db +** of handle DB. Then invoke its xWrite method to write data DATA to offset +** OFFSET. +*/ +static int SQLITE_TCLAPI test_write_db( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db = 0; + Tcl_WideInt iOff = 0; + const unsigned char *aData = 0; + int nData = 0; + sqlite3_file *pFile = 0; + int rc; + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB OFFSET DATA"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + if( Tcl_GetWideIntFromObj(interp, objv[2], &iOff) ) return TCL_ERROR; + aData = Tcl_GetByteArrayFromObj(objv[3], &nData); + + sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFile); + rc = pFile->pMethods->xWrite(pFile, aData, nData, iOff); + + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); + return TCL_OK; +} + /* ** Usage: decode_hexdb TEXT ** @@ -8029,6 +8090,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "restore_prng_state", restore_prng_state, 0 }, { "reset_prng_state", reset_prng_state, 0 }, { "prng_seed", prng_seed, 0 }, + { "extra_schema_checks", extra_schema_checks, 0}, { "database_never_corrupt", database_never_corrupt, 0}, { "database_may_be_corrupt", database_may_be_corrupt, 0}, { "optimization_control", optimization_control,0}, @@ -8167,6 +8229,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_mmap_warm", test_mmap_warm, 0 }, { "sqlite3_config_sorterref", test_config_sorterref, 0 }, { "decode_hexdb", test_decode_hexdb, 0 }, + { "test_write_db", test_write_db, 0 }, }; static int bitmask_size = sizeof(Bitmask)*8; static int longdouble_size = sizeof(LONGDOUBLE_TYPE); @@ -8192,7 +8255,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ #endif #endif #if defined(SQLITE_ENABLE_SELECTTRACE) - extern int sqlite3SelectTrace; + extern u32 sqlite3_unsupported_selecttrace; #endif for(i=0; izTableName ){ sqlite3_stmt *pStmt = 0; rc = sqlite3_prepare(db, - "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?", + "SELECT sql FROM sqlite_schema WHERE type = 'table' AND name = ?", -1, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_text(pStmt, 1, pVtab->zTableName, -1, 0); diff --git a/src/test_config.c b/src/test_config.c index e6c0330..6df5eed 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -226,11 +226,13 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "json1", "0", TCL_GLOBAL_ONLY); #endif +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC Tcl_SetVar2(interp, "sqlite_options", "has_codec", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "has_codec", "0", TCL_GLOBAL_ONLY); #endif +/* END SQLCIPHER */ #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS Tcl_SetVar2(interp, "sqlite_options", "like_match_blobs", "0", TCL_GLOBAL_ONLY); diff --git a/src/test_malloc.c b/src/test_malloc.c index 32a03e7..8146501 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -112,32 +112,6 @@ static void *faultsimRealloc(void *pOld, int n){ return p; } -/* -** The following method calls are passed directly through to the underlying -** malloc system: -** -** xFree -** xSize -** xRoundup -** xInit -** xShutdown -*/ -static void faultsimFree(void *p){ - memfault.m.xFree(p); -} -static int faultsimSize(void *p){ - return memfault.m.xSize(p); -} -static int faultsimRoundup(int n){ - return memfault.m.xRoundup(n); -} -static int faultsimInit(void *p){ - return memfault.m.xInit(memfault.m.pAppData); -} -static void faultsimShutdown(void *p){ - memfault.m.xShutdown(memfault.m.pAppData); -} - /* ** This routine configures the malloc failure simulation. After ** calling this routine, the next nDelay mallocs will succeed, followed @@ -204,16 +178,6 @@ static void faultsimEndBenign(void){ ** the argument is non-zero, the */ static int faultsimInstall(int install){ - static struct sqlite3_mem_methods m = { - faultsimMalloc, /* xMalloc */ - faultsimFree, /* xFree */ - faultsimRealloc, /* xRealloc */ - faultsimSize, /* xSize */ - faultsimRoundup, /* xRoundup */ - faultsimInit, /* xInit */ - faultsimShutdown, /* xShutdown */ - 0 /* pAppData */ - }; int rc; install = (install ? 1 : 0); @@ -227,6 +191,9 @@ static int faultsimInstall(int install){ rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m); assert(memfault.m.xMalloc); if( rc==SQLITE_OK ){ + sqlite3_mem_methods m = memfault.m; + m.xMalloc = faultsimMalloc; + m.xRealloc = faultsimRealloc; rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m); } sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, diff --git a/src/test_multiplex.c b/src/test_multiplex.c index ed8c9f7..5ef1ec1 100644 --- a/src/test_multiplex.c +++ b/src/test_multiplex.c @@ -267,11 +267,14 @@ static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){ if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){ char *z; int n = pGroup->nName; - pGroup->aReal[iChunk].z = z = sqlite3_malloc64( n+5 ); + z = sqlite3_malloc64( n+5 ); if( z==0 ){ return SQLITE_NOMEM; } multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z); + pGroup->aReal[iChunk].z = sqlite3_create_filename(z,"","",0,0); + sqlite3_free(z); + if( pGroup->aReal[iChunk].z==0 ) return SQLITE_NOMEM; } return SQLITE_OK; } @@ -438,7 +441,7 @@ static void multiplexSubClose( } sqlite3_free(pGroup->aReal[iChunk].p); } - sqlite3_free(pGroup->aReal[iChunk].z); + sqlite3_free_filename(pGroup->aReal[iChunk].z); memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk])); } @@ -530,7 +533,7 @@ static int multiplexOpen( pGroup->szChunk += 65536; } } - pGroup->flags = flags; + pGroup->flags = (flags & ~SQLITE_OPEN_URI); rc = multiplexSubFilename(pGroup, 1); if( rc==SQLITE_OK ){ pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0); @@ -542,7 +545,7 @@ static int multiplexOpen( rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz64); if( rc==SQLITE_OK && zName ){ int bExists; - if( flags & SQLITE_OPEN_MASTER_JOURNAL ){ + if( flags & SQLITE_OPEN_SUPER_JOURNAL ){ pGroup->bEnabled = 0; }else if( sz64==0 ){ @@ -588,9 +591,9 @@ static int multiplexOpen( if( rc==SQLITE_OK ){ if( pSubOpen->pMethods->iVersion==1 ){ - pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; + pConn->pMethods = &gMultiplex.sIoMethodsV1; }else{ - pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; + pConn->pMethods = &gMultiplex.sIoMethodsV2; } }else{ multiplexFreeComponents(pGroup); diff --git a/src/test_mutex.c b/src/test_mutex.c index 8f43e5a..8bd9ff8 100644 --- a/src/test_mutex.c +++ b/src/test_mutex.c @@ -30,7 +30,7 @@ extern const char *sqlite3ErrName(int); static const char *aName[MAX_MUTEXES+1] = { - "fast", "recursive", "static_master", "static_mem", + "fast", "recursive", "static_main", "static_mem", "static_open", "static_prng", "static_lru", "static_pmem", "static_app1", "static_app2", "static_app3", "static_vfs1", "static_vfs2", "static_vfs3", 0 diff --git a/src/test_osinst.c b/src/test_osinst.c index a008bab..3e698c0 100644 --- a/src/test_osinst.c +++ b/src/test_osinst.c @@ -740,7 +740,7 @@ int sqlite3_vfslog_new( zFile = (char *)&p->base.zName[nVfs+1]; pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile); - flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL; + flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_SUPER_JOURNAL; pParent->xDelete(pParent, zFile, 0); rc = pParent->xOpen(pParent, zFile, p->pLog, flags, &flags); if( rc==SQLITE_OK ){ @@ -893,7 +893,7 @@ static int vlogConnect( pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile); sqlite3_free(zFile); - flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL; + flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_SUPER_JOURNAL; rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags); if( rc==SQLITE_OK ){ diff --git a/src/test_schema.c b/src/test_schema.c index cdf0857..d2cae7f 100644 --- a/src/test_schema.c +++ b/src/test_schema.c @@ -192,18 +192,18 @@ static int schemaNext(sqlite3_vtab_cursor *cur){ } /* Set zSql to the SQL to pull the list of tables from the - ** sqlite_master (or sqlite_temp_master) table of the database + ** sqlite_schema (or sqlite_temp_schema) table of the database ** identified by the row pointed to by the SQL statement pCur->pDbList ** (iterating through a "PRAGMA database_list;" statement). */ if( sqlite3_column_int(pCur->pDbList, 0)==1 ){ zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_temp_master WHERE type='table'" + "SELECT name FROM sqlite_temp_schema WHERE type='table'" ); }else{ sqlite3_stmt *pDbList = pCur->pDbList; zSql = sqlite3_mprintf( - "SELECT name FROM %Q.sqlite_master WHERE type='table'", + "SELECT name FROM %Q.sqlite_schema WHERE type='table'", sqlite3_column_text(pDbList, 1) ); } diff --git a/src/test_sqllog.c b/src/test_sqllog.c index 9b207cf..9ae0e50 100644 --- a/src/test_sqllog.c +++ b/src/test_sqllog.c @@ -118,7 +118,7 @@ struct SLConn { /* This object is a singleton that keeps track of all data loggers. */ static struct SLGlobal { - /* Protected by MUTEX_STATIC_MASTER */ + /* Protected by MUTEX_STATIC_MAIN */ sqlite3_mutex *mutex; /* Recursive mutex */ int nConn; /* Size of aConn[] array */ @@ -467,28 +467,28 @@ static int sqllogTraceDb(sqlite3 *db){ */ static void testSqllog(void *pCtx, sqlite3 *db, const char *zSql, int eType){ struct SLConn *p = 0; - sqlite3_mutex *master = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex *mainmtx = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN); assert( eType==0 || eType==1 || eType==2 ); assert( (eType==2)==(zSql==0) ); /* This is a database open command. */ if( eType==0 ){ - sqlite3_mutex_enter(master); + sqlite3_mutex_enter(mainmtx); if( sqllogglobal.mutex==0 ){ sqllogglobal.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE); } - sqlite3_mutex_leave(master); + sqlite3_mutex_leave(mainmtx); sqlite3_mutex_enter(sqllogglobal.mutex); if( sqllogglobal.bRec==0 && sqllogTraceDb(db) ){ - sqlite3_mutex_enter(master); + sqlite3_mutex_enter(mainmtx); p = &sqllogglobal.aConn[sqllogglobal.nConn++]; p->fd = 0; p->db = db; p->iLog = sqllogglobal.iNextLog++; - sqlite3_mutex_leave(master); + sqlite3_mutex_leave(mainmtx); /* Open the log and take a copy of the main database file */ sqllogOpenlog(p); @@ -507,7 +507,7 @@ static void testSqllog(void *pCtx, sqlite3 *db, const char *zSql, int eType){ /* A database handle close command */ if( eType==2 ){ - sqlite3_mutex_enter(master); + sqlite3_mutex_enter(mainmtx); if( ifd ) fclose(p->fd); p->db = 0; @@ -524,7 +524,7 @@ static void testSqllog(void *pCtx, sqlite3 *db, const char *zSql, int eType){ memmove(p, &p[1], nShift*sizeof(struct SLConn)); } } - sqlite3_mutex_leave(master); + sqlite3_mutex_leave(mainmtx); /* An ordinary SQL command. */ }else if( ifd ){ diff --git a/src/test_thread.c b/src/test_thread.c index 20b4cf1..537ff26 100644 --- a/src/test_thread.c +++ b/src/test_thread.c @@ -287,6 +287,7 @@ static int SQLITE_TCLAPI sqlthread_open( zFilename = Tcl_GetString(objv[2]); sqlite3_open(zFilename, &db); +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( db && objc>=4 ){ const char *zKey; @@ -303,6 +304,7 @@ static int SQLITE_TCLAPI sqlthread_open( } } #endif +/* END SQLCIPHER */ Md5_Register(db, 0, 0); sqlite3_busy_handler(db, xBusy, 0); diff --git a/src/tokenize.c b/src/tokenize.c index 48f9221..70f0c63 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -422,6 +422,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ } /* If the next character is a digit, this is a floating point ** number that begins with ".". Fall thru into the next case */ + /* no break */ deliberate_fall_through } case CC_DIGIT: { testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); @@ -526,6 +527,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ #endif /* If it is not a BLOB literal, then it must be an ID, since no ** SQL keywords start with the letter 'x'. Fall through */ + /* no break */ deliberate_fall_through } case CC_ID: { i = 1; @@ -568,7 +570,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->nVdbeActive==0 ){ - db->u1.isInterrupted = 0; + AtomicStore(&db->u1.isInterrupted, 0); } pParse->rc = SQLITE_OK; pParse->zTail = zSql; @@ -613,7 +615,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); #endif /* SQLITE_OMIT_WINDOWFUNC */ - if( db->u1.isInterrupted ){ + if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; break; } diff --git a/src/treeview.c b/src/treeview.c index 90a1d2d..187f1a0 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -138,8 +138,8 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){ sqlite3_str_appendf(&x, " %s", pItem->zName); } if( pItem->pTab ){ - sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p", - pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab); + sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", + pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); } if( pItem->zAlias ){ sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); @@ -398,14 +398,14 @@ void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ const char *zBinOp = 0; /* Binary operator */ const char *zUniOp = 0; /* Unary operator */ - char zFlgs[60]; + char zFlgs[200]; pView = sqlite3TreeViewPush(pView, moreToFollow); if( pExpr==0 ){ sqlite3TreeViewLine(pView, "nil"); sqlite3TreeViewPop(pView); return; } - if( pExpr->flags || pExpr->affExpr ){ + if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ StrAccum x; sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); sqlite3_str_appendf(&x, " fg.af=%x.%c", @@ -416,6 +416,9 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ if( ExprHasProperty(pExpr, EP_FromDDL) ){ sqlite3_str_appendf(&x, " DDL"); } + if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ + sqlite3_str_appendf(&x, " IMMUTABLE"); + } sqlite3StrAccumFinish(&x); }else{ zFlgs[0] = 0; @@ -522,6 +525,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ case TK_RSHIFT: zBinOp = "RSHIFT"; break; case TK_CONCAT: zBinOp = "CONCAT"; break; case TK_DOT: zBinOp = "DOT"; break; + case TK_LIMIT: zBinOp = "LIMIT"; break; case TK_UMINUS: zUniOp = "UMINUS"; break; case TK_UPLUS: zUniOp = "UPLUS"; break; @@ -578,8 +582,10 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ #endif } if( pExpr->op==TK_AGG_FUNCTION ){ - sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s", - pExpr->op2, pExpr->u.zToken, zFlgs); + sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p", + pExpr->op2, pExpr->u.zToken, zFlgs, + pExpr->pAggInfo ? pExpr->pAggInfo->selId : 0, + pExpr->iAgg, pExpr->pAggInfo); }else if( pExpr->op2!=0 ){ const char *zOp2; char zBuf[8]; @@ -611,7 +617,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ break; } case TK_SELECT: { - sqlite3TreeViewLine(pView, "SELECT-expr flags=0x%x", pExpr->flags); + sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags); sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); break; } diff --git a/src/trigger.c b/src/trigger.c index 458aa29..7ab5d95 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -26,6 +26,7 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){ sqlite3SelectDelete(db, pTmp->pSelect); sqlite3IdListDelete(db, pTmp->pIdList); sqlite3UpsertDelete(db, pTmp->pUpsert); + sqlite3SrcListDelete(db, pTmp->pFrom); sqlite3DbFree(db, pTmp->zSpan); sqlite3DbFree(db, pTmp); @@ -128,7 +129,7 @@ void sqlite3BeginTrigger( ** ^^^^^^^^ ** ** To maintain backwards compatibility, ignore the database - ** name on pTableName if we are reparsing out of SQLITE_MASTER. + ** name on pTableName if we are reparsing out of the schema table */ if( db->init.busy && iDb!=1 ){ sqlite3DbFree(db, pTableName->a[0].zDatabase); @@ -318,21 +319,22 @@ void sqlite3FinishTrigger( #endif /* if we are not initializing, - ** build the sqlite_master entry + ** build the sqlite_schema entry */ if( !db->init.busy ){ Vdbe *v; char *z; - /* Make an entry in the sqlite_master table */ + /* Make an entry in the sqlite_schema table */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; sqlite3BeginWriteOperation(pParse, 0, iDb); z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); testcase( z==0 ); sqlite3NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", - db->aDb[iDb].zDbSName, MASTER_NAME, zName, + "INSERT INTO %Q." DFLT_SCHEMA_TABLE + " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", + db->aDb[iDb].zDbSName, zName, pTrig->table, z); sqlite3DbFree(db, z); sqlite3ChangeCookie(pParse, iDb); @@ -485,6 +487,7 @@ TriggerStep *sqlite3TriggerInsertStep( TriggerStep *sqlite3TriggerUpdateStep( Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ + SrcList *pFrom, ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ @@ -499,16 +502,20 @@ TriggerStep *sqlite3TriggerUpdateStep( if( IN_RENAME_OBJECT ){ pTriggerStep->pExprList = pEList; pTriggerStep->pWhere = pWhere; + pTriggerStep->pFrom = pFrom; pEList = 0; pWhere = 0; + pFrom = 0; }else{ pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + pTriggerStep->pFrom = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE); } pTriggerStep->orconf = orconf; } sqlite3ExprListDelete(db, pEList); sqlite3ExprDelete(db, pWhere); + sqlite3SrcListDelete(db, pFrom); return pTriggerStep; } @@ -580,7 +587,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; inDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ - if( zDb && sqlite3StrICmp(db->aDb[j].zDbSName, zDb) ) continue; + if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue; assert( sqlite3SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName); if( pTrigger ) break; @@ -639,8 +646,8 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ */ if( (v = sqlite3GetVdbe(pParse))!=0 ){ sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'", - db->aDb[iDb].zDbSName, MASTER_NAME, pTrigger->zName + "DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'", + db->aDb[iDb].zDbSName, pTrigger->zName ); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0); @@ -735,25 +742,28 @@ Trigger *sqlite3TriggersExist( ** trigger is in TEMP in which case it can refer to any other database it ** wants. */ -static SrcList *targetSrcList( +SrcList *sqlite3TriggerStepSrc( Parse *pParse, /* The parsing context */ TriggerStep *pStep /* The trigger containing the target token */ ){ sqlite3 *db = pParse->db; - int iDb; /* Index of the database to use */ - SrcList *pSrc; /* SrcList to be returned */ - + SrcList *pSrc; /* SrcList to be returned */ + char *zName = sqlite3DbStrDup(db, pStep->zTarget); pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + assert( pSrc==0 || pSrc->nSrc==1 ); + assert( zName || pSrc==0 ); if( pSrc ){ - assert( pSrc->nSrc>0 ); - pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget); - iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema); - if( iDb==0 || iDb>=2 ){ - const char *zDb; - assert( iDbnDb ); - zDb = db->aDb[iDb].zDbSName; - pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, zDb); + Schema *pSchema = pStep->pTrig->pSchema; + pSrc->a[0].zName = zName; + if( pSchema!=db->aDb[1].pSchema ){ + pSrc->a[0].pSchema = pSchema; } + if( pStep->pFrom ){ + SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); + pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup); + } + }else{ + sqlite3DbFree(db, zName); } return pSrc; } @@ -802,7 +812,7 @@ static int codeTriggerProgram( switch( pStep->op ){ case TK_UPDATE: { sqlite3Update(pParse, - targetSrcList(pParse, pStep), + sqlite3TriggerStepSrc(pParse, pStep), sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3ExprDup(db, pStep->pWhere, 0), pParse->eOrconf, 0, 0, 0 @@ -811,7 +821,7 @@ static int codeTriggerProgram( } case TK_INSERT: { sqlite3Insert(pParse, - targetSrcList(pParse, pStep), + sqlite3TriggerStepSrc(pParse, pStep), sqlite3SelectDup(db, pStep->pSelect, 0), sqlite3IdListDup(db, pStep->pIdList), pParse->eOrconf, @@ -821,7 +831,7 @@ static int codeTriggerProgram( } case TK_DELETE: { sqlite3DeleteFrom(pParse, - targetSrcList(pParse, pStep), + sqlite3TriggerStepSrc(pParse, pStep), sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0 ); break; diff --git a/src/update.c b/src/update.c index 61b1740..a9c43d6 100644 --- a/src/update.c +++ b/src/update.c @@ -46,17 +46,17 @@ static void updateVirtualTable( ** literal default values specified: a number, null or a string. (If a more ** complicated default expression value was provided, it is evaluated ** when the ALTER TABLE is executed and one of the literal values written -** into the sqlite_master table.) +** into the sqlite_schema table.) ** ** Therefore, the P4 parameter is only required if the default value for ** the column is a literal number, string or null. The sqlite3ValueFromExpr() ** function is capable of transforming these types of expressions into ** sqlite3_value objects. ** -** If parameter iReg is not negative, code an OP_RealAffinity instruction -** on register iReg. This is used when an equivalent integer value is -** stored in place of an 8-byte floating point value in order to save -** space. +** If column as REAL affinity and the table is an ordinary b-tree table +** (not a virtual table) then the value might have been stored as an +** integer. In that case, add an OP_RealAffinity opcode to make sure +** it has been converted into REAL. */ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ assert( pTab!=0 ); @@ -73,7 +73,7 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ } } #ifndef SQLITE_OMIT_FLOATING_POINT - if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ + if( pTab->aCol[i].affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){ sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); } #endif @@ -130,12 +130,149 @@ static int indexWhereClauseMightChange( aXRef, chngRowid); } +/* +** Allocate and return a pointer to an expression of type TK_ROW with +** Expr.iColumn set to value (iCol+1). The resolver will modify the +** expression to be a TK_COLUMN reading column iCol of the first +** table in the source-list (pSrc->a[0]). +*/ +static Expr *exprRowColumn(Parse *pParse, int iCol){ + Expr *pRet = sqlite3PExpr(pParse, TK_ROW, 0, 0); + if( pRet ) pRet->iColumn = iCol+1; + return pRet; +} + +/* +** Assuming both the pLimit and pOrderBy parameters are NULL, this function +** generates VM code to run the query: +** +** SELECT , pChanges FROM pTabList WHERE pWhere +** +** and write the results to the ephemeral table already opened as cursor +** iEph. None of pChanges, pTabList or pWhere are modified or consumed by +** this function, they must be deleted by the caller. +** +** Or, if pLimit and pOrderBy are not NULL, and pTab is not a view: +** +** SELECT , pChanges FROM pTabList +** WHERE pWhere +** GROUP BY +** ORDER BY pOrderBy LIMIT pLimit +** +** If pTab is a view, the GROUP BY clause is omitted. +** +** Exactly how results are written to table iEph, and exactly what +** the in the query above are is determined by the type +** of table pTabList->a[0].pTab. +** +** If the table is a WITHOUT ROWID table, then argument pPk must be its +** PRIMARY KEY. In this case are the primary key columns +** of the table, in order. The results of the query are written to ephemeral +** table iEph as index keys, using OP_IdxInsert. +** +** If the table is actually a view, then are all columns of +** the view. The results are written to the ephemeral table iEph as records +** with automatically assigned integer keys. +** +** If the table is a virtual or ordinary intkey table, then +** is its rowid. For a virtual table, the results are written to iEph as +** records with automatically assigned integer keys For intkey tables, the +** rowid value in is used as the integer key, and the +** remaining fields make up the table record. +*/ +static void updateFromSelect( + Parse *pParse, /* Parse context */ + int iEph, /* Cursor for open eph. table */ + Index *pPk, /* PK if table 0 is WITHOUT ROWID */ + ExprList *pChanges, /* List of expressions to return */ + SrcList *pTabList, /* List of tables to select from */ + Expr *pWhere, /* WHERE clause for query */ + ExprList *pOrderBy, /* ORDER BY clause */ + Expr *pLimit /* LIMIT clause */ +){ + int i; + SelectDest dest; + Select *pSelect = 0; + ExprList *pList = 0; + ExprList *pGrp = 0; + Expr *pLimit2 = 0; + ExprList *pOrderBy2 = 0; + sqlite3 *db = pParse->db; + Table *pTab = pTabList->a[0].pTab; + SrcList *pSrc; + Expr *pWhere2; + int eDest; + +#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + if( pOrderBy && pLimit==0 ) { + sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on UPDATE"); + return; + } + pOrderBy2 = sqlite3ExprListDup(db, pOrderBy, 0); + pLimit2 = sqlite3ExprDup(db, pLimit, 0); +#else + UNUSED_PARAMETER(pOrderBy); + UNUSED_PARAMETER(pLimit); +#endif + + pSrc = sqlite3SrcListDup(db, pTabList, 0); + pWhere2 = sqlite3ExprDup(db, pWhere, 0); + + assert( pTabList->nSrc>1 ); + if( pSrc ){ + pSrc->a[0].iCursor = -1; + pSrc->a[0].pTab->nTabRef--; + pSrc->a[0].pTab = 0; + } + if( pPk ){ + for(i=0; inKeyCol; i++){ + Expr *pNew = exprRowColumn(pParse, pPk->aiColumn[i]); +#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + if( pLimit ){ + pGrp = sqlite3ExprListAppend(pParse, pGrp, sqlite3ExprDup(db, pNew, 0)); + } +#endif + pList = sqlite3ExprListAppend(pParse, pList, pNew); + } + eDest = SRT_Upfrom; + }else if( pTab->pSelect ){ + for(i=0; inCol; i++){ + pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); + } + eDest = SRT_Table; + }else{ + eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom; + pList = sqlite3ExprListAppend(pParse, 0, sqlite3PExpr(pParse,TK_ROW,0,0)); +#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT + if( pLimit ){ + pGrp = sqlite3ExprListAppend(pParse, 0, sqlite3PExpr(pParse,TK_ROW,0,0)); + } +#endif + } + if( ALWAYS(pChanges) ){ + for(i=0; inExpr; i++){ + pList = sqlite3ExprListAppend(pParse, pList, + sqlite3ExprDup(db, pChanges->a[i].pExpr, 0) + ); + } + } + pSelect = sqlite3SelectNew(pParse, pList, + pSrc, pWhere2, pGrp, 0, pOrderBy2, SF_UpdateFrom|SF_IncludeHidden, pLimit2 + ); + sqlite3SelectDestInit(&dest, eDest, iEph); + dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1); + sqlite3Select(pParse, pSelect, &dest); + sqlite3SelectDelete(db, pSelect); +} + /* ** Process an UPDATE statement. ** -** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; -** \_______/ \________/ \______/ \________________/ -* onError pTabList pChanges pWhere +** UPDATE OR IGNORE tbl SET a=b, c=d FROM tbl2... WHERE e<5 AND f NOT NULL; +** \_______/ \_/ \______/ \_____/ \________________/ +** onError | pChanges | pWhere +** \_______________________/ +** pTabList */ void sqlite3Update( Parse *pParse, /* The parser context */ @@ -150,7 +287,7 @@ void sqlite3Update( int i, j, k; /* Loop counters */ Table *pTab; /* The table to be updated */ int addrTop = 0; /* VDBE instruction address of the start of the loop */ - WhereInfo *pWInfo; /* Information about the WHERE clause */ + WhereInfo *pWInfo = 0; /* Information about the WHERE clause */ Vdbe *v; /* The virtual database engine */ Index *pIdx; /* For looping over indices */ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */ @@ -169,6 +306,7 @@ void sqlite3Update( u8 chngRowid; /* Rowid changed in a normal table */ u8 chngKey; /* Either chngPk or chngRowid */ Expr *pRowidExpr = 0; /* Expression defining the new record number */ + int iRowidExpr = -1; /* Index of "rowid=" (or IPK) assignment in pChanges */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ @@ -192,6 +330,7 @@ void sqlite3Update( i16 nPk = 0; /* Number of components of the PRIMARY KEY */ int bReplace = 0; /* True if REPLACE conflict resolution might happen */ int bFinishSeek = 1; /* The OP_FinishSeek opcode is needed */ + int nChangeFrom = 0; /* If there is a FROM, pChanges->nExpr, else 0 */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ @@ -207,7 +346,6 @@ void sqlite3Update( if( pParse->nErr || db->mallocFailed ){ goto update_cleanup; } - assert( pTabList->nSrc==1 ); /* Locate the table which we want to update. */ @@ -232,8 +370,15 @@ void sqlite3Update( # define isView 0 #endif + /* If there was a FROM clause, set nChangeFrom to the number of expressions + ** in the change-list. Otherwise, set it to 0. There cannot be a FROM + ** clause if this function is being called to generate code for part of + ** an UPSERT statement. */ + nChangeFrom = (pTabList->nSrc>1) ? pChanges->nExpr : 0; + assert( nChangeFrom==0 || pUpsert==0 ); + #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT - if( !isView ){ + if( !isView && nChangeFrom==0 ){ pWhere = sqlite3LimitWhere( pParse, pTabList, pWhere, pOrderBy, pLimit, "UPDATE" ); @@ -302,7 +447,9 @@ void sqlite3Update( */ chngRowid = chngPk = 0; for(i=0; inExpr; i++){ - if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ + /* If this is an UPDATE with a FROM clause, do not resolve expressions + ** here. The call to sqlite3Select() below will do that. */ + if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; } for(j=0; jnCol; j++){ @@ -310,6 +457,7 @@ void sqlite3Update( if( j==pTab->iPKey ){ chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; + iRowidExpr = i; }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){ chngPk = 1; } @@ -332,6 +480,7 @@ void sqlite3Update( j = -1; chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; + iRowidExpr = i; }else{ sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zEName); pParse->checkSchema = 1; @@ -461,7 +610,7 @@ void sqlite3Update( ** an ephemeral table. */ #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) - if( isView ){ + if( nChangeFrom==0 && isView ){ sqlite3MaterializeView(pParse, pTab, pWhere, pOrderBy, pLimit, iDataCur ); @@ -473,7 +622,7 @@ void sqlite3Update( /* Resolve the column names in all the expressions in the ** WHERE clause. */ - if( sqlite3ResolveExprNames(&sNC, pWhere) ){ + if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pWhere) ){ goto update_cleanup; } @@ -500,105 +649,128 @@ void sqlite3Update( sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } - if( HasRowid(pTab) ){ + if( nChangeFrom==0 && HasRowid(pTab) ){ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); }else{ - assert( pPk!=0 ); - nPk = pPk->nKeyCol; + assert( pPk!=0 || HasRowid(pTab) ); + nPk = pPk ? pPk->nKeyCol : 0; iPk = pParse->nMem+1; pParse->nMem += nPk; + pParse->nMem += nChangeFrom; regKey = ++pParse->nMem; if( pUpsert==0 ){ + int nEphCol = nPk + nChangeFrom + (isView ? pTab->nCol : 0); iEph = pParse->nTab++; - sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1); - addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); - sqlite3VdbeSetP4KeyInfo(pParse, pPk); + if( pPk ) sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1); + addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nEphCol); + if( pPk ){ + KeyInfo *pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pPk); + if( pKeyInfo ){ + pKeyInfo->nAllField = nEphCol; + sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO); + } + } + if( nChangeFrom ){ + updateFromSelect( + pParse, iEph, pPk, pChanges, pTabList, pWhere, pOrderBy, pLimit + ); +#ifndef SQLITE_OMIT_SUBQUERY + if( isView ) iDataCur = iEph; +#endif + } } } - if( pUpsert ){ - /* If this is an UPSERT, then all cursors have already been opened by - ** the outer INSERT and the data cursor should be pointing at the row - ** that is to be updated. So bypass the code that searches for the - ** row(s) to be updated. - */ - pWInfo = 0; - eOnePass = ONEPASS_SINGLE; - sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL); - bFinishSeek = 0; + if( nChangeFrom ){ + sqlite3MultiWrite(pParse); + eOnePass = ONEPASS_OFF; + nKey = nPk; + regKey = iPk; }else{ - /* Begin the database scan. - ** - ** Do not consider a single-pass strategy for a multi-row update if - ** there are any triggers or foreign keys to process, or rows may - ** be deleted as a result of REPLACE conflict handling. Any of these - ** things might disturb a cursor being used to scan through the table - ** or index, causing a single-pass approach to malfunction. */ - flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE; - if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ - flags |= WHERE_ONEPASS_MULTIROW; - } - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur); - if( pWInfo==0 ) goto update_cleanup; - - /* A one-pass strategy that might update more than one row may not - ** be used if any column of the index used for the scan is being - ** updated. Otherwise, if there is an index on "b", statements like - ** the following could create an infinite loop: - ** - ** UPDATE t1 SET b=b+1 WHERE b>? - ** - ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI - ** strategy that uses an index for which one or more columns are being - ** updated. */ - eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); - bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo); - if( eOnePass!=ONEPASS_SINGLE ){ - sqlite3MultiWrite(pParse); - if( eOnePass==ONEPASS_MULTI ){ - int iCur = aiCurOnePass[1]; - if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){ - eOnePass = ONEPASS_OFF; + if( pUpsert ){ + /* If this is an UPSERT, then all cursors have already been opened by + ** the outer INSERT and the data cursor should be pointing at the row + ** that is to be updated. So bypass the code that searches for the + ** row(s) to be updated. + */ + pWInfo = 0; + eOnePass = ONEPASS_SINGLE; + sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL); + bFinishSeek = 0; + }else{ + /* Begin the database scan. + ** + ** Do not consider a single-pass strategy for a multi-row update if + ** there are any triggers or foreign keys to process, or rows may + ** be deleted as a result of REPLACE conflict handling. Any of these + ** things might disturb a cursor being used to scan through the table + ** or index, causing a single-pass approach to malfunction. */ + flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE; + if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ + flags |= WHERE_ONEPASS_MULTIROW; + } + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); + if( pWInfo==0 ) goto update_cleanup; + + /* A one-pass strategy that might update more than one row may not + ** be used if any column of the index used for the scan is being + ** updated. Otherwise, if there is an index on "b", statements like + ** the following could create an infinite loop: + ** + ** UPDATE t1 SET b=b+1 WHERE b>? + ** + ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI + ** strategy that uses an index for which one or more columns are being + ** updated. */ + eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo); + if( eOnePass!=ONEPASS_SINGLE ){ + sqlite3MultiWrite(pParse); + if( eOnePass==ONEPASS_MULTI ){ + int iCur = aiCurOnePass[1]; + if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){ + eOnePass = ONEPASS_OFF; + } + assert( iCur!=iDataCur || !HasRowid(pTab) ); } - assert( iCur!=iDataCur || !HasRowid(pTab) ); + } + } + + if( HasRowid(pTab) ){ + /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF + ** mode, write the rowid into the FIFO. In either of the one-pass modes, + ** leave it in register regOldRowid. */ + sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); + if( eOnePass==ONEPASS_OFF ){ + /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ + aRegIdx[nAllIdx] = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); + } + }else{ + /* Read the PK of the current row into an array of registers. In + ** ONEPASS_OFF mode, serialize the array into a record and store it in + ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change + ** the OP_OpenEphemeral instruction to a Noop (the ephemeral table + ** is not required) and leave the PK fields in the array of registers. */ + for(i=0; iaiColumn[i]>=0 ); + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, + pPk->aiColumn[i], iPk+i); + } + if( eOnePass ){ + if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen); + nKey = nPk; + regKey = iPk; + }else{ + sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey, + sqlite3IndexAffinityStr(db, pPk), nPk); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk); } } } - if( HasRowid(pTab) ){ - /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF - ** mode, write the rowid into the FIFO. In either of the one-pass modes, - ** leave it in register regOldRowid. */ - sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); - if( eOnePass==ONEPASS_OFF ){ - /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ - aRegIdx[nAllIdx] = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); - } - }else{ - /* Read the PK of the current row into an array of registers. In - ** ONEPASS_OFF mode, serialize the array into a record and store it in - ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change - ** the OP_OpenEphemeral instruction to a Noop (the ephemeral table - ** is not required) and leave the PK fields in the array of registers. */ - for(i=0; iaiColumn[i]>=0 ); - sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, - pPk->aiColumn[i], iPk+i); - } - if( eOnePass ){ - if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen); - nKey = nPk; - regKey = iPk; - }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey, - sqlite3IndexAffinityStr(db, pPk), nPk); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk); - } - } - if( pUpsert==0 ){ - if( eOnePass!=ONEPASS_MULTI ){ + if( nChangeFrom==0 && eOnePass!=ONEPASS_MULTI ){ sqlite3WhereEnd(pWInfo); } @@ -616,7 +788,9 @@ void sqlite3Update( } sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen, 0, 0); - if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); + if( addrOnce ){ + sqlite3VdbeJumpHereOrPopInst(v, addrOnce); + } } /* Top of the update loop */ @@ -632,12 +806,31 @@ void sqlite3Update( sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); VdbeCoverageIf(v, pPk==0); VdbeCoverageIf(v, pPk!=0); - }else if( pPk ){ + }else if( pPk || nChangeFrom ){ labelContinue = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); - addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); - VdbeCoverage(v); + addrTop = sqlite3VdbeCurrentAddr(v); + if( nChangeFrom ){ + if( !isView ){ + if( pPk ){ + for(i=0; i=0 ); + if( nChangeFrom==0 ){ + sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); + }else{ + sqlite3VdbeAddOp3(v, OP_Column, iEph, iRowidExpr, regNewRowid); + } sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); VdbeCoverage(v); } @@ -706,7 +904,13 @@ void sqlite3Update( }else{ j = aXRef[i]; if( j>=0 ){ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr, k); + if( nChangeFrom ){ + int nOff = (isView ? pTab->nCol : nPk); + assert( eOnePass==ONEPASS_OFF ); + sqlite3VdbeAddOp3(v, OP_Column, iEph, nOff+j, k); + }else{ + sqlite3ExprCode(pParse, pChanges->a[j].pExpr, k); + } }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){ /* This branch loads the value of a column that will not be changed ** into a register. This is done if there are no BEFORE triggers, or @@ -738,43 +942,45 @@ void sqlite3Update( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue); - /* The row-trigger may have deleted the row being updated. In this - ** case, jump to the next row. No updates or AFTER triggers are - ** required. This behavior - what happens when the row being updated - ** is deleted or renamed by a BEFORE trigger - is left undefined in the - ** documentation. - */ - if( pPk ){ - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey); - VdbeCoverage(v); - }else{ - sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); - VdbeCoverage(v); - } - - /* After-BEFORE-trigger-reload-loop: - ** If it did not delete it, the BEFORE trigger may still have modified - ** some of the columns of the row being updated. Load the values for - ** all columns not modified by the update statement into their registers - ** in case this has happened. Only unmodified columns are reloaded. - ** The values computed for modified columns use the values before the - ** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26) - ** for an example. - */ - for(i=0, k=regNew; inCol; i++, k++){ - if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){ - if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) k--; - }else if( aXRef[i]<0 && i!=pTab->iPKey ){ - sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k); + if( !isView ){ + /* The row-trigger may have deleted the row being updated. In this + ** case, jump to the next row. No updates or AFTER triggers are + ** required. This behavior - what happens when the row being updated + ** is deleted or renamed by a BEFORE trigger - is left undefined in the + ** documentation. + */ + if( pPk ){ + sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey); + VdbeCoverage(v); + }else{ + sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid); + VdbeCoverage(v); + } + + /* After-BEFORE-trigger-reload-loop: + ** If it did not delete it, the BEFORE trigger may still have modified + ** some of the columns of the row being updated. Load the values for + ** all columns not modified by the update statement into their registers + ** in case this has happened. Only unmodified columns are reloaded. + ** The values computed for modified columns use the values before the + ** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26) + ** for an example. + */ + for(i=0, k=regNew; inCol; i++, k++){ + if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){ + if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) k--; + }else if( aXRef[i]<0 && i!=pTab->iPKey ){ + sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k); + } } - } #ifndef SQLITE_OMIT_GENERATED_COLUMNS - if( pTab->tabFlags & TF_HasGenerated ){ - testcase( pTab->tabFlags & TF_HasVirtual ); - testcase( pTab->tabFlags & TF_HasStored ); - sqlite3ComputeGeneratedColumns(pParse, regNew, pTab); - } + if( pTab->tabFlags & TF_HasGenerated ){ + testcase( pTab->tabFlags & TF_HasVirtual ); + testcase( pTab->tabFlags & TF_HasStored ); + sqlite3ComputeGeneratedColumns(pParse, regNew, pTab); + } #endif + } } if( !isView ){ @@ -877,7 +1083,7 @@ void sqlite3Update( }else if( eOnePass==ONEPASS_MULTI ){ sqlite3VdbeResolveLabel(v, labelContinue); sqlite3WhereEnd(pWInfo); - }else if( pPk ){ + }else if( pPk || nChangeFrom ){ sqlite3VdbeResolveLabel(v, labelContinue); sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); }else{ @@ -962,7 +1168,7 @@ static void updateVirtualTable( int i; /* Loop counter */ sqlite3 *db = pParse->db; /* Database connection */ const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); - WhereInfo *pWInfo; + WhereInfo *pWInfo = 0; int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */ int regArg; /* First register in VUpdate arg array */ int regRec; /* Register in which to assemble record */ @@ -980,69 +1186,96 @@ static void updateVirtualTable( addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg); regArg = pParse->nMem + 1; pParse->nMem += nArg; - regRec = ++pParse->nMem; - regRowid = ++pParse->nMem; - - /* Start scanning the virtual table */ - pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0); - if( pWInfo==0 ) return; - - /* Populate the argument registers. */ - for(i=0; inCol; i++){ - assert( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ); - if( aXRef[i]>=0 ){ - sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); - }else{ - sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); - sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */ - } - } - if( HasRowid(pTab) ){ - sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); + if( pSrc->nSrc>1 ){ + Expr *pRow; + ExprList *pList; if( pRowid ){ - sqlite3ExprCode(pParse, pRowid, regArg+1); + pRow = sqlite3ExprDup(db, pRowid, 0); }else{ - sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1); + pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0); } + pList = sqlite3ExprListAppend(pParse, 0, pRow); + + for(i=0; inCol; i++){ + if( aXRef[i]>=0 ){ + pList = sqlite3ExprListAppend(pParse, pList, + sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0) + ); + }else{ + pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i)); + } + } + + updateFromSelect(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0); + sqlite3ExprListDelete(db, pList); + eOnePass = ONEPASS_OFF; }else{ - Index *pPk; /* PRIMARY KEY index */ - i16 iPk; /* PRIMARY KEY column */ - pPk = sqlite3PrimaryKeyIndex(pTab); - assert( pPk!=0 ); - assert( pPk->nKeyCol==1 ); - iPk = pPk->aiColumn[0]; - sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg); - sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1); - } + regRec = ++pParse->nMem; + regRowid = ++pParse->nMem; - eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy); + /* Start scanning the virtual table */ + pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); + if( pWInfo==0 ) return; - /* There is no ONEPASS_MULTI on virtual tables */ - assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE ); + /* Populate the argument registers. */ + for(i=0; inCol; i++){ + assert( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ); + if( aXRef[i]>=0 ){ + sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); + }else{ + sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* For sqlite3_vtab_nochange() */ + } + } + if( HasRowid(pTab) ){ + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); + if( pRowid ){ + sqlite3ExprCode(pParse, pRowid, regArg+1); + }else{ + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1); + } + }else{ + Index *pPk; /* PRIMARY KEY index */ + i16 iPk; /* PRIMARY KEY column */ + pPk = sqlite3PrimaryKeyIndex(pTab); + assert( pPk!=0 ); + assert( pPk->nKeyCol==1 ); + iPk = pPk->aiColumn[0]; + sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg); + sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1); + } - if( eOnePass ){ - /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded - ** above. */ - sqlite3VdbeChangeToNoop(v, addr); - sqlite3VdbeAddOp1(v, OP_Close, iCsr); - }else{ - /* Create a record from the argument register contents and insert it into - ** the ephemeral table. */ - sqlite3MultiWrite(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec); -#ifdef SQLITE_DEBUG - /* Signal an assert() within OP_MakeRecord that it is allowed to - ** accept no-change records with serial_type 10 */ - sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); + eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy); + + /* There is no ONEPASS_MULTI on virtual tables */ + assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE ); + + if( eOnePass ){ + /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded + ** above. */ + sqlite3VdbeChangeToNoop(v, addr); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); + }else{ + /* Create a record from the argument register contents and insert it into + ** the ephemeral table. */ + sqlite3MultiWrite(pParse); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec); +#if defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_NULL_TRIM) + /* Signal an assert() within OP_MakeRecord that it is allowed to + ** accept no-change records with serial_type 10 */ + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); #endif - sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid); + sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid); + } } if( eOnePass==ONEPASS_OFF ){ /* End the virtual table scan */ - sqlite3WhereEnd(pWInfo); + if( pSrc->nSrc==1 ){ + sqlite3WhereEnd(pWInfo); + } /* Begin scannning through the ephemeral table. */ addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v); diff --git a/src/utf.c b/src/utf.c index f5fb3ef..5f27bab 100644 --- a/src/utf.c +++ b/src/utf.c @@ -105,26 +105,6 @@ static const unsigned char sqlite3Utf8Trans1[] = { } \ } -#define READ_UTF16LE(zIn, TERM, c){ \ - c = (*zIn++); \ - c += ((*zIn++)<<8); \ - if( c>=0xD800 && c<0xE000 && TERM ){ \ - int c2 = (*zIn++); \ - c2 += ((*zIn++)<<8); \ - c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - } \ -} - -#define READ_UTF16BE(zIn, TERM, c){ \ - c = ((*zIn++)<<8); \ - c += (*zIn++); \ - if( c>=0xD800 && c<0xE000 && TERM ){ \ - int c2 = ((*zIn++)<<8); \ - c2 += (*zIn++); \ - c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ - } \ -} - /* ** Translate a single UTF-8 character. Return the unicode value. ** @@ -301,13 +281,59 @@ SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ if( pMem->enc==SQLITE_UTF16LE ){ /* UTF-16 Little-endian -> UTF-8 */ while( zIn=0xd800 && c<0xe000 ){ +#ifdef SQLITE_REPLACE_INVALID_UTF + if( c>=0xdc00 || zIn>=zTerm ){ + c = 0xfffd; + }else{ + int c2 = *(zIn++); + c2 += (*(zIn++))<<8; + if( c2<0xdc00 || c2>=0xe000 ){ + zIn -= 2; + c = 0xfffd; + }else{ + c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000; + } + } +#else + if( zIn UTF-8 */ while( zIn=0xd800 && c<0xe000 ){ +#ifdef SQLITE_REPLACE_INVALID_UTF + if( c>=0xdc00 || zIn>=zTerm ){ + c = 0xfffd; + }else{ + int c2 = (*(zIn++))<<8; + c2 += *(zIn++); + if( c2<0xdc00 || c2>=0xe000 ){ + zIn -= 2; + c = 0xfffd; + }else{ + c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000; + } + } +#else + if( zInn+(desiredEnc==SQLITE_UTF8?1:2))<=len ); - c = pMem->flags; + c = MEM_Str|MEM_Term|(pMem->flags&(MEM_AffMask|MEM_Subtype)); sqlite3VdbeMemRelease(pMem); - pMem->flags = MEM_Str|MEM_Term|(c&(MEM_AffMask|MEM_Subtype)); + pMem->flags = c; pMem->enc = desiredEnc; pMem->z = (char*)zOut; pMem->zMalloc = pMem->z; @@ -466,18 +492,15 @@ int sqlite3Utf16ByteLen(const void *zIn, int nChar){ unsigned char const *z = zIn; int n = 0; - if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ - while( n=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2; + n++; } - return (int)(z-(unsigned char const *)zIn); + return (int)(z-(unsigned char const *)zIn) + - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE); } #if defined(SQLITE_TEST) @@ -507,30 +530,6 @@ void sqlite3UtfSelfTest(void){ assert( c==t ); assert( (z-zBuf)==n ); } - for(i=0; i<0x00110000; i++){ - if( i>=0xD800 && i<0xE000 ) continue; - z = zBuf; - WRITE_UTF16LE(z, i); - n = (int)(z-zBuf); - assert( n>0 && n<=4 ); - z[0] = 0; - z = zBuf; - READ_UTF16LE(z, 1, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } - for(i=0; i<0x00110000; i++){ - if( i>=0xD800 && i<0xE000 ) continue; - z = zBuf; - WRITE_UTF16BE(z, i); - n = (int)(z-zBuf); - assert( n>0 && n<=4 ); - z[0] = 0; - z = zBuf; - READ_UTF16BE(z, 1, c); - assert( c==i ); - assert( (z-zBuf)==n ); - } } #endif /* SQLITE_TEST */ #endif /* SQLITE_OMIT_UTF16 */ diff --git a/src/util.c b/src/util.c index 3e3a924..7d1341f 100644 --- a/src/util.c +++ b/src/util.c @@ -317,6 +317,19 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } +/* +** Compute an 8-bit hash on a string that is insensitive to case differences +*/ +u8 sqlite3StrIHash(const char *z){ + u8 h = 0; + if( z==0 ) return 0; + while( z[0] ){ + h += UpperToLower[(unsigned char)z[0]]; + z++; + } + return h; +} + /* ** Compute 10 to the E-th power. Examples: E==1 results in 10. ** E==2 results in 100. E==50 results in 1.0e50. @@ -582,6 +595,30 @@ do_atof_calc: #pragma warning(default : 4756) #endif +/* +** Render an signed 64-bit integer as text. Store the result in zOut[]. +** +** The caller must ensure that zOut[] is at least 21 bytes in size. +*/ +void sqlite3Int64ToText(i64 v, char *zOut){ + int i; + u64 x; + char zTemp[22]; + if( v<0 ){ + x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; + }else{ + x = v; + } + i = sizeof(zTemp)-2; + zTemp[sizeof(zTemp)-1] = 0; + do{ + zTemp[i--] = (x%10) + '0'; + x = x/10; + }while( x ); + if( v<0 ) zTemp[i--] = '-'; + memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); +} + /* ** Compare the 19-character string zNum against the text representation ** value 2^63: 9223372036854775808. Return negative, zero, or positive @@ -822,10 +859,28 @@ int sqlite3GetInt32(const char *zNum, int *pValue){ */ int sqlite3Atoi(const char *z){ int x = 0; - if( z ) sqlite3GetInt32(z, &x); + sqlite3GetInt32(z, &x); return x; } +/* +** Try to convert z into an unsigned 32-bit integer. Return true on +** success and false if there is an error. +** +** Only decimal notation is accepted. +*/ +int sqlite3GetUInt32(const char *z, u32 *pI){ + u64 v = 0; + int i; + for(i=0; sqlite3Isdigit(z[i]); i++){ + v = v*10 + z[i] - '0'; + if( v>4294967296LL ){ *pI = 0; return 0; } + } + if( i==0 || z[i]!=0 ){ *pI = 0; return 0; } + *pI = (u32)v; + return 1; +} + /* ** The variable-length integer encoding is as follows: ** @@ -1128,8 +1183,7 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ u64 v64; u8 n; - p -= 2; - n = sqlite3GetVarint(p, &v64); + n = sqlite3GetVarint(p-2, &v64); assert( n>3 && n<=9 ); if( (v64 & SQLITE_MAX_U32)!=v64 ){ *v = 0xffffffff; @@ -1256,6 +1310,7 @@ u8 sqlite3HexToInt(int h){ return (u8)(h & 0xf); } +/* BEGIN SQLCIPHER */ #if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* ** Convert a BLOB literal of the form "x'hhhhhh'" into its binary @@ -1278,6 +1333,7 @@ void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ return zBlob; } #endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ +/* END SQLCIPHER */ /* ** Log an error that is an API call on a connection pointer that should diff --git a/src/vacuum.c b/src/vacuum.c index e8555ef..5742078 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -41,7 +41,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 ); /* The secondary SQL must be one of CREATE TABLE, CREATE INDEX, ** or INSERT. Historically there have been attacks that first - ** corrupt the sqlite_master.sql field with other kinds of statements + ** corrupt the sqlite_schema.sql field with other kinds of statements ** then run VACUUM to get those statements to execute at inappropriate ** times. */ if( zSubSql @@ -233,9 +233,10 @@ SQLITE_NOINLINE int sqlite3RunVacuum( } db->mDbFlags |= DBFLAG_VacuumInto; } - nRes = sqlite3BtreeGetOptimalReserve(pMain); + nRes = sqlite3BtreeGetRequestedReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ +/* BEGIN SQLCIPHER */ #ifdef SQLITE_HAS_CODEC if( db->nextPagesize ){ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); @@ -245,6 +246,7 @@ SQLITE_NOINLINE int sqlite3RunVacuum( if( nKey ) db->nextPagesize = 0; } #endif +/* END SQLCIPHER */ sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); @@ -283,14 +285,14 @@ SQLITE_NOINLINE int sqlite3RunVacuum( */ db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */ rc = execSqlF(db, pzErrMsg, - "SELECT sql FROM \"%w\".sqlite_master" + "SELECT sql FROM \"%w\".sqlite_schema" " WHERE type='table'AND name<>'sqlite_sequence'" " AND coalesce(rootpage,1)>0", zDbMain ); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execSqlF(db, pzErrMsg, - "SELECT sql FROM \"%w\".sqlite_master" + "SELECT sql FROM \"%w\".sqlite_schema" " WHERE type='index'", zDbMain ); @@ -304,7 +306,7 @@ SQLITE_NOINLINE int sqlite3RunVacuum( rc = execSqlF(db, pzErrMsg, "SELECT'INSERT INTO vacuum_db.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" - "FROM vacuum_db.sqlite_master " + "FROM vacuum_db.sqlite_schema " "WHERE type='table'AND coalesce(rootpage,1)>0", zDbMain ); @@ -315,11 +317,11 @@ SQLITE_NOINLINE int sqlite3RunVacuum( /* Copy the triggers, views, and virtual tables from the main database ** over to the temporary database. None of these objects has any ** associated storage, so all we have to do is copy their entries - ** from the SQLITE_MASTER table. + ** from the schema table. */ rc = execSqlF(db, pzErrMsg, - "INSERT INTO vacuum_db.sqlite_master" - " SELECT*FROM \"%w\".sqlite_master" + "INSERT INTO vacuum_db.sqlite_schema" + " SELECT*FROM \"%w\".sqlite_schema" " WHERE type IN('view','trigger')" " OR(type='table'AND rootpage=0)", zDbMain @@ -388,7 +390,7 @@ end_of_vacuum: db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; db->mTrace = saved_mTrace; - sqlite3BtreeSetPageSize(pMain, -1, -1, 1); + sqlite3BtreeSetPageSize(pMain, -1, 0, 1); /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file diff --git a/src/vdbe.c b/src/vdbe.c index b9d2d12..4d1e056 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -671,9 +671,9 @@ int sqlite3VdbeExec( u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ int iCompare = 0; /* Result of last comparison */ - unsigned nVmStep = 0; /* Number of virtual machine steps */ + u64 nVmStep = 0; /* Number of virtual machine steps */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK - unsigned nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */ + u64 nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */ #endif Mem *aMem = p->aMem; /* Copy of p->aMem */ Mem *pIn1 = 0; /* 1st input operand */ @@ -693,7 +693,7 @@ int sqlite3VdbeExec( assert( 0 < db->nProgressOps ); nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps); }else{ - nProgressLimit = 0xffffffff; + nProgressLimit = LARGEST_UINT64; } #endif if( p->rc==SQLITE_NOMEM ){ @@ -702,12 +702,14 @@ int sqlite3VdbeExec( goto no_mem; } assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY ); + testcase( p->rc!=SQLITE_OK ); + p->rc = SQLITE_OK; assert( p->bIsReader || p->readOnly!=0 ); p->iCurrentTime = 0; assert( p->explain==0 ); p->pResultSet = 0; db->busyHandler.nBusy = 0; - if( db->u1.isInterrupted ) goto abort_due_to_interrupt; + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; sqlite3VdbeIOTraceSql(p); #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); @@ -891,7 +893,7 @@ jump_to_p2_and_check_for_interrupt: ** checks on every opcode. This helps sqlite3_step() to run about 1.5% ** faster according to "valgrind --tool=cachegrind" */ check_for_interrupt: - if( db->u1.isInterrupted ) goto abort_due_to_interrupt; + if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt; #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Call the progress callback if it is configured and the required number ** of VDBE ops have been executed (either since this invocation of @@ -903,7 +905,7 @@ check_for_interrupt: assert( db->nProgressOps!=0 ); nProgressLimit += db->nProgressOps; if( db->xProgress(db->pProgressArg) ){ - nProgressLimit = 0xffffffff; + nProgressLimit = LARGEST_UINT64; rc = SQLITE_INTERRUPT; goto abort_due_to_error; } @@ -1031,6 +1033,7 @@ case OP_HaltIfNull: { /* in3 */ #endif if( (pIn3->flags & MEM_Null)==0 ) break; /* Fall through into OP_Halt */ + /* no break */ deliberate_fall_through } /* Opcode: Halt P1 P2 * P4 P5 @@ -1201,6 +1204,7 @@ case OP_String8: { /* same as TK_STRING, out2 */ pOp->opcode = OP_String; assert( rc==SQLITE_OK ); /* Fall through to the next case, OP_String */ + /* no break */ deliberate_fall_through } /* Opcode: String P1 P2 P3 P4 P5 @@ -1512,7 +1516,7 @@ case OP_ResultRow: { if( db->mallocFailed ) goto no_mem; if( db->mTrace & SQLITE_TRACE_ROW ){ - db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); + db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); } @@ -1544,7 +1548,6 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; - testcase( pIn1==pIn2 ); testcase( pOut==pIn2 ); assert( pIn1!=pOut ); flags1 = pIn1->flags; @@ -2075,7 +2078,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ if( (flags1 | flags3)&MEM_Str ){ if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn1,0); - testcase( flags3!=pIn3->flags ); + testcase( flags3==pIn3->flags ); flags3 = pIn3->flags; } if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){ @@ -2098,7 +2101,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ sqlite3VdbeMemStringify(pIn1, encoding, 1); testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); - if( pIn1==pIn3 ) flags3 = flags1 | MEM_Str; + if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; } if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn3->flags & MEM_Int ); @@ -2249,10 +2252,10 @@ case OP_Compare: { int p1; int p2; const KeyInfo *pKeyInfo; - int idx; + u32 idx; CollSeq *pColl; /* Collating sequence to use on this term */ int bRev; /* True for DESCENDING sort order */ - int *aPermute; /* The permutation */ + u32 *aPermute; /* The permutation */ if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ aPermute = 0; @@ -2272,7 +2275,7 @@ case OP_Compare: { #ifdef SQLITE_DEBUG if( aPermute ){ int k, mx = 0; - for(k=0; kmx ) mx = aPermute[k]; + for(k=0; k(u32)mx ) mx = aPermute[k]; assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); }else{ @@ -2281,7 +2284,7 @@ case OP_Compare: { } #endif /* SQLITE_DEBUG */ for(i=0; ip1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); - p2 = pOp->p2; + p2 = (u32)pOp->p2; /* If the cursor cache is stale (meaning it is not currently point at ** the correct row) then bring it up-to-date by doing the necessary @@ -2622,7 +2625,7 @@ case OP_Column: { pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pC!=0 ); - assert( p2nField ); + assert( p2<(u32)pC->nField ); aOffset = pC->aOffset; assert( pC->eCurType!=CURTYPE_VTAB ); assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); @@ -2713,7 +2716,7 @@ case OP_Column: { /* Make sure zData points to enough of the record to cover the header. */ if( pC->aRow==0 ){ memset(&sMem, 0, sizeof(sMem)); - rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, 0, aOffset[0], &sMem); + rc = sqlite3VdbeMemFromBtreeZeroOffset(pC->uc.pCursor,aOffset[0],&sMem); if( rc!=SQLITE_OK ) goto abort_due_to_error; zData = (u8*)sMem.z; }else{ @@ -2737,7 +2740,7 @@ case OP_Column: { offset64 += sqlite3VdbeSerialTypeLen(t); } aOffset[++i] = (u32)(offset64 & 0xffffffff); - }while( i<=p2 && zHdrp5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB ); +#endif pRec->uTemp = 10; }else{ pRec->uTemp = 0; @@ -3188,13 +3204,16 @@ case OP_MakeRecord: { break; } -/* Opcode: Count P1 P2 * * * +/* Opcode: Count P1 P2 p3 * * ** Synopsis: r[P2]=count() ** ** Store the number of entries (an integer value) in the table or index -** opened by cursor P1 in register P2 +** opened by cursor P1 in register P2. +** +** If P3==0, then an exact count is obtained, which involves visiting +** every btree page of the table. But if P3 is non-zero, an estimate +** is returned based on the current cursor position. */ -#ifndef SQLITE_OMIT_BTREECOUNT case OP_Count: { /* out2 */ i64 nEntry; BtCursor *pCrsr; @@ -3202,14 +3221,17 @@ case OP_Count: { /* out2 */ assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE ); pCrsr = p->apCsr[pOp->p1]->uc.pCursor; assert( pCrsr ); - nEntry = 0; /* Not needed. Only used to silence a warning. */ - rc = sqlite3BtreeCount(db, pCrsr, &nEntry); - if( rc ) goto abort_due_to_error; + if( pOp->p3 ){ + nEntry = sqlite3BtreeRowCountEst(pCrsr); + }else{ + nEntry = 0; /* Not needed. Only used to silence a warning. */ + rc = sqlite3BtreeCount(db, pCrsr, &nEntry); + if( rc ) goto abort_due_to_error; + } pOut = out2Prerelease(p, pOp); pOut->u.i = nEntry; goto check_for_interrupt; } -#endif /* Opcode: Savepoint P1 * * P4 * ** @@ -3609,7 +3631,7 @@ case OP_ReadCookie: { /* out2 */ break; } -/* Opcode: SetCookie P1 P2 P3 * * +/* Opcode: SetCookie P1 P2 P3 * P5 ** ** Write the integer value P3 into cookie number P2 of database P1. ** P2==1 is the schema version. P2==2 is the database format. @@ -3618,6 +3640,11 @@ case OP_ReadCookie: { /* out2 */ ** database file used to store temporary tables. ** ** A transaction must be started before executing this opcode. +** +** If P2 is the SCHEMA_VERSION cookie (cookie number 1) then the internal +** schema version is set to P3-P5. The "PRAGMA schema_version=N" statement +** has P5 set to 1, so that the internal schema version will be different +** from the database schema version, resulting in a schema reset. */ case OP_SetCookie: { Db *pDb; @@ -3634,7 +3661,7 @@ case OP_SetCookie: { rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ - pDb->pSchema->schema_cookie = pOp->p3; + pDb->pSchema->schema_cookie = pOp->p3 - pOp->p5; db->mDbFlags |= DBFLAG_SchemaChange; }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ @@ -3665,7 +3692,7 @@ case OP_SetCookie: { **
        **
      • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for ** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT -** of OP_SeekLE/OP_IdxGT) +** of OP_SeekLE/OP_IdxLT) **
      ** ** The P4 value may be either an integer (P4_INT32) or a pointer to @@ -3695,7 +3722,7 @@ case OP_SetCookie: { **
        **
      • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for ** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT -** of OP_SeekLE/OP_IdxGT) +** of OP_SeekLE/OP_IdxLT) **
      ** ** See also: OP_OpenRead, OP_OpenWrite @@ -3719,7 +3746,7 @@ case OP_SetCookie: { **
        **
      • 0x02 OPFLAG_SEEKEQ: This cursor will only be used for ** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT -** of OP_SeekLE/OP_IdxGT) +** of OP_SeekLE/OP_IdxLT) **
      • 0x08 OPFLAG_FORDELETE: This cursor is used only to seek ** and subsequently delete entries in an index btree. This is a ** hint to the storage engine that the storage engine is allowed to @@ -3737,7 +3764,7 @@ case OP_SetCookie: { case OP_ReopenIdx: { int nField; KeyInfo *pKeyInfo; - int p2; + u32 p2; int iDb; int wrFlag; Btree *pX; @@ -3768,7 +3795,7 @@ case OP_OpenWrite: nField = 0; pKeyInfo = 0; - p2 = pOp->p2; + p2 = (u32)pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDbnDb ); assert( DbMaskTest(p->btreeMask, iDb) ); @@ -3787,7 +3814,7 @@ case OP_OpenWrite: } if( pOp->p5 & OPFLAG_P2ISREG ){ assert( p2>0 ); - assert( p2<=(p->nMem+1 - p->nCursor) ); + assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); assert( pOp->opcode==OP_OpenWrite ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); @@ -3831,9 +3858,7 @@ open_cursor_set_hints: assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ ); testcase( pOp->p5 & OPFLAG_BULKCSR ); -#ifdef SQLITE_ENABLE_CURSOR_HINTS testcase( pOp->p2 & OPFLAG_SEEKEQ ); -#endif sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); if( rc ) goto abort_due_to_error; @@ -3942,10 +3967,10 @@ case OP_OpenEphemeral: { */ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(pCx->pBtx, (int*)&pCx->pgnoRoot, + rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ - assert( pCx->pgnoRoot==MASTER_ROOT+1 ); + assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); assert( pKeyInfo->db==db ); assert( pKeyInfo->enc==ENC(db) ); rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, @@ -3953,8 +3978,8 @@ case OP_OpenEphemeral: { } pCx->isTable = 0; }else{ - pCx->pgnoRoot = MASTER_ROOT; - rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR, + pCx->pgnoRoot = SCHEMA_ROOT; + rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, 0, pCx->uc.pCursor); pCx->isTable = 1; } @@ -4089,11 +4114,13 @@ case OP_ColumnsUsed: { ** greater than or equal to the key and P2 is not zero, then jump to P2. ** ** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this -** opcode will always land on a record that equally equals the key, or -** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this -** opcode must be followed by an IdxLE opcode with the same arguments. -** The IdxLE opcode will be skipped if this opcode succeeds, but the -** IdxLE opcode will be used on subsequent loop iterations. +** opcode will either land on a record that exactly matches the key, or +** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ, +** this opcode must be followed by an IdxLE opcode with the same arguments. +** The IdxGT opcode will be skipped if this opcode succeeds, but the +** IdxGT opcode will be used on subsequent loop iterations. The +** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this +** is an equality search. ** ** This opcode leaves the cursor configured to move in forward order, ** from the beginning toward the end. In other words, the cursor is @@ -4109,7 +4136,7 @@ case OP_ColumnsUsed: { ** to an SQL index, then P3 is the first in an array of P4 registers ** that are used as an unpacked index key. ** -** Reposition cursor P1 so that it points to the smallest entry that +** Reposition cursor P1 so that it points to the smallest entry that ** is greater than the key value. If there are no records greater than ** the key and P2 is not zero, then jump to P2. ** @@ -4154,11 +4181,13 @@ case OP_ColumnsUsed: { ** configured to use Prev, not Next. ** ** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this -** opcode will always land on a record that equally equals the key, or -** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this -** opcode must be followed by an IdxGE opcode with the same arguments. +** opcode will either land on a record that exactly matches the key, or +** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ, +** this opcode must be followed by an IdxLE opcode with the same arguments. ** The IdxGE opcode will be skipped if this opcode succeeds, but the -** IdxGE opcode will be used on subsequent loop iterations. +** IdxGE opcode will be used on subsequent loop iterations. The +** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this +** is an equality search. ** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ @@ -4195,7 +4224,7 @@ case OP_SeekGT: { /* jump, in3, group */ pC->cacheStatus = CACHE_STALE; if( pC->isTable ){ u16 flags3, newType; - /* The BTREE_SEEK_EQ flag is only set on index cursors */ + /* The OPFLAG_SEEKEQ/BTREE_SEEK_EQ flag is only set on index cursors */ assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 || CORRUPT_DB ); @@ -4254,14 +4283,17 @@ case OP_SeekGT: { /* jump, in3, group */ goto abort_due_to_error; } }else{ - /* For a cursor with the BTREE_SEEK_EQ hint, only the OP_SeekGE and - ** OP_SeekLE opcodes are allowed, and these must be immediately followed - ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key. + /* For a cursor with the OPFLAG_SEEKEQ/BTREE_SEEK_EQ hint, only the + ** OP_SeekGE and OP_SeekLE opcodes are allowed, and these must be + ** immediately followed by an OP_IdxGT or OP_IdxLT opcode, respectively, + ** with the same key. */ if( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){ eqOnly = 1; assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); + assert( pOp->opcode==OP_SeekGE || pOp[1].opcode==OP_IdxLT ); + assert( pOp->opcode==OP_SeekLE || pOp[1].opcode==OP_IdxGT ); assert( pOp[1].p1==pOp[0].p1 ); assert( pOp[1].p2==pOp[0].p2 ); assert( pOp[1].p3==pOp[0].p3 ); @@ -4355,7 +4387,7 @@ seek_not_found: ** Synopsis: seekHit=P2 ** ** Set the seekHit flag on cursor P1 to the value in P2. -* The seekHit flag is used by the IfNoHope opcode. +** The seekHit flag is used by the IfNoHope opcode. ** ** P1 must be a valid b-tree cursor. P2 must be a boolean value, ** either 0 or 1. @@ -4477,6 +4509,7 @@ case OP_IfNoHope: { /* jump, in3 */ assert( pC!=0 ); if( pC->seekHit ) break; /* Fall through into OP_NotFound */ + /* no break */ deliberate_fall_through } case OP_NoConflict: /* jump, in3 */ case OP_NotFound: /* jump, in3 */ @@ -4631,6 +4664,7 @@ case OP_SeekRowid: { /* jump, in3 */ goto notExistsWithKey; } /* Fall through into OP_NotExists */ + /* no break */ deliberate_fall_through case OP_NotExists: /* jump, in3 */ pIn3 = &aMem[pOp->p3]; assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); @@ -5199,17 +5233,13 @@ case OP_RowData: { */ assert( pC->deferredMoveto==0 ); assert( sqlite3BtreeCursorIsValid(pCrsr) ); -#if 0 /* Not required due to the previous to assert() statements */ - rc = sqlite3VdbeCursorMoveto(pC); - if( rc!=SQLITE_OK ) goto abort_due_to_error; -#endif n = sqlite3BtreePayloadSize(pCrsr); if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } testcase( n==0 ); - rc = sqlite3VdbeMemFromBtree(pCrsr, 0, n, pOut); + rc = sqlite3VdbeMemFromBtreeZeroOffset(pCrsr, n, pOut); if( rc ) goto abort_due_to_error; if( !pOp->p3 ) Deephemeralize(pOut); UPDATE_MAX_BLOBSIZE(pOut); @@ -5406,6 +5436,7 @@ case OP_Sort: { /* jump */ #endif p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ + /* no break */ deliberate_fall_through } /* Opcode: Rewind P1 P2 * * * ** @@ -5587,14 +5618,6 @@ next_tail: ** This instruction only works for indices. The equivalent instruction ** for tables is OP_Insert. */ -/* Opcode: SorterInsert P1 P2 * * * -** Synopsis: key=r[P2] -** -** Register P2 holds an SQL index key made using the -** MakeRecord instructions. This opcode writes that key -** into the sorter P1. Data for the entry is nil. -*/ -case OP_SorterInsert: /* in2 */ case OP_IdxInsert: { /* in2 */ VdbeCursor *pC; BtreePayload x; @@ -5603,38 +5626,66 @@ case OP_IdxInsert: { /* in2 */ pC = p->apCsr[pOp->p1]; sqlite3VdbeIncrWriteCounter(p, pC); assert( pC!=0 ); - assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) ); + assert( !isSorter(pC) ); pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - assert( pC->eCurType==CURTYPE_BTREE || pOp->opcode==OP_SorterInsert ); + assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->isTable==0 ); rc = ExpandBlob(pIn2); if( rc ) goto abort_due_to_error; - if( pOp->opcode==OP_SorterInsert ){ - rc = sqlite3VdbeSorterWrite(pC, pIn2); - }else{ - x.nKey = pIn2->n; - x.pKey = pIn2->z; - x.aMem = aMem + pOp->p3; - x.nMem = (u16)pOp->p4.i; - rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, - (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) - ); - assert( pC->deferredMoveto==0 ); - pC->cacheStatus = CACHE_STALE; - } + x.nKey = pIn2->n; + x.pKey = pIn2->z; + x.aMem = aMem + pOp->p3; + x.nMem = (u16)pOp->p4.i; + rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, + (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) + ); + assert( pC->deferredMoveto==0 ); + pC->cacheStatus = CACHE_STALE; if( rc) goto abort_due_to_error; break; } -/* Opcode: IdxDelete P1 P2 P3 * * +/* Opcode: SorterInsert P1 P2 * * * +** Synopsis: key=r[P2] +** +** Register P2 holds an SQL index key made using the +** MakeRecord instructions. This opcode writes that key +** into the sorter P1. Data for the entry is nil. +*/ +case OP_SorterInsert: { /* in2 */ + VdbeCursor *pC; + + assert( pOp->p1>=0 && pOp->p1nCursor ); + pC = p->apCsr[pOp->p1]; + sqlite3VdbeIncrWriteCounter(p, pC); + assert( pC!=0 ); + assert( isSorter(pC) ); + pIn2 = &aMem[pOp->p2]; + assert( pIn2->flags & MEM_Blob ); + assert( pC->isTable==0 ); + rc = ExpandBlob(pIn2); + if( rc ) goto abort_due_to_error; + rc = sqlite3VdbeSorterWrite(pC, pIn2); + if( rc) goto abort_due_to_error; + break; +} + +/* Opcode: IdxDelete P1 P2 P3 * P5 ** Synopsis: key=r[P2@P3] ** ** The content of P3 registers starting at register P2 form ** an unpacked index key. This opcode removes that entry from the ** index opened by cursor P1. +** +** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error +** if no matching index entry is found. This happens when running +** an UPDATE or DELETE statement and the index entry to be updated +** or deleted is not found. For some uses of IdxDelete +** (example: the EXCEPT operator) it does not matter that no matching +** entry is found. For those cases, P5 is zero. */ case OP_IdxDelete: { VdbeCursor *pC; @@ -5651,7 +5702,6 @@ case OP_IdxDelete: { sqlite3VdbeIncrWriteCounter(p, pC); pCrsr = pC->uc.pCursor; assert( pCrsr!=0 ); - assert( pOp->p5==0 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; r.default_rc = 0; @@ -5661,6 +5711,9 @@ case OP_IdxDelete: { if( res==0 ){ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); if( rc ) goto abort_due_to_error; + }else if( pOp->p5 ){ + rc = SQLITE_CORRUPT_INDEX; + goto abort_due_to_error; } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; @@ -5950,7 +6003,7 @@ case OP_Clear: { assert( p->readOnly==0 ); assert( DbMaskTest(p->btreeMask, pOp->p2) ); rc = sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) + db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) ); if( pOp->p3 ){ p->nChange += nChange; @@ -5999,7 +6052,7 @@ case OP_ResetSorter: { ** The root page number of the new b-tree is stored in register P2. */ case OP_CreateBtree: { /* out2 */ - int pgno; + Pgno pgno; Db *pDb; sqlite3VdbeIncrWriteCounter(p, 0); @@ -6032,7 +6085,7 @@ case OP_SqlExec: { /* Opcode: ParseSchema P1 * * P4 * ** -** Read and parse all entries from the SQLITE_MASTER table of database P1 +** Read and parse all entries from the schema table of database P1 ** that match the WHERE clause P4. If P4 is a NULL pointer, then the ** entire schema for P1 is reparsed. ** @@ -6041,7 +6094,7 @@ case OP_SqlExec: { */ case OP_ParseSchema: { int iDb; - const char *zMaster; + const char *zSchema; char *zSql; InitData initData; @@ -6069,14 +6122,15 @@ case OP_ParseSchema: { }else #endif { - zMaster = MASTER_NAME; + zSchema = DFLT_SCHEMA_TABLE; initData.db = db; initData.iDb = iDb; initData.pzErrMsg = &p->zErrMsg; initData.mInitFlags = 0; + initData.mxPage = sqlite3BtreeLastPage(db->aDb[iDb].pBt); zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", - db->aDb[iDb].zDbSName, zMaster, pOp->p4.z); + db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); if( zSql==0 ){ rc = SQLITE_NOMEM_BKPT; }else{ @@ -6090,7 +6144,7 @@ case OP_ParseSchema: { if( rc==SQLITE_OK && initData.nInitRow==0 ){ /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse ** at least one SQL statement. Any less than that indicates that - ** the sqlite_master table is corrupt. */ + ** the sqlite_schema table is corrupt. */ rc = SQLITE_CORRUPT_BKPT; } sqlite3DbFreeNN(db, zSql); @@ -6187,7 +6241,7 @@ case OP_DropTrigger: { */ case OP_IntegrityCk: { int nRoot; /* Number of tables to check. (Number of root pages.) */ - int *aRoot; /* Array of rootpage numbers for tables to be checked */ + Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ @@ -6196,7 +6250,7 @@ case OP_IntegrityCk: { nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); - assert( aRoot[0]==nRoot ); + assert( aRoot[0]==(Pgno)nRoot ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pnErr = &aMem[pOp->p3]; assert( (pnErr->flags & MEM_Int)!=0 ); @@ -6463,7 +6517,7 @@ case OP_Program: { /* jump */ int i; for(i=0; inMem; i++){ aMem[i].pScopyFrom = 0; /* Prevent false-positive AboutToChange() errs */ - aMem[i].flags |= MEM_Undefined; /* Cause a fault if this reg is reused */ + MemSetTypeFlag(&aMem[i], MEM_Undefined); /* Fault if this reg is reused */ } } #endif @@ -6736,6 +6790,7 @@ case OP_AggStep: { pOp->opcode = OP_AggStep1; /* Fall through into OP_AggStep */ + /* no break */ deliberate_fall_through } case OP_AggStep1: { int i; @@ -7574,7 +7629,7 @@ case OP_MaxPgcnt: { /* out2 */ #endif /* Opcode: Function P1 P2 P3 P4 * -** Synopsis: r[P3]=func(r[P2@P5]) +** Synopsis: r[P3]=func(r[P2@NP]) ** ** Invoke a user function (P4 is a pointer to an sqlite3_context object that ** contains a pointer to the function to be run) with arguments taken @@ -7593,7 +7648,7 @@ case OP_MaxPgcnt: { /* out2 */ ** See also: AggStep, AggFinal, PureFunc */ /* Opcode: PureFunc P1 P2 P3 P4 * -** Synopsis: r[P3]=func(r[P2@P5]) +** Synopsis: r[P3]=func(r[P2@NP]) ** ** Invoke a user function (P4 is a pointer to an sqlite3_context object that ** contains a pointer to the function to be run) with arguments taken @@ -7725,18 +7780,17 @@ case OP_Init: { /* jump */ ){ #ifndef SQLITE_OMIT_DEPRECATED if( db->mTrace & SQLITE_TRACE_LEGACY ){ - void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace; char *z = sqlite3VdbeExpandSql(p, zTrace); - x(db->pTraceArg, z); + db->trace.xLegacy(db->pTraceArg, z); sqlite3_free(z); }else #endif if( db->nVdbeExec>1 ){ char *z = sqlite3MPrintf(db, "-- %s", zTrace); - (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, z); + (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z); sqlite3DbFree(db, z); }else{ - (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); + (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); } } #ifdef SQLITE_USE_FCNTL_TRACE @@ -7949,7 +8003,7 @@ vdbe_return: while( nVmStep>=nProgressLimit && db->xProgress!=0 ){ nProgressLimit += db->nProgressOps; if( db->xProgress(db->pProgressArg) ){ - nProgressLimit = 0xffffffff; + nProgressLimit = LARGEST_UINT64; rc = SQLITE_INTERRUPT; goto abort_due_to_error; } @@ -7982,9 +8036,7 @@ no_mem: ** flag. */ abort_due_to_interrupt: - assert( db->u1.isInterrupted ); - rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; - p->rc = rc; - sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); + assert( AtomicLoad(&db->u1.isInterrupted) ); + rc = SQLITE_INTERRUPT; goto abort_due_to_error; } diff --git a/src/vdbe.h b/src/vdbe.h index bfde258..17f11fd 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -57,7 +57,7 @@ struct VdbeOp { Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ - int *ai; /* Used when p4type is P4_INTARRAY */ + u32 *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Table *pTab; /* Used when p4type is P4_TABLE */ #ifdef SQLITE_ENABLE_CURSOR_HINTS @@ -230,6 +230,7 @@ void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); void sqlite3VdbeChangeP5(Vdbe*, u16 P5); void sqlite3VdbeJumpHere(Vdbe*, int addr); +void sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr); int sqlite3VdbeChangeToNoop(Vdbe*, int addr); int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op); #ifdef SQLITE_DEBUG @@ -289,6 +290,9 @@ void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); int sqlite3VdbeHasSubProgram(Vdbe*); int sqlite3NotPureFunc(sqlite3_context*); +#ifdef SQLITE_ENABLE_BYTECODE_VTAB +int sqlite3VdbeBytecodeVtabInit(sqlite3*); +#endif /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on ** each VDBE opcode. diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 9311591..9015697 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -31,7 +31,8 @@ ** "explain" P4 display logic is enabled. */ #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \ - || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) + || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \ + || defined(SQLITE_ENABLE_BYTECODE_VTAB) # define VDBE_DISPLAY_P4 1 #else # define VDBE_DISPLAY_P4 0 @@ -88,7 +89,7 @@ struct VdbeCursor { Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ Btree *pBtx; /* Separate file holding temporary table */ i64 seqCount; /* Sequence counter */ - int *aAltMap; /* Mapping from table to index column numbers */ + u32 *aAltMap; /* Mapping from table to index column numbers */ /* Cached OP_Column parse information is only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of @@ -418,9 +419,9 @@ struct Vdbe { u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ + u8 doingRerun; /* True if rerunning after an auto-reprepare */ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ bft explain:2; /* True if EXPLAIN present on SQL command */ - bft doingRerun:1; /* True if rerunning after an auto-reprepare */ bft changeCntOn:1; /* True to update the change-counter */ bft runOnlyOnce:1; /* Automatically expire on reset */ bft usesStmtJournal:1; /* True if uses a statement journal */ @@ -484,7 +485,7 @@ void sqlite3VdbeError(Vdbe*, const char *, ...); void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); -int sqlite3VdbeCursorMoveto(VdbeCursor**, int*); +int sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); int sqlite3VdbeCursorRestore(VdbeCursor*); u32 sqlite3VdbeSerialTypeLen(u32); u8 sqlite3VdbeOneByteSerialTypeLen(u8); @@ -496,7 +497,14 @@ int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*); int sqlite3VdbeExec(Vdbe*); -#ifndef SQLITE_OMIT_EXPLAIN +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) +int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**); +char *sqlite3VdbeDisplayP4(sqlite3*,Op*); +#endif +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) +char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*); +#endif +#if !defined(SQLITE_OMIT_EXPLAIN) int sqlite3VdbeList(Vdbe*); #endif int sqlite3VdbeHalt(Vdbe*); @@ -532,12 +540,13 @@ int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*); int sqlite3VdbeMemCast(Mem*,u8,u8); int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*); +int sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*); void sqlite3VdbeMemRelease(Mem *p); int sqlite3VdbeMemFinalize(Mem*, FuncDef*); #ifndef SQLITE_OMIT_WINDOWFUNC int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); #endif -#ifndef SQLITE_OMIT_EXPLAIN +#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB) const char *sqlite3OpcodeName(int); #endif int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 074d458..a9cbf92 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -73,7 +73,7 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ } #endif if( db->mTrace & SQLITE_TRACE_PROFILE ){ - db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); + db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); } p->startTime = 0; } @@ -655,6 +655,13 @@ static int sqlite3Step(Vdbe *p){ if( p->pc<0 && p->expired ){ p->rc = SQLITE_SCHEMA; rc = SQLITE_ERROR; + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ + /* If this statement was prepared using saved SQL and an + ** error has occurred, then return the error code in p->rc to the + ** caller. Set the error code in the database handle to the same value. + */ + rc = sqlite3VdbeTransferError(p); + } goto end_of_step; } if( p->pc<0 ){ @@ -663,7 +670,7 @@ static int sqlite3Step(Vdbe *p){ ** from interrupting a statement that has not yet started. */ if( db->nVdbeActive==0 ){ - db->u1.isInterrupted = 0; + AtomicStore(&db->u1.isInterrupted, 0); } assert( db->nVdbeWrite>0 || db->autoCommit==0 @@ -710,35 +717,27 @@ static int sqlite3Step(Vdbe *p){ if( p->rc!=SQLITE_OK ){ rc = SQLITE_ERROR; } + }else if( rc!=SQLITE_DONE && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ + /* If this statement was prepared using saved SQL and an + ** error has occurred, then return the error code in p->rc to the + ** caller. Set the error code in the database handle to the same value. + */ + rc = sqlite3VdbeTransferError(p); } } db->errCode = rc; if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ p->rc = SQLITE_NOMEM_BKPT; + if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ) rc = p->rc; } end_of_step: - /* At this point local variable rc holds the value that should be - ** returned if this statement was compiled using the legacy - ** sqlite3_prepare() interface. According to the docs, this can only - ** be one of the values in the first assert() below. Variable p->rc - ** contains the value that would be returned if sqlite3_finalize() - ** were called on statement p. - */ - assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR + /* There are only a limited number of result codes allowed from the + ** statements prepared using the legacy sqlite3_prepare() interface */ + assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 + || rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE ); - assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); - if( rc!=SQLITE_ROW - && rc!=SQLITE_DONE - && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 - ){ - /* If this statement was prepared using saved SQL and an - ** error has occurred, then return the error code in p->rc to the - ** caller. Set the error code in the database handle to the same value. - */ - rc = sqlite3VdbeTransferError(p); - } return (rc&db->errMask); } @@ -1355,7 +1354,7 @@ static int vdbeUnbind(Vdbe *p, int i){ /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. ** - ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host + ** IMPLEMENTATION-OF: R-57496-20354 If the specific value bound to a host ** parameter in the WHERE clause might influence the choice of query plan ** for a statement, then the statement will be automatically recompiled, ** as if there had been a schema change, on the first sqlite3_step() call diff --git a/src/vdbeaux.c b/src/vdbeaux.c index fab8b70..5f8b8a8 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -424,7 +424,7 @@ void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ #endif /* -** Add a new OP_ opcode. +** Add a new OP_Explain opcode. ** ** If the bPush flag is true, then make this opcode the parent for ** subsequent Explains until sqlite3VdbeExplainPop() is called. @@ -808,7 +808,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ switch( pOp->opcode ){ case OP_Transaction: { if( pOp->p2!=0 ) p->readOnly = 0; - /* fall thru */ + /* no break */ deliberate_fall_through } case OP_AutoCommit: case OP_Savepoint: { @@ -855,6 +855,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ n = pOp[-1].p1; if( n>nMaxArgs ) nMaxArgs = n; /* Fall through into the default case */ + /* no break */ deliberate_fall_through } #endif default: { @@ -1065,6 +1066,34 @@ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ sqlite3VdbeChangeP2(p, addr, p->nOp); } +/* +** Change the P2 operand of the jump instruction at addr so that +** the jump lands on the next opcode. Or if the jump instruction was +** the previous opcode (and is thus a no-op) then simply back up +** the next instruction counter by one slot so that the jump is +** overwritten by the next inserted opcode. +** +** This routine is an optimization of sqlite3VdbeJumpHere() that +** strives to omit useless byte-code like this: +** +** 7 Once 0 8 0 +** 8 ... +*/ +void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){ + if( addr==p->nOp-1 ){ + assert( p->aOp[addr].opcode==OP_Once + || p->aOp[addr].opcode==OP_If + || p->aOp[addr].opcode==OP_FkIfZero ); + assert( p->aOp[addr].p4type==0 ); +#ifdef SQLITE_VDBE_COVERAGE + sqlite3VdbeGetOp(p,-1)->iSrcLine = 0; /* Erase VdbeCoverage() macros */ +#endif + p->nOp--; + }else{ + sqlite3VdbeChangeP2(p, addr, p->nOp); + } +} + /* ** If the input FuncDef structure is ephemeral, then free it. If @@ -1436,17 +1465,19 @@ static int translateP(char c, const Op *pOp){ ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0 ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x */ -static int displayComment( +char *sqlite3VdbeDisplayComment( + sqlite3 *db, /* Optional - Oom error reporting only */ const Op *pOp, /* The opcode to be commented */ - const char *zP4, /* Previously obtained value for P4 */ - char *zTemp, /* Write result here */ - int nTemp /* Space available in zTemp[] */ + const char *zP4 /* Previously obtained value for P4 */ ){ const char *zOpName; const char *zSynopsis; int nOpName; - int ii, jj; + int ii; char zAlt[50]; + StrAccum x; + + sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); zOpName = sqlite3OpcodeName(pOp->opcode); nOpName = sqlite3Strlen30(zOpName); if( zOpName[nOpName+1] ){ @@ -1461,53 +1492,64 @@ static int displayComment( } zSynopsis = zAlt; } - for(ii=jj=0; jjzComment); + sqlite3_str_appendall(&x, pOp->zComment); seenCom = 1; }else{ int v1 = translateP(c, pOp); int v2; - sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1); if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ ii += 3; - jj += sqlite3Strlen30(zTemp+jj); v2 = translateP(zSynopsis[ii], pOp); if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ ii += 2; v2++; } - if( v2>1 ){ - sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1); + if( v2<2 ){ + sqlite3_str_appendf(&x, "%d", v1); + }else{ + sqlite3_str_appendf(&x, "%d..%d", v1, v1+v2-1); + } + }else if( strncmp(zSynopsis+ii+1, "@NP", 3)==0 ){ + sqlite3_context *pCtx = pOp->p4.pCtx; + if( pOp->p4type!=P4_FUNCCTX || pCtx->argc==1 ){ + sqlite3_str_appendf(&x, "%d", v1); + }else if( pCtx->argc>1 ){ + sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1); + }else{ + assert( x.nChar>2 ); + x.nChar -= 2; + ii++; + } + ii += 3; + }else{ + sqlite3_str_appendf(&x, "%d", v1); + if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ + ii += 4; } - }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){ - ii += 4; } } - jj += sqlite3Strlen30(zTemp+jj); }else{ - zTemp[jj++] = c; + sqlite3_str_appendchar(&x, 1, c); } } - if( !seenCom && jjzComment ){ - sqlite3_snprintf(nTemp-jj, zTemp+jj, "; %s", pOp->zComment); - jj += sqlite3Strlen30(zTemp+jj); + if( !seenCom && pOp->zComment ){ + sqlite3_str_appendf(&x, "; %s", pOp->zComment); } - if( jjzComment ){ - sqlite3_snprintf(nTemp, zTemp, "%s", pOp->zComment); - jj = sqlite3Strlen30(zTemp); - }else{ - zTemp[0] = 0; - jj = 0; + sqlite3_str_appendall(&x, pOp->zComment); } - return jj; + if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){ + sqlite3OomFault(db); + } + return sqlite3StrAccumFinish(&x); } -#endif /* SQLITE_DEBUG */ +#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */ #if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) /* @@ -1588,11 +1630,11 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){ ** Compute a string that describes the P4 parameter for an opcode. ** Use zTemp for any required temporary buffer space. */ -static char *displayP4(Op *pOp, char *zTemp, int nTemp){ - char *zP4 = zTemp; +char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ + char *zP4 = 0; StrAccum x; - assert( nTemp>=20 ); - sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0); + + sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH); switch( pOp->p4type ){ case P4_KEYINFO: { int j; @@ -1618,8 +1660,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ } #endif case P4_COLLSEQ: { + static const char *const encnames[] = {"?", "8", "16LE", "16BE"}; CollSeq *pColl = pOp->p4.pColl; - sqlite3_str_appendf(&x, "(%.20s)", pColl->zName); + assert( pColl->enc>=0 && pColl->enc<4 ); + sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName, + encnames[pColl->enc]); break; } case P4_FUNCDEF: { @@ -1668,41 +1713,37 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ } #endif case P4_INTARRAY: { - int i; - int *ai = pOp->p4.ai; - int n = ai[0]; /* The first element of an INTARRAY is always the + u32 i; + u32 *ai = pOp->p4.ai; + u32 n = ai[0]; /* The first element of an INTARRAY is always the ** count of the number of elements to follow */ for(i=1; i<=n; i++){ - sqlite3_str_appendf(&x, ",%d", ai[i]); + sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]); } - zTemp[0] = '['; sqlite3_str_append(&x, "]", 1); break; } case P4_SUBPROGRAM: { - sqlite3_str_appendf(&x, "program"); + zP4 = "program"; break; } case P4_DYNBLOB: case P4_ADVANCE: { - zTemp[0] = 0; break; } case P4_TABLE: { - sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName); + zP4 = pOp->p4.pTab->zName; break; } default: { zP4 = pOp->p4.z; - if( zP4==0 ){ - zP4 = zTemp; - zTemp[0] = 0; - } } } - sqlite3StrAccumFinish(&x); - assert( zP4!=0 ); - return zP4; + if( zP4 ) sqlite3_str_appendall(&x, zP4); + if( (x.accError & SQLITE_NOMEM)!=0 ){ + sqlite3OomFault(db); + } + return sqlite3StrAccumFinish(&x); } #endif /* VDBE_DISPLAY_P4 */ @@ -1792,24 +1833,30 @@ void sqlite3VdbeLeave(Vdbe *p){ */ void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ char *zP4; - char zPtr[50]; - char zCom[100]; + char *zCom; + sqlite3 dummyDb; static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n"; if( pOut==0 ) pOut = stdout; - zP4 = displayP4(pOp, zPtr, sizeof(zPtr)); + sqlite3BeginBenignMalloc(); + dummyDb.mallocFailed = 1; + zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - displayComment(pOp, zP4, zCom, sizeof(zCom)); + zCom = sqlite3VdbeDisplayComment(0, pOp, zP4); #else - zCom[0] = 0; + zCom = 0; #endif /* NB: The sqlite3OpcodeName() function is implemented by code created ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the ** information from the vdbe.c source text */ fprintf(pOut, zFormat1, pc, - sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5, - zCom + sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, + zP4 ? zP4 : "", pOp->p5, + zCom ? zCom : "" ); fflush(pOut); + sqlite3_free(zP4); + sqlite3_free(zCom); + sqlite3EndBenignMalloc(); } #endif @@ -1900,6 +1947,121 @@ void sqlite3VdbeFrameMemDel(void *pArg){ pFrame->v->pDelFrame = pFrame; } +#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN) +/* +** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN +** QUERY PLAN output. +** +** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no +** more opcodes to be displayed. +*/ +int sqlite3VdbeNextOpcode( + Vdbe *p, /* The statement being explained */ + Mem *pSub, /* Storage for keeping track of subprogram nesting */ + int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */ + int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */ + int *piAddr, /* OUT: Write index into (*paOp)[] here */ + Op **paOp /* OUT: Write the opcode array here */ +){ + int nRow; /* Stop when row count reaches this */ + int nSub = 0; /* Number of sub-vdbes seen so far */ + SubProgram **apSub = 0; /* Array of sub-vdbes */ + int i; /* Next instruction address */ + int rc = SQLITE_OK; /* Result code */ + Op *aOp = 0; /* Opcode array */ + int iPc; /* Rowid. Copy of value in *piPc */ + + /* When the number of output rows reaches nRow, that means the + ** listing has finished and sqlite3_step() should return SQLITE_DONE. + ** nRow is the sum of the number of rows in the main program, plus + ** the sum of the number of rows in all trigger subprograms encountered + ** so far. The nRow value will increase as new trigger subprograms are + ** encountered, but p->pc will eventually catch up to nRow. + */ + nRow = p->nOp; + if( pSub!=0 ){ + if( pSub->flags&MEM_Blob ){ + /* pSub is initiallly NULL. It is initialized to a BLOB by + ** the P4_SUBPROGRAM processing logic below */ + nSub = pSub->n/sizeof(Vdbe*); + apSub = (SubProgram **)pSub->z; + } + for(i=0; inOp; + } + } + iPc = *piPc; + while(1){ /* Loop exits via break */ + i = iPc++; + if( i>=nRow ){ + p->rc = SQLITE_OK; + rc = SQLITE_DONE; + break; + } + if( inOp ){ + /* The rowid is small enough that we are still in the + ** main program. */ + aOp = p->aOp; + }else{ + /* We are currently listing subprograms. Figure out which one and + ** pick up the appropriate opcode. */ + int j; + i -= p->nOp; + assert( apSub!=0 ); + assert( nSub>0 ); + for(j=0; i>=apSub[j]->nOp; j++){ + i -= apSub[j]->nOp; + assert( inOp || j+1aOp; + } + + /* When an OP_Program opcode is encounter (the only opcode that has + ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms + ** kept in p->aMem[9].z to hold the new program - assuming this subprogram + ** has not already been seen. + */ + if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){ + int nByte = (nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jrc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); + if( p->rc!=SQLITE_OK ){ + rc = SQLITE_ERROR; + break; + } + apSub = (SubProgram **)pSub->z; + apSub[nSub++] = aOp[i].p4.pProgram; + MemSetTypeFlag(pSub, MEM_Blob); + pSub->n = nSub*sizeof(SubProgram*); + nRow += aOp[i].p4.pProgram->nOp; + } + } + if( eMode==0 ) break; +#ifdef SQLITE_ENABLE_BYTECODE_VTAB + if( eMode==2 ){ + Op *pOp = aOp + i; + if( pOp->opcode==OP_OpenRead ) break; + if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break; + if( pOp->opcode==OP_ReopenIdx ) break; + }else +#endif + { + assert( eMode==1 ); + if( aOp[i].opcode==OP_Explain ) break; + if( aOp[i].opcode==OP_Init && iPc>1 ) break; + } + } + *piPc = iPc; + *piAddr = i; + *paOp = aOp; + return rc; +} +#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */ + /* ** Delete a VdbeFrame object and its contents. VdbeFrame objects are @@ -1940,16 +2102,14 @@ void sqlite3VdbeFrameDelete(VdbeFrame *p){ int sqlite3VdbeList( Vdbe *p /* The VDBE */ ){ - int nRow; /* Stop when row count reaches this */ - int nSub = 0; /* Number of sub-vdbes seen so far */ - SubProgram **apSub = 0; /* Array of sub-vdbes */ Mem *pSub = 0; /* Memory cell hold array of subprogs */ sqlite3 *db = p->db; /* The database connection */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ Mem *pMem = &p->aMem[1]; /* First Mem of result set */ int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0); - Op *pOp = 0; + Op *aOp; /* Array of opcodes */ + Op *pOp; /* Current opcode */ assert( p->explain ); assert( p->magic==VDBE_MAGIC_RUN ); @@ -1969,14 +2129,6 @@ int sqlite3VdbeList( return SQLITE_ERROR; } - /* When the number of output rows reaches nRow, that means the - ** listing has finished and sqlite3_step() should return SQLITE_DONE. - ** nRow is the sum of the number of rows in the main program, plus - ** the sum of the number of rows in all trigger subprograms encountered - ** so far. The nRow value will increase as new trigger subprograms are - ** encountered, but p->pc will eventually catch up to nRow. - */ - nRow = p->nOp; if( bListSubprogs ){ /* The first 8 memory cells are used for the result set. So we will ** commandeer the 9th cell to use as storage for an array of pointers @@ -1984,147 +2136,55 @@ int sqlite3VdbeList( ** cells. */ assert( p->nMem>9 ); pSub = &p->aMem[9]; - if( pSub->flags&MEM_Blob ){ - /* On the first call to sqlite3_step(), pSub will hold a NULL. It is - ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */ - nSub = pSub->n/sizeof(Vdbe*); - apSub = (SubProgram **)pSub->z; - } - for(i=0; inOp; - } + }else{ + pSub = 0; } - while(1){ /* Loop exits via break */ - i = p->pc++; - if( i>=nRow ){ - p->rc = SQLITE_OK; - rc = SQLITE_DONE; - break; - } - if( inOp ){ - /* The output line number is small enough that we are still in the - ** main program. */ - pOp = &p->aOp[i]; - }else{ - /* We are currently listing subprograms. Figure out which one and - ** pick up the appropriate opcode. */ - int j; - i -= p->nOp; - assert( apSub!=0 ); - assert( nSub>0 ); - for(j=0; i>=apSub[j]->nOp; j++){ - i -= apSub[j]->nOp; - assert( inOp || j+1aOp[i]; - } - - /* When an OP_Program opcode is encounter (the only opcode that has - ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms - ** kept in p->aMem[9].z to hold the new program - assuming this subprogram - ** has not already been seen. - */ - if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){ - int nByte = (nSub+1)*sizeof(SubProgram*); - int j; - for(j=0; jp4.pProgram ) break; - } - if( j==nSub ){ - p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; - break; - } - apSub = (SubProgram **)pSub->z; - apSub[nSub++] = pOp->p4.pProgram; - pSub->flags |= MEM_Blob; - pSub->n = nSub*sizeof(SubProgram*); - nRow += pOp->p4.pProgram->nOp; - } - } - if( p->explain<2 ) break; - if( pOp->opcode==OP_Explain ) break; - if( pOp->opcode==OP_Init && p->pc>1 ) break; - } + /* Figure out which opcode is next to display */ + rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp); if( rc==SQLITE_OK ){ - if( db->u1.isInterrupted ){ + pOp = aOp + i; + if( AtomicLoad(&db->u1.isInterrupted) ){ p->rc = SQLITE_INTERRUPT; rc = SQLITE_ERROR; sqlite3VdbeError(p, sqlite3ErrStr(p->rc)); }else{ - char *zP4; - if( p->explain==1 ){ - pMem->flags = MEM_Int; - pMem->u.i = i; /* Program counter */ - pMem++; - - pMem->flags = MEM_Static|MEM_Str|MEM_Term; - pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ - assert( pMem->z!=0 ); - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - pMem++; - } - - pMem->flags = MEM_Int; - pMem->u.i = pOp->p1; /* P1 */ - pMem++; - - pMem->flags = MEM_Int; - pMem->u.i = pOp->p2; /* P2 */ - pMem++; - - pMem->flags = MEM_Int; - pMem->u.i = pOp->p3; /* P3 */ - pMem++; - - if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; - } - pMem->flags = MEM_Str|MEM_Term; - zP4 = displayP4(pOp, pMem->z, pMem->szMalloc); - if( zP4!=pMem->z ){ - pMem->n = 0; - sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); + char *zP4 = sqlite3VdbeDisplayP4(db, pOp); + if( p->explain==2 ){ + sqlite3VdbeMemSetInt64(pMem, pOp->p1); + sqlite3VdbeMemSetInt64(pMem+1, pOp->p2); + sqlite3VdbeMemSetInt64(pMem+2, pOp->p3); + sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free); + p->nResColumn = 4; }else{ - assert( pMem->z!=0 ); - pMem->n = sqlite3Strlen30(pMem->z); - pMem->enc = SQLITE_UTF8; - } - pMem++; - - if( p->explain==1 ){ - if( sqlite3VdbeMemClearAndResize(pMem, 4) ){ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; - } - pMem->flags = MEM_Str|MEM_Term; - pMem->n = 2; - sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ - pMem->enc = SQLITE_UTF8; - pMem++; - + sqlite3VdbeMemSetInt64(pMem+0, i); + sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode), + -1, SQLITE_UTF8, SQLITE_STATIC); + sqlite3VdbeMemSetInt64(pMem+2, pOp->p1); + sqlite3VdbeMemSetInt64(pMem+3, pOp->p2); + sqlite3VdbeMemSetInt64(pMem+4, pOp->p3); + /* pMem+5 for p4 is done last */ + sqlite3VdbeMemSetInt64(pMem+6, pOp->p5); #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS - if( sqlite3VdbeMemClearAndResize(pMem, 500) ){ - assert( p->db->mallocFailed ); - return SQLITE_ERROR; + { + char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4); + sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free); } - pMem->flags = MEM_Str|MEM_Term; - pMem->n = displayComment(pOp, zP4, pMem->z, 500); - pMem->enc = SQLITE_UTF8; #else - pMem->flags = MEM_Null; /* Comment */ + sqlite3VdbeMemSetNull(pMem+7); #endif + sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free); + p->nResColumn = 8; + } + p->pResultSet = pMem; + if( db->mallocFailed ){ + p->rc = SQLITE_NOMEM; + rc = SQLITE_ERROR; + }else{ + p->rc = SQLITE_OK; + rc = SQLITE_ROW; } - - p->nResColumn = 8 - 4*(p->explain-1); - p->pResultSet = &p->aMem[1]; - p->rc = SQLITE_OK; - rc = SQLITE_ROW; } } return rc; @@ -2332,6 +2392,7 @@ void sqlite3VdbeMakeReady( }; int iFirst, mx, i; if( nMem<10 ) nMem = 10; + p->explain = pParse->explain; if( pParse->explain==2 ){ sqlite3VdbeSetNumCols(p, 4); iFirst = 8; @@ -2382,7 +2443,6 @@ void sqlite3VdbeMakeReady( p->pVList = pParse->pVList; pParse->pVList = 0; - p->explain = pParse->explain; if( db->mallocFailed ){ p->nVar = 0; p->nCursor = 0; @@ -2570,13 +2630,13 @@ int sqlite3VdbeSetColName( ** A read or write transaction may or may not be active on database handle ** db. If a transaction is active, commit it. If there is a ** write-transaction spanning more than one database file, this routine -** takes care of the master journal trickery. +** takes care of the super-journal trickery. */ static int vdbeCommit(sqlite3 *db, Vdbe *p){ int i; int nTrans = 0; /* Number of databases with an active write-transaction ** that are candidates for a two-phase commit using a - ** master-journal */ + ** super-journal */ int rc = SQLITE_OK; int needXcommit = 0; @@ -2589,7 +2649,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* Before doing anything else, call the xSync() callback for any ** virtual module tables written in this transaction. This has to - ** be done before determining whether a master journal file is + ** be done before determining whether a super-journal file is ** required, as an xSync() callback may add an attached database ** to the transaction. */ @@ -2598,15 +2658,15 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* This loop determines (a) if the commit hook should be invoked and ** (b) how many database files have open write transactions, not ** including the temp database. (b) is important because if more than - ** one database file has an open write transaction, a master journal + ** one database file has an open write transaction, a super-journal ** file is required for an atomic commit. */ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ - /* Whether or not a database might need a master journal depends upon + /* Whether or not a database might need a super-journal depends upon ** its journal mode (among other things). This matrix determines which - ** journal modes use a master journal and which do not */ + ** journal modes use a super-journal and which do not */ static const u8 aMJNeeded[] = { /* DELETE */ 1, /* PERSIST */ 1, @@ -2644,7 +2704,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* The simple case - no more than one database file (not counting the ** TEMP database) has a transaction active. There is no need for the - ** master-journal. + ** super-journal. ** ** If the return value of sqlite3BtreeGetFilename() is a zero length ** string, it means the main database is :memory: or a temp file. In @@ -2678,61 +2738,62 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ } /* The complex case - There is a multi-file write-transaction active. - ** This requires a master journal file to ensure the transaction is + ** This requires a super-journal file to ensure the transaction is ** committed atomically. */ #ifndef SQLITE_OMIT_DISKIO else{ sqlite3_vfs *pVfs = db->pVfs; - char *zMaster = 0; /* File-name for the master journal */ + char *zSuper = 0; /* File-name for the super-journal */ char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); - sqlite3_file *pMaster = 0; + sqlite3_file *pSuperJrnl = 0; i64 offset = 0; int res; int retryCount = 0; int nMainFile; - /* Select a master journal file name */ + /* Select a super-journal file name */ nMainFile = sqlite3Strlen30(zMainFile); - zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz%c%c", zMainFile, 0, 0); - if( zMaster==0 ) return SQLITE_NOMEM_BKPT; + zSuper = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0); + if( zSuper==0 ) return SQLITE_NOMEM_BKPT; + zSuper += 4; do { u32 iRandom; if( retryCount ){ if( retryCount>100 ){ - sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster); - sqlite3OsDelete(pVfs, zMaster, 0); + sqlite3_log(SQLITE_FULL, "MJ delete: %s", zSuper); + sqlite3OsDelete(pVfs, zSuper, 0); break; }else if( retryCount==1 ){ - sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster); + sqlite3_log(SQLITE_FULL, "MJ collide: %s", zSuper); } } retryCount++; sqlite3_randomness(sizeof(iRandom), &iRandom); - sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X", + sqlite3_snprintf(13, &zSuper[nMainFile], "-mj%06X9%02X", (iRandom>>8)&0xffffff, iRandom&0xff); - /* The antipenultimate character of the master journal name must + /* The antipenultimate character of the super-journal name must ** be "9" to avoid name collisions when using 8+3 filenames. */ - assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' ); - sqlite3FileSuffix3(zMainFile, zMaster); - rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); + assert( zSuper[sqlite3Strlen30(zSuper)-3]=='9' ); + sqlite3FileSuffix3(zMainFile, zSuper); + rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); }while( rc==SQLITE_OK && res ); if( rc==SQLITE_OK ){ - /* Open the master journal. */ - rc = sqlite3OsOpenMalloc(pVfs, zMaster, &pMaster, + /* Open the super-journal. */ + rc = sqlite3OsOpenMalloc(pVfs, zSuper, &pSuperJrnl, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE| - SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0 + SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_SUPER_JOURNAL, 0 ); } if( rc!=SQLITE_OK ){ - sqlite3DbFree(db, zMaster); + sqlite3DbFree(db, zSuper-4); return rc; } /* Write the name of each database file in the transaction into the new - ** master journal file. If an error occurs at this point close - ** and delete the master journal file. All the individual journal files - ** still have 'null' as the master journal pointer, so they will roll + ** super-journal file. If an error occurs at this point close + ** and delete the super-journal file. All the individual journal files + ** still have 'null' as the super-journal pointer, so they will roll ** back independently if a failure occurs. */ for(i=0; inDb; i++){ @@ -2743,59 +2804,59 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ continue; /* Ignore TEMP and :memory: databases */ } assert( zFile[0]!=0 ); - rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); + rc = sqlite3OsWrite(pSuperJrnl, zFile, sqlite3Strlen30(zFile)+1,offset); offset += sqlite3Strlen30(zFile)+1; if( rc!=SQLITE_OK ){ - sqlite3OsCloseFree(pMaster); - sqlite3OsDelete(pVfs, zMaster, 0); - sqlite3DbFree(db, zMaster); + sqlite3OsCloseFree(pSuperJrnl); + sqlite3OsDelete(pVfs, zSuper, 0); + sqlite3DbFree(db, zSuper-4); return rc; } } } - /* Sync the master journal file. If the IOCAP_SEQUENTIAL device + /* Sync the super-journal file. If the IOCAP_SEQUENTIAL device ** flag is set this is not required. */ - if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) - && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) + if( 0==(sqlite3OsDeviceCharacteristics(pSuperJrnl)&SQLITE_IOCAP_SEQUENTIAL) + && SQLITE_OK!=(rc = sqlite3OsSync(pSuperJrnl, SQLITE_SYNC_NORMAL)) ){ - sqlite3OsCloseFree(pMaster); - sqlite3OsDelete(pVfs, zMaster, 0); - sqlite3DbFree(db, zMaster); + sqlite3OsCloseFree(pSuperJrnl); + sqlite3OsDelete(pVfs, zSuper, 0); + sqlite3DbFree(db, zSuper-4); return rc; } /* Sync all the db files involved in the transaction. The same call - ** sets the master journal pointer in each individual journal. If - ** an error occurs here, do not delete the master journal file. + ** sets the super-journal pointer in each individual journal. If + ** an error occurs here, do not delete the super-journal file. ** ** If the error occurs during the first call to ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the - ** master journal file will be orphaned. But we cannot delete it, - ** in case the master journal file name was written into the journal + ** super-journal file will be orphaned. But we cannot delete it, + ** in case the super-journal file name was written into the journal ** file before the failure occurred. */ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster); + rc = sqlite3BtreeCommitPhaseOne(pBt, zSuper); } } - sqlite3OsCloseFree(pMaster); + sqlite3OsCloseFree(pSuperJrnl); assert( rc!=SQLITE_BUSY ); if( rc!=SQLITE_OK ){ - sqlite3DbFree(db, zMaster); + sqlite3DbFree(db, zSuper-4); return rc; } - /* Delete the master journal file. This commits the transaction. After + /* Delete the super-journal file. This commits the transaction. After ** doing this the directory is synced again before any individual ** transaction files are deleted. */ - rc = sqlite3OsDelete(pVfs, zMaster, 1); - sqlite3DbFree(db, zMaster); - zMaster = 0; + rc = sqlite3OsDelete(pVfs, zSuper, 1); + sqlite3DbFree(db, zSuper-4); + zSuper = 0; if( rc ){ return rc; } @@ -3242,7 +3303,11 @@ int sqlite3VdbeReset(Vdbe *p){ */ if( p->pc>=0 ){ vdbeInvokeSqllog(p); - sqlite3VdbeTransferError(p); + if( db->pErr || p->zErrMsg ){ + sqlite3VdbeTransferError(p); + }else{ + db->errCode = p->rc; + } if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call @@ -3262,8 +3327,10 @@ int sqlite3VdbeReset(Vdbe *p){ for(i=0; inMem; i++) assert( p->aMem[i].flags==MEM_Undefined ); } #endif - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; + if( p->zErrMsg ){ + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; + } p->pResultSet = 0; #ifdef SQLITE_DEBUG p->nWrite = 0; @@ -3495,12 +3562,12 @@ int sqlite3VdbeCursorRestore(VdbeCursor *p){ ** If the cursor is already pointing to the correct row and that row has ** not been deleted out from under the cursor, then this routine is a no-op. */ -int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ +int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ VdbeCursor *p = *pp; assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); if( p->deferredMoveto ){ - int iMap; - if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){ + u32 iMap; + if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ *pp = p->pAltCursor; *piCol = iMap - 1; return SQLITE_OK; @@ -4503,7 +4570,7 @@ int sqlite3VdbeRecordCompareWithSkip( /* RHS is a string */ else if( pRhs->flags & MEM_Str ){ - getVarint32(&aKey1[idx1], serial_type); + getVarint32NR(&aKey1[idx1], serial_type); testcase( serial_type==12 ); if( serial_type<12 ){ rc = -1; @@ -4537,7 +4604,7 @@ int sqlite3VdbeRecordCompareWithSkip( /* RHS is a blob */ else if( pRhs->flags & MEM_Blob ){ assert( (pRhs->flags & MEM_Zero)==0 || pRhs->n==0 ); - getVarint32(&aKey1[idx1], serial_type); + getVarint32NR(&aKey1[idx1], serial_type); testcase( serial_type==12 ); if( serial_type<12 || (serial_type & 0x01) ){ rc = -1; @@ -4726,7 +4793,10 @@ static int vdbeRecordCompareString( assert( pPKey2->aMem[0].flags & MEM_Str ); vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); - getVarint32(&aKey1[1], serial_type); + serial_type = (u8)(aKey1[1]); + if( serial_type >= 0x80 ){ + sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type); + } if( serial_type<12 ){ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ }else if( !(serial_type & 0x01) ){ @@ -4847,13 +4917,13 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ /* Read in the complete content of the index entry */ sqlite3VdbeMemInit(&m, db, 0); - rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m); + rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); if( rc ){ return rc; } /* The index entry must begin with a header size */ - (void)getVarint32((u8*)m.z, szHdr); + getVarint32NR((u8*)m.z, szHdr); testcase( szHdr==3 ); testcase( szHdr==m.n ); testcase( szHdr>0x7fffffff ); @@ -4864,7 +4934,7 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ /* The last field of the index should be an integer - the ROWID. ** Verify that the last entry really is an integer. */ - (void)getVarint32((u8*)&m.z[szHdr-1], typeRowid); + getVarint32NR((u8*)&m.z[szHdr-1], typeRowid); testcase( typeRowid==1 ); testcase( typeRowid==2 ); testcase( typeRowid==3 ); @@ -4929,7 +4999,7 @@ int sqlite3VdbeIdxKeyCompare( return SQLITE_CORRUPT_BKPT; } sqlite3VdbeMemInit(&m, db, 0); - rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m); + rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m); if( rc ){ return rc; } diff --git a/src/vdbemem.c b/src/vdbemem.c index ddb6b2c..384eacc 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -104,16 +104,25 @@ int sqlite3VdbeCheckMemInvariants(Mem *p){ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ StrAccum acc; assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); - sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); + assert( sz>22 ); if( p->flags & MEM_Int ){ - sqlite3_str_appendf(&acc, "%lld", p->u.i); - }else if( p->flags & MEM_IntReal ){ - sqlite3_str_appendf(&acc, "%!.15g", (double)p->u.i); +#if GCC_VERSION>=7000000 + /* Work-around for GCC bug + ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ + i64 x; + assert( (p->flags&MEM_Int)*2==sizeof(x) ); + memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); + sqlite3Int64ToText(x, zBuf); +#else + sqlite3Int64ToText(p->u.i, zBuf); +#endif }else{ - sqlite3_str_appendf(&acc, "%!.15g", p->u.r); + sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); + sqlite3_str_appendf(&acc, "%!.15g", + (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); + assert( acc.zText==zBuf && acc.mxAlloc<=0 ); + zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ } - assert( acc.zText==zBuf && acc.mxAlloc<=0 ); - zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ } #ifdef SQLITE_DEBUG @@ -961,7 +970,7 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); } - /* If pX is marked as a shallow copy of pMem, then verify that + /* If pX is marked as a shallow copy of pMem, then try to verify that ** no significant changes have been made to pX since the OP_SCopy. ** A significant change would indicated a missed call to this ** function for pX. Minor changes, such as adding or removing a @@ -969,11 +978,6 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ ** same. */ mFlags = pMem->flags & pX->flags & pX->mScopyFlags; assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); - /* assert( (mFlags&MEM_Real)==0 || pMem->u.r==pX->u.r ); */ - /* ^^ */ - /* Cannot reliably compare doubles for equality */ - assert( (mFlags&MEM_Str)==0 || (pMem->n==pX->n && pMem->z==pX->z) ); - assert( (mFlags&MEM_Blob)==0 || sqlite3BlobCompare(pMem,pX)==0 ); /* pMem is the register that is changing. But also mark pX as ** undefined so that we can quickly detect the shallow-copy error */ @@ -1169,7 +1173,7 @@ int sqlite3VdbeMemSetStr( ** If this routine fails for any reason (malloc returns NULL or unable ** to read from the disk) then the pMem is left in an inconsistent state. */ -static SQLITE_NOINLINE int vdbeMemFromBtreeResize( +int sqlite3VdbeMemFromBtree( BtCursor *pCur, /* Cursor pointing at record to retrieve. */ u32 offset, /* Offset from the start of data to return bytes from. */ u32 amt, /* Number of bytes to return. */ @@ -1192,13 +1196,11 @@ static SQLITE_NOINLINE int vdbeMemFromBtreeResize( } return rc; } -int sqlite3VdbeMemFromBtree( +int sqlite3VdbeMemFromBtreeZeroOffset( BtCursor *pCur, /* Cursor pointing at record to retrieve. */ - u32 offset, /* Offset from the start of data to return bytes from. */ u32 amt, /* Number of bytes to return. */ Mem *pMem /* OUT: Return data in this Mem structure. */ ){ - char *zData; /* Data from the btree layer */ u32 available = 0; /* Number of bytes available on the local btree page */ int rc = SQLITE_OK; /* Return code */ @@ -1208,15 +1210,14 @@ int sqlite3VdbeMemFromBtree( /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() ** that both the BtShared and database handle mutexes are held. */ assert( !sqlite3VdbeMemIsRowSet(pMem) ); - zData = (char *)sqlite3BtreePayloadFetch(pCur, &available); - assert( zData!=0 ); + pMem->z = (char *)sqlite3BtreePayloadFetch(pCur, &available); + assert( pMem->z!=0 ); - if( offset+amt<=available ){ - pMem->z = &zData[offset]; + if( amt<=available ){ pMem->flags = MEM_Blob|MEM_Ephem; pMem->n = (int)amt; }else{ - rc = vdbeMemFromBtreeResize(pCur, offset, amt, pMem); + rc = sqlite3VdbeMemFromBtree(pCur, 0, amt, pMem); } return rc; diff --git a/src/vdbesort.c b/src/vdbesort.c index ad93489..777c205 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -815,8 +815,8 @@ static int vdbeSorterCompareText( int n2; int res; - getVarint32(&p1[1], n1); - getVarint32(&p2[1], n2); + getVarint32NR(&p1[1], n1); + getVarint32NR(&p2[1], n2); res = memcmp(v1, v2, (MIN(n1, n2) - 13)/2); if( res==0 ){ res = n1 - n2; @@ -1773,7 +1773,7 @@ int sqlite3VdbeSorterWrite( assert( pCsr->eCurType==CURTYPE_SORTER ); pSorter = pCsr->uc.pSorter; - getVarint32((const u8*)&pVal->z[1], t); + getVarint32NR((const u8*)&pVal->z[1], t); if( t>0 && t<10 && t!=7 ){ pSorter->typeMask &= SORTER_TYPE_INTEGER; }else if( t>10 && (t & 0x01) ){ diff --git a/src/vdbevtab.c b/src/vdbevtab.c new file mode 100644 index 0000000..e9bafd4 --- /dev/null +++ b/src/vdbevtab.c @@ -0,0 +1,424 @@ +/* +** 2020-03-23 +** +** 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 implements virtual-tables for examining the bytecode content +** of a prepared statement. +*/ +#include "sqliteInt.h" +#if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE) +#include "vdbeInt.h" + +/* An instance of the bytecode() table-valued function. +*/ +typedef struct bytecodevtab bytecodevtab; +struct bytecodevtab { + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database connection */ + int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */ +}; + +/* A cursor for scanning through the bytecode +*/ +typedef struct bytecodevtab_cursor bytecodevtab_cursor; +struct bytecodevtab_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */ + int iRowid; /* The rowid of the output table */ + int iAddr; /* Address */ + int needFinalize; /* Cursors owns pStmt and must finalize it */ + int showSubprograms; /* Provide a listing of subprograms */ + Op *aOp; /* Operand array */ + char *zP4; /* Rendered P4 value */ + const char *zType; /* tables_used.type */ + const char *zSchema; /* tables_used.schema */ + const char *zName; /* tables_used.name */ + Mem sub; /* Subprograms */ +}; + +/* +** Create a new bytecode() table-valued function. +*/ +static int bytecodevtabConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + bytecodevtab *pNew; + int rc; + int isTabUsed = pAux!=0; + const char *azSchema[2] = { + /* bytecode() schema */ + "CREATE TABLE x(" + "addr INT," + "opcode TEXT," + "p1 INT," + "p2 INT," + "p3 INT," + "p4 TEXT," + "p5 INT," + "comment TEXT," + "subprog TEXT," + "stmt HIDDEN" + ");", + + /* Tables_used() schema */ + "CREATE TABLE x(" + "type TEXT," + "schema TEXT," + "name TEXT," + "wr INT," + "subprog TEXT," + "stmt HIDDEN" + ");" + }; + + rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]); + if( rc==SQLITE_OK ){ + pNew = sqlite3_malloc( sizeof(*pNew) ); + *ppVtab = (sqlite3_vtab*)pNew; + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + pNew->db = db; + pNew->bTablesUsed = isTabUsed*2; + } + return rc; +} + +/* +** This method is the destructor for bytecodevtab objects. +*/ +static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){ + bytecodevtab *p = (bytecodevtab*)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Constructor for a new bytecodevtab_cursor object. +*/ +static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + bytecodevtab *pVTab = (bytecodevtab*)p; + bytecodevtab_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Clear all internal content from a bytecodevtab cursor. +*/ +static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){ + sqlite3_free(pCur->zP4); + pCur->zP4 = 0; + sqlite3VdbeMemRelease(&pCur->sub); + sqlite3VdbeMemSetNull(&pCur->sub); + if( pCur->needFinalize ){ + sqlite3_finalize(pCur->pStmt); + } + pCur->pStmt = 0; + pCur->needFinalize = 0; + pCur->zType = 0; + pCur->zSchema = 0; + pCur->zName = 0; +} + +/* +** Destructor for a bytecodevtab_cursor. +*/ +static int bytecodevtabClose(sqlite3_vtab_cursor *cur){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + bytecodevtabCursorClear(pCur); + sqlite3_free(pCur); + return SQLITE_OK; +} + + +/* +** Advance a bytecodevtab_cursor to its next row of output. +*/ +static int bytecodevtabNext(sqlite3_vtab_cursor *cur){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + bytecodevtab *pTab = (bytecodevtab*)cur->pVtab; + int rc; + if( pCur->zP4 ){ + sqlite3_free(pCur->zP4); + pCur->zP4 = 0; + } + if( pCur->zName ){ + pCur->zName = 0; + pCur->zType = 0; + pCur->zSchema = 0; + } + rc = sqlite3VdbeNextOpcode( + (Vdbe*)pCur->pStmt, + pCur->showSubprograms ? &pCur->sub : 0, + pTab->bTablesUsed, + &pCur->iRowid, + &pCur->iAddr, + &pCur->aOp); + if( rc!=SQLITE_OK ){ + sqlite3VdbeMemSetNull(&pCur->sub); + pCur->aOp = 0; + } + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int bytecodevtabEof(sqlite3_vtab_cursor *cur){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + return pCur->aOp==0; +} + +/* +** Return values of columns for the row at which the bytecodevtab_cursor +** is currently pointing. +*/ +static int bytecodevtabColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab; + Op *pOp = pCur->aOp + pCur->iAddr; + if( pVTab->bTablesUsed ){ + if( i==4 ){ + i = 8; + }else{ + if( i<=2 && pCur->zType==0 ){ + Schema *pSchema; + HashElem *k; + int iDb = pOp->p3; + Pgno iRoot = (Pgno)pOp->p2; + sqlite3 *db = pVTab->db; + pSchema = db->aDb[iDb].pSchema; + pCur->zSchema = db->aDb[iDb].zDbSName; + for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ + Table *pTab = (Table*)sqliteHashData(k); + if( !IsVirtual(pTab) && pTab->tnum==iRoot ){ + pCur->zName = pTab->zName; + pCur->zType = "table"; + break; + } + } + if( pCur->zName==0 ){ + for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){ + Index *pIdx = (Index*)sqliteHashData(k); + if( pIdx->tnum==iRoot ){ + pCur->zName = pIdx->zName; + pCur->zType = "index"; + } + } + } + } + i += 10; + } + } + switch( i ){ + case 0: /* addr */ + sqlite3_result_int(ctx, pCur->iAddr); + break; + case 1: /* opcode */ + sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode), + -1, SQLITE_STATIC); + break; + case 2: /* p1 */ + sqlite3_result_int(ctx, pOp->p1); + break; + case 3: /* p2 */ + sqlite3_result_int(ctx, pOp->p2); + break; + case 4: /* p3 */ + sqlite3_result_int(ctx, pOp->p3); + break; + case 5: /* p4 */ + case 7: /* comment */ + if( pCur->zP4==0 ){ + pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp); + } + if( i==5 ){ + sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC); + }else{ +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4); + sqlite3_result_text(ctx, zCom, -1, sqlite3_free); +#endif + } + break; + case 6: /* p5 */ + sqlite3_result_int(ctx, pOp->p5); + break; + case 8: { /* subprog */ + Op *aOp = pCur->aOp; + assert( aOp[0].opcode==OP_Init ); + assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 ); + if( pCur->iRowid==pCur->iAddr+1 ){ + break; /* Result is NULL for the main program */ + }else if( aOp[0].p4.z!=0 ){ + sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC); + }else{ + sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC); + } + break; + } + case 10: /* tables_used.type */ + sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC); + break; + case 11: /* tables_used.schema */ + sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC); + break; + case 12: /* tables_used.name */ + sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC); + break; + case 13: /* tables_used.wr */ + sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite); + break; + } + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. In this implementation, the +** rowid is the same as the output value. +*/ +static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** Initialize a cursor. +** +** idxNum==0 means show all subprograms +** idxNum==1 means show only the main bytecode and omit subprograms. +*/ +static int bytecodevtabFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor; + bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab; + int rc = SQLITE_OK; + + bytecodevtabCursorClear(pCur); + pCur->iRowid = 0; + pCur->iAddr = 0; + pCur->showSubprograms = idxNum==0; + assert( argc==1 ); + if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){ + const char *zSql = (const char*)sqlite3_value_text(argv[0]); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0); + pCur->needFinalize = 1; + } + }else{ + pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer"); + } + if( pCur->pStmt==0 ){ + pVTab->base.zErrMsg = sqlite3_mprintf( + "argument to %s() is not a valid SQL statement", + pVTab->bTablesUsed ? "tables_used" : "bytecode" + ); + rc = SQLITE_ERROR; + }else{ + bytecodevtabNext(pVtabCursor); + } + return rc; +} + +/* +** We must have a single stmt=? constraint that will be passed through +** into the xFilter method. If there is no valid stmt=? constraint, +** then return an SQLITE_CONSTRAINT error. +*/ +static int bytecodevtabBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + int i; + int rc = SQLITE_CONSTRAINT; + struct sqlite3_index_constraint *p; + bytecodevtab *pVTab = (bytecodevtab*)tab; + int iBaseCol = pVTab->bTablesUsed ? 4 : 8; + pIdxInfo->estimatedCost = (double)100; + pIdxInfo->estimatedRows = 100; + pIdxInfo->idxNum = 0; + for(i=0, p=pIdxInfo->aConstraint; inConstraint; i++, p++){ + if( p->usable==0 ) continue; + if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){ + rc = SQLITE_OK; + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + } + if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){ + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->idxNum = 1; + } + } + return rc; +} + +/* +** This following structure defines all the methods for the +** virtual table. +*/ +static sqlite3_module bytecodevtabModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ bytecodevtabConnect, + /* xBestIndex */ bytecodevtabBestIndex, + /* xDisconnect */ bytecodevtabDisconnect, + /* xDestroy */ 0, + /* xOpen */ bytecodevtabOpen, + /* xClose */ bytecodevtabClose, + /* xFilter */ bytecodevtabFilter, + /* xNext */ bytecodevtabNext, + /* xEof */ bytecodevtabEof, + /* xColumn */ bytecodevtabColumn, + /* xRowid */ bytecodevtabRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0 +}; + + +int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ + int rc; + rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db); + } + return rc; +} +#elif defined(SQLITE_ENABLE_BYTECODE_VTAB) +int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ return SQLITE_OK; } +#endif /* SQLITE_ENABLE_BYTECODE_VTAB */ diff --git a/src/vtab.c b/src/vtab.c index 013511c..b2c01f2 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -405,7 +405,7 @@ void sqlite3VtabBeginParse( #ifndef SQLITE_OMIT_AUTHORIZATION /* Creating a virtual table invokes the authorization callback twice. ** The first invocation, to obtain permission to INSERT a row into the - ** sqlite_master table, has already been made by sqlite3StartTable(). + ** sqlite_schema table, has already been made by sqlite3StartTable(). ** The second call, to obtain permission to create the table, is made now. */ if( pTable->azModuleArg ){ @@ -446,9 +446,9 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ /* If the CREATE VIRTUAL TABLE statement is being entered for the ** first time (in other words if the virtual table is actually being - ** created now instead of just being read out of sqlite_master) then + ** created now instead of just being read out of sqlite_schema) then ** do additional initialization work and store the statement text - ** in the sqlite_master table. + ** in the sqlite_schema table. */ if( !db->init.busy ){ char *zStmt; @@ -466,19 +466,19 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ zStmt = sqlite3MPrintf(db, "CREATE VIRTUAL TABLE %T", &pParse->sNameToken); /* A slot for the record has already been allocated in the - ** SQLITE_MASTER table. We just need to update that slot with all + ** schema table. We just need to update that slot with all ** the information we've collected. ** ** The VM register number pParse->regRowid holds the rowid of an - ** entry in the sqlite_master table tht was created for this vtab + ** entry in the sqlite_schema table tht was created for this vtab ** by sqlite3StartTable(). */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3NestedParse(pParse, - "UPDATE %Q.%s " + "UPDATE %Q." DFLT_SCHEMA_TABLE " " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " "WHERE rowid=#%d", - db->aDb[iDb].zDbSName, MASTER_NAME, + db->aDb[iDb].zDbSName, pTab->zName, pTab->zName, zStmt, @@ -497,7 +497,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); } - /* If we are rereading the sqlite_master table create the in-memory + /* If we are rereading the sqlite_schema table create the in-memory ** record of the table. The xConnect() method is not called until ** the first time the virtual table is used in an SQL statement. This ** allows a schema that contains virtual tables to be loaded before @@ -1177,7 +1177,7 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ if( pTab==pToplevel->apVtabLock[i] ) return; } n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); - apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n); + apVtabLock = sqlite3Realloc(pToplevel->apVtabLock, n); if( apVtabLock ){ pToplevel->apVtabLock = apVtabLock; pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; diff --git a/src/wal.c b/src/wal.c index 9873f8b..c77686a 100644 --- a/src/wal.c +++ b/src/wal.c @@ -258,18 +258,6 @@ int sqlite3WalTrace = 0; # define WALTRACE(X) #endif -/* -** WAL mode depends on atomic aligned 32-bit loads and stores in a few -** places. The following macros try to make this explicit. -*/ -#if GCC_VESRION>=5004000 -# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) -# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED) -#else -# define AtomicLoad(PTR) (*(PTR)) -# define AtomicStore(PTR,VAL) (*(PTR) = (VAL)) -#endif - /* ** The maximum (and only) versions of the wal and wal-index formats ** that may be interpreted by this version of SQLite. @@ -479,6 +467,9 @@ struct Wal { #ifdef SQLITE_ENABLE_SNAPSHOT WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */ #endif +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + sqlite3 *db; +#endif }; /* @@ -517,7 +508,7 @@ typedef u16 ht_slot; ** This functionality is used by the checkpoint code (see walCheckpoint()). */ struct WalIterator { - int iPrior; /* Last result returned from the iterator */ + u32 iPrior; /* Last result returned from the iterator */ int nSegment; /* Number of entries in aSegment[] */ struct WalSegment { int iNext; /* Next slot in aIndex[] not yet returned */ @@ -577,7 +568,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc( if( pWal->nWiData<=iPage ){ sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; - apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); + apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte); if( !apNew ){ *ppPage = 0; return SQLITE_NOMEM_BKPT; @@ -599,7 +590,9 @@ static SQLITE_NOINLINE int walIndexPageRealloc( ); assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); - if( (rc&0xff)==SQLITE_READONLY ){ + if( rc==SQLITE_OK ){ + if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; + }else if( (rc&0xff)==SQLITE_READONLY ){ pWal->readOnly |= WAL_SHM_RDONLY; if( rc==SQLITE_READONLY ){ rc = SQLITE_OK; @@ -698,18 +691,35 @@ static void walChecksumBytes( aOut[1] = s2; } +/* +** If there is the possibility of concurrent access to the SHM file +** from multiple threads and/or processes, then do a memory barrier. +*/ static void walShmBarrier(Wal *pWal){ if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ sqlite3OsShmBarrier(pWal->pDbFd); } } +/* +** Add the SQLITE_NO_TSAN as part of the return-type of a function +** definition as a hint that the function contains constructs that +** might give false-positive TSAN warnings. +** +** See tag-20200519-1. +*/ +#if defined(__clang__) && !defined(SQLITE_NO_TSAN) +# define SQLITE_NO_TSAN __attribute__((no_sanitize_thread)) +#else +# define SQLITE_NO_TSAN +#endif + /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ -static void walIndexWriteHdr(Wal *pWal){ +static SQLITE_NO_TSAN void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); @@ -717,6 +727,7 @@ static void walIndexWriteHdr(Wal *pWal){ pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); + /* Possible TSAN false-positive. See tag-20200519-1 */ memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); walShmBarrier(pWal); memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr)); @@ -852,7 +863,7 @@ static int walLockShared(Wal *pWal, int lockIdx){ SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, walLockName(lockIdx), rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) return rc; } static void walUnlockShared(Wal *pWal, int lockIdx){ @@ -868,7 +879,7 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){ SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, walLockName(lockIdx), n, rc ? "failed" : "ok")); - VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) + VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); ) return rc; } static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ @@ -956,6 +967,7 @@ static int walFramePage(u32 iFrame){ && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) ); + assert( iHash>=0 ); return iHash; } @@ -1087,7 +1099,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; } sLoc.aPgno[idx] = iPage; - sLoc.aHash[iKey] = (ht_slot)idx; + AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT /* Verify that the number of entries in the hash table exactly equals @@ -1152,12 +1164,6 @@ static int walIndexRecover(Wal *pWal){ assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); - if( rc==SQLITE_OK ){ - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); - if( rc!=SQLITE_OK ){ - walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); - } - } if( rc ){ return rc; } @@ -1173,15 +1179,16 @@ static int walIndexRecover(Wal *pWal){ if( nSize>WAL_HDRSIZE ){ u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ + u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */ u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ int szFrame; /* Number of bytes in buffer aFrame[] */ u8 *aData; /* Pointer to data part of aFrame buffer */ - int iFrame; /* Index of last frame read */ - i64 iOffset; /* Next offset to read from log file */ int szPage; /* Page size according to the log */ u32 magic; /* Magic value read from WAL header */ u32 version; /* Magic value read from WAL header */ int isValid; /* True if this frame is valid */ + u32 iPg; /* Current 32KB wal-index page */ + u32 iLastFrame; /* Last frame in wal, based on nSize alone */ /* Read in the WAL header. */ rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); @@ -1228,38 +1235,82 @@ static int walIndexRecover(Wal *pWal){ /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlite3_malloc64(szFrame); + aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ); if( !aFrame ){ rc = SQLITE_NOMEM_BKPT; goto recovery_error; } aData = &aFrame[WAL_FRAME_HDRSIZE]; + aPrivate = (u32*)&aData[szPage]; /* Read all frames from the log file. */ - iFrame = 0; - for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){ - u32 pgno; /* Database page number for frame */ - u32 nTruncate; /* dbsize field from frame header */ + iLastFrame = (nSize - WAL_HDRSIZE) / szFrame; + for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){ + u32 *aShare; + u32 iFrame; /* Index of last frame read */ + u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE); + u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE); + u32 nHdr, nHdr32; + rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); + if( rc ) break; + pWal->apWiData[iPg] = aPrivate; + + for(iFrame=iFirst; iFrame<=iLast; iFrame++){ + i64 iOffset = walFrameOffset(iFrame, szPage); + u32 pgno; /* Database page number for frame */ + u32 nTruncate; /* dbsize field from frame header */ - /* Read and decode the next log frame. */ - iFrame++; - rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); - if( rc!=SQLITE_OK ) break; - isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); - if( !isValid ) break; - rc = walIndexAppend(pWal, iFrame, pgno); - if( rc!=SQLITE_OK ) break; + /* Read and decode the next log frame. */ + rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); + if( rc!=SQLITE_OK ) break; + isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); + if( !isValid ) break; + rc = walIndexAppend(pWal, iFrame, pgno); + if( NEVER(rc!=SQLITE_OK) ) break; - /* If nTruncate is non-zero, this is a commit record. */ - if( nTruncate ){ - pWal->hdr.mxFrame = iFrame; - pWal->hdr.nPage = nTruncate; - pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); - testcase( szPage<=32768 ); - testcase( szPage>=65536 ); - aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; - aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; + /* If nTruncate is non-zero, this is a commit record. */ + if( nTruncate ){ + pWal->hdr.mxFrame = iFrame; + pWal->hdr.nPage = nTruncate; + pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); + testcase( szPage<=32768 ); + testcase( szPage>=65536 ); + aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; + aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; + } } + pWal->apWiData[iPg] = aShare; + nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); + nHdr32 = nHdr / sizeof(u32); +#ifndef SQLITE_SAFER_WALINDEX_RECOVERY + /* Memcpy() should work fine here, on all reasonable implementations. + ** Technically, memcpy() might change the destination to some + ** intermediate value before setting to the final value, and that might + ** cause a concurrent reader to malfunction. Memcpy() is allowed to + ** do that, according to the spec, but no memcpy() implementation that + ** we know of actually does that, which is why we say that memcpy() + ** is safe for this. Memcpy() is certainly a lot faster. + */ + memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); +#else + /* In the event that some platform is found for which memcpy() + ** changes the destination to some intermediate value before + ** setting the final value, this alternative copy routine is + ** provided. + */ + { + int i; + for(i=nHdr32; inBackfill = 0; pInfo->nBackfillAttempted = pWal->hdr.mxFrame; pInfo->aReadMark[0] = 0; - for(i=1; iaReadMark[i] = READMARK_NOT_USED; - if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame; + for(i=1; ihdr.mxFrame ){ + pInfo->aReadMark[i] = pWal->hdr.mxFrame; + }else{ + pInfo->aReadMark[i] = READMARK_NOT_USED; + } + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); + }else if( rc!=SQLITE_BUSY ){ + goto recovery_error; + } + } /* If more than one frame was recovered from the log file, report an ** event via sqlite3_log(). This is to help with identifying performance @@ -1300,7 +1362,6 @@ finished: recovery_error: WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); - walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); return rc; } @@ -1688,6 +1749,89 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ return rc; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +/* +** Attempt to enable blocking locks. Blocking locks are enabled only if (a) +** they are supported by the VFS, and (b) the database handle is configured +** with a busy-timeout. Return 1 if blocking locks are successfully enabled, +** or 0 otherwise. +*/ +static int walEnableBlocking(Wal *pWal){ + int res = 0; + if( pWal->db ){ + int tmout = pWal->db->busyTimeout; + if( tmout ){ + int rc; + rc = sqlite3OsFileControl( + pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout + ); + res = (rc==SQLITE_OK); + } + } + return res; +} + +/* +** Disable blocking locks. +*/ +static void walDisableBlocking(Wal *pWal){ + int tmout = 0; + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout); +} + +/* +** If parameter bLock is true, attempt to enable blocking locks, take +** the WRITER lock, and then disable blocking locks. If blocking locks +** cannot be enabled, no attempt to obtain the WRITER lock is made. Return +** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not +** an error if blocking locks can not be enabled. +** +** If the bLock parameter is false and the WRITER lock is held, release it. +*/ +int sqlite3WalWriteLock(Wal *pWal, int bLock){ + int rc = SQLITE_OK; + assert( pWal->readLock<0 || bLock==0 ); + if( bLock ){ + assert( pWal->db ); + if( walEnableBlocking(pWal) ){ + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + } + walDisableBlocking(pWal); + } + }else if( pWal->writeLock ){ + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + pWal->writeLock = 0; + } + return rc; +} + +/* +** Set the database handle used to determine if blocking locks are required. +*/ +void sqlite3WalDb(Wal *pWal, sqlite3 *db){ + pWal->db = db; +} + +/* +** Take an exclusive WRITE lock. Blocking if so configured. +*/ +static int walLockWriter(Wal *pWal){ + int rc; + walEnableBlocking(pWal); + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + walDisableBlocking(pWal); + return rc; +} +#else +# define walEnableBlocking(x) 0 +# define walDisableBlocking(x) +# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) +# define sqlite3WalDb(pWal, db) +#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ + + /* ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and ** n. If the attempt fails and parameter xBusy is not NULL, then it is a @@ -1705,6 +1849,12 @@ static int walBusyLock( do { rc = walLockExclusive(pWal, lockIdx, n); }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ){ + walDisableBlocking(pWal); + rc = SQLITE_BUSY; + } +#endif return rc; } @@ -1742,7 +1892,7 @@ static void walRestartHdr(Wal *pWal, u32 salt1){ sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); memcpy(&pWal->hdr.aSalt[1], &salt1, 4); walIndexWriteHdr(pWal); - pInfo->nBackfill = 0; + AtomicStore(&pInfo->nBackfill, 0); pInfo->nBackfillAttempted = 0; pInfo->aReadMark[1] = 0; for(i=2; iaReadMark[i] = READMARK_NOT_USED; @@ -1817,32 +1967,13 @@ static int walCheckpoint( mxSafeFrame = pWal->hdr.mxFrame; mxPage = pWal->hdr.nPage; for(i=1; iaReadMark[i]; + u32 y = AtomicLoad(pInfo->aReadMark+i); if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ - pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); + u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); + AtomicStore(pInfo->aReadMark+i, iMark); walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc==SQLITE_BUSY ){ mxSafeFrame = y; @@ -1860,7 +1991,7 @@ static int walCheckpoint( } if( pIter - && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK + && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK ){ u32 nBackfill = pInfo->nBackfill; @@ -1875,18 +2006,27 @@ static int walCheckpoint( if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); i64 nSize; /* Current size of database file */ + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); + } } - } + } /* Iterate through the contents of the WAL, copying data to the db file */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); - if( db->u1.isInterrupted ){ + if( AtomicLoad(&db->u1.isInterrupted) ){ rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; break; } @@ -1902,6 +2042,7 @@ static int walCheckpoint( rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); if( rc!=SQLITE_OK ) break; } + sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); /* If work was actually accomplished... */ if( rc==SQLITE_OK ){ @@ -1914,11 +2055,7 @@ static int walCheckpoint( } } if( rc==SQLITE_OK ){ - rc = sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0); - if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK; - } - if( rc==SQLITE_OK ){ - pInfo->nBackfill = mxSafeFrame; + AtomicStore(&pInfo->nBackfill, mxSafeFrame); } } @@ -2077,7 +2214,7 @@ int sqlite3WalClose( ** If the checksum cannot be verified return non-zero. If the header ** is read successfully and the checksum verified, return zero. */ -static int walIndexTryHdr(Wal *pWal, int *pChanged){ +static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){ u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr volatile *aHdr; /* Header in shared memory */ @@ -2090,13 +2227,19 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){ ** meaning it is possible that an inconsistent snapshot is read ** from the file. If this happens, return non-zero. ** + ** tag-20200519-1: ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from - ** reordering the reads and writes. + ** reordering the reads and writes. TSAN and similar tools can sometimes + ** give false-positive warnings about these accesses because the tools do not + ** account for the double-read and the memory barrier. The use of mutexes + ** here would be problematic as the memory being accessed is potentially + ** shared among multiple processes and not all mutex implementions work + ** reliably in that environment. */ aHdr = walIndexHdr(pWal); - memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); + memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */ walShmBarrier(pWal); memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); @@ -2186,28 +2329,32 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ /* If the first attempt failed, it might have been due to a race ** with a writer. So get a WRITE lock and try again. */ - assert( badHdr==0 || pWal->writeLock==0 ); if( badHdr ){ if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){ if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } - }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ - pWal->writeLock = 1; - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ - badHdr = walIndexTryHdr(pWal, pChanged); - if( badHdr ){ - /* If the wal-index header is still malformed even while holding - ** a WRITE lock, it can only mean that the header is corrupted and - ** needs to be reconstructed. So run recovery to do exactly that. - */ - rc = walIndexRecover(pWal); - *pChanged = 1; + }else{ + int bWriteLock = pWal->writeLock; + if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ + pWal->writeLock = 1; + if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ + badHdr = walIndexTryHdr(pWal, pChanged); + if( badHdr ){ + /* If the wal-index header is still malformed even while holding + ** a WRITE lock, it can only mean that the header is corrupted and + ** needs to be reconstructed. So run recovery to do exactly that. + */ + rc = walIndexRecover(pWal); + *pChanged = 1; + } + } + if( bWriteLock==0 ){ + pWal->writeLock = 0; + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } } - pWal->writeLock = 0; - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } } @@ -2537,7 +2684,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ assert( pWal->nWiData>0 ); assert( pWal->apWiData[0]!=0 ); pInfo = walCkptInfo(pWal); - if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame + if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame #ifdef SQLITE_ENABLE_SNAPSHOT && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0) #endif @@ -2599,7 +2746,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ for(i=1; iaReadMark+i,mxFrame); + AtomicStore(pInfo->aReadMark+i,mxFrame); + mxReadMark = mxFrame; mxI = i; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); break; @@ -2703,7 +2851,7 @@ int sqlite3WalSnapshotRecover(Wal *pWal){ rc = SQLITE_NOMEM; }else{ u32 i = pInfo->nBackfillAttempted; - for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){ + for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){ WalHashLoc sLoc; /* Hash table location */ u32 pgno; /* Page number in db file */ i64 iDbOff; /* Offset of db file entry */ @@ -2758,12 +2906,35 @@ int sqlite3WalSnapshotRecover(Wal *pWal){ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ - #ifdef SQLITE_ENABLE_SNAPSHOT int bChanged = 0; WalIndexHdr *pSnapshot = pWal->pSnapshot; - if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ - bChanged = 1; +#endif + + assert( pWal->ckptLock==0 ); + +#ifdef SQLITE_ENABLE_SNAPSHOT + if( pSnapshot ){ + if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){ + bChanged = 1; + } + + /* It is possible that there is a checkpointer thread running + ** concurrent with this code. If this is the case, it may be that the + ** checkpointer has already determined that it will checkpoint + ** snapshot X, where X is later in the wal file than pSnapshot, but + ** has not yet set the pInfo->nBackfillAttempted variable to indicate + ** its intent. To avoid the race condition this leads to, ensure that + ** there is no checkpointer process by taking a shared CKPT lock + ** before checking pInfo->nBackfillAttempted. */ + (void)walEnableBlocking(pWal); + rc = walLockShared(pWal, WAL_CKPT_LOCK); + walDisableBlocking(pWal); + + if( rc!=SQLITE_OK ){ + return rc; + } + pWal->ckptLock = 1; } #endif @@ -2796,48 +2967,42 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 ); assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame ); - /* It is possible that there is a checkpointer thread running - ** concurrent with this code. If this is the case, it may be that the - ** checkpointer has already determined that it will checkpoint - ** snapshot X, where X is later in the wal file than pSnapshot, but - ** has not yet set the pInfo->nBackfillAttempted variable to indicate - ** its intent. To avoid the race condition this leads to, ensure that - ** there is no checkpointer process by taking a shared CKPT lock - ** before checking pInfo->nBackfillAttempted. - ** - ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing - ** this already? - */ - rc = walLockShared(pWal, WAL_CKPT_LOCK); - - if( rc==SQLITE_OK ){ - /* Check that the wal file has not been wrapped. Assuming that it has - ** not, also check that no checkpointer has attempted to checkpoint any - ** frames beyond pSnapshot->mxFrame. If either of these conditions are - ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr - ** with *pSnapshot and set *pChanged as appropriate for opening the - ** snapshot. */ - if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) - && pSnapshot->mxFrame>=pInfo->nBackfillAttempted - ){ - assert( pWal->readLock>0 ); - memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); - *pChanged = bChanged; - }else{ - rc = SQLITE_ERROR_SNAPSHOT; - } - - /* Release the shared CKPT lock obtained above. */ - walUnlockShared(pWal, WAL_CKPT_LOCK); - pWal->minFrame = 1; + /* Check that the wal file has not been wrapped. Assuming that it has + ** not, also check that no checkpointer has attempted to checkpoint any + ** frames beyond pSnapshot->mxFrame. If either of these conditions are + ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr + ** with *pSnapshot and set *pChanged as appropriate for opening the + ** snapshot. */ + if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) + && pSnapshot->mxFrame>=pInfo->nBackfillAttempted + ){ + assert( pWal->readLock>0 ); + memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr)); + *pChanged = bChanged; + }else{ + rc = SQLITE_ERROR_SNAPSHOT; } + /* A client using a non-current snapshot may not ignore any frames + ** from the start of the wal file. This is because, for a system + ** where (minFrame < iSnapshot < maxFrame), a checkpointer may + ** have omitted to checkpoint a frame earlier than minFrame in + ** the file because there exists a frame after iSnapshot that + ** is the same database page. */ + pWal->minFrame = 1; if( rc!=SQLITE_OK ){ sqlite3WalEndReadTransaction(pWal); } } } + + /* Release the shared CKPT lock obtained above. */ + if( pWal->ckptLock ){ + assert( pSnapshot ); + walUnlockShared(pWal, WAL_CKPT_LOCK); + pWal->ckptLock = 0; + } #endif return rc; } @@ -2917,14 +3082,15 @@ int sqlite3WalFindFrame( int iKey; /* Hash slot index */ int nCollide; /* Number of hash collisions remaining */ int rc; /* Error code */ + u32 iH; rc = walHashGet(pWal, iHash, &sLoc); if( rc!=SQLITE_OK ){ return rc; } nCollide = HASHTABLE_NSLOT; - for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ - u32 iH = sLoc.aHash[iKey]; + iKey = walHash(pgno); + while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ u32 iFrame = iH + sLoc.iZero; if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH]==pgno ){ assert( iFrame>iRead || CORRUPT_DB ); @@ -2933,6 +3099,7 @@ int sqlite3WalFindFrame( if( (nCollide--)==0 ){ return SQLITE_CORRUPT_BKPT; } + iKey = walNextHash(iKey); } if( iRead ) break; } @@ -3008,6 +3175,16 @@ Pgno sqlite3WalDbsize(Wal *pWal){ int sqlite3WalBeginWriteTransaction(Wal *pWal){ int rc; +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* If the write-lock is already held, then it was obtained before the + ** read-transaction was even opened, making this call a no-op. + ** Return early. */ + if( pWal->writeLock ){ + assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); + return SQLITE_OK; + } +#endif + /* Cannot start a write transaction without first holding a read ** transaction. */ assert( pWal->readLock>=0 ); @@ -3592,45 +3769,52 @@ int sqlite3WalCheckpoint( if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive - ** "checkpoint" lock on the database file. */ - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); - if( rc ){ - /* EVIDENCE-OF: R-10421-19736 If any other process is running a - ** checkpoint operation at the same time, the lock cannot be obtained and - ** SQLITE_BUSY is returned. - ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, - ** it will not be invoked in this case. - */ - testcase( rc==SQLITE_BUSY ); - testcase( xBusy!=0 ); - return rc; - } - pWal->ckptLock = 1; + /* Enable blocking locks, if possible. If blocking locks are successfully + ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ + sqlite3WalDb(pWal, db); + (void)walEnableBlocking(pWal); - /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and - ** TRUNCATE modes also obtain the exclusive "writer" lock on the database - ** file. - ** - ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained - ** immediately, and a busy-handler is configured, it is invoked and the - ** writer lock retried until either the busy-handler returns 0 or the - ** lock is successfully obtained. + /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive + ** "checkpoint" lock on the database file. + ** EVIDENCE-OF: R-10421-19736 If any other process is running a + ** checkpoint operation at the same time, the lock cannot be obtained and + ** SQLITE_BUSY is returned. + ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, + ** it will not be invoked in this case. */ - if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); - if( rc==SQLITE_OK ){ - pWal->writeLock = 1; - }else if( rc==SQLITE_BUSY ){ - eMode2 = SQLITE_CHECKPOINT_PASSIVE; - xBusy2 = 0; - rc = SQLITE_OK; + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + testcase( rc==SQLITE_BUSY ); + testcase( rc!=SQLITE_OK && xBusy2!=0 ); + if( rc==SQLITE_OK ){ + pWal->ckptLock = 1; + + /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and + ** TRUNCATE modes also obtain the exclusive "writer" lock on the database + ** file. + ** + ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained + ** immediately, and a busy-handler is configured, it is invoked and the + ** writer lock retried until either the busy-handler returns 0 or the + ** lock is successfully obtained. + */ + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + }else if( rc==SQLITE_BUSY ){ + eMode2 = SQLITE_CHECKPOINT_PASSIVE; + xBusy2 = 0; + rc = SQLITE_OK; + } } } + /* Read the wal-index header. */ if( rc==SQLITE_OK ){ + walDisableBlocking(pWal); rc = walIndexReadHdr(pWal, &isChanged); + (void)walEnableBlocking(pWal); if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); } @@ -3662,11 +3846,19 @@ int sqlite3WalCheckpoint( memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); } + walDisableBlocking(pWal); + sqlite3WalDb(pWal, 0); + /* Release the locks. */ sqlite3WalEndWriteTransaction(pWal); - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); - pWal->ckptLock = 0; + if( pWal->ckptLock ){ + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); + pWal->ckptLock = 0; + } WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; +#endif return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); } @@ -3783,7 +3975,10 @@ int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){ /* Try to open on pSnapshot when the next read-transaction starts */ -void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){ +void sqlite3WalSnapshotOpen( + Wal *pWal, + sqlite3_snapshot *pSnapshot +){ pWal->pSnapshot = (WalIndexHdr*)pSnapshot; } diff --git a/src/wal.h b/src/wal.h index 1610607..02e2bab 100644 --- a/src/wal.h +++ b/src/wal.h @@ -146,5 +146,10 @@ int sqlite3WalFramesize(Wal *pWal); /* Return the sqlite3_file object for the WAL file */ sqlite3_file *sqlite3WalFile(Wal *pWal); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +int sqlite3WalWriteLock(Wal *pWal, int bLock); +void sqlite3WalDb(Wal *pWal, sqlite3 *db); +#endif + #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* SQLITE_WAL_H */ diff --git a/src/walker.c b/src/walker.c index 5733210..7649036 100644 --- a/src/walker.c +++ b/src/walker.c @@ -156,15 +156,16 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ struct SrcList_item *pItem; pSrc = p->pSrc; - assert( pSrc!=0 ); - for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ - if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){ - return WRC_Abort; - } - if( pItem->fg.isTabFunc - && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg) - ){ - return WRC_Abort; + if( pSrc ){ + for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ + if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){ + return WRC_Abort; + } + if( pItem->fg.isTabFunc + && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg) + ){ + return WRC_Abort; + } } } return WRC_Continue; @@ -206,3 +207,40 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){ }while( p!=0 ); return WRC_Continue; } + +/* Increase the walkerDepth when entering a subquery, and +** descrease when leaving the subquery. +*/ +int sqlite3WalkerDepthIncrease(Walker *pWalker, Select *pSelect){ + UNUSED_PARAMETER(pSelect); + pWalker->walkerDepth++; + return WRC_Continue; +} +void sqlite3WalkerDepthDecrease(Walker *pWalker, Select *pSelect){ + UNUSED_PARAMETER(pSelect); + pWalker->walkerDepth--; +} + + +/* +** No-op routine for the parse-tree walker. +** +** When this routine is the Walker.xExprCallback then expression trees +** are walked without any actions being taken at each node. Presumably, +** when this routine is used for Walker.xExprCallback then +** Walker.xSelectCallback is set to do something useful for every +** subquery in the parser tree. +*/ +int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + return WRC_Continue; +} + +/* +** No-op routine for the parse-tree walker for SELECT statements. +** subquery in the parser tree. +*/ +int sqlite3SelectWalkNoop(Walker *NotUsed, Select *NotUsed2){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + return WRC_Continue; +} diff --git a/src/where.c b/src/where.c index e7447de..e3f7435 100644 --- a/src/where.c +++ b/src/where.c @@ -59,8 +59,12 @@ int sqlite3WhereIsDistinct(WhereInfo *pWInfo){ } /* -** Return TRUE if the WHERE clause returns rows in ORDER BY order. -** Return FALSE if the output needs to be sorted. +** Return the number of ORDER BY terms that are satisfied by the +** WHERE clause. A return of 0 means that the output must be +** completely sorted. A return equal to the number of ORDER BY +** terms means that no sorting is needed at all. A return that +** is positive but less than the number of ORDER BY terms means that +** block sorting is required. */ int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ return pWInfo->nOBSat; @@ -2298,7 +2302,9 @@ static void whereLoopOutputAdjust( /* In the absence of explicit truth probabilities, use heuristics to ** guess a reasonable truth probability. */ pLoop->nOut--; - if( pTerm->eOperator&(WO_EQ|WO_IS) ){ + if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 + && (pTerm->wtFlags & TERM_HIGHTRUTH)==0 /* tag-20200224-1 */ + ){ Expr *pRight = pTerm->pExpr->pRight; int k = 0; testcase( pTerm->pExpr->op==TK_IS ); @@ -2307,7 +2313,10 @@ static void whereLoopOutputAdjust( }else{ k = 20; } - if( iReducewtFlags |= TERM_HEURTRUTH; + iReduce = k; + } } } } @@ -2489,9 +2498,9 @@ static int whereLoopAddBtreeIndex( } if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ - pBuilder->bldFlags |= SQLITE_BLDF_UNIQUE; + pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE; }else{ - pBuilder->bldFlags |= SQLITE_BLDF_INDEXED; + pBuilder->bldFlags1 |= SQLITE_BLDF1_INDEXED; } pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; @@ -2656,6 +2665,27 @@ static int whereLoopAddBtreeIndex( if( rc!=SQLITE_OK ) break; /* Jump out of the pTerm loop */ if( nOut ){ pNew->nOut = sqlite3LogEst(nOut); + if( nEq==1 + /* TUNING: Mark terms as "low selectivity" if they seem likely + ** to be true for half or more of the rows in the table. + ** See tag-202002240-1 */ + && pNew->nOut+10 > pProbe->aiRowLogEst[0] + ){ +#if WHERETRACE_ENABLED /* 0x01 */ + if( sqlite3WhereTrace & 0x01 ){ + sqlite3DebugPrintf( + "STAT4 determines term has low selectivity:\n"); + sqlite3WhereTermPrint(pTerm, 999); + } +#endif + pTerm->wtFlags |= TERM_HIGHTRUTH; + if( pTerm->wtFlags & TERM_HEURTRUTH ){ + /* If the term has previously been used with an assumption of + ** higher selectivity, then set the flag to rerun the + ** loop computations. */ + pBuilder->bldFlags2 |= SQLITE_BLDF2_2NDPASS; + } + } if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut; pNew->nOut -= nIn; } @@ -2732,6 +2762,7 @@ static int whereLoopAddBtreeIndex( && saved_nEq+1nKeyCol && saved_nEq==pNew->nLTerm && pProbe->noSkipScan==0 + && pProbe->hasStat1!=0 && OptimizationEnabled(db, SQLITE_SkipScan) && pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */ && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK @@ -3001,6 +3032,7 @@ static int whereLoopAddBtree( pNew->nOut = rSize; pNew->u.btree.pIndex = pProbe; b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); + /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ @@ -3030,6 +3062,7 @@ static int whereLoopAddBtree( if( b || !HasRowid(pTab) || pProbe->pPartIdxWhere!=0 + || pSrc->fg.isIndexedBy || ( m==0 && pProbe->bUnordered==0 && (pProbe->szIdxRowszTabRow) @@ -3079,9 +3112,9 @@ static int whereLoopAddBtree( } } - pBuilder->bldFlags = 0; + pBuilder->bldFlags1 = 0; rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); - if( pBuilder->bldFlags==SQLITE_BLDF_INDEXED ){ + if( pBuilder->bldFlags1==SQLITE_BLDF1_INDEXED ){ /* If a non-unique index is used, or if a prefix of the key for ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the @@ -3577,7 +3610,6 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; WhereLoop *pNew; - u8 priorJointype = 0; /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; @@ -3588,12 +3620,13 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ pNew->iTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); - if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){ + if( (pItem->fg.jointype & (JT_LEFT|JT_CROSS))!=0 ){ /* This condition is true when pItem is the FROM clause term on the ** right-hand-side of a LEFT or CROSS JOIN. */ mPrereq = mPrior; + }else{ + mPrereq = 0; } - priorJointype = pItem->fg.jointype; #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ struct SrcList_item *p; @@ -3711,7 +3744,9 @@ static i8 wherePathSatisfiesOrderBy( orderDistinctMask = 0; ready = 0; eqOpMask = WO_EQ | WO_IS | WO_ISNULL; - if( wctrlFlags & WHERE_ORDERBY_LIMIT ) eqOpMask |= WO_IN; + if( wctrlFlags & (WHERE_ORDERBY_LIMIT|WHERE_ORDERBY_MAX|WHERE_ORDERBY_MIN) ){ + eqOpMask |= WO_IN; + } for(iLoop=0; isOrderDistinct && obSat0 ) ready |= pLoop->maskSelf; if( iLoopnLTerm && pTerm!=pLoop->aLTerm[j]; j++){} if( j>=pLoop->nLTerm ) continue; } if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){ - if( sqlite3ExprCollSeqMatch(pWInfo->pParse, - pOrderBy->a[i].pExpr, pTerm->pExpr)==0 ){ + Parse *pParse = pWInfo->pParse; + CollSeq *pColl1 = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[i].pExpr); + CollSeq *pColl2 = sqlite3ExprCompareCollSeq(pParse, pTerm->pExpr); + assert( pColl1 ); + if( pColl2==0 || sqlite3StrICmp(pColl1->zName, pColl2->zName) ){ continue; } testcase( pTerm->pExpr->op==TK_IS ); @@ -4392,6 +4431,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } } + }else if( nLoop + && pWInfo->nOBSat==1 + && (pWInfo->wctrlFlags & (WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX))!=0 + ){ + pWInfo->bOrderedInnerLoop = 1; } } if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) @@ -4533,6 +4577,28 @@ static int exprIsDeterministic(Expr *p){ return w.eCode; } + +#ifdef WHERETRACE_ENABLED +/* +** Display all WhereLoops in pWInfo +*/ +static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){ + if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */ + WhereLoop *p; + int i; + static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" + "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; + for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){ + p->cId = zLabel[i%(sizeof(zLabel)-1)]; + sqlite3WhereLoopPrint(p, pWC); + } + } +} +# define WHERETRACE_ALL_LOOPS(W,C) showAllWhereLoops(W,C) +#else +# define WHERETRACE_ALL_LOOPS(W,C) +#endif + /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains @@ -4834,19 +4900,28 @@ WhereInfo *sqlite3WhereBegin( if( nTabList!=1 || whereShortCut(&sWLB)==0 ){ rc = whereLoopAddAll(&sWLB); if( rc ) goto whereBeginError; - -#ifdef WHERETRACE_ENABLED - if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */ - WhereLoop *p; - int i; - static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" - "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; - for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){ - p->cId = zLabel[i%(sizeof(zLabel)-1)]; - sqlite3WhereLoopPrint(p, sWLB.pWC); + +#ifdef SQLITE_ENABLE_STAT4 + /* If one or more WhereTerm.truthProb values were used in estimating + ** loop parameters, but then those truthProb values were subsequently + ** changed based on STAT4 information while computing subsequent loops, + ** then we need to rerun the whole loop building process so that all + ** loops will be built using the revised truthProb values. */ + if( sWLB.bldFlags2 & SQLITE_BLDF2_2NDPASS ){ + WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); + WHERETRACE(0xffff, + ("**** Redo all loop computations due to" + " TERM_HIGHTRUTH changes ****\n")); + while( pWInfo->pLoops ){ + WhereLoop *p = pWInfo->pLoops; + pWInfo->pLoops = p->pNextLoop; + whereLoopDelete(db, p); } + rc = whereLoopAddAll(&sWLB); + if( rc ) goto whereBeginError; } #endif + WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC); wherePathSolver(pWInfo, 0); if( db->mallocFailed ) goto whereBeginError; @@ -5117,7 +5192,7 @@ WhereInfo *sqlite3WhereBegin( && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 && pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED ){ - sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ + sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); } VdbeComment((v, "%s", pIx->zName)); #ifdef SQLITE_ENABLE_COLUMN_USED_MASK @@ -5275,12 +5350,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ if( pIn->eEndLoopOp!=OP_Noop ){ if( pIn->nPrefix ){ assert( pLoop->wsFlags & WHERE_IN_EARLYOUT ); - if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){ - sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur, - sqlite3VdbeCurrentAddr(v)+2+(pLevel->iLeftJoin!=0), - pIn->iBase, pIn->nPrefix); - VdbeCoverage(v); - } if( pLevel->iLeftJoin ){ /* For LEFT JOIN queries, cursor pIn->iCur may not have been ** opened yet. This occurs for WHERE clauses such as @@ -5291,10 +5360,17 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ ** jump over the OP_Next or OP_Prev instruction about to ** be coded. */ sqlite3VdbeAddOp2(v, OP_IfNotOpen, pIn->iCur, - sqlite3VdbeCurrentAddr(v) + 2 + sqlite3VdbeCurrentAddr(v) + 2 + + ((pLoop->wsFlags & WHERE_VIRTUALTABLE)==0) ); VdbeCoverage(v); } + if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){ + sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur, + sqlite3VdbeCurrentAddr(v)+2, + pIn->iBase, pIn->nPrefix); + VdbeCoverage(v); + } } sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); VdbeCoverage(v); diff --git a/src/whereInt.h b/src/whereInt.h index 7410162..e33dde5 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -291,6 +291,12 @@ struct WhereTerm { #define TERM_LIKE 0x0400 /* The original LIKE operator */ #define TERM_IS 0x0800 /* Term.pExpr is an IS operator */ #define TERM_VARSELECT 0x1000 /* Term.pExpr contains a correlated sub-query */ +#define TERM_HEURTRUTH 0x2000 /* Heuristic truthProb used */ +#ifdef SQLITE_ENABLE_STAT4 +# define TERM_HIGHTRUTH 0x4000 /* Term excludes few rows */ +#else +# define TERM_HIGHTRUTH 0 /* Only used with STAT4 */ +#endif /* ** An instance of the WhereScan object is used as an iterator for locating @@ -405,13 +411,16 @@ struct WhereLoopBuilder { UnpackedRecord *pRec; /* Probe for stat4 (if required) */ int nRecValid; /* Number of valid fields currently in pRec */ #endif - unsigned int bldFlags; /* SQLITE_BLDF_* flags */ + unsigned char bldFlags1; /* First set of SQLITE_BLDF_* flags */ + unsigned char bldFlags2; /* Second set of SQLITE_BLDF_* flags */ unsigned int iPlanLimit; /* Search limiter */ }; /* Allowed values for WhereLoopBuider.bldFlags */ -#define SQLITE_BLDF_INDEXED 0x0001 /* An index is used */ -#define SQLITE_BLDF_UNIQUE 0x0002 /* All keys of a UNIQUE index used */ +#define SQLITE_BLDF1_INDEXED 0x0001 /* An index is used */ +#define SQLITE_BLDF1_UNIQUE 0x0002 /* All keys of a UNIQUE index used */ + +#define SQLITE_BLDF2_2NDPASS 0x0004 /* Second builder pass needed */ /* The WhereLoopBuilder.iPlanLimit is used to limit the number of ** index+constraint combinations the query planner will consider for a diff --git a/src/wherecode.c b/src/wherecode.c index 0014a69..5473a02 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1052,7 +1052,7 @@ static void codeDeferredSeek( ){ int i; Table *pTab = pIdx->pTable; - int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1)); + u32 *ai = (u32*)sqlite3DbMallocZero(pParse->db, sizeof(u32)*(pTab->nCol+1)); if( ai ){ ai[0] = pTab->nCol; for(i=0; inColumn-1; i++){ @@ -1432,7 +1432,9 @@ Bitmask sqlite3WhereCodeOneLoopStart( pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0); if( pRight ){ pRight->iTable = iReg+j+2; - sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0); + sqlite3ExprIfFalse( + pParse, pCompare, pLevel->addrCont, SQLITE_JUMPIFNULL + ); } pCompare->pLeft = 0; sqlite3ExprDelete(db, pCompare); @@ -1709,6 +1711,9 @@ Bitmask sqlite3WhereCodeOneLoopStart( nExtraReg = 1; bSeekPastNull = 1; pLevel->regBignull = regBignull = ++pParse->nMem; + if( pLevel->iLeftJoin ){ + sqlite3VdbeAddOp2(v, OP_Integer, 0, regBignull); + } pLevel->addrBignull = sqlite3VdbeMakeLabel(pParse); } diff --git a/src/whereexpr.c b/src/whereexpr.c index cec0aef..3c91fc3 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -377,7 +377,8 @@ static int isAuxiliaryVtabOperator( ** MATCH(expression,vtab_column) */ pCol = pList->a[1].pExpr; - if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ + testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); + if( ExprIsVtab(pCol) ){ for(i=0; iu.zToken, aOp[i].zOp)==0 ){ *peOp2 = aOp[i].eOp2; @@ -399,7 +400,8 @@ static int isAuxiliaryVtabOperator( ** with function names in an arbitrary case. */ pCol = pList->a[0].pExpr; - if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){ + testcase( pCol->op==TK_COLUMN && pCol->y.pTab==0 ); + if( ExprIsVtab(pCol) ){ sqlite3_vtab *pVtab; sqlite3_module *pMod; void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**); @@ -422,10 +424,12 @@ static int isAuxiliaryVtabOperator( int res = 0; Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; - if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){ + testcase( pLeft->op==TK_COLUMN && pLeft->y.pTab==0 ); + if( ExprIsVtab(pLeft) ){ res++; } - if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){ + testcase( pRight && pRight->op==TK_COLUMN && pRight->y.pTab==0 ); + if( pRight && ExprIsVtab(pRight) ){ res++; SWAP(Expr*, pLeft, pRight); } diff --git a/src/window.c b/src/window.c index a72ec0d..88ff7d3 100644 --- a/src/window.c +++ b/src/window.c @@ -783,7 +783,7 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ } } } - /* Fall through. */ + /* no break */ deliberate_fall_through case TK_AGG_FUNCTION: case TK_COLUMN: { @@ -803,6 +803,7 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup); } if( p->pSub ){ + int f = pExpr->flags & EP_Collate; assert( ExprHasProperty(pExpr, EP_Static)==0 ); ExprSetProperty(pExpr, EP_Static); sqlite3ExprDelete(pParse->db, pExpr); @@ -813,6 +814,7 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){ pExpr->iColumn = (iCol<0 ? p->pSub->nExpr-1: iCol); pExpr->iTable = p->pWin->iEphCsr; pExpr->y.pTab = p->pTab; + pExpr->flags = f; } if( pParse->db->mallocFailed ) return WRC_Abort; break; @@ -895,13 +897,19 @@ static ExprList *exprListAppendList( int i; int nInit = pList ? pList->nExpr : 0; for(i=0; inExpr; i++){ - int iDummy; Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0); assert( pDup==0 || !ExprHasProperty(pDup, EP_MemToken) ); - if( bIntToNull && pDup && sqlite3ExprIsInteger(pDup, &iDummy) ){ - pDup->op = TK_NULL; - pDup->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); - pDup->u.zToken = 0; + if( bIntToNull && pDup ){ + int iDummy; + Expr *pSub; + for(pSub=pDup; ExprHasProperty(pSub, EP_Skip); pSub=pSub->pLeft){ + assert( pSub ); + } + if( sqlite3ExprIsInteger(pSub, &iDummy) ){ + pSub->op = TK_NULL; + pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse); + pSub->u.zToken = 0; + } } pList = sqlite3ExprListAppend(pParse, pList, pDup); if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags; @@ -910,6 +918,23 @@ static ExprList *exprListAppendList( return pList; } +/* +** When rewriting a query, if the new subquery in the FROM clause +** contains TK_AGG_FUNCTION nodes that refer to an outer query, +** then we have to increase the Expr->op2 values of those nodes +** due to the extra subquery layer that was added. +** +** See also the incrAggDepth() routine in resolve.c +*/ +static int sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_AGG_FUNCTION + && pExpr->op2>=pWalker->walkerDepth + ){ + pExpr->op2++; + } + return WRC_Continue; +} + /* ** If the SELECT statement passed as the second argument does not invoke ** any SQL window functions, this function is a no-op. Otherwise, it @@ -930,14 +955,19 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ ExprList *pSort = 0; ExprList *pSublist = 0; /* Expression list for sub-query */ - Window *pMWin = p->pWin; /* Master window object */ + Window *pMWin = p->pWin; /* Main window object */ Window *pWin; /* Window object iterator */ Table *pTab; + Walker w; + + u32 selFlags = p->selFlags; pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ){ return sqlite3ErrorToParser(db, SQLITE_NOMEM); } + sqlite3AggInfoPersistWalkerInit(&w, pParse); + sqlite3WalkSelect(&w, p); p->pSrc = 0; p->pWhere = 0; @@ -1015,6 +1045,9 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ pSub = sqlite3SelectNew( pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 ); + SELECTTRACE(1,pParse,pSub, + ("New window-function subquery in FROM clause of (%u/%p)\n", + p->selId, p)); p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( p->pSrc ){ Table *pTab2; @@ -1022,6 +1055,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ sqlite3SrcListAssignCursors(pParse, p->pSrc); pSub->selFlags |= SF_Expanded; pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE); + pSub->selFlags |= (selFlags & SF_Aggregate); if( pTab2==0 ){ /* Might actually be some other kind of error, but in that case ** pParse->nErr will be set, so if SQLITE_NOMEM is set, we will get @@ -1032,6 +1066,11 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ pTab->tabFlags |= TF_Ephemeral; p->pSrc->a[0].pTab = pTab; pTab = pTab2; + memset(&w, 0, sizeof(w)); + w.xExprCallback = sqlite3WindowExtraAggFuncDepth; + w.xSelectCallback = sqlite3WalkerDepthIncrease; + w.xSelectCallback2 = sqlite3WalkerDepthDecrease; + sqlite3WalkSelect(&w, pSub); } }else{ sqlite3SelectDelete(db, pSub); @@ -1045,7 +1084,6 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ assert( pParse->db->mallocFailed ); sqlite3ErrorToParser(pParse->db, SQLITE_NOMEM); } - sqlite3SelectReset(pParse, p); } return rc; } @@ -1910,6 +1948,7 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){ Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ FuncDef *pFunc = pWin->pFunc; + assert( pWin->regAccum ); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); nArg = MAX(nArg, windowArgCount(pWin)); if( pMWin->regStartRowid==0 ){ @@ -2288,6 +2327,10 @@ Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ pNew->eStart = p->eStart; pNew->eExclude = p->eExclude; pNew->regResult = p->regResult; + pNew->regAccum = p->regAccum; + pNew->iArgCol = p->iArgCol; + pNew->iEphCsr = p->iEphCsr; + pNew->bExprArgs = p->bExprArgs; pNew->pStart = sqlite3ExprDup(db, p->pStart, 0); pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0); pNew->pOwner = pOwner; diff --git a/test/aggnested.test b/test/aggnested.test index d712c84..dcb1f95 100644 --- a/test/aggnested.test +++ b/test/aggnested.test @@ -17,6 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix aggnested do_test aggnested-1.1 { db eval { @@ -259,6 +260,52 @@ do_execsql_test aggnested-4.4 { SELECT max((SELECT a FROM (SELECT count(*) AS a FROM ty) AS s)) FROM tx; } {3} +#-------------------------------------------------------------------------- +# +reset_db +do_execsql_test 5.0 { + CREATE TABLE x1(a, b); + INSERT INTO x1 VALUES(1, 2); + CREATE TABLE x2(x); + INSERT INTO x2 VALUES(NULL), (NULL), (NULL); +} + +# At one point, aggregate "total()" in the query below was being processed +# as part of the outer SELECT, not as part of the sub-select with no FROM +# clause. +do_execsql_test 5.1 { + SELECT ( SELECT total( (SELECT b FROM x1) ) ) FROM x2; +} {2.0 2.0 2.0} + +do_execsql_test 5.2 { + SELECT ( SELECT total( (SELECT 2 FROM x1) ) ) FROM x2; +} {2.0 2.0 2.0} + +do_execsql_test 5.3 { + CREATE TABLE t1(a); + CREATE TABLE t2(b); +} + +do_execsql_test 5.4 { + SELECT( + SELECT max(b) LIMIT ( + SELECT total( (SELECT a FROM t1) ) + ) + ) + FROM t2; +} {{}} + +do_execsql_test 5.5 { + CREATE TABLE a(b); + WITH c AS(SELECT a) + SELECT(SELECT(SELECT group_concat(b, b) + LIMIT(SELECT 0.100000 * + AVG(DISTINCT(SELECT 0 FROM a ORDER BY b, b, b)))) + FROM a GROUP BY b, + b, b) FROM a EXCEPT SELECT b FROM a ORDER BY b, + b, b; +} + diff --git a/test/alter.test b/test/alter.test index 0ec485e..43d2a6d 100644 --- a/test/alter.test +++ b/test/alter.test @@ -840,6 +840,7 @@ do_test alter-13.3 { do_test alter-14.1 { catchsql { CREATE TABLE t3651(a UNIQUE); + INSERT INTO t3651 VALUES(5); ALTER TABLE t3651 ADD COLUMN b UNIQUE; } } {1 {Cannot add a UNIQUE column}} diff --git a/test/alter3.test b/test/alter3.test index b16a7f3..30bc1cb 100644 --- a/test/alter3.test +++ b/test/alter3.test @@ -116,6 +116,7 @@ do_test alter3-1.99 { do_test alter3-2.1 { execsql { CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1,2); } catchsql { ALTER TABLE t1 ADD c PRIMARY KEY; diff --git a/test/alter4.test b/test/alter4.test index 92f33e7..3aca7df 100644 --- a/test/alter4.test +++ b/test/alter4.test @@ -123,6 +123,7 @@ do_test alter4-1.99 { do_test alter4-2.1 { execsql { CREATE TABLE temp.t1(a, b); + INSERT INTO t1 VALUES(1,2); } catchsql { ALTER TABLE t1 ADD c PRIMARY KEY; @@ -397,6 +398,7 @@ do_test alter4-10.1 { reset_db do_execsql_test alter4-11.0 { CREATE TABLE t1(c INTEGER PRIMARY KEY, d); + INSERT INTO t1(c,d) VALUES(1,2); PRAGMA foreign_keys = on; ALTER TABLE t1 ADD COLUMN e; } diff --git a/test/altertab.test b/test/altertab.test index 7dcf8a5..435620d 100644 --- a/test/altertab.test +++ b/test/altertab.test @@ -594,7 +594,6 @@ reset_db do_execsql_test 18.1.0 { CREATE TABLE t0 (c0 INTEGER, PRIMARY KEY(c0)) WITHOUT ROWID; } -breakpoint do_execsql_test 18.1.1 { ALTER TABLE t0 RENAME COLUMN c0 TO c1; } @@ -613,4 +612,69 @@ do_execsql_test 18.2.2 { SELECT sql FROM sqlite_master; } {{CREATE TABLE t0 (c1 INTEGER, PRIMARY KEY(c1))}} +# 2020-02-23 ticket f50af3e8a565776b +reset_db +do_execsql_test 19.100 { + CREATE TABLE t1(x); + CREATE VIEW t2 AS SELECT 1 FROM t1, (t1 AS a0, t1); + ALTER TABLE t1 RENAME TO t3; + SELECT sql FROM sqlite_master; +} {{CREATE TABLE "t3"(x)} {CREATE VIEW t2 AS SELECT 1 FROM "t3", ("t3" AS a0, "t3")}} +do_execsql_test 19.110 { + INSERT INTO t3(x) VALUES(123); + SELECT * FROM t2; +} {1} +do_execsql_test 19.120 { + INSERT INTO t3(x) VALUES('xyz'); + SELECT * FROM t2; +} {1 1 1 1 1 1 1 1} + +# Ticket 4722bdab08cb14 +reset_db +do_execsql_test 20.0 { + CREATE TABLE a(a); + CREATE VIEW b AS SELECT(SELECT *FROM c JOIN a USING(d, a, a, a) JOIN a) IN(); +} +do_execsql_test 20.1 { + ALTER TABLE a RENAME a TO e; +} {} + +reset_db +do_execsql_test 21.0 { + CREATE TABLE a(b); + CREATE VIEW c AS + SELECT NULL INTERSECT + SELECT NULL ORDER BY + likelihood(NULL, (d, (SELECT c))); +} {} +do_catchsql_test 21.1 { + SELECT likelihood(NULL, (d, (SELECT c))); +} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}} +do_catchsql_test 21.2 { + SELECT * FROM c; +} {1 {1st ORDER BY term does not match any column in the result set}} + +do_catchsql_test 21.3 { + ALTER TABLE a RENAME TO e; +} {1 {error in view c: 1st ORDER BY term does not match any column in the result set}} + +# After forum thread https://sqlite.org/forum/forumpost/ddbe1c7efa +# Ensure that PRAGMA schema_version=N causes a full schema reload. +# +reset_db +do_execsql_test 22.0 { + CREATE TABLE t1(a INT, b TEXT NOT NULL); + INSERT INTO t1 VALUES(1,2),('a','b'); + BEGIN; + PRAGMA writable_schema=ON; + UPDATE sqlite_schema SET sql='CREATE TABLE t1(a INT, b TEXT)' WHERE name LIKE 't1'; + PRAGMA schema_version=1234; + COMMIT; + PRAGMA integrity_check; +} {ok} +do_execsql_test 22.1 { + ALTER TABLE t1 ADD COLUMN c INT DEFAULT 78; + SELECT * FROM t1; +} {1 2 78 a b 78} + finish_test diff --git a/test/altertab3.test b/test/altertab3.test index b390655..005a0ee 100644 --- a/test/altertab3.test +++ b/test/altertab3.test @@ -586,5 +586,19 @@ do_execsql_test 24.4 { DELETE FROM v2; END}} +#------------------------------------------------------------------------ +# +reset_db +do_execsql_test 25.1 { + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(a, b, c); + CREATE TRIGGER ttt AFTER INSERT ON t1 BEGIN + UPDATE t1 SET a=t2.a FROM t2 WHERE t1.a=t2.a; + END; +} +#do_execsql_test 25.2 { +# ALTER TABLE t2 RENAME COLUMN a TO aaa; +#} + finish_test diff --git a/test/analyzeG.test b/test/analyzeG.test new file mode 100644 index 0000000..eb1853b --- /dev/null +++ b/test/analyzeG.test @@ -0,0 +1,88 @@ +# 2020-02-23 +# +# 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. +# +#*********************************************************************** +# Tests for functionality related to ANALYZE. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat4 { + finish_test + return +} +set testprefix analyzeG + +proc do_scan_order_test {tn sql expect} { + uplevel [list do_test $tn [subst -nocommands { + set res "" + db eval "explain query plan $sql" { + lappend res [set detail] + } + set res + }] [list {*}$expect]] +} + +#------------------------------------------------------------------------- +# Test cases 1.* seek to verify that even if an index is not used, its +# stat4 data may be used by the planner to estimate the number of +# rows that match an unindexed constraint on the same column. +# +do_execsql_test 1.0 { + PRAGMA automatic_index = 0; + CREATE TABLE t1(a, x); + CREATE TABLE t2(b, y); + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 + ) + INSERT INTO t1 SELECT (i%50), NULL FROM s; + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100 + ) + INSERT INTO t2 SELECT (CASE WHEN i<95 THEN 44 ELSE i END), NULL FROM s; +} + +# Join tables t1 and t2. Both contain 100 rows. (a=44) matches 2 rows +# in "t1", (b=44) matches 95 rows in table "t2". But the planner doesn't +# know this, so it has no preference as to which order the tables are +# scanned in. In practice this means that tables are scanned in the order +# they are specified in in the FROM clause. +do_scan_order_test 1.1.1 { + SELECT * FROM t1, t2 WHERE a=44 AND b=44; +} { + {SCAN TABLE t1} {SCAN TABLE t2} +} +do_scan_order_test 1.1.2 { + SELECT * FROM t2, t1 WHERE a=44 AND b=44 +} { + {SCAN TABLE t2} {SCAN TABLE t1} +} + +do_execsql_test 1.2 { + CREATE INDEX t2b ON t2(b); + ANALYZE; +} + +# Now, with the ANALYZE data, the planner knows that (b=44) matches a +# large number of rows. So it elects to scan table "t1" first, regardless +# of the order in which the tables are specified in the FROM clause. +do_scan_order_test 1.3.1 { + SELECT * FROM t1, t2 WHERE a=44 AND b=44; +} { + {SCAN TABLE t1} {SCAN TABLE t2} +} +do_scan_order_test 1.3.2 { + SELECT * FROM t2, t1 WHERE a=44 AND b=44 +} { + {SCAN TABLE t1} {SCAN TABLE t2} +} + + +finish_test diff --git a/test/atrc.c b/test/atrc.c index c6e4ce3..673f12c 100644 --- a/test/atrc.c +++ b/test/atrc.c @@ -75,7 +75,7 @@ int rename_all_tables( int cnt = 0; rc = sqlite3_prepare_v2(db, - "SELECT name FROM sqlite_master WHERE type='table'" + "SELECT name FROM sqlite_schema WHERE type='table'" " AND name NOT LIKE 'sqlite_%';", -1, &pStmt, 0); if( rc ) return rc; diff --git a/test/attach.test b/test/attach.test index 5ddab1a..1e1d426 100644 --- a/test/attach.test +++ b/test/attach.test @@ -148,10 +148,8 @@ do_test attach-1.14 { ATTACH 'test.db' as db9; } } {1 {database db9 is already in use}} -do_test attach-1.15 { - catchsql { - ATTACH 'test.db' as main; - } +do_catchsql_test attach-1.15 { + ATTACH 'test.db' as main; } {1 {database main is already in use}} ifcapable tempdb { do_test attach-1.16 { @@ -160,10 +158,8 @@ ifcapable tempdb { } } {1 {database temp is already in use}} } -do_test attach-1.17 { - catchsql { - ATTACH 'test.db' as MAIN; - } +do_catchsql_test attach-1.17 { + ATTACH 'test.db' as MAIN; } {1 {database MAIN is already in use}} do_test attach-1.18 { catchsql { @@ -231,6 +227,7 @@ do_test attach-1.26 { } } {1 {cannot detach database main}} + ifcapable tempdb { do_test attach-1.27 { catchsql { diff --git a/test/bestindex7.test b/test/bestindex7.test new file mode 100644 index 0000000..aa1d70b --- /dev/null +++ b/test/bestindex7.test @@ -0,0 +1,79 @@ +# 2020-01-29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix bestindex7 + +ifcapable !vtab { + finish_test + return +} + +register_tcl_module db + +proc vtab_command {src method args} { + switch -- $method { + xConnect { + return "CREATE TABLE xxx(a)" + } + + xBestIndex { + set clist [lindex $args 0] + set iCons 0 + set ret [list] + foreach cons $clist { + catch { array unset C } + array set C $cons + if {$C(usable)} { + lappend ret use $iCons + } + incr iCons + } + return $ret + } + + xFilter { + return [list sql "SELECT rowid, x FROM $src"] + } + + } + + return {} +} + +do_execsql_test 1.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(0), (2); + CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1); +} + +do_execsql_test 1.1 { select * from vt1 } {0 2} +do_execsql_test 1.2 { select * from vt1 WHERE a=0 } {0} +do_execsql_test 1.3 { select * from vt1 WHERE a=1 } {} +do_execsql_test 1.4 { select * from vt1 WHERE a=1 OR a=0} {0} + +do_execsql_test 1.5 { + UPDATE t1 SET x=NULL WHERE x=2; +} + +do_execsql_test 1.6 { select * from vt1 } {0 {}} +do_execsql_test 1.7 { select * from vt1 WHERE a=0 } {0} +do_execsql_test 1.8 { select * from vt1 WHERE a=1 } {} +do_execsql_test 1.9 { select * from vt1 WHERE a=1 OR a=0} {0} +do_execsql_test 1.10 { select * from vt1 WHERE a IN (2) } {} +do_execsql_test 1.10 { select * from vt1 WHERE a IN (0,1,2,3) } {0} +do_execsql_test 1.11 { select * from vt1 WHERE a IN (0, NULL) } {0} +do_execsql_test 1.12 { select * from vt1 WHERE a IN (NULL) } {} + +finish_test + diff --git a/test/busy2.test b/test/busy2.test new file mode 100644 index 0000000..fb9ef23 --- /dev/null +++ b/test/busy2.test @@ -0,0 +1,135 @@ +# 2020 June 30 +# +# 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 test the busy handler +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set testprefix busy2 + +do_multiclient_test tn { + do_test 1.$tn.0 { + sql2 { + CREATE TABLE t1(a, b); + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES('A', 'B'); + } + } {wal} + + do_test 1.$tn.1 { + code1 { db timeout 1000 } + sql1 { SELECT * FROM t1 } + } {A B} + + do_test 1.$tn.2 { + sql2 { + BEGIN; + INSERT INTO t1 VALUES('C', 'D'); + } + } {} + + do_test 1.$tn.3 { + set us [lindex [time { catch { sql1 { BEGIN EXCLUSIVE } } }] 0] + expr {$us>950000 && $us<1500000} + } {1} + + do_test 1.$tn.4 { + sql2 { + COMMIT + } + } {} +} + +#------------------------------------------------------------------------- + +do_multiclient_test tn { + # Make the db a WAL mode db. And add a table and a row to it. Then open + # a second connection within process 1. Process 1 now has connections + # [db] and [db1.2], process 2 has connection [db2] only. + # + # Configure all connections to use a 1000 ms timeout. + # + do_test 2.$tn.0 { + code1 { + sqlite3 db1.2 test.db + } + sql1 { + PRAGMA auto_vacuum = off; + PRAGMA journal_mode = wal; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + code2 { + db2 timeout 1000 + } + code1 { + db1.2 timeout 1000 + db timeout 1000 + db1.2 eval {SELECT * FROM t1} + } + } {1 2} + + # Take a read lock with [db] in process 1. + # + do_test 2.$tn.1 { + sql1 { + BEGIN; + SELECT * FROM t1; + } + } {1 2} + + # Insert a row using [db2] in process 2. Then try a passive checkpoint. + # It fails to checkpoint the final frame (due to the readlock taken by + # [db]), and returns in less than 250ms. + do_test 2.$tn.2 { + sql2 { INSERT INTO t1 VALUES(3, 4) } + set us [lindex [time { + set res [code2 { db2 eval { PRAGMA wal_checkpoint } }] + }] 0] + list [expr $us < 250000] $res + } {1 {0 4 3}} + + # Now try a FULL checkpoint with [db2]. It returns SQLITE_BUSY. And takes + # over 950ms to do so. + do_test 2.$tn.3 { + set us [lindex [time { + set res [code2 { db2 eval { PRAGMA wal_checkpoint = FULL } }] + }] 0] + list [expr $us > 950000] $res + } {1 {1 4 3}} + + # Passive checkpoint with [db1.2] (process 1). No SQLITE_BUSY, returns + # in under 250ms. + do_test 2.$tn.4 { + set us [lindex [time { + set res [code1 { db1.2 eval { PRAGMA wal_checkpoint } }] + }] 0] + list [expr $us < 250000] $res + } {1 {0 4 3}} + + # Full checkpoint with [db1.2] (process 1). SQLITE_BUSY returned in + # a bit over 950ms. + do_test 2.$tn.5 { + set us [lindex [time { + set res [code1 { db1.2 eval { PRAGMA wal_checkpoint = FULL } }] + }] 0] + list [expr $us > 950000] $res + } {1 {1 4 3}} + + code1 { + db1.2 close + } +} + +finish_test + diff --git a/test/check.test b/test/check.test index 3e16b9d..d28006e 100644 --- a/test/check.test +++ b/test/check.test @@ -166,7 +166,7 @@ do_test check-2.6 { catchsql { INSERT INTO t2 VALUES(NULL, NULL, 3.14159); } -} {1 {CHECK constraint failed: three}} +} {0 {}} # Undocumented behavior: The CONSTRAINT name clause can follow a constraint. # Such a clause is ignored. But the parser must accept it for backwards diff --git a/test/corrupt3.test b/test/corrupt3.test index 3c911da..7a2e174 100644 --- a/test/corrupt3.test +++ b/test/corrupt3.test @@ -94,7 +94,7 @@ do_test corrupt3-1.9 { catchsql { SELECT substr(x,1,10) FROM t1 } -} [list 0 0123456789] +} [list 1 {database disk image is malformed}] do_test corrupt3-1.10 { catchsql { PRAGMA integrity_check diff --git a/test/corruptL.test b/test/corruptL.test index 72aedd9..9af9fd2 100644 --- a/test/corruptL.test +++ b/test/corruptL.test @@ -1138,5 +1138,204 @@ do_catchsql_test 13.1 { INSERT INTO t1(b,c) SELECT last_insert_rowid(), x FROM c; } {1 {database disk image is malformed}} +#------------------------------------------------------------------------- +reset_db +do_test 14.0 { + sqlite3 db {} + db deserialize [decode_hexdb { +| size 512 pagesize 65536 filename clusterfuzz-testcase-minimized-sqlite3_dbfuzz2_fuzzer-4806406219825152 +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 00 01 02 01 00 40 20 20 00 63 2e 78 00 00 00 07 .....@ .c.x.... +| 32: 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00 04 ................ +| 48: 00 00 00 00 00 00 01 00 35 05 43 00 04 00 00 00 ........5.C..... +| 80: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 0c ................ +| 96: 00 2e 2c 50 0d 00 00 00 03 00 00 00 01 da 01 b0 ..,P............ +| 112: 01 56 01 86 01 2a 01 02 00 00 00 00 00 00 00 1c .V...*.......... +| 128: 00 38 80 b2 e6 0e 00 00 00 00 00 00 00 00 00 10 .8.............. +| 144: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................ +| 160: 00 00 00 00 00 00 00 00 00 00 00 00 45 20 54 41 ............E TA +| 256: 00 00 00 00 00 00 22 07 06 17 11 11 01 35 74 61 .............5ta +| 272: 62 6c 00 10 00 00 34 07 43 52 54 45 20 54 41 42 bl....4.CRTE TAB +| 288: 4c 45 20 74 33 28 63 2e 78 2c 65 2c 66 15 28 3a LE t3(c.x,e,f.(: +| 304: 06 17 11 11 01 65 78 8c cc 87 85 35 05 43 72 45 .....ex....5.CrE +| 320: 41 54 48 20 49 4e 44 45 58 20 74 33 78 20 4f 4e ATH INDEX t3x ON +| 336: 20 74 33 28 78 39 2e 04 06 17 15 11 01 45 69 6e t3(x9.......Ein +| 352: 64 65 78 74 32 63 64 74 32 05 43 52 45 41 54 45 dext2cdt2.CREATE +| 368: 20 49 4e 44 45 58 20 74 32 63 64 20 4f 4e 20 74 INDEX t2cd ON t +| 384: 32 28 63 2a 44 29 28 05 fa e8 ee ed 01 3d 74 63 2(c*D)(......=tc +| 400: 62 6c 65 74 33 74 33 07 43 52 45 41 54 45 20 54 blet3t3.CREATE T +| 416: 41 42 4c 45 20 74 33 28 63 2e 78 2c 65 2c 66 15 ABLE t3(c.x,e,f. +| 432: 28 3a 06 17 11 11 01 3d 74 61 62 6c 65 74 32 74 (:.....=tablet2t +| 448: 32 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 2.CREATE TABLE t +| 464: 32 28 63 2c 64 2c 65 2c 66 29 24 01 06 17 11 11 2(c,d,e,f)$..... +| 480: 01 35 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 .5tablet1t1.CREA +| 496: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 63 29 TE TABLE t1(a,c) +| end clusterfuzz-testcase-minimized-sqlite3_dbfuzz2_fuzzer-4806406219825152 +}]} {} + +extra_schema_checks 0 +do_catchsql_test 14.1 { + PRAGMA integrity_check; +} {1 {database disk image is malformed}} + +do_catchsql_test 14.2 { + ALTER TABLE t1 RENAME TO alkjalkjdfiiiwuer987lkjwer82mx97sf98788s9789s; +} {1 {database disk image is malformed}} +extra_schema_checks 1 + +#------------------------------------------------------------------------- +reset_db +do_test 15.0 { + sqlite3 db {} + db deserialize [decode_hexdb { +| size 28672 pagesize 4096 filename crash-3afa1ca9e9c1bd.db +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 07 .....@ ........ +| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................ +| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ +| 96: 00 00 00 00 0d 00 00 00 06 0e 88 00 0f b8 0f 6d ...............m +| 112: 0f 3a 0f 0b 0e d5 0e 88 01 00 00 00 00 00 00 00 .:.............. +| 3712: 00 00 00 00 00 00 00 00 4b 06 06 17 25 25 01 5b ........K...%%.[ +| 3728: 74 61 62 6c 65 73 71 6c 69 74 65 5f 73 74 61 74 tablesqlite_stat +| 3744: 31 73 71 6c 69 74 65 5f 73 74 61 74 31 07 43 52 1sqlite_stat1.CR +| 3760: 45 41 54 45 20 54 41 42 4c 45 20 73 71 6c 69 74 EATE TABLE sqlit +| 3776: 65 5f 73 74 61 74 31 28 74 62 6c 2c 69 64 78 2c e_stat1(tbl,idx, +| 3792: 73 74 61 74 29 34 05 06 17 13 11 01 53 69 6e 64 stat)4......Sind +| 3808: 65 78 63 31 63 63 31 06 43 52 45 41 54 45 20 55 exc1cc1.CREATE U +| 3824: 4e 49 51 55 45 20 49 4e 44 45 58 20 63 31 63 20 NIQUE INDEX c1c +| 3840: 4f 4e 20 63 31 28 63 2c 20 62 29 2d 04 06 17 13 ON c1(c, b)-.... +| 3856: 11 01 45 69 6e 64 65 78 63 31 64 63 31 05 43 52 ..Eindexc1dc1.CR +| 3872: 45 41 54 45 20 49 4e 44 45 58 20 63 31 64 20 4f EATE INDEX c1d O +| 3888: 4e 20 63 31 28 64 2c 20 62 29 31 03 06 17 13 11 N c1(d, b)1..... +| 3904: 01 4d 69 6e 64 65 78 62 31 63 62 31 05 43 52 45 .Mindexb1cb1.CRE +| 3920: 41 54 45 20 55 4e 49 51 55 45 20 49 4e 44 45 58 ATE UNIQUE INDEX +| 3936: 20 62 31 63 20 4f 4e 20 62 31 28 63 29 49 02 06 b1c ON b1(c)I.. +| 3952: 17 11 11 0f 7f 74 61 62 6c 65 63 31 63 31 03 43 .....tablec1c1.C +| 3968: 52 45 41 54 45 20 54 41 42 4c 45 20 63 31 28 61 REATE TABLE c1(a +| 3984: 20 49 4e 54 20 50 52 49 4d 41 52 59 20 4b 45 59 INT PRIMARY KEY +| 4000: 2c 20 62 2c 20 63 2c 20 64 29 20 57 49 54 48 4f , b, c, d) WITHO +| 4016: 55 54 20 52 4f 57 49 44 46 01 06 17 11 11 01 79 UT ROWIDF......y +| 4032: 74 61 62 6c 65 62 31 62 31 02 43 52 45 41 54 45 tableb1b1.CREATE +| 4048: 20 54 41 42 4c 45 20 62 31 28 61 20 49 4e 54 20 TABLE b1(a INT +| 4064: 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 2c 20 PRIMARY KEY, b, +| 4080: 63 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 c) WITHOUT ROWID +| page 2 offset 4096 +| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f e2 ................ +| 16: 0f da 00 00 00 01 00 00 00 00 00 00 00 00 00 00 ................ +| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 06 ................ +| 4048: 67 07 07 04 01 0f 01 06 66 06 07 04 01 0f 01 05 g.......f....... +| 4064: 65 05 07 04 01 0f 01 04 64 04 07 04 01 0f 01 03 e.......d....... +| 4080: 63 03 07 04 01 0f 01 02 62 0f 05 04 09 0f 09 61 c.......b......a +| page 3 offset 8192 +| 0: 0a 00 00 00 07 0f bd 00 0f f9 0f ef 0f e5 0f db ................ +| 16: 0f d1 0f c7 0f bd 00 00 00 00 01 00 00 00 00 00 ................ +| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 09 05 01 ................ +| 4032: 0f 01 01 07 61 07 07 09 05 01 0f 01 01 06 61 06 ....a.........a. +| 4048: 06 09 05 01 0f 01 01 05 61 05 05 09 05 01 0f 01 ........a....... +| 4064: 01 04 61 04 04 09 05 01 0f 01 01 03 61 03 03 09 ..a.........a... +| 4080: 05 01 0f 01 01 02 61 0f 02 06 05 09 0f 09 09 61 ......a........a +| page 4 offset 12288 +| 0: 0a 00 00 00 07 0f d8 00 0f fc 0f f0 0f ea 0f e4 ................ +| 16: 0f de 0f d8 0f f6 00 00 00 00 00 00 00 00 00 00 ................ +| 4048: 00 00 00 00 00 00 00 00 05 03 01 01 07 07 05 03 ................ +| 4064: 01 01 06 06 05 03 01 01 05 05 05 03 01 01 04 04 ................ +| 4080: 05 03 01 01 03 03 05 03 01 01 0f 02 03 03 09 09 ................ +| page 5 offset 16384 +| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f f2 0f ea 0f 00 ................ +| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................ +| 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a....... +| 4064: 61 05 07 04 01 1f 01 04 61 04 07 04 01 0f 01 03 a.......a....... +| 4080: 61 03 07 04 01 0f 01 02 61 02 05 04 09 0f 09 61 a.......a......a +| page 6 offset 20480 +| 0: 0a 00 00 00 07 0f ca 00 0f fa 0f ea 0f e2 00 00 ................ +| 4032: 00 00 00 00 00 00 00 00 00 00 07 04 01 0f 01 07 ................ +| 4048: 61 07 07 04 01 0f 01 06 61 06 07 04 01 0f 01 05 a.......a....... +| 4064: 61 05 07 04 01 0f 01 04 61 04 07 04 01 0f 01 03 a.......a....... +| 4080: 61 03 07 04 01 0f 01 0f 61 02 05 04 09 0f 09 61 a.......a......a +| page 7 offset 24576 +| 0: 0d 00 00 00 05 0f 1c 00 0f f0 0f e0 0f d3 0f c5 ................ +| 16: 0f b8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +| 4016: 00 00 00 00 00 00 00 00 0b 05 04 11 11 13 62 31 ..............b1 +| 4032: 62 31 37 20 31 0c 04 04 11 13 13 62 31 62 31 63 b17 1......b1b1c +| 4048: 37 20 31 0b 03 04 11 11 13 63 31 63 31 37 20 31 7 1......c1c17 1 +| 4064: 0e 02 04 11 13 07 63 31 63 31 64 37 20 31 20 31 ......c1c1d7 1 1 +| 4080: 0e 01 04 11 13 17 63 31 63 31 63 37 20 31 00 00 ......c1c1c7 1.. +| end crash-3afa1ca9e9c1bd.db +}]} {} + +extra_schema_checks 0 +do_execsql_test 15.1 { + PRAGMA cell_size_check = 0; + UPDATE c1 SET c= NOT EXISTS(SELECT 1 FROM c1 ORDER BY (SELECT 1 FROM c1 ORDER BY a)) +10 WHERE d BETWEEN 4 AND 7; +} {} +extra_schema_checks 1 + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 16.0 { + CREATE TABLE t1(w, x, y, z, UNIQUE(w, x), UNIQUE(y, z)); + INSERT INTO t1 VALUES(1, 1, 1, 1); + + CREATE TABLE t1idx(x, y, i INTEGER, PRIMARY KEY(x)) WITHOUT ROWID; + INSERT INTO t1idx VALUES(10, NULL, 5); + + PRAGMA writable_schema = 1; + UPDATE sqlite_master SET rootpage = ( + SELECT rootpage FROM sqlite_master WHERE name='t1idx' + ) WHERE type = 'index'; +} + +extra_schema_checks 0 +db close +sqlite3 db test.db +extra_schema_checks 1 + +do_catchsql_test 16.1 { + PRAGMA writable_schema = ON; + INSERT INTO t1(rowid, w, x, y, z) VALUES(5, 10, 11, 10, NULL); +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +# Test that corruption is reported from within a checkpoint if the +# expected final size of the database (according to the last commit +# frame in the wal file) is greater than the combined initial sizes +# of the database and wal file. +# +if {[wal_is_capable]} { + reset_db + do_execsql_test 17.0 { + CREATE TABLE t1(o INTEGER PRIMARY KEY, t UNIQUE); + INSERT INTO t1(t) VALUES(randomblob(123)); + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES(-1, 'b'); + } {wal} + + do_test 17.1 { + set fd [open test.db r+] + chan truncate $fd 2048 + file size test.db + } {2048} + + do_catchsql_test 17.2 { + PRAGMA wal_checkpoint + } {1 {database disk image is malformed}} + + do_test 17.3 { + close $fd + } {} +} + finish_test diff --git a/test/cost.test b/test/cost.test index 2922a0a..592973a 100644 --- a/test/cost.test +++ b/test/cost.test @@ -230,10 +230,10 @@ do_test 9.2 { set L [list a=? b=? c=? d=? e=? f=? g=? h=? i=? j=?] foreach {tn nTerm nRow} { 1 1 10 - 2 2 9 + 2 2 10 3 3 8 4 4 7 - 5 5 6 + 5 5 7 6 6 5 7 7 5 8 8 5 diff --git a/test/count.test b/test/count.test index 862b62a..250eb66 100644 --- a/test/count.test +++ b/test/count.test @@ -196,4 +196,42 @@ do_catchsql_test count-6.1 { SELECT count(DISTINCT) FROM t6 GROUP BY x; } {1 {DISTINCT aggregates must have exactly one argument}} +# 2020-05-08. +# The count() optimization should honor the NOT INDEXED clause +# +reset_db +do_execsql_test count-7.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c VARCHAR(1000)); + CREATE INDEX t1b ON t1(b); + INSERT INTO t1(a,b,c) values(1,2,'count.test cases for NOT INDEXED'); + ANALYZE; + UPDATE sqlite_stat1 SET stat='1000000 10' WHERE idx='t1b'; + ANALYZE sqlite_master; +} +do_eqp_test count-7.2 { + SELECT count(1) FROM t1; +} { + QUERY PLAN + `--SCAN TABLE t1 USING COVERING INDEX t1b +} +do_eqp_test count-7.3 { + SELECT count(1) FROM t1 NOT INDEXED +} { + QUERY PLAN + `--SCAN TABLE t1 +} +do_eqp_test count-7.3 { + SELECT count(*) FROM t1; +} { + QUERY PLAN + `--SCAN TABLE t1 USING COVERING INDEX t1b +} +do_eqp_test count-7.4 { + SELECT count(*) FROM t1 NOT INDEXED +} { + QUERY PLAN + `--SCAN TABLE t1 +} + + finish_test diff --git a/test/cse.test b/test/cse.test index 57cdef5..3912bc3 100644 --- a/test/cse.test +++ b/test/cse.test @@ -18,6 +18,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix cse do_test cse-1.1 { execsql { @@ -157,4 +158,43 @@ for {set i 1} {$i<100} {incr i} { } $answer } +#------------------------------------------------------------------------- +# Ticket fd1bda016d1a +# +reset_db +do_execsql_test 3.0 { + CREATE TABLE t1(a TEXT, b); + INSERT INTO t1 VALUES('hello', 0); + INSERT INTO t1 VALUES('world', 0); + + CREATE TABLE t2(x TEXT); + INSERT INTO t2 VALUES('hello'); + INSERT INTO t2 VALUES('world'); + + CREATE TABLE t3(y); + INSERT INTO t3 VALUES(1000); +} {} + +do_execsql_test 3.1 { + SELECT 1000 = y FROM t3 +} {1} + +do_execsql_test 3.2 { + SELECT 1000 IN (SELECT x FROM t2), 1000 = y FROM t3 +} {0 1} + +do_execsql_test 3.3 { + SELECT 0 IN (SELECT a), (SELECT a LIMIT 0) FROM t1 +} {0 {} 0 {}} + +do_execsql_test 3.4 { + SELECT 0 IN (SELECT a) FROM t1 WHERE a = 'hello' OR (SELECT a LIMIT 0); +} {0} + +do_execsql_test 3.5 { + CREATE TABLE v0(v1 VARCHAR0); + INSERT INTO v0 VALUES(2), (3); + SELECT 0 IN(SELECT v1) FROM v0 WHERE v1 = 2 OR(SELECT v1 LIMIT 0); +} {0} + finish_test diff --git a/test/cursorhint.test b/test/cursorhint.test index ae86120..a3397b8 100644 --- a/test/cursorhint.test +++ b/test/cursorhint.test @@ -69,7 +69,7 @@ do_test 1.2 { p5_of_opcode db OpenRead { SELECT * FROM t1 CROSS JOIN t2 WHERE a=x } -} {00 00} +} {0 0} # Do the same test the other way around. # @@ -82,7 +82,7 @@ do_test 2.2 { p5_of_opcode db OpenRead { SELECT * FROM t2 CROSS JOIN t1 WHERE a=x } -} {00 00} +} {0 0} # Various expressions captured by CursorHint # @@ -117,7 +117,7 @@ do_test 4.2 { p5_of_opcode db OpenRead { SELECT * FROM t1 WHERE b>11; } -} {02 00} +} {2 0} do_test 4.3asc { p4_of_opcode db CursorHint { SELECT c FROM t1 WHERE b<11 ORDER BY b ASC; @@ -132,7 +132,7 @@ do_test 4.4 { p5_of_opcode db OpenRead { SELECT c FROM t1 WHERE b<11; } -} {00} +} {0} do_test 4.5asc { p4_of_opcode db CursorHint { diff --git a/test/dbfuzz001.test b/test/dbfuzz001.test index 7ef4cd2..2a430de 100644 --- a/test/dbfuzz001.test +++ b/test/dbfuzz001.test @@ -361,6 +361,7 @@ do_test dbfuzz001-310 { }] } {} +extra_schema_checks 0 do_catchsql_test dbfuzz001-320 { PRAGMA integrity_check; } {1 {database disk image is malformed}} @@ -368,5 +369,6 @@ do_catchsql_test dbfuzz001-320 { do_catchsql_test dbfuzz001-330 { DELETE FROM t3 WHERE x IN (SELECT x FROM t4); } {1 {database disk image is malformed}} +extra_schema_checks 1 finish_test diff --git a/test/dbfuzz2.c b/test/dbfuzz2.c index 804222c..e351629 100644 --- a/test/dbfuzz2.c +++ b/test/dbfuzz2.c @@ -54,7 +54,7 @@ */ static const char *azSql[] = { "PRAGMA integrity_check;", - "SELECT * FROM sqlite_master;", + "SELECT * FROM sqlite_schema;", "SELECT sum(length(name)) FROM dbstat;", "UPDATE t1 SET b=a, a=b WHERE a=0; +} {} +do_execsql_test 2010 { + SELECT *, '|' + FROM t1 AS a, t1 AS b + WHERE a.seq<>b.seq + AND decimal_cmp(a.val,b.val)==0; +} {} +do_execsql_test 2020 { + SELECT *, '|' + FROM t1 AS a, t1 AS b + WHERE a.seq>b.seq + AND decimal_cmp(a.val,b.val)<=0; +} {} +do_execsql_test 2030 { + SELECT seq FROM t1 ORDER BY val COLLATE decimal; +} {1 2 3 4 5 6 7 8 9 10 11 12} +do_execsql_test 2040 { + SELECT seq FROM t1 ORDER BY val COLLATE decimal DESC; +} {12 11 10 9 8 7 6 5 4 3 2 1} + +do_execsql_test 3000 { + CREATE TABLE t3(seq INTEGER PRIMARY KEY, val TEXT); + WITH RECURSIVE c(x) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<10) + INSERT INTO t3(seq, val) SELECT x, x FROM c; + WITH RECURSIVE c(x) AS (VALUES(1) UNION SELECT x+1 FROM c WHERE x<5) + INSERT INTO t3(seq, val) SELECT x+10, x*1000 FROM c; + SELECT decimal(val) FROM t3 ORDER BY seq; +} {1 2 3 4 5 6 7 8 9 10 1000 2000 3000 4000 5000} +do_execsql_test 3020 { + SELECT decimal_add(val,'0.5') FROM t3 WHERE seq>5 ORDER BY seq +} {6.5 7.5 8.5 9.5 10.5 1000.5 2000.5 3000.5 4000.5 5000.5} +do_execsql_test 3030 { + SELECT decimal_add(val,'-10') FROM t3 ORDER BY seq; +} {-9 -8 -7 -6 -5 -4 -3 -2 -1 0 990 1990 2990 3990 4990} + +do_execsql_test 4000 { + SELECT decimal_sum(val) FROM t3; +} {15055} +do_execsql_test 4010 { + SELECT decimal_sum(decimal_add(val,val||'e+10')) FROM t3; +} {150550000015055} +do_execsql_test 4010 { + SELECT decimal_sum(decimal_add(val||'e+20',decimal_add(val,val||'e-20'))) + FROM t3; +} {1505500000000000000015055.00000000000000015055} + +do_execsql_test 5000 { + WITH RECURSIVE c(x,y,z) AS ( + VALUES(0,'1','1') + UNION ALL + SELECT x+1, decimal_mul(y,'2'), decimal_mul(z,'0.5') + FROM c WHERE x<32 + ) + SELECT count(*) FROM c WHERE decimal_mul(y,z)='1'; +} {33} + +do_execsql_test 5100 { + SELECT decimal_mul('1234.00','2.00'); +} {2468.00} +do_execsql_test 5101 { + SELECT decimal_mul('1234.00','2.0000'); +} {2468.00} +do_execsql_test 5102 { + SELECT decimal_mul('1234.0000','2.000'); +} {2468.000} +do_execsql_test 5103 { + SELECT decimal_mul('1234.0000','2'); +} {2468} + +if {[catch {load_static_extension db ieee754} error]} { + puts "Skipping ieee754 tests, hit load error: $error" + finish_test; return +} + +do_execsql_test 6000 { + CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT); + WITH RECURSIVE c(x,v) AS ( + VALUES(0,'1') + UNION ALL + SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971 + ) INSERT INTO pow2(x,v) SELECT x, v FROM c; + WITH RECURSIVE c(x,v) AS ( + VALUES(-1,'0.5') + UNION ALL + SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075 + ) INSERT INTO pow2(x,v) SELECT x, v FROM c; +} {} +do_execsql_test 6010 { + WITH c(n) AS (SELECT ieee754_from_blob(x'0000000000000001')) +SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v) + FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n); +} {0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625} +do_execsql_test 6020 { + WITH c(n) AS (SELECT ieee754_from_blob(x'7fefffffffffffff')) +SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v) + FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n); +} {179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368} + +do_execsql_test 6100 { + SELECT ieee754(ieee754_from_blob(x'0000000000000001')); +} {ieee754(1,-1074)} +do_execsql_test 6110 { + SELECT ieee754(ieee754_from_blob(x'7fefffffffffffff')); +} {ieee754(9007199254740991,971)} +do_execsql_test 6120 { + SELECT printf('%.8e',ieee754_from_blob(x'0000000000000001')); +} {4.94065646e-324} +do_execsql_test 6130 { + SELECT printf('%.8e',ieee754_from_blob(x'ffefffffffffffff')); +} {-1.79769313e+308} + + + + +finish_test diff --git a/test/default.test b/test/default.test index d691303..06a180c 100644 --- a/test/default.test +++ b/test/default.test @@ -128,4 +128,13 @@ do_catchsql_test default-4.4 { CREATE TABLE t2(a TEXT, b TEXT DEFAULT(98+coalesce(5,:xyz))); } {1 {default value of column [b] is not constant}} +# 2020-03-09 out-of-bounds memory access discovered by "Eternal Sakura" +# and reported to chromium. +# +reset_db +do_catchsql_test default-5.1 { + CREATE TABLE t1 (a,b DEFAULT(random() NOTNULL IN (RAISE(IGNORE),2,3))); + INSERT INTO t1(a) VALUES(1); +} {1 {RAISE() may only be used within a trigger-program}} + finish_test diff --git a/test/distinct.test b/test/distinct.test index 9957ac3..f6f3c6c 100644 --- a/test/distinct.test +++ b/test/distinct.test @@ -51,8 +51,8 @@ proc do_temptables_test {tn sql temptables} { set ret "" db eval "EXPLAIN [set sql]" { if {$opcode == "OpenEphemeral" || $opcode == "SorterOpen"} { - if {$p5 != "08" && $p5!="00"} { error "p5 = $p5" } - if {$p5 == "08"} { + if {$p5!=8 && $p5!=0} { error "p5 = $p5" } + if {$p5==8} { lappend ret hash } else { lappend ret btree diff --git a/test/e_createtable.test b/test/e_createtable.test index 7f6cf48..c9742ea 100644 --- a/test/e_createtable.test +++ b/test/e_createtable.test @@ -395,17 +395,19 @@ do_createtable_tests 1.2.2 { 4 {CREATE TABLE auxb.xyz(z)} {} } drop_all_tables -do_createtable_tests 1.3 -tclquery { - unset -nocomplain X - array set X [table_list] - list $X(main) $X(temp) $X(auxa) $X(auxb) -} { - 1 "CREATE TABLE main.abc(a, b, c)" {abc {} {} {}} - 2 "CREATE TABLE main.t1(a, b, c)" {{abc t1} {} {} {}} - 3 "CREATE TABLE temp.tmp(a, b, c)" {{abc t1} tmp {} {}} - 4 "CREATE TABLE auxb.tbl(x, y)" {{abc t1} tmp {} tbl} - 5 "CREATE TABLE auxb.t1(k, v)" {{abc t1} tmp {} {t1 tbl}} - 6 "CREATE TABLE auxa.next(c, d)" {{abc t1} tmp next {t1 tbl}} +if {[permutation]!="maindbname"} { + do_createtable_tests 1.3 -tclquery { + unset -nocomplain X + array set X [table_list] + list $X(main) $X(temp) $X(auxa) $X(auxb) + } { + 1 "CREATE TABLE main.abc(a, b, c)" {abc {} {} {}} + 2 "CREATE TABLE main.t1(a, b, c)" {{abc t1} {} {} {}} + 3 "CREATE TABLE temp.tmp(a, b, c)" {{abc t1} tmp {} {}} + 4 "CREATE TABLE auxb.tbl(x, y)" {{abc t1} tmp {} tbl} + 5 "CREATE TABLE auxb.t1(k, v)" {{abc t1} tmp {} {t1 tbl}} + 6 "CREATE TABLE auxa.next(c, d)" {{abc t1} tmp next {t1 tbl}} + } } # EVIDENCE-OF: R-18895-27365 If the "TEMP" or "TEMPORARY" keyword occurs @@ -413,13 +415,15 @@ do_createtable_tests 1.3 -tclquery { # temp database. # drop_all_tables -do_createtable_tests 1.4 -tclquery { - unset -nocomplain X - array set X [table_list] - list $X(main) $X(temp) $X(auxa) $X(auxb) -} { - 1 "CREATE TEMP TABLE t1(a, b)" {{} t1 {} {}} - 2 "CREATE TEMPORARY TABLE t2(a, b)" {{} {t1 t2} {} {}} +if {[permutation]!="maindbname"} { + do_createtable_tests 1.4 -tclquery { + unset -nocomplain X + array set X [table_list] + list $X(main) $X(temp) $X(auxa) $X(auxb) + } { + 1 "CREATE TEMP TABLE t1(a, b)" {{} t1 {} {}} + 2 "CREATE TEMPORARY TABLE t2(a, b)" {{} {t1 t2} {} {}} + } } # EVIDENCE-OF: R-23976-43329 It is an error to specify both a @@ -436,30 +440,34 @@ do_createtable_tests 1.5.1 -error { 4 "CREATE TEMPORARY TABLE main.xxx(x)" {} } drop_all_tables -do_createtable_tests 1.5.2 -tclquery { - unset -nocomplain X - array set X [table_list] - list $X(main) $X(temp) $X(auxa) $X(auxb) -} { - 1 "CREATE TEMP TABLE temp.t1(a, b)" {{} t1 {} {}} - 2 "CREATE TEMPORARY TABLE temp.t2(a, b)" {{} {t1 t2} {} {}} - 3 "CREATE TEMP TABLE TEMP.t3(a, b)" {{} {t1 t2 t3} {} {}} - 4 "CREATE TEMPORARY TABLE TEMP.xxx(x)" {{} {t1 t2 t3 xxx} {} {}} +if {[permutation]!="maindbname"} { + do_createtable_tests 1.5.2 -tclquery { + unset -nocomplain X + array set X [table_list] + list $X(main) $X(temp) $X(auxa) $X(auxb) + } { + 1 "CREATE TEMP TABLE temp.t1(a, b)" {{} t1 {} {}} + 2 "CREATE TEMPORARY TABLE temp.t2(a, b)" {{} {t1 t2} {} {}} + 3 "CREATE TEMP TABLE TEMP.t3(a, b)" {{} {t1 t2 t3} {} {}} + 4 "CREATE TEMPORARY TABLE TEMP.xxx(x)" {{} {t1 t2 t3 xxx} {} {}} + } } # EVIDENCE-OF: R-31997-24564 If no schema name is specified and the TEMP # keyword is not present then the table is created in the main database. # drop_all_tables -do_createtable_tests 1.6 -tclquery { - unset -nocomplain X - array set X [table_list] - list $X(main) $X(temp) $X(auxa) $X(auxb) -} { - 1 "CREATE TABLE t1(a, b)" {t1 {} {} {}} - 2 "CREATE TABLE t2(a, b)" {{t1 t2} {} {} {}} - 3 "CREATE TABLE t3(a, b)" {{t1 t2 t3} {} {} {}} - 4 "CREATE TABLE xxx(x)" {{t1 t2 t3 xxx} {} {} {}} +if {[permutation]!="maindbname"} { + do_createtable_tests 1.6 -tclquery { + unset -nocomplain X + array set X [table_list] + list $X(main) $X(temp) $X(auxa) $X(auxb) + } { + 1 "CREATE TABLE t1(a, b)" {t1 {} {} {}} + 2 "CREATE TABLE t2(a, b)" {{t1 t2} {} {} {}} + 3 "CREATE TABLE t3(a, b)" {{t1 t2 t3} {} {} {}} + 4 "CREATE TABLE xxx(x)" {{t1 t2 t3 xxx} {} {} {}} + } } drop_all_tables diff --git a/test/e_droptrigger.test b/test/e_droptrigger.test index 84dfe72..5cd5d0a 100644 --- a/test/e_droptrigger.test +++ b/test/e_droptrigger.test @@ -127,8 +127,8 @@ foreach {tn tbl droptrigger before after} { } $after } -# EVIDENCE-OF: R-50239-29811 Once removed, the trigger definition is no -# longer present in the sqlite_master (or sqlite_temp_master) table and +# EVIDENCE-OF: R-04950-25529 Once removed, the trigger definition is no +# longer present in the sqlite_schema (or sqlite_temp_schema) table and # is not fired by any subsequent INSERT, UPDATE or DELETE statements. # # Test cases e_droptrigger-1.* test the first part of this statement diff --git a/test/e_dropview.test b/test/e_dropview.test index 04c4ad8..00f59dd 100644 --- a/test/e_dropview.test +++ b/test/e_dropview.test @@ -126,37 +126,37 @@ do_execsql_test 3.1.0 { SELECT * FROM temp.v1 } {{a temp} {b temp}} do_execsql_test 3.1.1 { DROP VIEW temp.v1 } {} do_catchsql_test 3.1.2 { SELECT * FROM temp.v1 } {1 {no such table: temp.v1}} do_test 3.1.3 { list_all_views } {main.v1 main.v2 aux.v1 aux.v2 aux.v3} -do_test 3.1.4 { list_all_data } $databasedata +do_test 3.1.4 { string compare [list_all_data] $databasedata } 0 do_execsql_test 3.2.0 { SELECT * FROM v1 } {{a main} {b main}} do_execsql_test 3.2.1 { DROP VIEW v1 } {} do_catchsql_test 3.2.2 { SELECT * FROM main.v1 } {1 {no such table: main.v1}} do_test 3.2.3 { list_all_views } {main.v2 aux.v1 aux.v2 aux.v3} -do_test 3.2.4 { list_all_data } $databasedata +do_test 3.2.4 { string compare [list_all_data] $databasedata } 0 do_execsql_test 3.3.0 { SELECT * FROM v2 } {{a main} {b main}} do_execsql_test 3.3.1 { DROP VIEW v2 } {} do_catchsql_test 3.3.2 { SELECT * FROM main.v2 } {1 {no such table: main.v2}} do_test 3.3.3 { list_all_views } {aux.v1 aux.v2 aux.v3} -do_test 3.3.4 { list_all_data } $databasedata +do_test 3.3.4 { string compare [list_all_data] $databasedata } 0 do_execsql_test 3.4.0 { SELECT * FROM v1 } {{a aux} {b aux}} do_execsql_test 3.4.1 { DROP VIEW v1 } {} do_catchsql_test 3.4.2 { SELECT * FROM v1 } {1 {no such table: v1}} do_test 3.4.3 { list_all_views } {aux.v2 aux.v3} -do_test 3.4.4 { list_all_data } $databasedata +do_test 3.4.4 { string compare [list_all_data] $databasedata } 0 -do_execsql_test 3.4.0 { SELECT * FROM aux.v2 } {{a aux} {b aux}} -do_execsql_test 3.4.1 { DROP VIEW aux.v2 } {} -do_catchsql_test 3.4.2 { SELECT * FROM aux.v2 } {1 {no such table: aux.v2}} -do_test 3.4.3 { list_all_views } {aux.v3} -do_test 3.4.4 { list_all_data } $databasedata +do_execsql_test 3.5.0 { SELECT * FROM aux.v2 } {{a aux} {b aux}} +do_execsql_test 3.5.1 { DROP VIEW aux.v2 } {} +do_catchsql_test 3.5.2 { SELECT * FROM aux.v2 } {1 {no such table: aux.v2}} +do_test 3.5.3 { list_all_views } {aux.v3} +do_test 3.5.4 { string compare [list_all_data] $databasedata } 0 -do_execsql_test 3.5.0 { SELECT * FROM v3 } {{a aux} {b aux}} -do_execsql_test 3.5.1 { DROP VIEW v3 } {} -do_catchsql_test 3.5.2 { SELECT * FROM v3 } {1 {no such table: v3}} -do_test 3.5.3 { list_all_views } {} -do_test 3.5.4 { list_all_data } $databasedata +do_execsql_test 3.6.0 { SELECT * FROM v3 } {{a aux} {b aux}} +do_execsql_test 3.6.1 { DROP VIEW v3 } {} +do_catchsql_test 3.6.2 { SELECT * FROM v3 } {1 {no such table: v3}} +do_test 3.6.3 { list_all_views } {} +do_test 3.6.4 { string compare [list_all_data] $databasedata } 0 # EVIDENCE-OF: R-25558-37487 If the specified view cannot be found and # the IF EXISTS clause is not present, it is an error. @@ -179,11 +179,11 @@ do_dropview_tests 5 -repair { dropview_reopen_db } -tclquery { list_all_views - expr {[list_all_views] == "main.v1 main.v2 temp.v1 aux.v1 aux.v2 aux.v3"} + #expr {[list_all_views] == "main.v1 main.v2 temp.v1 aux.v1 aux.v2 aux.v3"} } { - 1 "DROP VIEW IF EXISTS xx" 1 - 2 "DROP VIEW IF EXISTS main.xx" 1 - 3 "DROP VIEW IF EXISTS temp.v2" 1 + 1 "DROP VIEW IF EXISTS xx" "main.v1 main.v2 temp.v1 aux.v1 aux.v2 aux.v3" + 2 "DROP VIEW IF EXISTS main.xx" "main.v1 main.v2 temp.v1 aux.v1 aux.v2 aux.v3" + 3 "DROP VIEW IF EXISTS temp.v2" "main.v1 main.v2 temp.v1 aux.v1 aux.v2 aux.v3" } diff --git a/test/e_expr.test b/test/e_expr.test index 94e66af..8dd7507 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -1139,7 +1139,7 @@ sqlite3 db test.db #------------------------------------------------------------------------- # Test cases for the testable statements related to the CASE expression. # -# EVIDENCE-OF: R-15199-61389 There are two basic forms of the CASE +# EVIDENCE-OF: R-57495-24088 There are two fundamental forms of the CASE # expression: those with a base expression and those without. # do_execsql_test e_expr-20.1 { @@ -1235,11 +1235,11 @@ db nullvalue {} # evaluating WHEN terms. # do_execsql_test e_expr-21.4.1 { - SELECT CASE WHEN NULL THEN 'A' WHEN 1 THEN 'B' END -} {B} + SELECT CASE WHEN NULL THEN 'A' WHEN 1 THEN 'B' END, iif(NULL,8,99); +} {B 99} do_execsql_test e_expr-21.4.2 { - SELECT CASE WHEN 0 THEN 'A' WHEN NULL THEN 'B' ELSE 'C' END -} {C} + SELECT CASE WHEN 0 THEN 'A' WHEN NULL THEN 'B' ELSE 'C' END, iif(0,8,99); +} {C 99} # EVIDENCE-OF: R-38620-19499 In a CASE with a base expression, the base # expression is evaluated just once and the result is compared against @@ -1952,39 +1952,39 @@ foreach {tn expr} { # 'english' and '0' are all considered to be false. # do_execsql_test e_expr-37.1 { - SELECT CASE WHEN NULL THEN 'true' ELSE 'false' END; -} {false} + SELECT CASE WHEN NULL THEN 'true' ELSE 'false' END, iif(NULL,'true','false'); +} {false false} do_execsql_test e_expr-37.2 { - SELECT CASE WHEN 0.0 THEN 'true' ELSE 'false' END; -} {false} + SELECT CASE WHEN 0.0 THEN 'true' ELSE 'false' END, iif(0.0,'true','false'); +} {false false} do_execsql_test e_expr-37.3 { - SELECT CASE WHEN 0 THEN 'true' ELSE 'false' END; -} {false} + SELECT CASE WHEN 0 THEN 'true' ELSE 'false' END, iif(0,'true','false'); +} {false false} do_execsql_test e_expr-37.4 { - SELECT CASE WHEN 'engligh' THEN 'true' ELSE 'false' END; -} {false} + SELECT CASE WHEN 'engligh' THEN 'true' ELSE 'false' END, iif('engligh','true','false'); +} {false false} do_execsql_test e_expr-37.5 { - SELECT CASE WHEN '0' THEN 'true' ELSE 'false' END; -} {false} + SELECT CASE WHEN '0' THEN 'true' ELSE 'false' END, iif('0','true','false'); +} {false false} # EVIDENCE-OF: R-55532-10108 Values 1, 1.0, 0.1, -0.1 and '1english' are # considered to be true. # do_execsql_test e_expr-37.6 { - SELECT CASE WHEN 1 THEN 'true' ELSE 'false' END; -} {true} + SELECT CASE WHEN 1 THEN 'true' ELSE 'false' END, iif(1,'true','false'); +} {true true} do_execsql_test e_expr-37.7 { - SELECT CASE WHEN 1.0 THEN 'true' ELSE 'false' END; -} {true} + SELECT CASE WHEN 1.0 THEN 'true' ELSE 'false' END, iif(1.0,'true','false'); +} {true true} do_execsql_test e_expr-37.8 { - SELECT CASE WHEN 0.1 THEN 'true' ELSE 'false' END; -} {true} + SELECT CASE WHEN 0.1 THEN 'true' ELSE 'false' END, iif(0.1,'true','false'); +} {true true} do_execsql_test e_expr-37.9 { - SELECT CASE WHEN -0.1 THEN 'true' ELSE 'false' END; -} {true} + SELECT CASE WHEN -0.1 THEN 'true' ELSE 'false' END, iif(-0.1,'true','false'); +} {true true} do_execsql_test e_expr-37.10 { - SELECT CASE WHEN '1english' THEN 'true' ELSE 'false' END; -} {true} + SELECT CASE WHEN '1english' THEN 'true' ELSE 'false' END, iif('1engl','true','false'); +} {true true} finish_test diff --git a/test/e_fkey.test b/test/e_fkey.test index 8d465ab..c5ac5fd 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -2507,7 +2507,7 @@ proc test_efkey_6 {tn zAlter isError} { drop_all_tables do_test e_fkey-56.$tn.1 " - execsql { CREATE TABLE tbl(a, b) } + execsql { CREATE TABLE tbl(a, b); INSERT INTO tbl VALUES(1, 2); } [list catchsql $zAlter] " [lindex {{0 {}} {1 {Cannot add a REFERENCES column with non-NULL default value}}} $isError] @@ -2528,8 +2528,8 @@ test_efkey_6 3 "ALTER TABLE tbl ADD COLUMN c DEFAULT 0 REFERENCES xx" 1 # # Test that these adjustments are visible in the sqlite_master table. # -# EVIDENCE-OF: R-63827-54774 The text of the child CREATE TABLE -# statement or statements stored in the sqlite_master table are modified +# EVIDENCE-OF: R-43040-62530 The text of the child CREATE TABLE +# statement or statements stored in the sqlite_schema table are modified # to reflect the new parent table name. # do_test e_fkey-56.1 { @@ -2771,7 +2771,7 @@ do_test e_fkey-60.6 { # do_test e_fkey-61.1.1 { drop_all_tables - execsql { CREATE TABLE t1(a, b) } + execsql { CREATE TABLE t1(a, b) ; INSERT INTO t1 VALUES(1, 2) } catchsql { ALTER TABLE t1 ADD COLUMN c DEFAULT 'xxx' REFERENCES t2 } } {1 {Cannot add a REFERENCES column with non-NULL default value}} do_test e_fkey-61.1.2 { diff --git a/test/enc.test b/test/enc.test index 5c24bbb..ffe2416 100644 --- a/test/enc.test +++ b/test/enc.test @@ -169,4 +169,84 @@ do_test enc-11.2 { } } {2} +#------------------------------------------------------------------------- +reset_db +forcedelete test.db2 +forcedelete test.db3 + +do_execsql_test enc-12.0 { + PRAGMA encoding = 'utf-8'; + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES('a', 'b', 'c'); + ATTACH 'test.db3' AS aux; + CREATE TABLE aux.t3(x, y, z); + INSERT INTO t3 VALUES('xxx', 'yyy', 'zzz'); + PRAGMA encoding; +} {UTF-8} + +do_test enc-12.1 { + sqlite3 db2 test.db2 + db2 eval { + PRAGMA encoding = 'UTF-16le'; + CREATE TABLE t2(d, e, f); + INSERT INTO t2 VALUES('d', 'e', 'f'); + PRAGMA encoding; + } +} {UTF-16le} + +do_test enc-12.2 { + db2 backup test.db + db2 close +} {} + +do_catchsql_test enc-12.3 { + SELECT * FROM t2; +} {1 {attached databases must use the same text encoding as main database}} + +db close +sqlite3 db test.db3 +do_execsql_test enc-12.4 { + SELECT * FROM t3; + PRAGMA encoding = 'UTF-16le'; + SELECT * FROM t3; +} {xxx yyy zzz xxx yyy zzz} + +db close +sqlite3 db test.db3 +breakpoint +do_execsql_test enc-12.5 { + PRAGMA encoding = 'UTF-16le'; + PRAGMA encoding; +} {UTF-8} + +reset_db +do_execsql_test enc-12.6 { + PRAGMA encoding = 'UTF-8'; + CREATE TEMP TABLE t1(a, b, c); + INSERT INTO t1 VALUES('xxx', 'yyy', 'zzz'); +} +do_test enc-12.7 { + sqlite3 db2 test.db2 + db2 backup test.db + db2 close + db eval { + SELECT * FROM t1; + } +} {xxx yyy zzz} +do_catchsql_test enc-12.8 { + SELECT * FROM t2; + SELECT * FROM t1; +} {1 {attached databases must use the same text encoding as main database}} + +db close +sqlite3 db test.db +do_execsql_test enc-12.9 { + CREATE TEMP TABLE t1(a, b, c); + INSERT INTO t1 VALUES('xxx', 'yyy', 'zzz'); +} +do_execsql_test enc-12.10 { + SELECT * FROM t2; + SELECT * FROM t1; +} {d e f xxx yyy zzz} + finish_test diff --git a/test/filter1.test b/test/filter1.test index ee17099..7b2cf9c 100644 --- a/test/filter1.test +++ b/test/filter1.test @@ -204,4 +204,22 @@ do_execsql_test 6.3 { SELECT (SELECT COUNT(a) FROM t2) FROM t1; } {2} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 7.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(321, 100000); + INSERT INTO t1 VALUES(111, 110000); + INSERT INTO t1 VALUES(444, 120000); + INSERT INTO t1 VALUES(222, 130000); +} + +do_execsql_test 7.1 { + SELECT max(a), max(a) FILTER (WHERE b<12345), b FROM t1; +} { + 444 {} 120000 +} + + + finish_test diff --git a/test/fkey2.test b/test/fkey2.test index e7fa7b6..015c43c 100644 --- a/test/fkey2.test +++ b/test/fkey2.test @@ -955,6 +955,7 @@ ifcapable altertable { execsql { CREATE TABLE t1(a PRIMARY KEY); CREATE TABLE t2(a, b); + INSERT INTO t2 VALUES(1,2); } catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } } {0 {}} @@ -1046,6 +1047,7 @@ ifcapable altertable { execsql { CREATE TEMP TABLE t1(a PRIMARY KEY); CREATE TEMP TABLE t2(a, b); + INSERT INTO temp.t2 VALUES(1,2); } catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } } {0 {}} @@ -1130,6 +1132,7 @@ ifcapable altertable { ATTACH ':memory:' AS aux; CREATE TABLE aux.t1(a PRIMARY KEY); CREATE TABLE aux.t2(a, b); + INSERT INTO aux.t2(a,b) VALUES(1,2); } catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } } {0 {}} diff --git a/test/fkey5.test b/test/fkey5.test index 3c44cd3..d467a64 100644 --- a/test/fkey5.test +++ b/test/fkey5.test @@ -15,10 +15,10 @@ # EVIDENCE-OF: R-15402-03103 PRAGMA schema.foreign_key_check; PRAGMA # schema.foreign_key_check(table-name); # -# EVIDENCE-OF: R-23918-17301 The foreign_key_check pragma checks the +# EVIDENCE-OF: R-41653-15278 The foreign_key_check pragma checks the # database, or the table called "table-name", for foreign key -# constraints that are violated and returns one row of output for each -# violation. +# constraints that are violated. The foreign_key_check pragma returns +# one row output for each foreign key violation. set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -430,4 +430,63 @@ do_catchsql_test 11.1 { PRAGMA foreign_key_check; } {1 {foreign key mismatch - "c11" referencing "tt"}} +# 2020-07-03 Bug in foreign_key_check discovered while working on the +# forum reports that pragma_foreign_key_check does not accept an argument: +# If two separate schemas seem to reference one another, that causes +# problems for foreign_key_check. +# +reset_db +do_execsql_test 12.0 { + ATTACH ':memory:' as aux; + CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b TEXT REFERENCES t2); + CREATE TABLE main.t2(x TEXT PRIMARY KEY, y INT); + INSERT INTO main.t2 VALUES('abc',11),('def',22),('xyz',99); + INSERT INTO aux.t1 VALUES(5,'abc'),(7,'xyz'),(9,'oops'); + PRAGMA foreign_key_check=t1; +} {t1 5 t2 0 t1 7 t2 0 t1 9 t2 0} +do_execsql_test 12.1 { + CREATE TABLE aux.t2(x TEXT PRIMARY KEY, y INT); + INSERT INTO aux.t2 VALUES('abc',11),('def',22),('xyz',99); + PRAGMA foreign_key_check=t1; +} {t1 9 t2 0} + +# 2020-07-03: the pragma_foreign_key_check virtual table should +# accept arguments for the table name and/or schema name. +# +ifcapable vtab { + do_execsql_test 13.0 { + SELECT *, 'x' FROM pragma_foreign_key_check('t1'); + } {t1 9 t2 0 x} + do_catchsql_test 13.1 { + SELECT *, 'x' FROM pragma_foreign_key_check('t1','main'); + } {1 {no such table: main.t1}} + do_execsql_test 13.2 { + SELECT *, 'x' FROM pragma_foreign_key_check('t1','aux'); + } {t1 9 t2 0 x} +} + +ifcapable vtab { + reset_db + do_execsql_test 13.10 { + PRAGMA foreign_keys=OFF; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT REFERENCES t2); + CREATE TABLE t2(x TEXT PRIMARY KEY, y INT); + CREATE TABLE t3(w TEXT, z INT REFERENCES t1); + INSERT INTO t2 VALUES('abc',11),('def',22),('xyz',99); + INSERT INTO t1 VALUES(5,'abc'),(7,'xyz'),(9,'oops'); + INSERT INTO t3 VALUES(11,7),(22,19); + } {} + do_execsql_test 13.11 { + SELECT x.*, '|' + FROM sqlite_schema, pragma_foreign_key_check(name) AS x + WHERE type='table' + ORDER BY x."table"; + } {t1 9 t2 0 | t3 2 t1 0 |} + do_execsql_test 13.12 { + SELECT *, '|' + FROM pragma_foreign_key_check AS x + ORDER BY x."table"; + } {t1 9 t2 0 | t3 2 t1 0 |} +} + finish_test diff --git a/test/fordelete.test b/test/fordelete.test index 9a382d9..39c0c35 100644 --- a/test/fordelete.test +++ b/test/fordelete.test @@ -48,7 +48,7 @@ proc analyze_delete_program {sql} { set obj $T($root) set O($obj) "" - if {"0x$R(p5)" & 0x08} { + if {$R(p5) & 0x08} { set O($obj) * } else { set O($obj) "" diff --git a/test/fts3corrupt.test b/test/fts3corrupt.test index 4019509..828964b 100644 --- a/test/fts3corrupt.test +++ b/test/fts3corrupt.test @@ -181,4 +181,16 @@ do_catchsql_test 6.10 { INSERT INTO f(f) VALUES ("merge=1"); } {1 {database disk image is malformed}} +# 2020-03-02 https://bugs.chromium.org/p/chromium/issues/detail?id=1057441 +# The ticket complains of use of an uninitialized value. That part is harmless. +# The only reason to fix this is the failure to detect a subtly corrupt +# inverted index. +# +reset_db +do_catchsql_test 7.10 { + CREATE VIRTUAL TABLE f USING fts3(a,b); + INSERT INTO f_segdir VALUES (0,0,1,0,'0 0',x'01010101020101'); + SELECT matchinfo( f , 'pcx') FROM f WHERE b MATCH x'c533'; +} {1 {database disk image is malformed}} + finish_test diff --git a/test/fts3corrupt2.test b/test/fts3corrupt2.test index 40783fa..5864353 100644 --- a/test/fts3corrupt2.test +++ b/test/fts3corrupt2.test @@ -16,6 +16,7 @@ source $testdir/tester.tcl ifcapable !fts3 { finish_test ; return } set ::testprefix fts3corrupt2 +sqlite3_fts3_may_be_corrupt 1 set data [list] lappend data {*}{ @@ -107,5 +108,4 @@ foreach c {50 100 150 200 250} { - finish_test diff --git a/test/fts3corrupt4.test b/test/fts3corrupt4.test index 7404dde..bddcc9f 100644 --- a/test/fts3corrupt4.test +++ b/test/fts3corrupt4.test @@ -27,6 +27,7 @@ ifcapable !fts3 { sqlite3_fts3_may_be_corrupt 1 database_may_be_corrupt +extra_schema_checks 0 do_execsql_test 1.0 { BEGIN; @@ -5821,4 +5822,480 @@ do_catchsql_test 36.0 { INSERT INTO f(f) VALUES ('merge=53,216'); } {0 {}} +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 36.0 { + CREATE VIRTUAL TABLE f USING fts3(a,b); + CREATE TABLE 'f_stat'(id INTEGER PRIMARY KEY, value BLOB); + INSERT INTO f_stat VALUES (1,x'11014101000101c5c5014b010164c5014b010101c50101c5c5010201010101014101000101c5c5014b010101c5014b010101c50101c5c501010100c50101c5c5010101010101e40201010101014101000201010101014101000101010201010101014101000101c5c503b5fefefe3afeffffc5c5c5c50101010101010201010101014101adadadadadadadadadadadad91adadadadadadadad0101c50101c5c501f9ffffffffffffffff0001010102010101010140f5000101c5c5014b010101c50101c5c501010101e6010201010101014101000101c5c5014b010101c50101c5c5010101114b0101c5c50101010a0101020101e60101'); +} + +do_catchsql_test 36.1 { + INSERT INTO f(f) VALUES ('merge=59,59'); +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 37.0 { + CREATE VIRTUAL TABLE f USING fts3(a,b); + INSERT INTO f_segdir VALUES (28,0,0,0,'0 0',x'00'); + INSERT INTO f_segdir VALUES (0,241,0,0,'0 0',x'0001000030310000f1'); +} + +do_catchsql_test 37.1 { + INSERT INTO f VALUES (0,x'00'); +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +# +reset_db +do_test 38.0 { + sqlite3 db {} + db deserialize [decode_hexdb { +.open --hexdb +| size 24576 pagesize 4096 filename crash-1cc4f8a70485ce.db +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 00 .....@ ........ +| 96: 00 00 00 00 0d 0e b1 00 06 0d a4 00 0f 8d 0f 21 ...............! +| 112: 0e b9 0d c8 0e 7e 0d a4 00 00 00 00 00 00 00 00 .....~.......... +| 3488: 00 00 00 00 22 07 06 17 11 11 01 31 74 61 62 6c ...........1tabl +| 3504: 65 74 32 74 32 07 43 52 45 41 54 45 20 54 41 42 et2t2.CREATE TAB +| 3520: 4c 45 20 74 32 28 78 29 81 33 05 07 17 1f 1f 01 LE t2(x).3...... +| 3536: 82 35 74 61 62 6c 65 74 31 5f 73 65 67 64 69 72 .5tablet1_segdir +| 3552: 74 31 5f 73 65 67 64 69 72 05 43 52 45 41 54 45 t1_segdir.CREATE +| 3568: 20 54 41 42 4c 45 20 27 74 31 5f 73 65 67 64 69 TABLE 't1_segdi +| 3584: 72 27 28 6c 65 76 65 6c 20 49 4e 54 45 47 45 52 r'(level INTEGER +| 3600: 2c 69 64 78 20 49 4e 54 45 47 45 52 2c 73 74 61 ,idx INTEGER,sta +| 3616: 72 74 5f 62 6c 6f 63 6b 20 49 4e 54 45 47 45 52 rt_block INTEGER +| 3632: 2c 6c 65 61 76 65 73 5f 65 6e 64 5f 62 6c 6f 63 ,leaves_end_bloc +| 3648: 6b 20 49 4e 54 45 47 45 52 2c 65 6e 64 5f 62 6c k INTEGER,end_bl +| 3664: 6f 63 6b 20 49 4e 54 45 47 45 62 2c 72 6f 6f 74 ock INTEGEb,root +| 3680: 20 42 4c 4f 42 2c 50 52 49 4d 41 52 59 20 4b 45 BLOB,PRIMARY KE +| 3696: 59 28 6c 65 76 65 6c 2c 20 69 64 78 29 29 31 06 Y(level, idx))1. +| 3712: 06 17 45 1f 01 00 69 6e 64 65 78 73 71 6c 69 74 ..E...indexsqlit +| 3728: 65 5f 61 75 74 6f 69 6e 64 65 78 5f 74 31 5f 73 e_autoindex_t1_s +| 3744: 65 67 64 69 72 5f 31 74 31 5f 73 65 67 64 69 72 egdir_1t1_segdir +| 3760: 06 0f c7 00 08 00 00 00 00 66 04 07 17 23 23 01 .........f...##. +| 3776: 81 13 74 61 62 6c 65 74 31 5f 73 65 67 6d 65 6e ..tablet1_segmen +| 3792: 74 73 74 31 5f 73 65 67 6d 65 6e 74 73 04 43 52 tst1_segments.CR +| 3808: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 73 EATE TABLE 't1_s +| 3824: 65 67 6d 65 6e 74 73 27 28 62 6c 6f 63 6b 69 64 egments'(blockid +| 3840: 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 INTEGER PRIMARY +| 3856: 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 KEY, block BLOB +| 3872: 29 6a 03 07 17 21 21 01 81 1f 74 61 62 6c 65 74 )j...!!...tablet +| 3888: 31 5f 63 6f 6e 74 65 6e 74 74 31 5f 63 6f 6e 74 1_contentt1_cont +| 3904: 65 6e 74 03 43 52 45 41 54 45 20 54 41 42 4c 45 ent.CREATE TABLE +| 3920: 20 27 74 31 5f 63 6f 6e 74 65 6e 74 27 28 64 6f 't1_content'(do +| 3936: 63 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d cid INTEGER PRIM +| 3952: 41 52 59 20 4b 45 59 2c 20 27 63 30 61 27 2c 20 ARY KEY, 'c0a', +| 3968: 27 63 31 62 27 2c 20 27 63 32 63 27 29 38 02 06 'c1b', 'c2c')8.. +| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR +| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB +| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 33 LE t1 USING fts3 +| 4032: 28 61 2c 62 2c 63 29 00 00 00 00 00 00 00 00 00 (a,b,c)......... +| page 3 offset 8192 +| 0: 0d 00 00 00 25 0b 48 00 0f d8 0f af 0f 86 0f 74 ....%.H........t +| 16: 0f 61 0f 4e 0f 2f 0f 0f 0e ef 0e d7 0e be 0e a5 .a.N./.......... +| 32: 0e 8d 0e 74 0e 5b 0e 40 0e 24 0e 08 0d ef 00 00 ...t.[.@.$...... +| 2880: 00 00 00 00 00 00 00 00 81 3f 25 06 00 82 7e f0 .........?%...~. +| 2896: 00 43 4f 4d 50 49 4c 45 52 3d 67 63 63 2d 35 2e .COMPILER=gcc-5. +| 2912: 34 23 00 20 42 30 31 36 2f 36 30 39 20 44 45 42 4#. B016/609 DEB +| 2928: 55 47 20 45 4e 41 42 4c 45 20 44 42 53 54 41 54 UG ENABLE DBSTAT +| 2944: 20 56 54 41 42 20 45 4e 42 92 4c 45 20 46 54 53 VTAB ENB.LE FTS +| 2960: 34 20 45 4e 41 42 4c 45 20 46 54 53 35 20 45 4e 4 ENABLE FTS5 EN +| 2976: 41 42 4c 45 20 47 45 4f 50 4f 4c 59 20 45 4e 41 ABLE GEOPOLY ENA +| 2992: 42 5c 45 1f 4a 53 4f 4e 31 20 45 4e 41 42 4c 45 B.E.JSON1 ENABLE +| 3008: 20 4d 45 4d 53 59 53 35 20 45 4e 41 42 4c 45 20 MEMSYS5 ENABLE +| 3024: 52 54 52 45 45 20 4d 41 58 20 4d 45 4d 4f 52 59 RTREE MAX MEMORY +| 3040: 3d 35 30 30 30 30 30 30 30 20 4f 4d 49 54 20 4c =50000000 OMIT L +| 3056: 4f 41 44 20 45 58 54 45 4e 53 49 4f 4e 20 54 48 OAD EXTENSION TH +| 3072: 52 45 41 44 53 41 46 45 3d 30 18 24 05 00 25 0f READSAFE=0.$..%. +| 3088: 19 54 48 52 45 41 44 53 41 46 45 3d 30 58 42 49 .THREADSAFE=0XBI +| 3104: 4e 41 52 59 18 23 05 00 25 0f 19 54 48 52 45 41 NARY.#..%..THREA +| 3120: 44 53 41 46 45 3d 30 88 4e 4f 43 41 53 45 17 22 DSAFE=0.NOCASE.. +| 3136: 05 00 25 0f 17 54 48 52 45 41 44 53 41 46 45 3d ..%..THREADSAFE= +| 3152: 30 58 52 54 52 49 4d 1f 21 05 00 33 0f 19 4f 4d 0XRTRIM.!..3..OM +| 3168: 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 4f IT LOAD EXTENSIO +| 3184: 4e 58 42 49 4e 41 52 59 1f 20 05 00 33 0f 19 4f NXBINARY. ..3..O +| 3200: 4d 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 MIT LOAD EXTENSI +| 3216: 4f 4e 58 4e 4f 43 41 53 45 1e 1f 05 00 33 0f 17 ONXNOCASE....3.. +| 3232: 4f 4d 49 54 20 4c 4f 41 54 20 45 58 54 45 4e 53 OMIT LOAT EXTENS +| 3248: 49 4f 4e 58 52 54 52 49 4d 1f 1e 04 00 33 0f 19 IONXRTRIM....3.. +| 3264: 82 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 30 30 .AX MEMORY=50000 +| 3280: 30 30 30 58 42 49 4e 41 52 59 1f 1d 05 00 33 0f 000XBINARY....3. +| 3296: 19 4d 41 58 20 4d 45 4d 4f 52 59 3d 35 30 30 30 .MAX MEMORY=5000 +| 3312: 30 30 30 30 58 4e 4f 43 41 53 45 1e 1c 05 00 33 0000XNOCASE....3 +| 3328: 0f 17 4d 41 58 20 4d 45 4d fa 52 59 3d 35 30 20 ..MAX MEM.RY=50 +| 3344: 30 30 30 30 30 58 52 54 52 49 4d 18 1b 05 00 25 00000XRTRIM....% +| 3360: 0f 19 45 4e 41 42 4c 45 20 52 54 52 45 45 58 42 ..ENABLE RTREEXB +| 3376: 49 4e 41 52 59 18 1a 05 00 25 0f 19 45 4e 41 42 INARY....%..ENAB +| 3392: 4c 45 20 52 54 52 45 45 58 4e 4f 43 41 53 45 17 LE RTREEXNOCASE. +| 3408: 19 05 00 25 0f 17 45 4e 42 42 4c 45 20 52 54 52 ...%..ENBBLE RTR +| 3424: 45 45 58 52 54 52 49 4d 1a 18 05 00 29 0f 19 45 EEXRTRIM....)..E +| 3440: 4e 41 42 4c 45 20 4d 45 4d 53 59 53 35 58 42 49 NABLE MEMSYS5XBI +| 3456: 4e 41 52 59 1a 17 05 00 29 0f 19 45 4e 41 42 3c NARY....)..ENAB< +| 3472: 45 20 4d 45 4d 53 59 53 35 58 4e 4f 43 41 53 45 E MEMSYS5XNOCASE +| 3488: 19 16 05 00 29 0f 17 45 4e 41 42 4c 45 20 4d 45 ....)..ENABLE ME +| 3504: 4d 53 59 53 35 58 52 54 52 49 4d 18 15 05 00 25 MSYS5XRTRIM....% +| 3520: 0f 19 45 4e 41 42 4c 45 20 4a 53 4f 4e 31 58 42 ..ENABLE JSON1XB +| 3536: 49 4e 41 52 59 18 14 05 00 25 0f 19 45 4e 41 42 INARY....%..ENAB +| 3552: 4c 45 20 4a 53 4f 4e 31 58 4e 4f 43 41 53 45 17 LE JSON1XNOCASE. +| 3568: 13 05 00 25 0f 17 45 4e 41 42 4c 45 20 4a 53 4f ...%..ENABLE JSO +| 3584: 4e 31 58 52 54 52 49 4d 95 12 05 00 29 0f 19 45 N1XRTRIM....)..E +| 3600: 4e 31 42 4c 45 20 47 45 4e 50 4f 4c 59 58 42 49 N1BLE GENPOLYXBI +| 3616: 4e 41 52 59 1a 11 05 00 29 0f 19 45 3e f2 1e 4c NARY....)..E>..L +| 3632: 45 20 47 45 4f 50 4f 4c 59 58 4e 4f 43 41 53 45 E GEOPOLYXNOCASE +| 3648: 19 10 05 00 29 0f 17 45 4e 41 42 4c 45 20 47 45 ....)..ENABLE GE +| 3664: 4f 50 4f 4c 59 58 52 54 52 49 4d 17 0f 05 00 23 OPOLYXRTRIM....# +| 3680: 0f 19 45 4e 41 42 4c 45 20 46 54 53 35 58 42 49 ..ENABLE FTS5XBI +| 3696: 4e 41 52 59 17 0e 05 00 23 0f 19 45 4e 41 42 3c NARY....#..ENAB< +| 3712: 45 20 46 54 53 35 58 4e 4f 43 41 53 45 16 0d 05 E FTS5XNOCASE... +| 3728: 00 23 0f 17 45 4e 41 42 4c 45 20 46 54 53 35 58 .#..ENABLE FTS5X +| 3744: 5d 24 52 49 4d 17 0c 05 00 23 0f 19 45 4e 41 42 ]$RIM....#..ENAB +| 3760: 4c 45 20 46 54 53 34 58 42 49 4e 41 52 59 17 0b LE FTS4XBINARY.. +| 3776: 05 00 23 0f 19 45 4e 41 42 4c 45 20 46 54 53 35 ..#..ENABLE FTS5 +| 3792: 58 4e 4f 43 40 53 45 16 0a 05 00 23 0f 17 45 4e XNOC@SE....#..EN +| 3808: 41 42 4c 45 20 46 54 53 34 58 52 54 52 49 4d 1e ABLE FTS4XRTRIM. +| 3824: 09 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS +| 3840: 54 41 54 20 56 54 41 42 58 42 49 4e 41 52 59 1e TAT VTABXBINARY. +| 3856: 08 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS +| 3872: 54 41 54 20 56 54 41 42 58 4e 4f 43 41 53 45 1d TAT VTABXNOCASE. +| 3888: 07 05 00 31 0f 17 45 4e 41 42 4b 45 20 44 42 53 ...1..ENABKE DBS +| 3904: 54 41 54 20 56 53 41 42 58 52 54 62 49 4d 11 06 TAT VSABXRTbIM.. +| 3920: 05 00 17 0f 19 44 45 42 54 47 58 42 49 4e 41 52 .....DEBTGXBINAR +| 3936: 59 11 05 05 00 17 0f 19 44 45 42 55 47 58 4e 4f Y.......DEBUGXNO +| 3952: 43 41 53 45 10 04 05 00 17 0f 17 44 45 42 55 47 CASE.......DEBUG +| 3968: 68 52 54 52 49 4d 27 03 05 00 43 0f 19 43 4f 4d hRTRIM'...C..COM +| 3984: 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e 30 20 PILER=gcc-5.4.0 +| 4000: 32 30 31 36 30 36 30 39 58 42 49 4e 41 52 59 27 20160609XBINARY' +| 4016: 02 05 00 43 0f 19 43 4f 4d 50 49 4c 45 52 3d 67 ...C..COMPILER=g +| 4032: 63 63 2d 35 2e 34 2e 30 20 32 30 31 36 30 36 30 cc-5.4.0 2016060 +| 4048: 39 58 4f 4f 43 41 53 45 26 01 05 00 43 0f 17 43 9XOOCASE&...C..C +| 4064: 4f 4d 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e OMPILER=gcc-5.4. +| 4080: 30 20 32 30 31 36 30 36 30 39 58 52 54 52 49 4d 0 20160609XRTRIM +| page 4 offset 12288 +| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +| page 5 offset 16384 +| 0: 0d 00 00 00 02 0b a0 00 0c ad 0b a0 00 00 00 00 ................ +| 2976: 82 0a 02 08 08 09 08 08 17 84 06 30 20 32 35 33 ...........0 253 +| 2992: 00 01 30 04 25 06 1b 00 00 08 32 30 31 36 8c 36 ..0.%.....2016.6 +| 3008: 30 39 03 25 07 00 00 01 34 03 25 05 00 00 01 35 09.%....4.%....5 +| 3024: 03 25 04 00 01 07 30 30 30 30 30 30 30 03 25 1a .%....0000000.%. +| 3040: 00 00 08 63 6f 5d 70 69 6c 65 72 03 25 02 00 00 ...co]piler.%... +| 3056: 06 64 62 73 74 61 74 03 25 0a 00 01 04 65 62 75 .dbstat.%....ebu +| 3072: 67 03 25 08 00 00 06 65 6e 61 62 6c 65 09 25 09 g.%....enable.%. +| 3088: 05 04 04 04 04 04 00 01 08 78 74 65 7e 73 69 6f .........xte~sio +| 3104: 6e 03 25 1d 00 00 04 66 74 73 34 03 25 0d 00 03 n.%....fts4.%... +| 3120: 01 35 03 25 0f 00 00 03 67 63 63 03 25 03 00 01 .5.%....gcc.%... +| 3136: 06 65 6f 70 7f 6c 79 03 25 11 00 00 05 6a 73 6f .eop.ly.%....jso +| 3152: 6e 31 03 25 14 00 e8 04 6c 6f 61 64 03 25 1c 00 n1.%....load.%.. +| 3168: 00 03 6d 61 78 03 25 18 00 01 05 65 6d 6f 72 79 ..max.%....emory +| 3184: 03 25 19 00 03 04 73 79 73 35 03 25 15 00 00 04 .%....sys5.%.... +| 3200: 6f 6d 69 74 03 25 1b 00 00 05 72 74 72 65 65 03 omit.%....rtree. +| 3216: 25 17 00 00 0a 74 68 72 65 61 64 73 61 66 65 03 %....threadsafe. +| 3232: 25 1e 00 00 04 76 74 61 62 03 25 0b 00 86 50 01 %....vtab.%...P. +| 3248: 08 08 08 08 08 17 8d 12 30 20 38 33 35 00 01 30 ........0 835..0 +| 3264: 12 01 06 00 01 06 00 01 06 00 1f 03 00 01 03 00 ................ +| 3280: 01 03 00 00 08 32 30 31 36 30 36 30 39 09 01 07 .....20160609... +| 3296: 00 01 07 00 01 07 00 00 01 34 09 01 05 00 01 05 .........4...... +| 3312: 00 01 05 00 00 01 35 09 01 04 00 01 04 00 01 04 ......5......... +| 3328: 00 01 07 30 30 30 30 30 30 30 09 1c 04 00 01 04 ...0000000...... +| 3344: 00 01 04 00 00 06 62 69 6e 61 72 79 3c 03 01 02 ......binary<... +| 3360: 02 00 03 01 02 02 00 03 01 02 02 00 03 01 02 02 ................ +| 3376: 00 03 01 02 c2 00 03 01 02 02 00 03 01 04 82 00 ................ +| 3392: 03 01 02 02 00 03 01 02 02 00 03 01 02 02 00 03 ................ +| 3408: 01 02 02 00 03 01 02 02 00 00 08 63 6f 6d 70 69 ...........compi +| 3424: 6c 65 72 09 01 02 00 01 02 00 01 02 00 00 06 64 ler............d +| 3440: 62 73 74 61 74 09 07 03 00 01 03 00 01 03 00 01 bstat........... +| 3456: 04 65 62 75 67 09 04 02 00 01 02 00 01 02 00 00 .ebug........... +| 3472: 06 65 6e 60 62 6c 65 3f 07 02 00 01 02 92 e1 a4 .en`ble?........ +| 3488: ff fc a2 8c 95 b2 3f 01 01 f0 f1 02 00 57 02 00 ......?......W.. +| 3504: 01 02 00 01 02 00 01 02 00 01 02 00 01 02 10 01 ................ +| 3520: 02 00 01 02 00 01 02 00 01 02 01 01 02 00 01 02 ................ +| 3536: 00 01 02 00 00 f2 00 01 08 78 74 65 6e 73 69 6f .........xtensio +| 3552: 6e 09 1f 04 00 01 04 00 01 04 00 00 04 66 74 73 n............fts +| 3568: 34 09 0a 03 00 01 03 00 01 03 00 03 01 35 09 0d 4............5.. +| 3584: 03 00 01 03 00 01 03 00 00 03 67 63 63 09 01 03 ..........gcc... +| 3600: 00 01 03 00 01 03 00 01 06 65 5f 70 6f 6c 79 09 .........e_poly. +| 3616: 10 03 00 01 03 00 01 03 00 00 b3 6a 73 6f 6e 31 ...........json1 +| 3632: 09 13 03 00 01 03 00 01 03 00 00 04 6c 6f 61 64 ............load +| 3648: 09 1f 03 00 01 03 00 01 03 00 00 03 6d 61 78 09 ............max. +| 3664: 1c 02 00 01 02 00 01 02 00 01 05 65 6d 6f 72 79 ...........emory +| 3680: 09 1c 03 00 01 03 00 01 03 00 03 04 73 79 73 35 ............sys5 +| 3696: 09 16 03 00 01 03 00 01 03 cc 00 06 6e 6f 63 61 ............noca +| 3712: 73 65 3c 02 01 02 02 00 03 01 02 02 00 03 01 02 se<............. +| 3728: 02 00 03 01 02 02 00 03 01 02 02 00 03 01 02 02 ................ +| 3744: 00 03 01 02 02 00 03 01 02 02 00 03 01 02 02 00 ................ +| 3760: 03 01 02 02 00 03 01 02 02 00 03 01 02 02 00 00 ................ +| 3776: 04 6f 6d 69 74 09 1f 02 00 01 02 00 01 02 00 00 .omit........... +| 3792: 05 72 74 62 65 65 09 19 03 00 01 03 00 01 03 00 .rtbee.......... +| 3808: 03 02 69 6d 3c 01 01 02 02 00 03 01 02 02 00 03 ..im<........... +| 3824: 01 02 02 00 03 01 02 02 00 03 01 02 02 00 03 01 ................ +| 3840: 02 02 00 03 01 02 02 00 03 01 02 02 00 03 01 02 ................ +| 3856: 02 00 03 01 02 02 00 03 01 02 01 00 03 01 02 02 ................ +| 3872: 00 00 0a 74 68 72 65 61 64 73 61 66 65 09 22 02 ...threadsafe... +| 3888: 00 01 02 00 02 02 00 00 04 76 74 61 62 09 07 04 .........vtab... +| 3904: 00 01 03 00 01 04 00 00 01 78 b4 01 01 01 01 02 .........x...... +| 3920: 00 01 01 01 02 00 01 01 01 02 00 01 01 01 02 00 ................ +| 3936: 01 01 01 02 00 01 01 01 02 00 01 01 01 02 00 01 ................ +| 3952: 01 01 02 00 01 01 01 02 00 01 01 01 02 00 01 01 ................ +| 3968: 01 02 00 01 01 01 02 00 01 01 01 02 00 01 01 01 ................ +| 3984: 02 01 01 01 01 02 00 01 01 01 02 00 01 01 01 02 ................ +| 4000: 00 01 01 01 02 00 01 01 01 02 00 01 01 01 02 00 ................ +| 4016: 01 01 01 02 00 01 01 01 02 00 01 01 01 02 00 01 ................ +| 4032: 01 01 02 00 01 01 01 da 00 00 f1 01 02 00 01 01 ................ +| 4048: 01 02 00 01 01 01 01 ff ff 01 01 02 00 01 01 01 ................ +| 4064: 02 00 01 01 01 02 00 01 01 01 02 00 01 01 01 02 ................ +| 4080: 00 01 01 01 02 00 01 01 01 02 00 01 01 01 02 00 ................ +| page 6 offset 20480 +| 0: 0a 00 00 00 02 0f f5 00 0f fb 0f f5 01 00 00 00 ................ +| 4080: 00 00 00 00 00 05 04 08 09 01 02 04 04 08 08 09 ................ +| end crash-1cc4f8a70485ce.db +}]} {} + +do_execsql_test 38.1 { + UPDATE t1 SET b=a; +} + +do_catchsql_test 38.2 { + SELECT b FROM t1 WHERE a MATCH 'e*e*e*e*e*e*e*e*e*e*e*e*e*e*e*e*' +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db +set saved $sqlite_fts3_enable_parentheses +set sqlite_fts3_enable_parentheses 1 +do_execsql_test 39.0 { + CREATE VIRTUAL TABLE t0 USING fts3( + col0 INTEGER PRIMARY KEY, + col1 VARCHAR(8), + col2 BINARY, + col3 BINARY + ); + INSERT INTO t0_content VALUES(1,1,'1234','aaaa','bbbb'); + INSERT INTO t0_segdir VALUES(0,0,0,0,'0 42',X'000131030782000103323334050101010200000461616161050101020200000462626262050101030200'); +} + +do_test 39.1 { + catch { + db eval { SELECT rowid FROM t0 WHERE t0 MATCH '1 NEAR 1' } + } +} 0 + +do_test 39.2 { + catch { + db eval { + SELECT matchinfo(t0,'yxy') FROM t0 WHERE t0 MATCH x'2b0a312b0a312a312a2a0b5d0a0b0b0a312a0a0b0b0a312a0b310a392a0b0a27312a2a0b5d0a312a0b310a31315d0b310a312a316d2a0b313b15bceaa50a312a0b0a27312a2a0b5d0a312a0b310a312b0b2a310a312a0b2a0b2a0b2e5d0a0bff313336e34a2a312a0b0a3c310b0a0b4b4b0b4b2a4bec40322b2a0b310a0a312a0a0a0a0a0a0a0a0a0b310a312a2a2a0b5d0a0b0b0a312a0b310a312a0b0a4e4541530b310a5df5ced70a0a0a0a0a4f520a0a0a0a0a0a0a312a0b0a4e4541520b310a5d616161610a0a0a0a4f520a0a0a0a0a0a312b0a312a312a0a0a0a0a0a0a004a0b0a310b220a0b0a310a4a22310a0b0a7e6fe0e0e030e0e0e0e0e01176e02000e0e0e0e0e01131320226310a0b0a310a4a22310a0b0a310a766f8b8b4ee0e0300ae0090909090909090909090909090909090909090909090909090909090909090947aaaa540b09090909090909090909090909090909090909090909090909090909090909fae0e0f2f22164e0e0f273e07fefefef7d6dfafafafa6d6d6d6d'; + } + } +} 0 +set sqlite_fts3_enable_parentheses $saved + +#------------------------------------------------------------------------- +reset_db +set saved $sqlite_fts3_enable_parentheses +set sqlite_fts3_enable_parentheses 1 + +do_execsql_test 40.1 { + + CREATE VIRTUAL TABLE t0 USING fts3(col0 INTEGER PRIMARY KEY, col1, col2 ,col3 ); + INSERT INTO t0_segdir VALUES(0,0,0,0,'0 42', + X'0001310301020001033233340500010102000004616161bc050101020200000462626262050101030200' + ); +} + +do_execsql_test 40.2 { + SELECT 0==matchinfo(t0,'sx') FROM t0 WHERE t0 MATCH '1* 2 3 4 5 6 OR 1'; +} 0 + +set sqlite_fts3_enable_parentheses $saved + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 41.1 { + CREATE VIRTUAL TABLE t1 USING fts3(a,b,c); + INSERT INTO t1_segdir VALUES(0,0,0,0,'0 835',X'000130120106000106000106001f030001030001030000083230313630363039090107000107000107000001340901050001050001050000013509010400010400010400010730303030303030091c0400010400010400000662696e6172793c0301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000008636f3870696c657209010200010200010200000664627374617409070300010300010300010465627567090402000102000102000006656e61626c653f07020001020001020001020001020001020001020001020001020001030001010002020001020001020001020001120001020001020001020001020001020001087874656e73696f6e091f0400010400010400000466747334090a0300010300010400030135090d03000103000103000003676363090103000103000103000106656f706f6c790910030001030001030000056a736f6e310913030001030001030000046c6f6164091f030001030001030000036d6178091c02000102000102000105656d6f7279091c03000103000103000304737973350916030001030001030000066e6f636173653c02010202000301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000301020200030102020000046f6d6974091f020001020001020000057274726565091903000103000103000302696d3c010102020003010202000301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000301020200000a746872656164736166650922020001020001020000047674616209070400010400010400000178b401010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200'); +} + +do_execsql_test 41.2 { + SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'rtree ner "json1^enable"'; +} + +#------------------------------------------------------------------------- +do_execsql_test 42.1 { + CREATE VIRTUAL TABLE f USING fts3(a, b); +} +do_execsql_test 42.2 { + INSERT INTO f_segdir VALUES(0,2,1111,0,0,X'00'); + INSERT INTO f_segdir VALUES(0,3,0 ,0,0,X'00013003010200'); +} +do_execsql_test 42.3 { + INSERT INTO f(f) VALUES ('merge=107,2'); +} + +#------------------------------------------------------------------------- +reset_db +set saved $sqlite_fts3_enable_parentheses +set sqlite_fts3_enable_parentheses 1 +do_execsql_test 43.1 { + CREATE VIRTUAL TABLE def USING fts3(xyz); + INSERT INTO def_segdir VALUES(0,0,0,0,0, X'0001310301c9000103323334050d81'); +} {} + +do_execsql_test 43.2 { + SELECT rowid FROM def WHERE def MATCH '1 NEAR 1' +} {1} + +set sqlite_fts3_enable_parentheses $saved + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 44.1 { + CREATE VIRTUAL TABLE t0 USING fts3(col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY); + INSERT INTO t0_content VALUES(0,NULL,NULL,NULL,NULL); + INSERT INTO t0_segdir VALUES(0,0,0,0,'0 42',X'00013103010200010332333405010201ba00000461616161050101020200000462626262050101030200'); +} + +do_execsql_test 44.2 { + SELECT matchinfo(t0, t0) IS NULL FROM t0 WHERE t0 MATCH '1*' +} {0} + +#------------------------------------------------------------------------- +# +reset_db +do_test 45.0 { + sqlite3 db {} + db deserialize [decode_hexdb { +.open --hexdb +| size 24576 pagesize 4096 filename crash-65c98512cc9e49.db +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........ +| 96: 00 00 00 00 0d 0e fc 00 06 0d bc 00 0f ca 0f 6c ...............l +| 112: 0f 04 0e 13 0e c9 0d bc 00 00 00 00 00 00 00 00 ................ +| 3504: 00 00 00 00 00 00 00 00 00 00 00 00 55 06 07 17 ............U... +| 3520: 1b 1b 01 81 01 74 61 62 6c 65 78 31 5f 73 74 61 .....tablex1_sta +| 3536: 74 78 31 5f 73 74 61 74 06 43 52 45 41 54 45 20 tx1_stat.CREATE +| 3552: 54 41 42 4c 45 20 27 78 31 5f 73 74 61 74 27 28 TABLE 'x1_stat'( +| 3568: 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 id INTEGER PRIMA +| 3584: 52 59 20 4b 45 59 2c 20 76 61 6c 75 65 20 42 4c RY KEY, value BL +| 3600: 41 82 29 81 33 04 07 17 1f 1f 01 82 35 74 61 62 A.).3.......5tab +| 3616: 6c 65 78 31 5f 73 65 67 64 69 72 78 31 5f 73 65 lex1_segdirx1_se +| 3632: 67 64 69 72 04 43 52 45 41 54 45 20 54 41 42 4c gdir.CREATE TABL +| 3648: 45 20 27 78 31 5f 73 65 67 64 69 72 27 28 6c 65 E 'x1_segdir'(le +| 3664: 76 65 6c 20 49 4e 54 45 47 45 52 2c 69 64 78 20 vel INTEGER,idx +| 3680: 49 4e 54 45 47 45 52 2c 73 74 61 72 74 5f 62 6c INTEGER,start_bl +| 3696: 6f 63 6b 20 49 4e 54 45 47 45 52 2c 6c 65 61 76 ock INTEGER,leav +| 3712: 65 73 5f 65 6e 64 5f 62 6c 6f 63 6b 20 49 4e 54 es_end_block INT +| 3728: 45 47 45 52 2c 65 6e 64 5f 62 6c 6f 63 6b 20 49 EGER,end_block I +| 3744: 4e 54 45 47 45 52 2c 72 6f 6f 74 20 42 4c 4f 42 NTEGER,root BLOB +| 3760: 2c 50 52 49 4d 41 52 59 20 4b 45 59 28 6c 65 76 ,PRIMARY KEY(lev +| 3776: 65 6c 2c 20 69 64 78 29 29 31 05 06 17 45 1f 01 el, idx))1...E.. +| 3792: 00 69 6e 64 65 78 73 71 6c 69 74 65 5f 61 75 74 .indexsqlite_aut +| 3808: 6f 69 6e 64 65 78 5f 78 31 5f 73 65 67 64 69 72 oindex_x1_segdir +| 3824: 5f 31 78 31 5f 73 65 67 64 69 72 05 00 00 00 08 _1x1_segdir..... +| 3840: 60 00 00 00 66 03 07 17 23 23 01 81 13 74 61 62 `...f...##...tab +| 3856: 6c 65 78 31 5f 73 65 67 6d 65 6e 74 73 78 31 5f lex1_segmentsx1_ +| 3872: 73 65 67 6d 65 6e 74 73 03 43 52 45 41 54 45 20 segments.CREATE +| 3888: 54 41 42 4c 45 20 27 78 31 5f 73 65 67 6d 65 6e TABLE 'x1_segmen +| 3904: 74 73 27 28 62 6c 6f 63 6b 69 64 20 49 4e 54 45 ts'(blockid INTE +| 3920: 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c GER PRIMARY KEY, +| 3936: 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 5c 02 07 17 block BLOB).... +| 3952: 21 21 01 81 03 74 61 62 6c 65 78 31 5f 63 6f 6e !!...tablex1_con +| 3968: 74 65 6e 74 78 31 5f 63 6f 6e 74 65 6e 74 02 43 tentx1_content.C +| 3984: 52 45 41 54 45 20 54 41 42 4c 45 20 27 78 31 5f REATE TABLE 'x1_ +| 4000: 63 6f 6e 74 65 6e 74 27 28 64 6f 63 69 64 20 49 content'(docid I +| 4016: 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b NTEGER PRIMARY K +| 4032: 45 59 2c 20 27 63 30 78 27 29 34 01 06 17 11 11 EY, 'c0x')4..... +| 4048: 08 57 74 61 62 6c 65 78 31 78 31 43 52 45 41 54 .Wtablex1x1CREAT +| 4064: 45 20 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 E VIRTUAL TABLE +| 4080: 78 31 20 55 53 49 4e 47 20 66 74 73 33 28 78 29 x1 USING fts3(x) +| page 2 offset 4096 +| 0: 0d 00 00 00 11 0f 77 f0 0f f8 0f f0 0f e8 0f e0 ......w......... +| 16: 0f d8 0f d0 0f c8 0f c0 00 00 00 00 00 00 00 00 ................ +| 3952: 00 00 00 00 00 00 00 00 06 11 03 00 13 77 78 79 .............wxy +| 3968: 06 10 03 00 13 74 75 76 06 0f 03 00 13 71 33 73 .....tuv.....q3s +| 3984: 06 0e 03 00 13 6e 6f 70 06 0d 03 00 13 6b 6c 6d .....nop.....klm +| 4000: 06 0c 03 04 c3 68 69 6a 06 0b 03 00 13 65 66 67 .....hij.....efg +| 4016: 06 0a 03 00 13 62 63 64 06 09 03 00 13 79 7a 61 .....bcd.....yza +| 4032: 06 08 03 00 13 76 77 78 06 07 03 00 13 73 74 75 .....vwx.....stu +| 4048: 06 06 03 00 13 70 71 72 06 05 03 00 13 6d 6e 6f .....pqr.....mno +| 4064: 06 03 03 00 13 6a 6b 6c 06 03 03 00 13 67 68 69 .....jkl.....ghi +| 4080: 06 02 02 00 03 64 65 66 06 01 03 00 13 61 52 63 .....def.....aRc +| page 3 offset 8192 +| 0: 0d 00 00 00 03 0f a7 00 0f b5 0f a7 0f fa 01 00 ................ +| 4000: 00 00 00 00 00 00 00 0c 02 03 00 1e 00 03 6b 6c ..............kl +| 4016: 6d 03 0d 02 00 43 01 04 00 81 0a 00 03 61 62 63 m....C.......abc +| 4032: 03 0b 32 00 00 03 62 63 64 03 0a 02 00 00 03 64 ..2...bcd......d +| 4048: 69 26 03 02 02 00 00 03 65 66 67 03 0b 02 00 00 i&......efg..... +| 4064: 03 67 68 69 03 03 02 00 00 03 68 69 6a 03 0c 02 .ghi......hij... +| 4080: 00 00 03 6a 6a 2c 03 04 02 00 03 81 00 03 00 00 ...jj,.......... +| page 4 offset 12288 +| 0: 0d 0f 3a 00 05 0f 25 00 0f 9e 0f 88 0f 43 0f 25 ..:...%......C.% +| 16: 0f 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .r.............. +| 3856: 00 00 00 00 00 00 00 00 00 56 01 08 08 13 1e 03 .........V...... +| 3872: 30 20 39 00 03 13 05 07 08 08 18 08 13 1e 30 20 0 9...........0 +| 3888: 39 00 03 77 78 79 03 11 02 00 0f 6c 00 09 01 08 9..wxy.....l.... +| 3904: 08 15 54 27 04 07 09 01 08 08 15 42 02 30 20 33 ..T'.......B.0 3 +| 3920: 36 00 03 6e 6f 70 03 0e 02 00 00 03 71 72 73 03 6..nop......qrs. +| 3936: 0f 02 00 00 03 74 75 76 03 10 02 00 0f cf b1 06 .....tuv........ +| 3952: 01 08 14 06 07 01 08 09 01 1b 14 02 02 31 32 38 .............128 +| 3968: 20 2d 37 32 10 01 01 6b 14 03 07 09 09 08 08 15 -72...k........ +| 3984: 1e 30 20 33 36 00 03 79 7a 61 03 09 02 00 2f 02 .0 36..yza..../. +| 4000: 07 09 08 08 08 15 54 30 20 33 36 00 03 6d 6e 6f ......T0 36..mno +| 4016: 03 05 02 00 00 03 70 71 72 03 06 02 00 00 03 73 ......pqr......s +| 4032: 74 75 03 07 02 00 00 03 76 77 78 03 08 02 00 00 tu......vwx..... +| 4048: 00 00 4a 08 08 08 15 54 30 20 33 36 00 03 61 62 ..J....T0 36..ab +| 4064: 63 03 01 02 00 00 03 64 65 66 03 02 02 00 00 03 c......def...... +| 4080: 67 68 69 03 03 67 00 00 03 6a 6b 6c 03 04 02 00 ghi..g...jkl.... +| page 5 offset 16384 +| 0: 0a 0f e7 00 05 0f da 00 0f e1 0f fa 0f f4 0f ed ................ +| 16: 0f da 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +| 4048: 00 00 00 00 00 00 00 1a 01 03 06 04 01 08 01 02 ................ +| 4064: 06 05 04 08 08 01 05 00 00 00 06 01 03 06 04 09 ................ +| 4080: 02 01 02 04 05 04 09 09 01 03 05 04 09 08 01 02 ................ +| page 6 offset 20480 +| 0: 0d 00 10 00 01 0f f9 00 0f f9 00 00 00 00 00 00 ................ +| 4080: 00 00 00 00 00 00 00 00 00 05 01 03 00 10 01 03 ................ +| end crash-65c98512cc9e49.db +}]} {} + +do_catchsql_test 45.2 { + INSERT INTO x1(x1) VALUES( 'merge=1' ) +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db +set saved $sqlite_fts3_enable_parentheses +set sqlite_fts3_enable_parentheses 1 +do_execsql_test 46.1 { + CREATE VIRTUAL TABLE t0 USING fts3(a INTEGER PRIMARY KEY,b,c,d); + INSERT INTO t0_segdir VALUES(0,0,0,0,'0 42',X'0001310301c9000103323334050d8000f200000461616161050101020200000462626262050101030200'); +} {} + +do_catchsql_test 46.2 { + SELECT * FROM t0 + WHERE t0 MATCH x'2b0a312b0a312a312a2a0b5d0a0b0b0a312a0a0b0b0a312a0b310a392a0b0a27312a2a0b5d0a312a0b310a31315d0b310a312a316d2a0b313b15bceaa50a312a0b0a27312a2a0b5d0a312a0b310a312b0b2a310a312a0b2a0b2a0b2e5d0a0bff313336e34a2a312a0b0a3c310b0a0b4b4b0b4b2a4bec40322b2a0b310a0a312a0a0a0a0a0a0a0a0a0b310a312a2a2a0b5d0a0b0b0a312a0b310a312a0b0a4e4541530b310a5df5ced70a0a0a0a0a4f520a0a0a0a0a0a0a312a0b0a4e4541520b310a5d616161610a0a0a0a4f520a0a0a0a0a0a312b0a312a312a0a0a0a0a0a0a004a0b0a310b220a0b0a310a4a22310a0b0a7e6fe0e0e030e0e0e0e0e01176e02000e0e0e0e0e01131320226310a0b0a310a4a22310a0b0a310a766f8b8b4ee0e0300ae0090909090909090909090909090909090909090909090909090909090909090947aaaa540b09090909090909090909090909090909090909090909090909090909090909fae0e0f2f22164e0e0f273e07fefefef7d6dfafafafa6d6d6d6d'; +} {1 {database disk image is malformed}} + +set sqlite_fts3_enable_parentheses $saved +extra_schema_checks 1 + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 47.1 { + CREATE VIRTUAL TABLE t1 USING fts3(a,b,c); +} +do_execsql_test 47.2 { + INSERT INTO t1_segdir VALUES(0,0,0,0,0,X'000130120106000106000106001f030001030001030000083230313630363039090107000107000107000001340901050001050001050000013509010400010400010400010730303030303030091c0400010400010400000662696e6172793c0301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000008636f6d70696c657209010200010200010200000664627374617409070300010300010300010465627567090402000102000102000006656e61626c653f07020001020001020001020001020001020001020001020001020001020001020001020001010001020001020001020001020001020001020001020001020001087874656e73696f6e091f0400010400010400000466747334090a0300010300010300030135090d03000103000103000003676363090103000103000103000106656f706f6c790910030001030001030000056a736f6e310913030001030001030000046c6f6164091f030001030001030000036d6178091c02000102000102000105656d6f7279091c03000103000103000304737973350916030001030001030000066e6f636173653c02010202000301020200030102020003010202000301020200030102020003010202000301020200030102020003010202000301020200030102020000046f6d6974091f020001020001020000057274726565091903000103000103000302696d3c01010202000301020200030102020003010202000301020200030102020003010202000301a202000301020200030102020003010202000301020200000a746872656164736166650922020001020001020000047674616209070400010400010400000178b401010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200010101020001010102000101010200'); + INSERT INTO t1_segdir VALUES(0,1,0,0,0,X'0001300425061b000008323031363036303903250700000134032505000001350325040001073030303030303003251a000008636f6d70696c657203250200000664627374617403250a00010465627567032508000006656e61626c650925090504040404040001087874656e73696f6e03251d0000046674733403250d0003013503250f000003676363032503000106656f706f6c790325110000056a736f6e310325130000046c6f616403251c0000036d6178032518000105656d6f7279032519000304737973350325150000046f6d697403251b000005727472656503251700000a7468726561647361666503251e0000047674616333250b00'); +} + +do_catchsql_test 47.3 { + SELECT matchinfo(t1) FROM t1 WHERE t1 MATCH '"json1 enable"'; +} {1 {database disk image is malformed}} + finish_test diff --git a/test/fts3corrupt6.test b/test/fts3corrupt6.test new file mode 100644 index 0000000..9e22bdf --- /dev/null +++ b/test/fts3corrupt6.test @@ -0,0 +1,60 @@ +# 2020 June 8 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is testing the FTS3 module. +# +# $Id: fts3aa.test,v 1.1 2007/08/20 17:38:42 shess Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/fts3_common.tcl +set testprefix fts3corrupt6 + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +set ::saved_sqlite_fts3_enable_parentheses $::sqlite_fts3_enable_parentheses +set sqlite_fts3_enable_parentheses 1 +sqlite3_fts3_may_be_corrupt 1 +database_may_be_corrupt + +do_execsql_test 1.0 { + BEGIN TRANSACTION; + CREATE TABLE t_content(col0 INTEGER); + PRAGMA writable_schema=ON; + CREATE VIRTUAL TABLE t0 USING fts3(col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY); + INSERT INTO t0_content VALUES(0,NULL,NULL,NULL,NULL); + INSERT INTO t0_segdir VALUES(0,0,0,0,'0 42',X'000131030102000103323334050101010200000461616161050101020200000462626262050101030200'); + COMMIT; +} + +do_execsql_test 1.1 { + SELECT 0+matchinfo(t0,'yxyyxy') FROM t0 WHERE t0 MATCH CAST( x'2b0a312b0a312a312a2a0b5d0a0b0b0a312a0a0b0b0a312a0b310a392a0b0a27312a2a0b5d0a312a0b310a31315d0b310a312a316d2a0b313b15bceaa50a312a0b0a27312a2a0b5d0a312a0b310a312b0b2a310a312a0b2a0b2a0b2e5d0a0bff313336e34a2a312a0b0a3c310b0a0b4b4b0b4b2a4bec40322b2a0b310a0a312a0a0a0a0a0a0a0a0a0b310a312a2a2a0b5d0a0b0b0a312a0b310a312a0b0a4e4541530b310a5df5ced70a0a0a0a0a4f520a0a0a0a0a0a0a312a0b0a4e4541520b310a5d616161610a0a0a0a4f520a0a0a0a0a0a312b0a312a312a0a0a0a0a0a0a004a0b0a310b220a0b0a310a4a22310a0b0a7e6fe0e0e030e0e0e0e0e01176e02000e0e0e0e0e01131320226310a0b0a310a4a22310a0b0a310a766f8b8b4ee0e0300ae0090909090909090909090909090909090909090909090909090909090909090947aaaa540b09090909090909090909090909090909090909090909090909090909090909fae0e0f2f22164e0e0f273e07fefefef7d6dfafafafa6d6d6d6d' AS TEXT); +} {0} + +do_execsql_test 1.2 { + CREATE VIRTUAL TABLE t1 USING fts3(col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY); + INSERT INTO t1_content VALUES(0,NULL,NULL,NULL,NULL); + INSERT INTO t1_segdir VALUES(0,0,0,0,'0 42',X'000131030102000103323334050101010200000461616161050101020200000462626262050101030200'); +} + +do_execsql_test 1.3 { + SELECT 42+matchinfo(t1,'yxyyxy') FROM t1 WHERE t1 MATCH x'2b0a312b0a312a312a2a0b5d0a0b0b0a312a0a0b0b0a312a0b310a392a0b0a27312a2a0b5d0a312a0b310a31315d0b310a312a316d2a0b313b15bceaa50a312a0b0a27312a2a0b5d0a312a0b310a312b0b2a310a312a0b2a0b2a0b2e5d0a0bff313336e34a2a312a0b0a3c310b0a0b4b4b0b4b2a4bec40322b2a0b310a0a312a0a0a0a0a0a0a0a0a0b310a312a2a2a0b5d0a0b0b0a312a0b310a312a0b0a4e4541530b310a5df5ced70a0a0a0a0a4f520a0a0a0a0a0a0a312a0b0a4e4541520b310a5d616161610a0a0a0a4f520a0a0a0a0a0a312b0a312a312a0a0a0a0a0a0a004a0b0a310b220a0b0a310a4a22310a0b0a7e6fe0e0e030e0e0e0e0e01176e02000e0e0e0e0e01131320226310a0b0a310a4a22310a0b0a310a766f8b8b4ee0e0300ae0090909090909090909090909090909090909090909090909090909090909090947aaaa540b09090909090909090909090909090909090909090909090909090909090909fae0e0f2f22164e0e0f273e07fefefef7d6dfafafafa6d6d6d6d'; +} {42} + +set sqlite_fts3_enable_parentheses $saved_sqlite_fts3_enable_parentheses +finish_test + + diff --git a/test/fts3matchinfo2.test b/test/fts3matchinfo2.test new file mode 100644 index 0000000..670e107 --- /dev/null +++ b/test/fts3matchinfo2.test @@ -0,0 +1,35 @@ +# 2020-05-14 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for the FTS3 module. The focus +# of this file is tables created with the "matchinfo=fts3" option. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# If SQLITE_ENABLE_FTS3 is not defined, omit this file. +ifcapable !fts3 { finish_test ; return } + +set sqlite_fts3_enable_parentheses 1 + +# Crash case found by cyg0810 at gmail.com 2020-05-14. Reported to +# chromium (which is not vulnerable) who kindly referred it to us. +# +do_execsql_test 1.0 { + CREATE TABLE t_content(col0 INTEGER); + CREATE VIRTUAL TABLE t0 USING fts3(col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY); + INSERT INTO t0 VALUES (1, '1234','aaaa','bbbb'); + SELECT hex(matchinfo(t0,'yxy')) FROM t0 WHERE t0 MATCH x'2b0a312b0a312a312a2a0b5d0a0b0b0a312a0a0b0b0a312a0b310a392a0b0a27312a2a0b5d0a312a0b310a31315d0b310a312a316d2a0b313b15bceaa50a312a0b0a27312a2a0b5d0a312a0b310a312b0b2a310a312a0b2a0b2a0b2e5d0a0bff313336e34a2a312a0b0a3c310b0a0b4b4b0b4b2a4bec40322b2a0b310a0a312a0a0a0a0a0a0a0a0a0b310a312a2a2a0b5d0a0b0b0a312a0b310a312a0b0a4e4541530b310a5df5ced70a0a0a0a0a4f520a0a0a0a0a0a0a312a0b0a4e4541520b310a5d616161610a0a0a0a4f520a0a0a0a0a0a312b0a312a312a0a0a0a0a0a0a004a0b0a310b220a0b0a310a4a22310a0b0a7e6fe0e0e030e0e0e0e0e01176e02000e0e0e0e0e01131320226310a0b0a310a4a22310a0b0a310a766f8b8b4ee0e0300ae0090909090909090909090909090909090909090909090909090909090909090947aaaa540b09090909090909090909090909090909090909090909090909090909090909fae0e0f2f22164e0e0f273e07fefefef7d6dfafafafa6d6d6d6d'; +} {/000000.*0000000/} + + +set sqlite_fts3_enable_parentheses 0 +finish_test diff --git a/test/fts3misc.test b/test/fts3misc.test index 92b93d0..a1bec42 100644 --- a/test/fts3misc.test +++ b/test/fts3misc.test @@ -303,4 +303,24 @@ do_execsql_test 9.2 { -4764623217061966105 8324454597464624651 } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 10.0 { + CREATE VIRTUAL TABLE f USING fts3(a,b); + CREATE TABLE 'f_stat'(id INTEGER PRIMARY KEY, value BLOB); + INSERT INTO f_stat VALUES (1,x'3b3b3b3b3b3b3b28ffffffffffffffffff1807f9073481f1d43bc93b3b3b3b3b3b3b3b3b3b18073b3b3b3b3b3b3b9b003b'); +} {} + +do_catchsql_test 10.1 { + INSERT INTO f(f) VALUES ('merge=69,59'); +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +do_execsql_test 11.0 { + CREATE VIRTUAL TABLE xyz USING fts3(); +} +do_execsql_test 11.1 { + SELECT * FROM xyz WHERE xyz MATCH 'a NEAR/4294836224 a'; +} + finish_test diff --git a/test/fts3snippet.test b/test/fts3snippet.test index 749aa4e..ae022b6 100644 --- a/test/fts3snippet.test +++ b/test/fts3snippet.test @@ -588,18 +588,5 @@ do_execsql_test 5.1 { {[a70] [a71] [a72]} } -#------------------------------------------------------------------------- -# Request a snippet from a query with more than 64 phrases. -# -reset_db -do_execsql_test 6.0 { - CREATE VIRTUAL TABLE f USING fts3(b); - INSERT INTO f VALUES ( x'746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218'); -} - -do_execsql_test 6.1 { - SELECT length(snippet(f))>0 FROM f WHERE b MATCH x'1065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a010f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c2a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e0f42'; -} {1} - set sqlite_fts3_enable_parentheses 0 finish_test diff --git a/test/fts3snippet2.test b/test/fts3snippet2.test new file mode 100644 index 0000000..fc39941 --- /dev/null +++ b/test/fts3snippet2.test @@ -0,0 +1,60 @@ +# 2020-05-14 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# +# The tests in this file test the FTS3 auxillary functions offsets(), +# snippet() and matchinfo() work. At time of writing, running this file +# provides full coverage of fts3_snippet.c. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix fts3snippet + +# If SQLITE_ENABLE_FTS3 is not defined, omit this file. +ifcapable !fts3 { finish_test ; return } +source $testdir/fts3_common.tcl + +set sqlite_fts3_enable_parentheses 1 +#------------------------------------------------------------------------- +# Request a snippet from a query with more than 64 phrases. +# +reset_db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE f USING fts3(b); + INSERT INTO f VALUES ( x'746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218'); +} + +do_execsql_test 1.1 { + SELECT length(snippet(f))>0 FROM f WHERE b MATCH x'1065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a010f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c2a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e082a011065616e656d655a616c702a2f65732e0f42014001380230018218021001081e0a3d746e6e6d64612e0f42'; +} {1} + +reset_db +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE t0 USING fts3(col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY); + INSERT INTO t0 VALUES (1, '1234','aaaa','bbbb'); + SELECT snippet(t0) FROM t0 WHERE t0 MATCH x'0a4d4d4d4d320a4f52d70a310a310a4e4541520a0a31f6ce0a4f520a0a310a310a310a4f520a75fc2a242424' ; +} {1} + +reset_db +do_execsql_test 2.1 { + CREATE VIRTUAL TABLE t0 USING fts3( + col0 INTEGER PRIMARY KEY,col1 VARCHAR(8),col2 BINARY,col3 BINARY + ); + INSERT INTO t0 VALUES ('one', '1234','aaaa','bbbb'); +} +do_execsql_test 2.2 { + SELECT snippet(t0) FROM t0 WHERE t0 MATCH + '(def AND (one NEAR abc)) OR one' +} {one} + +set sqlite_fts3_enable_parentheses 0 +finish_test + diff --git a/test/fts4aa.test b/test/fts4aa.test index 7349841..0c6c0b9 100644 --- a/test/fts4aa.test +++ b/test/fts4aa.test @@ -229,13 +229,18 @@ do_catchsql_test fts4aa-5.70 { # 2019-11-18 https://bugs.chromium.org/p/chromium/issues/detail?id=1025467 db close sqlite3 db :memory: -do_execsql_test fts4aa-6.10 { +if {$tcl_platform(byteOrder)=="littleEndian"} { + set res {X'0200000000000000000000000E0000000E00000001000000010000000100000001000000'} +} else { + set res {X'0000000200000000000000000000000E0000000E00000001000000010000000100000001'} +} +do_catchsql_test fts4aa-6.10 { CREATE VIRTUAL TABLE f USING fts4(); INSERT INTO f_segdir VALUES (77,91,0,0,'255 77',x'0001308000004d5c4ddddddd4d4d7b4d4d4d614d8019ff4d05000001204d4d2e4d6e4d4d4d4b4d6c4d004d4d4d4d4d4d3d000000004d5d4d4d645d4d004d4d4d4d4d4d4d4d4d454d6910004d05ffff054d646c4d004d5d4d4d4d4d3d000000004d4d4d4d4d4d4d4d4d4d4d69624d4d4d04004d4d4d4d4d604d4ce1404d554d45'); INSERT INTO f_segdir VALUES (77,108,0,0,'255 77',x'0001310000fa64004d4d4d3c5d4d654d4d4d614d8000ff4d05000001204d4d2e4d6e4d4d4dff4d4d4d4d4d4d00104d4d4d4d000000004d4d4d0400311d4d4d4d4d4d4d4d4d4d684d6910004d05ffff054d4d6c4d004d4d4d4d4d4d3d000000004d4d4d4d644d4d4d4d4d4d69624d4d4d03ed4d4d4d4d4d604d4ce1404d550080'); INSERT INTO f_stat VALUES (0,x'80808080100000000064004d4d4d3c4d4d654d4d4d614d8000ff4df6ff1a00204d4d2e4d6e4d4d4d104d4d4d4d4d4d00104d4d4d4d4d4d69574d4d4d000031044d4d4d3e4d4d4c4d05004d6910'); SELECT quote(matchinfo(f,'pnax')) from f where f match '0 1'; -} {X'0200000000000000000000000E0000000E00000001000000010000000100000001000000'} +} {1 {database disk image is malformed}} # 2019-11-18 Detect infinite loop in fts3SelectLeaf() db close diff --git a/test/fts4min.test b/test/fts4min.test new file mode 100644 index 0000000..ca63b39 --- /dev/null +++ b/test/fts4min.test @@ -0,0 +1,53 @@ +# 2020 February 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/fts3_common.tcl +set ::testprefix fts4min + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +#------------------------------------------------------------------ +do_execsql_test 0.0 { + CREATE TABLE t1(a NOT NULL, b); + CREATE INDEX i1 ON t1(a); +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts3(c); + INSERT INTO ft(docid, c) VALUES(22, 'hello world'); + INSERT INTO ft(docid, c) VALUES(44, 'hello world'); + INSERT INTO ft(docid, c) VALUES(11, 'hello world'); +} + +do_eqp_test 1.1.1 { + SELECT max(rowid) FROM ft +} {VIRTUAL TABLE INDEX 0:DESC} + +do_eqp_test 1.1.2 { + SELECT min(rowid) FROM ft +} {VIRTUAL TABLE INDEX 0:ASC} + +do_execsql_test 1.2.1 { + SELECT max(rowid) FROM ft +} {44} + +do_execsql_test 1.2.2 { + SELECT min(rowid) FROM ft +} {11} + +finish_test diff --git a/test/fts4upfrom.test b/test/fts4upfrom.test new file mode 100644 index 0000000..b1b43a0 --- /dev/null +++ b/test/fts4upfrom.test @@ -0,0 +1,140 @@ +# 2020 February 24 +# +# 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 implements regression tests for SQLite library. The +# focus of this script is testing UPDATE statements with FROM clauses +# against FTS4 tables. +# +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix fts4upfrom + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +foreach {tn create_table} { + 0 { CREATE VIRTUAL TABLE ft USING fts5(a, b, c) } + 1 { CREATE VIRTUAL TABLE ft USING fts3(a, b, c) } + 2 { CREATE TABLE ft(a, b, c) } + 3 { + CREATE TABLE real(a, b, c); + CREATE INDEX i1 ON real(a); + CREATE VIEW ft AS SELECT rowid, a, b, c FROM real; + CREATE TRIGGER tr1 INSTEAD OF INSERT ON ft BEGIN + INSERT INTO real(rowid, a, b, c) VALUES(new.rowid, new.a, new.b, new.c); + END; + CREATE TRIGGER tr2 INSTEAD OF UPDATE ON ft BEGIN + UPDATE real SET rowid=new.rowid, a=new.a, b=new.b, c=new.c + WHERE rowid=old.rowid; + END; + } +} { + if {$tn==0} { ifcapable !fts5 { continue } } + catchsql { DROP VIEW IF EXISTS changes } + catchsql { DROP TABLE IF EXISTS ft } + catchsql { DROP VIEW IF EXISTS ft } + execsql $create_table + + do_execsql_test 1.$tn.0 { + INSERT INTO ft(a, b, c) VALUES('a', NULL, 'apple'); + INSERT INTO ft(a, b, c) VALUES('b', NULL, 'banana'); + INSERT INTO ft(a, b, c) VALUES('c', NULL, 'cherry'); + INSERT INTO ft(a, b, c) VALUES('d', NULL, 'damson plum'); + } + + do_execsql_test 1.$tn.1 { + SELECT a, b, c FROM ft ORDER BY rowid; + } { + a {} apple + b {} banana + c {} cherry + d {} {damson plum} + } + + do_execsql_test 1.$tn.2 { + UPDATE ft SET b=o.c FROM ft AS o WHERE (ft.a == char(unicode(o.a)+1)) + } + + do_execsql_test 1.$tn.3 { + SELECT a, b, c FROM ft ORDER BY rowid; + } { + a {} apple + b apple banana + c banana cherry + d cherry {damson plum} + } + + do_catchsql_test 1.$tn.4 { + UPDATE ft SET c=v FROM changes WHERE a=k; + } {1 {no such table: changes}} + + do_execsql_test 1.$tn.5 { + create view changes(k, v) AS + VALUES( 'd', 'dewberry' ) UNION ALL + VALUES( 'c', 'clementine' ) UNION ALL + VALUES( 'b', 'blueberry' ) UNION ALL + VALUES( 'a', 'apricot' ) + ; + } + + do_execsql_test 1.$tn.6 { + UPDATE ft SET c=v FROM changes WHERE a=k; + } + + do_execsql_test 1.$tn.7 { + SELECT rowid, a, b, c FROM ft ORDER BY rowid; + } { + 1 a {} apricot + 2 b apple blueberry + 3 c banana clementine + 4 d cherry dewberry + } + + do_execsql_test 1.$tn.8 " + WITH x1(o, n) AS ( + VALUES(1, 11) UNION ALL + VALUES(2, 12) UNION ALL + VALUES(3, 13) UNION ALL + VALUES(4, 14) + ) + SELECT ft.rowid, a, b, c, o, n FROM ft, x1 WHERE ft.rowid = o; + " { + 1 a {} apricot 1 11 + 2 b apple blueberry 2 12 + 3 c banana clementine 3 13 + 4 d cherry dewberry 4 14 + } + + set ROWID rowid + if {$tn==1} { set ROWID docid } + do_execsql_test 1.$tn.9 " + WITH x1(o, n) AS ( + VALUES(1, 11) UNION ALL + VALUES(2, 12) UNION ALL + VALUES(3, 13) UNION ALL + VALUES(4, 14) + ) + UPDATE ft SET $ROWID = n FROM x1 WHERE ft.rowid = o; + SELECT rowid, a, b, c FROM ft ORDER BY rowid; + " { + 11 a {} apricot + 12 b apple blueberry + 13 c banana clementine + 14 d cherry dewberry + } +} + +finish_test + diff --git a/test/func.test b/test/func.test index 34a6f18..4b235be 100644 --- a/test/func.test +++ b/test/func.test @@ -1477,4 +1477,24 @@ do_execsql_test func-34.10 { SELECT * FROM t1; } {1 2} +# 2020-03-11 COALESCE() should short-circuit +# See also ticket 3c9eadd2a6ba0aa5 +# Both issues stem from the fact that functions that could +# throw exceptions were being factored out into initialization +# code. The fix was to put those function calls inside of +# OP_Once instead. +# +reset_db +do_execsql_test func-35.100 { + CREATE TABLE t1(x); + SELECT coalesce(x, abs(-9223372036854775808)) FROM t1; +} {} +do_execsql_test func-35.110 { + SELECT coalesce(x, 'xyz' LIKE printf('%.1000000c','y')) FROM t1; +} {} +do_execsql_test func-35.200 { + CREATE TABLE t0(c0 CHECK(ABS(-9223372036854775808))); + PRAGMA integrity_check; +} {ok} + finish_test diff --git a/test/func4.test b/test/func4.test index e94f8c3..fe7d042 100644 --- a/test/func4.test +++ b/test/func4.test @@ -451,7 +451,7 @@ ifcapable check { catchsql { INSERT INTO t1 (x) VALUES ('1234.00'); } - } {1 {CHECK constraint failed: t1}} + } {0 {}} do_test func4-3.17 { catchsql { INSERT INTO t1 (x) VALUES (1234.00); @@ -461,7 +461,7 @@ ifcapable check { catchsql { INSERT INTO t1 (x) VALUES ('-9223372036854775809'); } - } {1 {CHECK constraint failed: t1}} + } {0 {}} if {$highPrecision(1)} { do_test func4-3.19 { catchsql { @@ -470,8 +470,8 @@ ifcapable check { } {1 {CHECK constraint failed: t1}} } do_execsql_test func4-3.20 { - SELECT x FROM t1 ORDER BY x; - } {1234 1234 1234} + SELECT x FROM t1 WHERE x>0 ORDER BY x; + } {1234 1234 1234 1234} ifcapable floatingpoint { do_execsql_test func4-4.1 { diff --git a/test/func5.test b/test/func5.test index bfd545b..8c3dd05 100644 --- a/test/func5.test +++ b/test/func5.test @@ -53,9 +53,10 @@ do_execsql_test func5-2.2 { WHERE x+counter1('hello')=counter1('hello')+x ORDER BY +x; } {} +set cvalue [db one {SELECT counter2('hello')+1}] do_execsql_test func5-2.3 { SELECT x, y FROM t2 - WHERE x+counter2('hello')=counter2('hello')+x + WHERE x+counter2('hello')=$cvalue+x ORDER BY +x; } {1 2 3 4 5 6 7 8} diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index 5add53c..c57cf56 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -11,8 +11,7 @@ ************************************************************************* ** ** This is a utility program designed to aid running regressions tests on -** the SQLite library using data from an external fuzzer, such as American -** Fuzzy Lop (AFL) (http://lcamtuf.coredump.cx/afl/). +** the SQLite library using data from external fuzzers. ** ** This program reads content from an SQLite database file with the following ** schema: @@ -63,6 +62,21 @@ ** If fuzzcheck does crash, it can be run in the debugger and the content ** of the global variable g.zTextName[] will identify the specific XSQL and ** DB values that were running when the crash occurred. +** +** DBSQLFUZZ: +** +** The dbsqlfuzz fuzzer includes both a database file and SQL to run against +** that database in its input. This utility can now process dbsqlfuzz +** input files. Load such files using the "--load-dbsql FILE ..." command-line +** option. +** +** Dbsqlfuzz inputs are ordinary text. The first part of the file is text +** that describes the content of the database (using a lot of hexadecimal), +** then there is a divider line followed by the SQL to run against the +** database. Because they are ordinary text, dbsqlfuzz inputs are stored +** in the XSQL table, as if they were ordinary SQL inputs. The isDbSql() +** function can look at a text string and determine whether or not it is +** a valid dbsqlfuzz input. */ #include #include @@ -80,11 +94,9 @@ # include #endif -#ifdef SQLITE_OSS_FUZZ -# include -# if !defined(_MSC_VER) -# include -# endif +#include +#if !defined(_MSC_VER) +# include #endif #if defined(_MSC_VER) @@ -322,6 +334,39 @@ static void readfileFunc( fclose(in); } +/* +** Implementation of the "readtextfile(X)" SQL function. The text content +** of the file named X through the end of the file or to the first \000 +** character, whichever comes first, is read and returned as TEXT. NULL +** is returned if the file does not exist or is unreadable. +*/ +static void readtextfileFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zName; + FILE *in; + long nIn; + char *pBuf; + + zName = (const char*)sqlite3_value_text(argv[0]); + if( zName==0 ) return; + in = fopen(zName, "rb"); + if( in==0 ) return; + fseek(in, 0, SEEK_END); + nIn = ftell(in); + rewind(in); + pBuf = sqlite3_malloc64( nIn+1 ); + if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ + pBuf[nIn] = 0; + sqlite3_result_text(context, pBuf, -1, sqlite3_free); + }else{ + sqlite3_free(pBuf); + } + fclose(in); +} + /* ** Implementation of the "writefile(X,Y)" SQL function. The argument Y ** is written into file X. The number of bytes written is returned. Or @@ -466,13 +511,64 @@ static int lengthLimit = 1000000; static int depthLimit = 500; /* Limit on the amount of heap memory that can be used */ -static sqlite3_int64 heapLimit = 1000000000; +static sqlite3_int64 heapLimit = 100000000; /* Maximum byte-code program length in SQLite */ static int vdbeOpLimit = 25000; /* Maximum size of the in-memory database */ static sqlite3_int64 maxDbSize = 104857600; +/* OOM simulation parameters */ +static unsigned int oomCounter = 0; /* Simulate OOM when equals 1 */ +static unsigned int oomRepeat = 0; /* Number of OOMs in a row */ +static void*(*defaultMalloc)(int) = 0; /* The low-level malloc routine */ + +/* This routine is called when a simulated OOM occurs. It is broken +** out as a separate routine to make it easy to set a breakpoint on +** the OOM +*/ +void oomFault(void){ + if( eVerbosity ){ + printf("Simulated OOM fault\n"); + } + if( oomRepeat>0 ){ + oomRepeat--; + }else{ + oomCounter--; + } +} + +/* This routine is a replacement malloc() that is used to simulate +** Out-Of-Memory (OOM) errors for testing purposes. +*/ +static void *oomMalloc(int nByte){ + if( oomCounter ){ + if( oomCounter==1 ){ + oomFault(); + return 0; + }else{ + oomCounter--; + } + } + return defaultMalloc(nByte); +} + +/* Register the OOM simulator. This must occur before any memory +** allocations */ +static void registerOomSimulator(void){ + sqlite3_mem_methods mem; + sqlite3_shutdown(); + sqlite3_config(SQLITE_CONFIG_GETMALLOC, &mem); + defaultMalloc = mem.xMalloc; + mem.xMalloc = oomMalloc; + sqlite3_config(SQLITE_CONFIG_MALLOC, &mem); +} + +/* Turn off any pending OOM simulation */ +static void disableOom(void){ + oomCounter = 0; + oomRepeat = 0; +} /* ** Translate a single byte of Hex into an integer. @@ -653,6 +749,9 @@ static int block_troublesome_sql( ){ return SQLITE_DENY; } + if( sqlite3_stricmp("oom",zArg1)==0 && zArg2!=0 && zArg2[0]!=0 ){ + oomCounter = atoi(zArg2); + } }else if( (eCode==SQLITE_ATTACH || eCode==SQLITE_DETACH) && zArg1 && zArg1[0] ){ return SQLITE_DENY; @@ -795,6 +894,7 @@ int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte){ if( depthLimit>0 ){ sqlite3_limit(cx.db, SQLITE_LIMIT_EXPR_DEPTH, depthLimit); } + sqlite3_limit(cx.db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 100); sqlite3_hard_heap_limit64(heapLimit); if( nDb>=20 && aDb[18]==2 && aDb[19]==2 ){ @@ -1324,6 +1424,7 @@ static void showHelp(void){ " -q|--quiet Reduced output\n" " --rebuild Rebuild and vacuum the database file\n" " --result-trace Show the results of each SQL command\n" +" --spinner Use a spinner to show progress\n" " --sqlid N Use only SQL where sqlid=N\n" " --timeout N Abort if any single test needs more than N seconds\n" " -v|--verbose Increased output. Repeat for more output.\n" @@ -1350,6 +1451,7 @@ int main(int argc, char **argv){ int rebuildFlag = 0; /* --rebuild */ int vdbeLimitFlag = 0; /* --limit-vdbe */ int infoFlag = 0; /* --info */ + int bSpinner = 0; /* True for --spinner */ int timeoutTest = 0; /* undocumented --timeout-test flag */ int runFlags = 0; /* Flags sent to runSql() */ char *zMsg = 0; /* Add this message */ @@ -1374,6 +1476,7 @@ int main(int argc, char **argv){ int openFlags4Data; /* Flags for sqlite3_open_v2() */ int nV; /* How much to increase verbosity with -vvvv */ + registerOomSimulator(); sqlite3_initialize(); iBegin = timeOfDay(); #ifdef __unix__ @@ -1425,7 +1528,8 @@ int main(int argc, char **argv){ vdbeLimitFlag = 1; }else if( strcmp(z,"load-sql")==0 ){ - zInsSql = "INSERT INTO xsql(sqltext)VALUES(CAST(readfile(?1) AS text))"; + zInsSql = "INSERT INTO xsql(sqltext)" + "VALUES(CAST(readtextfile(?1) AS text))"; iFirstInsArg = i+1; openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; break; @@ -1437,7 +1541,8 @@ int main(int argc, char **argv){ break; }else if( strcmp(z,"load-dbsql")==0 ){ - zInsSql = "INSERT INTO xsql(sqltext)VALUES(CAST(readfile(?1) AS text))"; + zInsSql = "INSERT INTO xsql(sqltext)" + "VALUES(CAST(readtextfile(?1) AS text))"; iFirstInsArg = i+1; openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; dbSqlOnly = 1; @@ -1473,6 +1578,9 @@ int main(int argc, char **argv){ if( strcmp(z,"result-trace")==0 ){ runFlags |= SQL_OUTPUT; }else + if( strcmp(z,"spinner")==0 ){ + bSpinner = 1; + }else if( strcmp(z,"sqlid")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); onlySqlid = integerValue(argv[++i]); @@ -1623,6 +1731,8 @@ int main(int argc, char **argv){ if( zInsSql ){ sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); + sqlite3_create_function(db, "readtextfile", 1, SQLITE_UTF8, 0, + readtextfileFunc, 0, 0); sqlite3_create_function(db, "isdbsql", 1, SQLITE_UTF8, 0, isDbSqlFunc, 0, 0); rc = sqlite3_prepare_v2(db, zInsSql, -1, &pStmt, 0); @@ -1741,6 +1851,7 @@ int main(int argc, char **argv){ /* Limit available memory, if requested */ sqlite3_shutdown(); + if( nMemThisDb>0 && nMem==0 ){ if( !nativeMalloc ){ pHeap = realloc(pHeap, nMemThisDb); @@ -1765,11 +1876,16 @@ int main(int argc, char **argv){ /* Run a test using each SQL script against each database. */ - if( !verboseFlag && !quietFlag ) printf("%s:", zDbName); + if( !verboseFlag && !quietFlag && !bSpinner ) printf("%s:", zDbName); for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){ if( isDbSql(pSql->a, pSql->sz) ){ sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d",pSql->id); - if( verboseFlag ){ + if( bSpinner ){ + int nTotal =g.nSql; + int idx = pSql->seq; + printf("\r%s: %d/%d ", zDbName, idx, nTotal); + fflush(stdout); + }else if( verboseFlag ){ printf("%s\n", g.zTestName); fflush(stdout); }else if( !quietFlag ){ @@ -1785,6 +1901,7 @@ int main(int argc, char **argv){ runCombinedDbSqlInput(pSql->a, pSql->sz); nTest++; g.zTestName[0] = 0; + disableOom(); continue; } for(pDb=g.pFirstDb; pDb; pDb=pDb->pNext){ @@ -1792,7 +1909,12 @@ int main(int argc, char **argv){ const char *zVfs = "inmem"; sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d,dbid=%d", pSql->id, pDb->id); - if( verboseFlag ){ + if( bSpinner ){ + int nTotal = g.nDb*g.nSql; + int idx = pSql->seq*g.nDb + pDb->id - 1; + printf("\r%s: %d/%d ", zDbName, idx, nTotal); + fflush(stdout); + }else if( verboseFlag ){ printf("%s\n", g.zTestName); fflush(stdout); }else if( !quietFlag ){ @@ -1871,7 +1993,9 @@ int main(int argc, char **argv){ } } } - if( !quietFlag && !verboseFlag ){ + if( bSpinner ){ + printf("\n"); + }else if( !quietFlag && !verboseFlag ){ printf(" 100%% - %d tests\n", g.nDb*g.nSql); } diff --git a/test/fuzzdata7.db b/test/fuzzdata7.db index 8706be4..99daab1 100644 Binary files a/test/fuzzdata7.db and b/test/fuzzdata7.db differ diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db index 5f6d746..2408696 100644 Binary files a/test/fuzzdata8.db and b/test/fuzzdata8.db differ diff --git a/test/gencol1.test b/test/gencol1.test index 5276d96..43f48df 100644 --- a/test/gencol1.test +++ b/test/gencol1.test @@ -560,4 +560,30 @@ do_catchsql_test gencol1-19.10 { INSERT INTO t0(c1) VALUES(0.16334143182538696), (0); } {1 {UNIQUE constraint failed: t0.c0}} +# 2020-06-29 forum bug report. +# https://sqlite.org/forum/forumpost/73b9a8ccfb +# +do_execsql_test gencol1-20.1 { + CREATE TEMPORARY TABLE tab ( + prim DATE PRIMARY KEY, + a INTEGER, + comp INTEGER AS (a), + b INTEGER, + x INTEGER + ); + -- Add some data + INSERT INTO tab (prim, a, b) VALUES ('2001-01-01', 0, 0); + -- Check that each column is 0 like I expect + SELECT * FROM tab; +} {2001-01-01 0 0 0 {}} +do_execsql_test gencol1-20.2 { + -- Do an UPSERT on the b column + INSERT INTO tab (prim, b) + VALUES ('2001-01-01',5) + ON CONFLICT(prim) DO UPDATE SET b=excluded.b; + -- Now b is NULL rather than 5 + SELECT * FROM tab; +} {2001-01-01 0 0 5 {}} + + finish_test diff --git a/test/hook.test b/test/hook.test index 1c9145b..d137e90 100644 --- a/test/hook.test +++ b/test/hook.test @@ -142,9 +142,8 @@ do_test hook-4.1.1a { set ::update_hook {} db update_hook [list lappend ::update_hook] # - # EVIDENCE-OF: R-52223-27275 The update hook is not invoked when - # internal system tables are modified (i.e. sqlite_master and - # sqlite_sequence). + # EVIDENCE-OF: R-24531-54682 The update hook is not invoked when + # internal system tables are modified (i.e. sqlite_sequence). # execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); diff --git a/test/icu.test b/test/icu.test index 4c4e6d1..644cbb1 100644 --- a/test/icu.test +++ b/test/icu.test @@ -146,4 +146,22 @@ ifcapable icu { } } +# 2020-03-19 +# The ESCAPE clause on LIKE takes precedence over wildcards +# +do_execsql_test idu-6.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(id INTEGER PRIMARY KEY, x TEXT); + INSERT INTO t1 VALUES + (1,'abcde'), + (2,'abc_'), + (3,'abc__'), + (4,'abc%'), + (5,'abc%%'); + SELECT id FROM t1 WHERE x LIKE 'abc%%' ESCAPE '%'; +} {4} +do_execsql_test icu-6.1 { + SELECT id FROM t1 WHERE x LIKE 'abc__' ESCAPE '_'; +} {2} + finish_test diff --git a/test/ieee754.test b/test/ieee754.test index bf06764..bd806d2 100644 --- a/test/ieee754.test +++ b/test/ieee754.test @@ -23,8 +23,8 @@ foreach {id float rep} { 3 0.5 1,-1 4 1.5 3,-1 5 0.0 0,-1075 - 6 4.9406564584124654e-324 4503599627370497,-1075 - 7 2.2250738585072009e-308 9007199254740991,-1075 + 6 4.9406564584124654e-324 1,-1074 + 7 2.2250738585072009e-308 4503599627370495,-1074 8 2.2250738585072014e-308 1,-1022 } { do_test ieee754-100-$id-1 { diff --git a/test/in.test b/test/in.test index 4595d5f..a1fe1d2 100644 --- a/test/in.test +++ b/test/in.test @@ -765,19 +765,25 @@ do_execsql_test in-18.1 { # # Also ticket https://sqlite.org/src/info/29f635e0af71234b # -do_execsql_test in-19.1 { +do_execsql_test in-19.10 { DROP TABLE IF EXISTS t0; CREATE TABLE t0(c0 REAL UNIQUE); - INSERT INTO t0(c0) VALUES(2.07093491255203046E18); - SELECT 1 FROM t0 WHERE c0 IN ('2070934912552030444'); + INSERT INTO t0(c0) VALUES(2.0625E00); + SELECT 1 FROM t0 WHERE c0 IN ('2.0625'); } {1} -do_execsql_test in-19.2 { - SELECT c0 IN ('2070934912552030444') FROM t0; +do_execsql_test in-19.20 { + SELECT c0 IN ('2.0625') FROM t0; } {1} -do_execsql_test in-19.3 { - SELECT c0 IN ('2070934912552030444',2,3) FROM t0; +do_execsql_test in-19.21 { + SELECT c0 = ('2.0625') FROM t0; } {1} -do_execsql_test in-19.4 { +do_execsql_test in-19.22 { + SELECT c0 = ('0.20625e+01') FROM t0; +} {1} +do_execsql_test in-19.30 { + SELECT c0 IN ('2.0625',2,3) FROM t0; +} {1} +do_execsql_test in-19.40 { DROP TABLE t0; CREATE TABLE t0(c0 TEXT, c1 REAL, c2, PRIMARY KEY(c2, c0, c1)); CREATE INDEX i0 ON t0(c1 IN (c0)); diff --git a/test/in6.test b/test/in6.test index 773ee58..3719a6c 100644 --- a/test/in6.test +++ b/test/in6.test @@ -77,4 +77,24 @@ do_execsql_test in6-2.1 { ORDER BY +d; } {1 {} 2 {} 3 {} 4 {} 5 {} 8 {} 9 {}} +# 2020-03-16 ticket 82b588d342d515d1 +# Ensure that the IN-early-out optimization works with LEFT JOINs +# +reset_db +do_execsql_test in6-3.100 { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(0); + CREATE TABLE t2(b, c, d); + INSERT INTO t2(b,c,d) VALUES(4,5,3),(4,5,4),(4,5,8); + CREATE INDEX t2bcd ON t2(b, c, d); + SELECT * FROM t1 LEFT JOIN t2 ON b=NULL AND c=5 AND d IN (2,3,4); +} {0 {} {} {}} +do_execsql_test in6-3.110 { + CREATE TABLE v0(v1); + CREATE TABLE v3(v5, v4); + INSERT INTO v0 VALUES(0); + CREATE INDEX v9 ON v3(v4, v4, v5); + SELECT quote(v5) FROM v0 LEFT JOIN v3 ON v4 = NULL AND v5 IN(0); +} {NULL} + finish_test diff --git a/test/index7.test b/test/index7.test index f57792e..084e8c3 100644 --- a/test/index7.test +++ b/test/index7.test @@ -339,5 +339,17 @@ do_execsql_test index7-7.1 { SELECT * FROM t6 WHERE y IS TRUE ORDER BY x; } {1 1} +# 2020-05-27. tag-20200527-1. +# Incomplete stat1 information on a table with few rows should still use the +# index. +reset_db +do_execsql_test index7-8.1 { + CREATE TABLE t1(x INTEGER PRIMARY KEY, y); + CREATE INDEX t1y ON t1(y) WHERE y IS NOT NULL; + INSERT INTO t1(x) VALUES(1),(2); + ANALYZE; + EXPLAIN QUERY PLAN SELECT 1 FROM t1 WHERE y=5; +} {/SEARCH TABLE t1 USING COVERING INDEX t1y/} + finish_test diff --git a/test/indexedby.test b/test/indexedby.test index 8624b10..18f7bb8 100644 --- a/test/indexedby.test +++ b/test/indexedby.test @@ -95,7 +95,7 @@ do_test indexedby-2.4 { # an error. do_test indexedby-2.4.1 { catchsql { SELECT b FROM t1 INDEXED BY i1 WHERE b = 'two' } -} {1 {no query solution}} +} {0 {}} do_test indexedby-2.5 { catchsql { SELECT * FROM t1 INDEXED BY i5 WHERE a = 'one' AND b = 'two'} @@ -135,10 +135,10 @@ do_eqp_test indexedby-3.3 { } {SEARCH TABLE t1 USING INDEX i2 (b=?)} do_test indexedby-3.4 { catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' } -} {1 {no query solution}} +} {0 {}} do_test indexedby-3.5 { catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a } -} {1 {no query solution}} +} {0 {}} do_test indexedby-3.6 { catchsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' } } {0 {}} @@ -154,7 +154,7 @@ do_eqp_test indexedby-3.9 { } {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?)} do_test indexedby-3.10 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 } -} {1 {no query solution}} +} {0 {}} do_test indexedby-3.11 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 } } {1 {no such index: sqlite_autoindex_t3_2}} @@ -172,19 +172,19 @@ do_eqp_test indexedby-4.2 { SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c } { QUERY PLAN - |--SCAN TABLE t2 - `--SEARCH TABLE t1 USING INDEX i1 (a=?) + |--SCAN TABLE t1 USING INDEX i1 + `--SEARCH TABLE t2 USING INDEX i3 (c=?) } do_test indexedby-4.3 { catchsql { SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c } -} {1 {no query solution}} +} {0 {}} do_test indexedby-4.4 { catchsql { SELECT * FROM t2 INDEXED BY i3, t1 INDEXED BY i1 WHERE a=c } -} {1 {no query solution}} +} {0 {}} # Test embedding an INDEXED BY in a CREATE VIEW statement. This block # also tests that nothing bad happens if an index refered to by @@ -205,7 +205,7 @@ do_test indexedby-5.4 { # Recreate index i1 in such a way as it cannot be used by the view query. execsql { CREATE INDEX i1 ON t1(b) } catchsql { SELECT * FROM v2 } -} {1 {no query solution}} +} {0 {}} do_test indexedby-5.5 { # Drop and recreate index i1 again. This time, create it so that it can # be used by the query. @@ -245,7 +245,7 @@ do_eqp_test indexedby-7.5 { } {SEARCH TABLE t1 USING INDEX i2 (b=?)} do_test indexedby-7.6 { catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5} -} {1 {no query solution}} +} {0 {}} # Test that "INDEXED BY" can be used in an UPDATE statement. # @@ -266,7 +266,7 @@ do_eqp_test indexedby-8.5 { } {SEARCH TABLE t1 USING INDEX i2 (b=?)} do_test indexedby-8.6 { catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5} -} {1 {no query solution}} +} {0 {}} # Test that bug #3560 is fixed. # @@ -284,10 +284,10 @@ do_test indexedby-9.2 { joinme as j indexed by joinme_id_text_idx on ( m.id = j.id_int) } -} {1 {no query solution}} +} {0 {}} do_test indexedby-9.3 { catchsql { select * from maintable, joinme INDEXED by joinme_id_text_idx } -} {1 {no query solution}} +} {0 {}} # Make sure we can still create tables, indices, and columns whose name # is "indexed". diff --git a/test/istrue.test b/test/istrue.test index d2768b3..b2f5b8d 100644 --- a/test/istrue.test +++ b/test/istrue.test @@ -172,4 +172,33 @@ do_execsql_test istrue-710 { SELECT 0.0 IS FALSE COLLATE BINARY; } {1 1 1 1 1 1 1 1 1} +# 2020-06-12 bug report from Chromium +# https://bugs.chromium.org/p/chromium/issues/detail?id=1094247 +do_catchsql_test istrue-800 { + SELECT 9 IN (false.false); +} {1 {no such column: false.false}} +do_execsql_test istrue-810 { + CREATE TABLE t8(a INT, true INT, false INT, d INT); + INSERT INTO t8(a,true,false,d) VALUES(5,6,7,8),(4,3,2,1),('a','b','c','d'); + SELECT * FROM t8 ORDER BY false; +} {4 3 2 1 5 6 7 8 a b c d} +do_catchsql_test istrue-820 { + SELECT 9 IN (false.false) FROM t8; +} {1 {no such column: false.false}} +do_execsql_test istrue-830 { + CREATE TABLE false(true INT, false INT, x INT CHECK (5 IN (false.false))); +} {} +do_execsql_test istrue-840 { + INSERT INTO False VALUES(4,5,6); +} {} +do_catchsql_test istrue-841 { + INSERT INTO False VALUES(5,6,7); +} {1 {CHECK constraint failed: false}} +do_execsql_test istrue-850 { + SELECT 9 IN (false.false) FROM false; +} {0} +do_execsql_test istrue-851 { + SELECT 5 IN (false.false) FROM false; +} {1} + finish_test diff --git a/test/join2.test b/test/join2.test index bfcecda..82d597c 100644 --- a/test/join2.test +++ b/test/join2.test @@ -293,5 +293,36 @@ do_execsql_test 8.1 { WHERE (t1.c0 BETWEEN 0 AND 0) > ('' AND t0.c0); } +#------------------------------------------------------------------------- +# Ticket [45f4bf4eb]. +# +reset_db +do_execsql_test 9.0 { + CREATE TABLE t0(c0 INT); + CREATE VIEW v0(c0) AS SELECT CAST(t0.c0 AS INTEGER) FROM t0; + INSERT INTO t0(c0) VALUES (0); +} + +do_execsql_test 9.1 { + SELECT typeof(c0), c0 FROM v0 WHERE c0>='0' +} {integer 0} + +do_execsql_test 9.2 { + SELECT * FROM t0, v0 WHERE v0.c0 >= '0'; +} {0 0} + +do_execsql_test 9.3 { + SELECT * FROM t0 LEFT JOIN v0 WHERE v0.c0 >= '0'; +} {0 0} + +do_execsql_test 9.4 { + SELECT * FROM t0 LEFT JOIN v0 ON v0.c0 >= '0'; +} {0 0} + +do_execsql_test 9.5 { + SELECT * FROM t0 LEFT JOIN v0 ON v0.c0 >= '0' WHERE TRUE + UNION SELECT 0,0 WHERE 0; +} {0 0} + finish_test diff --git a/test/kvtest.c b/test/kvtest.c index 8c73caf..04dc010 100644 --- a/test/kvtest.c +++ b/test/kvtest.c @@ -907,7 +907,7 @@ static int runMain(int argc, char **argv){ if( eType==PATH_DB ){ /* Recover any prior crashes prior to starting the timer */ sqlite3_open(zDb, &db); - sqlite3_exec(db, "SELECT rowid FROM sqlite_master LIMIT 1", 0, 0, 0); + sqlite3_exec(db, "SELECT rowid FROM sqlite_schema LIMIT 1", 0, 0, 0); sqlite3_close(db); db = 0; } diff --git a/test/like.test b/test/like.test index 0fc8025..c29ebb2 100644 --- a/test/like.test +++ b/test/like.test @@ -1113,4 +1113,22 @@ do_execsql_test 16.2 { SELECT * FROM t1 WHERE a LIKE ' 1-'; } {{ 1-}} +# 2020-03-19 +# The ESCAPE clause on LIKE takes precedence over wildcards +# +do_execsql_test 17.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(id INTEGER PRIMARY KEY, x TEXT); + INSERT INTO t1 VALUES + (1,'abcde'), + (2,'abc_'), + (3,'abc__'), + (4,'abc%'), + (5,'abc%%'); + SELECT id FROM t1 WHERE x LIKE 'abc%%' ESCAPE '%'; +} {4} +do_execsql_test 17.1 { + SELECT id FROM t1 WHERE x LIKE 'abc__' ESCAPE '_'; +} {2} + finish_test diff --git a/test/like3.test b/test/like3.test index 0705315..3bfe30c 100644 --- a/test/like3.test +++ b/test/like3.test @@ -237,7 +237,7 @@ do_eqp_test like3-6.110 { `--SEARCH TABLE t1 USING PRIMARY KEY (path>? AND path? AND path? AND path? AND path? AND path? AND path0} } {1} @@ -86,7 +86,7 @@ do_test mutex1-1.8 { do_test mutex1-1.9 { mutex_counters counters - list $counters(total) $counters(static_master) + list $counters(total) $counters(static_main) } {0 0} #------------------------------------------------------------------------- @@ -103,13 +103,13 @@ ifcapable threadsafe1&&shared_cache { singlethread {} multithread { fast static_app1 static_app2 static_app3 - static_lru static_master static_mem static_open + static_lru static_main static_mem static_open static_prng static_pmem static_vfs1 static_vfs2 static_vfs3 } serialized { fast recursive static_app1 static_app2 - static_app3 static_lru static_master static_mem + static_app3 static_lru static_main static_mem static_open static_prng static_pmem static_vfs1 static_vfs2 static_vfs3 } diff --git a/test/nulls1.test b/test/nulls1.test index 9f4402f..b794f35 100644 --- a/test/nulls1.test +++ b/test/nulls1.test @@ -297,5 +297,47 @@ do_eqp_test 9.4 { } {SEARCH TABLE v0 USING COVERING INDEX v3 (ANY(c1) AND c2=?)} +# 2020-03-01 ticket e12a0ae526bb51c7 +# NULLS LAST on a LEFT JOIN +# +reset_db +do_execsql_test 10.10 { + CREATE TABLE t1(x); + INSERT INTO t1(x) VALUES('X'); + CREATE TABLE t2(c, d); + CREATE INDEX t2dc ON t2(d, c); + SELECT c FROM t1 LEFT JOIN t2 ON d=NULL ORDER BY d, c NULLS LAST; +} {{}} +do_execsql_test 10.20 { + INSERT INTO t2(c,d) VALUES(5,'X'),(6,'Y'),(7,'Z'),(3,'A'),(4,'B'); + SELECT c FROM t1 LEFT JOIN t2 ON d=x ORDER BY d, c NULLS LAST; +} {5} +do_execsql_test 10.30 { + UPDATE t2 SET d='X'; + UPDATE t2 SET c=NULL WHERE c=6; + SELECT c FROM t1 LEFT JOIN t2 ON d=x ORDER BY d NULLS FIRST, c NULLS FIRST; +} {{} 3 4 5 7} +do_execsql_test 10.40 { + SELECT c FROM t1 LEFT JOIN t2 ON d=x ORDER BY d NULLS LAST, c NULLS LAST; +} {3 4 5 7 {}} +do_execsql_test 10.41 { + SELECT c FROM t1 LEFT JOIN t2 ON d=x ORDER BY c NULLS LAST; +} {3 4 5 7 {}} +do_execsql_test 10.42 { + SELECT c FROM t1 LEFT JOIN t2 ON d=x ORDER BY +d NULLS LAST, +c NULLS LAST; +} {3 4 5 7 {}} +do_execsql_test 10.50 { + INSERT INTO t1(x) VALUES(NULL),('Y'); + SELECT x, c, d, '|' FROM t1 LEFT JOIN t2 ON d=x + ORDER BY d NULLS LAST, c NULLS LAST; +} {X 3 X | X 4 X | X 5 X | X 7 X | X {} X | {} {} {} | Y {} {} |} +do_execsql_test 10.51 { + SELECT x, c, d, '|' FROM t1 LEFT JOIN t2 ON d=x + ORDER BY +d NULLS LAST, +c NULLS LAST; +} {X 3 X | X 4 X | X 5 X | X 7 X | X {} X | {} {} {} | Y {} {} |} + + + + finish_test diff --git a/test/optfuzz-db01.c b/test/optfuzz-db01.c index 1cd3867..e11f15c 100644 --- a/test/optfuzz-db01.c +++ b/test/optfuzz-db01.c @@ -945,4 +945,3 @@ unsigned char data001[] = { 78, 32,116, 49, 32, 79, 78, 32, 40,116, 49, 46, 97, 61, 53, 48, 45, 99, 48, 46,120, 41, }; - diff --git a/test/orderby5.test b/test/orderby5.test index e83116b..ccdcf1d 100644 --- a/test/orderby5.test +++ b/test/orderby5.test @@ -126,5 +126,62 @@ do_execsql_test 3.1 { SELECT a FROM t3 WHERE b=2 AND c=3 ORDER BY d DESC, e DESC, b, c, a DESC; } {~/B-TREE/} +#------------------------------------------------------------------------- +do_execsql_test 4.1.0 { + CREATE TABLE t4(b COLLATE nocase); + INSERT INTO t4 VALUES('abc'); + INSERT INTO t4 VALUES('ABC'); + INSERT INTO t4 VALUES('aBC'); +} +do_execsql_test 4.1.1 { + SELECT * FROM t4 ORDER BY b COLLATE binary +} {ABC aBC abc} +do_execsql_test 4.1.2 { + SELECT * FROM t4 WHERE b='abc' ORDER BY b COLLATE binary +} {ABC aBC abc} + +do_execsql_test 4.2.1 { + CREATE TABLE Records(typeID INTEGER, key TEXT COLLATE nocase, value TEXT); + CREATE INDEX RecordsIndex ON Records(typeID, key, value); +} +do_execsql_test 4.2.2 { + explain query plan + SELECT typeID, key, value FROM Records + WHERE typeID = 2 AND key = 'x' + ORDER BY key, value; +} {~/TEMP B-TREE/} +do_execsql_test 4.2.3 { + explain query plan + SELECT typeID, key, value FROM Records + WHERE typeID = 2 AND (key = 'x' COLLATE binary) + ORDER BY key, value; +} {~/TEMP B-TREE/} +do_execsql_test 4.2.4 { + explain query plan + SELECT typeID, key, value FROM Records + WHERE typeID = 2 + ORDER BY key, value; +} {~/TEMP B-TREE/} + +db collate hello [list string match] +do_execsql_test 4.3.1 { + CREATE TABLE t5(a INTEGER PRIMARY KEY, b COLLATE hello, c, d); +} +db close +sqlite3 db test.db +do_catchsql_test 4.3.2 { + SELECT a FROM t5 WHERE b='def' ORDER BY b; +} {1 {no such collation sequence: hello}} + +# 2020-02-13 ticket 41c1456a6e61c0e7 +do_execsql_test 4.4.0 { + DROP TABLE t1; + CREATE TABLE t1(a); + DROP TABLE t2; + CREATE TABLE t2(b INTEGER PRIMARY KEY, c INT); + SELECT DISTINCT * + FROM t1 LEFT JOIN t2 ON b=c AND b=(SELECT a FROM t1) + WHERE c>10; +} {} finish_test diff --git a/test/pager1.test b/test/pager1.test index 8216b46..20fd8bd 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -1930,6 +1930,7 @@ do_test pager1-18.4 { catchsql { SELECT length(x||'') FROM t2 } db2 } {1 {database disk image is malformed}} db2 close +extra_schema_checks 0 do_test pager1-18.5 { sqlite3 db "" sqlite3_db_config db DEFENSIVE 0 @@ -1944,6 +1945,7 @@ do_test pager1-18.5 { catchsql { SELECT * FROM x1 } } {1 {database disk image is malformed}} db close +extra_schema_checks 1 do_test pager1-18.6 { faultsim_delete_and_reopen diff --git a/test/permutations.test b/test/permutations.test index 5163c2a..4ea6cd2 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -136,9 +136,24 @@ if {[info exists ::env(QUICKTEST_INCLUDE)]} { set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)] } if {[info exists ::env(QUICKTEST_OMIT)]} { - foreach x [split $::env(QUICKTEST_OMIT) ,] { - regsub -all \\y$x\\y $allquicktests {} allquicktests + # If environment variable QUICKTEST_OMIT is set, it is a comma-separated + # list of regular expressions to match against test file names in + # the "allquicktests" set. Any matches are excluded. Only the filename + # is matched, not any directory component of the path. + set all [list] + foreach a $allquicktests { + set bIn 1 + foreach x [split $::env(QUICKTEST_OMIT) ,] { + if {[regexp $x [file tail $a]]} { + set bIn 0 + break + } + } + if {$bIn} { + lappend all $a + } } + set allquicktests $all } # If the TEST_FAILURE environment variable is set, it means that we what to @@ -171,6 +186,12 @@ test_suite "veryquick" -prefix "" -description { *fts5corrupt* *fts5big* *fts5aj* ] +test_suite "shell" -prefix "" -description { + Run tests of the command-line shell +} -files [ + test_set [glob $testdir/shell*.test] +] + test_suite "extraquick" -prefix "" -description { "Extra" quick test suite. Runs in a few minutes on a workstation. This test suite is the same as the "veryquick" tests, except that @@ -968,6 +989,7 @@ test_suite "journaltest" -description { pager1.test syscall.test tkt3457.test *malloc* mmap* multiplex* nolock* pager2.test *fault* rowal* snapshot* superlock* symlink.test delete_db.test shmlock.test chunksize.test + busy2.test }] if {[info commands register_demovfs] != ""} { @@ -1075,6 +1097,16 @@ test_suite "sorterref" -prefix "" -description { autoinstall_test_functions } +test_suite "maindbname" -prefix "" -description { + Run the "veryquick" test suite with SQLITE_DBCONFIG_MAINDBNAME used to + set the name of database 0 to "icecube". +} -files [ + test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \ + *fts5corrupt* *fts5big* *fts5aj* +] -dbconfig { + dbconfig_maindbname_icecube $::dbhandle +} + # End of tests ############################################################################# diff --git a/test/pg_common.tcl b/test/pg_common.tcl index b3f35cd..dd16659 100644 --- a/test/pg_common.tcl +++ b/test/pg_common.tcl @@ -18,6 +18,8 @@ sqlite3 sqlite "" proc execsql {sql} { + set sql [string map {{WITHOUT ROWID} {}} $sql] + set lSql [list] set frag "" while {[string length $sql]>0} { diff --git a/test/pragma.test b/test/pragma.test index 1881a5c..04f5bd0 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -387,11 +387,15 @@ ifcapable attach { PRAGMA integrity_check=4 } } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2}} - do_test pragma-3.6 { - execsql { - PRAGMA integrity_check=xyz - } - } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}} + do_catchsql_test pragma-3.6 { + PRAGMA integrity_check=xyz + } {1 {no such table: xyz}} + do_catchsql_test pragma-3.6b { + PRAGMA integrity_check=t2 + } {0 {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}} + do_catchsql_test pragma-3.6c { + PRAGMA integrity_check=sqlite_schema + } {0 ok} do_test pragma-3.7 { execsql { PRAGMA integrity_check=0 @@ -423,7 +427,7 @@ ifcapable attach { do_test pragma-3.8.2 { execsql {PRAGMA QUICK_CHECK} } {ok} - do_test pragma-3.9 { + do_test pragma-3.9a { execsql { ATTACH 'testerr.db' AS t2; PRAGMA integrity_check @@ -432,6 +436,12 @@ ifcapable attach { Page 4 is never used Page 5 is never used Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}} + do_execsql_test pragma-3.9b { + PRAGMA t2.integrity_check=t2; + } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}} + do_execsql_test pragma-3.9c { + PRAGMA t2.integrity_check=sqlite_schema; + } {ok} do_test pragma-3.10 { execsql { PRAGMA integrity_check=1 diff --git a/test/pragma4.test b/test/pragma4.test index 2eef060..b82df81 100644 --- a/test/pragma4.test +++ b/test/pragma4.test @@ -120,8 +120,15 @@ do_test 4.1.4 { sqlite3 db2 test.db2 execsql { DROP TABLE t1 } db3 execsql { DROP TABLE t2 } db2 -} {} -do_execsql_test 4.1.5 { PRAGMA table_info(t1) } +} {} +if {[permutation]=="prepare"} { + do_catchsql_test 4.1.5a { + PRAGMA table_info(t1) + } {1 {database schema has changed}} +} +do_execsql_test 4.1.5 { + PRAGMA table_info(t1) +} do_execsql_test 4.1.6 { PRAGMA table_info(t2) } db2 close diff --git a/test/printf.test b/test/printf.test index d099da8..445470f 100644 --- a/test/printf.test +++ b/test/printf.test @@ -538,9 +538,11 @@ do_test printf-2.1.2.8 { do_test printf-2.1.2.9 { sqlite3_mprintf_double {abc: %d %d (%1.1g) :xyz} 1 1 1.0e-20 } {abc: 1 1 (1e-20) :xyz} -do_test printf-2.1.2.10 { - sqlite3_mprintf_double {abc: %*.*f} 2000000000 1000000000 1.0e-20 -} {} +if {$SQLITE_MAX_LENGTH<=[expr 1000*1000*1000]} { + do_test printf-2.1.2.10 { + sqlite3_mprintf_double {abc: %*.*f} 2000000000 1000000000 1.0e-20 + } {} +} do_test printf-2.1.3.1 { sqlite3_mprintf_double {abc: (%*.*f) :xyz} 1 1 1.0 } {abc: (1.0) :xyz} @@ -3777,4 +3779,11 @@ foreach ::iRepeat {0 1} { } } +# 2020-05-23 +# ticket 23439ea582241138 +# +do_execsql_test printf-16.1 { + SELECT printf('%.*g',2147483647,0.01); +} {0.01} + finish_test diff --git a/test/select3.test b/test/select3.test index e15464f..fec8ba4 100644 --- a/test/select3.test +++ b/test/select3.test @@ -306,4 +306,21 @@ foreach {id x} { } {{} 1.0 ok} } +# 2020-03-10 ticket e0c2ad1aa8a9c691 +reset_db +do_execsql_test select3-9.100 { + CREATE TABLE t0(c0 REAL, c1 REAL GENERATED ALWAYS AS (c0)); + INSERT INTO t0(c0) VALUES (1); + SELECT * FROM t0 GROUP BY c0; +} {1.0 1.0} + +reset_db +do_execsql_test select3.10.100 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + SELECT max(t1.a), + (SELECT 'xyz' FROM (SELECT * FROM t2 WHERE 0) WHERE t1.b=1) + FROM t1; +} {{} {}} + finish_test diff --git a/test/select4.test b/test/select4.test index 51a1b1c..6dbfd4d 100644 --- a/test/select4.test +++ b/test/select4.test @@ -1005,6 +1005,25 @@ do_catchsql_test select4-17.3 { ORDER BY +x; } {1 {LIMIT clause should come after UNION not before}} +# 2020-04-03 ticket 51166be0159fd2ce from Yong Heng. +# Adverse interaction between the constant propagation and push-down +# optimizations. +# +reset_db +do_execsql_test select4-18.1 { + CREATE VIEW v0(v0) AS WITH v0 AS(SELECT 0 v0) SELECT(SELECT min(v0) OVER()) FROM v0 GROUP BY v0; + SELECT *FROM v0 v1 JOIN v0 USING(v0) WHERE datetime(v0) = (v0.v0)AND v0 = 10; +} {} +do_execsql_test select4-18.2 { + CREATE VIEW t1(aa) AS + WITH t2(bb) AS (SELECT 123) + SELECT (SELECT min(bb) OVER()) FROM t2 GROUP BY bb; + SELECT * FROM t1; +} {123} +do_execsql_test select4-18.3 { + SELECT * FROM t1 AS z1 JOIN t1 AS z2 USING(aa) + WHERE abs(z1.aa)=z2.aa AND z1.aa=123; +} {123} finish_test diff --git a/test/selectA.test b/test/selectA.test index 838e5f4..7ca0096 100644 --- a/test/selectA.test +++ b/test/selectA.test @@ -1446,5 +1446,26 @@ do_execsql_test 6.1 { SELECT * FROM (SELECT a FROM t1 UNION SELECT b FROM t2) WHERE a=a; } {12345} +# 2020-06-15 ticket 8f157e8010b22af0 +# +reset_db +do_execsql_test 7.1 { + CREATE TABLE t1(c1); INSERT INTO t1 VALUES(12),(123),(1234),(NULL),('abc'); + CREATE TABLE t2(c2); INSERT INTO t2 VALUES(44),(55),(123); + CREATE TABLE t3(c3,c4); INSERT INTO t3 VALUES(66,1),(123,2),(77,3); + CREATE VIEW t4 AS SELECT c3 FROM t3; + CREATE VIEW t5 AS SELECT c3 FROM t3 ORDER BY c4; +} +do_execsql_test 7.2 { + SELECT * FROM t1, t2 WHERE c1=(SELECT 123 INTERSECT SELECT c2 FROM t4) AND c1=123; +} {123 123} +do_execsql_test 7.3 { + SELECT * FROM t1, t2 WHERE c1=(SELECT 123 INTERSECT SELECT c2 FROM t5) AND c1=123; +} {123 123} +do_execsql_test 7.4 { + CREATE TABLE a(b); + CREATE VIEW c(d) AS SELECT b FROM a ORDER BY b; + SELECT sum(d) OVER( PARTITION BY(SELECT 0 FROM c JOIN a WHERE b =(SELECT b INTERSECT SELECT d FROM c) AND b = 123)) FROM c; +} {} finish_test diff --git a/test/sessionfuzz.c b/test/sessionfuzz.c index 1d0bee4..d8cc1be 100644 --- a/test/sessionfuzz.c +++ b/test/sessionfuzz.c @@ -698,7 +698,9 @@ static const char zHelp[] = #include #include #include +#ifndef OMIT_ZLIB #include "zlib.h" +#endif /* ** Implementation of the "sqlar_uncompress(X,SZ)" SQL function @@ -715,6 +717,9 @@ static void sqlarUncompressFunc( int argc, sqlite3_value **argv ){ +#ifdef OMIT_ZLIB + sqlite3_result_value(context, argv[0]); +#else uLong nData; uLongf sz; @@ -733,6 +738,7 @@ static void sqlarUncompressFunc( } sqlite3_free(pOut); } +#endif } diff --git a/test/shell1.test b/test/shell1.test index 734a21a..678feba 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -199,10 +199,10 @@ do_test shell1-2.2.4 { } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" -} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { @@ -230,7 +230,7 @@ do_test shell1-2.3.7 { # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -297,10 +297,11 @@ do_test shell1-3.4.2 { list [regexp {BEGIN TRANSACTION;} $res] \ [regexp {COMMIT;} $res] } {1 1} -do_test shell1-3.4.3 { - # too many arguments - catchcmd "test.db" ".dump FOO BAD" -} {1 {Usage: .dump ?--preserve-rowids? ?--newlines? ?LIKE-PATTERN?}} +# The .dump command now accepts multiple arguments +#do_test shell1-3.4.3 { +# # too many arguments +# catchcmd "test.db" ".dump FOO BAD" +#} {1 {Usage: .dump ?--preserve-rowids? ?--newlines? ?LIKE-PATTERN?}} # .echo ON|OFF Turn command echo on or off do_test shell1-3.5.1 { @@ -388,17 +389,14 @@ do_test shell1-3.10.2 { # .import FILE TABLE Import data from FILE into TABLE do_test shell1-3.11.1 { catchcmd "test.db" ".import" -} {1 {Usage: .import FILE TABLE}} +} {/1 .ERROR: missing FILE argument.*/} do_test shell1-3.11.2 { catchcmd "test.db" ".import FOO" -} {1 {Usage: .import FILE TABLE}} -#do_test shell1-3.11.2 { -# catchcmd "test.db" ".import FOO BAR" -#} {1 {Error: no such table: BAR}} +} {/1 .ERROR: missing TABLE argument.*/} do_test shell1-3.11.3 { # too many arguments catchcmd "test.db" ".import FOO BAR BAD" -} {1 {Usage: .import FILE TABLE}} +} {/1 .ERROR: extra argument: "BAD".*./} # .indexes ?TABLE? Show names of all indexes # If TABLE specified, only show indexes for tables @@ -432,7 +430,7 @@ do_test shell1-3.13.1 { } {0 {current output mode: list}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -465,10 +463,10 @@ do_test shell1-3.13.11 { # don't allow partial mode type matches do_test shell1-3.13.12 { catchcmd "test.db" ".mode l" -} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-3.13.13 { catchcmd "test.db" ".mode li" -} {1 {Error: mode should be one of: ascii column csv html insert line list quote tabs tcl}} +} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown quote table tabs tcl}} do_test shell1-3.13.14 { catchcmd "test.db" ".mode lin" } {0 {}} @@ -495,7 +493,14 @@ do_test shell1-3.15.2 { do_test shell1-3.15.3 { # too many arguments catchcmd "test.db" ".output FOO BAD" -} {1 {Usage: .output [-e|-x|FILE]}} +} {1 {ERROR: extra parameter: "BAD". Usage: +.output ?FILE? Send output to FILE or stdout if FILE is omitted + If FILE begins with '|' then open it as a pipe. + Options: + --bom Prefix output with a UTF8 byte-order mark + -e Send output to the system text editor + -x Send output as CSV to a spreadsheet +child process exited abnormally}} # .output stdout Send output to the screen do_test shell1-3.16.1 { @@ -504,7 +509,14 @@ do_test shell1-3.16.1 { do_test shell1-3.16.2 { # too many arguments catchcmd "test.db" ".output stdout BAD" -} {1 {Usage: .output [-e|-x|FILE]}} +} {1 {ERROR: extra parameter: "BAD". Usage: +.output ?FILE? Send output to FILE or stdout if FILE is omitted + If FILE begins with '|' then open it as a pipe. + Options: + --bom Prefix output with a UTF8 byte-order mark + -e Send output to the system text editor + -x Send output as CSV to a spreadsheet +child process exited abnormally}} # .prompt MAIN CONTINUE Replace the standard prompts do_test shell1-3.17.1 { @@ -697,11 +709,11 @@ do_test shell1-3.26.4 { # this should be treated the same as a '1' width for col 1 and 2 } {0 {}} do_test shell1-3.26.5 { - catchcmd "test.db" ".mode column\n.width 10 -10\nSELECT 'abcdefg', 123456;" + catchcmd "test.db" ".mode column\n.header off\n.width 10 -10\nSELECT 'abcdefg', 123456;" # this should be treated the same as a '1' width for col 1 and 2 } {0 {abcdefg 123456}} do_test shell1-3.26.6 { - catchcmd "test.db" ".mode column\n.width -10 10\nSELECT 'abcdefg', 123456;" + catchcmd "test.db" ".mode column\n.header off\n.width -10 10\nSELECT 'abcdefg', 123456;" # this should be treated the same as a '1' width for col 1 and 2 } {0 { abcdefg 123456 }} @@ -1154,4 +1166,57 @@ do_test shell1-7.1.7 { } +# Test case for the ieee754 and decimal extensions in the shell. +# See the "floatingpoint.html" file in the documentation for more +# information. +# +do_test shell1-8.1 { + catchcmd ":memory:" { + -- The pow2 table will hold all the necessary powers of two. + CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT); + WITH RECURSIVE c(x,v) AS ( + VALUES(0,'1') + UNION ALL + SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971 + ) INSERT INTO pow2(x,v) SELECT x, v FROM c; + WITH RECURSIVE c(x,v) AS ( + VALUES(-1,'0.5') + UNION ALL + SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075 + ) INSERT INTO pow2(x,v) SELECT x, v FROM c; + + -- This query finds the decimal representation of each value in the "c" table. + WITH c(n) AS (VALUES(47.49)) + ----XXXXX----------- Replace with whatever you want + SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v) + FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n); + } +} {0 47.49000000000000198951966012828052043914794921875} +do_test shell1-8.2 { + catchcmd :memory: { +.mode box +SELECT ieee754(47.49) AS x; + } +} {0 {┌───────────────────────────────┐ +│ x │ +├───────────────────────────────┤ +│ ieee754(6683623321994527,-47) │ +└───────────────────────────────┘}} +do_test shell1-8.3 { + catchcmd ":memory: --box" { + select ieee754(6683623321994527,-47) as x; + } +} {0 {┌───────┐ +│ x │ +├───────┤ +│ 47.49 │ +└───────┘}} +do_test shell1-8.4 { + catchcmd ":memory: --table" {SELECT ieee754_mantissa(47.49) AS M, ieee754_exponent(47.49) AS E;} +} {0 {+------------------+-----+ +| M | E | ++------------------+-----+ +| 6683623321994527 | -47 | ++------------------+-----+}} + finish_test diff --git a/test/shell5.test b/test/shell5.test index 72f20ca..8ec9a63 100644 --- a/test/shell5.test +++ b/test/shell5.test @@ -32,17 +32,14 @@ forcedelete test.db test.db-journal test.db-wal # .import FILE TABLE Import data from FILE into TABLE do_test shell5-1.1.1 { catchcmd "test.db" ".import" -} {1 {Usage: .import FILE TABLE}} +} {/1 .ERROR: missing FILE argument.*/} do_test shell5-1.1.2 { catchcmd "test.db" ".import FOO" -} {1 {Usage: .import FILE TABLE}} -#do_test shell5-1.1.2 { -# catchcmd "test.db" ".import FOO BAR" -#} {1 {Error: no such table: BAR}} +} {/1 .ERROR: missing TABLE argument.*/} do_test shell5-1.1.3 { # too many arguments catchcmd "test.db" ".import FOO BAR BAD" -} {1 {Usage: .import FILE TABLE}} +} {/1 .ERROR: extra argument.*/} # .separator STRING Change separator used by output mode and .import do_test shell5-1.2.1 { diff --git a/test/speedtest1.c b/test/speedtest1.c index ccb8a44..e3b9d22 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -7,7 +7,8 @@ static const char zHelp[] = "Usage: %s [--options] DATABASE\n" "Options:\n" " --autovacuum Enable AUTOVACUUM mode\n" - " --cachesize N Set the cache size to N\n" + " --cachesize N Set the cache size to N\n" + " --checkpoint Run PRAGMA wal_checkpoint after each test case\n" " --exclusive Enable locking_mode=EXCLUSIVE\n" " --explain Like --sqlonly but with added EXPLAIN keywords\n" " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n" @@ -15,11 +16,13 @@ static const char zHelp[] = " --journal M Set the journal_mode to M\n" " --key KEY Set the encryption key to KEY\n" " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n" + " --memdb Use an in-memory database\n" " --mmap SZ MMAP the first SZ bytes of the database file\n" " --multithread Set multithreaded mode\n" " --nomemstat Disable memory statistics\n" " --nosync Set PRAGMA synchronous=OFF\n" " --notnull Add NOT NULL constraints to table columns\n" + " --output FILE Store SQL output in FILE\n" " --pagesize N Set the page size to N\n" " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n" " --primarykey Use PRIMARY KEY instead of UNIQUE where appropriate\n" @@ -41,7 +44,6 @@ static const char zHelp[] = " --without-rowid Use WITHOUT ROWID where appropriate\n" ; - #include "sqlite3.h" #include #include @@ -61,6 +63,20 @@ static const char zHelp[] = # define sqlite3_int64 sqlite_int64 #endif +typedef sqlite3_uint64 u64; + +/* +** State structure for a Hash hash in progress +*/ +typedef struct HashContext HashContext; +struct HashContext { + unsigned char isInit; /* True if initialized */ + unsigned char i, j; /* State variables */ + unsigned char s[256]; /* State variables */ + unsigned char r[32]; /* Result */ +}; + + /* All global state is held in this structure */ static struct Global { sqlite3 *db; /* The open database connection */ @@ -76,12 +92,18 @@ static struct Global { int eTemp; /* 0: no TEMP. 9: always TEMP. */ int szTest; /* Scale factor for test iterations */ int nRepeat; /* Repeat selects this many times */ + int doCheckpoint; /* Run PRAGMA wal_checkpoint after each trans */ const char *zWR; /* Might be WITHOUT ROWID */ const char *zNN; /* Might be NOT NULL */ const char *zPK; /* Might be UNIQUE or PRIMARY KEY */ unsigned int x, y; /* Pseudo-random number generator state */ + u64 nResByte; /* Total number of result bytes */ int nResult; /* Size of the current result */ char zResult[3000]; /* Text of the current result */ +#ifndef SPEEDTEST_OMIT_HASH + FILE *hashFile; /* Store all hash results in this file */ + HashContext hash; /* Hash of all output */ +#endif } g; /* Return " TEMP" or "", as appropriate for creating a table. @@ -90,7 +112,6 @@ static const char *isTemp(int N){ return g.eTemp>=N ? " TEMP" : ""; } - /* Print an error message and exit */ static void fatal_error(const char *zMsg, ...){ va_list ap; @@ -100,6 +121,72 @@ static void fatal_error(const char *zMsg, ...){ exit(1); } +#ifndef SPEEDTEST_OMIT_HASH +/**************************************************************************** +** Hash algorithm used to verify that compilation is not miscompiled +** in such a was as to generate an incorrect result. +*/ + +/* +** Initialize a new hash. iSize determines the size of the hash +** in bits and should be one of 224, 256, 384, or 512. Or iSize +** can be zero to use the default hash size of 256 bits. +*/ +static void HashInit(void){ + unsigned int k; + g.hash.i = 0; + g.hash.j = 0; + for(k=0; k<256; k++) g.hash.s[k] = k; +} + +/* +** Make consecutive calls to the HashUpdate function to add new content +** to the hash +*/ +static void HashUpdate( + const unsigned char *aData, + unsigned int nData +){ + unsigned char t; + unsigned char i = g.hash.i; + unsigned char j = g.hash.j; + unsigned int k; + if( g.hashFile ) fwrite(aData, 1, nData, g.hashFile); + for(k=0; k>4]; + zChar[1] = "0123456789abcdef"[aBlob[iBlob]&15]; + HashUpdate(zChar,2); + } + g.nResByte += nBlob*2 + 2; + }else{ + HashUpdate((unsigned char*)z, len); + g.nResByte += len + 2; + } + } +#endif if( g.nResult+len0 ) g.zResult[g.nResult++] = ' '; memcpy(g.zResult + g.nResult, z, len+1); @@ -1987,7 +2156,8 @@ int main(int argc, char **argv){ int showStats = 0; /* True for --stats */ int nThread = 0; /* --threads value */ int mmapSize = 0; /* How big of a memory map to use */ - const char *zTSet = "main"; /* Which --testset torun */ + int memDb = 0; /* --memdb. Use an in-memory database */ + char *zTSet = "main"; /* Which --testset torun */ int doTrace = 0; /* True for --trace */ const char *zEncoding = 0; /* --utf16be or --utf16le */ const char *zDbName = 0; /* Name of the test database */ @@ -2000,7 +2170,7 @@ int main(int argc, char **argv){ int rc; /* API return code */ /* Display the version of SQLite being tested */ - printf("-- Speedtest1 for SQLite %s %.50s\n", + printf("-- Speedtest1 for SQLite %s %.48s\n", sqlite3_libversion(), sqlite3_sourceid()); /* Process command-line arguments */ @@ -2021,6 +2191,8 @@ int main(int argc, char **argv){ cacheSize = integerValue(argv[i]); }else if( strcmp(z,"exclusive")==0 ){ doExclusive = 1; + }else if( strcmp(z,"checkpoint")==0 ){ + g.doCheckpoint = 1; }else if( strcmp(z,"explain")==0 ){ g.bSqlOnly = 1; g.bExplain = 1; @@ -2042,6 +2214,8 @@ int main(int argc, char **argv){ nLook = integerValue(argv[i+1]); szLook = integerValue(argv[i+2]); i += 2; + }else if( strcmp(z,"memdb")==0 ){ + memDb = 1; #if SQLITE_VERSION_NUMBER>=3006000 }else if( strcmp(z,"multithread")==0 ){ sqlite3_config(SQLITE_CONFIG_MULTITHREAD); @@ -2057,6 +2231,22 @@ int main(int argc, char **argv){ noSync = 1; }else if( strcmp(z,"notnull")==0 ){ g.zNN = "NOT NULL"; + }else if( strcmp(z,"output")==0 ){ +#ifdef SPEEDTEST_OMIT_HASH + fatal_error("The --output option is not supported with" + " -DSPEEDTEST_OMIT_HASH\n"); +#else + if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); + i++; + if( strcmp(argv[i],"-")==0 ){ + g.hashFile = stdout; + }else{ + g.hashFile = fopen(argv[i], "wb"); + if( g.hashFile==0 ){ + fatal_error("cannot open \"%s\" for writing\n", argv[i]); + } + } +#endif }else if( strcmp(z,"pagesize")==0 ){ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); pageSize = integerValue(argv[++i]); @@ -2110,6 +2300,9 @@ int main(int argc, char **argv){ zEncoding = "utf16be"; }else if( strcmp(z,"verify")==0 ){ g.bVerify = 1; +#ifndef SPEEDTEST_OMIT_HASH + HashInit(); +#endif }else if( strcmp(z,"without-rowid")==0 ){ g.zWR = "WITHOUT ROWID"; g.zPK = "PRIMARY KEY"; @@ -2148,15 +2341,16 @@ int main(int argc, char **argv){ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); } #endif - + sqlite3_initialize(); + /* Open the database and the input file */ - if( sqlite3_open(zDbName, &g.db) ){ + if( sqlite3_open(memDb ? ":memory:" : zDbName, &g.db) ){ fatal_error("Cannot open database file: %s\n", zDbName); } #if SQLITE_VERSION_NUMBER>=3006001 if( nLook>0 && szLook>0 ){ pLook = malloc( nLook*szLook ); - rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_LOOKASIDE, pLook, szLook,nLook); + rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_LOOKASIDE,pLook,szLook,nLook); if( rc ) fatal_error("lookaside configuration failed: %d\n", rc); } #endif @@ -2166,6 +2360,9 @@ int main(int argc, char **argv){ #ifndef SQLITE_OMIT_DEPRECATED if( doTrace ) sqlite3_trace(g.db, traceCallback, 0); #endif + if( memDb>0 ){ + speedtest1_exec("PRAGMA temp_store=memory"); + } if( mmapSize>0 ){ speedtest1_exec("PRAGMA mmap_size=%d", mmapSize); } @@ -2196,30 +2393,68 @@ int main(int argc, char **argv){ } if( g.bExplain ) printf(".explain\n.echo on\n"); - if( strcmp(zTSet,"main")==0 ){ - testset_main(); - }else if( strcmp(zTSet,"debug1")==0 ){ - testset_debug1(); - }else if( strcmp(zTSet,"orm")==0 ){ - testset_orm(); - }else if( strcmp(zTSet,"cte")==0 ){ - testset_cte(); - }else if( strcmp(zTSet,"fp")==0 ){ - testset_fp(); - }else if( strcmp(zTSet,"trigger")==0 ){ - testset_trigger(); - }else if( strcmp(zTSet,"rtree")==0 ){ + do{ + char *zThisTest = zTSet; + char *zComma = strchr(zThisTest,','); + if( zComma ){ + *zComma = 0; + zTSet = zComma+1; + }else{ + zTSet = ""; + } + if( g.iTotal>0 || zComma!=0 ){ + printf(" Begin testset \"%s\"\n", zThisTest); + } + if( strcmp(zThisTest,"main")==0 ){ + testset_main(); + }else if( strcmp(zThisTest,"debug1")==0 ){ + testset_debug1(); + }else if( strcmp(zThisTest,"orm")==0 ){ + testset_orm(); + }else if( strcmp(zThisTest,"cte")==0 ){ + testset_cte(); + }else if( strcmp(zThisTest,"fp")==0 ){ + testset_fp(); + }else if( strcmp(zThisTest,"trigger")==0 ){ + testset_trigger(); + }else if( strcmp(zThisTest,"rtree")==0 ){ #ifdef SQLITE_ENABLE_RTREE - testset_rtree(6, 147); + testset_rtree(6, 147); #else - fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable " - "the R-Tree tests\n"); + fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable " + "the R-Tree tests\n"); #endif - }else{ - fatal_error("unknown testset: \"%s\"\n" - "Choices: cte debug1 fp main orm rtree trigger\n", - zTSet); - } + }else{ + fatal_error("unknown testset: \"%s\"\n" + "Choices: cte debug1 fp main orm rtree trigger\n", + zThisTest); + } + if( zTSet[0] ){ + char *zSql, *zObj; + speedtest1_begin_test(999, "Reset the database"); + while( 1 ){ + zObj = speedtest1_once( + "SELECT name FROM main.sqlite_master" + " WHERE sql LIKE 'CREATE %%TABLE%%'"); + if( zObj==0 ) break; + zSql = sqlite3_mprintf("DROP TABLE main.\"%w\"", zObj); + speedtest1_exec(zSql); + sqlite3_free(zSql); + sqlite3_free(zObj); + } + while( 1 ){ + zObj = speedtest1_once( + "SELECT name FROM temp.sqlite_master" + " WHERE sql LIKE 'CREATE %%TABLE%%'"); + if( zObj==0 ) break; + zSql = sqlite3_mprintf("DROP TABLE main.\"%w\"", zObj); + speedtest1_exec(zSql); + sqlite3_free(zSql); + sqlite3_free(zObj); + } + speedtest1_end_test(); + } + }while( zTSet[0] ); speedtest1_final(); if( showStats ){ diff --git a/test/sqlcipher-integrity.test b/test/sqlcipher-integrity.test index b1ad3d7..baf5d9b 100644 --- a/test/sqlcipher-integrity.test +++ b/test/sqlcipher-integrity.test @@ -63,7 +63,7 @@ do_test hmac-tamper-resistence-first-page { # write some junk into the hmac segment, leaving # the page data valid but with an invalid signature - hexio_write test.db 1000 0000 + hexio_write test.db 1000 000000 sqlite_orig db test.db @@ -102,7 +102,7 @@ do_test nohmac-not-tamper-resistent { db close # write some junk into the middle of the page - hexio_write test.db 2560 00 + hexio_write test.db 2560 000000 sqlite_orig db test.db @@ -142,7 +142,7 @@ do_test hmac-tamper-resistence { # write some junk into the hmac segment, leaving # the page data valid but with an invalid signature - hexio_write test.db 16500 0000 + hexio_write test.db 16500 000000 sqlite_orig db test.db @@ -198,8 +198,8 @@ file delete -force test.db # try cipher_integrity_check on a corrupted version 2 database do_test version-2-integrity-check-invalid { file copy -force $sampleDir/sqlcipher-2.0-le-testkey.db test.db - hexio_write test.db 8202 00 - hexio_write test.db 10250 00 + hexio_write test.db 8202 000000 + hexio_write test.db 10250 000000 sqlite_orig db test.db execsql { PRAGMA key = 'testkey'; @@ -226,8 +226,8 @@ file delete -force test.db # try cipher_integrity_check on a corrupted version 3 database do_test version-3-integrity-check-invalid { file copy -force $sampleDir/sqlcipher-3.0-testkey.db test.db - hexio_write test.db 8202 00 - hexio_write test.db 10250 00 + hexio_write test.db 8202 000000 + hexio_write test.db 10250 000000 sqlite_orig db test.db execsql { PRAGMA key = 'testkey'; @@ -254,11 +254,11 @@ file delete -force test.db do_test version-4-integrity-check-invalid { file copy -force $sampleDir/sqlcipher-4.0-testkey.db test.db # corrupt page data - hexio_write test.db 5120 00 + hexio_write test.db 5120 000000 # corrupt iv - hexio_write test.db 12208 00 + hexio_write test.db 12208 000000 # corrupt the mac segment - hexio_write test.db 16320 00 + hexio_write test.db 16320 000000 sqlite_orig db test.db execsql { PRAGMA key = 'testkey'; @@ -271,7 +271,7 @@ file delete -force test.db # try cipher_integrity_check on a corrupted version 4 database do_test version-4-integrity-check-invalid-last-page { file copy -force $sampleDir/sqlcipher-4.0-testkey.db test.db - hexio_write test.db 978944 0000 + hexio_write test.db 978944 0000 sqlite_orig db test.db execsql { PRAGMA key = 'testkey'; @@ -299,8 +299,8 @@ do_test integrity-check-plaintext-header { lappend rc [string equal [hexio_read test.db 16 5] "1000010150"] - hexio_write test.db 120 00 - hexio_write test.db 5120 00 + hexio_write test.db 120 000000 + hexio_write test.db 5120 000000 lappend rc [execsql { PRAGMA cipher_integrity_check; diff --git a/test/sqlcipher-plaintext-header.test b/test/sqlcipher-plaintext-header.test index 54279b5..4853ea6 100644 --- a/test/sqlcipher-plaintext-header.test +++ b/test/sqlcipher-plaintext-header.test @@ -40,6 +40,14 @@ source $testdir/sqlcipher.tcl set hexkeyspec "\"x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C483648101010101010101010101010101010101'\"" +# verify default plaintext header size is 0 +do_test test-default-plaintext-header-size { + sqlite_orig db :memory: + execsql { + PRAGMA cipher_default_plaintext_header_size; + } +} {0} + # verify pragma cipher_salt returns the first 16 bytes # of an existing database do_test test-pragma-salt-get { diff --git a/test/sqlcipher-pragmas.test b/test/sqlcipher-pragmas.test index 202a756..6d4e71f 100644 --- a/test/sqlcipher-pragmas.test +++ b/test/sqlcipher-pragmas.test @@ -46,7 +46,7 @@ do_test verify-pragma-cipher-version { execsql { PRAGMA cipher_version; } -} {{4.4.0 community}} +} {{4.4.1 community}} db close file delete -force test.db @@ -214,6 +214,17 @@ do_test verify-cipher-store-pass-before-key-does-not-segfault { db close file delete -force test.db +# verify setting cipher_store_pass results in deprecation warning +do_test verify-cipher-store-pass-deprecated { + sqlite_orig db test.db + execsql { + PRAGMA key = 'test'; + PRAGMA cipher_store_pass = 1; + } +} {ok {PRAGMA cipher_store_pass is deprecated, please remove from use}} +db close +file delete -force test.db + # verify the pragma cipher # reports the default value if_built_with_openssl verify-pragma-cipher-default { diff --git a/test/sqlcipher.test b/test/sqlcipher.test index 00bb087..80361b6 100644 --- a/test/sqlcipher.test +++ b/test/sqlcipher.test @@ -37,17 +37,21 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/sqlcipher.tcl +source $testdir/permutations.test -if {[file exist "$testdir/sqlcipher-license.test"]} { - slave_test_file $testdir/sqlcipher-license.test -} - -slave_test_file $testdir/sqlcipher-core.test -slave_test_file $testdir/sqlcipher-compatibility.test -slave_test_file $testdir/sqlcipher-rekey.test -slave_test_file $testdir/sqlcipher-plaintext-header.test -slave_test_file $testdir/sqlcipher-pragmas.test -slave_test_file $testdir/sqlcipher-integrity.test -slave_test_file $testdir/sqlcipher-backup.test +set pretests "" +test_suite "sqlcipher" -prefix "" -description { + Runs SQLCipher tests +} -files [ + test_set $pretests \ + sqlcipher-core.test \ + sqlcipher-compatibility.test \ + sqlcipher-rekey.test \ + sqlcipher-plaintext-header.test \ + sqlcipher-pragmas.test \ + sqlcipher-integrity.test \ + sqlcipher-backup.test +] +run_test_suite sqlcipher finish_test diff --git a/test/stat.test b/test/stat.test index 105169d..5eb7d6f 100644 --- a/test/stat.test +++ b/test/stat.test @@ -59,7 +59,7 @@ if {[wal_is_capable]} { PRAGMA journal_mode = delete; SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload FROM stat; - } {wal delete sqlite_master / 1 leaf 0 0 916 0} + } {wal delete sqlite_schema / 1 leaf 0 0 916 0} } do_test stat-1.0 { @@ -85,9 +85,9 @@ do_test stat-1.2 { do_test stat-1.3 { execsql { SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload - FROM stat WHERE name = 'sqlite_master'; + FROM stat WHERE name = 'sqlite_schema'; } -} {sqlite_master / 1 leaf 2 77 831 40} +} {sqlite_schema / 1 leaf 2 77 831 40} do_test stat-1.4 { execsql { DROP TABLE t1; @@ -108,7 +108,7 @@ do_execsql_test stat-2.1 { INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3 ORDER BY rowid; SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload - FROM stat WHERE name != 'sqlite_master' ORDER BY name; + FROM stat WHERE name != 'sqlite_schema' ORDER BY name; } [list \ sqlite_autoindex_t3_1 / 3 internal 3 368 623 125 \ sqlite_autoindex_t3_1 /000/ 8 leaf 8 946 46 123 \ @@ -138,7 +138,7 @@ do_execsql_test stat-2.1agg { SELECT * FROM dbstat WHERE aggregate=TRUE ORDER BY name; } [list \ sqlite_autoindex_t3_1 {} 5 {} 32 3898 1065 132 {} 5120 \ - sqlite_master {} 1 {} 2 84 824 49 {} 1024 \ + sqlite_schema {} 1 {} 2 84 824 49 {} 1024 \ t3 {} 17 {} 47 11188 5815 370 {} 17408 \ ] @@ -158,7 +158,7 @@ do_execsql_test stat-3.1 { CREATE INDEX i4 ON t4(x); INSERT INTO t4(rowid, x) VALUES(2, a_string(7777)); SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload - FROM stat WHERE name != 'sqlite_master' ORDER BY name; + FROM stat WHERE name != 'sqlite_schema' ORDER BY name; } [list \ i4 / 3 leaf 1 103 905 7782 \ i4 /000+000000 4 overflow 0 1020 0 0 \ @@ -183,7 +183,7 @@ do_execsql_test stat-3.2 { SELECT *, '|' FROM dbstat WHERE aggregate=TRUE ORDER BY name; } [list \ i4 {} 9 {} 1 7782 1386 7782 {} 9216 | \ - sqlite_master {} 1 {} 2 74 834 40 {} 1024 | \ + sqlite_schema {} 1 {} 2 74 834 40 {} 1024 | \ t4 {} 8 {} 1 7780 367 7780 {} 8192 | \ ] @@ -221,11 +221,11 @@ do_execsql_test stat-5.1 { do_execsql_test stat-5.20 { SELECT name, quote(path), pageno, quote(pagetype), ncell, payload, unused, mx_payload, '|' FROM dbstat('main',1); -} {sqlite_master NULL 1 NULL 1 34 878 34 | tx NULL 1 NULL 0 0 1016 0 |} +} {sqlite_schema NULL 1 NULL 1 34 878 34 | tx NULL 1 NULL 0 0 1016 0 |} do_execsql_test stat-5.21 { SELECT name, quote(path), pageno, quote(pagetype), ncell, payload, unused, mx_payload, '|' FROM dbstat('aux1',1); -} {sqlite_master NULL 1 NULL 1 34 878 34 | t1 NULL 3 NULL 2 3033 5 1517 |} +} {sqlite_schema NULL 1 NULL 1 34 878 34 | t1 NULL 3 NULL 2 3033 5 1517 |} do_catchsql_test stat-6.1 { @@ -247,27 +247,27 @@ do_execsql_test 7.1 { do_execsql_test 7.1.1 { SELECT * FROM dbstat('123'); } { - sqlite_master / 1 leaf 1 37 875 37 0 1024 + sqlite_schema / 1 leaf 1 37 875 37 0 1024 x1 / 2 leaf 1 4 1008 4 1024 1024 } do_execsql_test 7.1.2 { SELECT * FROM dbstat(123); } { - sqlite_master / 1 leaf 1 37 875 37 0 1024 + sqlite_schema / 1 leaf 1 37 875 37 0 1024 x1 / 2 leaf 1 4 1008 4 1024 1024 } do_execsql_test 7.1.3 { CREATE VIRTUAL TABLE x2 USING dbstat('123'); SELECT * FROM x2; } { - sqlite_master / 1 leaf 1 37 875 37 0 1024 + sqlite_schema / 1 leaf 1 37 875 37 0 1024 x1 / 2 leaf 1 4 1008 4 1024 1024 } do_execsql_test 7.1.4 { CREATE VIRTUAL TABLE x3 USING dbstat(123); SELECT * FROM x3; } { - sqlite_master / 1 leaf 1 37 875 37 0 1024 + sqlite_schema / 1 leaf 1 37 875 37 0 1024 x1 / 2 leaf 1 4 1008 4 1024 1024 } @@ -280,7 +280,7 @@ do_execsql_test 7.2 { do_execsql_test 7.2.1 { SELECT * FROM dbstat('123corp'); } { - sqlite_master / 1 leaf 1 37 875 37 0 1024 + sqlite_schema / 1 leaf 1 37 875 37 0 1024 x1 / 2 leaf 1 4 1008 4 1024 1024 } do_catchsql_test 7.2.2 { @@ -290,7 +290,7 @@ do_execsql_test 7.2.3 { CREATE VIRTUAL TABLE x2 USING dbstat('123corp'); SELECT * FROM x2; } { - sqlite_master / 1 leaf 1 37 875 37 0 1024 + sqlite_schema / 1 leaf 1 37 875 37 0 1024 x1 / 2 leaf 1 4 1008 4 1024 1024 } do_catchsql_test 7.2.4 { diff --git a/test/tester.tcl b/test/tester.tcl index 05c5234..b1acb06 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -129,6 +129,7 @@ if {[info command sqlite_orig]==""} { set ::dbhandle [lindex $args 0] uplevel #0 $::G(perm:dbconfig) } + [lindex $args 0] cache size 3 set res } else { # This command is not opening a new database connection. Pass the @@ -782,6 +783,9 @@ proc do_test {name cmd expected} { output2 "\nError: $result" fail_test $name } else { + if {[permutation]=="maindbname"} { + set result [string map [list [string tolower ICECUBE] main] $result] + } if {[regexp {^[~#]?/.*/$} $expected]} { # "expected" is of the form "/PATTERN/" then the result if correct if # regular expression PATTERN matches the result. "~/PATTERN/" means @@ -2475,6 +2479,7 @@ set sqlite_fts3_enable_parentheses 0 # this setting by invoking "database_can_be_corrupt" # database_never_corrupt +extra_schema_checks 1 source $testdir/thread_common.tcl source $testdir/malloc_common.tcl diff --git a/test/triggerupfrom.test b/test/triggerupfrom.test new file mode 100644 index 0000000..9bfacb8 --- /dev/null +++ b/test/triggerupfrom.test @@ -0,0 +1,174 @@ +# 2020 July 14 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix triggerupfrom + +do_execsql_test 1.0 { + CREATE TABLE map(k, v); + INSERT INTO map VALUES(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'); + + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + + CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN + UPDATE t1 SET c = v FROM map WHERE k=new.a AND a=new.a; + END; +} + +do_execsql_test 1.1 { + INSERT INTO t1(a) VALUES(1); +} + +do_execsql_test 1.2 { + SELECT a, c FROM t1 ORDER BY a; +} {1 one} + +do_execsql_test 1.3 { + INSERT INTO t1(a) VALUES(2), (3), (4), (5); + SELECT a, c FROM t1 ORDER BY a; +} {1 one 2 two 3 three 4 four 5 {}} + +forcedelete test.db2 +do_execsql_test 2.0 { + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t3(x, y); + INSERT INTO aux.t3 VALUES('x', 'y'); +} + +do_catchsql_test 2.1 { + CREATE TRIGGER tr2 AFTER INSERT ON t1 BEGIN + UPDATE t1 SET b = y FROM aux.t3 WHERE k=new.a; + END; +} {1 {trigger tr2 cannot reference objects in database aux}} + +do_execsql_test 2.2 { + CREATE TEMP TRIGGER tr2 AFTER INSERT ON t1 BEGIN + UPDATE t1 SET b = y FROM aux.t3 WHERE a=new.a; + END; + INSERT INTO t1(a) VALUES(10), (20); + SELECT * FROM t1; +} { + 1 {} one + 2 {} two + 3 {} three + 4 {} four + 5 {} {} + 10 y {} + 20 y {} +} + +do_execsql_test 2.3 { + CREATE TABLE link(f, t); + INSERT INTO link VALUES(5, 2), (20, 10), (2, 1); + CREATE TRIGGER tr3 BEFORE DELETE ON t1 BEGIN + UPDATE t1 SET b=coalesce(old.b,old.c) FROM main.link WHERE a=t AND old.a=f; + END; + DELETE FROM t1 WHERE a=2; + SELECT * FROM t1; +} { + 1 two one + 3 {} three + 4 {} four + 5 {} {} + 10 y {} + 20 y {} +} + +db close +sqlite3 db "" +do_catchsql_test 2.4 { + ATTACH 'test.db' AS yyy; + SELECT * FROM t1; +} {1 {malformed database schema (tr3) - trigger tr3 cannot reference objects in database main}} + +#------------------------------------------------------------------------- +reset_db +forcedelete test.db2 +do_execsql_test 3.0 { + CREATE TABLE mmm(x, y); + INSERT INTO mmm VALUES(1, 'one'); + INSERT INTO mmm VALUES(2, 'two'); + INSERT INTO mmm VALUES(3, 'three'); + + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t1(a, b); + CREATE TABLE aux.mmm(x, y); + INSERT INTO aux.mmm VALUES(1, 'ONE'); + INSERT INTO aux.mmm VALUES(2, 'TWO'); + INSERT INTO aux.mmm VALUES(3, 'THREE'); + + CREATE TRIGGER aux.ttt AFTER INSERT ON t1 BEGIN + UPDATE t1 SET b=y FROM mmm WHERE x=new.a AND a=new.a; + END; + + INSERT INTO t1(a) VALUES (2); + SELECT * FROM t1; +} {2 TWO} + +#------------------------------------------------------------------------- +# Test that INSTEAD OF UPDATE triggers on views work with UPDATE...FROM +# statements. Including, if the library is built with ENABLE_HIDDEN_COLUMNS, +# that they work correctly on views with hidden columns. +# +reset_db +do_execsql_test 4.0 { + CREATE TABLE t1(k, a, b); + INSERT INTO t1 VALUES('a', 1, 'one'); + INSERT INTO t1 VALUES('b', 2, 'two'); + INSERT INTO t1 VALUES('c', 3, 'three'); + INSERT INTO t1 VALUES('d', 4, 'four'); + + CREATE TABLE log(x); + CREATE VIEW v1 AS SELECT k, a, b AS __hidden__b FROM t1; + CREATE TRIGGER tr1 INSTEAD OF UPDATE ON v1 BEGIN + INSERT INTO log VALUES( + '('||old.a||','||old.__hidden__b||')->('||new.a||','||new.__hidden__b||')' + ); + END; +} + +ifcapable hiddencolumns { + do_execsql_test 4.1-hc-enabled { + SELECT * FROM v1 + } {a 1 b 2 c 3 d 4} +} else { + do_execsql_test 4.1-hc-disabled { + SELECT * FROM v1 + } {a 1 one b 2 two c 3 three d 4 four} +} + +do_execsql_test 4.2 { + UPDATE v1 SET a='xyz' WHERE k IN ('a', 'c'); + SELECT * FROM log; + DELETE FROM log; +} { + (1,one)->(xyz,one) + (3,three)->(xyz,three) +} + +do_execsql_test 4.3 { + CREATE TABLE map(k, v); + INSERT INTO map VALUES('b', 'twelve'); + INSERT INTO map VALUES('d', 'fourteen'); + UPDATE v1 SET a=map.v FROM map WHERE v1.k=map.k; + SELECT * FROM log; + DELETE FROM log; +} { + (2,two)->(twelve,two) + (4,four)->(fourteen,four) +} + + + +finish_test + diff --git a/test/tt3_stress.c b/test/tt3_stress.c index cdfab9c..be917b7 100644 --- a/test/tt3_stress.c +++ b/test/tt3_stress.c @@ -41,7 +41,7 @@ static char *stress_thread_2(int iTid, void *pArg){ Sqlite db = {0}; /* SQLite database connection */ while( !timetostop(&err) ){ opendb(&err, &db, "test.db", 0); - sql_script(&err, &db, "SELECT * FROM sqlite_master;"); + sql_script(&err, &db, "SELECT * FROM sqlite_schema;"); clear_error(&err, SQLITE_LOCKED); closedb(&err, &db); } @@ -266,7 +266,7 @@ static char *stress2_workload19(int iTid, void *pArg){ const char *zDb = (const char*)pArg; while( !timetostop(&err) ){ opendb(&err, &db, zDb, 0); - sql_script(&err, &db, "SELECT * FROM sqlite_master;"); + sql_script(&err, &db, "SELECT * FROM sqlite_schema;"); clear_error(&err, SQLITE_LOCKED); closedb(&err, &db); } @@ -362,7 +362,3 @@ static void stress2(int nMs){ sqlite3_enable_shared_cache(0); print_and_free_err(&err); } - - - - diff --git a/test/upfrom1.tcl b/test/upfrom1.tcl new file mode 100644 index 0000000..22fc68a --- /dev/null +++ b/test/upfrom1.tcl @@ -0,0 +1,115 @@ +# 2020 April 22 +# +# 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 $argv0] pg_common.tcl] + +#========================================================================= + +start_test upfrom1 "2020 April 22" + +foreach {tn wo} { + 1 "WITHOUT ROWID" + 2 "" +} { +eval [string map [list %TN% $tn %WITHOUT_ROWID% $wo] { +execsql_test 1.%TN%.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER) %WITHOUT_ROWID%; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); + + DROP TABLE IF EXISTS chng; + CREATE TABLE chng(a INTEGER, b INTEGER, c INTEGER); + INSERT INTO chng VALUES(1, 100, 1000); + INSERT INTO chng VALUES(7, 700, 7000); +} + +execsql_test 1.%TN%.1 { + SELECT * FROM t2; +} + +execsql_test 1.%TN%.2 { + UPDATE t2 SET b = chng.b, c = chng.c FROM chng WHERE chng.a = t2.a; + SELECT * FROM t2 ORDER BY a; +} + +execsql_test 1.%TN%.3 { + DELETE FROM t2; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); +} + +execsql_test 1.%TN%.4 { + UPDATE t2 SET (b, c) = (SELECT b, c FROM chng WHERE a=t2.a) + WHERE a IN (SELECT a FROM chng); + SELECT * FROM t2 ORDER BY a; +} + +execsql_test 1.%TN%.5 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT) %WITHOUT_ROWID%; + INSERT INTO t3 VALUES(1, 1, 'one'); + INSERT INTO t3 VALUES(2, 2, 'two'); + INSERT INTO t3 VALUES(3, 3, 'three'); + + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(x TEXT); + INSERT INTO t4 VALUES('five'); + + SELECT * FROM t3 ORDER BY a; +} + +execsql_test 1.%TN%.6 { + UPDATE t3 SET c=x FROM t4; + SELECT * FROM t3 ORDER BY a; +} +}]} + +execsql_test 2.1 { + DROP TABLE IF EXISTS t5; + DROP TABLE IF EXISTS m1; + DROP TABLE IF EXISTS m2; + CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT, c TEXT); + CREATE TABLE m1(x INTEGER PRIMARY KEY, y TEXT); + CREATE TABLE m2(u INTEGER PRIMARY KEY, v TEXT); + + INSERT INTO t5 VALUES(1, 'one', 'ONE'); + INSERT INTO t5 VALUES(2, 'two', 'TWO'); + INSERT INTO t5 VALUES(3, 'three', 'THREE'); + INSERT INTO t5 VALUES(4, 'four', 'FOUR'); + + INSERT INTO m1 VALUES(1, 'i'); + INSERT INTO m1 VALUES(2, 'ii'); + INSERT INTO m1 VALUES(3, 'iii'); + + INSERT INTO m2 VALUES(1, 'I'); + INSERT INTO m2 VALUES(3, 'II'); + INSERT INTO m2 VALUES(4, 'III'); +} + +execsql_test 2.2 { + UPDATE t5 SET b=y, c=v FROM m1 LEFT JOIN m2 ON (x=u) WHERE x=a; + SELECT * FROM t5 ORDER BY a; +} + +errorsql_test 2.3.1 { + UPDATE t5 SET b=1 FROM t5; +} +errorsql_test 2.3.2 { + UPDATE t5 AS apples SET b=1 FROM t5 AS apples; +} + + +finish_test + diff --git a/test/upfrom1.test b/test/upfrom1.test new file mode 100644 index 0000000..7996f97 --- /dev/null +++ b/test/upfrom1.test @@ -0,0 +1,178 @@ +# 2020 April 22 +# +# 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 implements regression tests for SQLite library. +# + +#################################################### +# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED! +#################################################### + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix upfrom1 + +do_execsql_test 1.1.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER) WITHOUT ROWID; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); + + DROP TABLE IF EXISTS chng; + CREATE TABLE chng(a INTEGER, b INTEGER, c INTEGER); + INSERT INTO chng VALUES(1, 100, 1000); + INSERT INTO chng VALUES(7, 700, 7000); +} {} + +do_execsql_test 1.1.1 { + SELECT * FROM t2; +} {1 2 3 4 5 6 7 8 9} + +do_execsql_test 1.1.2 { + UPDATE t2 SET b = chng.b, c = chng.c FROM chng WHERE chng.a = t2.a; + SELECT * FROM t2 ORDER BY a; +} {1 100 1000 4 5 6 7 700 7000} + +do_execsql_test 1.1.3 { + DELETE FROM t2; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); +} {} + +do_execsql_test 1.1.4 { + UPDATE t2 SET (b, c) = (SELECT b, c FROM chng WHERE a=t2.a) + WHERE a IN (SELECT a FROM chng); + SELECT * FROM t2 ORDER BY a; +} {1 100 1000 4 5 6 7 700 7000} + +do_execsql_test 1.1.5 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT) WITHOUT ROWID; + INSERT INTO t3 VALUES(1, 1, 'one'); + INSERT INTO t3 VALUES(2, 2, 'two'); + INSERT INTO t3 VALUES(3, 3, 'three'); + + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(x TEXT); + INSERT INTO t4 VALUES('five'); + + SELECT * FROM t3 ORDER BY a; +} {1 1 one 2 2 two 3 3 three} + +do_execsql_test 1.1.6 { + UPDATE t3 SET c=x FROM t4; + SELECT * FROM t3 ORDER BY a; +} {1 1 five 2 2 five 3 3 five} + +do_execsql_test 1.2.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER) ; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); + + DROP TABLE IF EXISTS chng; + CREATE TABLE chng(a INTEGER, b INTEGER, c INTEGER); + INSERT INTO chng VALUES(1, 100, 1000); + INSERT INTO chng VALUES(7, 700, 7000); +} {} + +do_execsql_test 1.2.1 { + SELECT * FROM t2; +} {1 2 3 4 5 6 7 8 9} + +do_execsql_test 1.2.2 { + UPDATE t2 SET b = chng.b, c = chng.c FROM chng WHERE chng.a = t2.a; + SELECT * FROM t2 ORDER BY a; +} {1 100 1000 4 5 6 7 700 7000} + +do_execsql_test 1.2.3 { + DELETE FROM t2; + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + INSERT INTO t2 VALUES(7, 8, 9); +} {} + +do_execsql_test 1.2.4 { + UPDATE t2 SET (b, c) = (SELECT b, c FROM chng WHERE a=t2.a) + WHERE a IN (SELECT a FROM chng); + SELECT * FROM t2 ORDER BY a; +} {1 100 1000 4 5 6 7 700 7000} + +do_execsql_test 1.2.5 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT) ; + INSERT INTO t3 VALUES(1, 1, 'one'); + INSERT INTO t3 VALUES(2, 2, 'two'); + INSERT INTO t3 VALUES(3, 3, 'three'); + + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(x TEXT); + INSERT INTO t4 VALUES('five'); + + SELECT * FROM t3 ORDER BY a; +} {1 1 one 2 2 two 3 3 three} + +do_execsql_test 1.2.6 { + UPDATE t3 SET c=x FROM t4; + SELECT * FROM t3 ORDER BY a; +} {1 1 five 2 2 five 3 3 five} + +do_execsql_test 2.1 { + DROP TABLE IF EXISTS t5; + DROP TABLE IF EXISTS m1; + DROP TABLE IF EXISTS m2; + CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT, c TEXT); + CREATE TABLE m1(x INTEGER PRIMARY KEY, y TEXT); + CREATE TABLE m2(u INTEGER PRIMARY KEY, v TEXT); + + INSERT INTO t5 VALUES(1, 'one', 'ONE'); + INSERT INTO t5 VALUES(2, 'two', 'TWO'); + INSERT INTO t5 VALUES(3, 'three', 'THREE'); + INSERT INTO t5 VALUES(4, 'four', 'FOUR'); + + INSERT INTO m1 VALUES(1, 'i'); + INSERT INTO m1 VALUES(2, 'ii'); + INSERT INTO m1 VALUES(3, 'iii'); + + INSERT INTO m2 VALUES(1, 'I'); + INSERT INTO m2 VALUES(3, 'II'); + INSERT INTO m2 VALUES(4, 'III'); +} {} + +do_execsql_test 2.2 { + UPDATE t5 SET b=y, c=v FROM m1 LEFT JOIN m2 ON (x=u) WHERE x=a; + SELECT * FROM t5 ORDER BY a; +} {1 i I 2 ii {} 3 iii II 4 four FOUR} + +# PG says ERROR: table name "t5" specified more than once +do_test 2.3.1 { catch { execsql { + UPDATE t5 SET b=1 FROM t5; +} } } 1 + +# PG says ERROR: table name "apples" specified more than once +do_test 2.3.2 { catch { execsql { + UPDATE t5 AS apples SET b=1 FROM t5 AS apples; +} } } 1 + +# Problem found by OSSFuzz on 2020-07-20 +# https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=24282 +# +reset_db +do_execsql_test 3.1 { + CREATE TABLE t0(a); + CREATE TABLE t1(b); + UPDATE t1 SET b=sum(a) FROM t0; + SELECT * FROM t0, t1; +} {} + +finish_test diff --git a/test/upfrom2.test b/test/upfrom2.test new file mode 100644 index 0000000..f903c1f --- /dev/null +++ b/test/upfrom2.test @@ -0,0 +1,371 @@ +# 2020 April 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix upfrom2 + +# Test cases: +# +# 1.*: Test that triggers are fired correctly for UPDATE FROM statements, +# and only once for each row. Except for INSTEAD OF triggers on +# views - these are fired once for each row returned by the join, +# including duplicates. +# +# 2.*: Test adding ORDER BY and LIMIT clauses with UPDATE FROM statements. +# +# 5.*: Test that specifying the target table name or alias in the FROM +# clause of an UPDATE statement is an error. +# + +foreach {tn wo} { + 1 "" + 2 "WITHOUT ROWID" +} { + reset_db + + eval [string map [list %WO% $wo %TN% $tn] { + do_execsql_test 1.%TN%.0 { + CREATE TABLE log(t TEXT); + CREATE TABLE t1(x PRIMARY KEY, y, z UNIQUE) %WO%; + CREATE INDEX t1y ON t1(y); + + INSERT INTO t1 VALUES(1, 'i', 'one'); + INSERT INTO t1 VALUES(2, 'ii', 'two'); + INSERT INTO t1 VALUES(3, 'iii', 'three'); + INSERT INTO t1 VALUES(4, 'iv', 'four'); + + CREATE TRIGGER tr1 BEFORE UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.z || '->' || new.z); + END; + CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.y || '->' || new.y); + END; + } + + do_execsql_test 1.%TN%.1 { + WITH data(k, v) AS ( + VALUES(3, 'thirty'), (1, 'ten') + ) + UPDATE t1 SET z=v FROM data WHERE x=k; + + SELECT * FROM t1; + SELECT * FROM log; + } { + 1 i ten 2 ii two 3 iii thirty 4 iv four + one->ten i->i + three->thirty iii->iii + } + + do_execsql_test 1.%TN%.2 { + CREATE TABLE t2(a, b); + CREATE TABLE t3(k, v); + + INSERT INTO t3 VALUES(5, 'v'); + INSERT INTO t3 VALUES(12, 'xii'); + + INSERT INTO t2 VALUES(2, 12); + INSERT INTO t2 VALUES(3, 5); + + DELETE FROM log; + UPDATE t1 SET y=v FROM t2, t3 WHERE t1.x=t2.a AND t3.k=t2.b; + + SELECT * FROM t1; + SELECT * FROM log; + } { + 1 i ten 2 xii two 3 v thirty 4 iv four + two->two ii->xii + thirty->thirty iii->v + } + + do_execsql_test 1.%TN%.3 { + DELETE FROM log; + WITH data(k, v) AS ( + VALUES(1, 'seven'), (1, 'eight'), (2, 'eleven'), (2, 'twelve') + ) + UPDATE t1 SET z=v FROM data WHERE x=k; + + SELECT * FROM t1; + SELECT * FROM log; + } { + 1 i eight 2 xii twelve 3 v thirty 4 iv four + ten->eight i->i + two->twelve xii->xii + } + + do_test 1.%TN%.4 { db changes } {2} + + do_execsql_test 1.%TN%.5 { + CREATE VIEW v1 AS SELECT * FROM t1; + CREATE TRIGGER v1tr INSTEAD OF UPDATE ON v1 BEGIN + UPDATE t1 SET y=new.y, z=new.z WHERE x=new.x; + END; + + DELETE FROM log; + WITH data(k, v) AS ( + VALUES(3, 'thirteen'), (3, 'fourteen'), (4, 'fifteen'), (4, 'sixteen') + ) + UPDATE v1 SET z=v FROM data WHERE x=k; + } + + do_execsql_test 1.%TN%.6 { + SELECT * FROM v1; + SELECT * FROM log; + } { + 1 i eight 2 xii twelve 3 v fourteen 4 iv sixteen + thirty->thirteen v->v + thirteen->fourteen v->v + four->fifteen iv->iv + fifteen->sixteen iv->iv + } + + #-------------------------------------------------------------- + + do_execsql_test 1.%TN%.7 { + CREATE TABLE o1(w, x, y, z UNIQUE, PRIMARY KEY(w, x)) %WO%; + CREATE INDEX o1y ON t1(y); + + INSERT INTO o1 VALUES(0, 0, 'i', 'one'); + INSERT INTO o1 VALUES(0, 1, 'ii', 'two'); + INSERT INTO o1 VALUES(1, 0, 'iii', 'three'); + INSERT INTO o1 VALUES(1, 1, 'iv', 'four'); + + CREATE TRIGGER tro1 BEFORE UPDATE ON o1 BEGIN + INSERT INTO log VALUES(old.z || '->' || new.z); + END; + CREATE TRIGGER tro2 AFTER UPDATE ON o1 BEGIN + INSERT INTO log VALUES(old.y || '->' || new.y); + END; + } + + do_execsql_test 1.%TN%.8 { + DELETE FROM log; + WITH data(k, v) AS ( + VALUES(3, 'thirty'), (1, 'ten') + ) + UPDATE o1 SET z=v FROM data WHERE (1+x+w*2)=k; + + SELECT * FROM o1; + SELECT * FROM log; + } { + 0 0 i ten 0 1 ii two 1 0 iii thirty 1 1 iv four + one->ten i->i + three->thirty iii->iii + } + + do_execsql_test 1.%TN%.9 { + DELETE FROM log; + UPDATE o1 SET y=v FROM t2, t3 WHERE (1+o1.w*2+o1.x)=t2.a AND t3.k=t2.b; + + SELECT * FROM o1; + SELECT * FROM log; + } { + 0 0 i ten 0 1 xii two 1 0 v thirty 1 1 iv four + two->two ii->xii + thirty->thirty iii->v + } + + do_execsql_test 1.%TN%.10 { + DELETE FROM log; + WITH data(k, v) AS ( + VALUES(1, 'seven'), (1, 'eight'), (2, 'eleven'), (2, 'twelve') + ) + UPDATE o1 SET z=v FROM data WHERE (1+w*2+x)=k; + + SELECT * FROM o1; + SELECT * FROM log; + } { + 0 0 i eight 0 1 xii twelve 1 0 v thirty 1 1 iv four + ten->eight i->i + two->twelve xii->xii + } + + do_test 1.%TN%.11 { db changes } {2} + + do_execsql_test 1.%TN%.12 { + CREATE VIEW w1 AS SELECT * FROM o1; + CREATE TRIGGER w1tr INSTEAD OF UPDATE ON w1 BEGIN + UPDATE o1 SET y=new.y, z=new.z WHERE w=new.w AND x=new.x; + END; + + DELETE FROM log; + WITH data(k, v) AS ( + VALUES(3, 'thirteen'), (3, 'fourteen'), (4, 'fifteen'), (4, 'sixteen') + ) + UPDATE w1 SET z=v FROM data WHERE (1+w*2+x)=k; + } + + do_execsql_test 1.%TN%.13 { + SELECT * FROM w1; + SELECT * FROM log; + } { + 0 0 i eight 0 1 xii twelve 1 0 v fourteen 1 1 iv sixteen + thirty->thirteen v->v + thirteen->fourteen v->v + four->fifteen iv->iv + fifteen->sixteen iv->iv + } + +}] +} + +ifcapable update_delete_limit { +foreach {tn wo} { + 1 "" + 2 "WITHOUT ROWID" +} { + reset_db + +eval [string map [list %WO% $wo %TN% $tn] { + do_execsql_test 2.%TN%.1 { + CREATE TABLE x1(a INTEGER PRIMARY KEY, b) %WO%; + INSERT INTO x1 VALUES + (1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), + (5, 'five'), (6, 'six'), (7, 'seven'), (8, 'eight'); + } + + do_execsql_test 2.%TN%.2 { + CREATE TABLE data1(x, y); + INSERT INTO data1 VALUES + (1, 'eleven'), (1, 'twenty-one'), (2, 'twelve'), (2, 'twenty-two'), + (3, 'thirteen'), (3, 'twenty-three'), (4, 'fourteen'), (4, 'twenty-four'); + } + + do_execsql_test 2.%TN%.3 { + UPDATE x1 SET b=y FROM data1 WHERE a=x ORDER BY a LIMIT 3; + SELECT * FROM x1; + } { + 1 eleven 2 twelve 3 thirteen 4 four 5 five 6 six 7 seven 8 eight + } + + do_execsql_test 2.%TN%.4 { + UPDATE x1 SET b=b||y FROM data1 WHERE a=x ORDER BY b LIMIT 3; + SELECT * FROM x1; + } { + 1 eleveneleven 2 twelve 3 thirteenthirteen 4 fourfourteen + 5 five 6 six 7 seven 8 eight + } + + do_catchsql_test 2.%TN%.5 { + UPDATE x1 SET b=b||b ORDER BY b; + } {1 {ORDER BY without LIMIT on UPDATE}} + do_catchsql_test 2.%TN%.6 { + UPDATE x1 SET b=b||y FROM data1 WHERE a=x ORDER BY b; + } {1 {ORDER BY without LIMIT on UPDATE}} + + #----------------------------------------------------------------------- + + do_execsql_test 2.%TN%.6 { + DROP TABLE x1; + CREATE TABLE x1(u, v, b, PRIMARY KEY(u, v)) %WO%; + INSERT INTO x1 VALUES + (0, 1, 'one'), (1, 0, 'two'), (1, 1, 'three'), (2, 0, 'four'), + (2, 1, 'five'), (3, 0, 'six'), (3, 1, 'seven'), (4, 0, 'eight'); + } + + do_execsql_test 2.%TN%.7 { + UPDATE x1 SET b=y FROM data1 WHERE (u*2+v)=x ORDER BY u, v LIMIT 3; + SELECT * FROM x1; + } { + 0 1 eleven 1 0 twelve 1 1 thirteen 2 0 four + 2 1 five 3 0 six 3 1 seven 4 0 eight + } + + do_execsql_test 2.%TN%.8 { + UPDATE x1 SET b=b||y FROM data1 WHERE (u*2+v)=x ORDER BY b LIMIT 3; + SELECT * FROM x1; + } { + 0 1 eleveneleven 1 0 twelve 1 1 thirteenthirteen 2 0 fourfourteen + 2 1 five 3 0 six 3 1 seven 4 0 eight + } + + +}] +}} + +reset_db +do_execsql_test 3.0 { + CREATE TABLE data(x, y, z); + CREATE VIEW t1 AS SELECT * FROM data; + CREATE TRIGGER t1_insert INSTEAD OF INSERT ON t1 BEGIN + INSERT INTO data VALUES(new.x, new.y, new.z); + END; + CREATE TRIGGER t1_update INSTEAD OF UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.z || '->' || new.z); + END; + + CREATE TABLE log(t TEXT); + + INSERT INTO t1 VALUES(1, 'i', 'one'); + INSERT INTO t1 VALUES(2, 'ii', 'two'); + INSERT INTO t1 VALUES(3, 'iii', 'three'); + INSERT INTO t1 VALUES(4, 'iv', 'four'); +} + +do_execsql_test 3.1 { + WITH input(k, v) AS ( + VALUES(3, 'thirty'), (1, 'ten') + ) + UPDATE t1 SET z=v FROM input WHERE x=k; +} + +foreach {tn sql} { + 2 { + CREATE TABLE x1(a INT PRIMARY KEY, b, c) WITHOUT ROWID; + } + 1 { + CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c); + } + 3 { + CREATE TABLE x1(a INT PRIMARY KEY, b, c); + } +} { + + reset_db + execsql $sql + + do_execsql_test 4.$tn.0 { + INSERT INTO x1 VALUES(1, 1, 1); + INSERT INTO x1 VALUES(2, 2, 2); + INSERT INTO x1 VALUES(3, 3, 3); + INSERT INTO x1 VALUES(4, 4, 4); + INSERT INTO x1 VALUES(5, 5, 5); + CREATE TABLE map(o, t); + INSERT INTO map VALUES(3, 30), (4, 40), (1, 10); + } + + do_execsql_test 4.$tn.1 { + UPDATE x1 SET a=t FROM map WHERE a=o; + SELECT * FROM x1 ORDER BY a; + } {2 2 2 5 5 5 10 1 1 30 3 3 40 4 4} +} + +reset_db +do_execsql_test 5.0 { + CREATE TABLE x1(a, b, c); + CREATE TABLE x2(a, b, c); +} + +foreach {tn update nm} { + 1 "UPDATE x1 SET a=5 FROM x1" x1 + 2 "UPDATE x1 AS grapes SET a=5 FROM x1 AS grapes" grapes + 3 "UPDATE x1 SET a=5 FROM x2, x1" x1 + 4 "UPDATE x1 AS grapes SET a=5 FROM x2, x1 AS grapes" grapes +} { + do_catchsql_test 5.$tn $update \ + "1 {target object/alias may not appear in FROM clause: $nm}" +} + + +finish_test + + diff --git a/test/upfrom3.test b/test/upfrom3.test new file mode 100644 index 0000000..d30b3fa --- /dev/null +++ b/test/upfrom3.test @@ -0,0 +1,262 @@ +# 2020 July 14 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix upfrom3 + +# Test plan: +# +# 1.*: Test UPDATE ... FROM statements that modify IPK fields. And that +# modify "INTEGER PRIMARY KEY" fields on WITHOUT ROWID tables. +# +# 2.*: Test UPDATE ... FROM statements that modify PK fields of WITHOUT +# ROWID tables. +# +# 3.*: Test that UPDATE ... FROM statements are not confused if there +# are multiple tables of the same name in attached databases. +# +# 4.*: Tests for UPDATE ... FROM statements and foreign keys. +# + +foreach {tn wo} { + 1 "" + 2 "WITHOUT ROWID" +} { + reset_db + eval [string map [list %WO% $wo %TN% $tn] { + + do_execsql_test 1.%TN%.0 { + CREATE TABLE log(t TEXT); + CREATE TABLE t1(x INTEGER PRIMARY KEY, y, z UNIQUE) %WO%; + CREATE INDEX t1y ON t1(y); + + INSERT INTO t1 VALUES(1, 'i', 'one'); + INSERT INTO t1 VALUES(2, 'ii', 'two'); + INSERT INTO t1 VALUES(3, 'iii', 'three'); + INSERT INTO t1 VALUES(4, 'iv', 'four'); + } + + do_execsql_test 1.%TN%.1 { + CREATE TABLE x1(o, n); + INSERT INTO x1 VALUES(1, 11); + INSERT INTO x1 VALUES(2, 12); + INSERT INTO x1 VALUES(3, 13); + INSERT INTO x1 VALUES(4, 14); + UPDATE t1 SET x=n FROM x1 WHERE x=o; + SELECT x, y, z FROM t1 ORDER BY 1; + } { + 11 i one + 12 ii two + 13 iii three + 14 iv four + } + + do_test 1.%TN%.2 { db changes } 4 + + do_execsql_test 1.%TN%.3 { + INSERT INTO x1 VALUES(11, 21); + INSERT INTO x1 VALUES(12, 22); + INSERT INTO x1 VALUES(13, 23); + INSERT INTO x1 VALUES(14, 24); + + INSERT INTO x1 VALUES(21, 31); + INSERT INTO x1 VALUES(22, 32); + INSERT INTO x1 VALUES(23, 33); + INSERT INTO x1 VALUES(24, 34); + UPDATE t1 SET x=n FROM x1 WHERE x=o; + SELECT x, y, z FROM t1 ORDER BY 1; + } { + 21 i one + 22 ii two + 23 iii three + 24 iv four + } + + do_execsql_test 1.%TN%.4 { + UPDATE t1 SET x=n FROM x1 WHERE x=o; + SELECT x, y, z FROM t1 ORDER BY 1; + } { + 31 i one + 32 ii two + 33 iii three + 34 iv four + } + + do_execsql_test 1.%TN%.5 { + INSERT INTO x1 VALUES(31, 32); + INSERT INTO x1 VALUES(33, 34); + UPDATE OR REPLACE t1 SET x=n FROM x1 WHERE x=o; + SELECT x, y, z FROM t1 ORDER BY 1; + } { + 32 i one + 34 iii three + } + + do_execsql_test 1.%TN%.6 { + INSERT INTO t1 VALUES(33, 'ii', 'two'); + INSERT INTO t1 VALUES(35, 'iv', 'four'); + } + + do_execsql_test 1.%TN%.7 { + CREATE TABLE x2(o, n, zz); + INSERT INTO x2 VALUES(32, 41, 'four'); + INSERT INTO x2 VALUES(33, 42, 'three'); + UPDATE OR IGNORE t1 SET x=n, z=zz FROM x2 WHERE x=o; + SELECT x, y, z FROM t1 ORDER BY 1; + } { + 32 i one + 33 ii two + 34 iii three + 35 iv four + } + + do_execsql_test 1.%TN%.8 { + UPDATE OR REPLACE t1 SET x=n, z=zz FROM x2 WHERE x=o; + SELECT x, y, z FROM t1 ORDER BY 1; + } { + 41 i four + 42 ii three + } + + }] +} + +do_execsql_test 2.1.1 { + CREATE TABLE u1(a, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID; + INSERT INTO u1 VALUES(0, 0, 0); + INSERT INTO u1 VALUES(1, 0, 1); + INSERT INTO u1 VALUES(2, 1, 0); + INSERT INTO u1 VALUES(3, 1, 1); +} + +do_execsql_test 2.1.2 { + CREATE TABLE map(f, t); + INSERT INTO map VALUES(0, 10); + INSERT INTO map VALUES(1, 11); + UPDATE u1 SET c=t FROM map WHERE c=f; + SELECT * FROM u1 ORDER BY a; +} { + 0 0 10 + 1 0 11 + 2 1 10 + 3 1 11 +} + +do_execsql_test 2.1.3 { + UPDATE u1 SET b=t FROM map WHERE b=f; + SELECT * FROM u1 ORDER BY a; +} { + 0 10 10 + 1 10 11 + 2 11 10 + 3 11 11 +} + +do_execsql_test 2.1.4 { + CREATE TABLE map2(o1, o2, n1, n2); + INSERT INTO map2 VALUES + (10, 10, 50, 50), (10, 11, 50, 60), + (11, 10, 60, 50), (11, 11, 60, 60); + UPDATE u1 SET b=n1, c=n2 FROM map2 WHERE b=o1 AND c=o2; + SELECT * FROM u1 ORDER BY a; +} { + 0 50 50 + 1 50 60 + 2 60 50 + 3 60 60 +} + +#------------------------------------------------------------------------- +foreach {tn wo} { + 1 "" + 2 "WITHOUT ROWID" +} { + reset_db + forcedelete test.db2 + eval [string map [list %WO% $wo %TN% $tn] { + do_execsql_test 3.$tn.1 { + CREATE TABLE g1(a, b, c, PRIMARY KEY(a, b)) %WO%; + INSERT INTO g1 VALUES(1, 1, 1); + + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.g1(a, b, c, PRIMARY KEY(a, b)) %WO%; + INSERT INTO aux.g1 VALUES(10, 1, 10); + INSERT INTO aux.g1 VALUES(20, 2, 20); + INSERT INTO aux.g1 VALUES(30, 3, 30); + } + + do_execsql_test 3.$tn.2 { + UPDATE aux.g1 SET c=101 FROM main.g1; + } + do_execsql_test 3.$tn.3 { + SELECT * FROM aux.g1; + } {10 1 101 20 2 101 30 3 101} + + do_execsql_test 3.$tn.4 { + UPDATE g1 SET c=101 FROM g1 AS g2; + } + do_execsql_test 3.$tn.5 { + SELECT * FROM g1; + } {1 1 101} + }] +} + +#------------------------------------------------------------------------- +reset_db +foreach {tn wo} { + 1 "" + 2 "WITHOUT ROWID" +} { + reset_db + forcedelete test.db2 + eval [string map [list %WO% $wo %TN% $tn] { + + do_execsql_test 4.$tn.1 { + CREATE TABLE p1(a INTEGER PRIMARY KEY, b) %WO%; + CREATE TABLE c1(x PRIMARY KEY, y REFERENCES p1 ON UPDATE CASCADE) %WO%; + PRAGMA foreign_keys = 1; + + INSERT INTO p1 VALUES(1, 'one'); + INSERT INTO p1 VALUES(11, 'eleven'); + INSERT INTO p1 VALUES(111, 'eleventyone'); + + INSERT INTO c1 VALUES('a', 1); + INSERT INTO c1 VALUES('b', 11); + INSERT INTO c1 VALUES('c', 111); + } + + do_execsql_test 4.$tn.2 { + CREATE TABLE map(f, t); + INSERT INTO map VALUES('a', 111); + INSERT INTO map VALUES('c', 112); + } + + do_catchsql_test 4.$tn.3 { + UPDATE c1 SET y=t FROM map WHERE x=f; + } {1 {FOREIGN KEY constraint failed}} + + do_execsql_test 4.$tn.4 { + INSERT INTO map VALUES('eleven', 12); + INSERT INTO map VALUES('eleventyone', 112); + UPDATE p1 SET a=t FROM map WHERE b=f; + } + + do_execsql_test 4.$tn.5 { + SELECT * FROM c1 + } {a 1 b 12 c 112} + + }] +} + +finish_test + diff --git a/test/upfromfault.test b/test/upfromfault.test new file mode 100644 index 0000000..fcb5956 --- /dev/null +++ b/test/upfromfault.test @@ -0,0 +1,140 @@ +# 2020 April 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix upfromfault + +foreach {tn sql} { + 1 { + CREATE TABLE t1(x PRIMARY KEY, y, z UNIQUE); + CREATE INDEX t1y ON t1(y); + } + 2 { + CREATE TABLE t1(x PRIMARY KEY, y, z UNIQUE) WITHOUT ROWID; + CREATE INDEX t1y ON t1(y); + } + 3 { + CREATE TABLE t1(x, y, z UNIQUE, PRIMARY KEY(x,y)) WITHOUT ROWID; + } + 4 { + CREATE VIRTUAL TABLE t1 USING fts5(x, y, z); + } + 5 { + CREATE TABLE real(x, y, z); + CREATE VIEW t1 AS SELECT * FROM real; + CREATE TRIGGER t1_insert INSTEAD OF INSERT ON t1 BEGIN + INSERT INTO real VALUES(new.x, new.y, new.z); + END; + CREATE TRIGGER t1_update INSTEAD OF UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.z || '->' || new.z); + UPDATE real SET y=new.y, z=new.z WHERE x=old.x; + END; + } +} { +if {$tn<5} continue + reset_db + + ifcapable !fts5 { if {$tn==4} continue } + + execsql $sql + do_execsql_test 1.$tn.0 { + CREATE TABLE log(t TEXT); + + INSERT INTO t1 VALUES(1, 'i', 'one'); + INSERT INTO t1 VALUES(2, 'ii', 'two'); + INSERT INTO t1 VALUES(3, 'iii', 'three'); + INSERT INTO t1 VALUES(4, 'iv', 'four'); + } + if {$tn!=4 && $tn!=5} { + do_execsql_test 1.$tn.0b { + CREATE TRIGGER tr1 BEFORE UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.z || '->' || new.z); + END; + CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN + INSERT INTO log VALUES(old.y || '->' || new.y); + END; + } + } + + faultsim_save_and_close + + do_faultsim_test 1.$tn -prep { + faultsim_restore_and_reopen + execsql { SELECT * FROM t1 } + } -body { + execsql { + WITH data(k, v) AS ( + VALUES(3, 'thirty'), (1, 'ten') + ) + UPDATE t1 SET z=v FROM data WHERE x=k; + } + } -test { + faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}} + if {$testrc==0} { + set res [execsql { SELECT * FROM t1 }] + if {$res!="1 i ten 2 ii two 3 iii thirty 4 iv four"} { + error "unexpected result: $res" + } + } + } +} + +reset_db +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(x, y, z); +} +faultsim_save_and_close +do_faultsim_test 2.1 -prep { + faultsim_restore_and_reopen +} -body { + execsql { + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN + UPDATE t2 SET x=a FROM t1 WHERE c=z; + END; + } +} -test { + faultsim_test_result {0 {}} +} + +faultsim_restore_and_reopen +do_execsql_test 2.2 { + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN + UPDATE t1 SET a=x FROM t2 WHERE c=z; + END; + + INSERT INTO t2 VALUES(1, 1, 1); + INSERT INTO t2 VALUES(2, 2, 2); + INSERT INTO t2 VALUES(3, 3, 3); +} +faultsim_save_and_close + +do_faultsim_test 2.3 -prep { + faultsim_restore_and_reopen +} -body { + execsql { + INSERT INTO t1 VALUES(NULL, NULL, 1), (NULL, NULL, 3); + } +} -test { + faultsim_test_result {0 {}} + if {$testrc==0} { + set res [execsql { SELECT * FROM t1 }] + if {$res!="1 {} 1 3 {} 3"} { + error "unexpected result: $res" + } + } +} + + +finish_test + diff --git a/test/wal.test b/test/wal.test index a003b6a..acc2780 100644 --- a/test/wal.test +++ b/test/wal.test @@ -43,6 +43,7 @@ proc sqlite3_wal {args} { [lindex $args 0] eval { PRAGMA journal_mode = wal } [lindex $args 0] eval { PRAGMA synchronous = normal } [lindex $args 0] function blob blob + db timeout 1000 } proc log_deleted {logfile} { diff --git a/test/wal2.test b/test/wal2.test index 9a56eb4..ae6134d 100644 --- a/test/wal2.test +++ b/test/wal2.test @@ -122,8 +122,12 @@ do_test wal2-1.1 { } {4 10} set RECOVER [list \ - {0 1 lock exclusive} {1 2 lock exclusive} {4 4 lock exclusive} \ - {1 2 unlock exclusive} {4 4 unlock exclusive} {0 1 unlock exclusive} \ + {0 1 lock exclusive} {1 2 lock exclusive} \ + {4 1 lock exclusive} {4 1 unlock exclusive} \ + {5 1 lock exclusive} {5 1 unlock exclusive} \ + {6 1 lock exclusive} {6 1 unlock exclusive} \ + {7 1 lock exclusive} {7 1 unlock exclusive} \ + {1 2 unlock exclusive} {0 1 unlock exclusive} \ ] set READ [list \ {4 1 lock shared} {4 1 unlock shared} \ @@ -394,9 +398,17 @@ set expected_locks [list] lappend expected_locks {1 1 lock exclusive} ;# Lock checkpoint lappend expected_locks {0 1 lock exclusive} ;# Lock writer lappend expected_locks {2 1 lock exclusive} ;# Lock recovery -lappend expected_locks {4 4 lock exclusive} ;# Lock all aReadMark[] +# lappend expected_locks {4 4 lock exclusive} ;# Lock all aReadMark[] +lappend expected_locks {4 1 lock exclusive} ;# Lock aReadMark[1] +lappend expected_locks {4 1 unlock exclusive} ;# Unlock aReadMark[1] +lappend expected_locks {5 1 lock exclusive} +lappend expected_locks {5 1 unlock exclusive} +lappend expected_locks {6 1 lock exclusive} +lappend expected_locks {6 1 unlock exclusive} +lappend expected_locks {7 1 lock exclusive} +lappend expected_locks {7 1 unlock exclusive} lappend expected_locks {2 1 unlock exclusive} ;# Unlock recovery -lappend expected_locks {4 4 unlock exclusive} ;# Unlock all aReadMark[] +# lappend expected_locks {4 4 unlock exclusive} ;# Unlock all aReadMark[] lappend expected_locks {0 1 unlock exclusive} ;# Unlock writer lappend expected_locks {3 1 lock exclusive} ;# Lock aReadMark[0] lappend expected_locks {3 1 unlock exclusive} ;# Unlock aReadMark[0] @@ -625,8 +637,12 @@ do_test wal2-6.4.1 { } {} set RECOVERY { - {0 1 lock exclusive} {1 2 lock exclusive} {4 4 lock exclusive} - {1 2 unlock exclusive} {4 4 unlock exclusive} {0 1 unlock exclusive} + {0 1 lock exclusive} {1 2 lock exclusive} + {4 1 lock exclusive} {4 1 unlock exclusive} + {5 1 lock exclusive} {5 1 unlock exclusive} + {6 1 lock exclusive} {6 1 unlock exclusive} + {7 1 lock exclusive} {7 1 unlock exclusive} + {1 2 unlock exclusive} {0 1 unlock exclusive} } set READMARK0_READ { {3 1 lock shared} {3 1 unlock shared} diff --git a/test/walprotocol.test b/test/walprotocol.test index b1d9e8c..a262cdd 100644 --- a/test/walprotocol.test +++ b/test/walprotocol.test @@ -52,18 +52,28 @@ do_test 1.1 { set ::locks [list] sqlite3 db test.db -vfs T execsql { SELECT * FROM x } - lrange $::locks 0 5 -} [list {0 1 lock exclusive} {1 2 lock exclusive} {4 4 lock exclusive} \ - {1 2 unlock exclusive} {4 4 unlock exclusive} {0 1 unlock exclusive} \ + lrange $::locks 0 11 +} [list {0 1 lock exclusive} {1 2 lock exclusive} \ + {4 1 lock exclusive} {4 1 unlock exclusive} \ + {5 1 lock exclusive} {5 1 unlock exclusive} \ + {6 1 lock exclusive} {6 1 unlock exclusive} \ + {7 1 lock exclusive} {7 1 unlock exclusive} \ + {1 2 unlock exclusive} \ + {0 1 unlock exclusive} \ ] do_test 1.2 { db close set ::locks [list] sqlite3 db test.db -vfs T execsql { SELECT * FROM x } - lrange $::locks 0 5 -} [list {0 1 lock exclusive} {1 2 lock exclusive} {4 4 lock exclusive} \ - {1 2 unlock exclusive} {4 4 unlock exclusive} {0 1 unlock exclusive} \ + lrange $::locks 0 11 +} [list {0 1 lock exclusive} {1 2 lock exclusive} \ + {4 1 lock exclusive} {4 1 unlock exclusive} \ + {5 1 lock exclusive} {5 1 unlock exclusive} \ + {6 1 lock exclusive} {6 1 unlock exclusive} \ + {7 1 lock exclusive} {7 1 unlock exclusive} \ + {1 2 unlock exclusive} \ + {0 1 unlock exclusive} \ ] proc lock_callback {method filename handle lock} { if {$lock == "1 2 lock exclusive"} { return SQLITE_BUSY } @@ -101,7 +111,7 @@ do_test 1.5 { set ::locks [list] sqlite3 db test.db -vfs T catchsql { SELECT * FROM x } -} {1 {locking protocol}} +} {0 z} db close T delete @@ -160,7 +170,7 @@ do_test 2.5 { } {Tehran Qom Markazi Qazvin Gilan Ardabil} do_test 2.6 { set ::r -} {1 {locking protocol}} +} {0 {Tehran Qom Markazi Qazvin Gilan Ardabil}} db close db2 close @@ -182,7 +192,7 @@ do_test 2.7 { } {Tehran Qom Markazi Qazvin Gilan Ardabil} do_test 2.8 { set ::r -} {1 {locking protocol}} +} {0 {Tehran Qom Markazi Qazvin Gilan Ardabil}} db close db2 close diff --git a/test/walsetlk.test b/test/walsetlk.test new file mode 100644 index 0000000..66513ad --- /dev/null +++ b/test/walsetlk.test @@ -0,0 +1,198 @@ +# 2020 May 06 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set testprefix walsetlk + +ifcapable !wal {finish_test ; return } +db timeout 1000 + +#------------------------------------------------------------------------- +# 1.*: Test that nothing goes wrong if recovery is forced while opening +# a write transaction or performing a checkpoint with blocking locks. +# + +do_execsql_test 1.0 { + CREATE TABLE t1(x, y); + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + INSERT INTO t1 VALUES(5, 6); + INSERT INTO t1 VALUES(7, 8); +} {wal} + +sqlite3 db2 test.db +db2 timeout 1000 + +do_execsql_test -db db2 1.1 { + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8} + +set fd [open test.db-shm r+] +puts $fd "blahblahblahblah" +flush $fd + +do_execsql_test 1.2 { + BEGIN; + INSERT INTO t1 VALUES(9, 10); +} + +do_execsql_test -db db2 1.3 { + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8} + +do_test 1.4 { + list [catch {db2 eval { BEGIN EXCLUSIVE }} msg] $msg +} {1 {database is locked}} + +do_execsql_test 1.5 { COMMIT } +do_execsql_test -db db2 1.6 { + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8 9 10} + +puts $fd "blahblahblahblah" +flush $fd + +do_execsql_test -db db2 1.7 { + PRAGMA wal_checkpoint = TRUNCATE +} {0 0 0} + +do_test 1.8 { + file size test.db-wal +} 0 + +close $fd +db close +db2 close +#------------------------------------------------------------------------- + +do_multiclient_test tn { + do_test 2.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(s, v); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + INSERT INTO t1 VALUES(5, 6); + } + code1 { db timeout 2000 } + } {} + + do_test 2.$tn.2 { + sql2 { + BEGIN; + INSERT INTO t1 VALUES(7, 8); + } + } {} + + do_test 2.$tn.3 { + set us [lindex [time { catch {db eval "BEGIN EXCLUSIVE"} }] 0] + expr $us>1000000 && $us<4000000 + } {1} + + do_test 2.$tn.4 { + sql2 { COMMIT } + sql1 { SELECT * FROM t1 } + } {1 2 3 4 5 6 7 8} + + do_test 2.$tn.5 { + sql2 { + BEGIN; + INSERT INTO t1 VALUES(9, 10); + } + } {} + + do_test 2.$tn.6 { + set us [lindex [time { catch {db eval "PRAGMA wal_checkpoint=RESTART"} }] 0] + expr $us>1000000 && $us<4000000 + } {1} + + do_test 2.$tn.7 { + sql2 { + COMMIT; + BEGIN; + SELECT * FROM t1; + } + } {1 2 3 4 5 6 7 8 9 10} + + do_test 2.$tn.8 { + set us [lindex [time { catch {db eval "PRAGMA wal_checkpoint=RESTART"} }] 0] + expr $us>1000000 && $us<4000000 + } {1} + + do_test 2.$tn.9 { + sql3 { + INSERT INTO t1 VALUES(11, 12); + } + sql2 { + COMMIT; + BEGIN; + SELECT * FROM t1; + } + sql3 { + INSERT INTO t1 VALUES(13, 14); + } + } {} + + do_test 2.$tn.10 { + set us [lindex [time { catch {db eval "PRAGMA wal_checkpoint=RESTART"} }] 0] + expr $us>1000000 && $us<4000000 + } {1} + + do_test 2.$tn.11 { + sql3 { + BEGIN; + SELECT * FROM t1; + } + sql1 { INSERT INTO t1 VALUES(15, 16); } + } {} + + do_test 2.$tn.12 { + set us [lindex [time { catch {db eval "PRAGMA wal_checkpoint=RESTART"} }] 0] + expr $us>1000000 && $us<4000000 + } {1} + + do_test 2.$tn.13 { + sql2 { + COMMIT; + BEGIN; + SELECT * FROM t1; + } + sql1 { INSERT INTO t1 VALUES(17, 18); } + } {} + + do_test 2.$tn.14 { + set us [lindex [time { catch {db eval "PRAGMA wal_checkpoint=RESTART"} }] 0] + expr $us>1000000 && $us<4000000 + } {1} + +} + +#------------------------------------------------------------------------- +reset_db +sqlite3 db2 test.db +db2 timeout 1000 +do_execsql_test 3.0 { + PRAGMA journal_mode = wal; + CREATE TABLE x1(x, y); + BEGIN; + INSERT INTO x1 VALUES(1, 2); +} {wal} + +do_test 3.1 { + list [catch { db2 eval {BEGIN EXCLUSIVE} } msg] $msg +} {1 {database is locked}} + +finish_test + diff --git a/test/walvfs.test b/test/walvfs.test index da0f43c..f21b65e 100644 --- a/test/walvfs.test +++ b/test/walvfs.test @@ -145,7 +145,7 @@ proc xWrite {method file args} { if {[file tail $file]=="test.db"} { incr ::cnt -1 if {$::cnt==0} { - sqlite3_memdebug_fail 5 -repeat 0 + sqlite3_memdebug_fail 1 -repeat 0 catchsql { SELECT 'a big long string!' } sqlite3_interrupt db } diff --git a/test/wapptest.tcl b/test/wapptest.tcl index 201078e..b7e16e7 100755 --- a/test/wapptest.tcl +++ b/test/wapptest.tcl @@ -164,7 +164,7 @@ proc count_tests_and_errors {name logfile} { } if {[regexp {runtime error: +(.*)} $line all msg]} { # skip over "value is outside range" errors - if {[regexp {value .* is outside the range of representable} $line]} { + if {[regexp {.* is outside the range of representable} $line]} { # noop } else { incr G(test.$name.nError) @@ -894,4 +894,3 @@ if {$G(noui)==0} { do_some_stuff vwait forever } - diff --git a/test/where.test b/test/where.test index 26bf3a0..9b072da 100644 --- a/test/where.test +++ b/test/where.test @@ -1496,8 +1496,8 @@ do_execsql_test where-25.0 { INSERT INTO t2 VALUES(3, 'three', 'iii'); PRAGMA writable_schema = 1; - UPDATE sqlite_master SET rootpage = ( - SELECT rootpage FROM sqlite_master WHERE name = 'i2' + UPDATE sqlite_schema SET rootpage = ( + SELECT rootpage FROM sqlite_schema WHERE name = 'i2' ) WHERE name = 'i1'; } db close @@ -1524,8 +1524,8 @@ do_execsql_test where-25.3 { INSERT INTO t2 VALUES(3, 'three', 'iii'); PRAGMA writable_schema = 1; - UPDATE sqlite_master SET rootpage = ( - SELECT rootpage FROM sqlite_master WHERE name = 'i2' + UPDATE sqlite_schema SET rootpage = ( + SELECT rootpage FROM sqlite_schema WHERE name = 'i2' ) WHERE name = 'i1'; } db close diff --git a/test/where9.test b/test/where9.test index b274609..0f770df 100644 --- a/test/where9.test +++ b/test/where9.test @@ -426,7 +426,7 @@ do_test where9-4.5 { AND (c=31031 OR d IS NULL) ORDER BY +a } -} {1 {no query solution}} +} {0 {92 93 97}} do_test where9-4.6 { count_steps { SELECT a FROM t1 NOT INDEXED @@ -442,7 +442,7 @@ do_test where9-4.7 { AND (c=31031 OR d IS NULL) ORDER BY +a } -} {1 {no query solution}} +} {0 {92 93 97}} do_test where9-4.8 { catchsql { SELECT a FROM t1 INDEXED BY t1d @@ -450,7 +450,7 @@ do_test where9-4.8 { AND (c=31031 OR d IS NULL) ORDER BY +a } -} {1 {no query solution}} +} {0 {92 93 97}} # The (c=31031 OR d IS NULL) clause is preferred over b>1000 because # the former is an equality test which is expected to return fewer rows. @@ -776,7 +776,7 @@ do_test where9-6.8.1 { OR (b NOT NULL AND c IS NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) } -} {1 {no query solution}} +} {0 {}} do_test where9-6.8.2 { catchsql { UPDATE t1 INDEXED BY t1b SET a=a+100 @@ -784,7 +784,7 @@ do_test where9-6.8.2 { OR (b NOT NULL AND c IS NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) } -} {1 {no query solution}} +} {0 {}} set solution_possible 0 ifcapable stat4 { @@ -818,7 +818,7 @@ if $solution_possible { OR (b NOT NULL AND c IS NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) } - } {1 {no query solution}} + } {0 {}} do_test where9-6.8.4 { catchsql { DELETE FROM t1 INDEXED BY t1b @@ -826,7 +826,7 @@ if $solution_possible { OR (b NOT NULL AND c IS NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) } - } {1 {no query solution}} + } {0 {}} } ############################################################################ # Test cases where terms inside an OR series are combined with AND terms diff --git a/test/whereD.test b/test/whereD.test index 8ced0ff..e727f47 100644 --- a/test/whereD.test +++ b/test/whereD.test @@ -337,6 +337,22 @@ do_searchcount_test 6.6.4 { SELECT c FROM x1 WHERE b=6 OR c=11 OR a=1 } {7 11 3 search 7} +# 2020-02-22 ticket aa4378693018aa99 +# In the OP_Column opcode, if a cursor is marked with OP_NullRow +# (because it is the right table of a LEFT JOIN that does not match) +# then do not substitute index cursors, as the index cursors do not +# have the VdbeCursor.nullRow flag set. +# +do_execsql_test 6.7 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a UNIQUE, b UNIQUE); + INSERT INTO t1(a,b) VALUES(null,2); + CREATE VIEW t2 AS SELECT * FROM t1 WHERE b<10 OR a<7 ORDER BY b; + SELECT t1.* FROM t1 LEFT JOIN t2 ON abs(t1.a)=abs(t2.b); +} {{} 2} + + #------------------------------------------------------------------------- # do_execsql_test 7.0 { diff --git a/test/whereG.test b/test/whereG.test index 9d4cde7..c6ae3ce 100644 --- a/test/whereG.test +++ b/test/whereG.test @@ -317,4 +317,15 @@ do_execsql_test 9.10 { SELECT coalesce(max(quote(a)),10) FROM t1 GROUP BY a; } {NULL '' 'X'} +# 2020-06-14: assert() changed back into testcase() +# ticket 9fb26d37cefaba40 +# +reset_db +do_execsql_test 10.1 { + CREATE TABLE a(b TEXT); INSERT INTO a VALUES(0),(4),(9); + CREATE TABLE c(d NUM); + CREATE VIEW f(g, h) AS SELECT b, 0 FROM a UNION SELECT d, d FROM c; + SELECT g = g FROM f GROUP BY h; +} {1} + finish_test diff --git a/test/whereL.test b/test/whereL.test index bd2f561..fbb424e 100644 --- a/test/whereL.test +++ b/test/whereL.test @@ -144,5 +144,51 @@ do_execsql_test 530 { SELECT 200, * FROM t0, v0 WHERE t0.c0 = 0 AND v0.c0 = t0.c0; } {} +# 2020-02-13: ticket 1dcb4d44964846ad +# A problem introduced while making optimizations on the fixes above. +# +reset_db +do_execsql_test 600 { + CREATE TABLE t1(x TEXT); + CREATE TABLE t2(y TEXT); + INSERT INTO t1 VALUES('good'),('bad'); + INSERT INTO t2 VALUES('good'),('bad'); + SELECT * FROM t1 JOIN t2 ON x=y + WHERE x='good' AND y='good'; +} {good good} + +# 2020-04-24: Another test case for the previous (1dcb4d44964846ad) +# ticket. The test case comes from +# https://stackoverflow.com/questions/61399253/sqlite3-different-result-in-console-compared-to-python-script/ +# Output verified against postgresql. +# +do_execsql_test 610 { + CREATE TABLE tableA( + ID int, + RunYearMonth int + ); + INSERT INTO tableA VALUES(1,202003),(2,202003),(3,202003),(4,202004), + (5,202004),(6,202004),(7,202004),(8,202004); + CREATE TABLE tableB ( + ID int, + RunYearMonth int + ); + INSERT INTO tableB VALUES(1,202004),(2,202004),(3,202004),(4,202004), + (5,202004); + SELECT * + FROM ( + SELECT * + FROM tableA + WHERE RunYearMonth = 202004 + ) AS A + INNER JOIN ( + SELECT * + FROM tableB + WHERE RunYearMonth = 202004 + ) AS B + ON A.ID = B.ID + AND A.RunYearMonth = B.RunYearMonth; +} {4 202004 4 202004 5 202004 5 202004} + finish_test diff --git a/test/wherelimit2.test b/test/wherelimit2.test index 83c04b1..8e39127 100644 --- a/test/wherelimit2.test +++ b/test/wherelimit2.test @@ -218,18 +218,22 @@ do_execsql_test 4.1 { ROLLBACK; } {3 4 5 6} -do_catchsql_test 4.2 { - DELETE FROM x1 INDEXED BY x1bc WHERE d=3 LIMIT 1; -} {1 {no query solution}} +# 2020-06-03: Query planner improved so that a solution is possible. +# +#do_catchsql_test 4.2 { +# DELETE FROM x1 INDEXED BY x1bc WHERE d=3 LIMIT 1; +#} {1 {no query solution}} do_execsql_test 4.3 { DELETE FROM x1 INDEXED BY x1bc WHERE b=3 LIMIT 1; SELECT a FROM x1; } {1 2 3 4 6} -do_catchsql_test 4.4 { - UPDATE x1 INDEXED BY x1bc SET d=5 WHERE d=3 LIMIT 1; -} {1 {no query solution}} +# 2020-06-03: Query planner improved so that a solution is possible. +# +#do_catchsql_test 4.4 { +# UPDATE x1 INDEXED BY x1bc SET d=5 WHERE d=3 LIMIT 1; +#} {1 {no query solution}} do_execsql_test 4.5 { UPDATE x1 INDEXED BY x1bc SET d=5 WHERE b=2 LIMIT 1; diff --git a/test/win32longpath.test b/test/win32longpath.test index 9e9ed35..01b4af7 100644 --- a/test/win32longpath.test +++ b/test/win32longpath.test @@ -24,7 +24,8 @@ do_test 1.0 { } win32 db close -set path [file nativename [get_pwd]] +set rawPath [get_pwd] +set path [file nativename $rawPath] sqlite3 db [file join $path test.db] -vfs win32-longpath do_test 1.1 { @@ -45,16 +46,32 @@ do_test 1.2 { } {1 2 3 4} set longPath(1) \\\\?\\$path\\[pid] +set uriPath(1a) %5C%5C%3F%5C$path\\[pid] +set uriPath(1b) %5C%5C%3F%5C$rawPath/[pid] + make_win32_dir $longPath(1) set longPath(2) $longPath(1)\\[string repeat X 255] +set uriPath(2a) $uriPath(1a)\\[string repeat X 255] +set uriPath(2b) $uriPath(1b)/[string repeat X 255] + make_win32_dir $longPath(2) set longPath(3) $longPath(2)\\[string repeat Y 255] +set uriPath(3a) $uriPath(2a)\\[string repeat Y 255] +set uriPath(3b) $uriPath(2b)/[string repeat Y 255] + make_win32_dir $longPath(3) set fileName $longPath(3)\\test.db +set uri(1a) file:$uriPath(3a)\\test.db +set uri(1b) file:$uriPath(3b)/test.db +set uri(1c) file:///$uriPath(3a)\\test.db +set uri(1d) file:///$uriPath(3b)/test.db +set uri(1e) file://localhost/$uriPath(3a)\\test.db +set uri(1f) file://localhost/$uriPath(3b)/test.db + do_test 1.3 { list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg } {1 {unable to open database file}} @@ -100,6 +117,18 @@ do_test 1.6 { db3 close # puts " Database exists \{[exists_win32_path $fileName]\}" +foreach tn {1a 1b 1c 1d 1e 1f} { + sqlite3 db3 $uri($tn) -vfs win32-longpath -uri 1 -translatefilename 0 + + do_test 1.7.$tn { + db3 eval { + SELECT x FROM t1 ORDER BY x; + } + } {5 6 7 8 9 10 11 12} + + db3 close +} + do_delete_win32_file $fileName # puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}" diff --git a/test/window1.test b/test/window1.test index 833e211..dbaf438 100644 --- a/test/window1.test +++ b/test/window1.test @@ -1593,5 +1593,412 @@ do_execsql_test 48.1 { FROM (SELECT (SELECT sum(a) FROM t1 GROUP BY a) AS x FROM t1); } {2 2 2} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 49.1 { + CREATE TABLE t1 (a PRIMARY KEY); + INSERT INTO t1 VALUES(1); +} + +do_execsql_test 49.2 { + SELECT b AS c FROM ( + SELECT a AS b FROM ( + SELECT a FROM t1 WHERE a=1 OR (SELECT sum(a) OVER ()) + ) + WHERE b=1 OR b<10 + ) + WHERE c=1 OR c>=10; +} {1} + + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 50.0 { + CREATE TABLE t1 (a DOUBLE PRIMARY KEY); + INSERT INTO t1 VALUES(10.0); +} + +do_execsql_test 50.1 { + SELECT * FROM t1 WHERE a%1 OR (SELECT sum(a) OVER (ORDER BY a%2)) +} {10.0} + +do_execsql_test 50.2 { + SELECT * FROM ( + SELECT * FROM t1 WHERE a%1 OR (SELECT sum(a) OVER (ORDER BY a%2)) + ) + WHERE a=1 OR ( (SELECT sum(a) OVER (ORDER BY a%4)) AND a<=10 ) +} {10.0} + +do_execsql_test 50.3 { + SELECT a FROM ( + SELECT * FROM ( + SELECT * FROM t1 WHERE a%1 OR (SELECT sum(a) OVER (ORDER BY a%2)) + ) + WHERE a=1 OR ( (SELECT sum(a) OVER (ORDER BY a%4)) AND a<=10 ) + ) + WHERE a=1 OR a=10.0 +} {10.0} + +do_execsql_test 50.4 { + SELECT a FROM ( + SELECT * FROM ( + SELECT * FROM t1 WHERE a%1 OR (SELECT sum(a) OVER (ORDER BY a%2)) + ) + WHERE a=1 OR ( (SELECT sum(a) OVER (ORDER BY a%4)) AND a<=10 ) + ) + WHERE a=1 OR ((SELECT sum(a) OVER(ORDER BY a%8)) AND 10<=a) +} {10.0} + +do_execsql_test 50.5 { +SELECT * FROM (SELECT * FROM t1 NATURAL JOIN t1 WHERE a%1 OR ((SELECT sum(a)OVER(ORDER BY a)) AND a<=10)) NATURAL JOIN t1 WHERE a=1 OR ((SELECT sum((SELECT * FROM (SELECT * FROM (SELECT * FROM t1 NATURAL JOIN t1 WHERE a%1 OR ((SELECT sum(a)OVER(ORDER BY a)) AND a<=10)) NATURAL JOIN t1 WHERE a=1 OR ((SELECT sum((SELECT * FROM t1 NATURAL JOIN t1 WHERE a=1 OR ((SELECT sum(a)OVER(ORDER BY a)) AND a<=10)))OVER(ORDER BY a% 1 )) AND a<=10)) NATURAL JOIN t1 WHERE a=1 OR ((SELECT sum(a)OVER(ORDER BY a)) AND 10<=a)))OVER(ORDER BY a%5)) AND a<=10); +} {10.0} + +# 2020-04-03 ticket af4556bb5c285c08 +# +reset_db +do_catchsql_test 51.1 { + CREATE TABLE a(b, c); + SELECT c FROM a GROUP BY c + HAVING(SELECT(sum(b) OVER(ORDER BY b), + sum(b) OVER(PARTITION BY min(DISTINCT c), c ORDER BY b))); +} {1 {row value misused}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 52.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES('AA','bb',356); + INSERT INTO t1 VALUES('CC','aa',158); + INSERT INTO t1 VALUES('BB','aa',399); + INSERT INTO t1 VALUES('FF','bb',938); +} + +do_execsql_test 52.2 { + SELECT + count() OVER win1, + sum(c) OVER win2, + first_value(c) OVER win2, + count(a) OVER (ORDER BY b) + FROM t1 + WINDOW + win1 AS (ORDER BY a), + win2 AS (PARTITION BY 6 ORDER BY a + RANGE BETWEEN 5 PRECEDING AND 0 PRECEDING ); +} { + 1 356 356 4 + 2 399 399 2 + 3 158 158 2 + 4 938 938 4 +} + +do_execsql_test 52.3 { +SELECT + count() OVER (), + sum(c) OVER win2, + first_value(c) OVER win2, + count(a) OVER (ORDER BY b) +FROM t1 +WINDOW + win1 AS (ORDER BY a), + win2 AS (PARTITION BY 6 COLLATE binary ORDER BY a + RANGE BETWEEN 5 PRECEDING AND 0 PRECEDING ); +} { + 4 356 356 4 + 4 399 399 2 + 4 158 158 2 + 4 938 938 4 +} + +do_execsql_test 52.4 { + SELECT + count() OVER win1, + sum(c) OVER win2, + first_value(c) OVER win2, + count(a) OVER (ORDER BY b) + FROM t1 + WINDOW + win1 AS (ORDER BY a), + win2 AS (PARTITION BY 6 COLLATE binary ORDER BY a + RANGE BETWEEN 5 PRECEDING AND 0 PRECEDING ); +} { + 1 356 356 4 + 2 399 399 2 + 3 158 158 2 + 4 938 938 4 +} + +# 2020-05-23 +# ticket 7a5279a25c57adf1 +# +reset_db +do_execsql_test 53.0 { + CREATE TABLE a(c UNIQUE); + INSERT INTO a VALUES(4),(0),(9),(-9); + SELECT a.c + FROM a + JOIN a AS b ON a.c=4 + JOIN a AS e ON a.c=e.c + WHERE a.c=(SELECT (SELECT coalesce(lead(2) OVER(),0) + sum(d.c)) + FROM a AS d + WHERE a.c); +} {4 4 4 4} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 54.1 { + CREATE TABLE t1(a VARCHAR(20), b FLOAT); + INSERT INTO t1 VALUES('1',10.0); +} + +do_catchsql_test 54.2 { + SELECT * FROM ( + SELECT sum(b) OVER() AS c FROM t1 + UNION + SELECT b AS c FROM t1 + ) WHERE c>10; +} {0 {}} + +do_execsql_test 54.3 { + INSERT INTO t1 VALUES('2',5.0); + INSERT INTO t1 VALUES('3',15.0); +} + +do_catchsql_test 54.4 { + SELECT * FROM ( + SELECT sum(b) OVER() AS c FROM t1 + UNION + SELECT b AS c FROM t1 + ) WHERE c>10; +} {0 {15.0 30.0}} + +# 2020-06-05 ticket c8d3b9f0a750a529 +reset_db +do_execsql_test 55.1 { + CREATE TABLE a(b); + SELECT + (SELECT b FROM a + GROUP BY b + HAVING (SELECT COUNT()OVER() + lead(b)OVER(ORDER BY SUM(DISTINCT b) + b)) + ) + FROM a + UNION + SELECT 99 + ORDER BY 1; +} {99} + +#------------------------------------------------------------------------ +reset_db +do_execsql_test 56.1 { + CREATE TABLE t1(a, b INTEGER); + CREATE TABLE t2(c, d); +} +do_catchsql_test 56.2 { + SELECT avg(b) FROM t1 + UNION ALL + SELECT min(c) OVER () FROM t2 + ORDER BY nosuchcolumn; +} {1 {1st ORDER BY term does not match any column in the result set}} + +reset_db +do_execsql_test 57.1 { + CREATE TABLE t4(a, b, c, d, e); +} + +do_catchsql_test 57.2 { + SELECT b FROM t4 + UNION + SELECT a FROM t4 + ORDER BY ( + SELECT sum(x) OVER() FROM ( + SELECT c AS x FROM t4 + UNION + SELECT d FROM t4 + ORDER BY (SELECT e FROM t4) + ) + ); +} {1 {1st ORDER BY term does not match any column in the result set}} + +# 2020-06-06 various dbsqlfuzz finds and +# ticket 0899cf62f597d7e7 +# +reset_db +do_execsql_test 57.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES(NULL,NULL,NULL); + SELECT + sum(a), + min(b) OVER (), + count(c) OVER (ORDER BY b) + FROM t1; +} {{} {} 0} +do_execsql_test 57.2 { + CREATE TABLE v0 ( v1 INTEGER PRIMARY KEY ) ; + INSERT INTO v0 VALUES ( 10 ) ; + SELECT DISTINCT v1, lead(v1) OVER() FROM v0 GROUP BY v1 ORDER BY 2; +} {10 {}} +do_catchsql_test 57.3 { + DROP TABLE t1; + CREATE TABLE t1(a); + INSERT INTO t1(a) VALUES(22); + CREATE TABLE t3(y); + INSERT INTO t3(y) VALUES(5),(11),(-9); + SELECT ( + SELECT max(y) OVER( ORDER BY (SELECT x FROM (SELECT sum(y) AS x FROM t1))) + ) + FROM t3; +} {1 {misuse of aggregate: sum()}} + +# 2020-06-06 ticket 1f6f353b684fc708 +reset_db +do_execsql_test 58.1 { + CREATE TABLE a(a, b, c); + INSERT INTO a VALUES(1, 2, 3); + INSERT INTO a VALUES(4, 5, 6); + SELECT sum(345+b) OVER (ORDER BY b), + sum(avg(678)) OVER (ORDER BY c) FROM a; +} {347 678.0} + +# 2020-06-06 ticket e5504e987e419fb0 +do_catchsql_test 59.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(x INTEGER PRIMARY KEY); + INSERT INTO t1 VALUES (123); + SELECT + ntile( (SELECT sum(x)) ) OVER(ORDER BY x), + min(x) OVER(ORDER BY x) + FROM t1; +} {1 {misuse of aggregate: sum()}} + +# 2020-06-07 ticket f7d890858f361402 +do_execsql_test 60.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1 (x INTEGER PRIMARY KEY); + INSERT INTO t1 VALUES (99); + SELECT EXISTS(SELECT count(*) OVER() FROM t1 ORDER BY sum(x) OVER()); +} {1} + +# 2020-06-07 test case generated by dbsqlfuzz showing how an AggInfo +# object might be referenced after the sqlite3Select() call that created +# it returns. This proves the need to persist all AggInfo objects until +# the Parse object is destroyed. +# +reset_db +do_execsql_test 61.1 { +CREATE TABLE t1(a); +INSERT INTO t1 VALUES(5),(NULL),('seventeen'); +SELECT (SELECT max(x)OVER(ORDER BY x) % min(x)OVER(ORDER BY CASE x WHEN 889 THEN x WHEN x THEN x END)) FROM (SELECT (SELECT sum(CAST(a IN(SELECT (SELECT max(x)OVER(ORDER BY CASE x WHEN 889 THEN 299 WHEN 863 THEN 863 END)) FROM (SELECT (SELECT sum(CAST((SELECT (SELECT max(x)OVER(ORDER BY x) / min(x)OVER(ORDER BY CASE x WHEN 889 THEN 299 WHEN -true THEN 863 END)) FROM (SELECT (SELECT sum(CAST(a IN(SELECT (SELECT max(x) & sum ( a )OVER(ORDER BY CASE x WHEN -8 THEN 299 WHEN 863 THEN 863 END)) FROM (SELECT (SELECT sum(CAST(a AS )) FROM t1) AS x FROM t1)) AS t1 )) FROM t1) AS x FROM t1)) AS x )) FROM t1) AS x FROM t1)) AS real)) FROM t1) AS x FROM t1); +} {{} {} {}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 62.1 { + CREATE TABLE t1(a VARCHAR(20), b FLOAT); + INSERT INTO t1 VALUES('1',10.0); +} + +do_execsql_test 62.2 { + SELECT * FROM ( + SELECT sum(b) OVER() AS c FROM t1 + UNION + SELECT b AS c FROM t1 + ) WHERE c>10; +} + +do_execsql_test 62.3 { + INSERT INTO t1 VALUES('2',5.0); + INSERT INTO t1 VALUES('3',15.0); +} + +do_execsql_test 62.4 { + SELECT * FROM ( + SELECT sum(b) OVER() AS c FROM t1 + UNION + SELECT b AS c FROM t1 + ) WHERE c>10; +} {15.0 30.0} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 63.1 { + CREATE TABLE t1(b, x); + CREATE TABLE t2(c, d); + CREATE TABLE t3(e, f); +} + +do_execsql_test 63.2 { + SELECT max(b) OVER( + ORDER BY SUM( + (SELECT c FROM t2 UNION SELECT x ORDER BY c) + ) + ) FROM t1; +} {{}} + +do_execsql_test 63.3 { + SELECT sum(b) over( + ORDER BY ( + SELECT max(b) OVER( + ORDER BY sum( + (SELECT x AS c UNION SELECT 1234 ORDER BY c) + ) + ) AS e + ORDER BY e + ) + ) + FROM t1; +} {{}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 64.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'abcd'); + INSERT INTO t1 VALUES(2, 'BCDE'); + INSERT INTO t1 VALUES(3, 'cdef'); + INSERT INTO t1 VALUES(4, 'DEFG'); +} + +do_execsql_test 64.2 { + SELECT rowid, max(b COLLATE nocase)||'' + FROM t1 + GROUP BY rowid + ORDER BY max(b COLLATE nocase)||''; +} {1 abcd 2 BCDE 3 cdef 4 DEFG} + +do_execsql_test 64.3 { + SELECT count() OVER (), rowid, max(b COLLATE nocase)||'' + FROM t1 + GROUP BY rowid + ORDER BY max(b COLLATE nocase)||''; +} {4 1 abcd 4 2 BCDE 4 3 cdef 4 4 DEFG} + +do_execsql_test 64.4 { + SELECT count() OVER (), rowid, max(b COLLATE nocase) + FROM t1 + GROUP BY rowid + ORDER BY max(b COLLATE nocase); +} {4 1 abcd 4 2 BCDE 4 3 cdef 4 4 DEFG} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 65.1 { + CREATE TABLE t1(c1); + INSERT INTO t1 VALUES('abcd'); +} +do_execsql_test 65.2 { + SELECT max(c1 COLLATE nocase) IN (SELECT 'aBCd') FROM t1; +} {1} + +do_execsql_test 65.3 { + SELECT + count() OVER (), + group_concat(c1 COLLATE nocase) IN (SELECT 'aBCd') FROM t1; +} {1 1} + +do_execsql_test 65.4 { + SELECT COUNT() OVER () LIKE lead(102030) OVER( + ORDER BY sum('abcdef' COLLATE nocase) IN (SELECT 54321) + ) + FROM t1; +} {{}} finish_test diff --git a/test/window4.tcl b/test/window4.tcl index 1b2b2ef..0b91d76 100644 --- a/test/window4.tcl +++ b/test/window4.tcl @@ -385,6 +385,20 @@ execsql_test 11.4 { ) sub; } +execsql_test 11.5 { + SELECT sum( min(t) ) OVER () FROM t8 GROUP BY total; +} +execsql_test 11.5 { + SELECT sum( max(t) ) OVER () FROM t8 GROUP BY total; +} + +execsql_test 11.7 { + SELECT sum( min(t) ) OVER () FROM t8; +} +execsql_test 11.8 { + SELECT sum( max(t) ) OVER () FROM t8; +} + execsql_test 12.0 { DROP TABLE IF EXISTS t2; CREATE TABLE t2(a INTEGER); diff --git a/test/window4.test b/test/window4.test index 6951a23..4194f75 100644 --- a/test/window4.test +++ b/test/window4.test @@ -1324,6 +1324,22 @@ do_execsql_test 11.4 { ) sub; } {0 1 2} +do_execsql_test 11.5 { + SELECT sum( min(t) ) OVER () FROM t8 GROUP BY total; +} {5 5} + +do_execsql_test 11.5 { + SELECT sum( max(t) ) OVER () FROM t8 GROUP BY total; +} {10 10} + +do_execsql_test 11.7 { + SELECT sum( min(t) ) OVER () FROM t8; +} {0} + +do_execsql_test 11.8 { + SELECT sum( max(t) ) OVER () FROM t8; +} {10} + do_execsql_test 12.0 { DROP TABLE IF EXISTS t2; CREATE TABLE t2(a INTEGER); diff --git a/test/window9.test b/test/window9.test index adfeaba..c342a4d 100644 --- a/test/window9.test +++ b/test/window9.test @@ -232,4 +232,37 @@ do_execsql_test 7.4 { 7.2 8.75 10.0 11.0 15.0 } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 8.1.1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2), (3, 4); + SELECT min( sum(a) ) OVER () FROM t1; +} {4} + +do_execsql_test 8.1.2 { + SELECT min( sum(a) ) OVER () FROM t1 GROUP BY a; +} {1 1} + +do_execsql_test 8.2 { + CREATE VIEW v1 AS + SELECT 0 AS x + UNION + SELECT count() OVER() FROM (SELECT 0) + ORDER BY 1 + ; +} + +do_catchsql_test 8.3 { + SELECT min( max((SELECT x FROM v1)) ) OVER() +} {0 0} + +do_execsql_test 8.4 { + SELECT( + SELECT x UNION + SELECT sum( avg((SELECT x FROM v1)) ) OVER() + ) + FROM v1; +} {0.0 0.0} + finish_test diff --git a/test/windowfault.test b/test/windowfault.test index e97544f..aea2340 100644 --- a/test/windowfault.test +++ b/test/windowfault.test @@ -263,4 +263,15 @@ do_faultsim_test 11 -faults oom* -prep { faultsim_test_result {0 {}} } +do_faultsim_test 11 -faults oom* -prep { +} -body { + execsql { + VALUES(false),(current_date collate binary) + intersect + values(count() not like group_concat(cast(cast(0e00 as text) as integer) <= NULL || 0.4e-0 || 0x8 & true ) over () collate rtrim); + } +} -test { + faultsim_test_result {0 {}} +} + finish_test diff --git a/test/without_rowid3.test b/test/without_rowid3.test index 24ef230..eae7e3c 100644 --- a/test/without_rowid3.test +++ b/test/without_rowid3.test @@ -921,6 +921,7 @@ ifcapable altertable { execsql { CREATE TABLE t1(a PRIMARY KEY) WITHOUT rowid; CREATE TABLE t2(a, b); + INSERT INTO t2(a,b) VALUES(1,2); } catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } } {0 {}} @@ -941,7 +942,7 @@ ifcapable altertable { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; - SELECT sql FROM sqlite_master WHERE name='t2'; + SELECT sql FROM sqlite_schema WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} @@ -975,7 +976,7 @@ ifcapable altertable { WITHOUT rowid; CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1); } - execsql { SELECT sql FROM sqlite_master WHERE type = 'table'} + execsql { SELECT sql FROM sqlite_schema WHERE type = 'table'} } [list \ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2) @@ -984,7 +985,7 @@ ifcapable altertable { ] do_test without_rowid3-14.2.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } - execsql { SELECT sql FROM sqlite_master WHERE type = 'table'} + execsql { SELECT sql FROM sqlite_schema WHERE type = 'table'} } [list \ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2) @@ -1015,6 +1016,7 @@ ifcapable altertable { execsql { CREATE TEMP TABLE t1(a PRIMARY KEY) WITHOUT rowid; CREATE TEMP TABLE t2(a, b); + INSERT INTO temp.t2(a,b) VALUES(1,2); } catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } } {0 {}} @@ -1035,7 +1037,7 @@ ifcapable altertable { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; - SELECT sql FROM temp.sqlite_master WHERE name='t2'; + SELECT sql FROM temp.sqlite_schema WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} @@ -1061,7 +1063,7 @@ ifcapable altertable { WITHOUT rowid; CREATE TEMP TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1); } - execsql { SELECT sql FROM sqlite_temp_master WHERE type = 'table'} + execsql { SELECT sql FROM sqlite_temp_schema WHERE type = 'table'} } [list \ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2) @@ -1070,7 +1072,7 @@ ifcapable altertable { ] do_test without_rowid3-14.2tmp.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } - execsql { SELECT sql FROM temp.sqlite_master WHERE type = 'table'} + execsql { SELECT sql FROM temp.sqlite_schema WHERE type = 'table'} } [list \ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2) @@ -1102,6 +1104,7 @@ ifcapable altertable { ATTACH ':memory:' AS aux; CREATE TABLE aux.t1(a PRIMARY KEY) WITHOUT rowid; CREATE TABLE aux.t2(a, b); + INSERT INTO aux.t2(a,b) VALUES(1,2); } catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 } } {0 {}} @@ -1122,7 +1125,7 @@ ifcapable altertable { PRAGMA foreign_keys = off; ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1; PRAGMA foreign_keys = on; - SELECT sql FROM aux.sqlite_master WHERE name='t2'; + SELECT sql FROM aux.sqlite_schema WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} @@ -1148,7 +1151,7 @@ ifcapable altertable { WITHOUT rowid; CREATE TABLE aux.t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1); } - execsql { SELECT sql FROM aux.sqlite_master WHERE type = 'table'} + execsql { SELECT sql FROM aux.sqlite_schema WHERE type = 'table'} } [list \ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2) @@ -1157,7 +1160,7 @@ ifcapable altertable { ] do_test without_rowid3-14.2aux.2.2 { execsql { ALTER TABLE t1 RENAME TO t4 } - execsql { SELECT sql FROM aux.sqlite_master WHERE type = 'table'} + execsql { SELECT sql FROM aux.sqlite_schema WHERE type = 'table'} } [list \ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2) diff --git a/tool/dbhash.c b/tool/dbhash.c index 7696ddb..78685dc 100644 --- a/tool/dbhash.c +++ b/tool/dbhash.c @@ -440,7 +440,7 @@ int main(int argc, char **argv){ fprintf(stderr, "cannot open database file '%s'\n", zDb); continue; } - rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, &zErrMsg); + rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_schema", 0, 0, &zErrMsg); if( rc || zErrMsg ){ sqlite3_close(g.db); g.db = 0; @@ -454,7 +454,7 @@ int main(int argc, char **argv){ /* Hash table content */ if( !omitContent ){ pStmt = db_prepare( - "SELECT name FROM sqlite_master\n" + "SELECT name FROM sqlite_schema\n" " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n" " AND name NOT LIKE 'sqlite_%%'\n" " AND name LIKE '%q'\n" @@ -476,7 +476,7 @@ int main(int argc, char **argv){ /* Hash the database schema */ if( !omitSchema ){ hash_one_query( - "SELECT type, name, tbl_name, sql FROM sqlite_master\n" + "SELECT type, name, tbl_name, sql FROM sqlite_schema\n" " WHERE tbl_name LIKE '%q'\n" " ORDER BY name COLLATE nocase;\n", zLike diff --git a/tool/enlargedb.c b/tool/enlargedb.c new file mode 100644 index 0000000..dab5ef1 --- /dev/null +++ b/tool/enlargedb.c @@ -0,0 +1,68 @@ +/* +** Try to enlarge an SQLite database by appending many unused pages. +** The resulting database will fail PRAGMA integrity_check due to the +** appended unused pages, but it should work otherwise. +** +** Usage: +** +** enlargedb DATABASE N +** +** Adds N blank pages onto the end of DATABASE. N can be decimal +** or hex. The total number of pages after adding must be no greater +** than 4294967297 +*/ +#include +#include +#include + +int main(int argc, char **argv){ + char *zEnd; + long long int toAppend; + long long int currentSz; + long long int newSz; + FILE *f; + size_t got; + int pgsz; + char zero = 0; + unsigned char buf[100]; + + if( argc!=3 ) goto usage_error; + toAppend = strtoll(argv[2], &zEnd, 0); + if( zEnd==argv[2] || zEnd[0] ) goto usage_error; + if( toAppend<1 ){ + fprintf(stderr, "N must be at least 1\n"); + exit(1); + } + f = fopen(argv[1], "r+b"); + if( f==0 ){ + fprintf(stderr, "cannot open \"%s\" for reading and writing\n", argv[1]); + exit(1); + } + got = fread(buf, 1, sizeof(buf), f); + if( got!=sizeof(buf) ) goto not_valid_db; + if( strcmp((char*)buf,"SQLite format 3")!=0 ) goto not_valid_db; + pgsz = (buf[16]<<8) + buf[17]; + if( pgsz==1 ) pgsz = 65536; + if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto not_valid_db; + currentSz = (buf[28]<<24) + (buf[29]<<16) + (buf[30]<<8) + buf[31]; + newSz = currentSz + toAppend; + if( newSz > 0xffffffff ) newSz = 0xffffffff; + buf[28] = (newSz>>24) & 0xff; + buf[29] = (newSz>>16) & 0xff; + buf[30] = (newSz>>8) & 0xff; + buf[31] = newSz & 0xff; + fseek(f, 28, SEEK_SET); + fwrite(&buf[28],4,1,f); + fseek(f, (long)(newSz*pgsz - 1), SEEK_SET); + fwrite(&zero,1,1,f); + fclose(f); + return 0; + +not_valid_db: + fprintf(stderr,"not a valid database: %s\n", argv[1]); + exit(1); + +usage_error: + fprintf(stderr,"Usage: %s DATABASE N\n", argv[0]); + exit(1); +} diff --git a/tool/fast_vacuum.c b/tool/fast_vacuum.c index 6a50dcc..5ca0271 100644 --- a/tool/fast_vacuum.c +++ b/tool/fast_vacuum.c @@ -150,7 +150,7 @@ int main(int argc, char **argv){ */ /* The vacuum will occur inside of a transaction. Set writable_schema - ** to ON so that we can directly update the sqlite_master table in the + ** to ON so that we can directly update the sqlite_schema table in the ** zTempDb database. */ execSql(db, "PRAGMA writable_schema=ON"); @@ -162,16 +162,16 @@ int main(int argc, char **argv){ */ execExecSql(db, "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) " - " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" + " FROM sqlite_schema WHERE type='table' AND name!='sqlite_sequence'" " AND rootpage>0" ); execExecSql(db, "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)" - " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %'" + " FROM sqlite_schema WHERE sql LIKE 'CREATE INDEX %'" ); execExecSql(db, "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) " - " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'" + " FROM sqlite_schema WHERE sql LIKE 'CREATE UNIQUE INDEX %'" ); /* Loop through the tables in the main database. For each, do @@ -181,7 +181,7 @@ int main(int argc, char **argv){ execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM main.' || quote(name) " - "FROM main.sqlite_master " + "FROM main.sqlite_schema " "WHERE type = 'table' AND name!='sqlite_sequence' " " AND rootpage>0" ); @@ -190,12 +190,12 @@ int main(int argc, char **argv){ */ execExecSql(db, "SELECT 'DELETE FROM vacuum_db.' || quote(name) " - "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence'" + "FROM vacuum_db.sqlite_schema WHERE name='sqlite_sequence'" ); execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM main.' || quote(name) " - "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence'" + "FROM vacuum_db.sqlite_schema WHERE name=='sqlite_sequence'" ); /* Copy the triggers, views, and virtual tables from the main database @@ -204,9 +204,9 @@ int main(int argc, char **argv){ ** from the SQLITE_MASTER table. */ execSql(db, - "INSERT INTO vacuum_db.sqlite_master " + "INSERT INTO vacuum_db.sqlite_schema " " SELECT type, name, tbl_name, rootpage, sql" - " FROM main.sqlite_master" + " FROM main.sqlite_schema" " WHERE type='view' OR type='trigger'" " OR (type='table' AND rootpage=0)" ); diff --git a/tool/index_usage.c b/tool/index_usage.c index 451fa65..9bd3c9f 100644 --- a/tool/index_usage.c +++ b/tool/index_usage.c @@ -104,7 +104,7 @@ int main(int argc, char **argv){ printf("Cannot open \"%s\" for reading: %s\n", argv[1], sqlite3_errmsg(db)); goto errorOut; } - rc = sqlite3_prepare_v2(db, "SELECT * FROM sqlite_master", -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, "SELECT * FROM sqlite_schema", -1, &pStmt, 0); if( rc ){ printf("Cannot read the schema from \"%s\" - %s\n", argv[1], sqlite3_errmsg(db)); @@ -126,7 +126,7 @@ int main(int argc, char **argv){ } rc = sqlite3_exec(db, "INSERT INTO temp.idxu(tbl,idx,cnt)" - " SELECT tbl_name, name, 0 FROM sqlite_master" + " SELECT tbl_name, name, 0 FROM sqlite_schema" " WHERE type='index' AND sql IS NOT NULL", 0, 0, 0); /* Open the LOG database */ @@ -205,9 +205,9 @@ int main(int argc, char **argv){ rc = sqlite3_prepare_v2(db, "SELECT tbl, idx, cnt, " " (SELECT group_concat(name,',') FROM pragma_index_info(idx))" - " FROM temp.idxu, main.sqlite_master" - " WHERE temp.idxu.tbl=main.sqlite_master.tbl_name" - " AND temp.idxu.idx=main.sqlite_master.name" + " FROM temp.idxu, main.sqlite_schema" + " WHERE temp.idxu.tbl=main.sqlite_schema.tbl_name" + " AND temp.idxu.idx=main.sqlite_schema.name" " ORDER BY cnt DESC, tbl, idx", -1, &pStmt, 0); if( rc ){ diff --git a/tool/lemon.c b/tool/lemon.c index 8dcf651..40e4e28 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -423,6 +423,7 @@ struct lemon { int nlookaheadtab; /* Number of entries in yy_lookahead[] */ int tablesize; /* Total table size of all tables in bytes */ int basisflag; /* Print only basis configurations */ + int printPreprocessed; /* Show preprocessor output on stdout */ int has_fallback; /* True if any %fallback is seen in the grammar */ int nolinenosflag; /* True if #line statements should not be printed */ char *argv0; /* Name of the program */ @@ -1636,12 +1637,14 @@ int main(int argc, char **argv) static int nolinenosflag = 0; static int noResort = 0; static int sqlFlag = 0; + static int printPP = 0; static struct s_options options[] = { {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, {OPT_FSTR, "d", (char*)&handle_d_option, "Output directory. Default '.'"}, {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FLAG, "E", (char*)&printPP, "Print input file after preprocessing."}, {OPT_FSTR, "f", 0, "Ignored. (Placeholder for -f compiler options.)"}, {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, {OPT_FSTR, "I", 0, "Ignored. (Placeholder for '-I' compiler options.)"}, @@ -1686,11 +1689,12 @@ int main(int argc, char **argv) lem.filename = OptArg(0); lem.basisflag = basisflag; lem.nolinenosflag = nolinenosflag; + lem.printPreprocessed = printPP; Symbol_new("$"); /* Parse the input file */ Parse(&lem); - if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.printPreprocessed || lem.errorcnt ) exit(lem.errorcnt); if( lem.nrule==0 ){ fprintf(stderr,"Empty grammar.\n"); exit(1); @@ -2779,13 +2783,108 @@ static void parseonetoken(struct pstate *psp) } } +/* The text in the input is part of the argument to an %ifdef or %ifndef. +** Evaluate the text as a boolean expression. Return true or false. +*/ +static int eval_preprocessor_boolean(char *z, int lineno){ + int neg = 0; + int res = 0; + int okTerm = 1; + int i; + for(i=0; z[i]!=0; i++){ + if( ISSPACE(z[i]) ) continue; + if( z[i]=='!' ){ + if( !okTerm ) goto pp_syntax_error; + neg = !neg; + continue; + } + if( z[i]=='|' && z[i+1]=='|' ){ + if( okTerm ) goto pp_syntax_error; + if( res ) return 1; + i++; + okTerm = 1; + continue; + } + if( z[i]=='&' && z[i+1]=='&' ){ + if( okTerm ) goto pp_syntax_error; + if( !res ) return 0; + i++; + okTerm = 1; + continue; + } + if( z[i]=='(' ){ + int k; + int n = 1; + if( !okTerm ) goto pp_syntax_error; + for(k=i+1; z[k]; k++){ + if( z[k]==')' ){ + n--; + if( n==0 ){ + z[k] = 0; + res = eval_preprocessor_boolean(&z[i+1], -1); + z[k] = ')'; + if( res<0 ){ + i = i-res; + goto pp_syntax_error; + } + i = k; + break; + } + }else if( z[k]=='(' ){ + n++; + }else if( z[k]==0 ){ + i = k; + goto pp_syntax_error; + } + } + if( neg ){ + res = !res; + neg = 0; + } + okTerm = 0; + continue; + } + if( ISALPHA(z[i]) ){ + int j, k, n; + if( !okTerm ) goto pp_syntax_error; + for(k=i+1; ISALNUM(z[k]) || z[k]=='_'; k++){} + n = k - i; + res = 0; + for(j=0; j0 ){ + fprintf(stderr, "%%if syntax error on line %d.\n", lineno); + fprintf(stderr, " %.*s <-- syntax error here\n", i+1, z); + exit(1); + }else{ + return -(i+1); + } +} + /* Run the preprocessor over the input file text. The global variables ** azDefine[0] through azDefine[nDefine-1] contains the names of all defined ** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and ** comments them out. Text in between is also commented out as appropriate. */ static void preprocess_input(char *z){ - int i, j, k, n; + int i, j, k; int exclude = 0; int start = 0; int lineno = 1; @@ -2801,21 +2900,33 @@ static void preprocess_input(char *z){ } } for(j=i; z[j] && z[j]!='\n'; j++) z[j] = ' '; - }else if( (strncmp(&z[i],"%ifdef",6)==0 && ISSPACE(z[i+6])) - || (strncmp(&z[i],"%ifndef",7)==0 && ISSPACE(z[i+7])) ){ + }else if( strncmp(&z[i],"%else",5)==0 && ISSPACE(z[i+5]) ){ + if( exclude==1){ + exclude = 0; + for(j=start; jprintPreprocessed ){ + printf("%s\n", filebuf); + return; + } /* Now scan the text of the input file */ lineno = 1; diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh index 7cd7da3..eacd9fa 100644 --- a/tool/mkautoconfamal.sh +++ b/tool/mkautoconfamal.sh @@ -2,8 +2,8 @@ # This script is used to build the amalgamation autoconf package. # It assumes the following: # -# 1. The files "sqlite3.c", "sqlite3.h" and "sqlite3ext.h" -# are available in the current directory. +# 1. The files "sqlite3.c", "sqlite3.h", "sqlite3ext.h", "shell.c", +# and "sqlite3rc.h" are available in the current directory. # # 2. Variable $TOP is set to the full path of the root directory # of the SQLite source tree. @@ -49,6 +49,7 @@ cp -R $TOP/autoconf $TMPSPACE cp sqlite3.c $TMPSPACE cp sqlite3.h $TMPSPACE cp sqlite3ext.h $TMPSPACE +cp sqlite3rc.h $TMPSPACE cp $TOP/sqlite3.1 $TMPSPACE cp $TOP/sqlite3.pc.in $TMPSPACE cp shell.c $TMPSPACE diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c index e4393e8..83ec179 100644 --- a/tool/mkkeywordhash.c +++ b/tool/mkkeywordhash.c @@ -652,12 +652,17 @@ int main(int argc, char **argv){ bestSize); printf(" for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){\n"); printf(" if( aKWLen[i]!=n ) continue;\n"); - printf(" j = 0;\n"); printf(" zKW = &zKWText[aKWOffset[i]];\n"); printf("#ifdef SQLITE_ASCII\n"); + printf(" if( (z[0]&~0x20)!=zKW[0] ) continue;\n"); + printf(" if( (z[1]&~0x20)!=zKW[1] ) continue;\n"); + printf(" j = 2;\n"); printf(" while( j]+)[">]} $line all hdr]} { if {[info exists available_hdr($hdr)]} { @@ -351,6 +351,7 @@ foreach file { vdbe.c vdbeblob.c vdbesort.c + vdbevtab.c memjournal.c walker.c diff --git a/tool/mksqlite3h.tcl b/tool/mksqlite3h.tcl index 216bd4e..9078a15 100644 --- a/tool/mksqlite3h.tcl +++ b/tool/mksqlite3h.tcl @@ -107,7 +107,7 @@ foreach file $filelist { } while {![eof $in]} { - set line [gets $in] + set line [string trimright [gets $in]] # File sqlite3rtree.h contains a line "#include ". Omit this # line when copying sqlite3rtree.h into sqlite3.h. diff --git a/tool/offsets.c b/tool/offsets.c index 8e098e7..26ee9fc 100644 --- a/tool/offsets.c +++ b/tool/offsets.c @@ -75,7 +75,7 @@ static void ofstRootAndColumn( ofstError(p, "cannot open database file \"%s\"", zFile); goto rootAndColumn_exit; } - zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master WHERE name=%Q", + zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_schema WHERE name=%Q", zTable); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ) ofstError(p, "%s: [%s]", sqlite3_errmsg(db), zSql); diff --git a/tool/showdb.c b/tool/showdb.c index fe4e9ac..09c4d9d 100644 --- a/tool/showdb.c +++ b/tool/showdb.c @@ -20,21 +20,22 @@ #include #include "sqlite3.h" +typedef unsigned char u8; /* unsigned 8-bit */ +typedef unsigned int u32; /* unsigned 32-bit */ +typedef sqlite3_int64 i64; /* signed 64-bit */ +typedef sqlite3_uint64 u64; /* unsigned 64-bit */ + static struct GlobalData { - int pagesize; /* Size of a database page */ + u32 pagesize; /* Size of a database page */ int dbfd; /* File descriptor for reading the DB */ - int mxPage; /* Last page number */ + u32 mxPage; /* Last page number */ int perLine; /* HEX elements to print per line */ int bRaw; /* True to access db file via OS APIs */ sqlite3_file *pFd; /* File descriptor for non-raw mode */ sqlite3 *pDb; /* Database handle that owns pFd */ } g = {1024, -1, 0, 16, 0, 0, 0}; - -typedef long long int i64; /* Datatype for 64-bit integers */ - - /* ** Convert the var-int format into i64. Return the number of bytes ** in the var-int. Write the var-int value into *pVal. @@ -54,7 +55,7 @@ static int decodeVarint(const unsigned char *z, i64 *pVal){ /* ** Extract a big-endian 32-bit integer */ -static unsigned int decodeInt32(const unsigned char *z){ +static u32 decodeInt32(const u8 *z){ return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3]; } @@ -141,7 +142,7 @@ static void fileClose(){ static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){ unsigned char *aData; int got; - aData = sqlite3_malloc(nByte+32); + aData = sqlite3_malloc64(32+(i64)nByte); if( aData==0 ) out_of_memory(); memset(aData, 0, nByte+32); if( g.bRaw==0 ){ @@ -161,8 +162,8 @@ static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){ /* ** Return the size of the file in byte. */ -static sqlite3_int64 fileGetsize(void){ - sqlite3_int64 res = 0; +static i64 fileGetsize(void){ + i64 res = 0; if( g.bRaw==0 ){ int rc = g.pFd->pMethods->xFileSize(g.pFd, &res); if( rc!=SQLITE_OK ){ @@ -185,9 +186,9 @@ static sqlite3_int64 fileGetsize(void){ ** Print a range of bytes as hex and as ascii. */ static unsigned char *print_byte_range( - int ofst, /* First byte in the range of bytes to print */ - int nByte, /* Number of bytes to print */ - int printOfst /* Add this amount to the index on the left column */ + sqlite3_int64 ofst, /* First byte in the range of bytes to print */ + int nByte, /* Number of bytes to print */ + int printOfst /* Add this amount to the index on the left column */ ){ unsigned char *aData; int i, j; @@ -207,6 +208,12 @@ static unsigned char *print_byte_range( aData = fileRead(ofst, nByte); for(i=0; inByte ){ break; } + if( aData[i+j] ){ go = 1; break; } + } + if( !go && i>0 && i+g.perLinenByte ){ @@ -230,18 +237,18 @@ static unsigned char *print_byte_range( /* ** Print an entire page of content as hex */ -static void print_page(int iPg){ - int iStart; +static void print_page(u32 iPg){ + i64 iStart; unsigned char *aData; - iStart = (iPg-1)*g.pagesize; - fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n", + iStart = ((i64)(iPg-1))*g.pagesize; + fprintf(stdout, "Page %u: (offsets 0x%llx..0x%llx)\n", iPg, iStart, iStart+g.pagesize-1); aData = print_byte_range(iStart, g.pagesize, 0); sqlite3_free(aData); } -/* Print a line of decode output showing a 4-byte integer. +/* Print a line of decoded output showing a 4-byte unsigned integer. */ static void print_decode_line( unsigned char *aData, /* Content being decoded */ @@ -249,7 +256,7 @@ static void print_decode_line( const char *zMsg /* Message to append */ ){ int i, j; - int val = aData[ofst]; + u32 val = aData[ofst]; char zBuf[100]; sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]); i = (int)strlen(zBuf); @@ -262,7 +269,7 @@ static void print_decode_line( } i += (int)strlen(&zBuf[i]); } - sprintf(&zBuf[i], " %9d", val); + sprintf(&zBuf[i], " %10u", val); printf("%s %s\n", zBuf, zMsg); } @@ -296,6 +303,7 @@ static void print_db_header(void){ print_decode_line(aData, 88, 4, "meta[12]"); print_decode_line(aData, 92, 4, "Change counter for version number"); print_decode_line(aData, 96, 4, "SQLite version number"); + sqlite3_free(aData); } /* @@ -408,7 +416,7 @@ static i64 describeCell( int i; i64 nDesc = 0; int n = 0; - int leftChild; + u32 leftChild; i64 nPayload; i64 rowid; i64 nLocal; @@ -418,7 +426,7 @@ static i64 describeCell( leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3]; a += 4; n += 4; - sprintf(zDesc, "lx: %d ", leftChild); + sprintf(zDesc, "lx: %u ", leftChild); nDesc = strlen(zDesc); } if( cType!=5 ){ @@ -439,10 +447,10 @@ static i64 describeCell( nDesc += strlen(&zDesc[nDesc]); } if( nLocal0 ){ a = fileRead((pgno-1)*g.pagesize, g.pagesize); @@ -741,9 +750,9 @@ static void decode_trunk_page( print_decode_line(a, 0, 4, "Next freelist trunk page"); print_decode_line(a, 4, 4, "Number of entries on this page"); if( detail ){ - n = (int)decodeInt32(&a[4]); - for(i=0; ig.mxPage ){ - printf("ERROR: page %d out of range 1..%d: %s\n", + printf("ERROR: page %d out of range 1..%u: %s\n", pgno, g.mxPage, zMsg); sqlite3_free(zMsg); return; @@ -796,7 +805,7 @@ static void page_usage_msg(int pgno, const char *zFormat, ...){ static void page_usage_cell( unsigned char cType, /* Page type */ unsigned char *a, /* Cell content */ - int pgno, /* page containing the cell */ + u32 pgno, /* page containing the cell */ int cellno /* Index of the cell on the page */ ){ int i; @@ -823,10 +832,10 @@ static void page_usage_cell( n += i; } if( nLocallwr ){ - cnt += showLocksInRange(fd, lwr, x.l_start-1); + nPending = 1; + aPending = malloc( sizeof(aPending[0]) ); + if( aPending==0 ){ + fprintf(stderr, "out of memory\n"); + exit(1); } - if( x.l_start+x.l_len=upr ) continue; + x.l_type = F_WRLCK; + x.l_whence = SEEK_SET; + x.l_start = lwr; + x.l_len = upr - lwr; + fcntl(fd, F_GETLK, &x); + if( x.l_type==F_UNLCK ) continue; + printf("start: %-12d len: %-5d pid: %-5d type: %s\n", + (int)x.l_start, (int)x.l_len, + x.l_pid, x.l_type==F_WRLCK ? "WRLCK" : "RDLCK"); + cnt++; + if( nPending+2 > nAlloc ){ + nAlloc = nAlloc*2 + 2; + aPending = realloc(aPending, sizeof(aPending[0])*nAlloc ); + } + if( aPending==0 ){ + fprintf(stderr, "unable to realloc for %d bytes\n", + (int)sizeof(aPending[0])*(nPending+2)); + exit(1); + } + if( lwr0 } -foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] { +set sql { SELECT name, tbl_name FROM sqlite_schema WHERE rootpage>0 } +foreach {name tblname} [concat sqlite_schema sqlite_schema [db eval $sql]] { set is_index [expr {$name!=$tblname}] set is_without_rowid [is_without_rowid $name] @@ -560,7 +560,7 @@ proc autovacuum_overhead {filePages pageSize} { # nautoindex: Number of indices created automatically. # nmanindex: Number of indices created manually. # user_payload: Number of bytes of payload in table btrees -# (not including sqlite_master) +# (not including sqlite_schema) # user_percent: $user_payload as a percentage of total file size. ### The following, setting $file_bytes based on the actual size of the file @@ -587,15 +587,18 @@ set free_percent2 [percent $free_pgcnt2 $file_pgcnt] set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}] -set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}] -set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}] -set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'} +# Account for the lockbyte page +if {$file_pgcnt2*$pageSize>1073742335} {incr file_pgcnt2} + +set ntable [db eval {SELECT count(*)+1 FROM sqlite_schema WHERE type='table'}] +set nindex [db eval {SELECT count(*) FROM sqlite_schema WHERE type='index'}] +set sql {SELECT count(*) FROM sqlite_schema WHERE name LIKE 'sqlite_autoindex%'} set nautoindex [db eval $sql] set nmanindex [expr {$nindex-$nautoindex}] # set total_payload [mem eval "SELECT sum(payload) FROM space_used"] set user_payload [mem one {SELECT int(sum(payload)) FROM space_used - WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}] + WHERE NOT is_index AND name NOT LIKE 'sqlite_schema'}] set user_percent [percent $user_payload $file_bytes] # Output the summary statistics calculated above. diff --git a/tool/speed-check.sh b/tool/speed-check.sh index 414e4b4..b77660e 100644 --- a/tool/speed-check.sh +++ b/tool/speed-check.sh @@ -79,6 +79,10 @@ while test "$1" != ""; do ;; --legacy) doWal=0 + CC_OPTS="$CC_OPTS -DSPEEDTEST_OMIT_HASH" + ;; + --verify) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --verify" ;; --wal) doWal=1 @@ -89,6 +93,9 @@ while test "$1" != ""; do --cachesize) shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --cachesize $1" ;; + --checkpoint) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --checkpoint" + ;; --explain) doExplain=1 ;; diff --git a/tool/sqldiff.c b/tool/sqldiff.c index 9f5b6fe..3590e2c 100644 --- a/tool/sqldiff.c +++ b/tool/sqldiff.c @@ -416,7 +416,7 @@ static void dump_table(const char *zTab, FILE *out){ const char *zSep; /* Separator string */ Str ins; /* Beginning of the INSERT statement */ - pStmt = db_prepare("SELECT sql FROM aux.sqlite_master WHERE name=%Q", zTab); + pStmt = db_prepare("SELECT sql FROM aux.sqlite_schema WHERE name=%Q", zTab); if( SQLITE_ROW==sqlite3_step(pStmt) ){ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } @@ -466,7 +466,7 @@ static void dump_table(const char *zTab, FILE *out){ sqlite3_finalize(pStmt); strFree(&ins); } /* endif !g.bSchemaOnly */ - pStmt = db_prepare("SELECT sql FROM aux.sqlite_master" + pStmt = db_prepare("SELECT sql FROM aux.sqlite_schema" " WHERE type='index' AND tbl_name=%Q AND sql IS NOT NULL", zTab); while( SQLITE_ROW==sqlite3_step(pStmt) ){ @@ -639,10 +639,10 @@ static void diff_one_table(const char *zTab, FILE *out){ /* Drop indexes that are missing in the destination */ pStmt = db_prepare( - "SELECT name FROM main.sqlite_master" + "SELECT name FROM main.sqlite_schema" " WHERE type='index' AND tbl_name=%Q" " AND sql IS NOT NULL" - " AND sql NOT IN (SELECT sql FROM aux.sqlite_master" + " AND sql NOT IN (SELECT sql FROM aux.sqlite_schema" " WHERE type='index' AND tbl_name=%Q" " AND sql IS NOT NULL)", zTab, zTab); @@ -700,10 +700,10 @@ static void diff_one_table(const char *zTab, FILE *out){ /* Create indexes that are missing in the source */ pStmt = db_prepare( - "SELECT sql FROM aux.sqlite_master" + "SELECT sql FROM aux.sqlite_schema" " WHERE type='index' AND tbl_name=%Q" " AND sql IS NOT NULL" - " AND sql NOT IN (SELECT sql FROM main.sqlite_master" + " AND sql NOT IN (SELECT sql FROM main.sqlite_schema" " WHERE type='index' AND tbl_name=%Q" " AND sql IS NOT NULL)", zTab, zTab); @@ -728,7 +728,7 @@ end_diff_one_table: */ static void checkSchemasMatch(const char *zTab){ sqlite3_stmt *pStmt = db_prepare( - "SELECT A.sql=B.sql FROM main.sqlite_master A, aux.sqlite_master B" + "SELECT A.sql=B.sql FROM main.sqlite_schema A, aux.sqlite_schema B" " WHERE A.name=%Q AND B.name=%Q", zTab, zTab ); if( SQLITE_ROW==sqlite3_step(pStmt) ){ @@ -1757,7 +1757,7 @@ const char *gobble_token(const char *zIn, char *zBuf, int nBuf){ ** module_name(SQL) ** ** The only argument should be an SQL statement of the type that may appear -** in the sqlite_master table. If the statement is a "CREATE VIRTUAL TABLE" +** in the sqlite_schema table. If the statement is a "CREATE VIRTUAL TABLE" ** statement, then the value returned is the name of the module that it ** uses. Otherwise, if the statement is not a CVT, NULL is returned. */ @@ -1816,32 +1816,32 @@ const char *all_tables_sql(){ assert( rc==SQLITE_OK ); return - "SELECT name FROM main.sqlite_master\n" + "SELECT name FROM main.sqlite_schema\n" " WHERE type='table' AND (\n" " module_name(sql) IS NULL OR \n" " module_name(sql) IN (SELECT module FROM temp.tblmap)\n" " ) AND name NOT IN (\n" " SELECT a.name || b.postfix \n" - "FROM main.sqlite_master AS a, temp.tblmap AS b \n" + "FROM main.sqlite_schema AS a, temp.tblmap AS b \n" "WHERE module_name(a.sql) = b.module\n" " )\n" "UNION \n" - "SELECT name FROM aux.sqlite_master\n" + "SELECT name FROM aux.sqlite_schema\n" " WHERE type='table' AND (\n" " module_name(sql) IS NULL OR \n" " module_name(sql) IN (SELECT module FROM temp.tblmap)\n" " ) AND name NOT IN (\n" " SELECT a.name || b.postfix \n" - "FROM aux.sqlite_master AS a, temp.tblmap AS b \n" + "FROM aux.sqlite_schema AS a, temp.tblmap AS b \n" "WHERE module_name(a.sql) = b.module\n" " )\n" " ORDER BY name"; }else{ return - "SELECT name FROM main.sqlite_master\n" + "SELECT name FROM main.sqlite_schema\n" " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n" " UNION\n" - "SELECT name FROM aux.sqlite_master\n" + "SELECT name FROM aux.sqlite_schema\n" " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n" " ORDER BY name"; } @@ -1955,7 +1955,7 @@ int main(int argc, char **argv){ if( rc ){ cmdlineError("cannot open database file \"%s\"", zDb1); } - rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, &zErrMsg); + rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_schema", 0, 0, &zErrMsg); if( rc || zErrMsg ){ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb1); } @@ -1974,7 +1974,7 @@ int main(int argc, char **argv){ if( rc || zErrMsg ){ cmdlineError("cannot attach database \"%s\"", zDb2); } - rc = sqlite3_exec(g.db, "SELECT * FROM aux.sqlite_master", 0, 0, &zErrMsg); + rc = sqlite3_exec(g.db, "SELECT * FROM aux.sqlite_schema", 0, 0, &zErrMsg); if( rc || zErrMsg ){ cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb2); } diff --git a/vsixtest/App.xaml.cpp b/vsixtest/App.xaml.cpp index da8f327..c90604a 100644 --- a/vsixtest/App.xaml.cpp +++ b/vsixtest/App.xaml.cpp @@ -117,4 +117,4 @@ void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e) void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e) { throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name); -} \ No newline at end of file +}