diff --git a/Makefile.in b/Makefile.in index e4687626f0..9b2fe86cf4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -82,7 +82,6 @@ TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@ # The same set of OMIT and ENABLE flags should be passed to the # LEMON parser generator and the mkkeywordhash tool as well. OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ -OPT_FEATURE_FLAGS += -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK TCC += $(OPT_FEATURE_FLAGS) @@ -417,7 +416,9 @@ TESTSRC = \ # TESTSRC += \ $(TOP)/ext/misc/amatch.c \ + $(TOP)/ext/misc/carray.c \ $(TOP)/ext/misc/closure.c \ + $(TOP)/ext/misc/csv.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ @@ -539,7 +540,8 @@ TESTPROGS = \ testfixture$(TEXE) \ sqlite3$(TEXE) \ sqlite3_analyzer$(TEXE) \ - sqldiff$(TEXE) + sqldiff$(TEXE) \ + dbhash$(TEXE) # Databases containing fuzzer test cases # @@ -556,7 +558,9 @@ TESTOPTS = --verbose=file --output=test-out.txt # Extra compiler options for various shell tools # SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 +# SHELL_OPT += -DSQLITE_ENABLE_FTS5 SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS +SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 @@ -587,8 +591,15 @@ sqlite3$(TEXE): $(TOP)/src/shell.c sqlite3.c $(TOP)/src/shell.c sqlite3.c \ $(LIBREADLINE) $(TLIBS) -rpath "$(libdir)" -sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h - $(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) +sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.lo sqlite3.h + $(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.lo $(TLIBS) + +dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.lo sqlite3.h + $(LTLINK) -o $@ $(TOP)/tool/dbhash.c sqlite3.lo $(TLIBS) + +scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.lo + $(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \ + $(TOP)/ext/misc/scrub.c sqlite3.lo $(TLIBS) srcck1$(BEXE): $(TOP)/tool/srcck1.c $(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c @@ -603,8 +614,8 @@ fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) -mptester$(TEXE): sqlite3.c $(TOP)/mptest/mptest.c - $(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ +mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c + $(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.lo \ $(TLIBS) -rpath "$(libdir)" MPTEST1=./mptester$(TEXE) mptest.db $(TOP)/mptest/crash01.test --repeat 20 @@ -1148,8 +1159,8 @@ rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h $(LTLINK) -I. -o $@ $(TOP)/tool/logest.c -wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c - $(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.c $(TLIBS) +wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo + $(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.lo $(TLIBS) speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS) @@ -1166,7 +1177,7 @@ loadfts$(EXE): $(TOP)/tool/loadfts.c libsqlite3.la # VALIDIDS=' sqlite3(changeset|changegroup|session)?_' checksymbols: sqlite3.lo - nm -g --defined-only sqlite3.o | egrep -v $(VALIDIDS); test $$? -ne 0 + nm -g --defined-only sqlite3.lo | egrep -v $(VALIDIDS); test $$? -ne 0 echo '0 errors out of 1 tests' # Build the amalgamation-autoconf package. The amalamgation-tarball target builds @@ -1247,6 +1258,7 @@ clean: rm -f fuzzershell fuzzershell.exe rm -f fuzzcheck fuzzcheck.exe rm -f sqldiff sqldiff.exe + rm -f dbhash dbhash.exe rm -f fts5.* fts5parse.* distclean: clean diff --git a/Makefile.msc b/Makefile.msc index e65f99d758..cd866ce4a4 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -198,6 +198,12 @@ DEBUG = 0 OPTIMIZATIONS = 2 !ENDIF +# Set this to non-0 to enable support for the session extension. +# +!IFNDEF SESSION +SESSION = 0 +!ENDIF + # Set the source code file to be used by executables and libraries when # they need the amalgamation. # @@ -264,6 +270,12 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1 +!ENDIF + +# Should the session extension be enabled? If so, add compilation options +# to enable it. +# +!IF $(SESSION)!=0 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1 !ENDIF @@ -1280,7 +1292,9 @@ TESTSRC = \ # TESTEXT = \ $(TOP)\ext\misc\amatch.c \ + $(TOP)\ext\misc\carray.c \ $(TOP)\ext\misc\closure.c \ + $(TOP)\ext\misc\csv.c \ $(TOP)\ext\misc\eval.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\fuzzer.c \ @@ -1365,7 +1379,8 @@ TESTPROGS = \ testfixture.exe \ $(SQLITE3EXE) \ sqlite3_analyzer.exe \ - sqldiff.exe + sqldiff.exe \ + dbhash.exe # Databases containing fuzzer test cases # @@ -1443,6 +1458,12 @@ $(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_S sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) +dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H) + $(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) + +scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H) + $(LTLINK) $(NO_WARN) $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) + srcck1.exe: $(TOP)\tool\srcck1.c $(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c @@ -2089,6 +2110,6 @@ clean: del /Q shell.c sqlite3ext.h sqlite3session.h 2>NUL del /Q sqlite3_analyzer.exe sqlite3_analyzer.c 2>NUL del /Q sqlite-*-output.vsix 2>NUL - del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe 2>NUL + del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe dbhash.exe 2>NUL del /Q fts5.* fts5parse.* 2>NUL # <> diff --git a/VERSION b/VERSION index 4eba2a62eb..f02113fe87 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.13.0 +3.15.0 diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am index cca23ba67d..e8211596d9 100644 --- a/autoconf/Makefile.am +++ b/autoconf/Makefile.am @@ -1,5 +1,5 @@ -AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE +AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @SESSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE lib_LTLIBRARIES = libsqlite3.la libsqlite3_la_SOURCES = sqlite3.c diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc index ed99ce9587..d3bbc6b4d2 100644 --- a/autoconf/Makefile.msc +++ b/autoconf/Makefile.msc @@ -24,6 +24,13 @@ TOP = . USE_FULLWARN = 0 !ENDIF +# Set this non-0 to enable full runtime error checks (-RTC1, etc). This +# has no effect if (any) optimizations are enabled. +# +!IFNDEF USE_RUNTIME_CHECKS +USE_RUNTIME_CHECKS = 0 +!ENDIF + # Set this non-0 to use "stdcall" calling convention for the core library # and shell executable. # @@ -183,6 +190,12 @@ DEBUG = 0 OPTIMIZATIONS = 2 !ENDIF +# Set this to non-0 to enable support for the session extension. +# +!IFNDEF SESSION +SESSION = 0 +!ENDIF + # Set the source code file to be used by executables and libraries when # they need the amalgamation. # @@ -249,6 +262,12 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1 +!ENDIF + +# Should the session extension be enabled? If so, add compilation options +# to enable it. +# +!IF $(SESSION)!=0 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1 OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1 !ENDIF @@ -437,12 +456,12 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS) # !IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0 !IF "$(PLATFORM)"=="x86" -CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall -SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall +CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall !ELSE !IFNDEF PLATFORM -CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall -SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall +CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall +SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall !ELSE CORE_CCONV_OPTS = SHELL_CCONV_OPTS = @@ -728,6 +747,10 @@ RCC = $(RCC) -D_DEBUG !IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0 TCC = $(TCC) -Od BCC = $(BCC) -Od +!IF $(USE_RUNTIME_CHECKS)!=0 +TCC = $(TCC) -RTC1 +BCC = $(BCC) -RTC1 +!ENDIF !ELSEIF $(OPTIMIZATIONS)>=3 TCC = $(TCC) -Ox BCC = $(BCC) -Ox diff --git a/autoconf/configure.ac b/autoconf/configure.ac index f86a39c37b..b9a11aac8f 100644 --- a/autoconf/configure.ac +++ b/autoconf/configure.ac @@ -30,39 +30,52 @@ AC_FUNC_STRERROR_R AC_CONFIG_FILES([Makefile sqlite3.pc]) AC_SUBST(BUILD_CFLAGS) -#----------------------------------------------------------------------- +#------------------------------------------------------------------------- +# Two options to enable readline compatible libraries: +# # --enable-editline # --enable-readline # -AC_ARG_ENABLE(editline, [AS_HELP_STRING( - [--enable-editline], - [use BSD libedit])], - [], [enable_editline=yes]) -AC_ARG_ENABLE(readline, [AS_HELP_STRING( - [--enable-readline], - [use readline])], - [], [enable_readline=no]) -if test x"$enable_editline" != xno ; then - sLIBS=$LIBS - LIBS="" - AC_SEARCH_LIBS([readline],[edit],[enable_readline=no],[enable_editline=no]) - READLINE_LIBS=$LIBS - if test x"$LIBS" != "x"; then - AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline) - else - unset ac_cv_search_readline - fi - LIBS=$sLIBS -fi -if test x"$enable_readline" != xno ; then - sLIBS=$LIBS - LIBS="" - AC_SEARCH_LIBS(tgetent, curses ncurses ncursesw, [], []) - AC_SEARCH_LIBS(readline, readline, [], [enable_readline=no]) - AC_CHECK_FUNCS(readline, [], []) - READLINE_LIBS=$LIBS - LIBS=$sLIBS -fi +# Both are enabled by default. If, after command line processing both are +# still enabled, the script searches for editline first and automatically +# disables readline if it is found. So, to use readline explicitly, the +# user must pass "--disable-editline". To disable command line editing +# support altogether, "--disable-editline --disable-readline". +# +# When searching for either library, check for headers before libraries +# as some distros supply packages that contain libraries but not header +# files, which come as a separate development package. +# +AC_ARG_ENABLE(editline, [AS_HELP_STRING([--enable-editline],[use BSD libedit])]) +AC_ARG_ENABLE(readline, [AS_HELP_STRING([--enable-readline],[use readline])]) + +AS_IF([ test x"$enable_editline" != xno ],[ + AC_CHECK_HEADERS([editline/readline.h],[ + sLIBS=$LIBS + LIBS="" + AC_SEARCH_LIBS([readline],[edit],[ + AC_DEFINE([HAVE_EDITLINE],1,Define to use BSD editline) + READLINE_LIBS=$LIBS + enable_readline=no + ]) + AS_UNSET(ac_cv_search_readline) + LIBS=$sLIBS + ]) +]) + +AS_IF([ test x"$enable_readline" != xno ],[ + AC_CHECK_HEADERS([readline/readline.h],[ + sLIBS=$LIBS + LIBS="" + AC_SEARCH_LIBS(tgetent, termcap curses ncurses ncursesw, [], []) + AC_SEARCH_LIBS(readline,[readline edit], [ + AC_DEFINE([HAVE_READLINE],1,Define to use readline or wrapper) + READLINE_LIBS=$LIBS + ]) + LIBS=$sLIBS + ]) +]) + AC_SUBST(READLINE_LIBS) #----------------------------------------------------------------------- @@ -103,7 +116,7 @@ AC_SUBST(DYNAMIC_EXTENSION_FLAGS) AC_ARG_ENABLE(fts5, [AS_HELP_STRING( [--enable-fts5], [include fts5 support [default=no]])], [], [enable_fts5=no]) -if test x"$enable_fts5" == "xyes"; then +if test x"$enable_fts5" = "xyes"; then AC_SEARCH_LIBS(log, m) FTS5_FLAGS=-DSQLITE_ENABLE_FTS5 fi @@ -116,12 +129,24 @@ AC_SUBST(FTS5_FLAGS) AC_ARG_ENABLE(json1, [AS_HELP_STRING( [--enable-json1], [include json1 support [default=no]])], [], [enable_json1=no]) -if test x"$enable_json1" == "xyes"; then +if test x"$enable_json1" = "xyes"; then JSON1_FLAGS=-DSQLITE_ENABLE_JSON1 fi AC_SUBST(JSON1_FLAGS) #----------------------------------------------------------------------- +#----------------------------------------------------------------------- +# --enable-session +# +AC_ARG_ENABLE(session, [AS_HELP_STRING( + [--enable-session], [enable the session extension [default=no]])], + [], [enable_session=no]) +if test x"$enable_session" = "xyes"; then + SESSION_FLAGS="-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK" +fi +AC_SUBST(SESSION_FLAGS) +#----------------------------------------------------------------------- + #----------------------------------------------------------------------- # --enable-static-shell # @@ -129,7 +154,7 @@ AC_ARG_ENABLE(static-shell, [AS_HELP_STRING( [--enable-static-shell], [statically link libsqlite3 into shell tool [default=yes]])], [], [enable_static_shell=yes]) -if test x"$enable_static_shell" == "xyes"; then +if test x"$enable_static_shell" = "xyes"; then EXTRA_SHELL_OBJ=sqlite3-sqlite3.$OBJEXT else EXTRA_SHELL_OBJ=libsqlite3.la diff --git a/autoconf/tea/configure.ac b/autoconf/tea/configure.ac index 8df0af6195..7fca05b2e7 100644 --- a/autoconf/tea/configure.ac +++ b/autoconf/tea/configure.ac @@ -78,7 +78,6 @@ TEA_ADD_LIBS([]) TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1]) TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1]) TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1]) -TEA_ADD_CFLAGS([-DSQLITE_OMIT_DEPRECATED=1]) TEA_ADD_STUB_SOURCES([]) TEA_ADD_TCL_SOURCES([]) diff --git a/configure b/configure index 27cb1ea0b9..75c7a3720d 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 sqlite 3.13.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.15.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -726,8 +726,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.13.0' -PACKAGE_STRING='sqlite 3.13.0' +PACKAGE_VERSION='3.15.0' +PACKAGE_STRING='sqlite 3.15.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -903,11 +903,14 @@ with_readline_inc enable_debug enable_amalgamation enable_load_extension +enable_memsys5 +enable_memsys3 enable_fts3 enable_fts4 enable_fts5 enable_json1 enable_rtree +enable_session enable_gcov ' ac_precious_vars='build_alias @@ -1460,7 +1463,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 sqlite 3.13.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.15.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1525,7 +1528,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.13.0:";; + short | recursive ) echo "Configuration of sqlite 3.15.0:";; esac cat <<\_ACEOF @@ -1551,11 +1554,14 @@ Optional Features: separately --disable-load-extension Disable loading of external extensions + --enable-memsys5 Enable MEMSYS5 + --enable-memsys3 Enable MEMSYS3 --enable-fts3 Enable the FTS3 extension --enable-fts4 Enable the FTS4 extension --enable-fts5 Enable the FTS5 extension --enable-json1 Enable the JSON1 extension --enable-rtree Enable the RTREE extension + --enable-session Enable the SESSION extension --enable-gcov Enable coverage testing using gcov Optional Packages: @@ -1646,7 +1652,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.13.0 +sqlite configure 3.15.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2065,7 +2071,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 sqlite $as_me 3.13.0, which was +It was created by sqlite $as_me 3.15.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3923,13 +3929,13 @@ if ${lt_cv_nm_interface+:} false; then : else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3926: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3932: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3929: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3935: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3932: output\"" >&5) + (eval echo "\"\$as_me:3938: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -5135,7 +5141,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5138 "configure"' > conftest.$ac_ext + echo '#line 5144 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6660,11 +6666,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6663: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6669: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6667: \$? = $ac_status" >&5 + echo "$as_me:6673: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -6999,11 +7005,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7002: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7008: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7006: \$? = $ac_status" >&5 + echo "$as_me:7012: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7104,11 +7110,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7107: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7113: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7111: \$? = $ac_status" >&5 + echo "$as_me:7117: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7159,11 +7165,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7162: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7168: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7166: \$? = $ac_status" >&5 + echo "$as_me:7172: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -9539,7 +9545,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 9542 "configure" +#line 9548 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -9635,7 +9641,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 9638 "configure" +#line 9644 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10755,6 +10761,20 @@ else fi fi + # Recent versions of Xcode on Macs hid the tclConfig.sh file + # in a strange place. + if test x"${ac_cv_c_tclconfig}" = x ; then + if test x"$cross_compiling" = xno; then + for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib + do + if test -f "$i/tclConfig.sh" ; then + ac_cv_c_tclconfig="$i" + break + fi + done + fi + fi + # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ @@ -11323,6 +11343,44 @@ else OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1" fi +########## +# Do we want to support memsys3 and/or memsys5 +# +# Check whether --enable-memsys5 was given. +if test "${enable_memsys5+set}" = set; then : + enableval=$enable_memsys5; enable_memsys5=yes +else + enable_memsys5=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5 +$as_echo_n "checking whether to support MEMSYS5... " >&6; } +if test "${enable_memsys5}" = "yes"; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS5" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +# Check whether --enable-memsys3 was given. +if test "${enable_memsys3+set}" = set; then : + enableval=$enable_memsys3; enable_memsys3=yes +else + enable_memsys3=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5 +$as_echo_n "checking whether to support MEMSYS3... " >&6; } +if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS3" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + ######### # See whether we should enable Full Text Search extensions # Check whether --enable-fts3 was given. @@ -11494,6 +11552,20 @@ if test "${enable_rtree}" = "yes" ; then OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_RTREE" fi +######### +# See whether we should enable the SESSION extension +# Check whether --enable-session was given. +if test "${enable_session+set}" = set; then : + enableval=$enable_session; enable_session=yes +else + enable_session=no +fi + +if test "${enable_session}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_SESSION" + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_PREUPDATE_HOOK" +fi + ######### # attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter for option in $CFLAGS $CPPFLAGS @@ -12079,7 +12151,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 sqlite $as_me 3.13.0, which was +This file was extended by sqlite $as_me 3.15.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12145,7 +12217,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="\\ -sqlite config.status 3.13.0 +sqlite config.status 3.15.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 08c8867f06..c9e3af9220 100644 --- a/configure.ac +++ b/configure.ac @@ -334,6 +334,20 @@ if test "${use_tcl}" = "yes" ; then fi fi + # Recent versions of Xcode on Macs hid the tclConfig.sh file + # in a strange place. + if test x"${ac_cv_c_tclconfig}" = x ; then + if test x"$cross_compiling" = xno; then + for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib + do + if test -f "$i/tclConfig.sh" ; then + ac_cv_c_tclconfig="$i" + break + fi + done + fi + fi + # then check for a private Tcl installation if test x"${ac_cv_c_tclconfig}" = x ; then for i in \ @@ -574,6 +588,30 @@ else OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1" fi +########## +# Do we want to support memsys3 and/or memsys5 +# +AC_ARG_ENABLE(memsys5, + AC_HELP_STRING([--enable-memsys5],[Enable MEMSYS5]), + [enable_memsys5=yes],[enable_memsys5=no]) +AC_MSG_CHECKING([whether to support MEMSYS5]) +if test "${enable_memsys5}" = "yes"; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS5" + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi +AC_ARG_ENABLE(memsys3, + AC_HELP_STRING([--enable-memsys3],[Enable MEMSYS3]), + [enable_memsys3=yes],[enable_memsys3=no]) +AC_MSG_CHECKING([whether to support MEMSYS3]) +if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_MEMSYS3" + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + ######### # See whether we should enable Full Text Search extensions AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3], @@ -615,6 +653,16 @@ if test "${enable_rtree}" = "yes" ; then OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_RTREE" fi +######### +# See whether we should enable the SESSION extension +AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session], + [Enable the SESSION extension]), + [enable_session=yes],[enable_session=no]) +if test "${enable_session}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_SESSION" + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_PREUPDATE_HOOK" +fi + ######### # attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter for option in $CFLAGS $CPPFLAGS diff --git a/ext/fts2/fts2_tokenizer.c b/ext/fts2/fts2_tokenizer.c index a3d6a6312a..dda33a72d2 100644 --- a/ext/fts2/fts2_tokenizer.c +++ b/ext/fts2/fts2_tokenizer.c @@ -99,7 +99,11 @@ static void scalarFunc( #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include /* diff --git a/ext/fts3/fts3_test.c b/ext/fts3/fts3_test.c index 2596e6dec7..a48a556c98 100644 --- a/ext/fts3/fts3_test.c +++ b/ext/fts3/fts3_test.c @@ -18,7 +18,14 @@ ** that the sqlite3_tokenizer_module.xLanguage() method is invoked correctly. */ -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #include #include @@ -143,7 +150,7 @@ static int nm_match_count( /* ** Tclcmd: fts3_near_match DOCUMENT EXPR ?OPTIONS? */ -static int fts3_near_match_cmd( +static int SQLITE_TCLAPI fts3_near_match_cmd( ClientData clientData, Tcl_Interp *interp, int objc, @@ -278,7 +285,7 @@ static int fts3_near_match_cmd( ** # Restore initial incr-load settings: ** eval fts3_configure_incr_load $cfg */ -static int fts3_configure_incr_load_cmd( +static int SQLITE_TCLAPI fts3_configure_incr_load_cmd( ClientData clientData, Tcl_Interp *interp, int objc, @@ -488,7 +495,7 @@ static int testTokenizerLanguage( } #endif -static int fts3_test_tokenizer_cmd( +static int SQLITE_TCLAPI fts3_test_tokenizer_cmd( ClientData clientData, Tcl_Interp *interp, int objc, @@ -517,7 +524,7 @@ static int fts3_test_tokenizer_cmd( return TCL_OK; } -static int fts3_test_varint_cmd( +static int SQLITE_TCLAPI fts3_test_varint_cmd( ClientData clientData, Tcl_Interp *interp, int objc, diff --git a/ext/fts3/fts3_tokenizer.c b/ext/fts3/fts3_tokenizer.c index b7d9d2b3b2..bfc36af3e3 100644 --- a/ext/fts3/fts3_tokenizer.c +++ b/ext/fts3/fts3_tokenizer.c @@ -224,7 +224,11 @@ int sqlite3Fts3InitTokenizer( #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include /* diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 96ecb38e33..a45c145d38 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -143,11 +143,13 @@ struct Fts5PhraseIter { ** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid ** ** with $p set to a phrase equivalent to the phrase iPhrase of the -** current query is executed. For each row visited, the callback function -** passed as the fourth argument is invoked. The context and API objects -** passed to the callback function may be used to access the properties of -** each matched row. Invoking Api.xUserData() returns a copy of the pointer -** passed as the third argument to pUserData. +** current query is executed. Any column filter that applies to +** phrase iPhrase of the current query is included in $p. For each +** row visited, the callback function passed as the fourth argument +** is invoked. The context and API objects passed to the callback +** function may be used to access the properties of each matched row. +** Invoking Api.xUserData() returns a copy of the pointer passed as +** the third argument to pUserData. ** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. @@ -316,7 +318,7 @@ struct Fts5ExtensionApi { ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: -** This function is used to allocate and inititalize a tokenizer instance. +** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) @@ -575,4 +577,3 @@ struct fts5_api { #endif #endif /* _FTS5_H */ - diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index c4e7506fb1..9ef338e821 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -686,7 +686,6 @@ int sqlite3Fts5ExprPopulatePoslists( Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int ); void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); -void sqlite3Fts5ExprClearEof(Fts5Expr*); int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); @@ -738,6 +737,7 @@ void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*); +Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*); void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index eada84f70e..ca795a6b35 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -167,6 +167,7 @@ static int fts5ExprGetToken( case ',': tok = FTS5_COMMA; break; case '+': tok = FTS5_PLUS; break; case '*': tok = FTS5_STAR; break; + case '-': tok = FTS5_MINUS; break; case '\0': tok = FTS5_EOF; break; case '"': { @@ -1655,6 +1656,17 @@ int sqlite3Fts5ExprClonePhrase( pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); } + if( rc==SQLITE_OK ){ + Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; + if( pColsetOrig ){ + int nByte = sizeof(Fts5Colset) + pColsetOrig->nCol * sizeof(int); + Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); + if( pColset ){ + memcpy(pColset, pColsetOrig, nByte); + } + pNew->pRoot->pNear->pColset = pColset; + } + } for(i=0; rc==SQLITE_OK && inTerm; i++){ int tflags = 0; @@ -1782,6 +1794,34 @@ static Fts5Colset *fts5ParseColset( return pNew; } +/* +** Allocate and return an Fts5Colset object specifying the inverse of +** the colset passed as the second argument. Free the colset passed +** as the second argument before returning. +*/ +Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ + Fts5Colset *pRet; + int nCol = pParse->pConfig->nCol; + + pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, + sizeof(Fts5Colset) + sizeof(int)*nCol + ); + if( pRet ){ + int i; + int iOld = 0; + for(i=0; i=p->nCol || p->aiCol[iOld]!=i ){ + pRet->aiCol[pRet->nCol++] = i; + }else{ + iOld++; + } + } + } + + sqlite3_free(p); + return pRet; +} + Fts5Colset *sqlite3Fts5ParseColset( Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ Fts5Colset *pColset, /* Existing colset object */ @@ -2606,17 +2646,6 @@ void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){ fts5ExprCheckPoslists(pExpr->pRoot, iRowid); } -static void fts5ExprClearEof(Fts5ExprNode *pNode){ - int i; - for(i=0; inChild; i++){ - fts5ExprClearEof(pNode->apChild[i]); - } - pNode->bEof = 0; -} -void sqlite3Fts5ExprClearEof(Fts5Expr *pExpr){ - fts5ExprClearEof(pExpr->pRoot); -} - /* ** This function is only called for detail=columns tables. */ diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 1d5ebe1428..65b92a2b99 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -3195,6 +3195,15 @@ static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){ } } +/* +** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match +** against no columns at all). +*/ +static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){ + UNUSED_PARAM(pSeg); + pIter->base.nData = 0; +} + /* ** xSetOutputs callback used by detail=col when there is a column filter ** and there are 100 or more columns. Also called as a fallback from @@ -3300,6 +3309,10 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){ pIter->xSetOutputs = fts5IterSetOutputs_Nocolset; } + else if( pIter->pColset->nCol==0 ){ + pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset; + } + else if( pConfig->eDetail==FTS5_DETAIL_FULL ){ pIter->xSetOutputs = fts5IterSetOutputs_Full; } diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 4feb3e1a2f..384d3dd8f7 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -1186,7 +1186,6 @@ static int fts5FilterMethod( pCsr->ePlan = FTS5_PLAN_SOURCE; pCsr->pExpr = pTab->pSortCsr->pExpr; rc = fts5CursorFirst(pTab, pCsr, bDesc); - sqlite3Fts5ExprClearEof(pCsr->pExpr); }else if( pMatch ){ const char *zExpr = (const char*)sqlite3_value_text(apVal[0]); if( zExpr==0 ) zExpr = ""; diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index 90df3396c3..a695887458 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -247,7 +247,11 @@ int sqlite3Fts5CreateTable( char *zErr = 0; rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s", - pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":"" + pConfig->zDb, pConfig->zName, zPost, zDefn, +#ifndef SQLITE_FTS5_NO_WITHOUT_ROWID + bWithout?" WITHOUT ROWID": +#endif + "" ); if( zErr ){ *pzErr = sqlite3_mprintf( diff --git a/ext/fts5/fts5_tcl.c b/ext/fts5/fts5_tcl.c index 72db65777f..5fe690f6b7 100644 --- a/ext/fts5/fts5_tcl.c +++ b/ext/fts5/fts5_tcl.c @@ -14,7 +14,14 @@ #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #ifdef SQLITE_ENABLE_FTS5 @@ -78,7 +85,7 @@ static int f5tResultToErrorCode(const char *zRes){ return SQLITE_ERROR; } -static int f5tDbAndApi( +static int SQLITE_TCLAPI f5tDbAndApi( Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **ppDb, @@ -164,7 +171,7 @@ static int xTokenizeCb( return rc; } -static int xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []); +static int SQLITE_TCLAPI xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []); static int xQueryPhraseCb( const Fts5ExtensionApi *pApi, @@ -209,7 +216,7 @@ static void xSetAuxdataDestructor(void *p){ ** ** Description... */ -static int xF5tApi( +static int SQLITE_TCLAPI xF5tApi( void * clientData, Tcl_Interp *interp, int objc, @@ -602,7 +609,7 @@ static void xF5tDestroy(void *pCtx){ ** ** Description... */ -static int f5tCreateFunction( +static int SQLITE_TCLAPI f5tCreateFunction( void * clientData, Tcl_Interp *interp, int objc, @@ -672,7 +679,7 @@ static int xTokenizeCb2( ** ** Description... */ -static int f5tTokenize( +static int SQLITE_TCLAPI f5tTokenize( void * clientData, Tcl_Interp *interp, int objc, @@ -878,7 +885,7 @@ static int f5tTokenizerTokenize( /* ** sqlite3_fts5_token ?-colocated? TEXT START END */ -static int f5tTokenizerReturn( +static int SQLITE_TCLAPI f5tTokenizerReturn( void * clientData, Tcl_Interp *interp, int objc, @@ -949,7 +956,7 @@ static void f5tDelTokenizer(void *pCtx){ ** SCRIPT2 should invoke the [sqlite3_fts5_token] command once for each ** token within the tokenized text. */ -static int f5tCreateTokenizer( +static int SQLITE_TCLAPI f5tCreateTokenizer( ClientData clientData, Tcl_Interp *interp, int objc, @@ -992,7 +999,7 @@ static int f5tCreateTokenizer( return TCL_OK; } -static void xF5tFree(ClientData clientData){ +static void SQLITE_TCLAPI xF5tFree(ClientData clientData){ ckfree(clientData); } @@ -1001,7 +1008,7 @@ static void xF5tFree(ClientData clientData){ ** ** Set or clear the global "may-be-corrupt" flag. Return the old value. */ -static int f5tMayBeCorrupt( +static int SQLITE_TCLAPI f5tMayBeCorrupt( void * clientData, Tcl_Interp *interp, int objc, @@ -1033,7 +1040,7 @@ static unsigned int f5t_fts5HashKey(int nSlot, const char *p, int n){ return (h % nSlot); } -static int f5tTokenHash( +static int SQLITE_TCLAPI f5tTokenHash( void * clientData, Tcl_Interp *interp, int objc, @@ -1058,7 +1065,7 @@ static int f5tTokenHash( return TCL_OK; } -static int f5tRegisterMatchinfo( +static int SQLITE_TCLAPI f5tRegisterMatchinfo( void * clientData, Tcl_Interp *interp, int objc, @@ -1083,7 +1090,7 @@ static int f5tRegisterMatchinfo( return TCL_OK; } -static int f5tRegisterTok( +static int SQLITE_TCLAPI f5tRegisterTok( void * clientData, Tcl_Interp *interp, int objc, diff --git a/ext/fts5/fts5parse.y b/ext/fts5/fts5parse.y index 1607d3846a..1cc4b88e18 100644 --- a/ext/fts5/fts5parse.y +++ b/ext/fts5/fts5parse.y @@ -120,10 +120,17 @@ cnearset(A) ::= colset(X) COLON nearset(Y). { %type colsetlist {Fts5Colset*} %destructor colsetlist { sqlite3_free($$); } +colset(A) ::= MINUS LCP colsetlist(X) RCP. { + A = sqlite3Fts5ParseColsetInvert(pParse, X); +} colset(A) ::= LCP colsetlist(X) RCP. { A = X; } colset(A) ::= STRING(X). { A = sqlite3Fts5ParseColset(pParse, 0, &X); } +colset(A) ::= MINUS STRING(X). { + A = sqlite3Fts5ParseColset(pParse, 0, &X); + A = sqlite3Fts5ParseColsetInvert(pParse, A); +} colsetlist(A) ::= colsetlist(Y) STRING(X). { A = sqlite3Fts5ParseColset(pParse, Y, &X); } @@ -131,7 +138,6 @@ colsetlist(A) ::= STRING(X). { A = sqlite3Fts5ParseColset(pParse, 0, &X); } - %type nearset {Fts5ExprNearset*} %type nearphrases {Fts5ExprNearset*} %destructor nearset { sqlite3Fts5ParseNearsetFree($$); } diff --git a/ext/fts5/test/fts5aux.test b/ext/fts5/test/fts5aux.test index 995fe85784..fa3167a4d1 100644 --- a/ext/fts5/test/fts5aux.test +++ b/ext/fts5/test/fts5aux.test @@ -246,5 +246,37 @@ foreach {tn lRow res} { } $res } +#------------------------------------------------------------------------- +# Test the built-in bm25() demo. +# +reset_db +do_execsql_test 9.1 { + CREATE VIRTUAL TABLE t1 USING fts5(a, b); + INSERT INTO t1 VALUES('a', NULL); -- 1 + INSERT INTO t1 VALUES('a', NULL); -- 2 + INSERT INTO t1 VALUES('a', NULL); -- 3 + INSERT INTO t1 VALUES('a', NULL); -- 4 + INSERT INTO t1 VALUES('a', NULL); -- 5 + INSERT INTO t1 VALUES('a', NULL); -- 6 + INSERT INTO t1 VALUES('a', NULL); -- 7 + INSERT INTO t1 VALUES('a', NULL); -- 8 + INSERT INTO t1 VALUES(NULL, 'a a b'); -- 9 + INSERT INTO t1 VALUES(NULL, 'b b a'); -- 10 +} + +do_execsql_test 9.2 { + SELECT rowid FROM t1('a AND b') ORDER BY rank; +} { + 10 9 +} + +do_execsql_test 9.3 { + SELECT rowid FROM t1('b:a AND b:b') ORDER BY rank; +} { + 9 10 +} + + + finish_test diff --git a/ext/fts5/test/fts5colset.test b/ext/fts5/test/fts5colset.test new file mode 100644 index 0000000000..e3333241ee --- /dev/null +++ b/ext/fts5/test/fts5colset.test @@ -0,0 +1,59 @@ +# 2016 August 10 +# +# 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 FTS5 module. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5colset + +# If SQLITE_ENABLE_FTS5 is not defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +foreach_detail_mode $::testprefix { + if {[detail_is_none]} continue + + do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, d, detail=%DETAIL%); + INSERT INTO t1 VALUES('a', 'b', 'c', 'd'); -- 1 + INSERT INTO t1 VALUES('d', 'a', 'b', 'c'); -- 2 + INSERT INTO t1 VALUES('c', 'd', 'a', 'b'); -- 3 + INSERT INTO t1 VALUES('b', 'c', 'd', 'a'); -- 4 + } + + foreach {tn q res} { + 1 "a" {1 2 3 4} + 2 "{a} : a" {1} + 3 "-{a} : a" {2 3 4} + 4 "- {a c} : a" {2 4} + 5 " - {d d c} : a" {1 2} + 6 "- {d c b a} : a" {} + 7 "-{\"a\"} : b" {1 2 3} + 8 "- c : a" {1 2 4} + 9 "-c : a" {1 2 4} + 10 "-\"c\" : a" {1 2 4} + } { + breakpoint + do_execsql_test 1.$tn { + SELECT rowid FROM t1($q) + } $res + } + + +} + + +finish_test + + diff --git a/ext/fts5/test/fts5rank.test b/ext/fts5/test/fts5rank.test index 4961b42605..a70c5d68e3 100644 --- a/ext/fts5/test/fts5rank.test +++ b/ext/fts5/test/fts5rank.test @@ -91,7 +91,61 @@ do_test 2.7 { } {1 3 2} +#-------------------------------------------------------------------------- +# At one point there was a problem with queries such as: +# +# ... MATCH 'x OR y' ORDER BY rank; +# +# if there were zero occurrences of token 'y' in the dataset. The +# following tests verify that that problem has been addressed. +# +foreach_detail_mode $::testprefix { + do_execsql_test 3.1.0 { + CREATE VIRTUAL TABLE y1 USING fts5(z, detail=%DETAIL%); + INSERT INTO y1 VALUES('test xyz'); + INSERT INTO y1 VALUES('test test xyz test'); + INSERT INTO y1 VALUES('test test xyz'); + } + do_execsql_test 3.1.1 { + SELECT rowid FROM y1('test OR tset'); + } {1 2 3} + + do_execsql_test 3.1.2 { + SELECT rowid FROM y1('test OR tset') ORDER BY bm25(y1) + } {2 3 1} + + do_execsql_test 3.1.3 { + SELECT rowid FROM y1('test OR tset') ORDER BY +rank + } {2 3 1} + + do_execsql_test 3.1.4 { + SELECT rowid FROM y1('test OR tset') ORDER BY rank + } {2 3 1} + + do_execsql_test 3.1.5 { + SELECT rowid FROM y1('test OR xyz') ORDER BY rank + } {3 2 1} + + + do_execsql_test 3.2.1 { + CREATE VIRTUAL TABLE z1 USING fts5(a, detail=%DETAIL%); + INSERT INTO z1 VALUES('wrinkle in time'); + SELECT * FROM z1 WHERE z1 MATCH 'wrinkle in time OR a wrinkle in time'; + } {{wrinkle in time}} +} + +do_execsql_test 4.1 { + DROP TABLE IF EXISTS VTest; + CREATE virtual TABLE VTest USING FTS5( + Title, AUthor, tokenize ='porter unicode61 remove_diacritics 1', + columnsize='1', detail=full + ); + INSERT INTO VTest (Title, Author) VALUES ('wrinkle in time', 'Bill Smith'); + + SELECT * FROM VTest WHERE + VTest MATCH 'wrinkle in time OR a wrinkle in time' ORDER BY rank; +} {{wrinkle in time} {Bill Smith}} diff --git a/ext/fts5/test/fts5simple.test b/ext/fts5/test/fts5simple.test index 2bc02cf49a..5ac413c885 100644 --- a/ext/fts5/test/fts5simple.test +++ b/ext/fts5/test/fts5simple.test @@ -265,7 +265,7 @@ do_test 11.0 { execsql " INSERT INTO t4 VALUES('a b c \x1A'); INSERT INTO t4 VALUES('a b c d\x1A'); - INSERT INTO t4 VALUES('a b c \x1Ad'); + INSERT INTO t4 VALUES('a b c \x1Ag'); INSERT INTO t4 VALUES('a b c d'); " } {} diff --git a/ext/misc/carray.c b/ext/misc/carray.c new file mode 100644 index 0000000000..025eb5db2c --- /dev/null +++ b/ext/misc/carray.c @@ -0,0 +1,364 @@ +/* +** 2016-06-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. +** +************************************************************************* +** +** This file demonstrates how to create a table-valued-function that +** returns the values in a C-language array. +** Examples: +** +** SELECT * FROM carray($ptr,5) +** +** The query above returns 5 integers contained in a C-language array +** at the address $ptr. $ptr is a pointer to the array of integers that +** has been cast to an integer. +** +** There is an optional third parameter to determine the datatype of +** the C-language array. Allowed values of the third parameter are +** 'int32', 'int64', 'double', 'char*'. Example: +** +** SELECT * FROM carray($ptr,10,'char*'); +** +** HOW IT WORKS +** +** The carray "function" is really a virtual table with the +** following schema: +** +** CREATE TABLE carray( +** value, +** pointer HIDDEN, +** count HIDDEN, +** ctype TEXT HIDDEN +** ); +** +** If the hidden columns "pointer" and "count" are unconstrained, then +** the virtual table has no rows. Otherwise, the virtual table interprets +** the integer value of "pointer" as a pointer to the array and "count" +** as the number of elements in the array. The virtual table steps through +** the array, element by element. +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include +#include + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** Allowed datatypes +*/ +#define CARRAY_INT32 0 +#define CARRAY_INT64 1 +#define CARRAY_DOUBLE 2 +#define CARRAY_TEXT 3 + +/* +** Names of types +*/ +static const char *azType[] = { "int32", "int64", "double", "char*" }; + + +/* carray_cursor is a subclass of sqlite3_vtab_cursor which will +** serve as the underlying representation of a cursor that scans +** over rows of the result +*/ +typedef struct carray_cursor carray_cursor; +struct carray_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3_int64 iRowid; /* The rowid */ + sqlite3_int64 iPtr; /* Pointer to array of values */ + sqlite3_int64 iCnt; /* Number of integers in the array */ + unsigned char eType; /* One of the CARRAY_type values */ +}; + +/* +** The carrayConnect() method is invoked to create a new +** carray_vtab that describes the carray virtual table. +** +** Think of this routine as the constructor for carray_vtab objects. +** +** All this routine needs to do is: +** +** (1) Allocate the carray_vtab object and initialize all fields. +** +** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the +** result set of queries against carray will look like. +*/ +static int carrayConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + sqlite3_vtab *pNew; + int rc; + +/* Column numbers */ +#define CARRAY_COLUMN_VALUE 0 +#define CARRAY_COLUMN_POINTER 1 +#define CARRAY_COLUMN_COUNT 2 +#define CARRAY_COLUMN_CTYPE 3 + + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)"); + if( rc==SQLITE_OK ){ + pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; +} + +/* +** This method is the destructor for carray_cursor objects. +*/ +static int carrayDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** Constructor for a new carray_cursor object. +*/ +static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + carray_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Destructor for a carray_cursor. +*/ +static int carrayClose(sqlite3_vtab_cursor *cur){ + sqlite3_free(cur); + return SQLITE_OK; +} + + +/* +** Advance a carray_cursor to its next row of output. +*/ +static int carrayNext(sqlite3_vtab_cursor *cur){ + carray_cursor *pCur = (carray_cursor*)cur; + pCur->iRowid++; + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the carray_cursor +** is currently pointing. +*/ +static int carrayColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + carray_cursor *pCur = (carray_cursor*)cur; + sqlite3_int64 x = 0; + switch( i ){ + case CARRAY_COLUMN_POINTER: x = pCur->iPtr; break; + case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break; + case CARRAY_COLUMN_CTYPE: { + sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC); + return SQLITE_OK; + } + default: { + switch( pCur->eType ){ + case CARRAY_INT32: { + int *p = (int*)pCur->iPtr; + sqlite3_result_int(ctx, p[pCur->iRowid-1]); + return SQLITE_OK; + } + case CARRAY_INT64: { + sqlite3_int64 *p = (sqlite3_int64*)pCur->iPtr; + sqlite3_result_int64(ctx, p[pCur->iRowid-1]); + return SQLITE_OK; + } + case CARRAY_DOUBLE: { + double *p = (double*)pCur->iPtr; + sqlite3_result_double(ctx, p[pCur->iRowid-1]); + return SQLITE_OK; + } + case CARRAY_TEXT: { + const char **p = (const char**)pCur->iPtr; + sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT); + return SQLITE_OK; + } + } + } + } + sqlite3_result_int64(ctx, x); + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. In this implementation, the +** rowid is the same as the output value. +*/ +static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + carray_cursor *pCur = (carray_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int carrayEof(sqlite3_vtab_cursor *cur){ + carray_cursor *pCur = (carray_cursor*)cur; + return pCur->iRowid>pCur->iCnt; +} + +/* +** This method is called to "rewind" the carray_cursor object back +** to the first row of output. +*/ +static int carrayFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + carray_cursor *pCur = (carray_cursor *)pVtabCursor; + if( idxNum ){ + pCur->iPtr = sqlite3_value_int64(argv[0]); + pCur->iCnt = sqlite3_value_int64(argv[1]); + if( idxNum<3 ){ + pCur->eType = CARRAY_INT32; + }else{ + unsigned char i; + const char *zType = (const char*)sqlite3_value_text(argv[2]); + for(i=0; i=sizeof(azType)/sizeof(azType[0]) ){ + pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf( + "unknown datatype: %Q", zType); + return SQLITE_ERROR; + }else{ + pCur->eType = i; + } + } + }else{ + pCur->iPtr = 0; + pCur->iCnt = 0; + } + pCur->iRowid = 1; + return SQLITE_OK; +} + +/* +** SQLite will invoke this method one or more times while planning a query +** that uses the carray virtual table. This routine needs to create +** a query plan for each invocation and compute an estimated cost for that +** plan. +** +** In this implementation idxNum is used to represent the +** query plan. idxStr is unused. +** +** idxNum is 2 if the pointer= and count= constraints exist, +** 3 if the ctype= constraint also exists, and is 0 otherwise. +** If idxNum is 0, then carray becomes an empty table. +*/ +static int carrayBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + int i; /* Loop over constraints */ + int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */ + int cntIdx = -1; /* Index of the count= constraint, or -1 if none */ + int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */ + + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pConstraint++){ + if( pConstraint->usable==0 ) continue; + if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; + switch( pConstraint->iColumn ){ + case CARRAY_COLUMN_POINTER: + ptrIdx = i; + break; + case CARRAY_COLUMN_COUNT: + cntIdx = i; + break; + case CARRAY_COLUMN_CTYPE: + ctypeIdx = i; + break; + } + } + if( ptrIdx>=0 && cntIdx>=0 ){ + pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1; + pIdxInfo->aConstraintUsage[ptrIdx].omit = 1; + pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2; + pIdxInfo->aConstraintUsage[cntIdx].omit = 1; + pIdxInfo->estimatedCost = (double)1; + pIdxInfo->estimatedRows = 100; + pIdxInfo->idxNum = 2; + if( ctypeIdx>=0 ){ + pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3; + pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1; + pIdxInfo->idxNum = 3; + } + }else{ + pIdxInfo->estimatedCost = (double)2147483647; + pIdxInfo->estimatedRows = 2147483647; + pIdxInfo->idxNum = 0; + } + return SQLITE_OK; +} + +/* +** This following structure defines all the methods for the +** carray virtual table. +*/ +static sqlite3_module carrayModule = { + 0, /* iVersion */ + 0, /* xCreate */ + carrayConnect, /* xConnect */ + carrayBestIndex, /* xBestIndex */ + carrayDisconnect, /* xDisconnect */ + 0, /* xDestroy */ + carrayOpen, /* xOpen - open a cursor */ + carrayClose, /* xClose - close a cursor */ + carrayFilter, /* xFilter - configure scan constraints */ + carrayNext, /* xNext - advance a cursor */ + carrayEof, /* xEof - check for end of scan */ + carrayColumn, /* xColumn - read data */ + carrayRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_carray_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "carray", &carrayModule, 0); +#endif + return rc; +} diff --git a/ext/misc/csv.c b/ext/misc/csv.c new file mode 100644 index 0000000000..2a9463bd6e --- /dev/null +++ b/ext/misc/csv.c @@ -0,0 +1,869 @@ +/* +** 2016-05-28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains the implementation of an SQLite virtual table for +** reading CSV files. +** +** Usage: +** +** .load ./csv +** CREATE VIRTUAL TABLE temp.csv USING csv(filename=FILENAME); +** SELECT * FROM csv; +** +** The columns are named "c1", "c2", "c3", ... by default. But the +** application can define its own CREATE TABLE statement as an additional +** parameter. For example: +** +** CREATE VIRTUAL TABLE temp.csv2 USING csv( +** filename = "../http.log", +** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)" +** ); +** +** Instead of specifying a file, the text of the CSV can be loaded using +** the data= parameter. +** +** If the columns=N parameter is supplied, then the CSV file is assumed to have +** N columns. If the columns parameter is omitted, the CSV file is opened +** as soon as the virtual table is constructed and the first row of the CSV +** is read in order to count the tables. +** +** Some extra debugging features (used for testing virtual tables) are available +** if this module is compiled with -DSQLITE_TEST. +*/ +#include +SQLITE_EXTENSION_INIT1 +#include +#include +#include +#include +#include +#include + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** A macro to hint to the compiler that a function should not be +** inlined. +*/ +#if defined(__GNUC__) +# define CSV_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) && _MSC_VER>=1310 +# define CSV_NOINLINE __declspec(noinline) +#else +# define CSV_NOINLINE +#endif + + +/* Max size of the error message in a CsvReader */ +#define CSV_MXERR 200 + +/* Size of the CsvReader input buffer */ +#define CSV_INBUFSZ 1024 + +/* A context object used when read a CSV file. */ +typedef struct CsvReader CsvReader; +struct CsvReader { + FILE *in; /* Read the CSV text from this input stream */ + 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 */ + char cTerm; /* Character that terminated the most recent field */ + size_t iIn; /* Next unread character in the input buffer */ + size_t nIn; /* Number of characters in the input buffer */ + char *zIn; /* The input buffer */ + char zErr[CSV_MXERR]; /* Error message */ +}; + +/* Initialize a CsvReader object */ +static void csv_reader_init(CsvReader *p){ + p->in = 0; + p->z = 0; + p->n = 0; + p->nAlloc = 0; + p->nLine = 0; + p->nIn = 0; + p->zIn = 0; + p->zErr[0] = 0; +} + +/* Close and reset a CsvReader object */ +static void csv_reader_reset(CsvReader *p){ + if( p->in ){ + fclose(p->in); + sqlite3_free(p->zIn); + } + sqlite3_free(p->z); + csv_reader_init(p); +} + +/* Report an error on a CsvReader */ +static void csv_errmsg(CsvReader *p, const char *zFormat, ...){ + va_list ap; + va_start(ap, zFormat); + sqlite3_vsnprintf(CSV_MXERR, p->zErr, zFormat, ap); + va_end(ap); +} + +/* Open the file associated with a CsvReader +** Return the number of errors. +*/ +static int csv_reader_open( + CsvReader *p, /* The reader to open */ + const char *zFilename, /* Read from this filename */ + const char *zData /* ... or use this data */ +){ + if( zFilename ){ + p->zIn = sqlite3_malloc( CSV_INBUFSZ ); + if( p->zIn==0 ){ + csv_errmsg(p, "out of memory"); + return 1; + } + p->in = fopen(zFilename, "rb"); + if( p->in==0 ){ + csv_reader_reset(p); + csv_errmsg(p, "cannot open '%s' for reading", zFilename); + return 1; + } + }else{ + assert( p->in==0 ); + p->zIn = (char*)zData; + p->nIn = strlen(zData); + } + return 0; +} + +/* The input buffer has overflowed. Refill the input buffer, then +** return the next character +*/ +static CSV_NOINLINE int csv_getc_refill(CsvReader *p){ + size_t got; + + assert( p->iIn>=p->nIn ); /* Only called on an empty input buffer */ + assert( p->in!=0 ); /* Only called if reading froma file */ + + got = fread(p->zIn, 1, CSV_INBUFSZ, p->in); + if( got==0 ) return EOF; + p->nIn = got; + p->iIn = 1; + return p->zIn[0]; +} + +/* Return the next character of input. Return EOF at end of input. */ +static int csv_getc(CsvReader *p){ + if( p->iIn >= p->nIn ){ + if( p->in!=0 ) return csv_getc_refill(p); + return EOF; + } + return p->zIn[p->iIn++]; +} + +/* Increase the size of p->z and append character c to the end. +** Return 0 on success and non-zero if there is an OOM error */ +static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){ + char *zNew; + int nNew = p->nAlloc*2 + 100; + zNew = sqlite3_realloc64(p->z, nNew); + if( zNew ){ + p->z = zNew; + p->nAlloc = nNew; + p->z[p->n++] = c; + return 0; + }else{ + csv_errmsg(p, "out of memory"); + return 1; + } +} + +/* Append a single character to the CsvReader.z[] array. +** Return 0 on success and non-zero if there is an OOM error */ +static int csv_append(CsvReader *p, char c){ + if( p->n>=p->nAlloc-1 ) return csv_resize_and_append(p, c); + p->z[p->n++] = c; + return 0; +} + +/* Read a single field of CSV text. Compatible with rfc4180 and extended +** with the option of having a separator other than ",". +** +** + Input comes from p->in. +** + Store results in p->z of length p->n. Space to hold p->z comes +** from sqlite3_malloc64(). +** + Keep track of the line number in p->nLine. +** + Store the character that terminates the field in p->cTerm. Store +** EOF on end-of-file. +** +** Return "" at EOF. Return 0 on an OOM error. +*/ +static char *csv_read_one_field(CsvReader *p){ + int c; + p->n = 0; + c = csv_getc(p); + if( c==EOF ){ + p->cTerm = EOF; + return ""; + } + if( c=='"' ){ + int pc, ppc; + int startLine = p->nLine; + pc = ppc = 0; + while( 1 ){ + c = csv_getc(p); + if( c<='"' || pc=='"' ){ + if( c=='\n' ) p->nLine++; + if( c=='"' ){ + if( pc=='"' ){ + pc = 0; + continue; + } + } + if( (c==',' && pc=='"') + || (c=='\n' && pc=='"') + || (c=='\n' && pc=='\r' && ppc=='"') + || (c==EOF && pc=='"') + ){ + do{ p->n--; }while( p->z[p->n]!='"' ); + p->cTerm = (char)c; + break; + } + if( pc=='"' && c!='\r' ){ + csv_errmsg(p, "line %d: unescaped %c character", p->nLine, '"'); + break; + } + if( c==EOF ){ + csv_errmsg(p, "line %d: unterminated %c-quoted field\n", + startLine, '"'); + p->cTerm = (char)c; + break; + } + } + if( csv_append(p, (char)c) ) return 0; + ppc = pc; + pc = c; + } + }else{ + while( c>',' || (c!=EOF && c!=',' && c!='\n') ){ + if( csv_append(p, (char)c) ) return 0; + c = csv_getc(p); + } + if( c=='\n' ){ + p->nLine++; + if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; + } + p->cTerm = (char)c; + } + if( p->z ) p->z[p->n] = 0; + return p->z; +} + + +/* Forward references to the various virtual table methods implemented +** in this file. */ +static int csvtabCreate(sqlite3*, void*, int, const char*const*, + sqlite3_vtab**,char**); +static int csvtabConnect(sqlite3*, void*, int, const char*const*, + sqlite3_vtab**,char**); +static int csvtabBestIndex(sqlite3_vtab*,sqlite3_index_info*); +static int csvtabDisconnect(sqlite3_vtab*); +static int csvtabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**); +static int csvtabClose(sqlite3_vtab_cursor*); +static int csvtabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); +static int csvtabNext(sqlite3_vtab_cursor*); +static int csvtabEof(sqlite3_vtab_cursor*); +static int csvtabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int); +static int csvtabRowid(sqlite3_vtab_cursor*,sqlite3_int64*); + +/* An instance of the CSV virtual table */ +typedef struct CsvTable { + sqlite3_vtab base; /* Base class. Must be first */ + char *zFilename; /* Name of the CSV file */ + char *zData; /* Raw CSV data in lieu of zFilename */ + long iStart; /* Offset to start of data in zFilename */ + int nCol; /* Number of columns in the CSV file */ + unsigned int tstFlags; /* Bit values used for testing */ +} CsvTable; + +/* Allowed values for tstFlags */ +#define CSVTEST_FIDX 0x0001 /* Pretend that constrained searchs cost less*/ + +/* A cursor for the CSV virtual table */ +typedef struct CsvCursor { + sqlite3_vtab_cursor base; /* Base class. Must be first */ + CsvReader rdr; /* The CsvReader object */ + char **azVal; /* Value of the current row */ + int *aLen; /* Length of each entry */ + sqlite3_int64 iRowid; /* The current rowid. Negative for EOF */ +} CsvCursor; + +/* Transfer error message text from a reader into a CsvTable */ +static void csv_xfer_error(CsvTable *pTab, CsvReader *pRdr){ + sqlite3_free(pTab->base.zErrMsg); + pTab->base.zErrMsg = sqlite3_mprintf("%s", pRdr->zErr); +} + +/* +** This method is the destructor fo a CsvTable object. +*/ +static int csvtabDisconnect(sqlite3_vtab *pVtab){ + CsvTable *p = (CsvTable*)pVtab; + sqlite3_free(p->zFilename); + sqlite3_free(p->zData); + sqlite3_free(p); + return SQLITE_OK; +} + +/* Skip leading whitespace. Return a pointer to the first non-whitespace +** character, or to the zero terminator if the string has only whitespace */ +static const char *csv_skip_whitespace(const char *z){ + while( isspace((unsigned char)z[0]) ) z++; + return z; +} + +/* Remove trailing whitespace from the end of string z[] */ +static void csv_trim_whitespace(char *z){ + size_t n = strlen(z); + while( n>0 && isspace((unsigned char)z[n]) ) n--; + z[n] = 0; +} + +/* Dequote the string */ +static void csv_dequote(char *z){ + int j; + char cQuote = z[0]; + size_t i, n; + + if( cQuote!='\'' && cQuote!='"' ) return; + n = strlen(z); + if( n<2 || z[n-1]!=z[0] ) return; + for(i=1, j=0; izErr. If there are no errors, p->zErr[0]==0. +*/ +static int csv_string_parameter( + CsvReader *p, /* Leave the error message here, if there is one */ + const char *zParam, /* Parameter we are checking for */ + const char *zArg, /* Raw text of the virtual table argment */ + char **pzVal /* Write the dequoted string value here */ +){ + const char *zValue; + zValue = csv_parameter(zParam,(int)strlen(zParam),zArg); + if( zValue==0 ) return 0; + p->zErr[0] = 0; + if( *pzVal ){ + csv_errmsg(p, "more than one '%s' parameter", zParam); + return 1; + } + *pzVal = sqlite3_mprintf("%s", zValue); + if( *pzVal==0 ){ + csv_errmsg(p, "out of memory"); + return 1; + } + csv_trim_whitespace(*pzVal); + csv_dequote(*pzVal); + return 1; +} + + +/* Return 0 if the argument is false and 1 if it is true. Return -1 if +** we cannot really tell. +*/ +static int csv_boolean(const char *z){ + if( sqlite3_stricmp("yes",z)==0 + || sqlite3_stricmp("on",z)==0 + || sqlite3_stricmp("true",z)==0 + || (z[0]=='1' && z[0]==0) + ){ + return 1; + } + if( sqlite3_stricmp("no",z)==0 + || sqlite3_stricmp("off",z)==0 + || sqlite3_stricmp("false",z)==0 + || (z[0]=='0' && z[1]==0) + ){ + return 0; + } + return -1; +} + + +/* +** Parameters: +** filename=FILENAME Name of file containing CSV content +** data=TEXT Direct CSV content. +** schema=SCHEMA Alternative CSV schema. +** header=YES|NO First row of CSV defines the names of +** columns if "yes". Default "no". +** columns=N Assume the CSV file contains N columns. +** +** Only available if compiled with SQLITE_TEST: +** +** testflags=N Bitmask of test flags. Optional +** +** If schema= is omitted, then the columns are named "c0", "c1", "c2", +** and so forth. If columns=N is omitted, then the file is opened and +** the number of columns in the first row is counted to determine the +** column count. If header=YES, then the first row is skipped. +*/ +static int csvtabConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + CsvTable *pNew = 0; /* The CsvTable object to construct */ + int bHeader = -1; /* header= flags. -1 means not seen yet */ + int rc = SQLITE_OK; /* Result code from this routine */ + int i, j; /* Loop counters */ +#ifdef SQLITE_TEST + int tstFlags = 0; /* Value for testflags=N parameter */ +#endif + int nCol = -99; /* Value of the columns= parameter */ + CsvReader sRdr; /* A CSV file reader used to store an error + ** message and/or to count the number of columns */ + static const char *azParam[] = { + "filename", "data", "schema", + }; + char *azPValue[3]; /* Parameter values */ +# define CSV_FILENAME (azPValue[0]) +# define CSV_DATA (azPValue[1]) +# define CSV_SCHEMA (azPValue[2]) + + + assert( sizeof(azPValue)==sizeof(azParam) ); + memset(&sRdr, 0, sizeof(sRdr)); + memset(azPValue, 0, sizeof(azPValue)); + for(i=3; i=0 ){ + csv_errmsg(&sRdr, "more than one 'header' parameter"); + goto csvtab_connect_error; + } + x = csv_boolean(zValue); + if( x==1 ){ + bHeader = 1; + }else if( x==0 ){ + bHeader = 0; + }else{ + csv_errmsg(&sRdr, "unrecognized argument to 'header': %s", zValue); + goto csvtab_connect_error; + } + }else +#ifdef SQLITE_TEST + if( (zValue = csv_parameter("testflags",9,z))!=0 ){ + tstFlags = (unsigned int)atoi(zValue); + }else +#endif + if( (zValue = csv_parameter("columns",7,z))!=0 ){ + if( nCol>0 ){ + csv_errmsg(&sRdr, "more than one 'columns' parameter"); + goto csvtab_connect_error; + } + nCol = atoi(zValue); + if( nCol<=0 ){ + csv_errmsg(&sRdr, "must have at least one column"); + goto csvtab_connect_error; + } + }else + { + csv_errmsg(&sRdr, "unrecognized parameter '%s'", z); + goto csvtab_connect_error; + } + } + if( (CSV_FILENAME==0)==(CSV_DATA==0) ){ + csv_errmsg(&sRdr, "must either filename= or data= but not both"); + goto csvtab_connect_error; + } + if( nCol<=0 && csv_reader_open(&sRdr, CSV_FILENAME, CSV_DATA) ){ + goto csvtab_connect_error; + } + pNew = sqlite3_malloc( sizeof(*pNew) ); + *ppVtab = (sqlite3_vtab*)pNew; + if( pNew==0 ) goto csvtab_connect_oom; + memset(pNew, 0, sizeof(*pNew)); + if( nCol>0 ){ + pNew->nCol = nCol; + }else{ + do{ + const char *z = csv_read_one_field(&sRdr); + if( z==0 ) goto csvtab_connect_oom; + pNew->nCol++; + }while( sRdr.cTerm==',' ); + } + pNew->zFilename = CSV_FILENAME; CSV_FILENAME = 0; + pNew->zData = CSV_DATA; CSV_DATA = 0; +#ifdef SQLITE_TEST + pNew->tstFlags = tstFlags; +#endif + pNew->iStart = bHeader==1 ? ftell(sRdr.in) : 0; + csv_reader_reset(&sRdr); + if( CSV_SCHEMA==0 ){ + char *zSep = ""; + CSV_SCHEMA = sqlite3_mprintf("CREATE TABLE x("); + if( CSV_SCHEMA==0 ) goto csvtab_connect_oom; + for(i=0; inCol; i++){ + CSV_SCHEMA = sqlite3_mprintf("%z%sc%d TEXT",CSV_SCHEMA, zSep, i); + zSep = ","; + } + CSV_SCHEMA = sqlite3_mprintf("%z);", CSV_SCHEMA); + } + rc = sqlite3_declare_vtab(db, CSV_SCHEMA); + if( rc ) goto csvtab_connect_error; + for(i=0; ibase); + for(i=0; ibase.pVtab; + int i; + for(i=0; inCol; i++){ + sqlite3_free(pCur->azVal[i]); + pCur->azVal[i] = 0; + pCur->aLen[i] = 0; + } +} + +/* +** The xConnect and xCreate methods do the same thing, but they must be +** different so that the virtual table is not an eponymous virtual table. +*/ +static int csvtabCreate( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + return csvtabConnect(db, pAux, argc, argv, ppVtab, pzErr); +} + +/* +** Destructor for a CsvCursor. +*/ +static int csvtabClose(sqlite3_vtab_cursor *cur){ + CsvCursor *pCur = (CsvCursor*)cur; + csvtabCursorRowReset(pCur); + csv_reader_reset(&pCur->rdr); + sqlite3_free(cur); + return SQLITE_OK; +} + +/* +** Constructor for a new CsvTable cursor object. +*/ +static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + CsvTable *pTab = (CsvTable*)p; + CsvCursor *pCur; + size_t nByte; + nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol; + pCur = sqlite3_malloc64( nByte ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, nByte); + pCur->azVal = (char**)&pCur[1]; + pCur->aLen = (int*)&pCur->azVal[pTab->nCol]; + *ppCursor = &pCur->base; + if( csv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData) ){ + csv_xfer_error(pTab, &pCur->rdr); + return SQLITE_ERROR; + } + return SQLITE_OK; +} + + +/* +** Advance a CsvCursor to its next row of input. +** Set the EOF marker if we reach the end of input. +*/ +static int csvtabNext(sqlite3_vtab_cursor *cur){ + CsvCursor *pCur = (CsvCursor*)cur; + CsvTable *pTab = (CsvTable*)cur->pVtab; + int i = 0; + char *z; + do{ + z = csv_read_one_field(&pCur->rdr); + if( z==0 ){ + csv_xfer_error(pTab, &pCur->rdr); + break; + } + if( inCol ){ + if( pCur->aLen[i] < pCur->rdr.n+1 ){ + char *zNew = sqlite3_realloc64(pCur->azVal[i], pCur->rdr.n+1); + if( zNew==0 ){ + csv_errmsg(&pCur->rdr, "out of memory"); + csv_xfer_error(pTab, &pCur->rdr); + break; + } + pCur->azVal[i] = zNew; + pCur->aLen[i] = pCur->rdr.n+1; + } + memcpy(pCur->azVal[i], z, pCur->rdr.n+1); + i++; + } + }while( pCur->rdr.cTerm==',' ); + while( inCol ){ + sqlite3_free(pCur->azVal[i]); + pCur->azVal[i] = 0; + pCur->aLen[i] = 0; + i++; + } + if( z==0 || pCur->rdr.cTerm==EOF ){ + pCur->iRowid = -1; + }else{ + pCur->iRowid++; + } + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the CsvCursor +** is currently pointing. +*/ +static int csvtabColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + CsvCursor *pCur = (CsvCursor*)cur; + CsvTable *pTab = (CsvTable*)cur->pVtab; + if( i>=0 && inCol && pCur->azVal[i]!=0 ){ + sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_STATIC); + } + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. +*/ +static int csvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + CsvCursor *pCur = (CsvCursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int csvtabEof(sqlite3_vtab_cursor *cur){ + CsvCursor *pCur = (CsvCursor*)cur; + return pCur->iRowid<0; +} + +/* +** Only a full table scan is supported. So xFilter simply rewinds to +** the beginning. +*/ +static int csvtabFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + CsvCursor *pCur = (CsvCursor*)pVtabCursor; + CsvTable *pTab = (CsvTable*)pVtabCursor->pVtab; + pCur->iRowid = 0; + if( pCur->rdr.in==0 ){ + assert( pCur->rdr.zIn==pTab->zData ); + assert( pTab->iStart>=0 ); + assert( (size_t)pTab->iStart<=pCur->rdr.nIn ); + pCur->rdr.iIn = pTab->iStart; + }else{ + fseek(pCur->rdr.in, pTab->iStart, SEEK_SET); + pCur->rdr.iIn = 0; + pCur->rdr.nIn = 0; + } + return csvtabNext(pVtabCursor); +} + +/* +** Only a forward full table scan is supported. xBestIndex is mostly +** a no-op. If CSVTEST_FIDX is set, then the presence of equality +** constraints lowers the estimated cost, which is fiction, but is useful +** for testing certain kinds of virtual table behavior. +*/ +static int csvtabBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + pIdxInfo->estimatedCost = 1000000; +#ifdef SQLITE_TEST + if( (((CsvTable*)tab)->tstFlags & CSVTEST_FIDX)!=0 ){ + /* The usual (and sensible) case is to always do a full table scan. + ** The code in this branch only runs when testflags=1. This code + ** generates an artifical and unrealistic plan which is useful + ** for testing virtual table logic but is not helpful to real applications. + ** + ** Any ==, LIKE, or GLOB constraint is marked as usable by the virtual + ** table (even though it is not) and the cost of running the virtual table + ** is reduced from 1 million to just 10. The constraints are *not* marked + ** as omittable, however, so the query planner should still generate a + ** plan that gives a correct answer, even if they plan is not optimal. + */ + int i; + int nConst = 0; + for(i=0; inConstraint; i++){ + unsigned char op; + if( pIdxInfo->aConstraint[i].usable==0 ) continue; + op = pIdxInfo->aConstraint[i].op; + if( op==SQLITE_INDEX_CONSTRAINT_EQ + || op==SQLITE_INDEX_CONSTRAINT_LIKE + || op==SQLITE_INDEX_CONSTRAINT_GLOB + ){ + pIdxInfo->estimatedCost = 10; + pIdxInfo->aConstraintUsage[nConst].argvIndex = nConst+1; + nConst++; + } + } + } +#endif + return SQLITE_OK; +} + + +static sqlite3_module CsvModule = { + 0, /* iVersion */ + csvtabCreate, /* xCreate */ + csvtabConnect, /* xConnect */ + csvtabBestIndex, /* xBestIndex */ + csvtabDisconnect, /* xDisconnect */ + csvtabDisconnect, /* xDestroy */ + csvtabOpen, /* xOpen - open a cursor */ + csvtabClose, /* xClose - close a cursor */ + csvtabFilter, /* xFilter - configure scan constraints */ + csvtabNext, /* xNext - advance a cursor */ + csvtabEof, /* xEof - check for end of scan */ + csvtabColumn, /* xColumn - read data */ + csvtabRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#ifdef SQLITE_TEST +/* +** For virtual table testing, make a version of the CSV virtual table +** available that has an xUpdate function. But the xUpdate always returns +** SQLITE_READONLY since the CSV file is not really writable. +*/ +static int csvtabUpdate(sqlite3_vtab *p,int n,sqlite3_value**v,sqlite3_int64*x){ + return SQLITE_READONLY; +} +static sqlite3_module CsvModuleFauxWrite = { + 0, /* iVersion */ + csvtabCreate, /* xCreate */ + csvtabConnect, /* xConnect */ + csvtabBestIndex, /* xBestIndex */ + csvtabDisconnect, /* xDisconnect */ + csvtabDisconnect, /* xDestroy */ + csvtabOpen, /* xOpen - open a cursor */ + csvtabClose, /* xClose - close a cursor */ + csvtabFilter, /* xFilter - configure scan constraints */ + csvtabNext, /* xNext - advance a cursor */ + csvtabEof, /* xEof - check for end of scan */ + csvtabColumn, /* xColumn - read data */ + csvtabRowid, /* xRowid - read data */ + csvtabUpdate, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; +#endif /* SQLITE_TEST */ + +#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ + + +#ifdef _WIN32 +__declspec(dllexport) +#endif +/* +** This routine is called when the extension is loaded. The new +** CSV virtual table module is registered with the calling database +** connection. +*/ +int sqlite3_csv_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + int rc; + SQLITE_EXTENSION_INIT2(pApi); + rc = sqlite3_create_module(db, "csv", &CsvModule, 0); +#ifdef SQLITE_TEST + if( rc==SQLITE_OK ){ + rc = sqlite3_create_module(db, "csv_wr", &CsvModuleFauxWrite, 0); + } +#endif + return rc; +#else + return SQLITE_OK; +#endif +} diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 99d299c29c..61c52ebe61 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -1211,6 +1211,26 @@ static void jsonTest1Func( ** Scalar SQL function implementations ****************************************************************************/ +/* +** Implementation of the json_QUOTE(VALUE) function. Return a JSON value +** corresponding to the SQL value input. Mostly this means putting +** double-quotes around strings and returning the unquoted string "null" +** when given a NULL input. +*/ +static void jsonQuoteFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonString jx; + UNUSED_PARAM(argc); + + jsonInit(&jx, ctx); + jsonAppendValue(&jx, argv[0]); + jsonResult(&jx); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); +} + /* ** Implementation of the json_array(VALUE,...) function. Return a JSON ** array that contains all values given in arguments. Or if any argument @@ -2124,6 +2144,7 @@ int sqlite3Json1Init(sqlite3 *db){ { "json_extract", -1, 0, jsonExtractFunc }, { "json_insert", -1, 0, jsonSetFunc }, { "json_object", -1, 0, jsonObjectFunc }, + { "json_quote", 1, 0, jsonQuoteFunc }, { "json_remove", -1, 0, jsonRemoveFunc }, { "json_replace", -1, 0, jsonReplaceFunc }, { "json_set", -1, 1, jsonSetFunc }, diff --git a/ext/misc/percentile.c b/ext/misc/percentile.c index 74a4c9d12b..a5d7e84913 100644 --- a/ext/misc/percentile.c +++ b/ext/misc/percentile.c @@ -167,7 +167,7 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){ /* ** Compare to doubles for sorting using qsort() */ -static int doubleCmp(const void *pA, const void *pB){ +static int SQLITE_CDECL doubleCmp(const void *pA, const void *pB){ double a = *(double*)pA; double b = *(double*)pB; if( a==b ) return 0; diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c new file mode 100644 index 0000000000..92718e23d1 --- /dev/null +++ b/ext/misc/scrub.c @@ -0,0 +1,610 @@ +/* +** 2016-05-05 +** +** 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 utility function (and a utility program) that +** makes a copy of an SQLite database while simultaneously zeroing out all +** deleted content. +** +** Normally (when PRAGMA secure_delete=OFF, which is the default) when SQLite +** deletes content, it does not overwrite the deleted content but rather marks +** the region of the file that held that content as being reusable. This can +** cause deleted content to recoverable from the database file. This stale +** content is removed by the VACUUM command, but VACUUM can be expensive for +** large databases. When in PRAGMA secure_delete=ON mode, the deleted content +** is zeroed, but secure_delete=ON has overhead as well. +** +** This utility attempts to make a copy of a complete SQLite database where +** all of the deleted content is zeroed out in the copy, and it attempts to +** do so while being faster than running VACUUM. +** +** Usage: +** +** int sqlite3_scrub_backup( +** const char *zSourceFile, // Source database filename +** const char *zDestFile, // Destination database filename +** char **pzErrMsg // Write error message here +** ); +** +** Simply call the API above specifying the filename of the source database +** and the name of the backup copy. The source database must already exist +** and can be in active use. (A read lock is held during the backup.) The +** destination file should not previously exist. If the pzErrMsg parameter +** is non-NULL and if an error occurs, then an error message might be written +** into memory obtained from sqlite3_malloc() and *pzErrMsg made to point to +** that error message. But if the error is an OOM, the error might not be +** reported. The routine always returns non-zero if there is an error. +** +** If compiled with -DSCRUB_STANDALONE then a main() procedure is added and +** this file becomes a standalone program that can be run as follows: +** +** ./sqlite3scrub SOURCE DEST +*/ +#include "sqlite3.h" +#include +#include +#include +#include +#include + +typedef struct ScrubState ScrubState; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + + +/* State information for a scrub-and-backup operation */ +struct ScrubState { + const char *zSrcFile; /* Name of the source file */ + const char *zDestFile; /* Name of the destination file */ + int rcErr; /* Error code */ + char *zErr; /* Error message text */ + sqlite3 *dbSrc; /* Source database connection */ + sqlite3_file *pSrc; /* Source file handle */ + sqlite3 *dbDest; /* Destination database connection */ + sqlite3_file *pDest; /* Destination file handle */ + u32 szPage; /* Page size */ + u32 szUsable; /* Usable bytes on each page */ + u32 nPage; /* Number of pages */ + u32 iLastPage; /* Page number of last page written so far*/ + u8 *page1; /* Content of page 1 */ +}; + +/* Store an error message */ +static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){ + va_list ap; + sqlite3_free(p->zErr); + va_start(ap, zFormat); + p->zErr = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( p->rcErr==0 ) p->rcErr = SQLITE_ERROR; +} + +/* Allocate memory to hold a single page of content */ +static u8 *scrubBackupAllocPage(ScrubState *p){ + u8 *pPage; + if( p->rcErr ) return 0; + pPage = sqlite3_malloc( p->szPage ); + if( pPage==0 ) p->rcErr = SQLITE_NOMEM; + return pPage; +} + +/* Read a page from the source database into memory. Use the memory +** provided by pBuf if not NULL or allocate a new page if pBuf==NULL. +*/ +static u8 *scrubBackupRead(ScrubState *p, int pgno, u8 *pBuf){ + int rc; + sqlite3_int64 iOff; + u8 *pOut = pBuf; + if( p->rcErr ) return 0; + if( pOut==0 ){ + pOut = scrubBackupAllocPage(p); + if( pOut==0 ) return 0; + } + iOff = (pgno-1)*(sqlite3_int64)p->szPage; + rc = p->pSrc->pMethods->xRead(p->pSrc, pOut, p->szPage, iOff); + if( rc!=SQLITE_OK ){ + if( pBuf==0 ) sqlite3_free(pOut); + pOut = 0; + scrubBackupErr(p, "read failed for page %d", pgno); + p->rcErr = SQLITE_IOERR; + } + return pOut; +} + +/* Write a page to the destination database */ +static void scrubBackupWrite(ScrubState *p, int pgno, const u8 *pData){ + int rc; + sqlite3_int64 iOff; + if( p->rcErr ) return; + iOff = (pgno-1)*(sqlite3_int64)p->szPage; + rc = p->pDest->pMethods->xWrite(p->pDest, pData, p->szPage, iOff); + if( rc!=SQLITE_OK ){ + scrubBackupErr(p, "write failed for page %d", pgno); + p->rcErr = SQLITE_IOERR; + } + if( pgno>p->iLastPage ) p->iLastPage = pgno; +} + +/* Prepare a statement against the "db" database. */ +static sqlite3_stmt *scrubBackupPrepare( + ScrubState *p, /* Backup context */ + sqlite3 *db, /* Database to prepare against */ + const char *zSql /* SQL statement */ +){ + sqlite3_stmt *pStmt; + if( p->rcErr ) return 0; + p->rcErr = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( p->rcErr ){ + scrubBackupErr(p, "SQL error \"%s\" on \"%s\"", + sqlite3_errmsg(db), zSql); + sqlite3_finalize(pStmt); + return 0; + } + return pStmt; +} + + +/* Open the source database file */ +static void scrubBackupOpenSrc(ScrubState *p){ + sqlite3_stmt *pStmt; + int rc; + /* Open the source database file */ + p->rcErr = sqlite3_open_v2(p->zSrcFile, &p->dbSrc, + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0); + if( p->rcErr ){ + scrubBackupErr(p, "cannot open source database: %s", + sqlite3_errmsg(p->dbSrc)); + return; + } + p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_master; BEGIN;", + 0, 0, 0); + if( p->rcErr ){ + scrubBackupErr(p, + "cannot start a read transaction on the source database: %s", + sqlite3_errmsg(p->dbSrc)); + return; + } + rc = sqlite3_wal_checkpoint_v2(p->dbSrc, "main", SQLITE_CHECKPOINT_FULL, + 0, 0); + if( rc ){ + scrubBackupErr(p, "cannot checkpoint the source database"); + return; + } + pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_size"); + if( pStmt==0 ) return; + rc = sqlite3_step(pStmt); + if( rc==SQLITE_ROW ){ + p->szPage = sqlite3_column_int(pStmt, 0); + }else{ + scrubBackupErr(p, "unable to determine the page size"); + } + sqlite3_finalize(pStmt); + if( p->rcErr ) return; + pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_count"); + if( pStmt==0 ) return; + rc = sqlite3_step(pStmt); + if( rc==SQLITE_ROW ){ + p->nPage = sqlite3_column_int(pStmt, 0); + }else{ + scrubBackupErr(p, "unable to determine the size of the source database"); + } + sqlite3_finalize(pStmt); + sqlite3_file_control(p->dbSrc, "main", SQLITE_FCNTL_FILE_POINTER, &p->pSrc); + if( p->pSrc==0 || p->pSrc->pMethods==0 ){ + scrubBackupErr(p, "cannot get the source file handle"); + p->rcErr = SQLITE_ERROR; + } +} + +/* Create and open the destination file */ +static void scrubBackupOpenDest(ScrubState *p){ + sqlite3_stmt *pStmt; + int rc; + char *zSql; + if( p->rcErr ) return; + p->rcErr = sqlite3_open_v2(p->zDestFile, &p->dbDest, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | + SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0); + if( p->rcErr ){ + scrubBackupErr(p, "cannot open destination database: %s", + sqlite3_errmsg(p->dbDest)); + return; + } + zSql = sqlite3_mprintf("PRAGMA page_size(%u);", p->szPage); + if( zSql==0 ){ + p->rcErr = SQLITE_NOMEM; + return; + } + p->rcErr = sqlite3_exec(p->dbDest, zSql, 0, 0, 0); + sqlite3_free(zSql); + if( p->rcErr ){ + scrubBackupErr(p, + "cannot set the page size on the destination database: %s", + sqlite3_errmsg(p->dbDest)); + return; + } + sqlite3_exec(p->dbDest, "PRAGMA journal_mode=OFF;", 0, 0, 0); + p->rcErr = sqlite3_exec(p->dbDest, "BEGIN EXCLUSIVE;", 0, 0, 0); + if( p->rcErr ){ + scrubBackupErr(p, + "cannot start a write transaction on the destination database: %s", + sqlite3_errmsg(p->dbDest)); + return; + } + pStmt = scrubBackupPrepare(p, p->dbDest, "PRAGMA page_count;"); + if( pStmt==0 ) return; + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + scrubBackupErr(p, "cannot measure the size of the destination"); + }else if( sqlite3_column_int(pStmt, 0)>1 ){ + scrubBackupErr(p, "destination database is not empty - holds %d pages", + sqlite3_column_int(pStmt, 0)); + } + sqlite3_finalize(pStmt); + sqlite3_file_control(p->dbDest, "main", SQLITE_FCNTL_FILE_POINTER, &p->pDest); + if( p->pDest==0 || p->pDest->pMethods==0 ){ + scrubBackupErr(p, "cannot get the destination file handle"); + p->rcErr = SQLITE_ERROR; + } +} + +/* Read a 32-bit big-endian integer */ +static u32 scrubBackupInt32(const u8 *a){ + u32 v = a[3]; + v += ((u32)a[2])<<8; + v += ((u32)a[1])<<16; + v += ((u32)a[0])<<24; + return v; +} + +/* Read a 16-bit big-endian integer */ +static u32 scrubBackupInt16(const u8 *a){ + return (a[0]<<8) + a[1]; +} + +/* +** Read a varint. Put the value in *pVal and return the number of bytes. +*/ +static int scrubBackupVarint(const u8 *z, sqlite3_int64 *pVal){ + sqlite3_int64 v = 0; + int i; + for(i=0; i<8; i++){ + v = (v<<7) + (z[i]&0x7f); + if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } + } + v = (v<<8) + (z[i]&0xff); + *pVal = v; + return 9; +} + +/* +** Return the number of bytes in a varint. +*/ +static int scrubBackupVarintSize(const u8 *z){ + int i; + for(i=0; i<8; i++){ + if( (z[i]&0x80)==0 ){ return i+1; } + } + return 9; +} + +/* +** Copy the freelist trunk page given, and all its descendents, +** zeroing out as much as possible in the process. +*/ +static void scrubBackupFreelist(ScrubState *p, int pgno, u32 nFree){ + u8 *a, *aBuf; + u32 n, mx; + + if( p->rcErr ) return; + aBuf = scrubBackupAllocPage(p); + if( aBuf==0 ) return; + + while( pgno && nFree){ + a = scrubBackupRead(p, pgno, aBuf); + if( a==0 ) break; + n = scrubBackupInt32(&a[4]); + mx = p->szUsable/4 - 2; + if( nszPage); + } + scrubBackupWrite(p, iLeaf, aZero); + nFree--; + } +#endif + } + sqlite3_free(aBuf); +} + +/* +** Copy an overflow chain from source to destination. Zero out any +** unused tail at the end of the overflow chain. +*/ +static void scrubBackupOverflow(ScrubState *p, int pgno, u32 nByte){ + u8 *a, *aBuf; + + aBuf = scrubBackupAllocPage(p); + if( aBuf==0 ) return; + while( nByte>0 && pgno!=0 ){ + a = scrubBackupRead(p, pgno, aBuf); + if( a==0 ) break; + if( nByte >= (p->szUsable)-4 ){ + nByte -= (p->szUsable) - 4; + }else{ + u32 x = (p->szUsable - 4) - nByte; + u32 i = p->szUsable - x; + memset(&a[i], 0, x); + nByte = 0; + } + scrubBackupWrite(p, pgno, a); + pgno = scrubBackupInt32(a); + } + sqlite3_free(aBuf); +} + + +/* +** Copy B-Tree page pgno, and all of its children, from source to destination. +** Zero out deleted content during the copy. +*/ +static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ + u8 *a; + u32 i, n, pc; + u32 nCell; + u32 nPrefix; + u32 szHdr; + u32 iChild; + u8 *aTop; + u8 *aCell; + u32 x, y; + int ln = 0; + + + if( p->rcErr ) return; + if( iDepth>50 ){ + scrubBackupErr(p, "corrupt: b-tree too deep at page %d", pgno); + return; + } + if( pgno==1 ){ + a = p->page1; + }else{ + a = scrubBackupRead(p, pgno, 0); + if( a==0 ) return; + } + nPrefix = pgno==1 ? 100 : 0; + aTop = &a[nPrefix]; + szHdr = 8 + 4*(aTop[0]==0x02 || aTop[0]==0x05); + aCell = aTop + szHdr; + nCell = scrubBackupInt16(&aTop[3]); + + /* Zero out the gap between the cell index and the start of the + ** cell content area */ + x = scrubBackupInt16(&aTop[5]); /* First byte of cell content area */ + if( x>p->szUsable ){ ln=__LINE__; goto btree_corrupt; } + y = szHdr + nPrefix + nCell*2; + if( y>x ){ ln=__LINE__; goto btree_corrupt; } + if( y0 && pc(p->szUsable)-4 ){ ln=__LINE__; goto btree_corrupt; } + n = scrubBackupInt16(&a[pc+2]); + if( pc+n>(p->szUsable) ){ ln=__LINE__; goto btree_corrupt; } + if( n>4 ) memset(&a[pc+4], 0, n-4); + x = scrubBackupInt16(&a[pc]); + if( x0 ){ ln=__LINE__; goto btree_corrupt; } + pc = x; + } + + /* Write this one page */ + scrubBackupWrite(p, pgno, a); + + /* Walk the tree and process child pages */ + for(i=0; i p->szUsable-3 ){ ln=__LINE__; goto btree_corrupt; } + if( aTop[0]==0x05 || aTop[0]==0x02 ){ + if( pc+4 > p->szUsable ){ ln=__LINE__; goto btree_corrupt; } + iChild = scrubBackupInt32(&a[pc]); + pc += 4; + scrubBackupBtree(p, iChild, iDepth+1); + if( aTop[0]==0x05 ) continue; + } + pc += scrubBackupVarint(&a[pc], &P); + if( pc >= p->szUsable ){ ln=__LINE__; goto btree_corrupt; } + if( aTop[0]==0x0d ){ + X = p->szUsable - 35; + }else{ + X = ((p->szUsable - 12)*64/255) - 23; + } + if( P<=X ){ + /* All content is local. No overflow */ + continue; + } + M = ((p->szUsable - 12)*32/255)-23; + K = M + ((P-M)%(p->szUsable-4)); + if( aTop[0]==0x0d ){ + pc += scrubBackupVarintSize(&a[pc]); + if( pc > (p->szUsable-4) ){ ln=__LINE__; goto btree_corrupt; } + } + nLocal = K<=X ? K : M; + if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; } + iChild = scrubBackupInt32(&a[pc+nLocal]); + scrubBackupOverflow(p, iChild, P-nLocal); + } + + /* Walk the right-most tree */ + if( aTop[0]==0x05 || aTop[0]==0x02 ){ + iChild = scrubBackupInt32(&aTop[8]); + scrubBackupBtree(p, iChild, iDepth+1); + } + + /* All done */ + if( pgno>1 ) sqlite3_free(a); + return; + +btree_corrupt: + scrubBackupErr(p, "corruption on page %d of source database (errid=%d)", + pgno, ln); + if( pgno>1 ) sqlite3_free(a); +} + +/* +** Copy all ptrmap pages from source to destination. +** This routine is only called if the source database is in autovacuum +** or incremental vacuum mode. +*/ +static void scrubBackupPtrmap(ScrubState *p){ + u32 pgno = 2; + u32 J = p->szUsable/5; + u32 iLock = (1073742335/p->szPage)+1; + u8 *a, *pBuf; + if( p->rcErr ) return; + pBuf = scrubBackupAllocPage(p); + if( pBuf==0 ) return; + while( pgno<=p->nPage ){ + a = scrubBackupRead(p, pgno, pBuf); + if( a==0 ) break; + scrubBackupWrite(p, pgno, a); + pgno += J+1; + if( pgno==iLock ) pgno++; + } + sqlite3_free(pBuf); +} + +int sqlite3_scrub_backup( + const char *zSrcFile, /* Source file */ + const char *zDestFile, /* Destination file */ + char **pzErr /* Write error here if non-NULL */ +){ + ScrubState s; + u32 n, i; + sqlite3_stmt *pStmt; + + memset(&s, 0, sizeof(s)); + s.zSrcFile = zSrcFile; + s.zDestFile = zDestFile; + + /* Open both source and destination databases */ + scrubBackupOpenSrc(&s); + scrubBackupOpenDest(&s); + + /* Read in page 1 */ + s.page1 = scrubBackupRead(&s, 1, 0); + if( s.page1==0 ) goto scrub_abort; + s.szUsable = s.szPage - s.page1[20]; + + /* Copy the freelist */ + n = scrubBackupInt32(&s.page1[36]); + i = scrubBackupInt32(&s.page1[32]); + if( n ) scrubBackupFreelist(&s, i, n); + + /* Copy ptrmap pages */ + n = scrubBackupInt32(&s.page1[52]); + if( n ) scrubBackupPtrmap(&s); + + /* Copy all of the btrees */ + scrubBackupBtree(&s, 1, 0); + pStmt = scrubBackupPrepare(&s, s.dbSrc, + "SELECT rootpage FROM sqlite_master WHERE coalesce(rootpage,0)>0"); + if( pStmt==0 ) goto scrub_abort; + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + i = (u32)sqlite3_column_int(pStmt, 0); + scrubBackupBtree(&s, i, 0); + } + sqlite3_finalize(pStmt); + + /* If the last page of the input db file is a free-list leaf, then the + ** backup file on disk is still smaller than the size indicated within + ** the database header. In this case, write a page of zeroes to the + ** last page of the backup database so that SQLite does not mistakenly + ** think the db is corrupt. */ + if( s.iLastPageiScore - b->iScore; diff --git a/ext/misc/vfsstat.c b/ext/misc/vfsstat.c new file mode 100644 index 0000000000..14753d277d --- /dev/null +++ b/ext/misc/vfsstat.c @@ -0,0 +1,816 @@ +/* +** 2016-05-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. +** +****************************************************************************** +** +** This file contains the implementation of an SQLite vfs shim that +** tracks I/O. Access to the accumulated status counts is provided using +** an eponymous virtual table. +*/ +#include +SQLITE_EXTENSION_INIT1 + +/* +** This module contains code for a wrapper VFS that cause stats for +** most VFS calls to be recorded. +** +** To use this module, first compile it as a loadable extension. See +** https://www.sqlite.org/loadext.html#build for compilations instructions. +** +** After compliing, load this extension, then open database connections to be +** measured. Query usages status using the vfsstat virtual table: +** +** SELECT * FROM vfsstat; +** +** Reset counters using UPDATE statements against vfsstat: +** +** UPDATE vfsstat SET count=0; +** +** EXAMPLE SCRIPT: +** +** .load ./vfsstat +** .open test.db +** DROP TABLE IF EXISTS t1; +** CREATE TABLE t1(x,y); +** INSERT INTO t1 VALUES(123, randomblob(5000)); +** CREATE INDEX t1x ON t1(x); +** DROP TABLE t1; +** VACUUM; +** SELECT * FROM vfsstat WHERE count>0; +** +** LIMITATIONS: +** +** This module increments counters without using mutex protection. So if +** two or more threads try to use this module at the same time, race conditions +** may occur which mess up the counts. This is harmless, other than giving +** incorrect statistics. +*/ +#include +#include +#include + +/* +** File types +*/ +#define VFSSTAT_MAIN 0 /* Main database file */ +#define VFSSTAT_JOURNAL 1 /* Rollback journal */ +#define VFSSTAT_WAL 2 /* Write-ahead log file */ +#define VFSSTAT_MASTERJRNL 3 /* Master journal */ +#define VFSSTAT_SUBJRNL 4 /* Subjournal */ +#define VFSSTAT_TEMPDB 5 /* TEMP database */ +#define VFSSTAT_TEMPJRNL 6 /* Journal for TEMP database */ +#define VFSSTAT_TRANSIENT 7 /* Transient database */ +#define VFSSTAT_ANY 8 /* Unspecified file type */ +#define VFSSTAT_nFile 9 /* This many file types */ + +/* Names of the file types. These are allowed values for the +** first column of the vfsstat virtual table. +*/ +static const char *azFile[] = { + "database", "journal", "wal", "master-journal", "sub-journal", + "temp-database", "temp-journal", "transient-db", "*" +}; + +/* +** Stat types +*/ +#define VFSSTAT_BYTESIN 0 /* Bytes read in */ +#define VFSSTAT_BYTESOUT 1 /* Bytes written out */ +#define VFSSTAT_READ 2 /* Read requests */ +#define VFSSTAT_WRITE 3 /* Write requests */ +#define VFSSTAT_SYNC 4 /* Syncs */ +#define VFSSTAT_OPEN 5 /* File opens */ +#define VFSSTAT_LOCK 6 /* Lock requests */ +#define VFSSTAT_ACCESS 0 /* xAccess calls. filetype==ANY only */ +#define VFSSTAT_DELETE 1 /* xDelete calls. filetype==ANY only */ +#define VFSSTAT_FULLPATH 2 /* xFullPathname calls. ANY only */ +#define VFSSTAT_RANDOM 3 /* xRandomness calls. ANY only */ +#define VFSSTAT_SLEEP 4 /* xSleep calls. ANY only */ +#define VFSSTAT_CURTIME 5 /* xCurrentTime calls. ANY only */ +#define VFSSTAT_nStat 7 /* This many stat types */ + + +/* Names for the second column of the vfsstat virtual table for all +** cases except when the first column is "*" or VFSSTAT_ANY. */ +static const char *azStat[] = { + "bytes-in", "bytes-out", "read", "write", "sync", "open", "lock", +}; +static const char *azStatAny[] = { + "access", "delete", "fullpathname", "randomness", "sleep", "currenttimestamp", + "not-used" +}; + +/* Total number of counters */ +#define VFSSTAT_MXCNT (VFSSTAT_nStat*VFSSTAT_nFile) + +/* +** Performance stats are collected in an instance of the following +** global array. +*/ +static sqlite3_uint64 aVfsCnt[VFSSTAT_MXCNT]; + +/* +** Access to a specific counter +*/ +#define STATCNT(filetype,stat) (aVfsCnt[(filetype)*VFSSTAT_nStat+(stat)]) + +/* +** Forward declaration of objects used by this utility +*/ +typedef struct VStatVfs VStatVfs; +typedef struct VStatFile VStatFile; + +/* An instance of the VFS */ +struct VStatVfs { + sqlite3_vfs base; /* VFS methods */ + sqlite3_vfs *pVfs; /* Parent VFS */ +}; + +/* An open file */ +struct VStatFile { + sqlite3_file base; /* IO methods */ + sqlite3_file *pReal; /* Underlying file handle */ + unsigned char eFiletype; /* What type of file is this */ +}; + +#define REALVFS(p) (((VStatVfs*)(p))->pVfs) + +/* +** Methods for VStatFile +*/ +static int vstatClose(sqlite3_file*); +static int vstatRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int vstatWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); +static int vstatTruncate(sqlite3_file*, sqlite3_int64 size); +static int vstatSync(sqlite3_file*, int flags); +static int vstatFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int vstatLock(sqlite3_file*, int); +static int vstatUnlock(sqlite3_file*, int); +static int vstatCheckReservedLock(sqlite3_file*, int *pResOut); +static int vstatFileControl(sqlite3_file*, int op, void *pArg); +static int vstatSectorSize(sqlite3_file*); +static int vstatDeviceCharacteristics(sqlite3_file*); +static int vstatShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**); +static int vstatShmLock(sqlite3_file*, int offset, int n, int flags); +static void vstatShmBarrier(sqlite3_file*); +static int vstatShmUnmap(sqlite3_file*, int deleteFlag); +static int vstatFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); +static int vstatUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); + +/* +** Methods for VStatVfs +*/ +static int vstatOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +static int vstatDelete(sqlite3_vfs*, const char *zName, int syncDir); +static int vstatAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int vstatFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); +static void *vstatDlOpen(sqlite3_vfs*, const char *zFilename); +static void vstatDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); +static void vstatDlClose(sqlite3_vfs*, void*); +static int vstatRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int vstatSleep(sqlite3_vfs*, int microseconds); +static int vstatCurrentTime(sqlite3_vfs*, double*); +static int vstatGetLastError(sqlite3_vfs*, int, char *); +static int vstatCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); + +static VStatVfs vstat_vfs = { + { + 2, /* iVersion */ + 0, /* szOsFile (set by register_vstat()) */ + 1024, /* mxPathname */ + 0, /* pNext */ + "vfslog", /* zName */ + 0, /* pAppData */ + vstatOpen, /* xOpen */ + vstatDelete, /* xDelete */ + vstatAccess, /* xAccess */ + vstatFullPathname, /* xFullPathname */ + vstatDlOpen, /* xDlOpen */ + vstatDlError, /* xDlError */ + vstatDlSym, /* xDlSym */ + vstatDlClose, /* xDlClose */ + vstatRandomness, /* xRandomness */ + vstatSleep, /* xSleep */ + vstatCurrentTime, /* xCurrentTime */ + vstatGetLastError, /* xGetLastError */ + vstatCurrentTimeInt64 /* xCurrentTimeInt64 */ + }, + 0 +}; + +static const sqlite3_io_methods vstat_io_methods = { + 3, /* iVersion */ + vstatClose, /* xClose */ + vstatRead, /* xRead */ + vstatWrite, /* xWrite */ + vstatTruncate, /* xTruncate */ + vstatSync, /* xSync */ + vstatFileSize, /* xFileSize */ + vstatLock, /* xLock */ + vstatUnlock, /* xUnlock */ + vstatCheckReservedLock, /* xCheckReservedLock */ + vstatFileControl, /* xFileControl */ + vstatSectorSize, /* xSectorSize */ + vstatDeviceCharacteristics, /* xDeviceCharacteristics */ + vstatShmMap, /* xShmMap */ + vstatShmLock, /* xShmLock */ + vstatShmBarrier, /* xShmBarrier */ + vstatShmUnmap, /* xShmUnmap */ + vstatFetch, /* xFetch */ + vstatUnfetch /* xUnfetch */ +}; + + + +/* +** Close an vstat-file. +*/ +static int vstatClose(sqlite3_file *pFile){ + VStatFile *p = (VStatFile *)pFile; + int rc = SQLITE_OK; + + if( p->pReal->pMethods ){ + rc = p->pReal->pMethods->xClose(p->pReal); + } + return rc; +} + + +/* +** Read data from an vstat-file. +*/ +static int vstatRead( + sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + int rc; + VStatFile *p = (VStatFile *)pFile; + + rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); + STATCNT(p->eFiletype,VFSSTAT_READ)++; + if( rc==SQLITE_OK ){ + STATCNT(p->eFiletype,VFSSTAT_BYTESIN) += iAmt; + } + return rc; +} + +/* +** Write data to an vstat-file. +*/ +static int vstatWrite( + sqlite3_file *pFile, + const void *z, + int iAmt, + sqlite_int64 iOfst +){ + int rc; + VStatFile *p = (VStatFile *)pFile; + + rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst); + STATCNT(p->eFiletype,VFSSTAT_WRITE)++; + if( rc==SQLITE_OK ){ + STATCNT(p->eFiletype,VFSSTAT_BYTESOUT) += iAmt; + } + return rc; +} + +/* +** Truncate an vstat-file. +*/ +static int vstatTruncate(sqlite3_file *pFile, sqlite_int64 size){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xTruncate(p->pReal, size); + return rc; +} + +/* +** Sync an vstat-file. +*/ +static int vstatSync(sqlite3_file *pFile, int flags){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xSync(p->pReal, flags); + STATCNT(p->eFiletype,VFSSTAT_SYNC)++; + return rc; +} + +/* +** Return the current file-size of an vstat-file. +*/ +static int vstatFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); + return rc; +} + +/* +** Lock an vstat-file. +*/ +static int vstatLock(sqlite3_file *pFile, int eLock){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xLock(p->pReal, eLock); + STATCNT(p->eFiletype,VFSSTAT_LOCK)++; + return rc; +} + +/* +** Unlock an vstat-file. +*/ +static int vstatUnlock(sqlite3_file *pFile, int eLock){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); + STATCNT(p->eFiletype,VFSSTAT_LOCK)++; + return rc; +} + +/* +** Check if another file-handle holds a RESERVED lock on an vstat-file. +*/ +static int vstatCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); + STATCNT(p->eFiletype,VFSSTAT_LOCK)++; + return rc; +} + +/* +** File control method. For custom operations on an vstat-file. +*/ +static int vstatFileControl(sqlite3_file *pFile, int op, void *pArg){ + VStatFile *p = (VStatFile *)pFile; + int rc; + rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); + if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){ + *(char**)pArg = sqlite3_mprintf("vstat/%z", *(char**)pArg); + } + return rc; +} + +/* +** Return the sector-size in bytes for an vstat-file. +*/ +static int vstatSectorSize(sqlite3_file *pFile){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xSectorSize(p->pReal); + return rc; +} + +/* +** Return the device characteristic flags supported by an vstat-file. +*/ +static int vstatDeviceCharacteristics(sqlite3_file *pFile){ + int rc; + VStatFile *p = (VStatFile *)pFile; + rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); + return rc; +} + +/* Create a shared memory file mapping */ +static int vstatShmMap( + sqlite3_file *pFile, + int iPg, + int pgsz, + int bExtend, + void volatile **pp +){ + VStatFile *p = (VStatFile *)pFile; + return p->pReal->pMethods->xShmMap(p->pReal, iPg, pgsz, bExtend, pp); +} + +/* Perform locking on a shared-memory segment */ +static int vstatShmLock(sqlite3_file *pFile, int offset, int n, int flags){ + VStatFile *p = (VStatFile *)pFile; + return p->pReal->pMethods->xShmLock(p->pReal, offset, n, flags); +} + +/* Memory barrier operation on shared memory */ +static void vstatShmBarrier(sqlite3_file *pFile){ + VStatFile *p = (VStatFile *)pFile; + p->pReal->pMethods->xShmBarrier(p->pReal); +} + +/* Unmap a shared memory segment */ +static int vstatShmUnmap(sqlite3_file *pFile, int deleteFlag){ + VStatFile *p = (VStatFile *)pFile; + return p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag); +} + +/* Fetch a page of a memory-mapped file */ +static int vstatFetch( + sqlite3_file *pFile, + sqlite3_int64 iOfst, + int iAmt, + void **pp +){ + VStatFile *p = (VStatFile *)pFile; + return p->pReal->pMethods->xFetch(p->pReal, iOfst, iAmt, pp); +} + +/* Release a memory-mapped page */ +static int vstatUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ + VStatFile *p = (VStatFile *)pFile; + return p->pReal->pMethods->xUnfetch(p->pReal, iOfst, pPage); +} + +/* +** Open an vstat file handle. +*/ +static int vstatOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + int rc; + VStatFile *p = (VStatFile*)pFile; + + p->pReal = (sqlite3_file*)&p[1]; + rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags); + if( flags & SQLITE_OPEN_MAIN_DB ){ + p->eFiletype = VFSSTAT_MAIN; + }else if( flags & SQLITE_OPEN_MAIN_JOURNAL ){ + p->eFiletype = VFSSTAT_JOURNAL; + }else if( flags & SQLITE_OPEN_WAL ){ + p->eFiletype = VFSSTAT_WAL; + }else if( flags & SQLITE_OPEN_MASTER_JOURNAL ){ + p->eFiletype = VFSSTAT_MASTERJRNL; + }else if( flags & SQLITE_OPEN_SUBJOURNAL ){ + p->eFiletype = VFSSTAT_SUBJRNL; + }else if( flags & SQLITE_OPEN_TEMP_DB ){ + p->eFiletype = VFSSTAT_TEMPDB; + }else if( flags & SQLITE_OPEN_TEMP_JOURNAL ){ + p->eFiletype = VFSSTAT_TEMPJRNL; + }else{ + p->eFiletype = VFSSTAT_TRANSIENT; + } + STATCNT(p->eFiletype,VFSSTAT_OPEN)++; + pFile->pMethods = rc ? 0 : &vstat_io_methods; + return rc; +} + +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int vstatDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + int rc; + rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync); + STATCNT(VFSSTAT_ANY,VFSSTAT_DELETE)++; + return rc; +} + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +*/ +static int vstatAccess( + sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + int rc; + rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut); + STATCNT(VFSSTAT_ANY,VFSSTAT_ACCESS)++; + return rc; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (INST_MAX_PATHNAME+1) bytes. +*/ +static int vstatFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + STATCNT(VFSSTAT_ANY,VFSSTAT_FULLPATH)++; + return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); +} + +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *vstatDlOpen(sqlite3_vfs *pVfs, const char *zPath){ + return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath); +} + +/* +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. +*/ +static void vstatDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); +} + +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ + return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym); +} + +/* +** Close the dynamic library handle pHandle. +*/ +static void vstatDlClose(sqlite3_vfs *pVfs, void *pHandle){ + REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle); +} + +/* +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. +*/ +static int vstatRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + STATCNT(VFSSTAT_ANY,VFSSTAT_RANDOM)++; + return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); +} + +/* +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. +*/ +static int vstatSleep(sqlite3_vfs *pVfs, int nMicro){ + STATCNT(VFSSTAT_ANY,VFSSTAT_SLEEP)++; + return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro); +} + +/* +** Return the current time as a Julian Day number in *pTimeOut. +*/ +static int vstatCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++; + return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut); +} + +static int vstatGetLastError(sqlite3_vfs *pVfs, int a, char *b){ + return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b); +} +static int vstatCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ + STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++; + return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p); +} + +/* +** A virtual table for accessing the stats collected by this VFS shim +*/ +static int vstattabConnect(sqlite3*, void*, int, const char*const*, + sqlite3_vtab**,char**); +static int vstattabBestIndex(sqlite3_vtab*,sqlite3_index_info*); +static int vstattabDisconnect(sqlite3_vtab*); +static int vstattabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**); +static int vstattabClose(sqlite3_vtab_cursor*); +static int vstattabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); +static int vstattabNext(sqlite3_vtab_cursor*); +static int vstattabEof(sqlite3_vtab_cursor*); +static int vstattabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int); +static int vstattabRowid(sqlite3_vtab_cursor*,sqlite3_int64*); +static int vstattabUpdate(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); + +/* A cursor for the vfsstat virtual table */ +typedef struct VfsStatCursor { + sqlite3_vtab_cursor base; /* Base class. Must be first */ + int i; /* Pointing to this aVfsCnt[] value */ +} VfsStatCursor; + + +static int vstattabConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + sqlite3_vtab *pNew; + int rc; + +/* Column numbers */ +#define VSTAT_COLUMN_FILE 0 +#define VSTAT_COLUMN_STAT 1 +#define VSTAT_COLUMN_COUNT 2 + + rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)"); + if( rc==SQLITE_OK ){ + pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; +} + +/* +** This method is the destructor for vstat table object. +*/ +static int vstattabDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** Constructor for a new vstat table cursor object. +*/ +static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + VfsStatCursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + + +/* +** Destructor for a VfsStatCursor. +*/ +static int vstattabClose(sqlite3_vtab_cursor *cur){ + sqlite3_free(cur); + return SQLITE_OK; +} + + +/* +** Advance a VfsStatCursor to its next row of output. +*/ +static int vstattabNext(sqlite3_vtab_cursor *cur){ + ((VfsStatCursor*)cur)->i++; + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the VfsStatCursor +** is currently pointing. +*/ +static int vstattabColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + VfsStatCursor *pCur = (VfsStatCursor*)cur; + switch( i ){ + case VSTAT_COLUMN_FILE: { + sqlite3_result_text(ctx, azFile[pCur->i/VFSSTAT_nStat], -1, SQLITE_STATIC); + break; + } + case VSTAT_COLUMN_STAT: { + const char **az; + az = (pCur->i/VFSSTAT_nStat)==VFSSTAT_ANY ? azStatAny : azStat; + sqlite3_result_text(ctx, az[pCur->i%VFSSTAT_nStat], -1, SQLITE_STATIC); + break; + } + case VSTAT_COLUMN_COUNT: { + sqlite3_result_int64(ctx, aVfsCnt[pCur->i]); + break; + } + } + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. +*/ +static int vstattabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + VfsStatCursor *pCur = (VfsStatCursor*)cur; + *pRowid = pCur->i; + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int vstattabEof(sqlite3_vtab_cursor *cur){ + VfsStatCursor *pCur = (VfsStatCursor*)cur; + return pCur->i >= VFSSTAT_MXCNT; +} + +/* +** Only a full table scan is supported. So xFilter simply rewinds to +** the beginning. +*/ +static int vstattabFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + VfsStatCursor *pCur = (VfsStatCursor*)pVtabCursor; + pCur->i = 0; + return SQLITE_OK; +} + +/* +** Only a forwards full table scan is supported. xBestIndex is a no-op. +*/ +static int vstattabBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + return SQLITE_OK; +} + +/* +** Any VSTAT_COLUMN_COUNT can be changed to a positive integer. +** No deletions or insertions are allowed. No changes to other +** columns are allowed. +*/ +static int vstattabUpdate( + sqlite3_vtab *tab, + int argc, sqlite3_value **argv, + sqlite3_int64 *pRowid +){ + sqlite3_int64 iRowid, x; + if( argc==1 ) return SQLITE_ERROR; + if( sqlite3_value_type(argv[0])!=SQLITE_INTEGER ) return SQLITE_ERROR; + iRowid = sqlite3_value_int64(argv[0]); + if( iRowid!=sqlite3_value_int64(argv[1]) ) return SQLITE_ERROR; + if( iRowid<0 || iRowid>=VFSSTAT_MXCNT ) return SQLITE_ERROR; + if( sqlite3_value_type(argv[VSTAT_COLUMN_COUNT+2])!=SQLITE_INTEGER ){ + return SQLITE_ERROR; + } + x = sqlite3_value_int64(argv[VSTAT_COLUMN_COUNT+2]); + if( x<0 ) return SQLITE_ERROR; + aVfsCnt[iRowid] = x; + return SQLITE_OK; +} + +static sqlite3_module VfsStatModule = { + 0, /* iVersion */ + 0, /* xCreate */ + vstattabConnect, /* xConnect */ + vstattabBestIndex, /* xBestIndex */ + vstattabDisconnect, /* xDisconnect */ + 0, /* xDestroy */ + vstattabOpen, /* xOpen - open a cursor */ + vstattabClose, /* xClose - close a cursor */ + vstattabFilter, /* xFilter - configure scan constraints */ + vstattabNext, /* xNext - advance a cursor */ + vstattabEof, /* xEof - check for end of scan */ + vstattabColumn, /* xColumn - read data */ + vstattabRowid, /* xRowid - read data */ + vstattabUpdate, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +/* +** This routine is an sqlite3_auto_extension() callback, invoked to register +** the vfsstat virtual table for all new database connections. +*/ +static int vstatRegister( + sqlite3 *db, + const char **pzErrMsg, + const struct sqlite3_api_routines *pThunk +){ + return sqlite3_create_module(db, "vfsstat", &VfsStatModule, 0); +} + +#ifdef _WIN32 +__declspec(dllexport) +#endif +/* +** This routine is called when the extension is loaded. +** +** Register the new VFS. Make arrangement to register the virtual table +** for each new database connection. +*/ +int sqlite3_vfsstat_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + vstat_vfs.pVfs = sqlite3_vfs_find(0); + vstat_vfs.base.szOsFile = sizeof(VStatFile) + vstat_vfs.pVfs->szOsFile; + rc = sqlite3_vfs_register(&vstat_vfs.base, 1); + if( rc==SQLITE_OK ){ + rc = sqlite3_auto_extension(vstatRegister); + } + if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; + return rc; +} diff --git a/ext/rbu/rbu1.test b/ext/rbu/rbu1.test index 51d2ce8171..cd41728886 100644 --- a/ext/rbu/rbu1.test +++ b/ext/rbu/rbu1.test @@ -10,10 +10,7 @@ #*********************************************************************** # -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. test] -} -source $testdir/tester.tcl +source [file join [file dirname [info script]] rbu_common.tcl] set ::testprefix rbu1 db close @@ -96,26 +93,6 @@ proc create_rbu5 {filename} { return $filename } -# Run the RBU in file $rbu on target database $target until completion. -# -proc run_rbu {target rbu} { - sqlite3rbu rbu $target $rbu - while 1 { - set rc [rbu step] - if {$rc!="SQLITE_OK"} break - } - rbu close -} - -proc step_rbu {target rbu} { - while 1 { - sqlite3rbu rbu $target $rbu - set rc [rbu step] - rbu close - if {$rc != "SQLITE_OK"} break - } - set rc -} # Same as [step_rbu], except using a URI to open the target db. # @@ -641,10 +618,25 @@ foreach {tn3 create_vfs destroy_vfs} { # correctly. reset_db forcedelete rbu.db - do_test $tn3.8 { + do_test $tn3.8.1 { list [catch { run_rbu test.db rbu.db } msg] $msg } {0 SQLITE_DONE} - + + # Test that an RBU database containing only empty data_xxx tables is + # also handled correctly. + reset_db + forcedelete rbu.db + do_execsql_test $tn3.8.2.1 { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 2); + ATTACH 'rbu.db' AS rbu; + CREATE TABLE data_t1(a, b, rbu_control); + DETACH rbu; + } + do_test $tn3.8.2.1 { + list [catch { run_rbu test.db rbu.db } msg] $msg + } {0 SQLITE_DONE} + # Test that RBU can update indexes containing NULL values. # reset_db diff --git a/ext/rbu/rbu5.test b/ext/rbu/rbu5.test index 9a0f17bc5d..8e4ae8cb69 100644 --- a/ext/rbu/rbu5.test +++ b/ext/rbu/rbu5.test @@ -12,35 +12,10 @@ # Test some properties of the pager_rbu_mode and rbu_mode pragmas. # -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. test] -} -source $testdir/tester.tcl +source [file join [file dirname [info script]] rbu_common.tcl] set ::testprefix rbu5 -# Run the RBU in file $rbu on target database $target until completion. -# -proc run_rbu {target rbu} { - sqlite3rbu rbu $target $rbu - while { [rbu step]=="SQLITE_OK" } {} - rbu close -} - - -# Run the RBU in file $rbu on target database $target one step at a -# time until completion. -# -proc step_rbu {target rbu} { - while 1 { - sqlite3rbu rbu $target $rbu - set rc [rbu step] - rbu close - if {$rc != "SQLITE_OK"} break - } - set rc -} - # Return a list of the primary key columns for table $tbl in the database # opened by database handle $db. # diff --git a/ext/rbu/rbu_common.tcl b/ext/rbu/rbu_common.tcl index 8190021baf..a58c3aa0a7 100644 --- a/ext/rbu/rbu_common.tcl +++ b/ext/rbu/rbu_common.tcl @@ -15,12 +15,43 @@ if {![info exists testdir]} { } source $testdir/tester.tcl +proc check_prestep_state {target state} { + set oal_exists [file exists $target-oal] + set wal_exists [file exists $target-wal] + set progress [rbu progress] + + if {($progress==0 && $state!="oal" && $state!="done") + || ($oal_exists && $wal_exists) + || ($progress>0 && $state=="oal" && (!$oal_exists || $wal_exists)) + || ($state=="move" && (!$oal_exists || $wal_exists)) + || ($state=="checkpoint" && ($oal_exists || !$wal_exists)) + || ($state=="done" && ($oal_exists && $progress!=0)) + } { + error "B: state=$state progress=$progress oal=$oal_exists wal=$wal_exists" + } +} + +proc check_poststep_state {rc target state} { + if {$rc=="SQLITE_OK" || $rc=="SQLITE_DONE"} { + set oal_exists [file exists $target-oal] + set wal_exists [file exists $target-wal] + if {$state=="move" && ($oal_exists || !$wal_exists)} { + error "A: state=$state progress=$progress oal=$oal_exists wal=$wal_exists" + } + } +} + # Run the RBU in file $rbu on target database $target until completion. # proc run_rbu {target rbu} { sqlite3rbu rbu $target $rbu while 1 { + set state [rbu state] + + check_prestep_state $target $state set rc [rbu step] + check_poststep_state $rc $target $state + if {$rc!="SQLITE_OK"} break } rbu close @@ -29,10 +60,33 @@ proc run_rbu {target rbu} { proc step_rbu {target rbu} { while 1 { sqlite3rbu rbu $target $rbu + set state [rbu state] + check_prestep_state $target $state set rc [rbu step] + check_poststep_state $rc $target $state rbu close if {$rc != "SQLITE_OK"} break } set rc } +proc do_rbu_vacuum_test {tn step} { + uplevel [list do_test $tn.1 { + if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db } + while 1 { + if {$step==1} { sqlite3rbu_vacuum rbu test.db state.db } + set state [rbu state] + check_prestep_state test.db $state + set rc [rbu step] + check_poststep_state $rc test.db $state + if {$rc!="SQLITE_OK"} break + if {$step==1} { rbu close } + } + rbu close + } {SQLITE_DONE}] + + uplevel [list do_execsql_test $tn.2 { + PRAGMA integrity_check + } ok] +} + diff --git a/ext/rbu/rbudiff.test b/ext/rbu/rbudiff.test index 0863f6e807..041a242428 100644 --- a/ext/rbu/rbudiff.test +++ b/ext/rbu/rbudiff.test @@ -25,6 +25,10 @@ proc get_rbudiff_sql {db1 db2} { exec $::PROG --rbu $db1 $db2 } +proc get_vtab_rbudiff_sql {db1 db2} { + exec $::PROG --vtab --rbu $db1 $db2 +} + proc step_rbu {target rbu} { while 1 { sqlite3rbu rbu $target $rbu @@ -44,6 +48,11 @@ proc apply_rbudiff {sql target} { step_rbu $target rbu.db } +proc sqlesc {id} { + set ret "'[string map {' ''} $id]'" + set ret +} + # The only argument is the output of an [sqldiff -rbu] run. This command # tests that the contents of the rbu_count table is correct. An exception # is thrown if it is not. @@ -54,7 +63,7 @@ proc test_rbucount {sql} { tmpdb eval { SELECT name FROM sqlite_master WHERE name LIKE 'data%' AND type='table' } { - set a [tmpdb eval "SELECT count(*) FROM $name"] + set a [tmpdb eval "SELECT count(*) FROM [sqlesc $name]"] set b [tmpdb eval {SELECT cnt FROM rbu_count WHERE tbl = $name}] if {$a != $b} { tmpdb close @@ -71,9 +80,11 @@ proc rbudiff_cksum {db1} { sqlite3 dbtmp $db1 foreach tbl [dbtmp eval {SELECT name FROM sqlite_master WHERE type='table'}] { set cols [list] - dbtmp eval "PRAGMA table_info = $tbl" { lappend cols "quote( $name )" } + dbtmp eval "PRAGMA table_info = [sqlesc $tbl]" { + lappend cols "quote( $name )" + } append txt [dbtmp eval \ - "SELECT [join $cols {||'.'||}] FROM $tbl ORDER BY 1" + "SELECT [join $cols {||'.'||}] FROM [sqlesc $tbl] ORDER BY 1" ] } dbtmp close @@ -159,5 +170,115 @@ foreach {tn init mod} { do_test 1.$tn.5 { rbudiff_cksum test.db } [rbudiff_cksum test.db2] } +#------------------------------------------------------------------------- +# Test that if the --vtab switch is present, [sqldiff] handles virtual +# table types fts[345] and rtree correctly. +# +ifcapable fts3&&fts5&&rtree { + +foreach {tn init mod} { + 1 { + CREATE VIRTUAL TABLE t1 USING fts5(c); + INSERT INTO t1 VALUES('a b c'); + INSERT INTO t1 VALUES('a b c'); + } { + DELETE FROM t1 WHERE rowid = 1; + INSERT INTO t1 VALUES('a b c'); + } + + 2 { + CREATE VIRTUAL TABLE "x y" USING 'rtree'(id, x1, x2); + INSERT INTO "x y" VALUES(1, 2, 3); + INSERT INTO "x y" VALUES(2, 4, 6); + } { + DELETE FROM "x y" WHERE rowid = 1; + INSERT INTO "x y" VALUES(3, 6, 9); + } + + 3 { + CREATE VIRTUAL TABLE 'x''y' USING fts3; + INSERT INTO 'x''y' VALUES('one two three'); + INSERT INTO 'x''y' VALUES('four five six'); + } { + DELETE FROM 'x''y' WHERE rowid = 1; + INSERT INTO 'x''y' VALUES('one two three'); + } +} { + + forcedelete test.db test.db2 + sqlite3 db test.db + db eval "$init" + sqlite3 db test.db2 + db eval "$init ; $mod" + db close + + do_test 2.$tn.1 { + set sql [get_vtab_rbudiff_sql test.db test.db2] + apply_rbudiff $sql test.db + } {SQLITE_DONE} + do_test 2.$tn.2 { rbudiff_cksum test.db } [rbudiff_cksum test.db2] +} + +} + +ifcapable fts5 { + foreach {tn init mod} { + 1 { + CREATE VIRTUAL TABLE t1 USING fts5(c); + INSERT INTO t1 VALUES('a b c'); + INSERT INTO t1 VALUES('a b c'); + } { + DELETE FROM t1 WHERE rowid = 1; + INSERT INTO t1 VALUES('a b c'); + } + + 2 { + CREATE VIRTUAL TABLE t1 USING FTs5(c); + INSERT INTO t1 VALUES('a b c'); + INSERT INTO t1 VALUES('a b c'); + } { + DELETE FROM t1 WHERE rowid = 1; + INSERT INTO t1 VALUES('a b c'); + } + + 3 { + creAte virTUal +tablE t1 USING FTs5(c); + INSERT INTO t1 VALUES('a b c'); + INSERT INTO t1 VALUES('a b c'); + } { + DELETE FROM t1 WHERE rowid = 1; + INSERT INTO t1 VALUES('a b c'); + } + + } { + forcedelete test.db test.db2 + sqlite3 db test.db + db eval "$init" + sqlite3 db test.db2 + db eval "$init ; $mod" + db eval { INSERT INTO t1(t1) VALUES('optimize') } + db close + + do_test 3.$tn.1 { + set sql [get_vtab_rbudiff_sql test.db test.db2] + apply_rbudiff $sql test.db + } {SQLITE_DONE} + + sqlite3 db test.db + sqlite3 db2 test.db2 + do_test 3.$tn.2 { + db2 eval { SELECT * FROM t1 ORDER BY rowid } + } [db eval { SELECT * FROM t1 ORDER BY rowid }] + + do_test 3.$tn.3 { + db2 eval { INSERT INTO t1(t1) VALUES('integrity-check') } + } {} + + db close + db2 close + } +} + finish_test diff --git a/ext/rbu/rbuprogress.test b/ext/rbu/rbuprogress.test index 6afbffe8ed..af202829c1 100644 --- a/ext/rbu/rbuprogress.test +++ b/ext/rbu/rbuprogress.test @@ -361,6 +361,8 @@ foreach {bReopen} { 0 1 } { } } { + if {$tn=="vtab"} { ifcapable !fts5 break } + foreach {tn2 rbusql r1 r2} { 1 { CREATE TABLE data0_t1(a, b, c, rbu_rowid, rbu_control); diff --git a/ext/rbu/rbuvacuum.test b/ext/rbu/rbuvacuum.test index 7d82e380d6..86f4aa770e 100644 --- a/ext/rbu/rbuvacuum.test +++ b/ext/rbu/rbuvacuum.test @@ -17,23 +17,6 @@ source [file join [file dirname [info script]] rbu_common.tcl] set ::testprefix rbuvacuum -proc do_rbu_vacuum_test {tn step} { - uplevel [list do_test $tn.1 { - if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db } - while 1 { - if {$step==1} { sqlite3rbu_vacuum rbu test.db state.db } - set rc [rbu step] - if {$rc!="SQLITE_OK"} break - if {$step==1} { rbu close } - } - rbu close - } {SQLITE_DONE}] - - uplevel [list do_execsql_test $tn.2 { - PRAGMA integrity_check - } ok] -} - foreach step {0 1} { set ::testprefix rbuvacuum-step=$step @@ -404,7 +387,6 @@ do_test 3.5 { list [catch { rbu close } msg] $msg } {0 SQLITE_DONE} - catch { db close } finish_test diff --git a/ext/rbu/rbuvacuum2.test b/ext/rbu/rbuvacuum2.test new file mode 100644 index 0000000000..bd38660320 --- /dev/null +++ b/ext/rbu/rbuvacuum2.test @@ -0,0 +1,204 @@ +# 2016 June 1 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file contains tests for the RBU module. More specifically, it +# contains tests to ensure that the sqlite3rbu_vacuum() API works as +# expected. +# + +source [file join [file dirname [info script]] rbu_common.tcl] + +foreach step {0 1} { + set ::testprefix rbuvacuum2-$step + + #------------------------------------------------------------------------- + # Test that a database that contains fts3 tables can be vacuumed. + # + ifcapable fts3 { + reset_db + do_execsql_test 1.1 { + CREATE VIRTUAL TABLE t1 USING fts3(z, y); + INSERT INTO t1 VALUES('fix this issue', 'at some point'); + } + + do_rbu_vacuum_test 1.2 $step + + do_execsql_test 1.3 { + SELECT * FROM t1; + } {{fix this issue} {at some point}} + + do_execsql_test 1.4 { + SELECT rowid FROM t1 WHERE t1 MATCH 'fix'; + } {1} + + do_execsql_test 1.5 { + INSERT INTO t1 VALUES('a b c', 'd e f'); + INSERT INTO t1 VALUES('l h i', 'd e f'); + DELETE FROM t1 WHERE docid = 2; + INSERT INTO t1 VALUES('a b c', 'x y z'); + } + + do_rbu_vacuum_test 1.6 $step + do_execsql_test 1.7 { + INSERT INTO t1(t1) VALUES('integrity-check'); + SELECT * FROM t1; + } { + {fix this issue} {at some point} + {l h i} {d e f} + {a b c} {x y z} + } + } + + #------------------------------------------------------------------------- + # Test that a database that contains fts5 tables can be vacuumed. + # + ifcapable fts5 { + reset_db + do_execsql_test 2.1 { + CREATE VIRTUAL TABLE t1 USING fts5(z, y); + INSERT INTO t1 VALUES('fix this issue', 'at some point'); + } + + do_rbu_vacuum_test 2.2 $step + + do_execsql_test 2.3 { + SELECT * FROM t1; + } {{fix this issue} {at some point}} + + do_execsql_test 2.4 { + SELECT rowid FROM t1 ('fix'); + } {1} + + do_execsql_test 2.5 { + INSERT INTO t1 VALUES('a b c', 'd e f'); + INSERT INTO t1 VALUES('l h i', 'd e f'); + DELETE FROM t1 WHERE rowid = 2; + INSERT INTO t1 VALUES('a b c', 'x y z'); + } + + do_rbu_vacuum_test 2.6 $step + do_execsql_test 2.7 { + INSERT INTO t1(t1) VALUES('integrity-check'); + SELECT * FROM t1; + } { + {fix this issue} {at some point} + {l h i} {d e f} + {a b c} {x y z} + } + } + + #------------------------------------------------------------------------- + # Test that a database that contains an rtree table can be vacuumed. + # + ifcapable rtree { + reset_db + do_execsql_test 3.1 { + CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2); + INSERT INTO rt VALUES(1, 45, 55); + INSERT INTO rt VALUES(2, 50, 60); + INSERT INTO rt VALUES(3, 55, 65); + } + + do_rbu_vacuum_test 3.2 $step + + do_execsql_test 3.3 { + SELECT * FROM rt; + } {1 45.0 55.0 2 50.0 60.0 3 55.0 65.0} + + do_execsql_test 3.4.1 { + SELECT rowid FROM rt WHERE x2>51 AND x1 < 51 + } {1 2} + do_execsql_test 3.4.2 { + SELECT rowid FROM rt WHERE x2>59 AND x1 < 59 + } {2 3} + + do_rbu_vacuum_test 3.5 $step + + do_execsql_test 3.6.1 { + SELECT rowid FROM rt WHERE x2>51 AND x1 < 51 + } {1 2} + do_execsql_test 3.6.2 { + SELECT rowid FROM rt WHERE x2>59 AND x1 < 59 + } {2 3} + } + + ifcapable trigger { + reset_db + do_execsql_test 4.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES(1, 2, 3); + CREATE VIEW v1 AS SELECT * FROM t1; + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END; + } + + do_execsql_test 4.2 { + SELECT * FROM sqlite_master; + } { + table t1 t1 2 {CREATE TABLE t1(a, b, c)} + view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1} + trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END} + } + + do_rbu_vacuum_test 4.3 $step + do_execsql_test 4.4 { + SELECT * FROM sqlite_master; + } { + table t1 t1 2 {CREATE TABLE t1(a, b, c)} + view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1} + trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END} + } + } +} + +#------------------------------------------------------------------------- +# Test that passing a NULL value as the second argument to +# sqlite3rbu_vacuum() causes it to: +# +# * Use -vacuum as the state db, and +# * Set the state db permissions to the same as those on the db file. +# +db close +if {$::tcl_platform(platform)=="unix"} { + forcedelete test.db + + sqlite3 db test.db + do_execsql_test 5.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + INSERT INTO t1 VALUES(5, 6); + INSERT INTO t1 VALUES(7, 8); + } + db close + + foreach {tn perm} { + 1 00755 + 2 00666 + 3 00644 + 4 00444 + } { + forcedelete test.db-vacuum + + do_test 5.$tn.1 { + file attributes test.db -permissions $perm + sqlite3rbu_vacuum rbu test.db + rbu step + } {SQLITE_OK} + + do_test 5.$tn.2 { file exists test.db-vacuum } 1 + do_test 5.$tn.3 { file attributes test.db-vacuum -permissions} $perm + rbu close + } +} + + +finish_test + diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c index dc80935ecf..746469a8af 100644 --- a/ext/rbu/sqlite3rbu.c +++ b/ext/rbu/sqlite3rbu.c @@ -921,12 +921,14 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){ int rc; memset(pIter, 0, sizeof(RbuObjIter)); - rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, + rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, + sqlite3_mprintf( "SELECT rbu_target_name(name, type='view') AS target, name " "FROM sqlite_master " "WHERE type IN ('table', 'view') AND target IS NOT NULL " + " %s " "ORDER BY name" - ); + , rbuIsVacuum(p) ? "AND rootpage!=0 AND rootpage IS NOT NULL" : "")); if( rc==SQLITE_OK ){ rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg, @@ -2332,15 +2334,18 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ ** error occurs, leave an error code and message in the RBU handle. */ static void rbuOpenDatabase(sqlite3rbu *p){ - assert( p->rc==SQLITE_OK ); - assert( p->dbMain==0 && p->dbRbu==0 ); - assert( rbuIsVacuum(p) || p->zTarget!=0 ); + assert( p->rc || (p->dbMain==0 && p->dbRbu==0) ); + assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 ); /* Open the RBU database */ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1); if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){ sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p); + if( p->zState==0 ){ + const char *zFile = sqlite3_db_filename(p->dbRbu, "main"); + p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile); + } } /* If using separate RBU and state databases, attach the state database to @@ -2505,9 +2510,9 @@ static void rbuFileSuffix3(const char *zBase, char *z){ #endif { int i, sz; - sz = sqlite3Strlen30(z); + sz = (int)strlen(z)&0xffffff; for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} - if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); + if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4); } #endif } @@ -3475,8 +3480,7 @@ static sqlite3rbu *openRbuHandle( sqlite3rbu *p; size_t nTarget = zTarget ? strlen(zTarget) : 0; size_t nRbu = strlen(zRbu); - size_t nState = zState ? strlen(zState) : 0; - size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1; + size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1; p = (sqlite3rbu*)sqlite3_malloc64(nByte); if( p ){ @@ -3498,8 +3502,7 @@ static sqlite3rbu *openRbuHandle( memcpy(p->zRbu, zRbu, nRbu+1); pCsr += nRbu+1; if( zState ){ - p->zState = pCsr; - memcpy(p->zState, zState, nState+1); + p->zState = rbuMPrintf(p, "%s", zState); } rbuOpenDatabase(p); } @@ -3553,30 +3556,7 @@ static sqlite3rbu *openRbuHandle( if( p->rc==SQLITE_OK ){ if( p->eStage==RBU_STAGE_OAL ){ sqlite3 *db = p->dbMain; - - if( pState->eStage==0 && rbuIsVacuum(p) ){ - rbuCopyPragma(p, "page_size"); - rbuCopyPragma(p, "auto_vacuum"); - } - - /* Open transactions both databases. The *-oal file is opened or - ** created at this point. */ - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg); - } - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg); - } - - /* Check if the main database is a zipvfs db. If it is, set the upper - ** level pager to use "journal_mode=off". This prevents it from - ** generating a large journal using a temp file. */ - if( p->rc==SQLITE_OK ){ - int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0); - if( frc==SQLITE_OK ){ - p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg); - } - } + p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg); /* Point the object iterator at the first object */ if( p->rc==SQLITE_OK ){ @@ -3587,12 +3567,34 @@ static sqlite3rbu *openRbuHandle( ** update finished. */ if( p->rc==SQLITE_OK && p->objiter.zTbl==0 ){ p->rc = SQLITE_DONE; - } + p->eStage = RBU_STAGE_DONE; + }else{ + if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){ + rbuCopyPragma(p, "page_size"); + rbuCopyPragma(p, "auto_vacuum"); + } - if( p->rc==SQLITE_OK ){ - rbuSetupOal(p, pState); - } + /* Open transactions both databases. The *-oal file is opened or + ** created at this point. */ + if( p->rc==SQLITE_OK ){ + p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg); + } + /* Check if the main database is a zipvfs db. If it is, set the upper + ** level pager to use "journal_mode=off". This prevents it from + ** generating a large journal using a temp file. */ + if( p->rc==SQLITE_OK ){ + int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0); + if( frc==SQLITE_OK ){ + p->rc = sqlite3_exec( + db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg); + } + } + + if( p->rc==SQLITE_OK ){ + rbuSetupOal(p, pState); + } + } }else if( p->eStage==RBU_STAGE_MOVE ){ /* no-op */ }else if( p->eStage==RBU_STAGE_CKPT ){ @@ -3610,6 +3612,20 @@ static sqlite3rbu *openRbuHandle( return p; } +/* +** Allocate and return an RBU handle with all fields zeroed except for the +** error code, which is set to SQLITE_MISUSE. +*/ +static sqlite3rbu *rbuMisuseError(void){ + sqlite3rbu *pRet; + pRet = sqlite3_malloc64(sizeof(sqlite3rbu)); + if( pRet ){ + memset(pRet, 0, sizeof(sqlite3rbu)); + pRet->rc = SQLITE_MISUSE; + } + return pRet; +} + /* ** Open and return a new RBU handle. */ @@ -3618,6 +3634,7 @@ sqlite3rbu *sqlite3rbu_open( const char *zRbu, const char *zState ){ + if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); } /* TODO: Check that zTarget and zRbu are non-NULL */ return openRbuHandle(zTarget, zRbu, zState); } @@ -3629,6 +3646,7 @@ sqlite3rbu *sqlite3rbu_vacuum( const char *zTarget, const char *zState ){ + if( zTarget==0 ){ return rbuMisuseError(); } /* TODO: Check that both arguments are non-NULL */ return openRbuHandle(0, zTarget, zState); } @@ -3706,6 +3724,7 @@ int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){ rbuEditErrmsg(p); rc = p->rc; *pzErrmsg = p->zErrmsg; + sqlite3_free(p->zState); sqlite3_free(p); }else{ rc = SQLITE_NOMEM; @@ -3759,9 +3778,39 @@ void sqlite3rbu_bp_progress(sqlite3rbu *p, int *pnOne, int *pnTwo){ } } +/* +** Return the current state of the RBU vacuum or update operation. +*/ +int sqlite3rbu_state(sqlite3rbu *p){ + int aRes[] = { + 0, SQLITE_RBU_STATE_OAL, SQLITE_RBU_STATE_MOVE, + 0, SQLITE_RBU_STATE_CHECKPOINT, SQLITE_RBU_STATE_DONE + }; + + assert( RBU_STAGE_OAL==1 ); + assert( RBU_STAGE_MOVE==2 ); + assert( RBU_STAGE_CKPT==4 ); + assert( RBU_STAGE_DONE==5 ); + assert( aRes[RBU_STAGE_OAL]==SQLITE_RBU_STATE_OAL ); + assert( aRes[RBU_STAGE_MOVE]==SQLITE_RBU_STATE_MOVE ); + assert( aRes[RBU_STAGE_CKPT]==SQLITE_RBU_STATE_CHECKPOINT ); + assert( aRes[RBU_STAGE_DONE]==SQLITE_RBU_STATE_DONE ); + + if( p->rc!=SQLITE_OK && p->rc!=SQLITE_DONE ){ + return SQLITE_RBU_STATE_ERROR; + }else{ + assert( p->rc!=SQLITE_DONE || p->eStage==RBU_STAGE_DONE ); + assert( p->eStage==RBU_STAGE_OAL + || p->eStage==RBU_STAGE_MOVE + || p->eStage==RBU_STAGE_CKPT + || p->eStage==RBU_STAGE_DONE + ); + return aRes[p->eStage]; + } +} + int sqlite3rbu_savestate(sqlite3rbu *p){ int rc = p->rc; - if( rc==SQLITE_DONE ) return SQLITE_OK; assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE ); diff --git a/ext/rbu/sqlite3rbu.h b/ext/rbu/sqlite3rbu.h index fce9e493db..3f5f29a8e6 100644 --- a/ext/rbu/sqlite3rbu.h +++ b/ext/rbu/sqlite3rbu.h @@ -319,16 +319,22 @@ sqlite3rbu *sqlite3rbu_open( ** An RBU vacuum is similar to SQLite's built-in VACUUM command, except ** that it can be suspended and resumed like an RBU update. ** -** The second argument to this function, which may not be NULL, identifies -** a database in which to store the state of the RBU vacuum operation if -** it is suspended. The first time sqlite3rbu_vacuum() is called, to start -** an RBU vacuum operation, the state database should either not exist or -** be empty (contain no tables). If an RBU vacuum is suspended by calling +** The second argument to this function identifies a database in which +** to store the state of the RBU vacuum operation if it is suspended. The +** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum +** operation, the state database should either not exist or be empty +** (contain no tables). If an RBU vacuum is suspended by calling ** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has ** returned SQLITE_DONE, the vacuum state is stored in the state database. ** The vacuum can be resumed by calling this function to open a new RBU ** handle specifying the same target and state databases. ** +** If the second argument passed to this function is NULL, then the +** name of the state database is "-vacuum", where +** is the name of the target database file. In this case, on UNIX, if the +** state database is not already present in the file-system, it is created +** with the same permissions as the target db is made. +** ** This function does not delete the state database after an RBU vacuum ** is completed, even if it created it. However, if the call to ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents @@ -474,6 +480,44 @@ sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu); */ void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo); +/* +** Obtain an indication as to the current stage of an RBU update or vacuum. +** This function always returns one of the SQLITE_RBU_STATE_XXX constants +** defined in this file. Return values should be interpreted as follows: +** +** SQLITE_RBU_STATE_OAL: +** RBU is currently building a *-oal file. The next call to sqlite3rbu_step() +** may either add further data to the *-oal file, or compute data that will +** be added by a subsequent call. +** +** SQLITE_RBU_STATE_MOVE: +** RBU has finished building the *-oal file. The next call to sqlite3rbu_step() +** will move the *-oal file to the equivalent *-wal path. If the current +** operation is an RBU update, then the updated version of the database +** file will become visible to ordinary SQLite clients following the next +** call to sqlite3rbu_step(). +** +** SQLITE_RBU_STATE_CHECKPOINT: +** RBU is currently performing an incremental checkpoint. The next call to +** sqlite3rbu_step() will copy a page of data from the *-wal file into +** the target database file. +** +** SQLITE_RBU_STATE_DONE: +** The RBU operation has finished. Any subsequent calls to sqlite3rbu_step() +** will immediately return SQLITE_DONE. +** +** SQLITE_RBU_STATE_ERROR: +** An error has occurred. Any subsequent calls to sqlite3rbu_step() will +** immediately return the SQLite error code associated with the error. +*/ +#define SQLITE_RBU_STATE_OAL 1 +#define SQLITE_RBU_STATE_MOVE 2 +#define SQLITE_RBU_STATE_CHECKPOINT 3 +#define SQLITE_RBU_STATE_DONE 4 +#define SQLITE_RBU_STATE_ERROR 5 + +int sqlite3rbu_state(sqlite3rbu *pRbu); + /* ** Create an RBU VFS named zName that accesses the underlying file-system ** via existing VFS zParent. Or, if the zParent parameter is passed NULL, diff --git a/ext/rbu/test_rbu.c b/ext/rbu/test_rbu.c index 2d52262c71..b1a2252741 100644 --- a/ext/rbu/test_rbu.c +++ b/ext/rbu/test_rbu.c @@ -17,7 +17,14 @@ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) #include "sqlite3rbu.h" -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #include /* From main.c */ @@ -49,7 +56,7 @@ void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){ } -static int test_sqlite3rbu_cmd( +static int SQLITE_TCLAPI test_sqlite3rbu_cmd( ClientData clientData, Tcl_Interp *interp, int objc, @@ -69,6 +76,8 @@ static int test_sqlite3rbu_cmd( {"dbMain_eval", 3, "SQL"}, /* 4 */ {"bp_progress", 2, ""}, /* 5 */ {"db", 3, "RBU"}, /* 6 */ + {"state", 2, ""}, /* 7 */ + {"progress", 2, ""}, /* 8 */ {0,0,0} }; int iCmd; @@ -166,6 +175,18 @@ static int test_sqlite3rbu_cmd( } break; } + case 7: /* state */ { + const char *aRes[] = { 0, "oal", "move", "checkpoint", "done", "error" }; + int eState = sqlite3rbu_state(pRbu); + assert( eState>0 && eState<=5 ); + Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC); + break; + } + case 8: /* progress */ { + sqlite3_int64 nStep = sqlite3rbu_progress(pRbu); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep)); + break; + } default: /* seems unlikely */ assert( !"cannot happen" ); @@ -178,7 +199,7 @@ static int test_sqlite3rbu_cmd( /* ** Tclcmd: sqlite3rbu CMD ?? */ -static int test_sqlite3rbu( +static int SQLITE_TCLAPI test_sqlite3rbu( ClientData clientData, Tcl_Interp *interp, int objc, @@ -208,7 +229,7 @@ static int test_sqlite3rbu( /* ** Tclcmd: sqlite3rbu_vacuum CMD */ -static int test_sqlite3rbu_vacuum( +static int SQLITE_TCLAPI test_sqlite3rbu_vacuum( ClientData clientData, Tcl_Interp *interp, int objc, @@ -219,13 +240,13 @@ static int test_sqlite3rbu_vacuum( const char *zTarget; const char *zStateDb = 0; - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB STATE-DB"); + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?"); return TCL_ERROR; } zCmd = Tcl_GetString(objv[1]); zTarget = Tcl_GetString(objv[2]); - zStateDb = Tcl_GetString(objv[3]); + if( objc==4 ) zStateDb = Tcl_GetString(objv[3]); pRbu = sqlite3rbu_vacuum(zTarget, zStateDb); Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0); @@ -236,7 +257,7 @@ static int test_sqlite3rbu_vacuum( /* ** Tclcmd: sqlite3rbu_create_vfs ?-default? NAME PARENT */ -static int test_sqlite3rbu_create_vfs( +static int SQLITE_TCLAPI test_sqlite3rbu_create_vfs( ClientData clientData, Tcl_Interp *interp, int objc, @@ -271,7 +292,7 @@ static int test_sqlite3rbu_create_vfs( /* ** Tclcmd: sqlite3rbu_destroy_vfs NAME */ -static int test_sqlite3rbu_destroy_vfs( +static int SQLITE_TCLAPI test_sqlite3rbu_destroy_vfs( ClientData clientData, Tcl_Interp *interp, int objc, @@ -292,7 +313,7 @@ static int test_sqlite3rbu_destroy_vfs( /* ** Tclcmd: sqlite3rbu_internal_test */ -static int test_sqlite3rbu_internal_test( +static int SQLITE_TCLAPI test_sqlite3rbu_internal_test( ClientData clientData, Tcl_Interp *interp, int objc, @@ -333,7 +354,11 @@ int SqliteRbu_Init(Tcl_Interp *interp){ } #else -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif int SqliteRbu_Init(Tcl_Interp *interp){ return TCL_OK; } #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */ #endif /* defined(SQLITE_TEST) */ diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 012c48d6d1..fc69acd6fa 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -2800,6 +2800,53 @@ static RtreeValue rtreeValueUp(sqlite3_value *v){ } #endif /* !defined(SQLITE_RTREE_INT_ONLY) */ +/* +** A constraint has failed while inserting a row into an rtree table. +** Assuming no OOM error occurs, this function sets the error message +** (at pRtree->base.zErrMsg) to an appropriate value and returns +** SQLITE_CONSTRAINT. +** +** Parameter iCol is the index of the leftmost column involved in the +** constraint failure. If it is 0, then the constraint that failed is +** the unique constraint on the id column. Otherwise, it is the rtree +** (c1<=c2) constraint on columns iCol and iCol+1 that has failed. +** +** If an OOM occurs, SQLITE_NOMEM is returned instead of SQLITE_CONSTRAINT. +*/ +static int rtreeConstraintError(Rtree *pRtree, int iCol){ + sqlite3_stmt *pStmt = 0; + char *zSql; + int rc; + + assert( iCol==0 || iCol%2 ); + zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", pRtree->zDb, pRtree->zName); + if( zSql ){ + rc = sqlite3_prepare_v2(pRtree->db, zSql, -1, &pStmt, 0); + }else{ + rc = SQLITE_NOMEM; + } + sqlite3_free(zSql); + + if( rc==SQLITE_OK ){ + if( iCol==0 ){ + const char *zCol = sqlite3_column_name(pStmt, 0); + pRtree->base.zErrMsg = sqlite3_mprintf( + "UNIQUE constraint failed: %s.%s", pRtree->zName, zCol + ); + }else{ + const char *zCol1 = sqlite3_column_name(pStmt, iCol); + const char *zCol2 = sqlite3_column_name(pStmt, iCol+1); + pRtree->base.zErrMsg = sqlite3_mprintf( + "rtree constraint failed: %s.(%s<=%s)", pRtree->zName, zCol1, zCol2 + ); + } + } + + sqlite3_finalize(pStmt); + return (rc==SQLITE_OK ? SQLITE_CONSTRAINT : rc); +} + + /* ** The xUpdate method for rtree module virtual tables. @@ -2850,7 +2897,7 @@ static int rtreeUpdate( cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]); cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]); if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ - rc = SQLITE_CONSTRAINT; + rc = rtreeConstraintError(pRtree, ii+1); goto constraint; } } @@ -2861,7 +2908,7 @@ static int rtreeUpdate( cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]); cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]); if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){ - rc = SQLITE_CONSTRAINT; + rc = rtreeConstraintError(pRtree, ii+1); goto constraint; } } @@ -2882,7 +2929,7 @@ static int rtreeUpdate( if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){ rc = rtreeDeleteRowid(pRtree, cell.iRowid); }else{ - rc = SQLITE_CONSTRAINT; + rc = rtreeConstraintError(pRtree, 0); goto constraint; } } @@ -2965,6 +3012,11 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){ int rc; i64 nRow = 0; + if( sqlite3_table_column_metadata(db,pRtree->zDb,"sqlite_stat1", + 0,0,0,0,0,0)==SQLITE_ERROR ){ + pRtree->nRowEst = RTREE_DEFAULT_ROWEST; + return SQLITE_OK; + } zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName); if( zSql==0 ){ rc = SQLITE_NOMEM; diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test index c9192de192..9dc101a7b2 100644 --- a/ext/rtree/rtree1.test +++ b/ext/rtree/rtree1.test @@ -194,13 +194,13 @@ do_test rtree-2.1.3 { do_test rtree-2.2.1 { catchsql { INSERT INTO t1 VALUES(2, 1, 3, 2, 4) } -} {1 {constraint failed}} +} {1 {UNIQUE constraint failed: t1.ii}} do_test rtree-2.2.2 { catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) } -} {1 {constraint failed}} +} {1 {rtree constraint failed: t1.(y1<=y2)}} do_test rtree-2.2.3 { catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) } -} {1 {constraint failed}} +} {1 {rtree constraint failed: t1.(x1<=x2)}} do_test rtree-2.2.4 { execsql { SELECT ii FROM t1 ORDER BY ii } } {1 2 3} @@ -236,7 +236,7 @@ do_test rtree-3.1.3 { # Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)): do_test rtree-3.2.1 { catchsql { INSERT INTO t1 VALUES(7, 2, 6, 4, 3) } -} {1 {constraint failed}} +} {1 {rtree constraint failed: t1.(y1<=y2)}} do_test rtree-3.2.2 { catchsql { INSERT INTO t1 VALUES(8, 2, 6, 3, 3) } } {0 {}} @@ -490,11 +490,11 @@ foreach {tn sql_template testdata} { } 4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" { - ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6} - ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} + ROLLBACK 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6} + ABORT 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} - FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} - REPLACE 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} + FAIL 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} + REPLACE 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} } } { @@ -510,7 +510,9 @@ foreach {tn sql_template testdata} { } set res(0) {0 {}} - set res(1) {1 {constraint failed}} + set res(1) {1 {UNIQUE constraint failed: t1.idx}} + set res(2) {1 {rtree constraint failed: t1.(x1<=x2)}} + do_catchsql_test $testname.1 $sql $res($error) do_test $testname.2 [list sql_uses_stmt db $sql] $uses do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data diff --git a/ext/rtree/rtree3.test b/ext/rtree/rtree3.test index fea5513069..1d863c6cc2 100644 --- a/ext/rtree/rtree3.test +++ b/ext/rtree/rtree3.test @@ -47,7 +47,8 @@ ifcapable !rtree { # # rtree3-8: Test OOM while registering the r-tree module with sqlite. # - +# rtree3-11: OOM following a constraint failure +# do_faultsim_test rtree3-1 -faults oom* -prep { faultsim_delete_and_reopen } -body { @@ -234,4 +235,32 @@ do_faultsim_test rtree3-10 -faults oom-* -prep { faultsim_test_result {0 2} } + +do_test rtree3-11.prep { + faultsim_delete_and_reopen + execsql { + CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); + INSERT INTO rt VALUES(1, 2, 3, 4, 5); + } + faultsim_save_and_close +} {} +do_faultsim_test rtree3-10.1 -faults oom-* -prep { + faultsim_restore_and_reopen + execsql { SELECT * FROM rt } +} -body { + execsql { INSERT INTO rt VALUES(1, 2, 3, 4, 5) } +} -test { + faultsim_test_result {1 {UNIQUE constraint failed: rt.ii}} \ + {1 {constraint failed}} +} +do_faultsim_test rtree3-10.2 -faults oom-* -prep { + faultsim_restore_and_reopen + execsql { SELECT * FROM rt } +} -body { + execsql { INSERT INTO rt VALUES(2, 2, 3, 5, 4) } +} -test { + faultsim_test_result {1 {rtree constraint failed: rt.(y1<=y2)}} \ + {1 {constraint failed}} +} + finish_test diff --git a/ext/rtree/rtreeC.test b/ext/rtree/rtreeC.test index 9a64df51d5..a26c401e0d 100644 --- a/ext/rtree/rtreeC.test +++ b/ext/rtree/rtreeC.test @@ -350,7 +350,3 @@ do_eqp_execsql_test 7.4 { } finish_test - - - -finish_test diff --git a/ext/rtree/rtreeG.test b/ext/rtree/rtreeG.test new file mode 100644 index 0000000000..bffd17ecaa --- /dev/null +++ b/ext/rtree/rtreeG.test @@ -0,0 +1,66 @@ +# 2016-05-32 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file contains tests for the r-tree module. +# +# Verify that no invalid SQL is run during initialization + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source $testdir/tester.tcl +ifcapable !rtree { finish_test ; return } + +db close +sqlite3_shutdown +test_sqlite3_log [list lappend ::log] +set ::log [list] +sqlite3 db test.db + + +set ::log {} +do_execsql_test rtreeG-1.1 { + CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1); +} {} +do_test rtreeG-1.1log { + set ::log +} {} + +do_execsql_test rtreeG-1.2 { + INSERT INTO t1 VALUES(1,10,15,5,23),(2,20,21,5,23),(3,10,15,20,30); + SELECT id from t1 WHERE x0>8 AND x1<16 AND y0>2 AND y1<25; +} {1} +do_test rtreeG-1.2log { + set ::log +} {} + +db close +sqlite3 db test.db +do_execsql_test rtreeG-1.3 { + SELECT id from t1 WHERE x0>8 AND x1<16 AND y0>2 AND y1<25; +} {1} +do_test rtreeG-1.3log { + set ::log +} {} + +do_execsql_test rtreeG-1.4 { + DROP TABLE t1; +} {} +do_test rtreeG-1.4log { + set ::log +} {} + +db close +sqlite3_shutdown +test_sqlite3_log +sqlite3_initialize +sqlite3 db test.db + +finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index e333dffcc7..70ca840dae 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -278,19 +278,18 @@ static int sessionVarintGet(u8 *aBuf, int *piVal){ return getVarint32(aBuf, *piVal); } +/* Load an unaligned and unsigned 32-bit integer */ +#define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) + /* ** Read a 64-bit big-endian integer value from buffer aRec[]. Return ** the value read. */ static sqlite3_int64 sessionGetI64(u8 *aRec){ - return (((sqlite3_int64)aRec[0]) << 56) - + (((sqlite3_int64)aRec[1]) << 48) - + (((sqlite3_int64)aRec[2]) << 40) - + (((sqlite3_int64)aRec[3]) << 32) - + (((sqlite3_int64)aRec[4]) << 24) - + (((sqlite3_int64)aRec[5]) << 16) - + (((sqlite3_int64)aRec[6]) << 8) - + (((sqlite3_int64)aRec[7]) << 0); + u64 x = SESSION_UINT32(aRec); + u32 y = SESSION_UINT32(aRec+4); + x = (x<<32) + y; + return (sqlite3_int64)x; } /* diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index 71f02159cf..23d9a33aef 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -1,5 +1,5 @@ -#ifndef __SQLITESESSION_H_ +#if !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) #define __SQLITESESSION_H_ 1 /* @@ -1274,4 +1274,4 @@ int sqlite3changegroup_output_strm(sqlite3_changegroup*, } #endif -#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ +#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */ diff --git a/ext/session/test_session.c b/ext/session/test_session.c index 1aa63b74ed..103a1c2d7a 100644 --- a/ext/session/test_session.c +++ b/ext/session/test_session.c @@ -5,7 +5,14 @@ #include "sqlite3session.h" #include #include -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif typedef struct TestSession TestSession; struct TestSession { @@ -107,7 +114,7 @@ static int testStreamOutput( ** $session patchset ** $session table_filter SCRIPT */ -static int test_session_cmd( +static int SQLITE_TCLAPI test_session_cmd( void *clientData, Tcl_Interp *interp, int objc, @@ -240,7 +247,7 @@ static int test_session_cmd( return TCL_OK; } -static void test_session_del(void *clientData){ +static void SQLITE_TCLAPI test_session_del(void *clientData){ TestSession *p = (TestSession*)clientData; if( p->pFilterScript ) Tcl_DecrRefCount(p->pFilterScript); sqlite3session_delete(p->pSession); @@ -250,7 +257,7 @@ static void test_session_del(void *clientData){ /* ** Tclcmd: sqlite3session CMD DB-HANDLE DB-NAME */ -static int test_sqlite3session( +static int SQLITE_TCLAPI test_sqlite3session( void * clientData, Tcl_Interp *interp, int objc, @@ -606,7 +613,7 @@ static int testStreamInput( /* ** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT? */ -static int test_sqlite3changeset_apply( +static int SQLITE_TCLAPI test_sqlite3changeset_apply( void * clientData, Tcl_Interp *interp, int objc, @@ -661,7 +668,7 @@ static int test_sqlite3changeset_apply( /* ** sqlite3changeset_apply_replace_all DB CHANGESET */ -static int test_sqlite3changeset_apply_replace_all( +static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all( void * clientData, Tcl_Interp *interp, int objc, @@ -696,7 +703,7 @@ static int test_sqlite3changeset_apply_replace_all( /* ** sqlite3changeset_invert CHANGESET */ -static int test_sqlite3changeset_invert( +static int SQLITE_TCLAPI test_sqlite3changeset_invert( void * clientData, Tcl_Interp *interp, int objc, @@ -735,7 +742,7 @@ static int test_sqlite3changeset_invert( /* ** sqlite3changeset_concat LEFT RIGHT */ -static int test_sqlite3changeset_concat( +static int SQLITE_TCLAPI test_sqlite3changeset_concat( void * clientData, Tcl_Interp *interp, int objc, @@ -783,7 +790,7 @@ static int test_sqlite3changeset_concat( /* ** sqlite3session_foreach VARNAME CHANGESET SCRIPT */ -static int test_sqlite3session_foreach( +static int SQLITE_TCLAPI test_sqlite3session_foreach( void * clientData, Tcl_Interp *interp, int objc, diff --git a/main.mk b/main.mk index f4f6287753..3322f209dc 100644 --- a/main.mk +++ b/main.mk @@ -324,7 +324,9 @@ TESTSRC = \ # TESTSRC += \ $(TOP)/ext/misc/amatch.c \ + $(TOP)/ext/misc/carray.c \ $(TOP)/ext/misc/closure.c \ + $(TOP)/ext/misc/csv.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ @@ -388,8 +390,7 @@ TESTSRC2 = \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c \ $(TOP)/ext/session/sqlite3session.c \ - $(TOP)/ext/session/test_session.c \ - $(FTS5_SRC) + $(TOP)/ext/session/test_session.c # Header files used by all library source files. # @@ -451,7 +452,8 @@ TESTPROGS = \ testfixture$(EXE) \ sqlite3$(EXE) \ sqlite3_analyzer$(EXE) \ - sqldiff$(EXE) + sqldiff$(EXE) \ + dbhash$(EXE) # Databases containing fuzzer test cases # @@ -468,6 +470,8 @@ TESTOPTS = --verbose=file --output=test-out.txt # Extra compiler options for various shell tools # SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 +SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS +SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 @@ -488,6 +492,13 @@ sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB) +dbhash$(EXE): $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h + $(TCCX) -o dbhash$(EXE) -DSQLITE_THREADSAFE=0 \ + $(TOP)/tool/dbhash.c sqlite3.c $(TLIBS) $(THREADLIB) + +scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o + $(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB) + srcck1$(EXE): $(TOP)/tool/srcck1.c $(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c diff --git a/manifest b/manifest index 20610275e2..b94ce9afa9 100644 --- a/manifest +++ b/manifest @@ -1,24 +1,24 @@ -C Fix\sa\stypo\sin\stest\sprogram\sbc_test1.c. -D 2016-07-06T08:32:53.109 -F Makefile.in 9e816d0323e418fbc0f8b2c05fc14e0b3763d9e8 +C Merge\sversion\s3.14\splus\ssome\ssubsequent\spatches\s(including\sthe\spage-cache\nperformance\spatch)\sfrom\strunk. +D 2016-08-11T19:12:25.266 +F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc 71b8b16cf9393f68e2e2035486ca104872558836 +F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7 -F VERSION 5d234da9b5dae329fab75ff75884cfe0a9cb3fda +F VERSION 25e2e333adeff5965520bc8db999c658898c972d 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 42f7eb1fb2b06ddd6c6adab3e4388cc0a237ba40 -F autoconf/Makefile.msc dc0d0500e4a827cb3891d08d846dd6dd40241b51 +F autoconf/Makefile.am 1a47d071e3d5435f8f7ebff7eb6703848bbd65d4 +F autoconf/Makefile.msc 7a909aa9b538138a21c0626e3ffc4b1f4519f50e F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1 -F autoconf/configure.ac d807b567f73a2b0986ccdd206b782cecb4d08fff +F autoconf/configure.ac cacf2616abf6e4a569bde2ef365c143caeec40bc F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873 F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43 -F autoconf/tea/configure.ac 93d43c79e936fb16556e22498177d7e8571efa04 +F autoconf/tea/configure.ac 8aa16e3f0a5ca7959d4af198f46934ec187d395f F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523 F autoconf/tea/pkgIndex.tcl.in 3ef61715cf1c7bdcff56947ffadb26bc991ca39d @@ -30,8 +30,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure dbd42c55ca6b65e0fb37ef54121755f26a890edf x -F configure.ac 89e4e02a83d8a1528011f8395621b8c3186b4089 +F configure 3f44893bc0e51c30653e02b0fd4dc2ab585e446a x +F configure.ac b5d3df43161374f8dffd2e5f4b88fbb51685b975 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html e2118945e5f07ed146b45c9cd2b2dd6eabb8ebf2 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 @@ -62,7 +62,7 @@ F ext/fts2/fts2_hash.c 011a1d32de45bb1b519a1fd0048e857d6a843558 F ext/fts2/fts2_hash.h 1824b99dfd8d0225facbdb26a2c87289b2e7dcf8 F ext/fts2/fts2_icu.c 51c5cd3c04954badd329fa738c95fcdb717b5188 F ext/fts2/fts2_porter.c 2cd4a507bf3c3085fe66f59b0f2a325f65aaacf5 -F ext/fts2/fts2_tokenizer.c 3dbe8058e97afb55fff3ea844120ce3208b114cc +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 @@ -81,9 +81,9 @@ F ext/fts3/fts3_icu.c deb46f7020d87ea7a14a433fb7a7f4bef42a9652 F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009 F ext/fts3/fts3_snippet.c 68ae118b0f834ea53d2b89e4087fc0f0b8c4ee4e F ext/fts3/fts3_term.c 88c55a6fa1a51ab494e33dced0401a6c28791fd7 -F ext/fts3/fts3_test.c a940cf104d545ad1abf926956ce65db2aa5af0cf +F ext/fts3/fts3_test.c 79f2a7fbb3f672fa032e5a432ca274ea3ee93c34 F ext/fts3/fts3_tokenize_vtab.c a27593ab19657166f6fa5ec073b678cc29a75860 -F ext/fts3/fts3_tokenizer.c 3cf21cd2212db17a88d4ef7da0fd8a80275979a1 +F ext/fts3/fts3_tokenizer.c a22bf311a71f3efa9d7012d8cc48fc9b0f3dace7 F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3 F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004 F ext/fts3/fts3_unicode.c a93f5edc0aff44ef8b06d7cb55b52026541ca145 @@ -97,24 +97,24 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl 2debed3f582d77b3fdd0b8830880250021571fd8 F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95 F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h ff9c2782e8ed890b0de2f697a8d63971939e70c7 -F ext/fts5/fts5Int.h 3677076aecbf645a7f2a019115c6a4ec3272dd78 +F ext/fts5/fts5.h 62f3e33ceeb9a428db139f9c012186b371da1cc7 +F ext/fts5/fts5Int.h b2eda36e0f224365c8e23dc8f559311834f1c13f F ext/fts5/fts5_aux.c daa57fb45216491814520bbb587e97bf81ced458 F ext/fts5/fts5_buffer.c 4c1502d4c956cd092c89ce4480867f9d8bf325cd F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857 -F ext/fts5/fts5_expr.c 5ca4bafe29aa3d27683c90e836192e4aefd20a3f +F ext/fts5/fts5_expr.c df0004b5bffcbe34c329f2992669c6352443f415 F ext/fts5/fts5_hash.c 880998e596b60f078348d48732ca4ad9a90caad2 -F ext/fts5/fts5_index.c b429e23fabb57506f71e406997cc46b89190dc97 -F ext/fts5/fts5_main.c b4a0fc5bf17f2f1f056ee76cdd7d2af08b360f55 -F ext/fts5/fts5_storage.c 3309c6a8e34b974513016fd1ef47c83f5898f94c -F ext/fts5/fts5_tcl.c f8731e0508299bd43f1a2eff7dbeaac870768966 +F ext/fts5/fts5_index.c 05386732609221d066d204b22c4a5275a0225ed4 +F ext/fts5/fts5_main.c f85281445dcf8be32d18841c93a6f90fe27dbfe2 +F ext/fts5/fts5_storage.c de0ed8a06738bde433afe11e92295ceaffbc4e58 +F ext/fts5/fts5_tcl.c 4a901f00c8553740dba63511603f5527d741c26a F ext/fts5/fts5_test_mi.c 783b86697ebf773c18fc109992426c0173a055bc F ext/fts5/fts5_test_tok.c db08af63673c3a7d39f053b36fd6e065017706be F ext/fts5/fts5_tokenize.c 2ce7b44183538ec46b7907726262ee43ffdd39a8 F ext/fts5/fts5_unicode2.c b450b209b157d598f7b9df9f837afb75a14c24bf F ext/fts5/fts5_varint.c a5aceacda04dafcbae725413d7a16818ecd65738 F ext/fts5/fts5_vocab.c dba72ca393d71c2588548b51380387f6b44c77a8 -F ext/fts5/fts5parse.y fcc5e92e570d38cab38488b2109cbf67468923b2 +F ext/fts5/fts5parse.y e51b375403421b8b37428a89b095d00597129aae F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba F ext/fts5/test/fts5_common.tcl b01c584144b5064f30e6c648145a2dd6bc440841 F ext/fts5/test/fts5aa.test bd2d88182b9f7f30d300044048ad14683306b745 @@ -131,10 +131,11 @@ F ext/fts5/test/fts5ak.test fb26389985407826f6076bb9f382c67d3db6b5d9 F ext/fts5/test/fts5al.test 18c277f5986df0a3d9071dfd7128afeb16fe9d5d F ext/fts5/test/fts5alter.test 6022c61467a82aa11c70822ccad22b328dcf0d04 F ext/fts5/test/fts5auto.test 401c20e89f1114d733b94809be1e6f893e16c09e -F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f +F ext/fts5/test/fts5aux.test 5dd158a1e7869e27e9762a2a452b189c728d1be3 F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb F ext/fts5/test/fts5bigtok.test 017a9397b14e7598883a6328ead4a6539b42d59a +F ext/fts5/test/fts5colset.test 1cdf56e079316005aabda790059aee86f2222ee4 F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07 F ext/fts5/test/fts5config.test 7788b9c058074d640dfcdd81d97b6a9480000368 F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5 @@ -176,11 +177,11 @@ F ext/fts5/test/fts5porter.test 7cdc07bef301d70eebbfa75dcaf45c3680e1d0e1 F ext/fts5/test/fts5porter2.test 2e65633d58a1c525d5af0f6c01e5a59155bb3487 F ext/fts5/test/fts5prefix.test efd42e00bb8e8a36383f25c838185508681c093f F ext/fts5/test/fts5query.test f5ec25f5f2fbb70033424113cdffc101b1985a40 -F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab +F ext/fts5/test/fts5rank.test 2bdc0c5f22ccc1f9dbe9f4d0b82a491dce6f8a32 F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17 F ext/fts5/test/fts5rowid.test 16908a99d6efc9ba21081b4f2b86b3fc699839a6 -F ext/fts5/test/fts5simple.test cd23d4072ea095d652c9b6db12284cc642e49c98 +F ext/fts5/test/fts5simple.test 5da9b15ed534eb0be9f279d8a2bb2e24d30e4e38 F ext/fts5/test/fts5simple2.test 98377ae1ff7749a42c21fe1a139c1ed312522c46 F ext/fts5/test/fts5simple3.test 8e71733b3d1b0e695011d02c68ebc5ca40b6124e F ext/fts5/test/fts5synonym.test 6475d189c2e20d60795808f83e36bf9318708d48 @@ -204,33 +205,37 @@ F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c 43df9d8ef2fae7a325100ebd713ab089dc829dd7 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/misc/amatch.c 211108e201105e4bb0c076527b8cfd34330fc234 +F ext/misc/carray.c 40c27641010a4dc67e3690bdb7c9d36ca58b3c2d F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704 F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83 +F ext/misc/csv.c 816a3715356e4210dae2d242057745e937050896 F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25 F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c -F ext/misc/json1.c b9c88d5c3b6ecd8c731ffdd7f5b3d902857f8c96 +F ext/misc/json1.c 9799e4252b305edcbe659329eec3ca80ed85f968 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 -F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 +F ext/misc/percentile.c 92699c8cd7d517ff610e6037e56506f8904dae2e F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a +F ext/misc/scrub.c 1c5bfb8b0cd18b602fcb55755e84abf0023ac2fb F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 -F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be +F ext/misc/spellfix.c a4723b6aff748a417b5091b68a46443265c40f0d F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 +F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212 F ext/rbu/rbu.c b2c0b5e6ae1a89affc0edfc127ebfa5f637a0ce4 -F ext/rbu/rbu1.test 42bd835e019eff789ec241017965277baeb658b1 +F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842 F ext/rbu/rbu10.test 046b0980041d30700464a800bbf6733ed2df515d F ext/rbu/rbu11.test 9bc68c2d3dbeb1720153626e3bd0466dcc017702 F ext/rbu/rbu12.test bde22ed0004dd5d1888c72a84ae407e574aeae16 F ext/rbu/rbu13.test 462ff799c4afedc3ef8a47ff818c0ffbf14ae4f2 F ext/rbu/rbu14.test 01f5dcba904aecadbaea69d4ccdc2ea43dd30560 F ext/rbu/rbu3.test 8bd4c6b87367c358981b6a47dc3d654fa60bff90 -F ext/rbu/rbu5.test 2e24fee3e615aecd99bbdd46967935a641e866f7 +F ext/rbu/rbu5.test cf1fa84d6107efd73e292a7be8e1fb31953ca45c F ext/rbu/rbu6.test 32e8ed60631b6facdb6366bd2b5f5f25245e7edb F ext/rbu/rbu7.test fd025d5ba440fcfe151fbb0e3835e1e7fe964fa1 F ext/rbu/rbu8.test 3bbf2c35d71a843c463efe93946f14ad10c3ede0 @@ -238,25 +243,26 @@ F ext/rbu/rbu9.test 0806d1772c9f4981774ff028de6656e4183082af F ext/rbu/rbuA.test c1a7b3e2d926b8f8448bb3b4ae787e314ee4b2b3 F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2 F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831 -F ext/rbu/rbu_common.tcl 0398545fed614f807d5f0ba55a85a51f08ba8f1a +F ext/rbu/rbu_common.tcl a38e8e2d4a50fd6aaf151633714c1b1d2fae3ead F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695 -F ext/rbu/rbudiff.test 2df0a8a7d998ecf81764c21eeda3cde5611c5091 +F ext/rbu/rbudiff.test b3c7675810b81de98a930a87fcd40d9ae545619d F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89 F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06 F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda -F ext/rbu/rbuprogress.test 2023a7df2c523e3df1cb532eff811cda385a789a +F ext/rbu/rbuprogress.test e3e25fb7622641b8f2df7c6b7a7eb6fddfc46a4b F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48 -F ext/rbu/rbuvacuum.test 66e02cf299836770e718e95c36686be0b26dbda3 -F ext/rbu/sqlite3rbu.c bf36625990c6865ecf08bd844d8097ed2d0a6958 -F ext/rbu/sqlite3rbu.h 2acd0a6344a6079de15c8dc9d84d3df83a665930 -F ext/rbu/test_rbu.c 9bbdf6bd8efd58fbc4f192698df50569598fbb9e +F ext/rbu/rbuvacuum.test 4a977447c15c2581ab668781d9ef4294382530e0 +F ext/rbu/rbuvacuum2.test 2569205b74ff40fbf3bda2fce33a58eb40eebdcc +F ext/rbu/sqlite3rbu.c e074c38798b90591f7f0cf0032d62f152ce5a95e +F ext/rbu/sqlite3rbu.h 1d91c5b7d066645bd1ff8e4b85c2b9b5dd29fb05 +F ext/rbu/test_rbu.c 5aa22616afac6f71ebd3d9bc9bf1006cfabcca88 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c 0b870ccb7b58b734a2a8e1e2755a7c0ded070920 +F ext/rtree/rtree.c d26a815b0df1c412a6881dae8d7fd3c9c08cce68 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e -F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda +F ext/rtree/rtree1.test 42dadfc7b44a436cd74a1bebc0b9b689e4eaf7ec F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba -F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc +F ext/rtree/rtree3.test 2cafe8265d1ff28f206fce88d114f208349df482 F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196 @@ -265,10 +271,11 @@ F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a F ext/rtree/rtree9.test b5eb13849545dfd271a54ff16784cb00d8792aea F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e -F ext/rtree/rtreeC.test 90aaaffe2fd4f0dcd12289cad5515f6d41f45ffd +F ext/rtree/rtreeC.test c0a9c67f2efa98b6fae12acb8a28348d231a481d F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca F ext/rtree/rtreeE.test 45a147a64a76306172819562309681d8e90f94bb F ext/rtree/rtreeF.test 66deb9fd1611c7ca2e374adba63debdc2dbb12b4 +F ext/rtree/rtreeG.test 3b185719630795f38594f64cd7d1de86a33f91f1 F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 9c5777af3d2921c7b4ae4954e8e5697502289d28 @@ -293,16 +300,16 @@ F ext/session/sessionG.test 01ef705096a9d3984eebdcca79807a211dee1b60 F ext/session/session_common.tcl a1293167d14774b5e728836720497f40fe4ea596 F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 -F ext/session/sqlite3session.c beb43b6b888801bb006320bc236baa95f4cc32ae -F ext/session/sqlite3session.h 64e9e7f185725ef43b97f4a9a0c0df0669844f1d -F ext/session/test_session.c 464f2c8bf502795d95969387eb8e93f68c513c15 +F ext/session/sqlite3session.c 37485891b4add26cf61495df193c419f36556a32 +F ext/session/sqlite3session.h 69bf73cfd71e58f2ae5d2aa935b2c1a541aee555 +F ext/session/test_session.c 2caed9a659586428c63ca46e4900347b374487d4 F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 968ad49af90277060b8ec7a15915273b76ee1d4d +F main.mk 67e7995f96dcb48ec0ece72901ba2cd2a2b50437 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -314,35 +321,35 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 1bb0709b3048e24217b80ec6bd78a3e99a47c01b +F src/alter.c cc28ab933ae615b22add0d609794ffb6596b42ea F src/analyze.c 37fedc80ac966ce1745811746e68e4d8fa64c7fe F src/attach.c 771153bd1f4ab0b97a44a13dde2c7e5e1efeba22 -F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 -F src/backup.c f60f0aa55d25d853ffde53d0b0370a7bb7ee41ce +F src/auth.c 5c8e0f37f785f935f589496801edd19840485853 +F src/backup.c 17cd25a36d49330df2bacd2cadf2a61f3b525976 F src/bitvec.c 3ee4c8b2c94ed3a7377256e18199e6ff5cf33f63 F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73 -F src/btree.c 6deeeeffaf9d35ba847ff6953ad76fc19e69d27d -F src/btree.h 248919b5863d041e3931702f922bae62cf613004 +F src/btree.c 8872dcc869d648ccc0b95834fc7e81cf49c456f7 +F src/btree.h c23154635a447f37308865ce09b5d1dd2e9deef0 F src/btreeInt.h ca40607c2c9d4b4b7ded98e3d707b15c2f17f1bf -F src/build.c 034b1b7c3d2c604a5f50d7527de1462b90d25112 +F src/build.c e3b41d597d4f6f24cee6bde1e64c648b1c3983ca F src/callback.c 2e76147783386374bf01b227f752c81ec872d730 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/ctime.c 60e135af364d777a9ab41c97e5e89cd224da6198 -F src/date.c 1cc9fb516ec9932c6fd4d2a0d2f8bc4480145c39 -F src/dbstat.c c845548d4346e606e2f2b7d2e714ace2b8a7dd1b -F src/delete.c 78eb999114ec04fcf1b7d123ccedb4b5b734930e -F src/expr.c 8796c0739b2ad091e6779253f62aad6e767e2be1 +F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 +F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b +F src/dbstat.c 4f6f7f52b49beb9636ffbd517cfe44a402ba4ad0 +F src/delete.c 4aba4214a377ce8ddde2d2e609777bcc8235200f +F src/expr.c 9c5eca8602f6c496e8d4eefefe2aae3d831dd510 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 4c0bd09e602b8ae8d36d81e31e4872d0b53c87bb -F src/func.c ef4c18c8a66143413ce41a58d582d2c14ddf78e1 +F src/fkey.c bc4145347595b7770f9a598cff1c848302cf5413 +F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771 F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 -F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 -F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 +F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 +F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da F src/insert.c 8f4e9fcbd8e95e85f15647ba8b413b18d556ec2b F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e -F src/loadext.c 8b3a73f0624c5f7cadbd5cb89940783bee1d39a6 -F src/main.c 405d13e3a4f7c5add9fb27702ae70ed0a6e32cca +F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec +F src/main.c 16c1b2114eae8804caf3a8de8cb47bf2c6d83ad3 F src/malloc.c 1443d1ad95d67c21d77af7ae3f44678252f0efec F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b @@ -350,117 +357,117 @@ F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944 F src/memjournal.c 95752936c11dc6995672d1dd783cd633eea0cc95 -F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495 +F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81 F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85 F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 F src/mutex_unix.c 27bb6cc49485ee46711a6580ab7b3f1402211d23 F src/mutex_w32.c 5e6fe1c298fb5a8a15aaed4161d5759311431c17 F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7 -F src/os.c 4d83917f072ad958fba9235136fa2ed43df47905 +F src/os.c add02933b1dce7a39a005b00a2f5364b763e9a24 F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa -F src/os_unix.c 8422fba2eb592fbbb2d4006b6f2a67cad8951495 -F src/os_win.c 852fc2ff6084296348ed3739c548b2cf32df394e -F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca -F src/pager.c 41559a2b4461a6cf4b24bedb47d4e303d02729b5 -F src/pager.h 6656436178c295d0668520739118e71a46163b8e -F src/parse.y 2e1d371f99535ff29d1cd85052aa11cc636e21c8 -F src/pcache.c b3230ecfc7f797063fbe167f2845da363e8f07f8 -F src/pcache.h 6b865be765d1ebd06145219550b10921c7da7cc9 -F src/pcache1.c 7f51d2b541aab57596adf62db2c4bb025d34f04d -F src/pragma.c faf42922bb7ab2f6672cb550356c1967abae3c84 +F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 +F src/os_unix.c be9ca0f901a2b6c1bc93dc338f4863675180c189 +F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0 +F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a +F src/pager.c bd4867fbbe328bbb5f0669d60e3c16ac03dd776f +F src/pager.h 3ae6549ec75fe9f36b426a274788a31b1b2b2203 +F src/parse.y e87265f23548aed30b331fb86b4712e1c13c4673 +F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df +F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490 +F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812 +F src/pragma.c c8b499756658cb8b82cfdbb5845c22cf11f297aa F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e -F src/printf.c 63e6fb12bbe702dd664dc3703776c090383a5a26 +F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c b8f7174e5f8c33c44ded3a25a973d0bb89228c20 -F src/rowset.c 49eb91c588a2bab36647368e031dc5b66928149d -F src/select.c fd4a7ce2937497181063cfedb92058ac89491a5d -F src/shell.c 14ff7f660530a52b117d110ba3390b7b2eb719b6 -F src/sqlite.h.in 9984129d86243424b765fcb3f147c697bd20bb54 +F src/resolve.c 0392c6686586b1d4dac9a4106959f03ddd70e9aa +F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac +F src/select.c f3c6e9065fb34f6a23af27ec7f1f717ffbfc2ee4 +F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 +F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 -F src/sqlite3ext.h 98f72cbfe00169c39089115427d06ea05fe4b4a2 -F src/sqliteInt.h e6ac0d8f8cbb062a57882858b1deed1a7924314b +F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae +F src/sqliteInt.h cf506e205f4b15618f5f0787e45d40fafc8ff42c F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 -F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba +F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 -F src/tclsqlite.c 9c4c4589d078de37813ded708d8838b338ffb060 -F src/test1.c ee8fd5c32acb93fb6fe885ca4801cfab85f5563f -F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b -F src/test3.c 0df6f8dbb4cbaa7106397c70a271fa6a43659042 -F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e -F src/test5.c 5a34feec76d9b3a86aab30fd4f6cc9c48cbab4c1 -F src/test6.c 2c014d4977efd6107ec9eef3dfdec56ac516f824 -F src/test7.c 9c89a4f1ed6bb13af0ed805b8d782bd83fcd57e3 -F src/test8.c fa262391d3edea6490a71bfaa8fed477ccbbac75 -F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 -F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 -F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 -F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 -F src/test_bestindex.c 29af3cc3b963ffe5760c85d142b9b3e5302c1e3d -F src/test_blob.c b2551a9b5573232db5f66f292307c37067937239 -F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f -F src/test_config.c 5165f79b3aea5305627ac36dd7b9b5f124572da1 -F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852 +F src/tclsqlite.c bdae822f21e229b6daced15938b6343ce44ef454 +F src/test1.c 0a0909cf7962d2359db329c08d15b90b4b6e724f +F src/test2.c b7174313e993754303a8b33c43df7c44b46857ab +F src/test3.c 1339a40be39650ae83894b6578f971dc7f96ea8a +F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 +F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d +F src/test6.c 55aa2775c154415dcf4ed7cd1e19a193122b3a02 +F src/test7.c 5612e9aecf934d6df7bba6ce861fdf5ba5456010 +F src/test8.c 4f4904721167b32f7a4fa8c7b32a07a673d6cc86 +F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5 +F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a +F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871 +F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0 +F src/test_bestindex.c d23f80d334c59662af69191854c76b8d3d0c8c96 +F src/test_blob.c a0f7ad49a0c9d4b72f693fe2a71c58d7e507174d +F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274 +F src/test_config.c 0e3fbf51a9e56a50b80d52bba032e295fae76848 +F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e F src/test_devsym.c 4e58dec2602d8e139ca08659f62a62450587cb58 -F src/test_fs.c f10f840ca4f8c72e4837908bd8347ac4bcab074b -F src/test_func.c 37453d346cfcf118774efd5bf6187f7e6a7e3254 -F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd -F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32 -F src/test_intarray.c 870124b95ec4c645d4eb84f15efb7133528fb1a5 -F src/test_intarray.h 9dc57417fb65bc7835cc18548852cc08cc062202 +F src/test_fs.c ac62ce7d5c0c23aa6932891cad5746945564c91c +F src/test_func.c a2b4993da973b0ea60dd5d51a7066238ede8c329 +F src/test_hexio.c 1d4469ca61ab202a1fcec6543f584d2407205e8d +F src/test_init.c 4413c211a94b62157ca4c145b3f27c497f03c664 +F src/test_intarray.c 988fc61cb0ff539f4172c0d95f15287c92516f64 +F src/test_intarray.h f3b7672f5d1056eac563c0d6ea8480a660b1475c F src/test_journal.c d3b83f2bcb7792c709e57abddc456a2b1818643a F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd -F src/test_malloc.c 94c18e88d6df6d471a0d04ddb809f833d1739950 -F src/test_multiplex.c eafc567ebe162e36f17b5062285dfe90461cf8e9 -F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3 -F src/test_mutex.c dbdfaff8580071f2212a0deae3325a93a737819c +F src/test_malloc.c c05f6c40bd6c8bfe5f1718212f81fd5687f91766 +F src/test_multiplex.c 7c5b218d855cd0505a43185d55283b3fc257c8da +F src/test_multiplex.h 5436d03f2d0501d04f3ed50a75819e190495b635 +F src/test_mutex.c 7f4337ba23ee6b1d2ec81c189653608cb069926a F src/test_onefile.c 416f87a28d6d673352d33fc4b1c7d39db878e50f -F src/test_osinst.c ad0233b1dabb0390e25edded4ebd79a2a61538c6 +F src/test_osinst.c 98ef31ff03d55497829ca0f6c74a9f4e1aa48690 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 -F src/test_quota.c 180813f43683be5725458fc1ff13ac455d8e722d +F src/test_quota.c 6cb9297115b551f433a9ad1741817a9831abed99 F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d -F src/test_rtree.c 43fff4c5a01576d6d213f27472598801a247890c -F src/test_schema.c 2bdba21b82f601da69793e1f1d11bf481a79b091 +F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b +F src/test_schema.c f575932cb6274d12147a77e13ea4b49d52408513 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c 0d138a8180a312bf996b37fa66da5c5799d4d57b -F src/test_superlock.c 06797157176eb7085027d9dd278c0d7a105e3ec9 -F src/test_syscall.c 268c072541162564a882c57f54a6fee12ef4a4d2 -F src/test_tclvar.c d86412527da65468ee6fa1b8607c65d0af736bc4 -F src/test_thread.c af391ec03d23486dffbcc250b7e58e073f172af9 -F src/test_vfs.c 4d02f38bfb8f7f273da7ba84bfe000f5babf206c +F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e +F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 +F src/test_tclvar.c df9fe1213c2634687a9ca0b0bec0d2119d359ae3 +F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858 +F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_windirent.c 8f5fada630348558d5745b334702f301da1ffc61 F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c -F src/tokenize.c 3b29883b0ce4a6c6f643965b66b5ca6613178e59 -F src/treeview.c e4b41a37530a191579d3c53142cc44ee2eb99373 +F src/tokenize.c 78c8085bc7af1922aa687f0f4bbd716821330de5 +F src/treeview.c c56d6ddbed564efda746236b35bcbb8238daac4b F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280 -F src/update.c 3e67ab3c0814635f355fb1f8ab010a2b9e016e7d +F src/update.c 4f05ea8cddfa367d045e03589756c02199e8f9bd F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d -F src/vacuum.c c1a94413f8fa9d9b2cbe3af9a89377f777be676c -F src/vdbe.c 4f0378eb44406a6a61686dff7970fa314f0f44bd -F src/vdbe.h 5591b5add447096e31288b5a0a78ec5d7b5c5170 -F src/vdbeInt.h ddb157974436d87652de7dc641f7191496d9a8cd -F src/vdbeapi.c ba85b78fe08dc4a9ce747e62c89a2b4a4547e74c -F src/vdbeaux.c 5234052cc3a3a7ea9b7e4bd05f6f827168e6b99d -F src/vdbeblob.c c9f2f494b911c6fa34efd9803f0a10807da80f77 -F src/vdbemem.c 5cfef60e60e19cab6275d1b975bf4c791d575beb -F src/vdbesort.c 0a8f98366ae794442e6d1ef71d9553226d885d19 -F src/vdbetrace.c f75c5455d8cf389ef86a8bfdfd3177e0e3692484 -F src/vtab.c 23b6cdfa996152d43b390504ed4a942c8caf3a00 +F src/vacuum.c ca88e6d3f5270533dea68e21008493b10023c214 +F src/vdbe.c 75556e9cd0fefd200515133af759205e48e9fb33 +F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 +F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d +F src/vdbeapi.c c3f6715a99995c11748ecad91d25e93fd9fc390b +F src/vdbeaux.c e46568111862599cf475e7cdcbc1322551064906 +F src/vdbeblob.c 83d2d266383157b02e2b809350bb197e89d7895b +F src/vdbemem.c 1ecaa5ee0caff07255f25d04e8dc88befb6f88d1 +F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c +F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834 +F src/vtab.c 6b3cfaff7e4397739d6b48511e777ca58c6d06d4 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c 69cbc05af0e81321e12b3f9c2f13c35b4a936b37 -F src/wal.h 327e94c3e366154f52a8d9be9be0ea790cc4e08c -F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c bae50f2f18f9e8584549a77363858623b07e4915 -F src/whereInt.h 7de94b751f088fe3fdc8cc04a491376f0900a059 -F src/wherecode.c 8fdad9fbba723df1c1e8d07e7ea8507572040340 -F src/whereexpr.c eacc0e60d029a082b4fc0cc42ea98544add1319e +F src/wal.c 8605bf47b9d0c0202b14b36cadb4de22ab1d4f84 +F src/wal.h 520cb6942a265ac3d172e634266329a46e8c5b70 +F src/walker.c 2d2cc7fb0f320f7f415215d7247f3c584141ac09 +F src/where.c 5c9df42d50888be8274a5a0eb062eb0629869bd3 +F src/whereInt.h e5b939701a7ceffc5a3a8188a37f9746416ebcd0 +F src/wherecode.c 99707d11907c71d289ee9553d2d1a22f1fd8ba41 +F src/whereexpr.c 98ce9f3b8d92b6f741b9f3498f9619695739256e F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -469,8 +476,8 @@ F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 F test/alter.test 2facdddf08d0d48e75dc6cc312cd2b030f4835dd F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 -F test/alter3.test b3568d11c38c4599c92f24242eda34144d78dc10 -F test/alter4.test c461150723ac957f3b2214aa0b11552cd72023ec +F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3 +F test/alter4.test 0c33c542247ba5aee4f4a0133ac44bcf8f97e5e0 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test 3eb35a4af972f98422e5dc0586501b17d103d321 @@ -500,8 +507,8 @@ F test/attach3.test 359eb65d00102cdfcef6fa4e81dc1648f8f80b27 F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0 F test/auth.test 872a122b3977c1d1bb9fd637dc20016e5c01880f -F test/auth2.test 264c6af53cad9aba5218c68bbe18036e39007bfa -F test/auth3.test 5cfa94ed90c6617c42b7ba4b133fd79678b251c7 +F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1 +F test/auth3.test b810826b193831929951c0d50783a200e5ef6b72 F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7 F test/autoindex1.test 14b63a9f1e405fe6d5bfc8c8d00249c2ebaf13ea F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df @@ -524,6 +531,7 @@ F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c F test/bc_test1.c e0a092579552e066ed4ce7bcdaecfa69c4aacc8d F test/bestindex1.test 0cf1bd2d7b97d3a3a8c10736125274f64765c4ee F test/bestindex2.test 4a06b8922ab2fd09434870da8d1cdf525aaf7060 +F test/bestindex3.test dd5fa4f483cd0356549203c4522f8c9e21cb1fc0 F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc @@ -569,6 +577,7 @@ F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868 F test/collate8.test cd9b3d3f999b8520ffaa7cc1647061fc5bab1334 F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 +F test/collateB.test 8ec2accd2d7166c1eff0d2a39bc90262c6f89632 F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b F test/concfault.test 500f17c3fcfe7705114422bcc6ddd3c740001a43 @@ -613,16 +622,18 @@ F test/crashM.test d95f59046fa749b0d0822edf18a717788c8f318d F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8 F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c -F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47 +F test/csv01.test e0ba3caaa57e4c667a0b45977689fb8082f14348 +F test/ctime.test 0b995accd44a52914bd4744d5c1b6e1a56c7897c F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856 +F test/cursorhint2.test 2b45341d32d1aae9991a00ef31ebca339b274255 F test/date.test 984ac1e3e5e031386866f034006148d3972b4a65 -F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204 +F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5 F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab -F test/delete4.test 738044ee892ee0c84e0848e36ba92c55f907d52b +F test/delete4.test 21d2113217eeaacac2d99defe14fe6611615ae86 F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240 F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 @@ -741,7 +752,7 @@ F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c -F test/fts3conf.test 1c8b8adb0ab28a41b68d1514df44380bd7353402 +F test/fts3conf.test 60317efd562080e198b5bdc9fcd222ce32cf01d7 F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 @@ -751,7 +762,7 @@ F test/fts3defer2.test c540f5f5c2840f70c68fd9b597df817ec7170468 F test/fts3defer3.test dd53fc13223c6d8264a98244e9b19abd35ed71cd F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297 F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851 -F test/fts3expr.test 3401d47b229c4504424caf362cc4ff704cad4162 +F test/fts3expr.test 9466627007804d855bf9df2a0cfb3dac23686fdc F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8 F test/fts3expr4.test c39a15d676b14fc439d9bf845aa7bddcf4a74dc3 @@ -802,7 +813,7 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test b47377143f0c80f91ed29d722861077ff34415d5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzcheck.c f01d432d001ba29e7916df8411be7d4e7cddc574 +F test/fuzzcheck.c f2e8102f7829f7b946ebdb6f1a16c6f942d9de66 F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664 F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973 F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -817,7 +828,7 @@ F test/hook.test 3b7b99d0eece6d279812c2aef6fa08bdfabc633e F test/icu.test 73956798bace8982909c00476b216714a6d0559a F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607 F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8 -F test/in.test 61a24ae38d4b64ec69f06ccdf022992f68a98176 +F test/in.test 61d940ced6817bee66e4e0b09d5bc8608f57134b F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068 @@ -838,8 +849,9 @@ F test/index2.test f835d5e13ca163bd78c4459ca15fd2e4ed487407 F test/index3.test 81bc47890b8abfb181bc35f8d10b56c069803386 F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6 F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7 -F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a +F test/index6.test 43b4e29258b978fcdab84fc61df4f5212119dd09 F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c +F test/index8.test bc2e3db70e8e62459aaa1bd7e4a9b39664f8f9d7 F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985 F test/indexexpr1.test cb71b6586177b840e28110dd952178bb2bdfedc2 F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d @@ -852,7 +864,7 @@ F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6 F test/instr.test 737bbf80685232033f3abedc6ae92f75860b5dd2 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4 F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc -F test/intpkey.test 153b70e32d008f4f4b2dff8daf448a6110b62a9c +F test/intpkey.test ac71107a49a06492b69b82aafaf225400598d3c8 F test/io.test f95bca1783b01ea7761671560d023360d2dfa4cc F test/ioerr.test 2a24bd6ed5a8b062e64bfe1f6cf94fb25e92210d F test/ioerr2.test 2593563599e2cc6b6b4fcf5878b177bdd5d8df26 @@ -872,7 +884,7 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307 F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/json101.test ef42283f0b60d8bacbc2243448e7c84988578e52 +F test/json101.test 865776ed8580703e1684fe4b8ee2e473333bb121 F test/json102.test bf3fe7a706d30936a76a0f7a0375e1e8e73aff5a F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0 F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff @@ -882,7 +894,8 @@ F test/like.test 81632c437a947bf1f7130b19537da6a1a844806a F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4 F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e -F test/loadext.test 42a3b8166dfcadcb0e0c8710dc520d97c31a8b98 +F test/limit2.test 40e79f3d95f2077742aa5b7676f58c9af1735d83 +F test/loadext.test d077450695ddb5c1ea3ad7d48e5f5850fe732ad9 F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7 F test/lock.test be4fe08118fb988fed741f429b7dd5d65e1c90db F test/lock2.test 5242d8ac4e2d59c403aebff606af449b455aceff @@ -923,7 +936,7 @@ F test/memdb.test c1f2a343ad14398d5d6debda6ea33e80d0dafcc7 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test 6d268d0ae90f8d61a2356a1838665654d83de518 F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08 -F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd +F test/minmax.test 6751e87b409fe11b02e70a306d846fa544e25a41 F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f @@ -931,7 +944,7 @@ F test/misc1.test 6430dabfb4b4fa480633590118964201f94d3ccc F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3 -F test/misc5.test f96428ea95b3820aafc6f1c50cf48a09e4597ee1 +F test/misc5.test fff0f75e834bc09a39f6079320dd8c37de956f4f F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2 F test/misc8.test 21ac9d35a5e110279ae9e1588b8914f54de1c60b @@ -981,7 +994,7 @@ F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305 F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442 F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff -F test/permutations.test b6b3e165fdc1b8c82a820033646dbfc6a7a01746 +F test/permutations.test 544a2f317fc5045bc512ae432f89eae303c0d640 F test/pragma.test 1e94755164a3a3264cd39836de4bebcb7809e5f8 F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed @@ -1002,7 +1015,7 @@ F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8 F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 F test/regexp2.test aa7ffcc21350007a78361b82bcf3b74d12227144 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 -F test/releasetest.tcl f2519836bebefcc7db36b4946e8028d4885baa59 +F test/releasetest.tcl 53560b838b79c468ec9973e0dfa3d8892628887f F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5 @@ -1036,7 +1049,7 @@ F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0 F test/select7.test 95e370c42d47c3c52377d05e9ffc01ccff7c1f61 F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95 -F test/selectA.test e452bdb975f488ea46d091382a9185b5853ed2c7 +F test/selectA.test 101e722370ac6e84978c2958b8931c78b10a1709 F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25 F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977 F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394 @@ -1057,7 +1070,7 @@ F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5 F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 -F test/shell1.test 023657b3f8ba108dbd5ff2ebc8fceb3cf6d4ff9f +F test/shell1.test 65b10cd8a90cda9b5af9100a45689a57dcc01a31 F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b F test/shell3.test da513d522ef6f01cee8475dcf8332bff8982b3dd F test/shell4.test 69995ee1cc278eb149aa8746ce1f935f4eaf98b9 @@ -1066,12 +1079,12 @@ F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329 -F test/skipscan1.test d37a75b4be4eb9dedeb69b4f38b1d0a74b5021d7 +F test/skipscan1.test f2d4482f195f018c3732374d3985fc34bcc3ae1a F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5 F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2 F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b -F test/snapshot.test 3adc4ef09d407b501f899a6c329bdf45dc725c1b +F test/snapshot.test a19465046168b4420b5afeed37c3214e42a49f4a F test/snapshot_fault.test 062ff0438a074978d45e9f9a92e7ad459b74ee73 F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087 @@ -1079,7 +1092,7 @@ F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0 F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5 F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c -F test/sort5.test e47ec7a490b9b36787755874175d8f413a3883d9 +F test/sort5.test 30cc17768e0c06ecb048e08efec59c11811fd186 F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66 F test/speed1.test f2974a91d79f58507ada01864c0e323093065452 F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb @@ -1089,14 +1102,14 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/speedtest1.c 4476f7030775507f060d27b789e1f4123c5a5866 +F test/speedtest1.c 28221f433d358dd3fcf2ca504ed51409aa66ffaf F test/spellfix.test f9c1f431e2c096c8775fec032952320c0e4700db F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 F test/sqldiff1.test 28cd737cf1b0078b1ec1bbf425e674c47785835e F test/sqllimits1.test a74ee2a3740b9f9c2437c246d8fb77354862a142 F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a -F test/stat.test 66e95f97b9f724f9ab921d054ee0db3c2689f1ee +F test/stat.test f8f1279ffffabe6df825723af18cc6e0ae70a893 F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1 F test/stmt.test 64844332db69cf1a735fcb3e11548557fc95392f F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f @@ -1109,17 +1122,19 @@ F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849 F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529 F test/syscall.test f59ba4e25f7ba4a4c031026cc2ef8b6e4b4c639c F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04 -F test/tabfunc01.test f977868fa8bb7beb4b2072883190411653473906 +F test/tabfunc01.test 50a9fb379f9747fd0d40ea6d8fa3a101361bb537 F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 -F test/tclsqlite.test e1306001a0ca92250b691ea6d3cecaca5b6342aa +F test/tclsqlite.test 1d73b9203b1ca8798d7d7310742b8d3febc0d56e F test/tempdb.test bd92eba8f20e16a9136e434e20b280794de3cdb6 +F test/tempdb2.test 4fc92055f2a3f7626c0f2eabbb637dc021b311d5 F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900 F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 -F test/temptable2.test c3d8c138f493207612960bbd6a8c50e84975e2ee +F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e +F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1 -F test/tester.tcl 30c7a9be8601d1c1c9c93d013545ebcb28d64254 +F test/tester.tcl 949b4a73bd0324b7c796818d0d6a6715712932b3 F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -1132,6 +1147,7 @@ F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c a70a8e94bef23339d34226eb9521015ef99f4df8 F test/threadtest3.c 4b413718ab3ef5e4f9f1ab0502b89707c5a9ccdc F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925 +F test/time-wordcount.sh 8e0b0f8109367827ad5d58f5cc849705731e4b90 F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 F test/tkt-2a5629202f.test 0521bd25658428baa26665aa53ffed9367d33af2 @@ -1276,6 +1292,7 @@ F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7 F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4 F test/trace.test 6f676313e3ebd2a50585036d2f212a3319dd5836 F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983 +F test/trace3.test 01e4111d582c7b20ab1c63156169157d256bc3d5 F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 @@ -1315,6 +1332,7 @@ F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 +F test/vacuummem.test e53a3fdca4612a99c515e1afe7934728a2383764 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/view.test 765802c7a66d37fabd5ac8e2f2dbe572b43eb9ab @@ -1356,20 +1374,21 @@ F test/walcksum.test bb234a1bb42248b3515d992b719708015c384278 F test/walcrash.test 21038858cc552077b0522f50b0fa87e38139306a F test/walcrash2.test a0edab4e5390f03b99a790de89aad15d6ec70b36 F test/walcrash3.test e426aa58122d20f2b9fbe9a507f9eb8cab85b8af +F test/walcrash4.test e7b6e7639a950a0cca8e210e248c8dad4d63bf20 F test/walfault.test 1f8389f7709877e9b4cc679033d71d6fe529056b F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496 F test/waloverwrite.test dad2f26567f1b45174e54fbf9a8dc1cb876a7f03 F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6 -F test/walprotocol.test 059cb75484a1ecf6357a2c1b3324b8156749221e +F test/walprotocol.test 0b92feb132ccebd855494d917d3f6c2d717ace20 F test/walro.test 4ab7ac01b77c2f894235c699d59e3e3c7f15a160 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e F test/where.test f0c325563acde44f2c4ea6ba348e9e29f7121757 F test/where2.test 478d2170637b9211f593120648858593bf2445a1 -F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e +F test/where3.test 54cdeb02157acc979de41530b804ae7b09552bf1 F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b @@ -1393,6 +1412,7 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d +F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc F test/with1.test cef099a491eac9874f2c28bd2dc86394fb3e47b3 F test/with2.test 2b40da883658eb74ad8ad06afabe11a408e7fb87 F test/with3.test 511bacdbe41c49cf34f9fd1bd3245fe1575bca98 @@ -1403,7 +1423,7 @@ F test/without_rowid3.test aad4f9d383e199349b6c7e508a778f7dff5dff79 F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e -F test/wordcount.c 2a0a6c0d0e8e8bbbac1f06d72a6791828c37c0cf +F test/wordcount.c 97856eec21fd00d77da720007b1888c383f63dcf F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa F test/zerodamage.test e59a56443d6298ecf7435f618f0b27654f0c849e F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5 @@ -1414,6 +1434,7 @@ F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 +F tool/dbhash.c a06228aa21ebc4e6ea8daa486601d938499238a5 F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2 F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1 F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439 @@ -1421,8 +1442,9 @@ F tool/fuzzershell.c 94019b185caceffc9f7c7b678a6489e42bc2aefa F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4 F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce -F tool/lemon.c 83318dff3911e47f2b85e136e56aa1c4674a2d2b -F tool/lempar.c 404ea3dc27dbeed343f0e61b1d36e97b9f5f0fb6 +F tool/lemon.c e3aa9ba3469804d7cae13b5e041aab192b7b381a +F tool/lempar.c 147e42a5cd83ce38275fde0d07a5df3330cb9b3b +F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9 F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862 F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca F tool/mkautoconfamal.sh e855df211ecbcc7131dee817110ff386cfb112f7 @@ -1433,15 +1455,16 @@ F tool/mkopcodeh.tcl a01d2c1d8a6205b03fc635adf3735b4c523befd3 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkpragmatab.tcl f0d5bb266d1d388cf86fce5ba01a891e95d72d41 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 -F tool/mksqlite3c-noext.tcl 87240b09c20042999b41d5fabe091b7111287835 +F tool/mksqlite3c-noext.tcl aa58ea3be311c81821c2cd3209f55e46b07ab656 F tool/mksqlite3c.tcl 63af8429841f08552e6da1d93b3dee4a93ff8071 F tool/mksqlite3h.tcl e7b106fc4f29fbc258e8ba9b88d9108332ea2ade F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mkvsix.tcl 4abcaf3267171b2faadaf9b82a0dfbaa6e98f8b7 F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091 F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97 +F tool/opcodesum.tcl 740ed206ba8c5040018988129abbf3089a0ccf4a F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b -F tool/replace.tcl 7727c60a04299b65a92f5e1590896fea0f25b9e0 +F tool/replace.tcl 60f91e8dd06ab81f74d213ecbd9c9945f32ac048 F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 F tool/run-speed-test.sh f95d19fd669b68c4c38b6b475242841d47c66076 @@ -1451,15 +1474,16 @@ F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68 F tool/showstat4.c bda40d6e395df7edb6e9ea630784d3d762c35b4b F tool/showwal.c ec79959834f7b21f1e0a2aa52bb7c056d2203977 F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe -F tool/spaceanal.tcl 93c1fdc9733c525b17a2024c7df193daa002e037 +F tool/spaceanal.tcl 85d90e6674d8298e3eaf82dbcef3abc2d5317f3e +F tool/speed-check.sh 45d3bf861b009993ff401f0d00e34a4cc937fce4 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 d9be87f1c340285a3e081eb19b4a247981ed290c -F tool/sqldiff.c ca315aca4e2d24233e8f2000edea5880c53d1875 -F tool/srcck1.c 4f65e1a6748e42f24c0ea629dddc934d821c729a +F tool/sqldiff.c c965d49bf2677db06103854b47e105484b5b1b84 +F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d F tool/symbols.sh c5a617b8c61a0926747a56c65f5671ef8ac0e148 @@ -1491,7 +1515,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d0d0bab4e92402b6af98366be6e8955588613a51 -R fd9335f42062b54523dedc8b82de076d -U dan -Z 5f55bfec2b6a0fc682708f8a8d6277b4 +P 2c61b7ab1808d5576a21fea84c3f414db51abfa5 ed406d31ff54ee3de8db91690a966e5c561f8f94 +R 6144d43db42087097a2449fa8c85b94a +U drh +Z 204e3a86f7529a03a274de60935ccb45 diff --git a/manifest.uuid b/manifest.uuid index aa42780861..51761e50c2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2c61b7ab1808d5576a21fea84c3f414db51abfa5 \ No newline at end of file +d9f8918c5b7b6c8540b3f433142e1b4aa4433885 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 642c1fb677..806ed49172 100644 --- a/src/alter.c +++ b/src/alter.c @@ -601,6 +601,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ Expr *pDflt; /* Default value for the new column */ sqlite3 *db; /* The database connection; */ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ + int r1; /* Temporary registers */ db = pParse->db; if( pParse->nErr || db->mallocFailed ) return; @@ -695,16 +696,18 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ db->flags = savedDbFlags; } - /* If the default value of the new column is NULL, then the file - ** format to 2. If the default value of the new column is not NULL, - ** the file format be 3. Back when this feature was first added - ** in 2006, we went to the trouble to upgrade the file format to the - ** minimum support values. But 10-years on, we can assume that all - ** extent versions of SQLite support file-format 4, so we always and - ** unconditionally upgrade to 4. + /* Make sure the schema version is at least 3. But do not upgrade + ** from less than 3 to 4, as that will corrupt any preexisting DESC + ** index. */ - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, - SQLITE_MAX_FILE_FORMAT); + r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); + sqlite3VdbeUsesBtree(v, iDb); + sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2); + sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); + sqlite3ReleaseTempReg(pParse, r1); /* Reload the schema of the modified table. */ reloadTableSchema(pParse, pTab, pTab->zName); diff --git a/src/auth.c b/src/auth.c index 9768fc2fc0..8a3f8e7d08 100644 --- a/src/auth.c +++ b/src/auth.c @@ -111,6 +111,7 @@ int sqlite3AuthReadCol( char *zDb = db->aDb[iDb].zName; /* Name of attached database */ int rc; /* Auth callback return code */ + if( db->init.busy ) return SQLITE_OK; rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext #ifdef SQLITE_USER_AUTHENTICATION ,db->auth.zAuthUser diff --git a/src/backup.c b/src/backup.c index 455671a1ad..19c3b2a647 100644 --- a/src/backup.c +++ b/src/backup.c @@ -777,10 +777,10 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ ** sqlite3_backup_step(), we can guarantee that the copy finishes ** within a single call (unless an error occurs). The assert() statement ** checks this assumption - (p->rc) should be set to either SQLITE_DONE - ** or an error code. - */ + ** or an error code. */ sqlite3_backup_step(&b, 0x7FFFFFFF); assert( b.rc!=SQLITE_OK ); + rc = sqlite3_backup_finish(&b); if( rc==SQLITE_OK ){ pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED; diff --git a/src/btree.c b/src/btree.c index 002a3ec5bd..a783d94fc9 100644 --- a/src/btree.c +++ b/src/btree.c @@ -646,6 +646,15 @@ static void releasePage(MemPage *pPage); /* Forward reference */ static int cursorHoldsMutex(BtCursor *p){ return sqlite3_mutex_held(p->pBt->mutex); } + +/* Verify that the cursor and the BtShared agree about what is the current +** database connetion. This is important in shared-cache mode. If the database +** connection pointers get out-of-sync, it is possible for routines like +** btreeInitPage() to reference an stale connection pointer that references a +** a connection that has already closed. This routine is used inside assert() +** statements only and for the purpose of double-checking that the btree code +** does keep the database connection pointers up-to-date. +*/ static int cursorOwnsBtShared(BtCursor *p){ assert( cursorHoldsMutex(p) ); return (p->pBtree->db==p->pBt->db); @@ -805,21 +814,19 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){ ** the key. */ static int saveCursorKey(BtCursor *pCur){ - int rc; + int rc = SQLITE_OK; assert( CURSOR_VALID==pCur->eState ); assert( 0==pCur->pKey ); assert( cursorHoldsMutex(pCur) ); - rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); - assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ - - /* If this is an intKey table, then the above call to BtreeKeySize() - ** stores the integer key in pCur->nKey. In this case this value is - ** all that is required. Otherwise, if pCur is not open on an intKey - ** table, then malloc space for and store the pCur->nKey bytes of key - ** data. */ - if( 0==pCur->curIntKey ){ - void *pKey = sqlite3Malloc( pCur->nKey ); + if( pCur->curIntKey ){ + /* Only the rowid is required for a table btree */ + pCur->nKey = sqlite3BtreeIntegerKey(pCur); + }else{ + /* For an index btree, save the complete key content */ + void *pKey; + pCur->nKey = sqlite3BtreePayloadSize(pCur); + pKey = sqlite3Malloc( pCur->nKey ); if( pKey ){ rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey); if( rc==SQLITE_OK ){ @@ -1879,11 +1886,11 @@ static int decodeFlags(MemPage *pPage, int flagByte){ pPage->xCellSize = cellSizePtr; pBt = pPage->pBt; if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ - /* EVIDENCE-OF: R-03640-13415 A value of 5 means the page is an interior - ** table b-tree page. */ + /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an + ** interior table b-tree page. */ assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); - /* EVIDENCE-OF: R-20501-61796 A value of 13 means the page is a leaf - ** table b-tree page. */ + /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a + ** leaf table b-tree page. */ assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); pPage->intKey = 1; if( pPage->leaf ){ @@ -1897,11 +1904,11 @@ static int decodeFlags(MemPage *pPage, int flagByte){ pPage->maxLocal = pBt->maxLeaf; pPage->minLocal = pBt->minLeaf; }else if( flagByte==PTF_ZERODATA ){ - /* EVIDENCE-OF: R-27225-53936 A value of 2 means the page is an interior - ** index b-tree page. */ + /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an + ** interior index b-tree page. */ assert( (PTF_ZERODATA)==2 ); - /* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf - ** index b-tree page. */ + /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a + ** leaf index b-tree page. */ assert( (PTF_ZERODATA|PTF_LEAF)==10 ); pPage->intKey = 0; pPage->intKeyLeaf = 0; @@ -2533,9 +2540,9 @@ int sqlite3BtreeOpen( #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* Add the new BtShared object to the linked list sharable BtShareds. */ + pBt->nRef = 1; if( p->sharable ){ MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) - pBt->nRef = 1; MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);) if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){ pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST); @@ -2606,6 +2613,7 @@ btree_open_out: assert( sqlite3_mutex_held(mutexOpen) ); sqlite3_mutex_leave(mutexOpen); } + assert( rc!=SQLITE_OK || sqlite3BtreeConnectionCount(*ppBtree)>0 ); return rc; } @@ -4658,46 +4666,33 @@ int sqlite3BtreeCursorIsValid(BtCursor *pCur){ #endif /* NDEBUG */ /* -** Set *pSize to the size of the buffer needed to hold the value of -** the key for the current entry. If the cursor is not pointing -** to a valid entry, *pSize is set to 0. -** -** For a table with the INTKEY flag set, this routine returns the key -** itself, not the number of bytes in the key. -** -** The caller must position the cursor prior to invoking this routine. -** -** This routine cannot fail. It always returns SQLITE_OK. +** Return the value of the integer key or "rowid" for a table btree. +** This routine is only valid for a cursor that is pointing into a +** ordinary table btree. If the cursor points to an index btree or +** is invalid, the result of this routine is undefined. */ -int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ +i64 sqlite3BtreeIntegerKey(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); + assert( pCur->curIntKey ); getCellInfo(pCur); - *pSize = pCur->info.nKey; - return SQLITE_OK; + return pCur->info.nKey; } /* -** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. +** Return the number of bytes of payload for the entry that pCur is +** currently pointing to. For table btrees, this will be the amount +** of data. For index btrees, this will be the size of the key. ** ** The caller must guarantee that the cursor is pointing to a non-NULL ** valid entry. In other words, the calling procedure must guarantee ** that the cursor has Cursor.eState==CURSOR_VALID. -** -** Failure is not possible. This function always returns SQLITE_OK. -** It might just as well be a procedure (returning void) but we continue -** to return an integer result code for historical reasons. */ -int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ - assert( cursorOwnsBtShared(pCur) ); +u32 sqlite3BtreePayloadSize(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 ); - assert( pCur->iPageapPage[pCur->iPage]->intKeyLeaf==1 ); getCellInfo(pCur); - *pSize = pCur->info.nPayload; - return SQLITE_OK; + return pCur->info.nPayload; } /* @@ -5139,10 +5134,7 @@ static const void *fetchPayload( ** These routines is used to get quick access to key and data ** in the common case where no overflow pages are used. */ -const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){ - return fetchPayload(pCur, pAmt); -} -const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){ +const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ return fetchPayload(pCur, pAmt); } @@ -5475,11 +5467,12 @@ int sqlite3BtreeMovetoUnpacked( assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( pRes ); assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); + assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) ); /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ - if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 - && pCur->curIntKey + if( pIdxKey==0 + && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ if( pCur->info.nKey==intKey ){ *pRes = 0; @@ -6478,9 +6471,7 @@ static int clearCell( static int fillInCell( MemPage *pPage, /* The page that contains the cell */ unsigned char *pCell, /* Complete text of the cell */ - const void *pKey, i64 nKey, /* The key */ - const void *pData,int nData, /* The data */ - int nZero, /* Extra zero bytes to append to pData */ + const BtreePayload *pX, /* Payload with which to construct the cell */ int *pnSize /* Write cell size here */ ){ int nPayload; @@ -6504,26 +6495,23 @@ static int fillInCell( /* Fill in the header. */ nHeader = pPage->childPtrSize; - nPayload = nData + nZero; - if( pPage->intKeyLeaf ){ - nHeader += putVarint32(&pCell[nHeader], nPayload); - }else{ - assert( nData==0 ); - assert( nZero==0 ); - } - nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - - /* Fill in the payload size */ if( pPage->intKey ){ - pSrc = pData; - nSrc = nData; - nData = 0; - }else{ - assert( nKey<=0x7fffffff && pKey!=0 ); - nPayload = (int)nKey; - pSrc = pKey; - nSrc = (int)nKey; + nPayload = pX->nData + pX->nZero; + pSrc = pX->pData; + nSrc = pX->nData; + assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */ + nHeader += putVarint32(&pCell[nHeader], nPayload); + nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey); + }else{ + assert( pX->nData==0 ); + assert( pX->nZero==0 ); + assert( pX->nKey<=0x7fffffff && pX->pKey!=0 ); + nSrc = nPayload = (int)pX->nKey; + pSrc = pX->pKey; + nHeader += putVarint32(&pCell[nHeader], nPayload); } + + /* Fill in the payload */ if( nPayload<=pPage->maxLocal ){ n = nHeader + nPayload; testcase( n==3 ); @@ -6561,7 +6549,7 @@ static int fillInCell( CellInfo info; pPage->xParseCell(pPage, pCell, &info); assert( nHeader==(int)(info.pPayload - pCell) ); - assert( info.nKey==nKey ); + assert( info.nKey==pX->nKey ); assert( *pnSize == info.nSize ); assert( spaceLeft == info.nLocal ); } @@ -6645,10 +6633,6 @@ static int fillInCell( pSrc += n; nSrc -= n; spaceLeft -= n; - if( nSrc==0 ){ - nSrc = nData; - pSrc = pData; - } } releasePage(pToRelease); return SQLITE_OK; @@ -6715,6 +6699,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ ** in pTemp or the original pCell) and also record its index. ** Allocating a new entry in pPage->aCell[] implies that ** pPage->nOverflow is incremented. +** +** *pRC must be SQLITE_OK when this routine is called. */ static void insertCell( MemPage *pPage, /* Page into which we are copying */ @@ -6730,8 +6716,7 @@ static void insertCell( u8 *data; /* The content of the whole page */ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ - if( *pRC ) return; - + assert( *pRC==SQLITE_OK ); assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( MX_CELL(pPage->pBt)<=10921 ); assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); @@ -6804,7 +6789,7 @@ static void insertCell( /* ** A CellArray object contains a cache of pointers and sizes for a -** consecutive sequence of cells that might be held multiple pages. +** consecutive sequence of cells that might be held on multiple pages. */ typedef struct CellArray CellArray; struct CellArray { @@ -7236,8 +7221,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ while( ((*(pOut++) = *(pCell++))&0x80) && pCellnCell, pSpace, (int)(pOut-pSpace), - 0, pPage->pgno, &rc); + if( rc==SQLITE_OK ){ + insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), + 0, pPage->pgno, &rc); + } /* Set the right-child pointer of pParent to point to the new page. */ put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); @@ -7757,7 +7744,7 @@ static int balance_nonroot( assert( r szLeft-(b.szCell[r]+2)) ){ + && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){ break; } szRight += b.szCell[d] + 2; @@ -8329,13 +8316,19 @@ static int balance(BtCursor *pCur){ /* -** Insert a new record into the BTree. The key is given by (pKey,nKey) -** and the data is given by (pData,nData). The cursor is used only to -** define what table the record should be inserted into. The cursor -** is left pointing at a random location. +** Insert a new record into the BTree. The content of the new record +** is described by the pX object. The pCur cursor is used only to +** define what table the record should be inserted into, and is left +** pointing at a random location. ** -** For an INTKEY table, only the nKey value of the key is used. pKey is -** ignored. For a ZERODATA table, the pData and nData are both ignored. +** For a table btree (used for rowid tables), only the pX.nKey value of +** the key is used. The pX.pKey value must be NULL. The pX.nKey is the +** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields +** hold the content of the row. +** +** For an index btree (used for indexes and WITHOUT ROWID tables), the +** key is an arbitrary byte sequence stored in pX.pKey,nKey. The +** pX.pData,nData,nZero fields must be zero. ** ** If the seekResult parameter is non-zero, then a successful call to ** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already @@ -8352,9 +8345,7 @@ static int balance(BtCursor *pCur){ */ int sqlite3BtreeInsert( BtCursor *pCur, /* Insert data into the table of this cursor */ - const void *pKey, i64 nKey, /* The key of the new record */ - const void *pData, int nData, /* The data of the new record */ - int nZero, /* Number of extra 0 bytes to append to data */ + const BtreePayload *pX, /* Content of the row to be inserted */ int appendBias, /* True if this is likely an append */ int seekResult /* Result of prior MovetoUnpacked() call */ ){ @@ -8384,7 +8375,7 @@ int sqlite3BtreeInsert( ** keys with no associated data. If the cursor was opened expecting an ** intkey table, the caller should be inserting integer keys with a ** blob of associated data. */ - assert( (pKey==0)==(pCur->pKeyInfo==0) ); + assert( (pX->pKey==0)==(pCur->pKeyInfo==0) ); /* Save the positions of any other cursors open on this table. ** @@ -8403,38 +8394,38 @@ int sqlite3BtreeInsert( } if( pCur->pKeyInfo==0 ){ - assert( pKey==0 ); + assert( pX->pKey==0 ); /* If this is an insert into a table b-tree, invalidate any incrblob ** cursors open on the row being replaced */ - invalidateIncrblobCursors(p, nKey, 0); + invalidateIncrblobCursors(p, pX->nKey, 0); /* If the cursor is currently on the last row and we are appending a ** new row onto the end, set the "loc" to avoid an unnecessary ** btreeMoveto() call */ - if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0 - && pCur->info.nKey==nKey-1 ){ + if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0 + && pCur->info.nKey==pX->nKey-1 ){ loc = -1; }else if( loc==0 ){ - rc = sqlite3BtreeMovetoUnpacked(pCur, 0, nKey, appendBias, &loc); + rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc); if( rc ) return rc; } }else if( loc==0 ){ - rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); + rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc); if( rc ) return rc; } assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); pPage = pCur->apPage[pCur->iPage]; - assert( pPage->intKey || nKey>=0 ); + assert( pPage->intKey || pX->nKey>=0 ); assert( pPage->leaf || !pPage->intKey ); TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", - pCur->pgnoRoot, nKey, nData, pPage->pgno, + pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); assert( pPage->isInit ); newCell = pBt->pTmpSpace; assert( newCell!=0 ); - rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); + rc = fillInCell(pPage, newCell, pX, &szNew); if( rc ) goto end_insert; assert( szNew==pPage->xCellSize(pPage, newCell) ); assert( szNew <= MX_CELL_SIZE(pBt) ); @@ -8460,6 +8451,7 @@ int sqlite3BtreeInsert( assert( pPage->leaf ); } insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); + assert( pPage->nOverflow==0 || rc==SQLITE_OK ); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); /* If no error has occurred and pPage has an overflow cell, call balance() @@ -8483,7 +8475,8 @@ int sqlite3BtreeInsert( ** row without seeking the cursor. This can be a big performance boost. */ pCur->info.nSize = 0; - if( rc==SQLITE_OK && pPage->nOverflow ){ + if( pPage->nOverflow ){ + assert( rc==SQLITE_OK ); pCur->curFlags &= ~(BTCF_ValidNKey); rc = balance(pCur); @@ -8619,7 +8612,9 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ pTmp = pBt->pTmpSpace; assert( pTmp!=0 ); rc = sqlite3PagerWrite(pLeaf->pDbPage); - insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); + if( rc==SQLITE_OK ){ + insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); + } dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); if( rc ) return rc; } @@ -10137,4 +10132,14 @@ int sqlite3BtreeExclusiveLock(Btree *p){ int sqlite3BtreeSharable(Btree *p){ return p->sharable; } + +/* +** Return the number of connections to the BtShared object accessed by +** the Btree handle passed as the only argument. For private caches +** this is always 1. For shared caches it may be 1 or greater. +*/ +int sqlite3BtreeConnectionCount(Btree *p){ + testcase( p->sharable ); + return p->pBt->nRef; +} #endif diff --git a/src/btree.h b/src/btree.h index 9e4af144fd..bffaf6da43 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,8 +13,8 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. */ -#ifndef _BTREE_H_ -#define _BTREE_H_ +#ifndef SQLITE_BTREE_H +#define SQLITE_BTREE_H /* TODO: This definition is just included so other modules compile. It ** needs to be revisited. @@ -39,6 +39,7 @@ typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; +typedef struct BtreePayload BtreePayload; int sqlite3BtreeOpen( @@ -250,19 +251,43 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags); #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ -int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, - const void *pData, int nData, - int nZero, int bias, int seekResult); +/* An instance of the BtreePayload object describes the content of a single +** entry in either an index or table btree. +** +** Index btrees (used for indexes and also WITHOUT ROWID tables) contain +** an arbitrary key and no data. These btrees have pKey,nKey set to their +** key and pData,nData,nZero set to zero. +** +** Table btrees (used for rowid tables) contain an integer rowid used as +** the key and passed in the nKey field. The pKey field is zero. +** pData,nData hold the content of the new entry. nZero extra zero bytes +** are appended to the end of the content when constructing the entry. +** +** This object is used to pass information into sqlite3BtreeInsert(). The +** same information used to be passed as five separate parameters. But placing +** the information into this object helps to keep the interface more +** organized and understandable, and it also helps the resulting code to +** run a little faster by using fewer registers for parameter passing. +*/ +struct BtreePayload { + const void *pKey; /* Key content for indexes. NULL for tables */ + sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */ + const void *pData; /* Data for tables. NULL for indexes */ + int nData; /* Size of pData. 0 if none. */ + int nZero; /* Extra zero data appended after pData,nData */ +}; + +int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, + int bias, int seekResult); int sqlite3BtreeFirst(BtCursor*, int *pRes); int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int *pRes); int sqlite3BtreeEof(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int *pRes); -int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); +i64 sqlite3BtreeIntegerKey(BtCursor*); int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); -const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt); -const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt); -int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); +u32 sqlite3BtreePayloadSize(BtCursor*); int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); @@ -305,11 +330,13 @@ void sqlite3BtreeCursorList(Btree*); void sqlite3BtreeEnterAll(sqlite3*); int sqlite3BtreeSharable(Btree*); void sqlite3BtreeEnterCursor(BtCursor*); + int sqlite3BtreeConnectionCount(Btree*); #else # define sqlite3BtreeEnter(X) # define sqlite3BtreeEnterAll(X) # define sqlite3BtreeSharable(X) 0 # define sqlite3BtreeEnterCursor(X) +# define sqlite3BtreeConnectionCount(X) 1 #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE @@ -333,4 +360,4 @@ void sqlite3BtreeCursorList(Btree*); # define sqlite3SchemaMutexHeld(X,Y,Z) 1 #endif -#endif /* _BTREE_H_ */ +#endif /* SQLITE_BTREE_H */ diff --git a/src/build.c b/src/build.c index 48abcd6eb8..744744a98b 100644 --- a/src/build.c +++ b/src/build.c @@ -338,7 +338,7 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ */ Table *sqlite3LocateTable( Parse *pParse, /* context in which to report errors */ - int isView, /* True if looking for a VIEW rather than a TABLE */ + u32 flags, /* LOCATE_VIEW or LOCATE_NOERR */ const char *zName, /* Name of the table we are looking for */ const char *zDbase /* Name of the database. Might be NULL */ ){ @@ -352,7 +352,7 @@ Table *sqlite3LocateTable( p = sqlite3FindTable(pParse->db, zName, zDbase); if( p==0 ){ - const char *zMsg = isView ? "no such view" : "no such table"; + const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUALTABLE if( sqlite3FindDbName(pParse->db, zDbase)<1 ){ /* If zName is the not the name of a table in the schema created using @@ -364,12 +364,14 @@ Table *sqlite3LocateTable( } } #endif - if( zDbase ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); + if( (flags & LOCATE_NOERR)==0 ){ + if( zDbase ){ + sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); + }else{ + sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); + } + pParse->checkSchema = 1; } - pParse->checkSchema = 1; } return p; @@ -386,7 +388,7 @@ Table *sqlite3LocateTable( */ Table *sqlite3LocateTableItem( Parse *pParse, - int isView, + u32 flags, struct SrcList_item *p ){ const char *zDb; @@ -397,7 +399,7 @@ Table *sqlite3LocateTableItem( }else{ zDb = p->zDatabase; } - return sqlite3LocateTable(pParse, isView, p->zName, zDb); + return sqlite3LocateTable(pParse, flags, p->zName, zDb); } /* @@ -592,16 +594,10 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ ** db parameter can be used with db->pnBytesFreed to measure the memory ** used by the Table object. */ -void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ +static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ Index *pIndex, *pNext; TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */ - assert( !pTable || pTable->nRef>0 ); - - /* Do not delete the table until the reference count reaches zero. */ - if( !pTable ) return; - if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return; - /* Record the number of outstanding lookaside allocations in schema Tables ** prior to doing any free() operations. Since schema Tables do not use ** lookaside, this number should not change. */ @@ -611,8 +607,9 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ /* Delete all indices associated with this table. */ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ pNext = pIndex->pNext; - assert( pIndex->pSchema==pTable->pSchema ); - if( !db || db->pnBytesFreed==0 ){ + assert( pIndex->pSchema==pTable->pSchema + || (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) ); + if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){ char *zName = pIndex->zName; TESTONLY ( Index *pOld = ) sqlite3HashInsert( &pIndex->pSchema->idxHash, zName, 0 @@ -641,6 +638,13 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ /* Verify that no lookaside memory was used by schema tables */ assert( nLookaside==0 || nLookaside==db->lookaside.nOut ); } +void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ + /* Do not delete the table until the reference count reaches zero. */ + if( !pTable ) return; + if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return; + deleteTable(db, pTable); +} + /* ** Unlink the given table from the hash tables and the delete the @@ -1287,7 +1291,7 @@ void sqlite3AddPrimaryKey( Column *pCol = 0; int iCol = -1, i; int nTerm; - if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit; + if( pTab==0 ) goto primary_key_exit; if( pTab->tabFlags & TF_HasPrimaryKey ){ sqlite3ErrorMsg(pParse, "table \"%s\" has more than one primary key", pTab->zName); @@ -1333,12 +1337,8 @@ void sqlite3AddPrimaryKey( "INTEGER PRIMARY KEY"); #endif }else{ - Index *p; - p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, - 0, sortOrder, 0); - if( p ){ - p->idxType = SQLITE_IDXTYPE_PRIMARYKEY; - } + sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, + 0, sortOrder, 0, SQLITE_IDXTYPE_PRIMARYKEY); pList = 0; } @@ -1655,21 +1655,23 @@ static int hasColumn(const i16 *aiCol, int nCol, int x){ ** are appropriate for a WITHOUT ROWID table instead of a rowid table. ** Changes include: ** -** (1) Convert the OP_CreateTable into an OP_CreateIndex. There is +** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL. +** (2) Convert the OP_CreateTable into an OP_CreateIndex. There is ** no rowid btree for a WITHOUT ROWID. Instead, the canonical ** data storage is a covering index btree. -** (2) Bypass the creation of the sqlite_master table entry +** (3) Bypass the creation of the sqlite_master table entry ** for the PRIMARY KEY as the primary key index is now ** identified by the sqlite_master table entry of the table itself. -** (3) Set the Index.tnum of the PRIMARY KEY Index object in the +** (4) Set the Index.tnum of the PRIMARY KEY Index object in the ** schema to the rootpage from the main table. -** (4) Set all columns of the PRIMARY KEY schema object to be NOT NULL. ** (5) Add all table columns to the PRIMARY KEY Index object ** so that the PRIMARY KEY is a covering index. The surplus ** columns are part of KeyInfo.nXField and are not used for ** sorting or lookup or uniqueness checks. ** (6) Replace the rowid tail on all automatically generated UNIQUE ** indices with the PRIMARY KEY columns. +** +** For virtual tables, only (1) is performed. */ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ Index *pIdx; @@ -1679,6 +1681,20 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ sqlite3 *db = pParse->db; Vdbe *v = pParse->pVdbe; + /* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables) + */ + if( !db->init.imposterTable ){ + for(i=0; inCol; i++){ + if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){ + pTab->aCol[i].notNull = OE_Abort; + } + } + } + + /* The remaining transformations only apply to b-tree tables, not to + ** virtual tables */ + if( IN_DECLARE_VTAB ) return; + /* Convert the OP_CreateTable opcode that would normally create the ** root-page for the table into an OP_CreateIndex opcode. The index ** created will become the PRIMARY KEY index. @@ -1700,9 +1716,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ if( pList==0 ) return; pList->a[0].sortOrder = pParse->iPkSortOrder; assert( pParse->pNewTable==pTab ); - pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0); - if( pPk==0 ) return; - pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY; + sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0, + SQLITE_IDXTYPE_PRIMARYKEY); + if( db->mallocFailed ) return; + pPk = sqlite3PrimaryKeyIndex(pTab); pTab->iPKey = -1; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); @@ -1730,19 +1747,11 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ } pPk->nKeyCol = j; } - pPk->isCovering = 1; assert( pPk!=0 ); + pPk->isCovering = 1; + if( !db->init.imposterTable ) pPk->uniqNotNull = 1; nPk = pPk->nKeyCol; - /* Make sure every column of the PRIMARY KEY is NOT NULL. (Except, - ** do not enforce this for imposter tables.) */ - if( !db->init.imposterTable ){ - for(i=0; iaCol[pPk->aiColumn[i]].notNull = OE_Abort; - } - pPk->uniqNotNull = 1; - } - /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; @@ -2221,7 +2230,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ pTable->nCol = 0; nErr++; } - if( pSelTab ) sqlite3DeleteTable(db, pSelTab); + sqlite3DeleteTable(db, pSelTab); sqlite3SelectDelete(db, pSel); db->lookaside.bDisable--; } else { @@ -2497,6 +2506,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ assert( pName->nSrc==1 ); if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; if( noErr ) db->suppressErr++; + assert( isView==0 || isView==LOCATE_VIEW ); pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); if( noErr ) db->suppressErr--; @@ -2867,12 +2877,8 @@ Index *sqlite3AllocateIndexObject( ** pList is a list of columns to be indexed. pList will be NULL if this ** is a primary key or unique-constraint on the most recent column added ** to the table currently under construction. -** -** If the index is created successfully, return a pointer to the new Index -** structure. This is used by sqlite3AddPrimaryKey() to mark the index -** as the tables primary key (Index.idxType==SQLITE_IDXTYPE_PRIMARYKEY) */ -Index *sqlite3CreateIndex( +void sqlite3CreateIndex( Parse *pParse, /* All information about this parse */ Token *pName1, /* First part of index name. May be NULL */ Token *pName2, /* Second part of index name. May be NULL */ @@ -2882,9 +2888,9 @@ Index *sqlite3CreateIndex( Token *pStart, /* The CREATE token that begins this statement */ Expr *pPIWhere, /* WHERE clause for partial indices */ int sortOrder, /* Sort order of primary key when pList==NULL */ - int ifNotExist /* Omit error if index already exists */ + int ifNotExist, /* Omit error if index already exists */ + u8 idxType /* The index type */ ){ - Index *pRet = 0; /* Pointer to return */ Table *pTab = 0; /* Table to be indexed */ Index *pIndex = 0; /* The index to be created */ char *zName = 0; /* Name of the index */ @@ -2902,7 +2908,10 @@ Index *sqlite3CreateIndex( char *zExtra = 0; /* Extra space after the Index object */ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - if( db->mallocFailed || IN_DECLARE_VTAB || pParse->nErr>0 ){ + if( db->mallocFailed || pParse->nErr>0 ){ + goto exit_create_index; + } + if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){ goto exit_create_index; } if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ @@ -3028,6 +3037,13 @@ Index *sqlite3CreateIndex( if( zName==0 ){ goto exit_create_index; } + + /* Automatic index names generated from within sqlite3_declare_vtab() + ** must have names that are distinct from normal automatic index names. + ** The following statement converts "sqlite3_autoindex..." into + ** "sqlite3_butoindex..." in order to make the names distinct. + ** The "vtab_err.test" test demonstrates the need of this statement. */ + if( IN_DECLARE_VTAB ) zName[7]++; } /* Check for authorization to create an index. @@ -3091,7 +3107,7 @@ Index *sqlite3CreateIndex( pIndex->pTable = pTab; pIndex->onError = (u8)onError; pIndex->uniqNotNull = onError!=OE_None; - pIndex->idxType = pName ? SQLITE_IDXTYPE_APPDEF : SQLITE_IDXTYPE_UNIQUE; + pIndex->idxType = idxType; pIndex->pSchema = db->aDb[iDb].pSchema; pIndex->nKeyCol = pList->nExpr; if( pPIWhere ){ @@ -3271,7 +3287,7 @@ Index *sqlite3CreateIndex( pIdx->onError = pIndex->onError; } } - pRet = pIdx; + if( idxType==SQLITE_IDXTYPE_PRIMARYKEY ) pIdx->idxType = idxType; goto exit_create_index; } } @@ -3283,6 +3299,7 @@ Index *sqlite3CreateIndex( assert( pParse->nErr==0 ); if( db->init.busy ){ Index *p; + assert( !IN_DECLARE_VTAB ); assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, pIndex); @@ -3364,7 +3381,7 @@ Index *sqlite3CreateIndex( sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); - sqlite3VdbeAddOp1(v, OP_Expire, 0); + sqlite3VdbeAddOp0(v, OP_Expire); } sqlite3VdbeJumpHere(v, pIndex->tnum); @@ -3389,7 +3406,6 @@ Index *sqlite3CreateIndex( pIndex->pNext = pOther->pNext; pOther->pNext = pIndex; } - pRet = pIndex; pIndex = 0; } @@ -3400,7 +3416,6 @@ exit_create_index: sqlite3ExprListDelete(db, pList); sqlite3SrcListDelete(db, pTblName); sqlite3DbFree(db, zName); - return pRet; } /* @@ -3429,10 +3444,11 @@ void sqlite3DefaultRowEst(Index *pIdx){ int i; /* Set the first entry (number of rows in the index) to the estimated - ** number of rows in the table. Or 10, if the estimated number of rows - ** in the table is less than that. */ + ** 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( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) ); + if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) ); + if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) ); /* 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. */ @@ -4314,10 +4330,6 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ /* ** Return a KeyInfo structure that is appropriate for the given Index. ** -** The KeyInfo structure for an index is cached in the Index object. -** So there might be multiple references to the returned pointer. The -** caller should not try to modify the KeyInfo object. -** ** The caller should invoke sqlite3KeyInfoUnref() on the returned object ** when it has finished using it. */ diff --git a/src/ctime.c b/src/ctime.c index f1bb69c16a..09587ea88e 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -45,6 +45,15 @@ static const char * const azCompileOpt[] = { #if SQLITE_CHECK_PAGES "CHECK_PAGES", #endif +#if defined(__clang__) && defined(__clang_major__) + "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "." + CTIMEOPT_VAL(__clang_minor__) "." + CTIMEOPT_VAL(__clang_patchlevel__), +#elif defined(_MSC_VER) + "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER), +#elif defined(__GNUC__) && defined(__VERSION__) + "COMPILER=gcc-" __VERSION__, +#endif #if SQLITE_COVERAGE_TEST "COVERAGE_TEST", #endif @@ -64,7 +73,7 @@ static const char * const azCompileOpt[] = { "DISABLE_LFS", #endif #if SQLITE_ENABLE_8_3_NAMES - "ENABLE_8_3_NAMES", + "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES), #endif #if SQLITE_ENABLE_API_ARMOR "ENABLE_API_ARMOR", diff --git a/src/date.c b/src/date.c index cce16305a3..8b2f2fa717 100644 --- a/src/date.c +++ b/src/date.c @@ -1112,7 +1112,6 @@ static void currentTimeFunc( ){ time_t t; char *zFormat = (char *)sqlite3_user_data(context); - sqlite3 *db; sqlite3_int64 iT; struct tm *pTm; struct tm sNow; diff --git a/src/dbstat.c b/src/dbstat.c index b8e79b0864..92ce650df1 100644 --- a/src/dbstat.c +++ b/src/dbstat.c @@ -58,10 +58,10 @@ */ #define VTAB_SCHEMA \ "CREATE TABLE xx( " \ - " name STRING, /* Name of table or index */" \ - " path INTEGER, /* Path to page from root */" \ + " name TEXT, /* Name of table or index */" \ + " path TEXT, /* Path to page from root */" \ " pageno INTEGER, /* Page number */" \ - " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" \ + " pagetype TEXT, /* 'internal', 'leaf' or 'overflow' */" \ " ncell INTEGER, /* Cells on page (0 for overflow) */" \ " payload INTEGER, /* Bytes of payload on this page */" \ " unused INTEGER, /* Bytes of unused space on this page */" \ diff --git a/src/delete.c b/src/delete.c index e3af2d7a78..e9e669e21c 100644 --- a/src/delete.c +++ b/src/delete.c @@ -239,11 +239,12 @@ void sqlite3DeleteFrom( int addrBypass = 0; /* Address of jump over the delete logic */ int addrLoop = 0; /* Top of the delete loop */ int addrEphOpen = 0; /* Instruction to open the Ephemeral table */ + int bComplex; /* True if there are triggers or FKs or + ** subqueries in the WHERE clause */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ Trigger *pTrigger; /* List of table triggers, if required */ - int bComplex; /* True if there are either triggers or FKs */ #endif memset(&sContext, 0, sizeof(sContext)); @@ -271,7 +272,6 @@ void sqlite3DeleteFrom( #else # define pTrigger 0 # define isView 0 -# define bComplex 0 #endif #ifdef SQLITE_OMIT_VIEW # undef isView @@ -373,7 +373,8 @@ void sqlite3DeleteFrom( }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ { - u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; + u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE; + if( sNC.ncFlags & NC_VarSelect ) bComplex = 1; wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); if( HasRowid(pTab) ){ /* For a rowid table, initialize the RowSet to an empty set */ diff --git a/src/expr.c b/src/expr.c index 9e3b664e8d..74f09bbee9 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1818,6 +1818,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; if( prRhsHasNull && !pTab->aCol[iCol].notNull ){ +#ifdef SQLITE_ENABLE_COLUMN_USED_MASK + const i64 sOne = 1; + sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, + iTab, 0, 0, (u8*)&sOne, P4_INT64); +#endif *prRhsHasNull = ++pParse->nMem; sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull); } @@ -1830,7 +1835,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){ /* If no preexisting index is available for the IN clause ** and IN_INDEX_NOOP is an allowed reply ** and the RHS of the IN operator is a list, not a subquery - ** and the RHS is not contant or has two or fewer terms, + ** and the RHS is not constant or has two or fewer terms, ** then it is not worth creating an ephemeral table to evaluate ** the IN operator so return IN_INDEX_NOOP. */ @@ -2222,8 +2227,7 @@ static void sqlite3ExprCodeIN( if( eType==IN_INDEX_ROWID ){ /* In this case, the RHS is the ROWID of table b-tree */ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1); + sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, r1); VdbeCoverage(v); }else{ /* In this case, the RHS is an index b-tree. @@ -2536,7 +2540,7 @@ void sqlite3ExprCodeGetColumnOfTable( }else{ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; int x = iCol; - if( !HasRowid(pTab) ){ + if( !HasRowid(pTab) && !IsVirtual(pTab) ){ x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol); } sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut); @@ -2939,6 +2943,11 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); zId = pExpr->u.zToken; pDef = sqlite3FindFunction(db, zId, nFarg, enc, 0); +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + if( pDef==0 && pParse->explain ){ + pDef = sqlite3FindFunction(db, "unknown", nFarg, enc, 0); + } +#endif if( pDef==0 || pDef->xFinalize!=0 ){ sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); break; @@ -3961,6 +3970,61 @@ int sqlite3ExprImpliesExpr(Expr *pE1, Expr *pE2, int iTab){ return 0; } +/* +** An instance of the following structure is used by the tree walker +** to determine if an expression can be evaluated by reference to the +** index only, without having to do a search for the corresponding +** table entry. The IdxCover.pIdx field is the index. IdxCover.iCur +** is the cursor for the table. +*/ +struct IdxCover { + Index *pIdx; /* The index to be tested for coverage */ + int iCur; /* Cursor number for the table corresponding to the index */ +}; + +/* +** Check to see if there are references to columns in table +** pWalker->u.pIdxCover->iCur can be satisfied using the index +** pWalker->u.pIdxCover->pIdx. +*/ +static int exprIdxCover(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN + && pExpr->iTable==pWalker->u.pIdxCover->iCur + && sqlite3ColumnOfIndex(pWalker->u.pIdxCover->pIdx, pExpr->iColumn)<0 + ){ + pWalker->eCode = 1; + return WRC_Abort; + } + return WRC_Continue; +} + +/* +** Determine if an index pIdx on table with cursor iCur contains will +** the expression pExpr. Return true if the index does cover the +** expression and false if the pExpr expression references table columns +** that are not found in the index pIdx. +** +** An index covering an expression means that the expression can be +** evaluated using only the index and without having to lookup the +** corresponding table entry. +*/ +int sqlite3ExprCoveredByIndex( + Expr *pExpr, /* The index to be tested */ + int iCur, /* The cursor number for the corresponding table */ + Index *pIdx /* The index that might be used for coverage */ +){ + Walker w; + struct IdxCover xcov; + memset(&w, 0, sizeof(w)); + xcov.iCur = iCur; + xcov.pIdx = pIdx; + w.xExprCallback = exprIdxCover; + w.u.pIdxCover = &xcov; + sqlite3WalkExpr(&w, pExpr); + return !w.eCode; +} + + /* ** An instance of the following structure is used by the tree walker ** to count references to table columns in the arguments of an diff --git a/src/fkey.c b/src/fkey.c index fa19fbc1ce..25d9f84917 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -1372,7 +1372,8 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){ FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ - assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); + assert( db==0 || IsVirtual(pTab) + || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ /* Remove the FK from the fkeyHash hash table. */ diff --git a/src/func.c b/src/func.c index 4feedc7440..79bb1e3dd2 100644 --- a/src/func.c +++ b/src/func.c @@ -740,7 +740,7 @@ static int patternCompare( } c2 = Utf8Read(zString); if( c==c2 ) continue; - if( noCase && c<0x80 && c2<0x80 && sqlite3Tolower(c)==sqlite3Tolower(c2) ){ + if( noCase && sqlite3Tolower(c)==sqlite3Tolower(c2) && c<0x80 && c2<0x80 ){ continue; } if( c==matchOne && zPattern!=zEscaped && c2!=0 ) continue; @@ -1316,6 +1316,26 @@ static void trimFunc( } +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION +/* +** The "unknown" function is automatically substituted in place of +** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN +** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used. +** When the "sqlite3" command-line shell is built using this functionality, +** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries +** involving application-defined functions to be examined in a generic +** sqlite3 shell. +*/ +static void unknownFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + /* no-op */ +} +#endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/ + + /* IMP: R-25361-16150 This function is omitted from SQLite by default. It ** is only available if the SQLITE_SOUNDEX compile-time option is used ** when SQLite is built. @@ -1786,13 +1806,16 @@ void sqlite3RegisterBuiltinFunctions(void){ AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize), LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), - #ifdef SQLITE_CASE_SENSITIVE_LIKE +#ifdef SQLITE_CASE_SENSITIVE_LIKE LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), - #else +#else LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE), LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE), - #endif +#endif +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + FUNCTION(unknown, -1, 0, 0, unknownFunc ), +#endif FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), diff --git a/src/hash.h b/src/hash.h index 6dfa4e035c..90540eda50 100644 --- a/src/hash.h +++ b/src/hash.h @@ -12,8 +12,8 @@ ** This is the header file for the generic hash-table implementation ** used in SQLite. */ -#ifndef _SQLITE_HASH_H_ -#define _SQLITE_HASH_H_ +#ifndef SQLITE_HASH_H +#define SQLITE_HASH_H /* Forward declarations of structures. */ typedef struct Hash Hash; @@ -93,4 +93,4 @@ void sqlite3HashClear(Hash*); */ /* #define sqliteHashCount(H) ((H)->count) // NOT USED */ -#endif /* _SQLITE_HASH_H_ */ +#endif /* SQLITE_HASH_H */ diff --git a/src/hwtime.h b/src/hwtime.h index b8bc5a295b..5b209db8af 100644 --- a/src/hwtime.h +++ b/src/hwtime.h @@ -13,8 +13,8 @@ ** This file contains inline asm code for retrieving "high-performance" ** counters for x86 class CPUs. */ -#ifndef _HWTIME_H_ -#define _HWTIME_H_ +#ifndef SQLITE_HWTIME_H +#define SQLITE_HWTIME_H /* ** The following routine only works on pentium-class (or newer) processors. @@ -82,4 +82,4 @@ #endif -#endif /* !defined(_HWTIME_H_) */ +#endif /* !defined(SQLITE_HWTIME_H) */ diff --git a/src/loadext.c b/src/loadext.c index f881d99710..9feabbd045 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -21,7 +21,6 @@ #include #ifndef SQLITE_OMIT_LOAD_EXTENSION - /* ** Some API routines are omitted when various features are ** excluded from a build of SQLite. Substitute a NULL pointer @@ -91,7 +90,7 @@ # define sqlite3_enable_shared_cache 0 #endif -#ifdef SQLITE_OMIT_TRACE +#if defined(SQLITE_OMIT_TRACE) || defined(SQLITE_OMIT_DEPRECATED) # define sqlite3_profile 0 # define sqlite3_trace 0 #endif @@ -111,6 +110,10 @@ #define sqlite3_blob_reopen 0 #endif +#if defined(SQLITE_OMIT_TRACE) +# define sqlite3_trace_v2 0 +#endif + /* ** The following structure contains pointers to all SQLite API routines. ** A pointer to this structure is passed into extensions when they are @@ -416,7 +419,10 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_strlike, sqlite3_db_cacheflush, /* Version 3.12.0 and later */ - sqlite3_system_errno + sqlite3_system_errno, + /* Version 3.14.0 and later */ + sqlite3_trace_v2, + sqlite3_expanded_sql }; /* @@ -439,13 +445,14 @@ static int sqlite3LoadExtension( ){ sqlite3_vfs *pVfs = db->pVfs; void *handle; - int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); + sqlite3_loadext_entry xInit; char *zErrmsg = 0; const char *zEntry; char *zAltEntry = 0; void **aHandle; u64 nMsg = 300 + sqlite3Strlen30(zFile); int ii; + int rc; /* Shared library endings to try if zFile cannot be loaded as written */ static const char *azEndings[] = { @@ -497,8 +504,7 @@ static int sqlite3LoadExtension( } return SQLITE_ERROR; } - xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) - sqlite3OsDlSym(pVfs, handle, zEntry); + xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); /* If no entry point was specified and the default legacy ** entry point name "sqlite3_extension_init" was not found, then @@ -530,8 +536,7 @@ static int sqlite3LoadExtension( } memcpy(zAltEntry+iEntry, "_init", 6); zEntry = zAltEntry; - xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) - sqlite3OsDlSym(pVfs, handle, zEntry); + xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); } if( xInit==0 ){ if( pzErrMsg ){ @@ -548,7 +553,9 @@ static int sqlite3LoadExtension( return SQLITE_ERROR; } sqlite3_free(zAltEntry); - if( xInit(db, &zErrmsg, &sqlite3Apis) ){ + rc = xInit(db, &zErrmsg, &sqlite3Apis); + if( rc ){ + if( rc==SQLITE_OK_LOAD_PERMANENTLY ) return SQLITE_OK; if( pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg); } @@ -659,7 +666,9 @@ static SQLITE_WSD struct sqlite3AutoExtList { ** Register a statically linked extension that is automatically ** loaded by every new database connection. */ -int sqlite3_auto_extension(void (*xInit)(void)){ +int sqlite3_auto_extension( + void (*xInit)(void) +){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); @@ -704,7 +713,9 @@ int sqlite3_auto_extension(void (*xInit)(void)){ ** Return 1 if xInit was found on the list and removed. Return 0 if xInit ** was not on the list. */ -int sqlite3_cancel_auto_extension(void (*xInit)(void)){ +int sqlite3_cancel_auto_extension( + void (*xInit)(void) +){ #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -753,7 +764,7 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){ u32 i; int go = 1; int rc; - int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); + sqlite3_loadext_entry xInit; wsdAutoextInit; if( wsdAutoext.nExt==0 ){ @@ -770,8 +781,7 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){ xInit = 0; go = 0; }else{ - xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) - wsdAutoext.aExt[i]; + xInit = (sqlite3_loadext_entry)wsdAutoext.aExt[i]; } sqlite3_mutex_leave(mutex); zErrmsg = 0; diff --git a/src/main.c b/src/main.c index 30370f8cab..f31f865818 100644 --- a/src/main.c +++ b/src/main.c @@ -1033,6 +1033,9 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); + if( db->mTrace & SQLITE_TRACE_CLOSE ){ + db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); + } /* Force xDisconnect calls on all virtual tables */ disconnectAllVtab(db); @@ -1801,7 +1804,8 @@ int sqlite3_overload_function( ** trace is a pointer to a function that is invoked at the start of each ** SQL statement. */ -void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ +#ifndef SQLITE_OMIT_DEPRECATED +void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){ void *pOld; #ifdef SQLITE_ENABLE_API_ARMOR @@ -1812,11 +1816,36 @@ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ #endif sqlite3_mutex_enter(db->mutex); pOld = db->pTraceArg; - db->xTrace = xTrace; + db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0; + db->xTrace = (int(*)(u32,void*,void*,void*))xTrace; db->pTraceArg = pArg; sqlite3_mutex_leave(db->mutex); return pOld; } +#endif /* SQLITE_OMIT_DEPRECATED */ + +/* Register a trace callback using the version-2 interface. +*/ +int sqlite3_trace_v2( + sqlite3 *db, /* Trace this connection */ + unsigned mTrace, /* Mask of events to be traced */ + int(*xTrace)(unsigned,void*,void*,void*), /* Callback to invoke */ + void *pArg /* Context */ +){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlite3_mutex_enter(db->mutex); + db->mTrace = mTrace; + db->xTrace = xTrace; + db->pTraceArg = pArg; + sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; +} + +#ifndef SQLITE_OMIT_DEPRECATED /* ** Register a profile function. The pArg from the previously registered ** profile function is returned. @@ -1845,6 +1874,7 @@ void *sqlite3_profile( sqlite3_mutex_leave(db->mutex); return pOld; } +#endif /* SQLITE_OMIT_DEPRECATED */ #endif /* SQLITE_OMIT_TRACE */ /* diff --git a/src/msvc.h b/src/msvc.h index 01ebf2b46f..3914b05bcf 100644 --- a/src/msvc.h +++ b/src/msvc.h @@ -12,8 +12,8 @@ ** ** This file contains code that is specific to MSVC. */ -#ifndef _MSVC_H_ -#define _MSVC_H_ +#ifndef SQLITE_MSVC_H +#define SQLITE_MSVC_H #if defined(_MSC_VER) #pragma warning(disable : 4054) @@ -33,4 +33,4 @@ #pragma warning(disable : 4706) #endif /* defined(_MSC_VER) */ -#endif /* _MSVC_H_ */ +#endif /* SQLITE_MSVC_H */ diff --git a/src/os.c b/src/os.c index 6138bf4f3f..5cf0014794 100644 --- a/src/os.c +++ b/src/os.c @@ -13,9 +13,7 @@ ** This file contains OS interface code that is common to all ** architectures. */ -#define _SQLITE_OS_C_ 1 #include "sqliteInt.h" -#undef _SQLITE_OS_C_ /* ** If we compile with the SQLITE_TEST macro set, then the following block diff --git a/src/os_setup.h b/src/os_setup.h index 68de1446ed..08aaa1195a 100644 --- a/src/os_setup.h +++ b/src/os_setup.h @@ -13,8 +13,8 @@ ** This file contains pre-processor directives related to operating system ** detection and/or setup. */ -#ifndef _OS_SETUP_H_ -#define _OS_SETUP_H_ +#ifndef SQLITE_OS_SETUP_H +#define SQLITE_OS_SETUP_H /* ** Figure out if we are dealing with Unix, Windows, or some other operating @@ -54,4 +54,4 @@ # endif #endif -#endif /* _OS_SETUP_H_ */ +#endif /* SQLITE_OS_SETUP_H */ diff --git a/src/os_unix.c b/src/os_unix.c index 01de00e0dd..08d4cb5d11 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5412,18 +5412,22 @@ static const char *unixTempFileDir(void){ "/tmp", "." }; - unsigned int i; + unsigned int i = 0; struct stat buf; const char *zDir = sqlite3_temp_directory; if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); - for(i=0; i<=sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ - if( zDir==0 ) continue; - if( osStat(zDir, &buf) ) continue; - if( !S_ISDIR(buf.st_mode) ) continue; - if( osAccess(zDir, 03) ) continue; - return zDir; + while(1){ + if( zDir!=0 + && osStat(zDir, &buf)==0 + && S_ISDIR(buf.st_mode) + && osAccess(zDir, 03)==0 + ){ + return zDir; + } + if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break; + zDir = azDirs[i++]; } return 0; } @@ -5525,6 +5529,27 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ return pUnused; } +/* +** Find the mode, uid and gid of file zFile. +*/ +static int getFileMode( + const char *zFile, /* File name */ + mode_t *pMode, /* OUT: Permissions of zFile */ + uid_t *pUid, /* OUT: uid of zFile. */ + gid_t *pGid /* OUT: gid of zFile. */ +){ + struct stat sStat; /* Output of stat() on database file */ + int rc = SQLITE_OK; + if( 0==osStat(zFile, &sStat) ){ + *pMode = sStat.st_mode & 0777; + *pUid = sStat.st_uid; + *pGid = sStat.st_gid; + }else{ + rc = SQLITE_IOERR_FSTAT; + } + return rc; +} + /* ** This function is called by unixOpen() to determine the unix permissions ** to create new files with. If no error occurs, then SQLITE_OK is returned @@ -5560,7 +5585,6 @@ static int findCreateFileMode( if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ - struct stat sStat; /* Output of stat() on database file */ /* zPath is a path to a WAL or journal file. The following block derives ** the path to the associated database file from zPath. This block handles @@ -5591,15 +5615,18 @@ static int findCreateFileMode( memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; - if( 0==osStat(zDb, &sStat) ){ - *pMode = sStat.st_mode & 0777; - *pUid = sStat.st_uid; - *pGid = sStat.st_gid; - }else{ - rc = SQLITE_IOERR_FSTAT; - } + rc = getFileMode(zDb, pMode, pUid, pGid); }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ *pMode = 0600; + }else if( flags & SQLITE_OPEN_URI ){ + /* If this is a main database file and the file was opened using a URI + ** filename, check for the "modeof" parameter. If present, interpret + ** its value as a filename and try to copy the mode, uid and gid from + ** that file. */ + const char *z = sqlite3_uri_parameter(zPath, "modeof"); + if( z ){ + rc = getFileMode(z, pMode, pUid, pGid); + } } return rc; } diff --git a/src/os_win.c b/src/os_win.c index 9a34d9a106..6224088583 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -290,6 +290,17 @@ struct winFile { #endif }; +/* +** The winVfsAppData structure is used for the pAppData member for all of the +** Win32 VFS variants. +*/ +typedef struct winVfsAppData winVfsAppData; +struct winVfsAppData { + const sqlite3_io_methods *pMethod; /* The file I/O methods to use. */ + void *pAppData; /* The extra pAppData, if any. */ + BOOL bNoLock; /* Non-zero if locking is disabled. */ +}; + /* ** Allowed values for winFile.ctrlFlags */ @@ -2611,7 +2622,12 @@ static int winClose(sqlite3_file *id){ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) ); #if SQLITE_OS_WINCE #define WINCE_DELETION_ATTEMPTS 3 - winceDestroyLock(pFile); + { + winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData; + if( pAppData==NULL || !pAppData->bNoLock ){ + winceDestroyLock(pFile); + } + } if( pFile->zDeleteOnClose ){ int cnt = 0; while( @@ -3343,6 +3359,44 @@ static int winUnlock(sqlite3_file *id, int locktype){ return rc; } +/****************************************************************************** +****************************** No-op Locking ********************************** +** +** Of the various locking implementations available, this is by far the +** simplest: locking is ignored. No attempt is made to lock the database +** file for reading or writing. +** +** This locking mode is appropriate for use on read-only databases +** (ex: databases that are burned into CD-ROM, for example.) It can +** also be used if the application employs some external mechanism to +** prevent simultaneous access of the same database by two or more +** database connections. But there is a serious risk of database +** corruption if this locking mode is used in situations where multiple +** database connections are accessing the same database file at the same +** time and one or more of those connections are writing. +*/ + +static int winNolockLock(sqlite3_file *id, int locktype){ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(locktype); + return SQLITE_OK; +} + +static int winNolockCheckReservedLock(sqlite3_file *id, int *pResOut){ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(pResOut); + return SQLITE_OK; +} + +static int winNolockUnlock(sqlite3_file *id, int locktype){ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(locktype); + return SQLITE_OK; +} + +/******************* End of the no-op lock implementation ********************* +******************************************************************************/ + /* ** If *pArg is initially negative then this is a query. Set *pArg to ** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. @@ -3621,12 +3675,12 @@ struct winShm { /* ** Apply advisory locks for all n bytes beginning at ofst. */ -#define _SHM_UNLCK 1 -#define _SHM_RDLCK 2 -#define _SHM_WRLCK 3 +#define WINSHM_UNLCK 1 +#define WINSHM_RDLCK 2 +#define WINSHM_WRLCK 3 static int winShmSystemLock( winShmNode *pFile, /* Apply locks to this open shared-memory segment */ - int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */ + int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ int ofst, /* Offset to first byte to be locked/unlocked */ int nByte /* Number of bytes to lock or unlock */ ){ @@ -3639,12 +3693,12 @@ static int winShmSystemLock( pFile->hFile.h, lockType, ofst, nByte)); /* Release/Acquire the system-level lock */ - if( lockType==_SHM_UNLCK ){ + if( lockType==WINSHM_UNLCK ){ rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); }else{ /* Initialize the locking parameters */ DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; - if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); } @@ -3656,7 +3710,7 @@ static int winShmSystemLock( } OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", - pFile->hFile.h, (lockType == _SHM_UNLCK) ? "winUnlockFile" : + pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); return rc; @@ -3784,7 +3838,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ /* Check to see if another process is holding the dead-man switch. ** If not, truncate the file to zero length. */ - if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ + if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); if( rc!=SQLITE_OK ){ rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), @@ -3792,8 +3846,8 @@ static int winOpenSharedMemory(winFile *pDbFd){ } } if( rc==SQLITE_OK ){ - winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); - rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1); + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); + rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); } if( rc ) goto shm_open_err; } @@ -3822,7 +3876,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ /* Jump here on any error */ shm_open_err: - winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ sqlite3_free(p); sqlite3_free(pNew); @@ -3911,7 +3965,7 @@ static int winShmLock( /* Unlock the system-level locks */ if( (mask & allMask)==0 ){ - rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n); + rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n); }else{ rc = SQLITE_OK; } @@ -3939,7 +3993,7 @@ static int winShmLock( /* Get shared locks at the system level, if necessary */ if( rc==SQLITE_OK ){ if( (allShared & mask)==0 ){ - rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n); + rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); }else{ rc = SQLITE_OK; } @@ -3964,7 +4018,7 @@ static int winShmLock( ** also mark the local connection as being locked. */ if( rc==SQLITE_OK ){ - rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n); + rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; @@ -4407,6 +4461,44 @@ static const sqlite3_io_methods winIoMethod = { winUnfetch /* xUnfetch */ }; +/* +** This vector defines all the methods that can operate on an +** sqlite3_file for win32 without performing any locking. +*/ +static const sqlite3_io_methods winIoNolockMethod = { + 3, /* iVersion */ + winClose, /* xClose */ + winRead, /* xRead */ + winWrite, /* xWrite */ + winTruncate, /* xTruncate */ + winSync, /* xSync */ + winFileSize, /* xFileSize */ + winNolockLock, /* xLock */ + winNolockUnlock, /* xUnlock */ + winNolockCheckReservedLock, /* xCheckReservedLock */ + winFileControl, /* xFileControl */ + winSectorSize, /* xSectorSize */ + winDeviceCharacteristics, /* xDeviceCharacteristics */ + winShmMap, /* xShmMap */ + winShmLock, /* xShmLock */ + winShmBarrier, /* xShmBarrier */ + winShmUnmap, /* xShmUnmap */ + winFetch, /* xFetch */ + winUnfetch /* xUnfetch */ +}; + +static winVfsAppData winAppData = { + &winIoMethod, /* pMethod */ + 0, /* pAppData */ + 0 /* bNoLock */ +}; + +static winVfsAppData winNolockAppData = { + &winIoNolockMethod, /* pMethod */ + 0, /* pAppData */ + 1 /* bNoLock */ +}; + /**************************************************************************** **************************** sqlite3_vfs methods **************************** ** @@ -4739,7 +4831,7 @@ static int winIsDir(const void *zConverted){ ** Open a file. */ static int winOpen( - sqlite3_vfs *pVfs, /* Used to get maximum path name length */ + sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */ const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ @@ -4754,6 +4846,7 @@ static int winOpen( #if SQLITE_OS_WINCE int isTemp = 0; #endif + winVfsAppData *pAppData; winFile *pFile = (winFile*)id; void *zConverted; /* Filename in OS encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ @@ -4975,15 +5068,20 @@ static int winOpen( "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ? *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); + pAppData = (winVfsAppData*)pVfs->pAppData; + #if SQLITE_OS_WINCE - if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB - && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK - ){ - osCloseHandle(h); - sqlite3_free(zConverted); - sqlite3_free(zTmpname); - OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc))); - return rc; + { + if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB + && ((pAppData==NULL) || !pAppData->bNoLock) + && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK + ){ + osCloseHandle(h); + sqlite3_free(zConverted); + sqlite3_free(zTmpname); + OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc))); + return rc; + } } if( isTemp ){ pFile->zDeleteOnClose = zConverted; @@ -4994,7 +5092,7 @@ static int winOpen( } sqlite3_free(zTmpname); - pFile->pMethod = &winIoMethod; + pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod; pFile->pVfs = pVfs; pFile->h = h; if( isReadonly ){ @@ -5269,6 +5367,18 @@ static int winFullPathname( int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */ ){ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) + DWORD nByte; + void *zConverted; + char *zOut; +#endif + + /* If this path name begins with "/X:", where "X" is any alphabetic + ** character, discard the initial "/" from the pathname. + */ + if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ + zRelative++; + } #if defined(__CYGWIN__) SimulateIOError( return SQLITE_ERROR ); @@ -5347,17 +5457,6 @@ static int winFullPathname( #endif #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) - DWORD nByte; - void *zConverted; - char *zOut; - - /* If this path name begins with "/X:", where "X" is any alphabetic - ** character, discard the initial "/" from the pathname. - */ - if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ - zRelative++; - } - /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. This function could fail if, for example, the @@ -5716,53 +5815,103 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ - 0, /* pNext */ - "win32", /* zName */ - 0, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ + 0, /* pNext */ + "win32", /* zName */ + &winAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ }; #if defined(SQLITE_WIN32_HAS_WIDE) static sqlite3_vfs winLongPathVfs = { - 3, /* iVersion */ - sizeof(winFile), /* szOsFile */ + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ - 0, /* pNext */ - "win32-longpath", /* zName */ - 0, /* pAppData */ - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ - winCurrentTimeInt64, /* xCurrentTimeInt64 */ - winSetSystemCall, /* xSetSystemCall */ - winGetSystemCall, /* xGetSystemCall */ - winNextSystemCall, /* xNextSystemCall */ + 0, /* pNext */ + "win32-longpath", /* zName */ + &winAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; +#endif + static sqlite3_vfs winNolockVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32-none", /* zName */ + &winNolockAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; +#if defined(SQLITE_WIN32_HAS_WIDE) + static sqlite3_vfs winLongPathNolockVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32-longpath-none", /* zName */ + &winNolockAppData, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ }; #endif @@ -5786,6 +5935,12 @@ int sqlite3_os_init(void){ sqlite3_vfs_register(&winLongPathVfs, 0); #endif + sqlite3_vfs_register(&winNolockVfs, 0); + +#if defined(SQLITE_WIN32_HAS_WIDE) + sqlite3_vfs_register(&winLongPathNolockVfs, 0); +#endif + return SQLITE_OK; } diff --git a/src/os_win.h b/src/os_win.h index 17d6a2bef4..27714ed079 100644 --- a/src/os_win.h +++ b/src/os_win.h @@ -12,8 +12,8 @@ ** ** This file contains code that is specific to Windows. */ -#ifndef _OS_WIN_H_ -#define _OS_WIN_H_ +#ifndef SQLITE_OS_WIN_H +#define SQLITE_OS_WIN_H /* ** Include the primary Windows SDK header file. @@ -85,4 +85,4 @@ # define SQLITE_OS_WIN_THREADS 0 #endif -#endif /* _OS_WIN_H_ */ +#endif /* SQLITE_OS_WIN_H */ diff --git a/src/pager.c b/src/pager.c index 36a936a9c3..240bbb5f63 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1926,20 +1926,24 @@ static int pager_error(Pager *pPager, int rc){ static int pager_truncate(Pager *pPager, Pgno nPage); /* -** The write transaction open on the pager passed as the only argument is -** being committed. This function returns true if all dirty pages should -** be flushed to disk, or false otherwise. Pages should be flushed to disk -** unless one of the following is true: +** The write transaction open on pPager is being committed (bCommit==1) +** or rolled back (bCommit==0). ** -** * The db is an in-memory database. +** Return TRUE if and only if all dirty pages should be flushed to disk. ** -** * The db is a temporary database and the db file has not been opened. +** Rules: ** -** * The db is a temporary database and the cache contains less than -** C/4 dirty pages, where C is the configured cache-size. +** * For non-TEMP databases, always sync to disk. This is necessary +** for transactions to be durable. +** +** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing +** file has been created already (via a spill on pagerStress()) and +** when the number of dirty pages in memory exceeds 25% of the total +** cache size. */ -static int pagerFlushOnCommit(Pager *pPager){ +static int pagerFlushOnCommit(Pager *pPager, int bCommit){ if( pPager->tempFile==0 ) return 1; + if( !bCommit ) return 0; if( !isOpen(pPager->fd) ) return 0; return (sqlite3PCachePercentDirty(pPager->pPCache)>=25); } @@ -2047,7 +2051,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); + rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile); pPager->journalOff = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if @@ -2081,12 +2085,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ pagerFreeBitvecs(pPager); pPager->nRec = 0; - if( MEMDB || pagerFlushOnCommit(pPager) ){ - sqlite3PcacheCleanAll(pPager->pPCache); - }else{ - sqlite3PcacheClearWritable(pPager->pPCache); + if( rc==SQLITE_OK ){ + if( pagerFlushOnCommit(pPager, bCommit) ){ + sqlite3PcacheCleanAll(pPager->pPCache); + }else{ + sqlite3PcacheClearWritable(pPager->pPCache); + } + sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); } - sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); if( pagerUseWal(pPager) ){ /* Drop the WAL write-lock, if any. Also, if the connection was in @@ -2420,7 +2426,6 @@ static int pager_playback_one_page( assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 ); pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK; if( rc!=SQLITE_OK ) return rc; - pPg->flags &= ~PGHDR_NEED_READ; sqlite3PcacheMakeDirty(pPg); } if( pPg ){ @@ -2434,33 +2439,10 @@ static int pager_playback_one_page( pData = pPg->pData; memcpy(pData, (u8*)aData, pPager->pageSize); pPager->xReiniter(pPg); - if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ - /* If the contents of this page were just restored from the main - ** journal file, then its content must be as they were when the - ** transaction was first opened. In this case we can mark the page - ** as clean, since there will be no need to write it out to the - ** database. - ** - ** There is one exception to this rule. If the page is being rolled - ** back as part of a savepoint (or statement) rollback from an - ** unsynced portion of the main journal file, then it is not safe - ** to mark the page as clean. This is because marking the page as - ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is - ** already in the journal file (recorded in Pager.pInJournal) and - ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to - ** again within this transaction, it will be marked as dirty but - ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially - ** be written out into the database file before its journal file - ** segment is synced. If a crash occurs during or following this, - ** database corruption may ensue. - ** - ** Update: Another exception is for temp files that are not - ** in-memory databases. In this case the page may have been dirty - ** at the start of the transaction. - */ - assert( !pagerUseWal(pPager) ); - if( pPager->tempFile==0 ) sqlite3PcacheMakeClean(pPg); - } + /* It used to be that sqlite3PcacheMakeClean(pPg) was called here. But + ** that call was dangerous and had no detectable benefit since the cache + ** is normally cleaned by sqlite3PcacheCleanAll() after rollback and so + ** has been removed. */ pager_set_pagehash(pPg); /* If this was page 1, then restore the value of Pager.dbFileVers. @@ -6086,14 +6068,21 @@ int sqlite3PagerIswriteable(DbPage *pPg){ ** ** Tests show that this optimization can quadruple the speed of large ** DELETE operations. +** +** This optimization cannot be used with a temp-file, as the page may +** have been dirty at the start of the transaction. In that case, if +** memory pressure forces page pPg out of the cache, the data does need +** to be written out to disk so that it may be read back in if the +** current transaction is rolled back. */ void sqlite3PagerDontWrite(PgHdr *pPg){ Pager *pPager = pPg->pPager; - if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ + if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) pPg->flags |= PGHDR_DONT_WRITE; pPg->flags &= ~PGHDR_WRITEABLE; + testcase( pPg->flags & PGHDR_NEED_SYNC ); pager_set_pagehash(pPg); } } @@ -6363,7 +6352,7 @@ int sqlite3PagerCommitPhaseOne( if( NEVER(pPager->errCode) ) return pPager->errCode; /* Provide the ability to easily simulate an I/O error during testing */ - if( (rc = sqlite3FaultSim(400))!=SQLITE_OK ) return rc; + if( sqlite3FaultSim(400) ) return SQLITE_IOERR; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); @@ -6373,7 +6362,7 @@ int sqlite3PagerCommitPhaseOne( assert( MEMDB==0 || pPager->tempFile ); assert( isOpen(pPager->fd) || pPager->tempFile ); - if( 0==pagerFlushOnCommit(pPager) ){ + if( 0==pagerFlushOnCommit(pPager, 1) ){ /* If this is an in-memory db, or no pages have been written to, or this ** function has already been called, it is mostly a no-op. However, any ** backup in progress needs to be restarted. */ @@ -6998,6 +6987,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ /* In order to be able to rollback, an in-memory database must journal ** the page we are moving from. */ + assert( pPager->tempFile || !MEMDB ); if( pPager->tempFile ){ rc = sqlite3PagerWrite(pPg); if( rc ) return rc; @@ -7072,8 +7062,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ ** to exist, in case the transaction needs to roll back. Use pPgOld ** as the original page since it has already been allocated. */ - if( pPager->tempFile ){ - assert( pPgOld ); + if( pPager->tempFile && pPgOld ){ sqlite3PcacheMove(pPgOld, origPgno); sqlite3PagerUnrefNotNull(pPgOld); } @@ -7330,6 +7319,7 @@ void sqlite3PagerClearCache(Pager *pPager){ } #endif + #ifndef SQLITE_OMIT_WAL /* ** This function is called when the user invokes "PRAGMA wal_checkpoint", @@ -7555,5 +7545,4 @@ int sqlite3PagerWalFramesize(Pager *pPager){ } #endif - #endif /* SQLITE_OMIT_DISKIO */ diff --git a/src/pager.h b/src/pager.h index 3fc191239d..03f1bd5c84 100644 --- a/src/pager.h +++ b/src/pager.h @@ -14,8 +14,8 @@ ** at a time and provides a journal for rollback. */ -#ifndef _PAGER_H_ -#define _PAGER_H_ +#ifndef SQLITE_PAGER_H +#define SQLITE_PAGER_H /* ** Default maximum size for persistent journal files. A negative @@ -204,7 +204,7 @@ const char *sqlite3PagerJournalname(Pager*); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); void sqlite3PagerCacheStat(Pager *, int, int, int *); -void sqlite3PagerClearCache(Pager *); +void sqlite3PagerClearCache(Pager*); int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ @@ -243,4 +243,4 @@ void *sqlite3PagerCodec(DbPage *); # define enable_simulated_io_errors() #endif -#endif /* _PAGER_H_ */ +#endif /* SQLITE_PAGER_H */ diff --git a/src/parse.y b/src/parse.y index 4d88434964..afa37c84a8 100644 --- a/src/parse.y +++ b/src/parse.y @@ -316,7 +316,8 @@ ccons ::= NULL onconf. ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);} ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I). {sqlite3AddPrimaryKey(pParse,0,R,I,Z);} -ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0,0,0);} +ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0,0,0, + SQLITE_IDXTYPE_UNIQUE);} ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse,X.pExpr);} ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R). {sqlite3CreateForeignKey(pParse,0,&T,TA,R);} @@ -365,7 +366,8 @@ tcons ::= CONSTRAINT nm(X). {pParse->constraintName = X;} tcons ::= PRIMARY KEY LP sortlist(X) autoinc(I) RP onconf(R). {sqlite3AddPrimaryKey(pParse,X,R,I,0);} tcons ::= UNIQUE LP sortlist(X) RP onconf(R). - {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0);} + {sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0, + SQLITE_IDXTYPE_UNIQUE);} tcons ::= CHECK LP expr(E) RP onconf. {sqlite3AddCheckConstraint(pParse,E.pExpr);} tcons ::= FOREIGN KEY LP eidlist(FA) RP @@ -1148,9 +1150,10 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { exprNot(pParse, N, &A); A.zEnd = &E.z[E.n]; } - expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z). [IN] { + expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z) paren_exprlist(E). [IN] { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); + if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0); sqlite3PExprAddSelect(pParse, A.pExpr, pSelect); exprNot(pParse, N, &A); @@ -1207,6 +1210,15 @@ nexprlist(A) ::= nexprlist(A) COMMA expr(Y). nexprlist(A) ::= expr(Y). {A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/} +%ifndef SQLITE_OMIT_SUBQUERY +/* A paren_exprlist is an optional expression list contained inside +** of parenthesis */ +%type paren_exprlist {ExprList*} +%destructor paren_exprlist {sqlite3ExprListDelete(pParse->db, $$);} +paren_exprlist(A) ::= . {A = 0;} +paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;} +%endif SQLITE_OMIT_SUBQUERY + ///////////////////////////// The CREATE INDEX command /////////////////////// // @@ -1214,7 +1226,7 @@ cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D) ON nm(Y) LP sortlist(Z) RP where_opt(W). { sqlite3CreateIndex(pParse, &X, &D, sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U, - &S, W, SQLITE_SO_ASC, NE); + &S, W, SQLITE_SO_ASC, NE, SQLITE_IDXTYPE_APPDEF); } %type uniqueflag {int} diff --git a/src/pcache.c b/src/pcache.c index f700c2ff66..ccbcc96cd0 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -14,7 +14,29 @@ #include "sqliteInt.h" /* -** A complete page cache is an instance of this structure. +** A complete page cache is an instance of this structure. Every +** entry in the cache holds a single page of the database file. The +** btree layer only operates on the cached copy of the database pages. +** +** A page cache entry is "clean" if it exactly matches what is currently +** on disk. A page is "dirty" if it has been modified and needs to be +** persisted to disk. +** +** pDirty, pDirtyTail, pSynced: +** All dirty pages are linked into the doubly linked list using +** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order +** such that p was added to the list more recently than p->pDirtyNext. +** PCache.pDirty points to the first (newest) element in the list and +** pDirtyTail to the last (oldest). +** +** The PCache.pSynced variable is used to optimize searching for a dirty +** page to eject from the cache mid-transaction. It is better to eject +** a page that does not require a journal sync than one that does. +** Therefore, pSynced is maintained to that it *almost* always points +** to either the oldest page in the pDirty/pDirtyTail list that has a +** clear PGHDR_NEED_SYNC flag or to a page that is older than this one +** (so that the right page to eject can be found by following pDirtyPrev +** pointers). */ struct PCache { PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ @@ -31,6 +53,95 @@ struct PCache { sqlite3_pcache *pCache; /* Pluggable cache module */ }; +/********************************** Test and Debug Logic **********************/ +/* +** Debug tracing macros. Enable by by changing the "0" to "1" and +** recompiling. +** +** When sqlite3PcacheTrace is 1, single line trace messages are issued. +** When sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries +** is displayed for many operations, resulting in a lot of output. +*/ +#if defined(SQLITE_DEBUG) && 0 + int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */ + int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */ +# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;} + void pcacheDump(PCache *pCache){ + int N; + int i, j; + sqlite3_pcache_page *pLower; + PgHdr *pPg; + unsigned char *a; + + if( sqlite3PcacheTrace<2 ) return; + if( pCache->pCache==0 ) return; + N = sqlite3PcachePagecount(pCache); + if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; + for(i=1; i<=N; i++){ + pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); + if( pLower==0 ) continue; + pPg = (PgHdr*)pLower->pExtra; + printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); + a = (unsigned char *)pLower->pBuf; + for(j=0; j<12; j++) printf("%02x", a[j]); + printf("\n"); + if( pPg->pPage==0 ){ + sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); + } + } + } + #else +# define pcacheTrace(X) +# define pcacheDump(X) +#endif + +/* +** Check invariants on a PgHdr entry. Return true if everything is OK. +** Return false if any invariant is violated. +** +** This routine is for use inside of assert() statements only. For +** example: +** +** assert( sqlite3PcachePageSanity(pPg) ); +*/ +#if SQLITE_DEBUG +int sqlite3PcachePageSanity(PgHdr *pPg){ + PCache *pCache; + assert( pPg!=0 ); + assert( pPg->pgno>0 ); /* Page number is 1 or more */ + pCache = pPg->pCache; + assert( pCache!=0 ); /* Every page has an associated PCache */ + if( pPg->flags & PGHDR_CLEAN ){ + assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */ + assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */ + assert( pCache->pDirtyTail!=pPg ); + } + /* WRITEABLE pages must also be DIRTY */ + if( pPg->flags & PGHDR_WRITEABLE ){ + assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */ + } + /* NEED_SYNC can be set independently of WRITEABLE. This can happen, + ** for example, when using the sqlite3PagerDontWrite() optimization: + ** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK. + ** (2) Page X moved to freelist, WRITEABLE is cleared + ** (3) Page X reused, WRITEABLE is set again + ** If NEED_SYNC had been cleared in step 2, then it would not be reset + ** in step 3, and page might be written into the database without first + ** syncing the rollback journal, which might cause corruption on a power + ** loss. + ** + ** Another example is when the database page size is smaller than the + ** disk sector size. When any page of a sector is journalled, all pages + ** in that sector are marked NEED_SYNC even if they are still CLEAN, just + ** in case they are later modified, since all pages in the same sector + ** must be journalled and synced before any of those pages can be safely + ** written. + */ + return 1; +} +#endif /* SQLITE_DEBUG */ + + /********************************** Linked List Management ********************/ /* Allowed values for second argument to pcacheManageDirtyList() */ @@ -47,17 +158,16 @@ struct PCache { static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ PCache *p = pPage->pCache; + pcacheTrace(("%p.DIRTYLIST.%s %d\n", p, + addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT", + pPage->pgno)); if( addRemove & PCACHE_DIRTYLIST_REMOVE ){ assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); assert( pPage->pDirtyPrev || pPage==p->pDirty ); /* Update the PCache1.pSynced variable if necessary. */ if( p->pSynced==pPage ){ - PgHdr *pSynced = pPage->pDirtyPrev; - while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ - pSynced = pSynced->pDirtyPrev; - } - p->pSynced = pSynced; + p->pSynced = pPage->pDirtyPrev; } if( pPage->pDirtyNext ){ @@ -69,10 +179,15 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ if( pPage->pDirtyPrev ){ pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; }else{ + /* If there are now no dirty pages in the cache, set eCreate to 2. + ** This is an optimization that allows sqlite3PcacheFetch() to skip + ** searching for a dirty page to eject from the cache when it might + ** otherwise have to. */ assert( pPage==p->pDirty ); p->pDirty = pPage->pDirtyNext; - if( p->pDirty==0 && p->bPurgeable ){ - assert( p->eCreate==1 ); + assert( p->bPurgeable || p->eCreate==2 ); + if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/ + assert( p->bPurgeable==0 || p->eCreate==1 ); p->eCreate = 2; } } @@ -94,10 +209,19 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ } } p->pDirty = pPage; - if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ + + /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set + ** pSynced to point to it. Checking the NEED_SYNC flag is an + ** optimization, as if pSynced points to a page with the NEED_SYNC + ** flag set sqlite3PcacheFetchStress() searches through all newer + ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */ + if( !p->pSynced + && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/ + ){ p->pSynced = pPage; } } + pcacheDump(p); } /* @@ -106,7 +230,9 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ */ static void pcacheUnpin(PgHdr *p){ if( p->pCache->bPurgeable ){ + pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno)); sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0); + pcacheDump(p->pCache); } } @@ -176,6 +302,7 @@ int sqlite3PcacheOpen( p->pStress = pStress; p->szCache = 100; p->szSpill = 1; + pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable)); return sqlite3PcacheSetPageSize(p, szPage); } @@ -198,6 +325,7 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ } pCache->pCache = pNew; pCache->szPage = szPage; + pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage)); } return SQLITE_OK; } @@ -232,11 +360,13 @@ sqlite3_pcache_page *sqlite3PcacheFetch( int createFlag /* If true, create page if it does not exist already */ ){ int eCreate; + sqlite3_pcache_page *pRes; assert( pCache!=0 ); assert( pCache->pCache!=0 ); assert( createFlag==3 || createFlag==0 ); assert( pgno>0 ); + assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) ); /* eCreate defines what to do if the page does not exist. ** 0 Do not allocate a new page. (createFlag==0) @@ -249,7 +379,10 @@ sqlite3_pcache_page *sqlite3PcacheFetch( assert( eCreate==0 || eCreate==1 || eCreate==2 ); assert( createFlag==0 || pCache->eCreate==eCreate ); assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); - return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); + pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); + pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno, + createFlag?" create":"",pRes)); + return pRes; } /* @@ -276,7 +409,11 @@ int sqlite3PcacheFetchStress( ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC ** cleared), but if that is not possible settle for any other ** unreferenced dirty page. - */ + ** + ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC + ** flag is currently referenced, then the following may leave pSynced + ** set incorrectly (pointing to other than the LRU page with NEED_SYNC + ** cleared). This is Ok, as pSynced is just an optimization. */ for(pPg=pCache->pSynced; pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); pPg=pPg->pDirtyPrev @@ -294,7 +431,9 @@ int sqlite3PcacheFetchStress( sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), numberOfCachePages(pCache)); #endif + pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno)); rc = pCache->xStress(pCache->pStress, pPg); + pcacheDump(pCache); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ return rc; } @@ -354,6 +493,7 @@ PgHdr *sqlite3PcacheFetchFinish( } pCache->nRefSum++; pPgHdr->nRef++; + assert( sqlite3PcachePageSanity(pPgHdr) ); return pPgHdr; } @@ -367,8 +507,11 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ if( (--p->nRef)==0 ){ if( p->flags&PGHDR_CLEAN ){ pcacheUnpin(p); - }else if( p->pDirtyPrev!=0 ){ - /* Move the page to the head of the dirty list. */ + }else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/ + /* Move the page to the head of the dirty list. If p->pDirtyPrev==0, + ** then page p is already at the head of the dirty list and the + ** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE + ** tag above. */ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); } } @@ -379,6 +522,7 @@ void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ */ void sqlite3PcacheRef(PgHdr *p){ assert(p->nRef>0); + assert( sqlite3PcachePageSanity(p) ); p->nRef++; p->pCache->nRefSum++; } @@ -390,6 +534,7 @@ void sqlite3PcacheRef(PgHdr *p){ */ void sqlite3PcacheDrop(PgHdr *p){ assert( p->nRef==1 ); + assert( sqlite3PcachePageSanity(p) ); if( p->flags&PGHDR_DIRTY ){ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); } @@ -403,13 +548,16 @@ void sqlite3PcacheDrop(PgHdr *p){ */ void sqlite3PcacheMakeDirty(PgHdr *p){ assert( p->nRef>0 ); - if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ + assert( sqlite3PcachePageSanity(p) ); + if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/ p->flags &= ~PGHDR_DONT_WRITE; if( p->flags & PGHDR_CLEAN ){ p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN); + pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno)); assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); } + assert( sqlite3PcachePageSanity(p) ); } } @@ -418,11 +566,14 @@ void sqlite3PcacheMakeDirty(PgHdr *p){ ** make it so. */ void sqlite3PcacheMakeClean(PgHdr *p){ - if( (p->flags & PGHDR_DIRTY) ){ + assert( sqlite3PcachePageSanity(p) ); + if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){ assert( (p->flags & PGHDR_CLEAN)==0 ); pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE); p->flags |= PGHDR_CLEAN; + pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno)); + assert( sqlite3PcachePageSanity(p) ); if( p->nRef==0 ){ pcacheUnpin(p); } @@ -434,6 +585,7 @@ void sqlite3PcacheMakeClean(PgHdr *p){ */ void sqlite3PcacheCleanAll(PCache *pCache){ PgHdr *p; + pcacheTrace(("%p.CLEAN-ALL\n",pCache)); while( (p = pCache->pDirty)!=0 ){ sqlite3PcacheMakeClean(p); } @@ -444,6 +596,7 @@ void sqlite3PcacheCleanAll(PCache *pCache){ */ void sqlite3PcacheClearWritable(PCache *pCache){ PgHdr *p; + pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache)); for(p=pCache->pDirty; p; p=p->pDirtyNext){ p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE); } @@ -468,6 +621,8 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ PCache *pCache = p->pCache; assert( p->nRef>0 ); assert( newPgno>0 ); + assert( sqlite3PcachePageSanity(p) ); + pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno)); sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); p->pgno = newPgno; if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ @@ -488,6 +643,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ if( pCache->pCache ){ PgHdr *p; PgHdr *pNext; + pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno)); for(p=pCache->pDirty; p; p=pNext){ pNext = p->pDirtyNext; /* This routine never gets call with a positive pgno except right @@ -518,6 +674,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ */ void sqlite3PcacheClose(PCache *pCache){ assert( pCache->pCache!=0 ); + pcacheTrace(("%p.CLOSE\n",pCache)); sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); } @@ -530,29 +687,31 @@ void sqlite3PcacheClear(PCache *pCache){ /* ** Merge two lists of pages connected by pDirty and in pgno order. -** Do not both fixing the pDirtyPrev pointers. +** Do not bother fixing the pDirtyPrev pointers. */ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ PgHdr result, *pTail; pTail = &result; - while( pA && pB ){ + assert( pA!=0 && pB!=0 ); + for(;;){ if( pA->pgnopgno ){ pTail->pDirty = pA; pTail = pA; pA = pA->pDirty; + if( pA==0 ){ + pTail->pDirty = pB; + break; + } }else{ pTail->pDirty = pB; pTail = pB; pB = pB->pDirty; + if( pB==0 ){ + pTail->pDirty = pA; + break; + } } } - if( pA ){ - pTail->pDirty = pA; - }else if( pB ){ - pTail->pDirty = pB; - }else{ - pTail->pDirty = 0; - } return result.pDirty; } @@ -593,7 +752,8 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ } p = a[0]; for(i=1; inPage is correct */ - unsigned int h; + TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */ + unsigned int h, iStop; assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); - for(h=0; hnHash; h++){ - PgHdr1 **pp = &pCache->apHash[h]; + assert( pCache->iMaxKey >= iLimit ); + assert( pCache->nHash > 0 ); + if( pCache->iMaxKey - iLimit < pCache->nHash ){ + /* If we are just shaving the last few pages off the end of the + ** cache, then there is no point in scanning the entire hash table. + ** Only scan those hash slots that might contain pages that need to + ** be removed. */ + h = iLimit % pCache->nHash; + iStop = pCache->iMaxKey % pCache->nHash; + TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */ + }else{ + /* This is the general case where many pages are being removed. + ** It is necessary to scan the entire hash table */ + h = pCache->nHash/2; + iStop = h - 1; + } + for(;;){ + PgHdr1 **pp; PgHdr1 *pPage; + assert( hnHash ); + pp = &pCache->apHash[h]; while( (pPage = *pp)!=0 ){ if( pPage->iKey>=iLimit ){ pCache->nPage--; @@ -646,11 +664,13 @@ static void pcache1TruncateUnsafe( pcache1FreePage(pPage); }else{ pp = &pPage->pNext; - TESTONLY( nPage++; ) + TESTONLY( if( nPage>=0 ) nPage++; ) } } + if( h==iStop ) break; + h = (h+1) % pCache->nHash; } - assert( pCache->nPage==nPage ); + assert( nPage<0 || pCache->nPage==(unsigned)nPage ); } /******************************************************************************/ @@ -1127,7 +1147,7 @@ static void pcache1Destroy(sqlite3_pcache *p){ PGroup *pGroup = pCache->pGroup; assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); pcache1EnterMutex(pGroup); - pcache1TruncateUnsafe(pCache, 0); + if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0); assert( pGroup->nMaxPage >= pCache->nMax ); pGroup->nMaxPage -= pCache->nMax; assert( pGroup->nMinPage >= pCache->nMin ); diff --git a/src/pragma.c b/src/pragma.c index e0a0255ec0..1ee08fbec0 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1030,7 +1030,7 @@ void sqlite3Pragma( ** compiler (eg. count_changes). So add an opcode to expire all ** compiled SQL statements after modifying a pragma value. */ - sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); + sqlite3VdbeAddOp0(v, OP_Expire); setAllPagerFlags(db); } break; @@ -1052,7 +1052,7 @@ void sqlite3Pragma( */ case PragTyp_TABLE_INFO: if( zRight ){ Table *pTab; - pTab = sqlite3FindTable(db, zRight, zDb); + pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb); if( pTab ){ static const char *azCol[] = { "cid", "name", "type", "notnull", "dflt_value", "pk" @@ -1334,12 +1334,10 @@ void sqlite3Pragma( sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); sqlite3ColumnDefault(v, pTab, iKey, regRow); sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, - sqlite3VdbeCurrentAddr(v)+3); VdbeCoverage(v); }else{ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); } - sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_SeekRowid, i, 0, regRow); VdbeCoverage(v); sqlite3VdbeGoto(v, addrOk); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); }else{ diff --git a/src/printf.c b/src/printf.c index 238997f37d..a888d50b94 100644 --- a/src/printf.c +++ b/src/printf.c @@ -15,26 +15,26 @@ ** Conversion types fall into various categories as defined by the ** following enumeration. */ -#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ -#define etFLOAT 2 /* Floating point. %f */ -#define etEXP 3 /* Exponentional notation. %e and %E */ -#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ -#define etSIZE 5 /* Return number of characters processed so far. %n */ -#define etSTRING 6 /* Strings. %s */ -#define etDYNSTRING 7 /* Dynamically allocated strings. %z */ -#define etPERCENT 8 /* Percent symbol. %% */ -#define etCHARX 9 /* Characters. %c */ +#define etRADIX 0 /* Integer types. %d, %x, %o, and so forth */ +#define etFLOAT 1 /* Floating point. %f */ +#define etEXP 2 /* Exponentional notation. %e and %E */ +#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */ +#define etSIZE 4 /* Return number of characters processed so far. %n */ +#define etSTRING 5 /* Strings. %s */ +#define etDYNSTRING 6 /* Dynamically allocated strings. %z */ +#define etPERCENT 7 /* Percent symbol. %% */ +#define etCHARX 8 /* Characters. %c */ /* The rest are extensions, not normally found in printf() */ -#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */ -#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '', +#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ +#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', NULL pointers replaced by SQL NULL. %Q */ -#define etTOKEN 12 /* a pointer to a Token structure */ -#define etSRCLIST 13 /* a pointer to a SrcList */ -#define etPOINTER 14 /* The %p conversion */ -#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */ -#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ +#define etTOKEN 11 /* a pointer to a Token structure */ +#define etSRCLIST 12 /* a pointer to a SrcList */ +#define etPOINTER 13 /* The %p conversion */ +#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ +#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ -#define etINVALID 0 /* Any unrecognized conversion type */ +#define etINVALID 16 /* Any unrecognized conversion type */ /* @@ -189,7 +189,7 @@ void sqlite3VXPrintf( etByte flag_long; /* True if "l" flag is present */ etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ - etByte xtype = 0; /* Conversion paradigm */ + etByte xtype = etINVALID; /* Conversion paradigm */ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ u8 useIntern; /* Ok to use internal conversions (ex: %T) */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ diff --git a/src/resolve.c b/src/resolve.c index 8e290af123..8ae7f0919c 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -718,7 +718,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); pNC->nErr++; is_agg = 0; - }else if( no_such_func && pParse->db->init.busy==0 ){ + }else if( no_such_func && pParse->db->init.busy==0 +#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION + && pParse->explain==0 +#endif + ){ sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); pNC->nErr++; }else if( wrong_num_args ){ @@ -763,6 +767,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); + pNC->ncFlags |= NC_VarSelect; } } break; diff --git a/src/rowset.c b/src/rowset.c index a70264ed1b..aa81607b9f 100644 --- a/src/rowset.c +++ b/src/rowset.c @@ -242,28 +242,26 @@ static struct RowSetEntry *rowSetEntryMerge( struct RowSetEntry *pTail; pTail = &head; - while( pA && pB ){ + assert( pA!=0 && pB!=0 ); + for(;;){ assert( pA->pRight==0 || pA->v<=pA->pRight->v ); assert( pB->pRight==0 || pB->v<=pB->pRight->v ); - if( pA->vv ){ - pTail->pRight = pA; + if( pA->v<=pB->v ){ + if( pA->vv ) pTail = pTail->pRight = pA; pA = pA->pRight; - pTail = pTail->pRight; - }else if( pB->vv ){ - pTail->pRight = pB; - pB = pB->pRight; - pTail = pTail->pRight; + if( pA==0 ){ + pTail->pRight = pB; + break; + } }else{ - pA = pA->pRight; + pTail = pTail->pRight = pB; + pB = pB->pRight; + if( pB==0 ){ + pTail->pRight = pA; + break; + } } } - if( pA ){ - assert( pA->pRight==0 || pA->v<=pA->pRight->v ); - pTail->pRight = pA; - }else{ - assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v ); - pTail->pRight = pB; - } return head.pRight; } @@ -286,9 +284,10 @@ static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){ aBucket[i] = pIn; pIn = pNext; } - pIn = 0; - for(i=0; iiECursor, regRecord); if( iLimit ){ int addr; + int r1 = 0; + /* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit + ** register is initialized with value of LIMIT+OFFSET.) After the sorter + ** fills up, delete the least entry in the sorter after each insert. + ** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */ addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v); sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); + if( pSort->bOrderedInnerLoop ){ + r1 = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1); + VdbeComment((v, "seq")); + } sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); + if( pSort->bOrderedInnerLoop ){ + /* If the inner loop is driven by an index such that values from + ** the same iteration of the inner loop are in sorted order, then + ** immediately jump to the next iteration of an inner loop if the + ** entry from the current iteration does not fit into the top + ** LIMIT+OFFSET entries of the sorter. */ + int iBrk = sqlite3VdbeCurrentAddr(v) + 2; + sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + VdbeCoverage(v); + } sqlite3VdbeJumpHere(v, addr); } } @@ -1006,7 +1028,7 @@ static void selectInnerLoop( */ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ int nExtra = (N+X)*(sizeof(CollSeq*)+1); - KeyInfo *p = sqlite3Malloc(sizeof(KeyInfo) + nExtra); + KeyInfo *p = sqlite3DbMallocRaw(db, sizeof(KeyInfo) + nExtra); if( p ){ p->aSortOrder = (u8*)&p->aColl[N+X]; p->nField = (u16)N; @@ -1028,7 +1050,7 @@ void sqlite3KeyInfoUnref(KeyInfo *p){ if( p ){ assert( p->nRef>0 ); p->nRef--; - if( p->nRef==0 ) sqlite3DbFree(0, p); + if( p->nRef==0 ) sqlite3DbFree(p->db, p); } } @@ -5176,6 +5198,7 @@ int sqlite3Select( } if( sSort.pOrderBy ){ sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo); + sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo); if( sSort.nOBSat==sSort.pOrderBy->nExpr ){ sSort.pOrderBy = 0; } diff --git a/src/shell.c b/src/shell.c index 5148177372..18c6ef7197 100644 --- a/src/shell.c +++ b/src/shell.c @@ -2543,13 +2543,22 @@ static FILE *output_file_open(const char *zFile){ /* ** A routine for handling output from sqlite3_trace(). */ -static void sql_trace_callback(void *pArg, const char *z){ +static int sql_trace_callback( + unsigned mType, + void *pArg, + void *pP, + void *pX +){ FILE *f = (FILE*)pArg; + UNUSED_PARAMETER(mType); + UNUSED_PARAMETER(pP); if( f ){ + const char *z = (const char*)pX; int i = (int)strlen(z); while( i>0 && z[i-1]==';' ){ i--; } utf8_printf(f, "%.*s;\n", i, z); } + return 0; } /* @@ -2946,10 +2955,10 @@ static int db_int(ShellState *p, const char *zSql){ /* ** Convert a 2-byte or 4-byte big-endian integer into a native integer */ -unsigned int get2byteInt(unsigned char *a){ +static unsigned int get2byteInt(unsigned char *a){ return (a[0]<<8) + a[1]; } -unsigned int get4byteInt(unsigned char *a){ +static unsigned int get4byteInt(unsigned char *a){ return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; } @@ -4655,9 +4664,9 @@ static int do_meta_command(char *zLine, ShellState *p){ p->traceOut = output_file_open(azArg[1]); #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( p->traceOut==0 ){ - sqlite3_trace(p->db, 0, 0); + sqlite3_trace_v2(p->db, 0, 0, 0); }else{ - sqlite3_trace(p->db, sql_trace_callback, p->traceOut); + sqlite3_trace_v2(p->db, SQLITE_TRACE_STMT, sql_trace_callback,p->traceOut); } #endif }else @@ -5317,6 +5326,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); +#else + (void)cmdline_option_value(argc, argv, ++i); #endif }else if( strcmp(z,"-scratch")==0 ){ int n, sz; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 795236f587..1ee39b6799 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,8 +30,8 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. */ -#ifndef _SQLITE3_H_ -#define _SQLITE3_H_ +#ifndef SQLITE3_H +#define SQLITE3_H #include /* Needed for the definition of va_list */ /* @@ -54,8 +54,17 @@ extern "C" { #ifndef SQLITE_CDECL # define SQLITE_CDECL #endif +#ifndef SQLITE_APICALL +# define SQLITE_APICALL +#endif #ifndef SQLITE_STDCALL -# define SQLITE_STDCALL +# define SQLITE_STDCALL SQLITE_APICALL +#endif +#ifndef SQLITE_CALLBACK +# define SQLITE_CALLBACK +#endif +#ifndef SQLITE_SYSAPI +# define SQLITE_SYSAPI #endif /* @@ -506,6 +515,7 @@ int sqlite3_exec( #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) +#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) /* ** CAPI3REF: Flags For File Open Operations @@ -1035,6 +1045,16 @@ struct sqlite3_io_methods { */ typedef struct sqlite3_mutex sqlite3_mutex; +/* +** CAPI3REF: Loadable Extension Thunk +** +** A pointer to the opaque sqlite3_api_routines structure is passed as +** the third parameter to entry points of [loadable extensions]. This +** structure must be typedefed in order to work around compiler warnings +** on some platforms. +*/ +typedef struct sqlite3_api_routines sqlite3_api_routines; + /* ** CAPI3REF: OS Interface Object ** @@ -1939,7 +1959,7 @@ struct sqlite3_mem_methods { ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. ** There should be two additional arguments. ** When the first argument to this interface is 1, then only the C-API is -** enabled and the SQL function remains disabled. If the first argment to +** enabled and the SQL function remains disabled. If the first argument to ** this interface is 0, then both the C-API and the SQL function are disabled. ** If the first argument is -1, then no changes are made to state of either the ** C-API or the SQL function. @@ -2232,7 +2252,7 @@ int sqlite3_complete16(const void *sql); ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ -int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); +int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*); /* ** CAPI3REF: Set A Busy Timeout @@ -2754,6 +2774,9 @@ int sqlite3_set_authorizer( ** CAPI3REF: Tracing And Profiling Functions ** METHOD: sqlite3 ** +** These routines are deprecated. Use the [sqlite3_trace_v2()] interface +** instead of the routines described here. +** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** @@ -2779,10 +2802,104 @@ int sqlite3_set_authorizer( ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ -void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, +SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, + void(*xTrace)(void*,const char*), void*); +SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); +/* +** CAPI3REF: SQL Trace Event Codes +** KEYWORDS: SQLITE_TRACE +** +** These constants identify classes of events that can be monitored +** using the [sqlite3_trace_v2()] tracing logic. The third argument +** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of +** the following constants. ^The first argument to the trace callback +** is one of the following constants. +** +** New tracing constants may be added in future releases. +** +** ^A trace callback has four arguments: xCallback(T,C,P,X). +** ^The T argument is one of the integer type codes above. +** ^The C argument is a copy of the context pointer passed in as the +** fourth argument to [sqlite3_trace_v2()]. +** The P and X arguments are pointers whose meanings depend on T. +** +**
+** [[SQLITE_TRACE_STMT]]
SQLITE_TRACE_STMT
+**
^An SQLITE_TRACE_STMT callback is invoked when a prepared statement +** first begins running and possibly at other times during the +** execution of the prepared statement, such as at the start of each +** trigger subprogram. ^The P argument is a pointer to the +** [prepared statement]. ^The X argument is a pointer to a string which +** is the unexpanded SQL text of the prepared statement or an SQL comment +** that indicates the invocation of a trigger. ^The callback can compute +** the same text that would have been returned by the legacy [sqlite3_trace()] +** interface by using the X argument when X begins with "--" and invoking +** [sqlite3_expanded_sql(P)] otherwise. +** +** [[SQLITE_TRACE_PROFILE]]
SQLITE_TRACE_PROFILE
+**
^An SQLITE_TRACE_PROFILE callback provides approximately the same +** information as is provided by the [sqlite3_profile()] callback. +** ^The P argument is a pointer to the [prepared statement] and the +** X argument points to a 64-bit integer which is the estimated of +** the number of nanosecond that the prepared statement took to run. +** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. +** +** [[SQLITE_TRACE_ROW]]
SQLITE_TRACE_ROW
+**
^An SQLITE_TRACE_ROW callback is invoked whenever a prepared +** statement generates a single row of result. +** ^The P argument is a pointer to the [prepared statement] and the +** X argument is unused. +** +** [[SQLITE_TRACE_CLOSE]]
SQLITE_TRACE_CLOSE
+**
^An SQLITE_TRACE_CLOSE callback is invoked when a database +** connection closes. +** ^The P argument is a pointer to the [database connection] object +** and the X argument is unused. +**
+*/ +#define SQLITE_TRACE_STMT 0x01 +#define SQLITE_TRACE_PROFILE 0x02 +#define SQLITE_TRACE_ROW 0x04 +#define SQLITE_TRACE_CLOSE 0x08 + +/* +** CAPI3REF: SQL Trace Hook +** METHOD: sqlite3 +** +** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback +** function X against [database connection] D, using property mask M +** and context pointer P. ^If the X callback is +** NULL or if the M mask is zero, then tracing is disabled. The +** M argument should be the bitwise OR-ed combination of +** zero or more [SQLITE_TRACE] constants. +** +** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides +** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). +** +** ^The X callback is invoked whenever any of the events identified by +** mask M occur. ^The integer return value from the callback is currently +** ignored, though this may change in future releases. Callback +** implementations should return zero to ensure future compatibility. +** +** ^A trace callback is invoked with four arguments: callback(T,C,P,X). +** ^The T argument is one of the [SQLITE_TRACE] +** constants to indicate why the callback was invoked. +** ^The C argument is a copy of the context pointer. +** The P and X arguments are pointers whose meanings depend on T. +** +** The sqlite3_trace_v2() interface is intended to replace the legacy +** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which +** are deprecated. +*/ +int sqlite3_trace_v2( + sqlite3*, + unsigned uMask, + int(*xCallback)(unsigned,void*,void*,void*), + void *pCtx +); + /* ** CAPI3REF: Query Progress Callbacks ** METHOD: sqlite3 @@ -3401,11 +3518,35 @@ int sqlite3_prepare16_v2( ** CAPI3REF: Retrieving Statement SQL ** METHOD: sqlite3_stmt ** -** ^This interface can be used to retrieve a saved copy of the original -** SQL text used to create a [prepared statement] if that statement was -** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. +** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8 +** SQL text used to create [prepared statement] P if P was +** created by either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. +** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8 +** string containing the SQL text of prepared statement P with +** [bound parameters] expanded. +** +** ^(For example, if a prepared statement is created using the SQL +** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 +** and parameter :xyz is unbound, then sqlite3_sql() will return +** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql() +** will return "SELECT 2345,NULL".)^ +** +** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory +** is available to hold the result, or if the result would exceed the +** the maximum string length determined by the [SQLITE_LIMIT_LENGTH]. +** +** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of +** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time +** option causes sqlite3_expanded_sql() to always return NULL. +** +** ^The string returned by sqlite3_sql(P) is managed by SQLite and is +** automatically freed when the prepared statement is finalized. +** ^The string returned by sqlite3_expanded_sql(P), on the other hand, +** is obtained from [sqlite3_malloc()] and must be free by the application +** by passing it to [sqlite3_free()]. */ const char *sqlite3_sql(sqlite3_stmt *pStmt); +char *sqlite3_expanded_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database @@ -4563,12 +4704,13 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** SQLite will invoke the destructor function X with parameter P exactly ** once, when the metadata is discarded. ** SQLite is free to discard the metadata at any time, including:
    -**
  • when the corresponding function parameter changes, or -**
  • when [sqlite3_reset()] or [sqlite3_finalize()] is called for the -** SQL statement, or -**
  • when sqlite3_set_auxdata() is invoked again on the same parameter, or -**
  • during the original sqlite3_set_auxdata() call when a memory -** allocation error occurs.
)^ +**
  • ^(when the corresponding function parameter changes)^, or +**
  • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the +** SQL statement)^, or +**
  • ^(when sqlite3_set_auxdata() is invoked again on the same +** parameter)^, or +**
  • ^(during the original sqlite3_set_auxdata() call when a memory +** allocation error occurs.)^ ** ** Note the last bullet in particular. The destructor X in ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the @@ -5395,7 +5537,7 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** column exists. ^The sqlite3_table_column_metadata() interface returns ** SQLITE_ERROR and if the specified column does not exist. ** ^If the column-name parameter to sqlite3_table_column_metadata() is a -** NULL pointer, then this routine simply checks for the existance of the +** NULL pointer, then this routine simply checks for the existence of the ** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it ** does not. ** @@ -5529,8 +5671,8 @@ int sqlite3_load_extension( ** ** ^This interface enables or disables both the C-API ** [sqlite3_load_extension()] and the SQL function [load_extension()]. -** Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..) -** to enable or disable only the C-API. +** ^(Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..) +** to enable or disable only the C-API.)^ ** ** Security warning: It is recommended that extension loading ** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method @@ -5550,7 +5692,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff); ** ** ^(Even though the function prototype shows that xEntryPoint() takes ** no arguments and returns void, SQLite invokes xEntryPoint() with three -** arguments and expects and integer result as if the signature of the +** arguments and expects an integer result as if the signature of the ** entry point where as follows: ** **
    @@ -5576,7 +5718,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
     ** See also: [sqlite3_reset_auto_extension()]
     ** and [sqlite3_cancel_auto_extension()]
     */
    -int sqlite3_auto_extension(void (*xEntryPoint)(void));
    +int sqlite3_auto_extension(void(*xEntryPoint)(void));
     
     /*
     ** CAPI3REF: Cancel Automatic Extension Loading
    @@ -5588,7 +5730,7 @@ int sqlite3_auto_extension(void (*xEntryPoint)(void));
     ** unregistered and it returns 0 if X was not on the list of initialization
     ** routines.
     */
    -int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void));
    +int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
     
     /*
     ** CAPI3REF: Reset Automatic Extension Loading
    @@ -6764,6 +6906,18 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
     ** memory used by all pager caches associated with the database connection.)^
     ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
     **
    +** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]] 
    +** ^(
    SQLITE_DBSTATUS_CACHE_USED_SHARED
    +**
    This parameter is similar to DBSTATUS_CACHE_USED, except that if a +** pager cache is shared between two or more connections the bytes of heap +** memory used by that pager cache is divided evenly between the attached +** connections.)^ In other words, if none of the pager caches associated +** with the database connection are shared, this request returns the same +** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are +** shared, the value returned by this call will be smaller than that returned +** by DBSTATUS_CACHE_USED. ^The highwater mark associated with +** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0. +** ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
    SQLITE_DBSTATUS_SCHEMA_USED
    **
    This parameter returns the approximate number of bytes of heap ** memory used to store the schema for all databases associated @@ -6821,7 +6975,8 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); #define SQLITE_DBSTATUS_CACHE_MISS 8 #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 -#define SQLITE_DBSTATUS_MAX 10 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 +#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */ /* @@ -7977,7 +8132,7 @@ int sqlite3_db_cacheflush(sqlite3*); ** ^The second parameter to the preupdate callback is a pointer to ** the [database connection] that registered the preupdate hook. ** ^The third parameter to the preupdate callback is one of the constants -** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to indentify the +** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to identify the ** kind of update operation that is about to occur. ** ^(The fourth parameter to the preupdate callback is the name of the ** database within the database connection that is being modified. This @@ -8204,4 +8359,4 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif -#endif /* _SQLITE3_H_ */ +#endif /* SQLITE3_H */ diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 20a2fcdf02..ce87e74690 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -15,12 +15,10 @@ ** as extensions by SQLite should #include this file instead of ** sqlite3.h. */ -#ifndef _SQLITE3EXT_H_ -#define _SQLITE3EXT_H_ +#ifndef SQLITE3EXT_H +#define SQLITE3EXT_H #include "sqlite3.h" -typedef struct sqlite3_api_routines sqlite3_api_routines; - /* ** The following structure holds pointers to all of the SQLite API ** routines. @@ -281,8 +279,21 @@ struct sqlite3_api_routines { int (*db_cacheflush)(sqlite3*); /* Version 3.12.0 and later */ int (*system_errno)(sqlite3*); + /* Version 3.14.0 and later */ + int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); + char *(*expanded_sql)(sqlite3_stmt*); }; +/* +** This is the function signature used for all extension entry points. It +** is also defined in the file "loadext.c". +*/ +typedef int (*sqlite3_loadext_entry)( + sqlite3 *db, /* Handle to the database. */ + char **pzErrMsg, /* Used to set error string on failure. */ + const sqlite3_api_routines *pThunk /* Extension API function pointers. */ +); + /* ** The following macros redefine the API routines so that they are ** redirected through the global sqlite3_api structure. @@ -526,6 +537,9 @@ struct sqlite3_api_routines { #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush /* Version 3.12.0 and later */ #define sqlite3_system_errno sqlite3_api->system_errno +/* Version 3.14.0 and later */ +#define sqlite3_trace_v2 sqlite3_api->trace_v2 +#define sqlite3_expanded_sql sqlite3_api->expanded_sql #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -543,4 +557,4 @@ struct sqlite3_api_routines { # define SQLITE_EXTENSION_INIT3 /*no-op*/ #endif -#endif /* _SQLITE3EXT_H_ */ +#endif /* SQLITE3EXT_H */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 24f8e9d300..3cbdec748b 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -12,8 +12,8 @@ ** Internal interface definitions for SQLite. ** */ -#ifndef _SQLITEINT_H_ -#define _SQLITEINT_H_ +#ifndef SQLITEINT_H +#define SQLITEINT_H /* Special Comments: ** @@ -42,6 +42,14 @@ ** asterisks and the comment text. */ +/* +** Make sure the Tcl calling convention macro is defined. This macro is +** only used by test code and Tcl integration code. +*/ +#ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +#endif + /* ** Make sure that rand_s() is available on Windows systems with MSVC 2005 ** or higher. @@ -226,7 +234,7 @@ ** the SQLITE_DISABLE_INTRINSIC define. */ #if !defined(SQLITE_DISABLE_INTRINSIC) -# if defined(_MSC_VER) && _MSC_VER>=1300 +# if defined(_MSC_VER) && _MSC_VER>=1400 # if !defined(_WIN32_WCE) # include # pragma intrinsic(_byteswap_ushort) @@ -411,7 +419,7 @@ ** be true and false so that the unreachable code they specify will ** not be counted as untested code. */ -#if defined(SQLITE_COVERAGE_TEST) +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) @@ -1241,6 +1249,15 @@ void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); const char*); #endif +#ifndef SQLITE_OMIT_DEPRECATED +/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing +** in the style of sqlite3_trace() +*/ +#define SQLITE_TRACE_LEGACY 0x80 +#else +#define SQLITE_TRACE_LEGACY 0 +#endif /* SQLITE_OMIT_DEPRECATED */ + /* ** Each database connection is an instance of the following structure. @@ -1271,6 +1288,7 @@ struct sqlite3 { u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ + u8 mTrace; /* zero or more SQLITE_TRACE flags */ int nextPagesize; /* Pagesize after VACUUM if >0 */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ @@ -1291,7 +1309,7 @@ struct sqlite3 { int nVDestroy; /* Number of active OP_VDestroy operations */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ - void (*xTrace)(void*,const char*); /* Trace function */ + int (*xTrace)(u32,void*,void*,void*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ @@ -2486,7 +2504,7 @@ struct SrcList { int regReturn; /* Register holding return address of addrFillSub */ int regResult; /* Registers holding results of a co-routine */ struct { - u8 jointype; /* Type of join between this able and the previous */ + u8 jointype; /* Type of join between this table and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ @@ -2532,18 +2550,20 @@ struct SrcList { #define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */ #define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */ #define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */ -#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */ -#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */ -#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ -#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ -#define WHERE_NO_AUTOINDEX 0x0080 /* Disallow automatic indexes */ -#define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */ -#define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */ -#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */ -#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */ -#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */ -#define WHERE_ONEPASS_MULTIROW 0x2000 /* ONEPASS is ok with multiple rows */ -#define WHERE_USE_LIMIT 0x4000 /* There is a constant LIMIT clause */ +#define WHERE_ONEPASS_MULTIROW 0x0008 /* ONEPASS is ok with multiple rows */ +#define WHERE_DUPLICATES_OK 0x0010 /* Ok to return a row more than once */ +#define WHERE_OR_SUBCLAUSE 0x0020 /* Processing a sub-WHERE as part of + ** the OR optimization */ +#define WHERE_GROUPBY 0x0040 /* pOrderBy is really a GROUP BY */ +#define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */ +#define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */ +#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */ +#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */ +#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */ + /* 0x1000 not currently used */ + /* 0x2000 not currently used */ +#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */ + /* 0x8000 not currently used */ /* Allowed return values from sqlite3WhereIsDistinct() */ @@ -2598,6 +2618,7 @@ struct NameContext { #define NC_InAggFunc 0x0008 /* True if analyzing arguments to an agg func */ #define NC_HasAgg 0x0010 /* One or more aggregate functions seen */ #define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */ +#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */ #define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */ /* @@ -3245,6 +3266,7 @@ struct Walker { struct SrcCount *pSrcCount; /* Counting column references */ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ int *aiCol; /* array of column indexes */ + struct IdxCover *pIdxCover; /* Check for index coverage */ } u; }; @@ -3428,11 +3450,15 @@ int sqlite3HeapNearlyFull(void); # define sqlite3StackFree(D,P) sqlite3DbFree(D,P) #endif -#ifdef SQLITE_ENABLE_MEMSYS3 -const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); -#endif +/* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they +** are, disable MEMSYS3 +*/ #ifdef SQLITE_ENABLE_MEMSYS5 const sqlite3_mem_methods *sqlite3MemGetMemsys5(void); +#undef SQLITE_ENABLE_MEMSYS3 +#endif +#ifdef SQLITE_ENABLE_MEMSYS3 +const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); #endif @@ -3615,8 +3641,8 @@ void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(sqlite3*, IdList*); void sqlite3SrcListDelete(sqlite3*, SrcList*); Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); -Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, - Expr*, int, int); +void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, + Expr*, int, int, u8); void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, @@ -3635,6 +3661,7 @@ void sqlite3WhereEnd(WhereInfo*); LogEst sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); +int sqlite3WhereOrderedInnerLoop(WhereInfo*); int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); int sqlite3WhereBreakLabel(WhereInfo*); @@ -3668,8 +3695,10 @@ void sqlite3ExprIfTrue(Parse*, Expr*, int, int); void sqlite3ExprIfFalse(Parse*, Expr*, int, int); void sqlite3ExprIfFalseDup(Parse*, Expr*, int, int); Table *sqlite3FindTable(sqlite3*,const char*, const char*); -Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*); -Table *sqlite3LocateTableItem(Parse*,int isView,struct SrcList_item *); +#define LOCATE_VIEW 0x01 +#define LOCATE_NOERR 0x02 +Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*); +Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *); Index *sqlite3FindIndex(sqlite3*,const char*, const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); @@ -3681,6 +3710,7 @@ int sqlite3ExprListCompare(ExprList*, ExprList*, int); int sqlite3ExprImpliesExpr(Expr*, Expr*, int); void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); +int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); int sqlite3FunctionUsesThisSrc(Expr*, SrcList*); Vdbe *sqlite3GetVdbe(Parse*); #ifndef SQLITE_OMIT_BUILTIN_TEST @@ -4232,4 +4262,4 @@ int sqlite3ThreadJoin(SQLiteThread*, void**); int sqlite3DbstatRegister(sqlite3*); #endif -#endif /* _SQLITEINT_H_ */ +#endif /* SQLITEINT_H */ diff --git a/src/status.c b/src/status.c index 69f92ff7c6..24dcad4572 100644 --- a/src/status.c +++ b/src/status.c @@ -158,7 +158,7 @@ int sqlite3_status64( return SQLITE_OK; } int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ - sqlite3_int64 iCur, iHwtr; + sqlite3_int64 iCur = 0, iHwtr = 0; int rc; #ifdef SQLITE_ENABLE_API_ARMOR if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; @@ -219,6 +219,7 @@ int sqlite3_db_status( ** by all pagers associated with the given database connection. The ** highwater mark is meaningless and is returned as zero. */ + case SQLITE_DBSTATUS_CACHE_USED_SHARED: case SQLITE_DBSTATUS_CACHE_USED: { int totalUsed = 0; int i; @@ -227,7 +228,11 @@ int sqlite3_db_status( Btree *pBt = db->aDb[i].pBt; if( pBt ){ Pager *pPager = sqlite3BtreePager(pBt); - totalUsed += sqlite3PagerMemUsed(pPager); + int nByte = sqlite3PagerMemUsed(pPager); + if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){ + nByte = nByte / sqlite3BtreeConnectionCount(pBt); + } + totalUsed += nByte; } } sqlite3BtreeLeaveAll(db); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index d6a36118c4..3db4a32c4d 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -30,10 +30,17 @@ ** If requested, include the SQLite compiler options file for MSVC. */ #if defined(INCLUDE_MSVC_H) -#include "msvc.h" +# include "msvc.h" #endif -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #include /* @@ -133,6 +140,7 @@ struct SqliteDb { char *zBusy; /* The busy callback routine */ char *zCommit; /* The commit hook callback routine */ char *zTrace; /* The trace callback routine */ + char *zTraceV2; /* The trace_v2 callback routine */ char *zProfile; /* The profile callback routine */ char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ @@ -192,7 +200,7 @@ static void closeIncrblobChannels(SqliteDb *pDb){ for(p=pDb->pIncrblob; p; p=pNext){ pNext = p->pNext; - /* Note: Calling unregister here call Tcl_Close on the incrblob channel, + /* Note: Calling unregister here call Tcl_Close on the incrblob channel, ** which deletes the IncrblobChannel structure at *p. So do not ** call Tcl_Free() here. */ @@ -203,7 +211,10 @@ static void closeIncrblobChannels(SqliteDb *pDb){ /* ** Close an incremental blob channel. */ -static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ +static int SQLITE_TCLAPI incrblobClose( + ClientData instanceData, + Tcl_Interp *interp +){ IncrblobChannel *p = (IncrblobChannel *)instanceData; int rc = sqlite3_blob_close(p->pBlob); sqlite3 *db = p->pDb->db; @@ -232,9 +243,9 @@ static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ /* ** Read data from an incremental blob channel. */ -static int incrblobInput( - ClientData instanceData, - char *buf, +static int SQLITE_TCLAPI incrblobInput( + ClientData instanceData, + char *buf, int bufSize, int *errorCodePtr ){ @@ -264,9 +275,9 @@ static int incrblobInput( /* ** Write data to an incremental blob channel. */ -static int incrblobOutput( - ClientData instanceData, - CONST char *buf, +static int SQLITE_TCLAPI incrblobOutput( + ClientData instanceData, + CONST char *buf, int toWrite, int *errorCodePtr ){ @@ -297,8 +308,8 @@ static int incrblobOutput( /* ** Seek an incremental blob channel. */ -static int incrblobSeek( - ClientData instanceData, +static int SQLITE_TCLAPI incrblobSeek( + ClientData instanceData, long offset, int seekMode, int *errorCodePtr @@ -323,10 +334,17 @@ static int incrblobSeek( } -static void incrblobWatch(ClientData instanceData, int mode){ - /* NO-OP */ +static void SQLITE_TCLAPI incrblobWatch( + ClientData instanceData, + int mode +){ + /* NO-OP */ } -static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){ +static int SQLITE_TCLAPI incrblobHandle( + ClientData instanceData, + int dir, + ClientData *hPtr +){ return TCL_ERROR; } @@ -352,11 +370,11 @@ static Tcl_ChannelType IncrblobChannelType = { ** Create a new incrblob channel. */ static int createIncrblobChannel( - Tcl_Interp *interp, - SqliteDb *pDb, + Tcl_Interp *interp, + SqliteDb *pDb, const char *zDb, - const char *zTable, - const char *zColumn, + const char *zTable, + const char *zColumn, sqlite_int64 iRow, int isReadonly ){ @@ -438,7 +456,7 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){ pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 ); pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName+1); - for(p=pDb->pFunc; p; p=p->pNext){ + for(p=pDb->pFunc; p; p=p->pNext){ if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){ Tcl_Free((char*)pNew); return p; @@ -485,7 +503,7 @@ static void flushStmtCache(SqliteDb *pDb){ ** TCL calls this procedure when an sqlite3 database command is ** deleted. */ -static void DbDeleteCmd(void *db){ +static void SQLITE_TCLAPI DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); closeIncrblobChannels(pDb); @@ -508,6 +526,9 @@ static void DbDeleteCmd(void *db){ if( pDb->zTrace ){ Tcl_Free(pDb->zTrace); } + if( pDb->zTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } if( pDb->zProfile ){ Tcl_Free(pDb->zProfile); } @@ -587,6 +608,82 @@ static void DbTraceHandler(void *cd, const char *zSql){ } #endif +#ifndef SQLITE_OMIT_TRACE +/* +** This routine is called by the SQLite trace_v2 handler whenever a new +** supported event is generated. Unsupported event types are ignored. +** The TCL script in pDb->zTraceV2 is executed, with the arguments for +** the event appended to it (as list elements). +*/ +static int DbTraceV2Handler( + unsigned type, /* One of the SQLITE_TRACE_* event types. */ + void *cd, /* The original context data pointer. */ + void *pd, /* Primary event data, depends on event type. */ + void *xd /* Extra event data, depends on event type. */ +){ + SqliteDb *pDb = (SqliteDb*)cd; + Tcl_Obj *pCmd; + + switch( type ){ + case SQLITE_TRACE_STMT: { + sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; + char *zSql = (char *)xd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewStringObj(zSql, -1)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_PROFILE: { + sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; + sqlite3_int64 ns = (sqlite3_int64)xd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)ns)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_ROW: { + sqlite3_stmt *pStmt = (sqlite3_stmt *)pd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)pStmt)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + case SQLITE_TRACE_CLOSE: { + sqlite3 *db = (sqlite3 *)pd; + + pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1); + Tcl_IncrRefCount(pCmd); + Tcl_ListObjAppendElement(pDb->interp, pCmd, + Tcl_NewWideIntObj((Tcl_WideInt)db)); + Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + Tcl_ResetResult(pDb->interp); + break; + } + } + return SQLITE_OK; +} +#endif + #ifndef SQLITE_OMIT_TRACE /* ** This routine is called by the SQLite profile handler after a statement @@ -637,9 +734,9 @@ static void DbRollbackHandler(void *clientData){ ** This procedure handles wal_hook callbacks. */ static int DbWalHandler( - void *clientData, - sqlite3 *db, - const char *zDb, + void *clientData, + sqlite3 *db, + const char *zDb, int nEntry ){ int ret = SQLITE_OK; @@ -653,7 +750,7 @@ static int DbWalHandler( Tcl_IncrRefCount(p); Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry)); - if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) + if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0) || TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret) ){ Tcl_BackgroundError(interp); @@ -695,11 +792,11 @@ static void DbUnlockNotify(void **apArg, int nArg){ ** Pre-update hook callback. */ static void DbPreUpdateHandler( - void *p, + void *p, sqlite3 *db, int op, - const char *zDb, - const char *zTbl, + const char *zDb, + const char *zTbl, sqlite_int64 iKey1, sqlite_int64 iKey2 ){ @@ -727,10 +824,10 @@ static void DbPreUpdateHandler( #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ static void DbUpdateHandler( - void *p, + void *p, int op, - const char *zDb, - const char *zTbl, + const char *zDb, + const char *zTbl, sqlite_int64 rowid ){ SqliteDb *pDb = (SqliteDb *)p; @@ -815,7 +912,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ ** script object, lappend the arguments, then evaluate the copy. ** ** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated. - ** The new Tcl_Obj contains pointers to the original list elements. + ** The new Tcl_Obj contains pointers to the original list elements. ** That way, when Tcl_EvalObjv() is run and shimmers the first element ** of the list to tclCmdNameType, that alternate representation will ** be preserved and reused on the next invocation. @@ -823,15 +920,15 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ Tcl_Obj **aArg; int nArg; if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ - sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return; - } + } pCmd = Tcl_NewListObj(nArg, aArg); Tcl_IncrRefCount(pCmd); for(i=0; iinterp, pCmd, pVal); if( rc ){ Tcl_DecrRefCount(pCmd); - sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return; } } @@ -881,7 +978,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ } if( rc && rc!=TCL_RETURN ){ - sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); }else{ Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); int n; @@ -983,7 +1080,7 @@ static int auth_callback( Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : ""); #ifdef SQLITE_USER_AUTHENTICATION Tcl_DStringAppendElement(&str, zArg5 ? zArg5 : ""); -#endif +#endif rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str)); Tcl_DStringFree(&str); zReply = rc==TCL_OK ? Tcl_GetStringResult(pDb->interp) : "SQLITE_DENY"; @@ -1054,7 +1151,7 @@ static char *local_getline(char *zPrompt, FILE *in){ ** It is invoked after evaluating the script SCRIPT to commit or rollback ** the transaction or savepoint opened by the [transaction] command. */ -static int DbTransPostCmd( +static int SQLITE_TCLAPI DbTransPostCmd( ClientData data[], /* data[0] is the Sqlite3Db* for $db */ Tcl_Interp *interp, /* Tcl interpreter */ int result /* Result of evaluating SCRIPT */ @@ -1075,12 +1172,12 @@ static int DbTransPostCmd( pDb->disableAuth++; if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ /* This is a tricky scenario to handle. The most likely cause of an - ** error is that the exec() above was an attempt to commit the + ** error is that the exec() above was an attempt to commit the ** top-level transaction that returned SQLITE_BUSY. Or, less likely, ** that an IO-error has occurred. In either case, throw a Tcl exception ** and try to rollback the transaction. ** - ** But it could also be that the user executed one or more BEGIN, + ** But it could also be that the user executed one or more BEGIN, ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing ** this method's logic. Not clear how this would be best handled. */ @@ -1099,7 +1196,7 @@ static int DbTransPostCmd( ** Unless SQLITE_TEST is defined, this function is a simple wrapper around ** sqlite3_prepare_v2(). If SQLITE_TEST is defined, then it uses either ** sqlite3_prepare_v2() or legacy interface sqlite3_prepare(), depending -** on whether or not the [db_use_legacy_prepare] command has been used to +** on whether or not the [db_use_legacy_prepare] command has been used to ** configure the connection. */ static int dbPrepare( @@ -1155,7 +1252,7 @@ static int dbPrepareAndBind( for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ int n = pPreStmt->nSql; - if( nSql>=n + if( nSql>=n && memcmp(pPreStmt->zSql, zSql, n)==0 && (zSql[n]==0 || zSql[n-1]==';') ){ @@ -1181,7 +1278,7 @@ static int dbPrepareAndBind( break; } } - + /* If no prepared statement was found. Compile the SQL text. Also allocate ** a new SqlPreparedStmt structure. */ if( pPreStmt==0 ){ @@ -1227,7 +1324,7 @@ static int dbPrepareAndBind( assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql ); assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) ); - /* Bind values to parameters that begin with $ or : */ + /* Bind values to parameters that begin with $ or : */ for(i=1; i<=nVar; i++){ const char *zVar = sqlite3_bind_parameter_name(pStmt, i); if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ @@ -1315,8 +1412,8 @@ static void dbReleaseStmt( assert( pDb->nStmt>0 ); } pDb->nStmt++; - - /* If we have too many statement in cache, remove the surplus from + + /* If we have too many statement in cache, remove the surplus from ** the end of the cache list. */ while( pDb->nStmt>pDb->maxStmt ){ SqlPreparedStmt *pLast = pDb->stmtLast; @@ -1370,8 +1467,8 @@ static void dbReleaseColumnNames(DbEvalContext *p){ ** If pArray is not NULL, then it contains the name of a Tcl array ** variable. The "*" member of this array is set to a list containing ** the names of the columns returned by the statement as part of each -** call to dbEvalStep(), in order from left to right. e.g. if the names -** of the returned columns are a, b and c, it does the equivalent of the +** call to dbEvalStep(), in order from left to right. e.g. if the names +** of the returned columns are a, b and c, it does the equivalent of the ** tcl command: ** ** set ${pArray}(*) {a b c} @@ -1492,7 +1589,7 @@ static int dbEvalStep(DbEvalContext *p){ #if SQLITE_TEST if( p->pDb->bLegacyPrepare && rcs==SQLITE_SCHEMA && zPrevSql ){ /* If the runtime error was an SQLITE_SCHEMA, and the database - ** handle is configured to use the legacy sqlite3_prepare() + ** handle is configured to use the legacy sqlite3_prepare() ** interface, retry prepare()/step() on the same SQL statement. ** This only happens once. If there is a second SQLITE_SCHEMA ** error, the error will be returned to the caller. */ @@ -1580,11 +1677,11 @@ static int DbUseNre(void){ return( (major==8 && minor>=6) || major>8 ); } #else -/* +/* ** Compiling using headers earlier than 8.6. In this case NR cannot be ** used, so DbUseNre() to always return zero. Add #defines for the other ** Tcl_NRxxx() functions to prevent them from causing compilation errors, -** even though the only invocations of them are within conditional blocks +** even though the only invocations of them are within conditional blocks ** of the form: ** ** if( DbUseNre() ) { ... } @@ -1601,7 +1698,7 @@ static int DbUseNre(void){ ** ** $db eval SQL ?ARRAYNAME? SCRIPT */ -static int DbEvalNextCmd( +static int SQLITE_TCLAPI DbEvalNextCmd( ClientData data[], /* data[0] is the (DbEvalContext*) */ Tcl_Interp *interp, /* Tcl interpreter */ int result /* Result so far */ @@ -1630,11 +1727,11 @@ static int DbEvalNextCmd( } } - /* The required interpreter variables are now populated with the data + /* The required interpreter variables are now populated with the data ** from the current row. If using NRE, schedule callbacks to evaluate ** script pScript, then to invoke this function again to fetch the next ** row (or clean up if there is no next row or the script throws an - ** exception). After scheduling the callbacks, return control to the + ** exception). After scheduling the callbacks, return control to the ** caller. ** ** If not using NRE, evaluate pScript directly and continue with the @@ -1659,7 +1756,7 @@ static int DbEvalNextCmd( } /* -** This function is used by the implementations of the following database +** This function is used by the implementations of the following database ** handle sub-commands: ** ** $db update_hook ?SCRIPT? @@ -1711,7 +1808,12 @@ static void DbHookCmd( ** and calls that connection "db1". The second command causes this ** subroutine to be invoked. */ -static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ +static int SQLITE_TCLAPI DbObjCmd( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ SqliteDb *pDb = (SqliteDb*)cd; int choice; int rc = TCL_OK; @@ -1726,9 +1828,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ "preupdate", "profile", "progress", "rekey", "restore", "rollback_hook", "status", "timeout", "total_changes", - "trace", "transaction", "unlock_notify", - "update_hook", "version", "wal_hook", - 0 + "trace", "trace_v2", "transaction", + "unlock_notify", "update_hook", "version", + "wal_hook", + 0 }; enum DB_enum { DB_AUTHORIZER, DB_BACKUP, DB_BUSY, @@ -1741,8 +1844,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ DB_PREUPDATE, DB_PROFILE, DB_PROGRESS, DB_REKEY, DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES, - DB_TRACE, DB_TRANSACTION, DB_UNLOCK_NOTIFY, - DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK, + DB_TRACE, DB_TRACE_V2, DB_TRANSACTION, + DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION, + DB_WAL_HOOK, }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -1928,7 +2032,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ return TCL_ERROR; }else{ if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){ - Tcl_AppendResult( interp, "cannot convert \"", + Tcl_AppendResult( interp, "cannot convert \"", Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0); return TCL_ERROR; }else{ @@ -1942,7 +2046,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } } }else{ - Tcl_AppendResult( interp, "bad option \"", + Tcl_AppendResult( interp, "bad option \"", Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", (char*)0); return TCL_ERROR; @@ -1953,7 +2057,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* $db changes ** ** Return the number of rows that were modified, inserted, or deleted by - ** the most recent INSERT, UPDATE or DELETE statement, not including + ** the most recent INSERT, UPDATE or DELETE statement, not including ** any changes made by trigger programs. */ case DB_CHANGES: { @@ -2000,7 +2104,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ pCollate->zScript = (char*)&pCollate[1]; pDb->pCollate = pCollate; memcpy(pCollate->zScript, zScript, nScript+1); - if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8, + if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8, pCollate, tclSqlCollate) ){ Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE); return TCL_ERROR; @@ -2126,7 +2230,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ const char *zSep; const char *zNull; if( objc<5 || objc>7 ){ - Tcl_WrongNumArgs(interp, 2, objv, + Tcl_WrongNumArgs(interp, 2, objv, "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"); return TCL_ERROR; } @@ -2155,7 +2259,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ strcmp(zConflict, "fail" ) != 0 && strcmp(zConflict, "ignore" ) != 0 && strcmp(zConflict, "replace" ) != 0 ) { - Tcl_AppendResult(interp, "Error: \"", zConflict, + Tcl_AppendResult(interp, "Error: \"", zConflict, "\", conflict-algorithm must be one of: rollback, " "abort, fail, ignore, or replace", (char*)0); return TCL_ERROR; @@ -2244,7 +2348,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ for(i=0; i0 && strcmp(azCol[i], zNull)==0) - || strlen30(azCol[i])==0 + || strlen30(azCol[i])==0 ){ sqlite3_bind_null(pStmt, i+1); }else{ @@ -2323,8 +2427,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** The onecolumn method is the equivalent of: ** lindex [$db eval $sql] 0 */ - case DB_EXISTS: + case DB_EXISTS: case DB_ONECOLUMN: { + Tcl_Obj *pResult = 0; DbEvalContext sEval; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); @@ -2335,21 +2440,22 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ rc = dbEvalStep(&sEval); if( choice==DB_ONECOLUMN ){ if( rc==TCL_OK ){ - Tcl_SetObjResult(interp, dbEvalColumnValue(&sEval, 0)); + pResult = dbEvalColumnValue(&sEval, 0); }else if( rc==TCL_BREAK ){ Tcl_ResetResult(interp); } }else if( rc==TCL_BREAK || rc==TCL_OK ){ - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc==TCL_OK)); + pResult = Tcl_NewBooleanObj(rc==TCL_OK); } dbEvalFinalize(&sEval); + if( pResult ) Tcl_SetObjResult(interp, pResult); if( rc==TCL_BREAK ){ rc = TCL_OK; } break; } - + /* ** $db eval $sql ?array? ?{ ...code... }? ** @@ -2395,7 +2501,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } pScript = objv[objc-1]; Tcl_IncrRefCount(pScript); - + p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); dbEvalInit(p, pDb, objv[2], pArray); @@ -2442,7 +2548,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ if( n>2 && strncmp(z, "-deterministic",n)==0 ){ flags |= SQLITE_DETERMINISTIC; }else{ - Tcl_AppendResult(interp, "bad option \"", z, + Tcl_AppendResult(interp, "bad option \"", z, "\": must be -argcount or -deterministic", 0 ); return TCL_ERROR; @@ -2551,7 +2657,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* - ** $db last_insert_rowid + ** $db last_insert_rowid ** ** Return an integer which is the ROWID for the most recent insert. */ @@ -2573,7 +2679,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ */ /* $db progress ?N CALLBACK? - ** + ** ** Invoke the given callback every N virtual machine opcodes while executing ** queries. */ @@ -2680,7 +2786,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* $db restore ?DATABASE? FILENAME ** - ** Open a database file named FILENAME. Transfer the content + ** Open a database file named FILENAME. Transfer the content ** of FILENAME into the local database DATABASE (default: "main"). */ case DB_RESTORE: { @@ -2741,7 +2847,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* ** $db status (step|sort|autoindex) ** - ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or + ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or ** SQLITE_STMTSTATUS_SORT for the most recent eval. */ case DB_STATUS: { @@ -2759,15 +2865,15 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else if( strcmp(zOp, "autoindex")==0 ){ v = pDb->nIndex; }else{ - Tcl_AppendResult(interp, - "bad argument: should be autoindex, step, or sort", + Tcl_AppendResult(interp, + "bad argument: should be autoindex, step, or sort", (char*)0); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); break; } - + /* ** $db timeout MILLESECONDS ** @@ -2783,11 +2889,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ sqlite3_busy_timeout(pDb->db, ms); break; } - + /* ** $db total_changes ** - ** Return the number of rows that were modified, inserted, or deleted + ** Return the number of rows that were modified, inserted, or deleted ** since the database handle was created. */ case DB_TOTAL_CHANGES: { @@ -2828,7 +2934,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ pDb->zTrace = 0; } -#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) \ + && !defined(SQLITE_OMIT_DEPRECATED) if( pDb->zTrace ){ pDb->interp = interp; sqlite3_trace(pDb->db, DbTraceHandler, pDb); @@ -2840,6 +2947,88 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* $db trace_v2 ?CALLBACK? ?MASK? + ** + ** Make arrangements to invoke the CALLBACK routine for each trace event + ** matching the mask that is generated. The parameters are appended to + ** CALLBACK before it is executed. + */ + case DB_TRACE_V2: { + if( objc>4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK? ?MASK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zTraceV2 ){ + Tcl_AppendResult(interp, pDb->zTraceV2, (char*)0); + } + }else{ + char *zTraceV2; + int len; + Tcl_WideInt wMask = 0; + if( objc==4 ){ + static const char *TTYPE_strs[] = { + "statement", "profile", "row", "close", 0 + }; + enum TTYPE_enum { + TTYPE_STMT, TTYPE_PROFILE, TTYPE_ROW, TTYPE_CLOSE + }; + int i; + if( TCL_OK!=Tcl_ListObjLength(interp, objv[3], &len) ){ + return TCL_ERROR; + } + for(i=0; izTraceV2 ){ + Tcl_Free(pDb->zTraceV2); + } + zTraceV2 = Tcl_GetStringFromObj(objv[2], &len); + if( zTraceV2 && len>0 ){ + pDb->zTraceV2 = Tcl_Alloc( len + 1 ); + memcpy(pDb->zTraceV2, zTraceV2, len+1); + }else{ + pDb->zTraceV2 = 0; + } +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) + if( pDb->zTraceV2 ){ + pDb->interp = interp; + sqlite3_trace_v2(pDb->db, (unsigned)wMask, DbTraceV2Handler, pDb); + }else{ + sqlite3_trace_v2(pDb->db, 0, 0, 0); + } +#endif + } + break; + } + /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT ** ** Start a new transaction (if we are not already in the midst of a @@ -2892,7 +3081,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* If using NRE, schedule a callback to invoke the script pScript, then ** a second callback to commit (or rollback) the transaction or savepoint ** opened above. If not using NRE, evaluate the script directly, then - ** call function DbTransPostCmd() to commit (or rollback) the transaction + ** call function DbTransPostCmd() to commit (or rollback) the transaction ** or savepoint. */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); @@ -2923,14 +3112,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Tcl_DecrRefCount(pDb->pUnlockNotify); pDb->pUnlockNotify = 0; } - + if( objc==3 ){ xNotify = DbUnlockNotify; pNotifyArg = (void *)pDb; pDb->pUnlockNotify = objv[2]; Tcl_IncrRefCount(pDb->pUnlockNotify); } - + if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0); rc = TCL_ERROR; @@ -3029,13 +3218,13 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** $db update_hook ?script? ** $db rollback_hook ?script? */ - case DB_WAL_HOOK: - case DB_UPDATE_HOOK: + case DB_WAL_HOOK: + case DB_UPDATE_HOOK: case DB_ROLLBACK_HOOK: { - /* set ppHook to point at pUpdateHook or pRollbackHook, depending on + /* set ppHook to point at pUpdateHook or pRollbackHook, depending on ** whether [$db update_hook] or [$db rollback_hook] was invoked. */ - Tcl_Obj **ppHook = 0; + Tcl_Obj **ppHook = 0; if( choice==DB_WAL_HOOK ) ppHook = &pDb->pWalHook; if( choice==DB_UPDATE_HOOK ) ppHook = &pDb->pUpdateHook; if( choice==DB_ROLLBACK_HOOK ) ppHook = &pDb->pRollbackHook; @@ -3067,7 +3256,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** Adaptor that provides an objCmd interface to the NRE-enabled ** interface implementation. */ -static int DbObjCmdAdaptor( +static int SQLITE_TCLAPI DbObjCmdAdaptor( void *cd, Tcl_Interp *interp, int objc, @@ -3092,7 +3281,12 @@ static int DbObjCmdAdaptor( ** The second argument is the name of the database file. ** */ -static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ +static int SQLITE_TCLAPI DbMain( + void *cd, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const*objv +){ SqliteDb *p; const char *zArg; char *zErrMsg; @@ -3196,7 +3390,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } } if( objc<3 || (objc&1)!=1 ){ - Tcl_WrongNumArgs(interp, 1, objv, + Tcl_WrongNumArgs(interp, 1, objv, "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?" " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?" #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL) @@ -3488,7 +3682,7 @@ static void MD5Init(MD5Context *ctx){ * Update context to reflect the concatenation of another buffer full * of bytes. */ -static +static void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ uint32 t; @@ -3534,7 +3728,7 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ } /* - * Final wrapup - pad to 64-byte boundary with the bit pattern + * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ static void MD5Final(unsigned char digest[16], MD5Context *ctx){ @@ -3610,16 +3804,21 @@ static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ /* ** A TCL command for md5. The argument is the text to be hashed. The -** Result is the hash in base64. +** Result is the hash in base64. */ -static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){ +static int SQLITE_TCLAPI md5_cmd( + void*cd, + Tcl_Interp *interp, + int argc, + const char **argv +){ MD5Context ctx; unsigned char digest[16]; char zBuf[50]; void (*converter)(unsigned char*, char*); if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], " TEXT\"", (char*)0); return TCL_ERROR; } @@ -3636,7 +3835,12 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){ ** A TCL command to take the md5 hash of a file. The argument is the ** name of the file. */ -static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){ +static int SQLITE_TCLAPI md5file_cmd( + void*cd, + Tcl_Interp *interp, + int argc, + const char **argv +){ FILE *in; MD5Context ctx; void (*converter)(unsigned char*, char*); @@ -3644,13 +3848,13 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){ char zBuf[10240]; if( argc!=2 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], + Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], " FILENAME\"", (char*)0); return TCL_ERROR; } in = fopen(argv[1],"rb"); if( in==0 ){ - Tcl_AppendResult(interp,"unable to open file \"", argv[1], + Tcl_AppendResult(interp,"unable to open file \"", argv[1], "\" for reading", (char*)0); return TCL_ERROR; } @@ -3716,8 +3920,12 @@ static void md5finalize(sqlite3_context *context){ MD5DigestToBase16(digest, zBuf); sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } -int Md5_Register(sqlite3 *db){ - int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0, +int Md5_Register( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pThunk +){ + int rc = sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0, md5step, md5finalize); sqlite3_overload_function(db, "md5sum", -1); /* To exercise this API */ return rc; @@ -3764,7 +3972,7 @@ static const char *tclsh_main_loop(void); #ifdef SQLITE_TEST static void init_all(Tcl_Interp *); -static int init_all_cmd( +static int SQLITE_TCLAPI init_all_cmd( ClientData cd, Tcl_Interp *interp, int objc, @@ -3794,7 +4002,7 @@ static int init_all_cmd( ** to use the sqlite3_prepare_v2() function to prepare statements. If it ** is false, sqlite3_prepare(). */ -static int db_use_legacy_prepare_cmd( +static int SQLITE_TCLAPI db_use_legacy_prepare_cmd( ClientData cd, Tcl_Interp *interp, int objc, @@ -3831,7 +4039,7 @@ static int db_use_legacy_prepare_cmd( ** return the text representation of the most recently used statement ** handle. */ -static int db_last_stmt_ptr( +static int SQLITE_TCLAPI db_last_stmt_ptr( ClientData cd, Tcl_Interp *interp, int objc, @@ -3868,7 +4076,7 @@ static int db_last_stmt_ptr( ** Configure the interpreter passed as the first argument to have access ** to the commands and linked variables that make up: ** -** * the [sqlite3] extension itself, +** * the [sqlite3] extension itself, ** ** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and ** @@ -3956,7 +4164,7 @@ static void init_all(Tcl_Interp *interp){ Sqlitetesttclvar_Init(interp); Sqlitetestfs_Init(interp); SqlitetestThread_Init(interp); - SqlitetestOnefile_Init(interp); + SqlitetestOnefile_Init(); SqlitetestOsinst_Init(interp); Sqlitetestbackup_Init(interp); Sqlitetestintarray_Init(interp); @@ -4000,7 +4208,7 @@ static void init_all(Tcl_Interp *interp){ #endif #define TCLSH_MAIN main /* Needed to fake out mktclapp */ -int TCLSH_MAIN(int argc, char **argv){ +int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; #if !defined(_WIN32_WCE) diff --git a/src/test1.c b/src/test1.c index 5b7581af7b..aced552173 100644 --- a/src/test1.c +++ b/src/test1.c @@ -19,7 +19,11 @@ #endif #include "vdbeInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include @@ -75,7 +79,7 @@ void *sqlite3TestTextToPtr(const char *z){ ** for an sqlite connection instance. Bad things happen if the ** input is not an sqlite connection. */ -static int get_sqlite_pointer( +static int SQLITE_TCLAPI get_sqlite_pointer( void * clientData, Tcl_Interp *interp, int objc, @@ -221,7 +225,7 @@ static void io_trace_callback(const char *zFormat, ...){ ** I/O tracing begins going into FILENAME. If FILENAME is an empty ** string, I/O tracing is turned off. */ -static int test_io_trace( +static int SQLITE_TCLAPI test_io_trace( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -262,7 +266,7 @@ static int test_io_trace( ** ** Also return true if the OMIT_MISUSE environment variable exists. */ -static int clang_sanitize_address( +static int SQLITE_TCLAPI clang_sanitize_address( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -289,7 +293,7 @@ static int clang_sanitize_address( ** DB. The SQL is the string FORMAT. The format string should contain ** one %s or %q. STRING is the value inserted into %s or %q. */ -static int test_exec_printf( +static int SQLITE_TCLAPI test_exec_printf( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -327,7 +331,7 @@ static int test_exec_printf( ** HEX into ASCII. Most characters are translated as is. %HH becomes ** a hex character. */ -static int test_exec_hex( +static int SQLITE_TCLAPI test_exec_hex( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -373,7 +377,7 @@ static int test_exec_hex( ** ** Enter or leave the mutex on a database connection. */ -static int db_enter( +static int SQLITE_TCLAPI db_enter( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -389,7 +393,7 @@ static int db_enter( sqlite3_mutex_enter(db->mutex); return TCL_OK; } -static int db_leave( +static int SQLITE_TCLAPI db_leave( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -411,7 +415,7 @@ static int db_leave( ** ** Invoke the sqlite3_exec interface using the open database DB */ -static int test_exec( +static int SQLITE_TCLAPI test_exec( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -458,7 +462,7 @@ static int test_exec( ** Invoke the sqlite3_exec interface using the open database DB. Discard ** all results */ -static int test_exec_nr( +static int SQLITE_TCLAPI test_exec_nr( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -485,7 +489,7 @@ static int test_exec_nr( ** concatenate arg0 through argn using separator as the separator. ** Return the result. */ -static int test_mprintf_z( +static int SQLITE_TCLAPI test_mprintf_z( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -508,7 +512,7 @@ static int test_mprintf_z( ** Test the %n format of sqlite_mprintf(). Return the length of the ** input string. */ -static int test_mprintf_n( +static int SQLITE_TCLAPI test_mprintf_n( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -532,7 +536,7 @@ static int test_mprintf_n( ** You pass in a format string that requires more than one argument, ** bad things will happen. */ -static int test_snprintf_int( +static int SQLITE_TCLAPI test_snprintf_int( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -558,7 +562,7 @@ static int test_snprintf_int( ** DB. The SQL is the string FORMAT. The format string should contain ** one %s or %q. STRING is the value inserted into %s or %q. */ -static int test_get_table_printf( +static int SQLITE_TCLAPI test_get_table_printf( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -621,7 +625,7 @@ static int test_get_table_printf( ** ** Returns the integer ROWID of the most recent insert. */ -static int test_last_rowid( +static int SQLITE_TCLAPI test_last_rowid( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -645,7 +649,7 @@ static int test_last_rowid( ** ** Set the codec key. */ -static int test_key( +static int SQLITE_TCLAPI test_key( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -673,7 +677,7 @@ static int test_key( ** ** Change the codec key. */ -static int test_rekey( +static int SQLITE_TCLAPI test_rekey( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -701,7 +705,7 @@ static int test_rekey( ** ** Closes the database opened by sqlite3_open. */ -static int sqlite_test_close( +static int SQLITE_TCLAPI sqlite_test_close( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -725,7 +729,7 @@ static int sqlite_test_close( ** ** Closes the database opened by sqlite3_open. */ -static int sqlite_test_close_v2( +static int SQLITE_TCLAPI sqlite_test_close_v2( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1009,7 +1013,7 @@ static void nondeterministicFunction( ** sqlite3_create_function function while a query is in progress in order ** to test the SQLITE_MISUSE detection logic. */ -static int test_create_function( +static int SQLITE_TCLAPI test_create_function( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1166,7 +1170,7 @@ static void legacyCountFinalize(sqlite3_context *context){ ** "legacy_count()" with the supplied database handle. This is used ** to test the deprecated sqlite3_aggregate_count() API. */ -static int test_create_aggregate( +static int SQLITE_TCLAPI test_create_aggregate( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1207,7 +1211,7 @@ static int test_create_aggregate( ** Puts uses a separate buffer and debugging statements will be out of ** sequence if it is used. */ -static int test_printf( +static int SQLITE_TCLAPI test_printf( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1229,7 +1233,7 @@ static int test_printf( ** ** Call mprintf with three integer arguments */ -static int sqlite3_mprintf_int( +static int SQLITE_TCLAPI sqlite3_mprintf_int( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1256,7 +1260,7 @@ static int sqlite3_mprintf_int( ** ** Call mprintf with three 64-bit integer arguments */ -static int sqlite3_mprintf_int64( +static int SQLITE_TCLAPI sqlite3_mprintf_int64( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1289,7 +1293,7 @@ static int sqlite3_mprintf_int64( ** same as sqlite3_mprintf_int or sqlite3_mprintf_int64, depending on ** platform. */ -static int sqlite3_mprintf_long( +static int SQLITE_TCLAPI sqlite3_mprintf_long( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1320,7 +1324,7 @@ static int sqlite3_mprintf_long( ** ** Call mprintf with two integer arguments and one string argument */ -static int sqlite3_mprintf_str( +static int SQLITE_TCLAPI sqlite3_mprintf_str( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1347,7 +1351,7 @@ static int sqlite3_mprintf_str( ** ** Call mprintf with two integer arguments and one string argument */ -static int sqlite3_snprintf_str( +static int SQLITE_TCLAPI sqlite3_snprintf_str( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1381,7 +1385,7 @@ static int sqlite3_snprintf_str( ** ** Call mprintf with two integer arguments and one double argument */ -static int sqlite3_mprintf_double( +static int SQLITE_TCLAPI sqlite3_mprintf_double( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1412,7 +1416,7 @@ static int sqlite3_mprintf_double( ** two arguments given above. This is used to generate overflow and underflow ** doubles to test that they are converted properly. */ -static int sqlite3_mprintf_scaled( +static int SQLITE_TCLAPI sqlite3_mprintf_scaled( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1442,7 +1446,7 @@ static int sqlite3_mprintf_scaled( ** two arguments given above. This is used to generate overflow and underflow ** doubles to test that they are converted properly. */ -static int sqlite3_mprintf_stronly( +static int SQLITE_TCLAPI sqlite3_mprintf_stronly( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1466,7 +1470,7 @@ static int sqlite3_mprintf_stronly( ** Call mprintf with a single double argument which is derived from the ** hexadecimal encoding of an IEEE double. */ -static int sqlite3_mprintf_hexdouble( +static int SQLITE_TCLAPI sqlite3_mprintf_hexdouble( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -1499,7 +1503,7 @@ static int sqlite3_mprintf_hexdouble( ** */ #if !defined(SQLITE_OMIT_SHARED_CACHE) -static int test_enable_shared( +static int SQLITE_TCLAPI test_enable_shared( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1536,7 +1540,7 @@ static int test_enable_shared( ** Usage: sqlite3_extended_result_codes DB BOOLEAN ** */ -static int test_extended_result_codes( +static int SQLITE_TCLAPI test_extended_result_codes( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1559,7 +1563,7 @@ static int test_extended_result_codes( ** Usage: sqlite3_libversion_number ** */ -static int test_libversion_number( +static int SQLITE_TCLAPI test_libversion_number( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1573,7 +1577,7 @@ static int test_libversion_number( ** Usage: sqlite3_table_column_metadata DB dbname tblname colname ** */ -static int test_table_column_metadata( +static int SQLITE_TCLAPI test_table_column_metadata( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1624,7 +1628,7 @@ static int test_table_column_metadata( #ifndef SQLITE_OMIT_INCRBLOB -static int blobHandleFromObj( +static int SQLITE_TCLAPI blobHandleFromObj( Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3_blob **ppBlob @@ -1653,7 +1657,7 @@ static int blobHandleFromObj( return TCL_OK; } -static int test_blob_reopen( +static int SQLITE_TCLAPI test_blob_reopen( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1729,7 +1733,7 @@ static int testCreateCollationCmp( return iRes; } -static int test_create_collation_v2( +static int SQLITE_TCLAPI test_create_collation_v2( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1804,7 +1808,7 @@ static void cf2Destroy(void *pUser){ if( p->pDestroy ) Tcl_DecrRefCount(p->pDestroy); sqlite3_free(p); } -static int test_create_function_v2( +static int SQLITE_TCLAPI test_create_function_v2( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The invoking TCL interpreter */ int objc, /* Number of arguments */ @@ -1892,7 +1896,7 @@ static int test_create_function_v2( /* ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC? */ -static int test_load_extension( +static int SQLITE_TCLAPI test_load_extension( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1950,7 +1954,7 @@ static int test_load_extension( /* ** Usage: sqlite3_enable_load_extension DB-HANDLE ONOFF */ -static int test_enable_load( +static int SQLITE_TCLAPI test_enable_load( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1996,7 +2000,7 @@ static int test_enable_load( ** This command is used to test the recoverability of a database in ** the event of a program crash. */ -static int sqlite_abort( +static int SQLITE_TCLAPI sqlite_abort( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -2055,7 +2059,7 @@ error_out: ** ** Register the test SQL function on the database DB under the name NAME. */ -static int test_register_func( +static int SQLITE_TCLAPI test_register_func( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -2084,7 +2088,7 @@ static int test_register_func( ** ** Finalize a statement handle. */ -static int test_finalize( +static int SQLITE_TCLAPI test_finalize( void * clientData, Tcl_Interp *interp, int objc, @@ -2116,7 +2120,7 @@ static int test_finalize( ** ** Get the value of a status counter from a statement. */ -static int test_stmt_status( +static int SQLITE_TCLAPI test_stmt_status( void * clientData, Tcl_Interp *interp, int objc, @@ -2161,7 +2165,7 @@ static int test_stmt_status( /* ** Usage: sqlite3_stmt_scanstatus STMT IDX */ -static int test_stmt_scanstatus( +static int SQLITE_TCLAPI test_stmt_scanstatus( void * clientData, Tcl_Interp *interp, int objc, @@ -2211,7 +2215,7 @@ static int test_stmt_scanstatus( /* ** Usage: sqlite3_stmt_scanstatus_reset STMT */ -static int test_stmt_scanstatus_reset( +static int SQLITE_TCLAPI test_stmt_scanstatus_reset( void * clientData, Tcl_Interp *interp, int objc, @@ -2234,7 +2238,7 @@ static int test_stmt_scanstatus_reset( ** ** Zero the SQLITE_CONFIG_SQLLOG configuration */ -static int test_config_sqllog( +static int SQLITE_TCLAPI test_config_sqllog( void * clientData, Tcl_Interp *interp, int objc, @@ -2254,7 +2258,7 @@ static int test_config_sqllog( ** ** Return the value returned by the default VFS's xCurrentTimeInt64 method. */ -static int vfsCurrentTimeInt64( +static int SQLITE_TCLAPI vfsCurrentTimeInt64( void * clientData, Tcl_Interp *interp, int objc, @@ -2275,7 +2279,7 @@ static int vfsCurrentTimeInt64( /* ** Usage: sqlite3_snapshot_get DB DBNAME */ -static int test_snapshot_get( +static int SQLITE_TCLAPI test_snapshot_get( void * clientData, Tcl_Interp *interp, int objc, @@ -2310,7 +2314,7 @@ static int test_snapshot_get( /* ** Usage: sqlite3_snapshot_open DB DBNAME SNAPSHOT */ -static int test_snapshot_open( +static int SQLITE_TCLAPI test_snapshot_open( void * clientData, Tcl_Interp *interp, int objc, @@ -2342,7 +2346,7 @@ static int test_snapshot_open( /* ** Usage: sqlite3_snapshot_free SNAPSHOT */ -static int test_snapshot_free( +static int SQLITE_TCLAPI test_snapshot_free( void * clientData, Tcl_Interp *interp, int objc, @@ -2363,7 +2367,7 @@ static int test_snapshot_free( /* ** Usage: sqlite3_snapshot_cmp SNAPSHOT1 SNAPSHOT2 */ -static int test_snapshot_cmp( +static int SQLITE_TCLAPI test_snapshot_cmp( void * clientData, Tcl_Interp *interp, int objc, @@ -2389,7 +2393,7 @@ static int test_snapshot_cmp( ** ** Return the next statment in sequence after STMT. */ -static int test_next_stmt( +static int SQLITE_TCLAPI test_next_stmt( void * clientData, Tcl_Interp *interp, int objc, @@ -2421,7 +2425,7 @@ static int test_next_stmt( ** Return true if STMT is a NULL pointer or a pointer to a statement ** that is guaranteed to leave the database unmodified. */ -static int test_stmt_readonly( +static int SQLITE_TCLAPI test_stmt_readonly( void * clientData, Tcl_Interp *interp, int objc, @@ -2448,7 +2452,7 @@ static int test_stmt_readonly( ** Return true if STMT is a non-NULL pointer to a statement ** that has been stepped but not to completion. */ -static int test_stmt_busy( +static int SQLITE_TCLAPI test_stmt_busy( void * clientData, Tcl_Interp *interp, int objc, @@ -2474,7 +2478,7 @@ static int test_stmt_busy( ** ** Return true if STMT uses a statement journal. */ -static int uses_stmt_journal( +static int SQLITE_TCLAPI uses_stmt_journal( void * clientData, Tcl_Interp *interp, int objc, @@ -2500,7 +2504,7 @@ static int uses_stmt_journal( ** ** Reset a statement handle. */ -static int test_reset( +static int SQLITE_TCLAPI test_reset( void * clientData, Tcl_Interp *interp, int objc, @@ -2535,7 +2539,7 @@ static int test_reset( ** ** Return TRUE if a recompilation of the statement is recommended. */ -static int test_expired( +static int SQLITE_TCLAPI test_expired( void * clientData, Tcl_Interp *interp, int objc, @@ -2559,7 +2563,7 @@ static int test_expired( ** ** Transfer all bindings from FROMSTMT over to TOSTMT */ -static int test_transfer_bind( +static int SQLITE_TCLAPI test_transfer_bind( void * clientData, Tcl_Interp *interp, int objc, @@ -2586,7 +2590,7 @@ static int test_transfer_bind( ** Return the number of changes made to the database by the last SQL ** execution. */ -static int test_changes( +static int SQLITE_TCLAPI test_changes( void * clientData, Tcl_Interp *interp, int objc, @@ -2621,7 +2625,7 @@ static int sqlite_static_bind_nbyte = 0; ** of the VALUE is made. If FLAGS=="blob10" then a VALUE is ignored ** an a 10-byte blob "abc\000xyz\000pq" is inserted. */ -static int test_bind( +static int SQLITE_TCLAPI test_bind( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -2743,7 +2747,7 @@ static int test_collate_func( Tcl_GetIntFromObj(i, Tcl_GetObjResult(i), &res); return res; } -static int test_collate( +static int SQLITE_TCLAPI test_collate( void * clientData, Tcl_Interp *interp, int objc, @@ -2817,7 +2821,7 @@ static int test_utf16bin_collate_func( if( res==0 ) res = nA - nB; return res; } -static int test_utf16bin_collate( +static int SQLITE_TCLAPI test_utf16bin_collate( void * clientData, Tcl_Interp *interp, int objc, @@ -2874,7 +2878,7 @@ static void test_collate_needed_cb( /* ** Usage: add_test_collate_needed DB */ -static int test_collate_needed( +static int SQLITE_TCLAPI test_collate_needed( void * clientData, Tcl_Interp *interp, int objc, @@ -2925,7 +2929,7 @@ static int alignmentCollFunc( } return rc; } -static int add_alignment_test_collations( +static int SQLITE_TCLAPI add_alignment_test_collations( void * clientData, Tcl_Interp *interp, int objc, @@ -3043,7 +3047,7 @@ static void test_function_utf16be( sqlite3ValueFree(pVal); } #endif /* SQLITE_OMIT_UTF16 */ -static int test_function( +static int SQLITE_TCLAPI test_function( void * clientData, Tcl_Interp *interp, int objc, @@ -3088,7 +3092,7 @@ bad_args: ** The result is a list of two elements, the string representation of the ** error code and the english language explanation. */ -static int test_errstr( +static int SQLITE_TCLAPI test_errstr( void * clientData, Tcl_Interp *interp, int objc, @@ -3121,7 +3125,7 @@ static int test_errstr( ** Then run testfixture in the debugger and wait for the breakpoint to ** fire. Then additional breakpoints can be set to trace down the bug. */ -static int test_breakpoint( +static int SQLITE_TCLAPI test_breakpoint( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -3137,7 +3141,7 @@ static int test_breakpoint( ** IDX is the index of a wildcard in the prepared statement. This command ** binds a N-byte zero-filled BLOB to the wildcard. */ -static int test_bind_zeroblob( +static int SQLITE_TCLAPI test_bind_zeroblob( void * clientData, Tcl_Interp *interp, int objc, @@ -3173,7 +3177,7 @@ static int test_bind_zeroblob( ** IDX is the index of a wildcard in the prepared statement. This command ** binds a N-byte zero-filled BLOB to the wildcard. */ -static int test_bind_zeroblob64( +static int SQLITE_TCLAPI test_bind_zeroblob64( void * clientData, Tcl_Interp *interp, int objc, @@ -3210,7 +3214,7 @@ static int test_bind_zeroblob64( ** N is the index of a wildcard in the prepared statement. This command ** binds a 32-bit integer VALUE to that wildcard. */ -static int test_bind_int( +static int SQLITE_TCLAPI test_bind_int( void * clientData, Tcl_Interp *interp, int objc, @@ -3241,6 +3245,145 @@ static int test_bind_int( } +/* +** Usage: intarray_addr INT ... +** +** Return the address of a C-language array of 32-bit integers. +** +** Space to hold the array is obtained from malloc(). Call this procedure once +** with no arguments in order to release memory. Each call to this procedure +** overwrites the previous array. +*/ +static int SQLITE_TCLAPI test_intarray_addr( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int i; + static int *p = 0; + + sqlite3_free(p); + p = 0; + if( objc>1 ){ + p = sqlite3_malloc( sizeof(p[0])*(objc-1) ); + if( p==0 ) return TCL_ERROR; + for(i=0; i1 ){ + p = sqlite3_malloc( sizeof(p[0])*(objc-1) ); + if( p==0 ) return TCL_ERROR; + for(i=0; i1 ){ + p = sqlite3_malloc( sizeof(p[0])*(objc-1) ); + if( p==0 ) return TCL_ERROR; + for(i=0; i1 ){ + p = sqlite3_malloc( sizeof(p[0])*(objc-1) ); + if( p==0 ) return TCL_ERROR; + for(i=0; ilen ){ + char zBuf[200]; + sqlite3_snprintf(sizeof(zBuf), zBuf, + "cannot use %d blob bytes, have %d", bytes, len); + Tcl_AppendResult(interp, zBuf, -1); + return TCL_ERROR; + } + rc = sqlite3_bind_blob(pStmt, idx, value, bytes, xDestructor); if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE_OK ){ @@ -3530,7 +3682,7 @@ static int test_bind_blob( ** ** Return the number of wildcards in the given statement. */ -static int test_bind_parameter_count( +static int SQLITE_TCLAPI test_bind_parameter_count( void * clientData, Tcl_Interp *interp, int objc, @@ -3554,7 +3706,7 @@ static int test_bind_parameter_count( ** An empty string is returned if N is out of range or if the wildcard ** is nameless. */ -static int test_bind_parameter_name( +static int SQLITE_TCLAPI test_bind_parameter_name( void * clientData, Tcl_Interp *interp, int objc, @@ -3581,7 +3733,7 @@ static int test_bind_parameter_name( ** Return the index of the wildcard called NAME. Return 0 if there is ** no such wildcard. */ -static int test_bind_parameter_index( +static int SQLITE_TCLAPI test_bind_parameter_index( void * clientData, Tcl_Interp *interp, int objc, @@ -3606,7 +3758,7 @@ static int test_bind_parameter_index( ** Usage: sqlite3_clear_bindings STMT ** */ -static int test_clear_bindings( +static int SQLITE_TCLAPI test_clear_bindings( void * clientData, Tcl_Interp *interp, int objc, @@ -3626,7 +3778,7 @@ static int test_clear_bindings( /* ** Usage: sqlite3_sleep MILLISECONDS */ -static int test_sleep( +static int SQLITE_TCLAPI test_sleep( void * clientData, Tcl_Interp *interp, int objc, @@ -3651,7 +3803,7 @@ static int test_sleep( ** Return the string representation of the most recent sqlite3_* API ** error code. e.g. "SQLITE_ERROR". */ -static int test_ex_errcode( +static int SQLITE_TCLAPI test_ex_errcode( void * clientData, Tcl_Interp *interp, int objc, @@ -3678,7 +3830,7 @@ static int test_ex_errcode( ** Return the string representation of the most recent sqlite3_* API ** error code. e.g. "SQLITE_ERROR". */ -static int test_errcode( +static int SQLITE_TCLAPI test_errcode( void * clientData, Tcl_Interp *interp, int objc, @@ -3704,7 +3856,7 @@ static int test_errcode( ** Returns the UTF-8 representation of the error message string for the ** most recent sqlite3_* API call. */ -static int test_errmsg( +static int SQLITE_TCLAPI test_errmsg( void * clientData, Tcl_Interp *interp, int objc, @@ -3733,7 +3885,7 @@ static int test_errmsg( ** level, and it includes the 0x00 0x00 terminator bytes at the end of the ** UTF-16 string. */ -static int test_errmsg16( +static int SQLITE_TCLAPI test_errmsg16( void * clientData, Tcl_Interp *interp, int objc, @@ -3770,7 +3922,7 @@ static int test_errmsg16( ** variable that is set to the unused portion of (if any). A ** STMT handle is returned. */ -static int test_prepare( +static int SQLITE_TCLAPI test_prepare( void * clientData, Tcl_Interp *interp, int objc, @@ -3827,7 +3979,7 @@ static int test_prepare( ** variable that is set to the unused portion of (if any). A ** STMT handle is returned. */ -static int test_prepare_v2( +static int SQLITE_TCLAPI test_prepare_v2( void * clientData, Tcl_Interp *interp, int objc, @@ -3895,7 +4047,7 @@ static int test_prepare_v2( ** Generate a prepared statement for a zero-byte string as a test ** for ticket #3134. The string should be preceded by a zero byte. */ -static int test_prepare_tkt3134( +static int SQLITE_TCLAPI test_prepare_tkt3134( void * clientData, Tcl_Interp *interp, int objc, @@ -3938,7 +4090,7 @@ static int test_prepare_tkt3134( ** variable that is set to the unused portion of (if any). A ** STMT handle is returned. */ -static int test_prepare16( +static int SQLITE_TCLAPI test_prepare16( void * clientData, Tcl_Interp *interp, int objc, @@ -3998,7 +4150,7 @@ static int test_prepare16( ** variable that is set to the unused portion of (if any). A ** STMT handle is returned. */ -static int test_prepare16_v2( +static int SQLITE_TCLAPI test_prepare16_v2( void * clientData, Tcl_Interp *interp, int objc, @@ -4053,7 +4205,7 @@ static int test_prepare16_v2( /* ** Usage: sqlite3_open filename ?options-list? */ -static int test_open( +static int SQLITE_TCLAPI test_open( void * clientData, Tcl_Interp *interp, int objc, @@ -4080,7 +4232,7 @@ static int test_open( /* ** Usage: sqlite3_open_v2 FILENAME FLAGS VFS */ -static int test_open_v2( +static int SQLITE_TCLAPI test_open_v2( void * clientData, Tcl_Interp *interp, int objc, @@ -4150,7 +4302,7 @@ static int test_open_v2( /* ** Usage: sqlite3_open16 filename options */ -static int test_open16( +static int SQLITE_TCLAPI test_open16( void * clientData, Tcl_Interp *interp, int objc, @@ -4182,7 +4334,7 @@ static int test_open16( ** Return 1 if the supplied argument is a complete SQL statement, or zero ** otherwise. */ -static int test_complete16( +static int SQLITE_TCLAPI test_complete16( void * clientData, Tcl_Interp *interp, int objc, @@ -4207,7 +4359,7 @@ static int test_complete16( ** ** Advance the statement to the next row. */ -static int test_step( +static int SQLITE_TCLAPI test_step( void * clientData, Tcl_Interp *interp, int objc, @@ -4230,7 +4382,7 @@ static int test_step( return TCL_OK; } -static int test_sql( +static int SQLITE_TCLAPI test_sql( void * clientData, Tcl_Interp *interp, int objc, @@ -4247,13 +4399,33 @@ static int test_sql( Tcl_SetResult(interp, (char *)sqlite3_sql(pStmt), TCL_VOLATILE); return TCL_OK; } +static int SQLITE_TCLAPI test_ex_sql( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + char *z; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "STMT"); + return TCL_ERROR; + } + + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + z = sqlite3_expanded_sql(pStmt); + Tcl_SetResult(interp, z, TCL_VOLATILE); + sqlite3_free(z); + return TCL_OK; +} /* ** Usage: sqlite3_column_count STMT ** ** Return the number of columns returned by the sql statement STMT. */ -static int test_column_count( +static int SQLITE_TCLAPI test_column_count( void * clientData, Tcl_Interp *interp, int objc, @@ -4278,7 +4450,7 @@ static int test_column_count( ** ** Return the type of the data in column 'column' of the current row. */ -static int test_column_type( +static int SQLITE_TCLAPI test_column_type( void * clientData, Tcl_Interp *interp, int objc, @@ -4327,7 +4499,7 @@ static int test_column_type( ** Return the data in column 'column' of the current row cast as an ** wide (64-bit) integer. */ -static int test_column_int64( +static int SQLITE_TCLAPI test_column_int64( void * clientData, Tcl_Interp *interp, int objc, @@ -4354,7 +4526,7 @@ static int test_column_int64( /* ** Usage: sqlite3_column_blob STMT column */ -static int test_column_blob( +static int SQLITE_TCLAPI test_column_blob( void * clientData, Tcl_Interp *interp, int objc, @@ -4386,7 +4558,7 @@ static int test_column_blob( ** ** Return the data in column 'column' of the current row cast as a double. */ -static int test_column_double( +static int SQLITE_TCLAPI test_column_double( void * clientData, Tcl_Interp *interp, int objc, @@ -4415,7 +4587,7 @@ static int test_column_double( ** ** Return the number of columns returned by the sql statement STMT. */ -static int test_data_count( +static int SQLITE_TCLAPI test_data_count( void * clientData, Tcl_Interp *interp, int objc, @@ -4442,7 +4614,7 @@ static int test_data_count( ** ** Usage: sqlite3_column_name STMT column */ -static int test_stmt_utf8( +static int SQLITE_TCLAPI test_stmt_utf8( void * clientData, /* Pointer to SQLite API function to be invoke */ Tcl_Interp *interp, int objc, @@ -4469,7 +4641,7 @@ static int test_stmt_utf8( return TCL_OK; } -static int test_global_recover( +static int SQLITE_TCLAPI test_global_recover( void * clientData, Tcl_Interp *interp, int objc, @@ -4494,7 +4666,7 @@ static int test_global_recover( ** ** Usage: sqlite3_column_name STMT column */ -static int test_stmt_utf16( +static int SQLITE_TCLAPI test_stmt_utf16( void * clientData, /* Pointer to SQLite API function to be invoked */ Tcl_Interp *interp, int objc, @@ -4538,7 +4710,7 @@ static int test_stmt_utf16( ** Usage: sqlite3_column_bytes16 STMT column ** */ -static int test_stmt_int( +static int SQLITE_TCLAPI test_stmt_int( void * clientData, /* Pointer to SQLite API function to be invoked */ Tcl_Interp *interp, int objc, @@ -4567,7 +4739,7 @@ static int test_stmt_int( ** ** Set the db->magic value. This is used to test error recovery logic. */ -static int sqlite_set_magic( +static int SQLITE_TCLAPI sqlite_set_magic( void * clientData, Tcl_Interp *interp, int argc, @@ -4599,7 +4771,7 @@ static int sqlite_set_magic( ** ** Trigger an interrupt on DB */ -static int test_interrupt( +static int SQLITE_TCLAPI test_interrupt( void * clientData, Tcl_Interp *interp, int argc, @@ -4640,7 +4812,7 @@ u64 sqlite3StackDepth(void){ ** ** Try to measure the amount of stack space used by a call to sqlite3_exec */ -static int test_stack_used( +static int SQLITE_TCLAPI test_stack_used( void * clientData, Tcl_Interp *interp, int argc, @@ -4668,7 +4840,7 @@ static int test_stack_used( ** is assumed that the user function was created as UTF8, any number of ** arguments (the way the TCL interface does it). */ -static int delete_function( +static int SQLITE_TCLAPI delete_function( void * clientData, Tcl_Interp *interp, int argc, @@ -4694,7 +4866,7 @@ static int delete_function( ** DB. It is assumed that the collation sequence was created as UTF8 (the ** way the TCL interface does it). */ -static int delete_collation( +static int SQLITE_TCLAPI delete_collation( void * clientData, Tcl_Interp *interp, int argc, @@ -4719,7 +4891,7 @@ static int delete_collation( ** Return true if the database DB is currently in auto-commit mode. ** Return false if not. */ -static int get_autocommit( +static int SQLITE_TCLAPI get_autocommit( void * clientData, Tcl_Interp *interp, int argc, @@ -4745,7 +4917,7 @@ static int get_autocommit( ** method of the TCL interface. But we need a way to test the case ** where it returns SQLITE_MISUSE. */ -static int test_busy_timeout( +static int SQLITE_TCLAPI test_busy_timeout( void * clientData, Tcl_Interp *interp, int argc, @@ -4771,7 +4943,7 @@ static int test_busy_timeout( ** Return the name of the internal representation for the ** value of the given variable. */ -static int tcl_variable_type( +static int SQLITE_TCLAPI tcl_variable_type( void * clientData, Tcl_Interp *interp, int objc, @@ -4797,7 +4969,7 @@ static int tcl_variable_type( ** The integer N is the number of bytes we are trying to release. The ** return value is the amount of memory actually released. */ -static int test_release_memory( +static int SQLITE_TCLAPI test_release_memory( void * clientData, Tcl_Interp *interp, int objc, @@ -4828,7 +5000,7 @@ static int test_release_memory( ** Attempt to release memory currently held by database DB. Return the ** result code (which in the current implementation is always zero). */ -static int test_db_release_memory( +static int SQLITE_TCLAPI test_db_release_memory( void * clientData, Tcl_Interp *interp, int objc, @@ -4851,7 +5023,7 @@ static int test_db_release_memory( ** ** Attempt to flush any dirty pages to disk. */ -static int test_db_cacheflush( +static int SQLITE_TCLAPI test_db_cacheflush( void * clientData, Tcl_Interp *interp, int objc, @@ -4879,7 +5051,7 @@ static int test_db_cacheflush( ** ** Return the low-level system errno value. */ -static int test_system_errno( +static int SQLITE_TCLAPI test_system_errno( void * clientData, Tcl_Interp *interp, int objc, @@ -4902,7 +5074,7 @@ static int test_system_errno( ** ** Return the name of a file associated with a database. */ -static int test_db_filename( +static int SQLITE_TCLAPI test_db_filename( void * clientData, Tcl_Interp *interp, int objc, @@ -4926,7 +5098,7 @@ static int test_db_filename( ** Return 1 or 0 if DBNAME is readonly or not. Return -1 if DBNAME does ** not exist. */ -static int test_db_readonly( +static int SQLITE_TCLAPI test_db_readonly( void * clientData, Tcl_Interp *interp, int objc, @@ -4951,7 +5123,7 @@ static int test_db_readonly( ** limit is only changed if the N is present. The previous limit ** is returned. */ -static int test_soft_heap_limit( +static int SQLITE_TCLAPI test_soft_heap_limit( void * clientData, Tcl_Interp *interp, int objc, @@ -4976,7 +5148,7 @@ static int test_soft_heap_limit( ** ** Call the sqlite3_thread_cleanup API. */ -static int test_thread_cleanup( +static int SQLITE_TCLAPI test_thread_cleanup( void * clientData, Tcl_Interp *interp, int objc, @@ -4994,7 +5166,7 @@ static int test_thread_cleanup( ** Return a list of numbers which are the PagerRefcount for all ** pagers on each database connection. */ -static int test_pager_refcounts( +static int SQLITE_TCLAPI test_pager_refcounts( void * clientData, Tcl_Interp *interp, int objc, @@ -5040,7 +5212,7 @@ static int test_pager_refcounts( ** and that the errors they are seeing in the test scripts might be ** a result of their defective TCL rather than problems in SQLite. */ -static int working_64bit_int( +static int SQLITE_TCLAPI working_64bit_int( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5065,7 +5237,7 @@ static int working_64bit_int( ** VFS when none are previously registered, and the ability to ** unregister the only available VFS. Ticket #2738 */ -static int vfs_unlink_test( +static int SQLITE_TCLAPI vfs_unlink_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5167,7 +5339,7 @@ static int vfs_unlink_test( ** This TCL command attempts to vfs_find and vfs_register when the ** sqlite3_initialize() interface is failing. All calls should fail. */ -static int vfs_initfail_test( +static int SQLITE_TCLAPI vfs_initfail_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5195,7 +5367,7 @@ static int nVfs = 0; ** ** Unregister all VFSes. */ -static int vfs_unregister_all( +static int SQLITE_TCLAPI vfs_unregister_all( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5217,7 +5389,7 @@ static int vfs_unregister_all( ** care to put the linked list back together in the same order as it was ** in before vfs_unregister_all was invoked. */ -static int vfs_reregister_all( +static int SQLITE_TCLAPI vfs_reregister_all( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5237,7 +5409,7 @@ static int vfs_reregister_all( ** This TCL command runs the sqlite3_file_control interface and ** verifies correct operation of the same. */ -static int file_control_test( +static int SQLITE_TCLAPI file_control_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5272,7 +5444,7 @@ static int file_control_test( ** This TCL command runs the sqlite3_file_control interface and ** verifies correct operation of the SQLITE_LAST_ERRNO verb. */ -static int file_control_lasterrno_test( +static int SQLITE_TCLAPI file_control_lasterrno_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5310,7 +5482,7 @@ static int file_control_lasterrno_test( ** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and ** SQLITE_SET_LOCKPROXYFILE verbs. */ -static int file_control_chunksize_test( +static int SQLITE_TCLAPI file_control_chunksize_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5347,7 +5519,7 @@ static int file_control_chunksize_test( ** This TCL command runs the sqlite3_file_control interface ** with SQLITE_FCNTL_SIZE_HINT */ -static int file_control_sizehint_test( +static int SQLITE_TCLAPI file_control_sizehint_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5385,7 +5557,7 @@ static int file_control_sizehint_test( ** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and ** SQLITE_SET_LOCKPROXYFILE verbs. */ -static int file_control_lockproxy_test( +static int SQLITE_TCLAPI file_control_lockproxy_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5455,7 +5627,7 @@ static int file_control_lockproxy_test( ** This TCL command runs the sqlite3_file_control interface with ** the SQLITE_FCNTL_WIN32_AV_RETRY opcode. */ -static int file_control_win32_av_retry( +static int SQLITE_TCLAPI file_control_win32_av_retry( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5488,7 +5660,7 @@ static int file_control_win32_av_retry( ** This TCL command runs the sqlite3_file_control interface with ** the SQLITE_FCNTL_WIN32_SET_HANDLE opcode. */ -static int file_control_win32_set_handle( +static int SQLITE_TCLAPI file_control_win32_set_handle( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5524,7 +5696,7 @@ static int file_control_win32_set_handle( ** This TCL command runs the sqlite3_file_control interface with ** the SQLITE_FCNTL_PERSIST_WAL opcode. */ -static int file_control_persist_wal( +static int SQLITE_TCLAPI file_control_persist_wal( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5556,7 +5728,7 @@ static int file_control_persist_wal( ** This TCL command runs the sqlite3_file_control interface with ** the SQLITE_FCNTL_POWERSAFE_OVERWRITE opcode. */ -static int file_control_powersafe_overwrite( +static int SQLITE_TCLAPI file_control_powersafe_overwrite( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5588,7 +5760,7 @@ static int file_control_powersafe_overwrite( ** ** Return a string that describes the stack of VFSes. */ -static int file_control_vfsname( +static int SQLITE_TCLAPI file_control_vfsname( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5620,7 +5792,7 @@ static int file_control_vfsname( ** ** Return a string that is a temporary filename */ -static int file_control_tempfilename( +static int SQLITE_TCLAPI file_control_tempfilename( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5653,7 +5825,7 @@ static int file_control_tempfilename( ** ** Return a tcl list containing the names of all registered vfs's. */ -static int vfs_list( +static int SQLITE_TCLAPI vfs_list( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5678,7 +5850,7 @@ static int vfs_list( ** This TCL command runs the sqlite3_limit interface and ** verifies correct operation of the same. */ -static int test_limit( +static int SQLITE_TCLAPI test_limit( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5741,7 +5913,7 @@ static int test_limit( ** At the same time, verify that sqlite3_test_control works even when ** called with an out-of-range opcode. */ -static int save_prng_state( +static int SQLITE_TCLAPI save_prng_state( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5757,7 +5929,7 @@ static int save_prng_state( /* ** tclcmd: restore_prng_state */ -static int restore_prng_state( +static int SQLITE_TCLAPI restore_prng_state( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5769,7 +5941,7 @@ static int restore_prng_state( /* ** tclcmd: reset_prng_state */ -static int reset_prng_state( +static int SQLITE_TCLAPI reset_prng_state( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5785,7 +5957,7 @@ static int reset_prng_state( ** Indicate that database files might be corrupt. In other words, set the normal ** state of operation. */ -static int database_may_be_corrupt( +static int SQLITE_TCLAPI database_may_be_corrupt( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5800,7 +5972,7 @@ static int database_may_be_corrupt( ** Indicate that database files are always well-formed. This enables extra assert() ** statements that test conditions that are always true for well-formed databases. */ -static int database_never_corrupt( +static int SQLITE_TCLAPI database_never_corrupt( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5813,7 +5985,7 @@ static int database_never_corrupt( /* ** tclcmd: pcache_stats */ -static int test_pcache_stats( +static int SQLITE_TCLAPI test_pcache_stats( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5855,7 +6027,7 @@ static void test_unlock_notify_cb(void **aArg, int nArg){ ** tclcmd: sqlite3_unlock_notify db */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY -static int test_unlock_notify( +static int SQLITE_TCLAPI test_unlock_notify( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5881,7 +6053,7 @@ static int test_unlock_notify( /* ** tclcmd: sqlite3_wal_checkpoint db ?NAME? */ -static int test_wal_checkpoint( +static int SQLITE_TCLAPI test_wal_checkpoint( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5925,7 +6097,7 @@ static int test_wal_checkpoint( ** the number of frames in the log and the number of frames in the log ** that have been checkpointed. */ -static int test_wal_checkpoint_v2( +static int SQLITE_TCLAPI test_wal_checkpoint_v2( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -5981,7 +6153,7 @@ static int test_wal_checkpoint_v2( /* ** tclcmd: sqlite3_wal_autocheckpoint db VALUE */ -static int test_wal_autocheckpoint( +static int SQLITE_TCLAPI test_wal_autocheckpoint( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6032,7 +6204,7 @@ static void xLogcallback(void *unused, int err, char *zMsg){ Tcl_EvalObjEx(logcallback.pInterp, pNew, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT); Tcl_DecrRefCount(pNew); } -static int test_sqlite3_log( +static int SQLITE_TCLAPI test_sqlite3_log( ClientData clientData, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6063,7 +6235,7 @@ static int test_sqlite3_log( ** Run a TCL command using its objProc interface. Throw an error if ** the command has no objProc interface. */ -static int runAsObjProc( +static int SQLITE_TCLAPI runAsObjProc( void * clientData, Tcl_Interp *interp, int objc, @@ -6126,7 +6298,7 @@ int printExplainQueryPlan(sqlite3_stmt *pStmt){ return sqlite3_finalize(pExplain); } -static int test_print_eqp( +static int SQLITE_TCLAPI test_print_eqp( void * clientData, Tcl_Interp *interp, int objc, @@ -6154,7 +6326,7 @@ static int test_print_eqp( /* ** sqlite3_test_control VERB ARGS... */ -static int test_test_control( +static int SQLITE_TCLAPI test_test_control( void * clientData, Tcl_Interp *interp, int objc, @@ -6233,7 +6405,7 @@ static int test_test_control( #include #include -static int test_getrusage( +static int SQLITE_TCLAPI test_getrusage( void * clientData, Tcl_Interp *interp, int objc, @@ -6276,7 +6448,7 @@ struct win32FileLocker { /* ** The background thread that does file locking. */ -static void win32_file_locker(void *pAppData){ +static void SQLITE_CDECL win32_file_locker(void *pAppData){ struct win32FileLocker *p = (struct win32FileLocker*)pAppData; if( p->evName ){ HANDLE ev = OpenEvent(EVENT_MODIFY_STATE, FALSE, p->evName); @@ -6307,7 +6479,7 @@ static void win32_file_locker(void *pAppData){ ** Get an exclusive manditory lock on file for DELAY2 milliseconds. ** Wait DELAY1 milliseconds before acquiring the lock. */ -static int win32_file_lock( +static int SQLITE_TCLAPI win32_file_lock( void * clientData, Tcl_Interp *interp, int objc, @@ -6371,7 +6543,7 @@ static int win32_file_lock( ** Returns non-zero if the specified path exists, whose fully qualified name ** may exceed 260 characters if it is prefixed with "\\?\". */ -static int win32_exists_path( +static int SQLITE_TCLAPI win32_exists_path( void *clientData, Tcl_Interp *interp, int objc, @@ -6393,7 +6565,7 @@ static int win32_exists_path( ** whose fully qualified name may exceed 248 characters if it is prefixed with ** "\\?\". */ -static int win32_find_file( +static int SQLITE_TCLAPI win32_find_file( void *clientData, Tcl_Interp *interp, int objc, @@ -6438,7 +6610,7 @@ static int win32_find_file( ** Deletes the specified file, whose fully qualified name may exceed 260 ** characters if it is prefixed with "\\?\". */ -static int win32_delete_file( +static int SQLITE_TCLAPI win32_delete_file( void *clientData, Tcl_Interp *interp, int objc, @@ -6462,7 +6634,7 @@ static int win32_delete_file( ** Creates the specified directory, whose fully qualified name may exceed 248 ** characters if it is prefixed with "\\?\". */ -static int win32_mkdir( +static int SQLITE_TCLAPI win32_mkdir( void *clientData, Tcl_Interp *interp, int objc, @@ -6486,7 +6658,7 @@ static int win32_mkdir( ** Removes the specified directory, whose fully qualified name may exceed 248 ** characters if it is prefixed with "\\?\". */ -static int win32_rmdir( +static int SQLITE_TCLAPI win32_rmdir( void *clientData, Tcl_Interp *interp, int objc, @@ -6513,7 +6685,7 @@ static int win32_rmdir( ** interface. Disable if BOOLEAN is false and enable if BOOLEAN is true. ** OPT is the name of the optimization to be disabled. */ -static int optimization_control( +static int SQLITE_TCLAPI optimization_control( void * clientData, Tcl_Interp *interp, int objc, @@ -6570,20 +6742,21 @@ static int optimization_control( return TCL_OK; } -typedef struct sqlite3_api_routines sqlite3_api_routines; /* ** load_static_extension DB NAME ... ** ** Load one or more statically linked extensions. */ -static int tclLoadStaticExtensionCmd( +static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_carray_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_csv_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); @@ -6600,7 +6773,9 @@ static int tclLoadStaticExtensionCmd( int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, + { "carray", sqlite3_carray_init }, { "closure", sqlite3_closure_init }, + { "csv", sqlite3_csv_init }, { "eval", sqlite3_eval_init }, { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, @@ -6650,7 +6825,7 @@ static int tclLoadStaticExtensionCmd( ** sorter_test_fakeheap BOOL ** */ -static int sorter_test_fakeheap( +static int SQLITE_TCLAPI sorter_test_fakeheap( void * clientData, Tcl_Interp *interp, int objc, @@ -6690,7 +6865,7 @@ static int sorter_test_fakeheap( ** Then execute statement $SQL2. Check that the statement returns the same ** set of integers in the same order as in the previous step (using $SQL1). */ -static int sorter_test_sort4_helper( +static int SQLITE_TCLAPI sorter_test_sort4_helper( void * clientData, Tcl_Interp *interp, int objc, @@ -6700,8 +6875,8 @@ static int sorter_test_sort4_helper( const char *zSql2; int nStep; int iStep; - int iCksum1 = 0; - int iCksum2 = 0; + unsigned int iCksum1 = 0; + unsigned int iCksum2 = 0; int rc; int iB; sqlite3 *db; @@ -6728,7 +6903,7 @@ static int sorter_test_sort4_helper( return TCL_ERROR; } - iCksum1 += (iCksum1 << 3) + a; + iCksum1 += (iCksum1 << 3) + (unsigned int)a; } rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ) goto sql_error; @@ -6737,7 +6912,7 @@ static int sorter_test_sort4_helper( if( rc!=SQLITE_OK ) goto sql_error; for(iStep=0; SQLITE_ROW==sqlite3_step(pStmt); iStep++){ int a = sqlite3_column_int(pStmt, 0); - iCksum2 += (iCksum2 << 3) + a; + iCksum2 += (iCksum2 << 3) + (unsigned int)a; } rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ) goto sql_error; @@ -6759,7 +6934,7 @@ static int sorter_test_sort4_helper( /* ** tclcmd: sqlite3_user_authenticate DB USERNAME PASSWORD */ -static int test_user_authenticate( +static int SQLITE_TCLAPI test_user_authenticate( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6790,7 +6965,7 @@ static int test_user_authenticate( /* ** tclcmd: sqlite3_user_add DB USERNAME PASSWORD ISADMIN */ -static int test_user_add( +static int SQLITE_TCLAPI test_user_add( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6823,7 +6998,7 @@ static int test_user_add( /* ** tclcmd: sqlite3_user_change DB USERNAME PASSWORD ISADMIN */ -static int test_user_change( +static int SQLITE_TCLAPI test_user_change( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6856,7 +7031,7 @@ static int test_user_change( /* ** tclcmd: sqlite3_user_delete DB USERNAME */ -static int test_user_delete( +static int SQLITE_TCLAPI test_user_delete( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6893,7 +7068,7 @@ static int test_user_delete( ** 3 Read after free ** 4 Panic */ -static int test_bad_behavior( +static int SQLITE_TCLAPI test_bad_behavior( ClientData clientData, /* Pointer to an integer containing zero */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6943,7 +7118,7 @@ static int test_bad_behavior( ** ** Cause the dbstat virtual table to be available on the connection DB */ -static int test_register_dbstat_vtab( +static int SQLITE_TCLAPI test_register_dbstat_vtab( void *clientData, Tcl_Interp *interp, int objc, @@ -6977,7 +7152,7 @@ static int test_register_dbstat_vtab( ** ** Invoke sqlite3_db_config() for one of the setting values. */ -static int test_sqlite3_db_config( +static int SQLITE_TCLAPI test_sqlite3_db_config( void *clientData, Tcl_Interp *interp, int objc, @@ -7034,7 +7209,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_hostid_num; #endif extern int sqlite3_max_blobsize; - extern int sqlite3BtreeSharedCacheReport(void*, + extern int SQLITE_TCLAPI sqlite3BtreeSharedCacheReport(void*, Tcl_Interp*,int,Tcl_Obj*CONST*); static int iZero = 0; static struct { @@ -7093,6 +7268,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "register_dbstat_vtab", test_register_dbstat_vtab }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, + { "intarray_addr", test_intarray_addr, 0 }, + { "int64array_addr", test_int64array_addr, 0 }, + { "doublearray_addr", test_doublearray_addr, 0 }, + { "textarray_addr", test_textarray_addr, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, { "sqlite3_bind_zeroblob64", test_bind_zeroblob64, 0 }, @@ -7129,6 +7308,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_changes", test_changes ,0 }, { "sqlite3_step", test_step ,0 }, { "sqlite3_sql", test_sql ,0 }, + { "sqlite3_expanded_sql", test_ex_sql ,0 }, { "sqlite3_next_stmt", test_next_stmt ,0 }, { "sqlite3_stmt_readonly", test_stmt_readonly ,0 }, { "sqlite3_stmt_busy", test_stmt_busy ,0 }, diff --git a/src/test2.c b/src/test2.c index d16ba5016a..dfca1d2b31 100644 --- a/src/test2.c +++ b/src/test2.c @@ -14,7 +14,11 @@ ** testing of the SQLite library. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include #include @@ -38,7 +42,7 @@ static void pager_test_reiniter(DbPage *pNotUsed){ ** ** Open a new pager */ -static int pager_open( +static int SQLITE_TCLAPI pager_open( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -75,7 +79,7 @@ static int pager_open( ** ** Close the given pager. */ -static int pager_close( +static int SQLITE_TCLAPI pager_close( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -102,7 +106,7 @@ static int pager_close( ** ** Rollback changes */ -static int pager_rollback( +static int SQLITE_TCLAPI pager_rollback( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -129,7 +133,7 @@ static int pager_rollback( ** ** Commit all changes */ -static int pager_commit( +static int SQLITE_TCLAPI pager_commit( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -161,7 +165,7 @@ static int pager_commit( ** ** Start a new checkpoint. */ -static int pager_stmt_begin( +static int SQLITE_TCLAPI pager_stmt_begin( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -188,7 +192,7 @@ static int pager_stmt_begin( ** ** Rollback changes to a checkpoint */ -static int pager_stmt_rollback( +static int SQLITE_TCLAPI pager_stmt_rollback( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -216,7 +220,7 @@ static int pager_stmt_rollback( ** ** Commit changes to a checkpoint */ -static int pager_stmt_commit( +static int SQLITE_TCLAPI pager_stmt_commit( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -243,7 +247,7 @@ static int pager_stmt_commit( ** ** Return pager statistics. */ -static int pager_stats( +static int SQLITE_TCLAPI pager_stats( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -276,7 +280,7 @@ static int pager_stats( ** ** Return the size of the database file. */ -static int pager_pagecount( +static int SQLITE_TCLAPI pager_pagecount( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -302,7 +306,7 @@ static int pager_pagecount( ** ** Return a pointer to a page from the database. */ -static int page_get( +static int SQLITE_TCLAPI page_get( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -339,7 +343,7 @@ static int page_get( ** Return a pointer to a page if the page is already in cache. ** If not in cache, return an empty string. */ -static int page_lookup( +static int SQLITE_TCLAPI page_lookup( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -367,7 +371,7 @@ static int page_lookup( /* ** Usage: pager_truncate ID PGNO */ -static int pager_truncate( +static int SQLITE_TCLAPI pager_truncate( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -392,7 +396,7 @@ static int pager_truncate( ** ** Drop a pointer to a page. */ -static int page_unref( +static int SQLITE_TCLAPI page_unref( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -414,7 +418,7 @@ static int page_unref( ** ** Return the content of a page */ -static int page_read( +static int SQLITE_TCLAPI page_read( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -438,7 +442,7 @@ static int page_read( ** ** Return the page number for a page. */ -static int page_number( +static int SQLITE_TCLAPI page_number( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -462,7 +466,7 @@ static int page_number( ** ** Write something into a page. */ -static int page_write( +static int SQLITE_TCLAPI page_write( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -498,7 +502,7 @@ static int page_write( ** new pages after N. If N is 2096 or bigger, this will test the ** ability of SQLite to write to large files. */ -static int fake_big_file( +static int SQLITE_TCLAPI fake_big_file( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -551,7 +555,7 @@ static int fake_big_file( ** ** Set the PENDING_BYTE using the sqlite3_test_control() interface. */ -static int testPendingByte( +static int SQLITE_TCLAPI testPendingByte( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -616,7 +620,7 @@ static int faultSimCallback(int x){ ** appended, whenever sqlite3FaultSim() is called. Or, if SCRIPT is the ** empty string, cancel the sqlite3FaultSim() callback. */ -static int faultInstallCmd( +static int SQLITE_TCLAPI faultInstallCmd( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -659,7 +663,7 @@ static int faultInstallCmd( ** Invoke the SQLITE_TESTCTRL_BITVEC_TEST operator on test_control. ** See comments on sqlite3BitvecBuiltinTest() for additional information. */ -static int testBitvecBuiltinTest( +static int SQLITE_TCLAPI testBitvecBuiltinTest( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ diff --git a/src/test3.c b/src/test3.c index 817e8a88fd..6995684c29 100644 --- a/src/test3.c +++ b/src/test3.c @@ -15,7 +15,11 @@ */ #include "sqliteInt.h" #include "btreeInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include @@ -33,7 +37,7 @@ static int nRefSqlite3 = 0; ** ** Open a new database */ -static int btree_open( +static int SQLITE_TCLAPI btree_open( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -79,7 +83,7 @@ static int btree_open( ** ** Close the given database. */ -static int btree_close( +static int SQLITE_TCLAPI btree_close( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -114,7 +118,7 @@ static int btree_close( ** ** Start a new transaction */ -static int btree_begin_transaction( +static int SQLITE_TCLAPI btree_begin_transaction( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -143,7 +147,7 @@ static int btree_begin_transaction( ** ** Returns pager statistics */ -static int btree_pager_stats( +static int SQLITE_TCLAPI btree_pager_stats( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -193,7 +197,7 @@ static int btree_pager_stats( ** ** Create a new cursor. Return the ID for the cursor. */ -static int btree_cursor( +static int SQLITE_TCLAPI btree_cursor( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -242,7 +246,7 @@ static int btree_cursor( ** ** Close a cursor opened using btree_cursor. */ -static int btree_close_cursor( +static int SQLITE_TCLAPI btree_close_cursor( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -279,7 +283,7 @@ static int btree_close_cursor( ** or 1 if the cursor was already on the last entry in the table or if ** the table is empty. */ -static int btree_next( +static int SQLITE_TCLAPI btree_next( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -314,7 +318,7 @@ static int btree_next( ** Move the cursor to the first entry in the table. Return 0 if the ** cursor was left point to something and 1 if the table is empty. */ -static int btree_first( +static int SQLITE_TCLAPI btree_first( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -349,7 +353,7 @@ static int btree_first( ** Return TRUE if the given cursor is not pointing at a valid entry. ** Return FALSE if the cursor does point to a valid entry. */ -static int btree_eof( +static int SQLITE_TCLAPI btree_eof( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -378,15 +382,14 @@ static int btree_eof( ** ** Return the number of bytes of payload */ -static int btree_payload_size( +static int SQLITE_TCLAPI btree_payload_size( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ BtCursor *pCur; - int n2; - u64 n1; + u32 n; char zBuf[50]; if( argc!=2 ){ @@ -396,17 +399,9 @@ static int btree_payload_size( } pCur = sqlite3TestTextToPtr(argv[1]); sqlite3BtreeEnter(pCur->pBtree); - - /* The cursor may be in "require-seek" state. If this is the case, the - ** call to BtreeDataSize() will fix it. */ - sqlite3BtreeDataSize(pCur, (u32*)&n2); - if( pCur->apPage[pCur->iPage]->intKey ){ - n1 = 0; - }else{ - sqlite3BtreeKeySize(pCur, (i64*)&n1); - } + n = sqlite3BtreePayloadSize(pCur); sqlite3BtreeLeave(pCur->pBtree); - sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); + sqlite3_snprintf(sizeof(zBuf),zBuf, "%u", n); Tcl_AppendResult(interp, zBuf, 0); return SQLITE_OK; } @@ -425,7 +420,7 @@ static int btree_payload_size( ** This command returns nothing if it works. It returns an error message ** if something goes wrong. */ -static int btree_varint_test( +static int SQLITE_TCLAPI btree_varint_test( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -509,7 +504,7 @@ static int btree_varint_test( ** sqlite3 db test.db ** set bt [btree_from_db db] */ -static int btree_from_db( +static int SQLITE_TCLAPI btree_from_db( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -549,7 +544,7 @@ static int btree_from_db( ** ** Return true if the B-Tree is currently stored entirely in memory. */ -static int btree_ismemdb( +static int SQLITE_TCLAPI btree_ismemdb( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -580,7 +575,7 @@ static int btree_ismemdb( ** ** Set the size of the cache used by btree $ID. */ -static int btree_set_cache_size( +static int SQLITE_TCLAPI btree_set_cache_size( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -610,7 +605,7 @@ static int btree_set_cache_size( ** ** Set the size of the cache used by btree $ID. */ -static int btree_insert( +static int SQLITE_TCLAPI btree_insert( ClientData clientData, Tcl_Interp *interp, int objc, @@ -618,27 +613,27 @@ static int btree_insert( ){ BtCursor *pCur; int rc; - void *pKey = 0; - int nKey = 0; - void *pData = 0; - int nData = 0; + BtreePayload x; if( objc!=4 && objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "?-intkey? CSR KEY VALUE"); return TCL_ERROR; } + memset(&x, 0, sizeof(x)); if( objc==4 ){ - if( Tcl_GetIntFromObj(interp, objv[2], &nKey) ) return TCL_ERROR; - pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &nData); + if( Tcl_GetIntFromObj(interp, objv[2], &rc) ) return TCL_ERROR; + x.nKey = rc; + x.pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &x.nData); }else{ - pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &nKey); + x.pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &rc); + x.nKey = rc; } pCur = (BtCursor*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); sqlite3_mutex_enter(pCur->pBtree->db->mutex); sqlite3BtreeEnter(pCur->pBtree); - rc = sqlite3BtreeInsert(pCur, pKey, nKey, pData, nData, 0, 0, 0); + rc = sqlite3BtreeInsert(pCur, &x, 0, 0); sqlite3BtreeLeave(pCur->pBtree); sqlite3_mutex_leave(pCur->pBtree->db->mutex); diff --git a/src/test4.c b/src/test4.c index d689030303..c5b5ec12cc 100644 --- a/src/test4.c +++ b/src/test4.c @@ -12,7 +12,11 @@ ** Code for testing the SQLite library in a multithreaded environment. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #if SQLITE_OS_UNIX && SQLITE_THREADSAFE #include #include @@ -121,7 +125,7 @@ static int parse_thread_id(Tcl_Interp *interp, const char *zArg){ ** NAME should be an upper case letter. Start the thread running with ** an open connection to the given database. */ -static int tcl_thread_create( +static int SQLITE_TCLAPI tcl_thread_create( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -170,7 +174,7 @@ static void thread_wait(Thread *p){ ** ** Wait on thread ID to reach its idle state. */ -static int tcl_thread_wait( +static int SQLITE_TCLAPI tcl_thread_wait( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -214,7 +218,7 @@ static void stop_thread(Thread *p){ ** Cause a thread to shut itself down. Wait for the shutdown to be ** completed. If ID is "*" then stop all threads. */ -static int tcl_thread_halt( +static int SQLITE_TCLAPI tcl_thread_halt( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -249,7 +253,7 @@ static int tcl_thread_halt( ** Wait on the most recent thread_step to complete, then return the ** number of columns in the result set. */ -static int tcl_thread_argc( +static int SQLITE_TCLAPI tcl_thread_argc( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -281,7 +285,7 @@ static int tcl_thread_argc( ** Wait on the most recent thread_step to complete, then return the ** value of the N-th columns in the result set. */ -static int tcl_thread_argv( +static int SQLITE_TCLAPI tcl_thread_argv( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -317,7 +321,7 @@ static int tcl_thread_argv( ** Wait on the most recent thread_step to complete, then return the ** name of the N-th columns in the result set. */ -static int tcl_thread_colname( +static int SQLITE_TCLAPI tcl_thread_colname( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -353,7 +357,7 @@ static int tcl_thread_colname( ** Wait on the most recent operation to complete, then return the ** result code from that operation. */ -static int tcl_thread_result( +static int SQLITE_TCLAPI tcl_thread_result( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -385,7 +389,7 @@ static int tcl_thread_result( ** Wait on the most recent operation to complete, then return the ** error string. */ -static int tcl_thread_error( +static int SQLITE_TCLAPI tcl_thread_error( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -430,7 +434,7 @@ static void do_compile(Thread *p){ ** ** Compile a new virtual machine. */ -static int tcl_thread_compile( +static int SQLITE_TCLAPI tcl_thread_compile( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -483,7 +487,7 @@ static void do_step(Thread *p){ ** ** Advance the virtual machine by one step */ -static int tcl_thread_step( +static int SQLITE_TCLAPI tcl_thread_step( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -525,7 +529,7 @@ static void do_finalize(Thread *p){ ** ** Finalize the virtual machine. */ -static int tcl_thread_finalize( +static int SQLITE_TCLAPI tcl_thread_finalize( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -556,7 +560,7 @@ static int tcl_thread_finalize( ** ** Interchange the sqlite* pointer between two threads. */ -static int tcl_thread_swap( +static int SQLITE_TCLAPI tcl_thread_swap( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -596,7 +600,7 @@ static int tcl_thread_swap( ** remove the pointer from the thread itself. Afterwards, the thread ** can be stopped and the connection can be used by the main thread. */ -static int tcl_thread_db_get( +static int SQLITE_TCLAPI tcl_thread_db_get( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -627,7 +631,7 @@ static int tcl_thread_db_get( ** Usage: thread_db_put ID DB ** */ -static int tcl_thread_db_put( +static int SQLITE_TCLAPI tcl_thread_db_put( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -659,7 +663,7 @@ static int tcl_thread_db_put( ** Return the database stmt pointer for the given thread. Then ** remove the pointer from the thread itself. */ -static int tcl_thread_stmt_get( +static int SQLITE_TCLAPI tcl_thread_stmt_get( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ diff --git a/src/test5.c b/src/test5.c index 952e3325e5..0d9242862b 100644 --- a/src/test5.c +++ b/src/test5.c @@ -17,7 +17,11 @@ */ #include "sqliteInt.h" #include "vdbeInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include @@ -26,7 +30,7 @@ ** object with the encoded representation of the string, including ** the NULL terminator. */ -static int binarize( +static int SQLITE_TCLAPI binarize( void * clientData, Tcl_Interp *interp, int objc, @@ -54,7 +58,7 @@ static int binarize( ** If is 0, then the calls to sqlite3_value_text() are not ** actually made. */ -static int test_value_overhead( +static int SQLITE_TCLAPI test_value_overhead( void * clientData, Tcl_Interp *interp, int objc, @@ -118,7 +122,7 @@ static u8 name_to_enc(Tcl_Interp *interp, Tcl_Obj *pObj){ ** Usage: test_translate ?? ** */ -static int test_translate( +static int SQLITE_TCLAPI test_translate( void * clientData, Tcl_Interp *interp, int objc, @@ -182,7 +186,7 @@ static int test_translate( ** translation. If there is a problem an assert() will fail. **/ void sqlite3UtfSelfTest(void); -static int test_translate_selftest( +static int SQLITE_TCLAPI test_translate_selftest( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test6.c b/src/test6.c index 2a09122c6c..5304bcc31f 100644 --- a/src/test6.c +++ b/src/test6.c @@ -16,7 +16,11 @@ */ #if SQLITE_TEST /* This file is used for testing only */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */ @@ -215,7 +219,9 @@ static int writeListSync(CrashFile *pFile, int isCrash){ } #ifdef TRACE_CRASHTEST - printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a")); + if( pFile ){ + printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a")); + } #endif ppPtr = &g.pWriteList; @@ -799,13 +805,34 @@ static int processDevSymArgs( return TCL_OK; } +/* +** tclcmd: sqlite3_crash_now +** +** Simulate a crash immediately. This function does not return +** (writeListSync() calls exit(-1)). +*/ +static int SQLITE_TCLAPI crashNowCmd( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + if( objc!=1 ){ + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + writeListSync(0, 1); + assert( 0 ); + return TCL_OK; +} + /* ** tclcmd: sqlite_crash_enable ENABLE ** ** Parameter ENABLE must be a boolean value. If true, then the "crash" ** vfs is added to the system. If false, it is removed. */ -static int crashEnableCmd( +static int SQLITE_TCLAPI crashEnableCmd( void * clientData, Tcl_Interp *interp, int objc, @@ -880,7 +907,7 @@ static int crashEnableCmd( ** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1 ** */ -static int crashParamsObjCmd( +static int SQLITE_TCLAPI crashParamsObjCmd( void * clientData, Tcl_Interp *interp, int objc, @@ -927,7 +954,7 @@ error: return TCL_ERROR; } -static int devSymObjCmd( +static int SQLITE_TCLAPI devSymObjCmd( void * clientData, Tcl_Interp *interp, int objc, @@ -950,7 +977,7 @@ static int devSymObjCmd( /* ** tclcmd: unregister_devsim */ -static int dsUnregisterObjCmd( +static int SQLITE_TCLAPI dsUnregisterObjCmd( void * clientData, Tcl_Interp *interp, int objc, @@ -970,7 +997,7 @@ static int dsUnregisterObjCmd( /* ** tclcmd: register_jt_vfs ?-default? PARENT-VFS */ -static int jtObjCmd( +static int SQLITE_TCLAPI jtObjCmd( void * clientData, Tcl_Interp *interp, int objc, @@ -1008,7 +1035,7 @@ static int jtObjCmd( /* ** tclcmd: unregister_jt_vfs */ -static int jtUnregisterObjCmd( +static int SQLITE_TCLAPI jtUnregisterObjCmd( void * clientData, Tcl_Interp *interp, int objc, @@ -1034,6 +1061,7 @@ int Sqlitetest6_Init(Tcl_Interp *interp){ #ifndef SQLITE_OMIT_DISKIO Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0); + Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0); Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0); diff --git a/src/test7.c b/src/test7.c index 6ba3631b41..d57e4b826b 100644 --- a/src/test7.c +++ b/src/test7.c @@ -13,7 +13,11 @@ ** Derived from test4.c. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif /* ** This test only works on UNIX with a SQLITE_THREADSAFE build that includes @@ -149,7 +153,7 @@ static int parse_client_id(Tcl_Interp *interp, const char *zArg){ ** NAME should be an upper case letter. Start the thread running with ** an open connection to the given database. */ -static int tcl_client_create( +static int SQLITE_TCLAPI tcl_client_create( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -202,7 +206,7 @@ static void client_wait(Thread *p){ ** ** Wait on thread ID to reach its idle state. */ -static int tcl_client_wait( +static int SQLITE_TCLAPI tcl_client_wait( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -246,7 +250,7 @@ static void stop_thread(Thread *p){ ** Cause a client thread to shut itself down. Wait for the shutdown to be ** completed. If ID is "*" then stop all client threads. */ -static int tcl_client_halt( +static int SQLITE_TCLAPI tcl_client_halt( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -294,7 +298,7 @@ static int tcl_client_halt( ** Wait on the most recent client_step to complete, then return the ** number of columns in the result set. */ -static int tcl_client_argc( +static int SQLITE_TCLAPI tcl_client_argc( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -326,7 +330,7 @@ static int tcl_client_argc( ** Wait on the most recent client_step to complete, then return the ** value of the N-th columns in the result set. */ -static int tcl_client_argv( +static int SQLITE_TCLAPI tcl_client_argv( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -362,7 +366,7 @@ static int tcl_client_argv( ** Wait on the most recent client_step to complete, then return the ** name of the N-th columns in the result set. */ -static int tcl_client_colname( +static int SQLITE_TCLAPI tcl_client_colname( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -400,7 +404,7 @@ extern const char *sqlite3ErrName(int); ** Wait on the most recent operation to complete, then return the ** result code from that operation. */ -static int tcl_client_result( +static int SQLITE_TCLAPI tcl_client_result( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -432,7 +436,7 @@ static int tcl_client_result( ** Wait on the most recent operation to complete, then return the ** error string. */ -static int tcl_client_error( +static int SQLITE_TCLAPI tcl_client_error( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -477,7 +481,7 @@ static void do_compile(Thread *p){ ** ** Compile a new virtual machine. */ -static int tcl_client_compile( +static int SQLITE_TCLAPI tcl_client_compile( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -530,7 +534,7 @@ static void do_step(Thread *p){ ** ** Advance the virtual machine by one step */ -static int tcl_client_step( +static int SQLITE_TCLAPI tcl_client_step( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -572,7 +576,7 @@ static void do_finalize(Thread *p){ ** ** Finalize the virtual machine. */ -static int tcl_client_finalize( +static int SQLITE_TCLAPI tcl_client_finalize( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -616,7 +620,7 @@ static void do_reset(Thread *p){ ** ** Finalize the virtual machine. */ -static int tcl_client_reset( +static int SQLITE_TCLAPI tcl_client_reset( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -647,7 +651,7 @@ static int tcl_client_reset( ** ** Interchange the sqlite* pointer between two threads. */ -static int tcl_client_swap( +static int SQLITE_TCLAPI tcl_client_swap( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ diff --git a/src/test8.c b/src/test8.c index fb781ac8fd..daab504e4e 100644 --- a/src/test8.c +++ b/src/test8.c @@ -14,7 +14,11 @@ ** testing of the SQLite library. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include @@ -1353,7 +1357,7 @@ static void moduleDestroy(void *p){ /* ** Register the echo virtual table module. */ -static int register_echo_module( +static int SQLITE_TCLAPI register_echo_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -1393,7 +1397,7 @@ static int register_echo_module( ** ** sqlite3_declare_vtab DB SQL */ -static int declare_vtab( +static int SQLITE_TCLAPI declare_vtab( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test9.c b/src/test9.c index e5993e8ff7..5b139e8a56 100644 --- a/src/test9.c +++ b/src/test9.c @@ -15,14 +15,18 @@ ** as there is not much point in binding to Tcl. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include /* ** c_collation_test */ -static int c_collation_test( +static int SQLITE_TCLAPI c_collation_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -63,7 +67,7 @@ error_out: /* ** c_realloc_test */ -static int c_realloc_test( +static int SQLITE_TCLAPI c_realloc_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -104,7 +108,7 @@ error_out: /* ** c_misuse_test */ -static int c_misuse_test( +static int SQLITE_TCLAPI c_misuse_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_async.c b/src/test_async.c index b0b943185b..c32c74c660 100644 --- a/src/test_async.c +++ b/src/test_async.c @@ -15,7 +15,14 @@ */ #define TCL_THREADS -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #ifdef SQLITE_ENABLE_ASYNCIO @@ -36,7 +43,7 @@ TCL_DECLARE_MUTEX(testasync_g_writerMutex); /* ** sqlite3async_initialize PARENT-VFS ISDEFAULT */ -static int testAsyncInit( +static int SQLITE_TCLAPI testAsyncInit( void * clientData, Tcl_Interp *interp, int objc, @@ -69,7 +76,7 @@ static int testAsyncInit( /* ** sqlite3async_shutdown */ -static int testAsyncShutdown( +static int SQLITE_TCLAPI testAsyncShutdown( void * clientData, Tcl_Interp *interp, int objc, @@ -93,7 +100,7 @@ static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){ ** ** Start a new writer thread. */ -static int testAsyncStart( +static int SQLITE_TCLAPI testAsyncStart( void * clientData, Tcl_Interp *interp, int objc, @@ -125,7 +132,7 @@ static int testAsyncStart( ** If the current writer thread is set to run forever then this ** command would block forever. To prevent that, an error is returned. */ -static int testAsyncWait( +static int SQLITE_TCLAPI testAsyncWait( void * clientData, Tcl_Interp *interp, int objc, @@ -151,7 +158,7 @@ static int testAsyncWait( /* ** sqlite3async_control OPTION ?VALUE? */ -static int testAsyncControl( +static int SQLITE_TCLAPI testAsyncControl( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_autoext.c b/src/test_autoext.c index a5236d2390..e23e41a08a 100644 --- a/src/test_autoext.c +++ b/src/test_autoext.c @@ -11,7 +11,14 @@ ************************************************************************* ** Test extension for testing the sqlite3_auto_extension() function. */ -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #include "sqlite3ext.h" #ifndef SQLITE_OMIT_LOAD_EXTENSION @@ -87,13 +94,13 @@ static int broken_init( ** ** Register the "sqr" extension to be loaded automatically. */ -static int autoExtSqrObjCmd( +static int SQLITE_TCLAPI autoExtSqrObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - int rc = sqlite3_auto_extension((void*)sqr_init); + int rc = sqlite3_auto_extension((void(*)(void))sqr_init); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return SQLITE_OK; } @@ -103,13 +110,13 @@ static int autoExtSqrObjCmd( ** ** Unregister the "sqr" extension. */ -static int cancelAutoExtSqrObjCmd( +static int SQLITE_TCLAPI cancelAutoExtSqrObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - int rc = sqlite3_cancel_auto_extension((void*)sqr_init); + int rc = sqlite3_cancel_auto_extension((void(*)(void))sqr_init); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return SQLITE_OK; } @@ -119,13 +126,13 @@ static int cancelAutoExtSqrObjCmd( ** ** Register the "cube" extension to be loaded automatically. */ -static int autoExtCubeObjCmd( +static int SQLITE_TCLAPI autoExtCubeObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - int rc = sqlite3_auto_extension((void*)cube_init); + int rc = sqlite3_auto_extension((void(*)(void))cube_init); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return SQLITE_OK; } @@ -135,13 +142,13 @@ static int autoExtCubeObjCmd( ** ** Unregister the "cube" extension. */ -static int cancelAutoExtCubeObjCmd( +static int SQLITE_TCLAPI cancelAutoExtCubeObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - int rc = sqlite3_cancel_auto_extension((void*)cube_init); + int rc = sqlite3_cancel_auto_extension((void(*)(void))cube_init); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return SQLITE_OK; } @@ -151,13 +158,13 @@ static int cancelAutoExtCubeObjCmd( ** ** Register the broken extension to be loaded automatically. */ -static int autoExtBrokenObjCmd( +static int SQLITE_TCLAPI autoExtBrokenObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - int rc = sqlite3_auto_extension((void*)broken_init); + int rc = sqlite3_auto_extension((void(*)(void))broken_init); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return SQLITE_OK; } @@ -167,13 +174,13 @@ static int autoExtBrokenObjCmd( ** ** Unregister the broken extension. */ -static int cancelAutoExtBrokenObjCmd( +static int SQLITE_TCLAPI cancelAutoExtBrokenObjCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - int rc = sqlite3_cancel_auto_extension((void*)broken_init); + int rc = sqlite3_cancel_auto_extension((void(*)(void))broken_init); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return SQLITE_OK; } @@ -186,7 +193,7 @@ static int cancelAutoExtBrokenObjCmd( ** ** Reset all auto-extensions */ -static int resetAutoExtObjCmd( +static int SQLITE_TCLAPI resetAutoExtObjCmd( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_backup.c b/src/test_backup.c index 6b4d6b9b1c..9b684a28f6 100644 --- a/src/test_backup.c +++ b/src/test_backup.c @@ -13,7 +13,14 @@ ** */ -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #include "sqlite3.h" #include @@ -23,7 +30,7 @@ extern const char *sqlite3ErrName(int); /* These functions are implemented in test1.c. */ extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); -static int backupTestCmd( +static int SQLITE_TCLAPI backupTestCmd( ClientData clientData, Tcl_Interp *interp, int objc, @@ -98,7 +105,7 @@ static int backupTestCmd( return TCL_OK; } -static void backupTestFinish(ClientData clientData){ +static void SQLITE_TCLAPI backupTestFinish(ClientData clientData){ sqlite3_backup *pBackup = (sqlite3_backup *)clientData; sqlite3_backup_finish(pBackup); } @@ -107,7 +114,7 @@ static void backupTestFinish(ClientData clientData){ ** sqlite3_backup CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME ** */ -static int backupTestInit( +static int SQLITE_TCLAPI backupTestInit( ClientData clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_bestindex.c b/src/test_bestindex.c index a955c2782c..94017f0349 100644 --- a/src/test_bestindex.c +++ b/src/test_bestindex.c @@ -93,7 +93,11 @@ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -179,8 +183,8 @@ static int tclConnect( return SQLITE_ERROR; } - zCmd = sqlite3_malloc(strlen(argv[3])+1); - pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab)); + zCmd = sqlite3_malloc64(strlen(argv[3])+1); + pTab = (tcl_vtab*)sqlite3_malloc64(sizeof(tcl_vtab)); if( zCmd && pTab ){ memcpy(zCmd, argv[3], strlen(argv[3])+1); tclDequote(zCmd); @@ -563,7 +567,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); /* ** Register the echo virtual table module. */ -static int register_tcl_module( +static int SQLITE_TCLAPI register_tcl_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_blob.c b/src/test_blob.c index ec2227bb39..3ac6c11421 100644 --- a/src/test_blob.c +++ b/src/test_blob.c @@ -12,7 +12,11 @@ ** */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include #include @@ -95,7 +99,7 @@ static char *blobStringFromObj(Tcl_Obj *pObj){ ** ** Tcl test harness for the sqlite3_blob_open() function. */ -static int test_blob_open( +static int SQLITE_TCLAPI test_blob_open( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* Calling TCL interpreter */ int objc, /* Number of arguments */ @@ -146,7 +150,7 @@ static int test_blob_open( /* ** sqlite3_blob_close HANDLE */ -static int test_blob_close( +static int SQLITE_TCLAPI test_blob_close( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -174,7 +178,7 @@ static int test_blob_close( /* ** sqlite3_blob_bytes HANDLE */ -static int test_blob_bytes( +static int SQLITE_TCLAPI test_blob_bytes( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -210,7 +214,7 @@ static int test_blob_bytes( ** text representation of the returned error code (i.e. "SQLITE_NOMEM") ** and a Tcl exception is thrown. */ -static int test_blob_read( +static int SQLITE_TCLAPI test_blob_read( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -262,7 +266,7 @@ static int test_blob_read( ** result is set to the text representation of the returned error code ** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown. */ -static int test_blob_write( +static int SQLITE_TCLAPI test_blob_write( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_btree.c b/src/test_btree.c index dfe77051b6..03b8b207c9 100644 --- a/src/test_btree.c +++ b/src/test_btree.c @@ -14,7 +14,11 @@ ** testing of the SQLite library. */ #include "btreeInt.h" -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif /* ** Usage: sqlite3_shared_cache_report @@ -22,7 +26,7 @@ ** Return a list of file that are shared and the number of ** references to each file. */ -int sqlite3BtreeSharedCacheReport( +int SQLITE_TCLAPI sqlite3BtreeSharedCacheReport( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_config.c b/src/test_config.c index be410eb389..fd24a94c87 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -24,7 +24,11 @@ # include "os_win.h" #endif -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include diff --git a/src/test_demovfs.c b/src/test_demovfs.c index 9410a309a6..ba7af863cd 100644 --- a/src/test_demovfs.c +++ b/src/test_demovfs.c @@ -641,10 +641,17 @@ sqlite3_vfs *sqlite3_demovfs(void){ #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif #if SQLITE_OS_UNIX -static int register_demovfs( +static int SQLITE_TCLAPI register_demovfs( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -653,7 +660,7 @@ static int register_demovfs( sqlite3_vfs_register(sqlite3_demovfs(), 1); return TCL_OK; } -static int unregister_demovfs( +static int SQLITE_TCLAPI unregister_demovfs( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_fs.c b/src/test_fs.c index 32026d0f38..87088bdae7 100644 --- a/src/test_fs.c +++ b/src/test_fs.c @@ -62,7 +62,11 @@ ** SELECT * FROM fstree WHERE path LIKE '/home/dan/sqlite/%' */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include @@ -871,7 +875,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); /* ** Register the echo virtual table module. */ -static int register_fs_module( +static int SQLITE_TCLAPI register_fs_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_func.c b/src/test_func.c index 579177b70c..26f0d369ef 100644 --- a/src/test_func.c +++ b/src/test_func.c @@ -13,7 +13,11 @@ ** implements new SQL functions used by the test scripts. */ #include "sqlite3.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include #include @@ -21,7 +25,6 @@ #include "sqliteInt.h" #include "vdbeInt.h" - /* ** Allocate nByte bytes of space using sqlite3_malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify @@ -640,7 +643,11 @@ static void test_setsubtype( sqlite3_result_subtype(context, (unsigned int)sqlite3_value_int(argv[1])); } -static int registerTestFunctions(sqlite3 *db){ +static int registerTestFunctions( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pThunk +){ static const struct { char *zName; signed char nArg; @@ -689,16 +696,16 @@ static int registerTestFunctions(sqlite3 *db){ ** the standard set of test functions to be loaded into each new ** database connection. */ -static int autoinstall_test_funcs( +static int SQLITE_TCLAPI autoinstall_test_funcs( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - extern int Md5_Register(sqlite3*); - int rc = sqlite3_auto_extension((void*)registerTestFunctions); + extern int Md5_Register(sqlite3 *, char **, const sqlite3_api_routines *); + int rc = sqlite3_auto_extension((void(*)(void))registerTestFunctions); if( rc==SQLITE_OK ){ - rc = sqlite3_auto_extension((void*)Md5_Register); + rc = sqlite3_auto_extension((void(*)(void))Md5_Register); } Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; @@ -717,7 +724,7 @@ static void tFinal(sqlite3_context *a){} ** Make various calls to sqlite3_create_function that do not have valid ** parameters. Verify that the error condition is detected and reported. */ -static int abuse_create_function( +static int SQLITE_TCLAPI abuse_create_function( void * clientData, Tcl_Interp *interp, int objc, @@ -783,6 +790,7 @@ abuse_err: return TCL_ERROR; } + /* ** Register commands with the TCL interpreter. */ @@ -795,13 +803,13 @@ int Sqlitetest_func_Init(Tcl_Interp *interp){ { "abuse_create_function", abuse_create_function }, }; int i; - extern int Md5_Register(sqlite3*); + extern int Md5_Register(sqlite3 *, char **, const sqlite3_api_routines *); for(i=0; i #include #include @@ -94,7 +98,7 @@ int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){ ** beginning of the file. Convert that information to hexadecimal ** and return the resulting HEX string. */ -static int hexio_read( +static int SQLITE_TCLAPI hexio_read( void * clientData, Tcl_Interp *interp, int objc, @@ -144,7 +148,7 @@ static int hexio_read( ** Write DATA into file FILENAME beginning at OFFSET from the ** beginning of the file. DATA is expressed in hexadecimal. */ -static int hexio_write( +static int SQLITE_TCLAPI hexio_write( void * clientData, Tcl_Interp *interp, int objc, @@ -192,7 +196,7 @@ static int hexio_write( ** the value of that integer. HEXDATA can contain between 2 and 8 ** hexadecimal digits. */ -static int hexio_get_int( +static int SQLITE_TCLAPI hexio_get_int( void * clientData, Tcl_Interp *interp, int objc, @@ -232,7 +236,7 @@ static int hexio_get_int( ** ** Render INTEGER has a 16-bit big-endian integer in hexadecimal. */ -static int hexio_render_int16( +static int SQLITE_TCLAPI hexio_render_int16( void * clientData, Tcl_Interp *interp, int objc, @@ -259,7 +263,7 @@ static int hexio_render_int16( ** ** Render INTEGER has a 32-bit big-endian integer in hexadecimal. */ -static int hexio_render_int32( +static int SQLITE_TCLAPI hexio_render_int32( void * clientData, Tcl_Interp *interp, int objc, @@ -289,7 +293,7 @@ static int hexio_render_int32( ** The UTF8 might not be well-formed. Run this string through ** sqlite3Utf8to8() convert it back to hex and return the result. */ -static int utf8_to_utf8( +static int SQLITE_TCLAPI utf8_to_utf8( void * clientData, Tcl_Interp *interp, int objc, @@ -340,7 +344,7 @@ static int getFts3Varint(const char *p, sqlite_int64 *v){ ** Read a varint from the start of BLOB. Set variable VARNAME to contain ** the interpreted value. Return the number of bytes of BLOB consumed. */ -static int read_fts3varint( +static int SQLITE_TCLAPI read_fts3varint( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_init.c b/src/test_init.c index 502d95c052..58465785d8 100644 --- a/src/test_init.c +++ b/src/test_init.c @@ -27,7 +27,11 @@ #include "sqliteInt.h" #include -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif static struct Wrapped { sqlite3_pcache_methods2 pcache; @@ -184,7 +188,7 @@ static void installInitWrappers(void){ sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcachemethods); } -static int init_wrapper_install( +static int SQLITE_TCLAPI init_wrapper_install( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -208,7 +212,7 @@ static int init_wrapper_install( return TCL_OK; } -static int init_wrapper_uninstall( +static int SQLITE_TCLAPI init_wrapper_uninstall( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -226,7 +230,7 @@ static int init_wrapper_uninstall( return TCL_OK; } -static int init_wrapper_clear( +static int SQLITE_TCLAPI init_wrapper_clear( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -243,7 +247,7 @@ static int init_wrapper_clear( return TCL_OK; } -static int init_wrapper_query( +static int SQLITE_TCLAPI init_wrapper_query( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_intarray.c b/src/test_intarray.c index 70e34db3e1..ddfb13fe20 100644 --- a/src/test_intarray.c +++ b/src/test_intarray.c @@ -270,7 +270,14 @@ SQLITE_API int sqlite3_intarray_bind( ** Everything below is interface for testing this module. */ #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif /* ** Routines to encode and decode pointers @@ -286,7 +293,7 @@ extern const char *sqlite3ErrName(int); ** Invoke the sqlite3_intarray_create interface. A string that becomes ** the first parameter to sqlite3_intarray_bind. */ -static int test_intarray_create( +static int SQLITE_TCLAPI test_intarray_create( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -322,7 +329,7 @@ static int test_intarray_create( ** ** Invoke the sqlite3_intarray_bind interface on the given array of integers. */ -static int test_intarray_bind( +static int SQLITE_TCLAPI test_intarray_bind( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_intarray.h b/src/test_intarray.h index 84b1f3fe66..cee55b5dae 100644 --- a/src/test_intarray.h +++ b/src/test_intarray.h @@ -75,8 +75,8 @@ ** action to free the intarray objects. */ #include "sqlite3.h" -#ifndef _INTARRAY_H_ -#define _INTARRAY_H_ +#ifndef SQLITE_INTARRAY_H +#define SQLITE_INTARRAY_H /* ** Make sure we can call this stuff from C++. @@ -125,4 +125,4 @@ SQLITE_API int sqlite3_intarray_bind( #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif -#endif /* _INTARRAY_H_ */ +#endif /* SQLITE_INTARRAY_H */ diff --git a/src/test_malloc.c b/src/test_malloc.c index e6be663998..e8c248f958 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -14,7 +14,11 @@ ** memory allocation subsystem. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include #include @@ -306,7 +310,7 @@ static int textToPointer(const char *z, void **pp){ ** ** Raw test interface for sqlite3_malloc(). */ -static int test_malloc( +static int SQLITE_TCLAPI test_malloc( void * clientData, Tcl_Interp *interp, int objc, @@ -331,7 +335,7 @@ static int test_malloc( ** ** Raw test interface for sqlite3_realloc(). */ -static int test_realloc( +static int SQLITE_TCLAPI test_realloc( void * clientData, Tcl_Interp *interp, int objc, @@ -360,7 +364,7 @@ static int test_realloc( ** ** Raw test interface for sqlite3_free(). */ -static int test_free( +static int SQLITE_TCLAPI test_free( void * clientData, Tcl_Interp *interp, int objc, @@ -391,7 +395,7 @@ int sqlite3TestBinToHex(char*,int); ** Set a chunk of memory (obtained from malloc, probably) to a ** specified hex pattern. */ -static int test_memset( +static int SQLITE_TCLAPI test_memset( void * clientData, Tcl_Interp *interp, int objc, @@ -437,7 +441,7 @@ static int test_memset( ** ** Return memory as hexadecimal text. */ -static int test_memget( +static int SQLITE_TCLAPI test_memget( void * clientData, Tcl_Interp *interp, int objc, @@ -484,7 +488,7 @@ static int test_memget( ** ** Raw test interface for sqlite3_memory_used(). */ -static int test_memory_used( +static int SQLITE_TCLAPI test_memory_used( void * clientData, Tcl_Interp *interp, int objc, @@ -499,7 +503,7 @@ static int test_memory_used( ** ** Raw test interface for sqlite3_memory_highwater(). */ -static int test_memory_highwater( +static int SQLITE_TCLAPI test_memory_highwater( void * clientData, Tcl_Interp *interp, int objc, @@ -524,7 +528,7 @@ static int test_memory_highwater( ** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined ** then this routine is a no-op. */ -static int test_memdebug_backtrace( +static int SQLITE_TCLAPI test_memdebug_backtrace( void * clientData, Tcl_Interp *interp, int objc, @@ -550,7 +554,7 @@ static int test_memdebug_backtrace( ** ** Write a summary of unfreed memory to FILENAME. */ -static int test_memdebug_dump( +static int SQLITE_TCLAPI test_memdebug_dump( void * clientData, Tcl_Interp *interp, int objc, @@ -575,7 +579,7 @@ static int test_memdebug_dump( ** ** Return the total number of times malloc() has been called. */ -static int test_memdebug_malloc_count( +static int SQLITE_TCLAPI test_memdebug_malloc_count( void * clientData, Tcl_Interp *interp, int objc, @@ -615,7 +619,7 @@ static int test_memdebug_malloc_count( ** ** To disable simulated failures, use a COUNTER of -1. */ -static int test_memdebug_fail( +static int SQLITE_TCLAPI test_memdebug_fail( void * clientData, Tcl_Interp *interp, int objc, @@ -681,7 +685,7 @@ static int test_memdebug_fail( ** simulated failure occurs. A negative return value indicates that ** no malloc() failure is scheduled. */ -static int test_memdebug_pending( +static int SQLITE_TCLAPI test_memdebug_pending( void * clientData, Tcl_Interp *interp, int objc, @@ -714,7 +718,7 @@ static int sqlite3_memdebug_title_count = 0; ** ** Each title overwrite the previous. */ -static int test_memdebug_settitle( +static int SQLITE_TCLAPI test_memdebug_settitle( void * clientData, Tcl_Interp *interp, int objc, @@ -795,7 +799,7 @@ static void test_memdebug_log_clear(void){ Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_KEYINTS); } -static int test_memdebug_log( +static int SQLITE_TCLAPI test_memdebug_log( void * clientData, Tcl_Interp *interp, int objc, @@ -893,7 +897,7 @@ static int test_memdebug_log( ** ** A negative SIZE causes the buffer pointer to be NULL. */ -static int test_config_scratch( +static int SQLITE_TCLAPI test_config_scratch( void * clientData, Tcl_Interp *interp, int objc, @@ -933,7 +937,7 @@ static int test_config_scratch( ** ** A negative SIZE causes the buffer pointer to be NULL. */ -static int test_config_pagecache( +static int SQLITE_TCLAPI test_config_pagecache( void * clientData, Tcl_Interp *interp, int objc, @@ -976,7 +980,7 @@ static int test_config_pagecache( ** is certainty. 0 is never. PRNG_SEED is the pseudo-random number generator ** seed. */ -static int test_alt_pcache( +static int SQLITE_TCLAPI test_alt_pcache( void * clientData, Tcl_Interp *interp, int objc, @@ -1017,7 +1021,7 @@ static int test_alt_pcache( ** ** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS. */ -static int test_config_memstatus( +static int SQLITE_TCLAPI test_config_memstatus( void * clientData, Tcl_Interp *interp, int objc, @@ -1038,7 +1042,7 @@ static int test_config_memstatus( ** Usage: sqlite3_config_lookaside SIZE COUNT ** */ -static int test_config_lookaside( +static int SQLITE_TCLAPI test_config_lookaside( void * clientData, Tcl_Interp *interp, int objc, @@ -1072,7 +1076,7 @@ static int test_config_lookaside( ** is 10KB in size. A BUFID of 0 indicates that the buffer should be NULL ** which will cause sqlite3_db_config() to allocate space on its own. */ -static int test_db_config_lookaside( +static int SQLITE_TCLAPI test_db_config_lookaside( void * clientData, Tcl_Interp *interp, int objc, @@ -1107,7 +1111,7 @@ static int test_db_config_lookaside( /* ** Usage: sqlite3_config_heap NBYTE NMINALLOC */ -static int test_config_heap( +static int SQLITE_TCLAPI test_config_heap( void * clientData, Tcl_Interp *interp, int objc, @@ -1144,7 +1148,7 @@ static int test_config_heap( /* ** Usage: sqlite3_config_heap_size NBYTE */ -static int test_config_heap_size( +static int SQLITE_TCLAPI test_config_heap_size( void * clientData, Tcl_Interp *interp, int objc, @@ -1174,7 +1178,7 @@ static int test_config_heap_size( ** Invoke sqlite3_config() or sqlite3_db_config() with invalid ** opcodes and verify that they return errors. */ -static int test_config_error( +static int SQLITE_TCLAPI test_config_error( void * clientData, Tcl_Interp *interp, int objc, @@ -1212,7 +1216,7 @@ static int test_config_error( ** Enables or disables interpretation of URI parameters by default using ** SQLITE_CONFIG_URI. */ -static int test_config_uri( +static int SQLITE_TCLAPI test_config_uri( void * clientData, Tcl_Interp *interp, int objc, @@ -1241,7 +1245,7 @@ static int test_config_uri( ** Enables or disables the use of the covering-index scan optimization. ** SQLITE_CONFIG_COVERING_INDEX_SCAN. */ -static int test_config_cis( +static int SQLITE_TCLAPI test_config_cis( void * clientData, Tcl_Interp *interp, int objc, @@ -1269,7 +1273,7 @@ static int test_config_cis( ** ** Set the minimum PMA size. */ -static int test_config_pmasz( +static int SQLITE_TCLAPI test_config_pmasz( void * clientData, Tcl_Interp *interp, int objc, @@ -1299,7 +1303,7 @@ static int test_config_pmasz( ** ** Write a summary of unfreed memsys3 allocations to FILENAME. */ -static int test_dump_memsys3( +static int SQLITE_TCLAPI test_dump_memsys3( void * clientData, Tcl_Interp *interp, int objc, @@ -1335,7 +1339,7 @@ static int test_dump_memsys3( ** Return a list of three elements which are the sqlite3_status() return ** code, the current value, and the high-water mark value. */ -static int test_status( +static int SQLITE_TCLAPI test_status( void * clientData, Tcl_Interp *interp, int objc, @@ -1392,7 +1396,7 @@ static int test_status( ** Return a list of three elements which are the sqlite3_db_status() return ** code, the current value, and the high-water mark value. */ -static int test_db_status( +static int SQLITE_TCLAPI test_db_status( void * clientData, Tcl_Interp *interp, int objc, @@ -1417,7 +1421,8 @@ static int test_db_status( { "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT }, { "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS }, { "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE }, - { "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS } + { "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS }, + { "CACHE_USED_SHARED", SQLITE_DBSTATUS_CACHE_USED_SHARED }, }; Tcl_Obj *pResult; if( objc!=4 ){ @@ -1452,7 +1457,7 @@ static int test_db_status( /* ** install_malloc_faultsim BOOLEAN */ -static int test_install_malloc_faultsim( +static int SQLITE_TCLAPI test_install_malloc_faultsim( void * clientData, Tcl_Interp *interp, int objc, @@ -1476,7 +1481,7 @@ static int test_install_malloc_faultsim( /* ** sqlite3_install_memsys3 */ -static int test_install_memsys3( +static int SQLITE_TCLAPI test_install_memsys3( void * clientData, Tcl_Interp *interp, int objc, @@ -1491,7 +1496,7 @@ static int test_install_memsys3( return TCL_OK; } -static int test_vfs_oom_test( +static int SQLITE_TCLAPI test_vfs_oom_test( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_multiplex.c b/src/test_multiplex.c index ba4e61b090..1027aa1326 100644 --- a/src/test_multiplex.c +++ b/src/test_multiplex.c @@ -1197,7 +1197,7 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); - sqlite3_auto_extension((void*)multiplexFuncInit); + sqlite3_auto_extension((void(*)(void))multiplexFuncInit); return SQLITE_OK; } @@ -1229,14 +1229,21 @@ int sqlite3_multiplex_shutdown(int eForce){ /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif extern const char *sqlite3ErrName(int); /* ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT */ -static int test_multiplex_initialize( +static int SQLITE_TCLAPI test_multiplex_initialize( void * clientData, Tcl_Interp *interp, int objc, @@ -1267,7 +1274,7 @@ static int test_multiplex_initialize( /* ** tclcmd: sqlite3_multiplex_shutdown */ -static int test_multiplex_shutdown( +static int SQLITE_TCLAPI test_multiplex_shutdown( void * clientData, Tcl_Interp *interp, int objc, @@ -1295,7 +1302,7 @@ static int test_multiplex_shutdown( /* ** tclcmd: sqlite3_multiplex_dump */ -static int test_multiplex_dump( +static int SQLITE_TCLAPI test_multiplex_dump( void * clientData, Tcl_Interp *interp, int objc, @@ -1350,7 +1357,7 @@ static int test_multiplex_dump( /* ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? */ -static int test_multiplex_control( +static int SQLITE_TCLAPI test_multiplex_control( ClientData cd, Tcl_Interp *interp, int objc, diff --git a/src/test_multiplex.h b/src/test_multiplex.h index d973e4af27..790c778a35 100644 --- a/src/test_multiplex.h +++ b/src/test_multiplex.h @@ -20,8 +20,8 @@ ** */ -#ifndef _TEST_MULTIPLEX_H -#define _TEST_MULTIPLEX_H +#ifndef SQLITE_TEST_MULTIPLEX_H +#define SQLITE_TEST_MULTIPLEX_H /* ** CAPI: File-control Operations Supported by Multiplex VFS @@ -96,4 +96,4 @@ extern int sqlite3_multiplex_shutdown(int eForce); } /* End of the 'extern "C"' block */ #endif -#endif /* _TEST_MULTIPLEX_H */ +#endif /* SQLITE_TEST_MULTIPLEX_H */ diff --git a/src/test_mutex.c b/src/test_mutex.c index 995b89a4c6..8f43e5ad30 100644 --- a/src/test_mutex.c +++ b/src/test_mutex.c @@ -12,7 +12,11 @@ ** This file contains test logic for the sqlite3_mutex interfaces. */ -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include "sqlite3.h" #include "sqliteInt.h" #include @@ -152,7 +156,7 @@ static void counterMutexLeave(sqlite3_mutex *p){ /* ** sqlite3_shutdown */ -static int test_shutdown( +static int SQLITE_TCLAPI test_shutdown( void * clientData, Tcl_Interp *interp, int objc, @@ -173,7 +177,7 @@ static int test_shutdown( /* ** sqlite3_initialize */ -static int test_initialize( +static int SQLITE_TCLAPI test_initialize( void * clientData, Tcl_Interp *interp, int objc, @@ -194,7 +198,7 @@ static int test_initialize( /* ** install_mutex_counters BOOLEAN */ -static int test_install_mutex_counters( +static int SQLITE_TCLAPI test_install_mutex_counters( void * clientData, Tcl_Interp *interp, int objc, @@ -255,7 +259,7 @@ static int test_install_mutex_counters( /* ** read_mutex_counters */ -static int test_read_mutex_counters( +static int SQLITE_TCLAPI test_read_mutex_counters( void * clientData, Tcl_Interp *interp, int objc, @@ -284,7 +288,7 @@ static int test_read_mutex_counters( /* ** clear_mutex_counters */ -static int test_clear_mutex_counters( +static int SQLITE_TCLAPI test_clear_mutex_counters( void * clientData, Tcl_Interp *interp, int objc, @@ -308,7 +312,7 @@ static int test_clear_mutex_counters( ** will be invalid since the mutex has already been freed. The ** return pointer just checks to see if the mutex really was allocated. */ -static int test_alloc_mutex( +static int SQLITE_TCLAPI test_alloc_mutex( void * clientData, Tcl_Interp *interp, int objc, @@ -335,7 +339,7 @@ static int test_alloc_mutex( ** ** Or OPTION can be an raw integer. */ -static int test_config( +static int SQLITE_TCLAPI test_config( void * clientData, Tcl_Interp *interp, int objc, @@ -397,7 +401,7 @@ static sqlite3_mutex *getStaticMutexPointer( return counterMutexAlloc(iMutex); } -static int test_enter_static_mutex( +static int SQLITE_TCLAPI test_enter_static_mutex( void * clientData, Tcl_Interp *interp, int objc, @@ -416,7 +420,7 @@ static int test_enter_static_mutex( return TCL_OK; } -static int test_leave_static_mutex( +static int SQLITE_TCLAPI test_leave_static_mutex( void * clientData, Tcl_Interp *interp, int objc, @@ -435,7 +439,7 @@ static int test_leave_static_mutex( return TCL_OK; } -static int test_enter_db_mutex( +static int SQLITE_TCLAPI test_enter_db_mutex( void * clientData, Tcl_Interp *interp, int objc, @@ -454,7 +458,7 @@ static int test_enter_db_mutex( return TCL_OK; } -static int test_leave_db_mutex( +static int SQLITE_TCLAPI test_leave_db_mutex( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_osinst.c b/src/test_osinst.c index e51ce77ef8..a008baba45 100644 --- a/src/test_osinst.c +++ b/src/test_osinst.c @@ -1104,9 +1104,16 @@ int sqlite3_vfslog_register(sqlite3 *db){ #if defined(SQLITE_TEST) || defined(TCLSH) -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif -static int test_vfslog( +static int SQLITE_TCLAPI test_vfslog( void *clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_quota.c b/src/test_quota.c index e8e0b34072..e87e9772f6 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -111,7 +111,7 @@ struct quotaFile { /* ** An instance of the following object represents each open connection -** to a file that participates in quota tracking. This object is a +** to a file that participates in quota tracking. This object is a ** subclass of sqlite3_file. The sqlite3_file object for the underlying ** VFS is appended to this structure. */ @@ -154,11 +154,11 @@ static struct { */ sqlite3_vfs sThisVfs; - /* The sIoMethods defines the methods used by sqlite3_file objects + /* The sIoMethods defines the methods used by sqlite3_file objects ** associated with this shim. It is initialized at start-time and does ** not require a mutex. ** - ** When the underlying VFS is called to open a file, it might return + ** When the underlying VFS is called to open a file, it might return ** either a version 1 or a version 2 sqlite3_file object. This shim ** has to create a wrapper sqlite3_file of the same version. Hence ** there are two I/O method structures, one for version 1 and the other @@ -190,7 +190,7 @@ static struct { static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); } static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); } -/* Count the number of open files in a quotaGroup +/* Count the number of open files in a quotaGroup */ static int quotaGroupOpenFileCount(quotaGroup *pGroup){ int N = 0; @@ -399,7 +399,7 @@ static char *quota_utf8_to_mbcs(const char *zUtf8){ return zMbcs; #else return (char*)zUtf8; /* No-op on unix */ -#endif +#endif } /* @@ -410,7 +410,7 @@ static void quota_mbcs_free(char *zOld){ sqlite3_free(zOld); #else /* No-op on unix */ -#endif +#endif } /************************* VFS Method Wrappers *****************************/ @@ -428,7 +428,7 @@ static int quotaOpen( int flags, /* Flags to control the opening */ int *pOutFlags /* Flags showing results of opening */ ){ - int rc; /* Result code */ + int rc; /* Result code */ quotaConn *pQuotaOpen; /* The new quota file descriptor */ quotaFile *pFile; /* Corresponding quotaFile obj */ quotaGroup *pGroup; /* The group file belongs to */ @@ -488,7 +488,7 @@ static int quotaDelete( const char *zName, /* Name of file to be deleted */ int syncDir /* Do a directory sync after deleting */ ){ - int rc; /* Result code */ + int rc; /* Result code */ quotaFile *pFile; /* Files in the quota */ quotaGroup *pGroup; /* The group file belongs to */ sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */ @@ -581,7 +581,7 @@ static int quotaWrite( szNew = pGroup->iSize - pFile->iSize + iEnd; if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ if( pGroup->xCallback ){ - pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, + pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, pGroup->pArg); } if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ @@ -738,7 +738,7 @@ static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){ /* ** Initialize the quota VFS shim. Use the VFS named zOrigVfsName ** as the VFS that does the actual work. Use the default if -** zOrigVfsName==NULL. +** zOrigVfsName==NULL. ** ** The quota VFS shim is named "quota". It will become the default ** VFS if makeDefault is non-zero. @@ -908,7 +908,7 @@ int sqlite3_quota_file(const char *zFilename){ if( rc==SQLITE_OK ){ zFull[strlen(zFull)+1] = '\0'; - rc = quotaOpen(&gQuota.sThisVfs, zFull, fd, + rc = quotaOpen(&gQuota.sThisVfs, zFull, fd, SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags); if( rc==SQLITE_OK ){ fd->pMethods->xFileSize(fd, &iSize); @@ -1016,7 +1016,7 @@ size_t sqlite3_quota_fwrite( szNew = pGroup->iSize - pFile->iSize + iEnd; if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ if( pGroup->xCallback ){ - pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, + pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, pGroup->pArg); } if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ @@ -1203,7 +1203,7 @@ sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE *p){ sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){ return p->pFile ? p->pFile->iSize : -1; } - + /* ** Determine the amount of data in bytes available for reading ** in the given file. @@ -1275,10 +1275,17 @@ int sqlite3_quota_remove(const char *zFilename){ sqlite3_free(zFull); return rc; } - + /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif /* ** Argument passed to a TCL quota-over-limit callback. @@ -1350,7 +1357,7 @@ static void tclCallbackDestructor(void *pObj){ /* ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT */ -static int test_quota_initialize( +static int SQLITE_TCLAPI test_quota_initialize( void * clientData, Tcl_Interp *interp, int objc, @@ -1379,7 +1386,7 @@ static int test_quota_initialize( /* ** tclcmd: sqlite3_quota_shutdown */ -static int test_quota_shutdown( +static int SQLITE_TCLAPI test_quota_shutdown( void * clientData, Tcl_Interp *interp, int objc, @@ -1402,7 +1409,7 @@ static int test_quota_shutdown( /* ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT */ -static int test_quota_set( +static int SQLITE_TCLAPI test_quota_set( void * clientData, Tcl_Interp *interp, int objc, @@ -1456,7 +1463,7 @@ static int test_quota_set( /* ** tclcmd: sqlite3_quota_file FILENAME */ -static int test_quota_file( +static int SQLITE_TCLAPI test_quota_file( void * clientData, Tcl_Interp *interp, int objc, @@ -1482,7 +1489,7 @@ static int test_quota_file( /* ** tclcmd: sqlite3_quota_dump */ -static int test_quota_dump( +static int SQLITE_TCLAPI test_quota_dump( void * clientData, Tcl_Interp *interp, int objc, @@ -1530,7 +1537,7 @@ static int test_quota_dump( /* ** tclcmd: sqlite3_quota_fopen FILENAME MODE */ -static int test_quota_fopen( +static int SQLITE_TCLAPI test_quota_fopen( void * clientData, Tcl_Interp *interp, int objc, @@ -1560,7 +1567,7 @@ extern void *sqlite3TestTextToPtr(const char*); /* ** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM */ -static int test_quota_fread( +static int SQLITE_TCLAPI test_quota_fread( void * clientData, Tcl_Interp *interp, int objc, @@ -1594,7 +1601,7 @@ static int test_quota_fread( /* ** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT */ -static int test_quota_fwrite( +static int SQLITE_TCLAPI test_quota_fwrite( void * clientData, Tcl_Interp *interp, int objc, @@ -1622,7 +1629,7 @@ static int test_quota_fwrite( /* ** tclcmd: sqlite3_quota_fclose HANDLE */ -static int test_quota_fclose( +static int SQLITE_TCLAPI test_quota_fclose( void * clientData, Tcl_Interp *interp, int objc, @@ -1644,7 +1651,7 @@ static int test_quota_fclose( /* ** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC? */ -static int test_quota_fflush( +static int SQLITE_TCLAPI test_quota_fflush( void * clientData, Tcl_Interp *interp, int objc, @@ -1670,7 +1677,7 @@ static int test_quota_fflush( /* ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE */ -static int test_quota_fseek( +static int SQLITE_TCLAPI test_quota_fseek( void * clientData, Tcl_Interp *interp, int objc, @@ -1708,7 +1715,7 @@ static int test_quota_fseek( /* ** tclcmd: sqlite3_quota_rewind HANDLE */ -static int test_quota_rewind( +static int SQLITE_TCLAPI test_quota_rewind( void * clientData, Tcl_Interp *interp, int objc, @@ -1727,7 +1734,7 @@ static int test_quota_rewind( /* ** tclcmd: sqlite3_quota_ftell HANDLE */ -static int test_quota_ftell( +static int SQLITE_TCLAPI test_quota_ftell( void * clientData, Tcl_Interp *interp, int objc, @@ -1748,7 +1755,7 @@ static int test_quota_ftell( /* ** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE */ -static int test_quota_ftruncate( +static int SQLITE_TCLAPI test_quota_ftruncate( void * clientData, Tcl_Interp *interp, int objc, @@ -1773,7 +1780,7 @@ static int test_quota_ftruncate( /* ** tclcmd: sqlite3_quota_file_size HANDLE */ -static int test_quota_file_size( +static int SQLITE_TCLAPI test_quota_file_size( void * clientData, Tcl_Interp *interp, int objc, @@ -1794,7 +1801,7 @@ static int test_quota_file_size( /* ** tclcmd: sqlite3_quota_file_truesize HANDLE */ -static int test_quota_file_truesize( +static int SQLITE_TCLAPI test_quota_file_truesize( void * clientData, Tcl_Interp *interp, int objc, @@ -1815,7 +1822,7 @@ static int test_quota_file_truesize( /* ** tclcmd: sqlite3_quota_file_mtime HANDLE */ -static int test_quota_file_mtime( +static int SQLITE_TCLAPI test_quota_file_mtime( void * clientData, Tcl_Interp *interp, int objc, @@ -1838,7 +1845,7 @@ static int test_quota_file_mtime( /* ** tclcmd: sqlite3_quota_remove FILENAME */ -static int test_quota_remove( +static int SQLITE_TCLAPI test_quota_remove( void * clientData, Tcl_Interp *interp, int objc, @@ -1862,7 +1869,7 @@ static int test_quota_remove( ** Test the glob pattern matching. Return 1 if TEXT matches PATTERN ** and return 0 if it does not. */ -static int test_quota_glob( +static int SQLITE_TCLAPI test_quota_glob( void * clientData, Tcl_Interp *interp, int objc, @@ -1888,7 +1895,7 @@ static int test_quota_glob( ** Return the number of bytes from the current file point to the end of ** the file. */ -static int test_quota_file_available( +static int SQLITE_TCLAPI test_quota_file_available( void * clientData, Tcl_Interp *interp, int objc, @@ -1911,7 +1918,7 @@ static int test_quota_file_available( ** ** Return true if the file handle is in the error state. */ -static int test_quota_ferror( +static int SQLITE_TCLAPI test_quota_ferror( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_rtree.c b/src/test_rtree.c index 797ec0026c..0c6dbf3cd7 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -14,7 +14,11 @@ */ #include "sqlite3.h" -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif /* Solely for the UNUSED_PARAMETER() macro. */ #include "sqliteInt.h" @@ -353,7 +357,11 @@ static int bfs_query_func(sqlite3_rtree_query_info *p){ *************************************************************************/ #include -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif typedef struct Cube Cube; struct Cube { @@ -432,7 +440,7 @@ static int cube_geom( } #endif /* SQLITE_ENABLE_RTREE */ -static int register_cube_geom( +static int SQLITE_TCLAPI register_cube_geom( void * clientData, Tcl_Interp *interp, int objc, @@ -460,7 +468,7 @@ static int register_cube_geom( return TCL_OK; } -static int register_circle_geom( +static int SQLITE_TCLAPI register_circle_geom( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_schema.c b/src/test_schema.c index 4ee18193b0..cdf085797f 100644 --- a/src/test_schema.c +++ b/src/test_schema.c @@ -35,10 +35,14 @@ ** to be compiled into an sqlite dynamic extension. */ #ifdef SQLITE_TEST - #include "sqliteInt.h" - #include "tcl.h" +# include "sqliteInt.h" +# if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +# else +# include "tcl.h" +# endif #else - #include "sqlite3ext.h" +# include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #endif @@ -302,7 +306,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); /* ** Register the schema virtual table module. */ -static int register_schema_module( +static int SQLITE_TCLAPI register_schema_module( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_superlock.c b/src/test_superlock.c index cac789842d..45d0d623a0 100644 --- a/src/test_superlock.c +++ b/src/test_superlock.c @@ -256,7 +256,14 @@ int sqlite3demo_superlock( #ifdef SQLITE_TEST -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif struct InterpAndScript { Tcl_Interp *interp; @@ -264,11 +271,11 @@ struct InterpAndScript { }; typedef struct InterpAndScript InterpAndScript; -static void superunlock_del(ClientData cd){ +static void SQLITE_TCLAPI superunlock_del(ClientData cd){ sqlite3demo_superunlock((void *)cd); } -static int superunlock_cmd( +static int SQLITE_TCLAPI superunlock_cmd( ClientData cd, Tcl_Interp *interp, int objc, @@ -300,7 +307,7 @@ static int superlock_busy(void *pCtx, int nBusy){ /* ** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT */ -static int superlock_cmd( +static int SQLITE_TCLAPI superlock_cmd( ClientData cd, Tcl_Interp *interp, int objc, diff --git a/src/test_syscall.c b/src/test_syscall.c index f1d5c61bc3..947f9a9d9d 100644 --- a/src/test_syscall.c +++ b/src/test_syscall.c @@ -76,7 +76,11 @@ #include "sqliteInt.h" #include "sqlite3.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include #include @@ -418,7 +422,7 @@ static void *ts_mremap(void *a, size_t b, size_t c, int d, ...){ return orig_mremap(a, b, c, d, pArg); } -static int test_syscall_install( +static int SQLITE_TCLAPI test_syscall_install( void * clientData, Tcl_Interp *interp, int objc, @@ -454,7 +458,7 @@ static int test_syscall_install( return TCL_OK; } -static int test_syscall_uninstall( +static int SQLITE_TCLAPI test_syscall_uninstall( void * clientData, Tcl_Interp *interp, int objc, @@ -478,7 +482,7 @@ static int test_syscall_uninstall( return TCL_OK; } -static int test_syscall_reset( +static int SQLITE_TCLAPI test_syscall_reset( void * clientData, Tcl_Interp *interp, int objc, @@ -516,7 +520,7 @@ static int test_syscall_reset( return TCL_OK; } -static int test_syscall_exists( +static int SQLITE_TCLAPI test_syscall_exists( void * clientData, Tcl_Interp *interp, int objc, @@ -537,7 +541,7 @@ static int test_syscall_exists( return TCL_OK; } -static int test_syscall_fault( +static int SQLITE_TCLAPI test_syscall_fault( void * clientData, Tcl_Interp *interp, int objc, @@ -566,7 +570,7 @@ static int test_syscall_fault( return TCL_OK; } -static int test_syscall_errno( +static int SQLITE_TCLAPI test_syscall_errno( void * clientData, Tcl_Interp *interp, int objc, @@ -612,7 +616,7 @@ static int test_syscall_errno( return TCL_OK; } -static int test_syscall_list( +static int SQLITE_TCLAPI test_syscall_list( void * clientData, Tcl_Interp *interp, int objc, @@ -642,7 +646,7 @@ static int test_syscall_list( return TCL_OK; } -static int test_syscall_defaultvfs( +static int SQLITE_TCLAPI test_syscall_defaultvfs( void * clientData, Tcl_Interp *interp, int objc, @@ -664,7 +668,7 @@ static int ts_getpagesize(void){ return gSyscall.pgsz; } -static int test_syscall_pagesize( +static int SQLITE_TCLAPI test_syscall_pagesize( void * clientData, Tcl_Interp *interp, int objc, @@ -699,7 +703,7 @@ static int test_syscall_pagesize( return TCL_OK; } -static int test_syscall( +static int SQLITE_TCLAPI test_syscall( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_tclvar.c b/src/test_tclvar.c index 63ed394734..8f7352112d 100644 --- a/src/test_tclvar.c +++ b/src/test_tclvar.c @@ -17,7 +17,11 @@ ** access to TCL variables. */ #include "sqliteInt.h" -#include "tcl.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #include #include @@ -407,7 +411,7 @@ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); /* ** Register the echo virtual table module. */ -static int register_tclvar_module( +static int SQLITE_TCLAPI register_tclvar_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ diff --git a/src/test_thread.c b/src/test_thread.c index a4d96e1942..20b4cf148b 100644 --- a/src/test_thread.c +++ b/src/test_thread.c @@ -16,7 +16,11 @@ */ #include "sqliteInt.h" -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif #if SQLITE_THREADSAFE @@ -72,7 +76,7 @@ extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int); /* ** Handler for events of type EvalEvent. */ -static int tclScriptEvent(Tcl_Event *evPtr, int flags){ +static int SQLITE_TCLAPI tclScriptEvent(Tcl_Event *evPtr, int flags){ int rc; EvalEvent *p = (EvalEvent *)evPtr; rc = Tcl_Eval(p->interp, p->zScript); @@ -167,7 +171,7 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){ ** ** The caller can wait for the script to terminate using [vwait VARNAME]. */ -static int sqlthread_spawn( +static int SQLITE_TCLAPI sqlthread_spawn( ClientData clientData, Tcl_Interp *interp, int objc, @@ -220,7 +224,7 @@ static int sqlthread_spawn( ** ** NOTE: At the moment, this doesn't work. FIXME. */ -static int sqlthread_parent( +static int SQLITE_TCLAPI sqlthread_parent( ClientData clientData, Tcl_Interp *interp, int objc, @@ -265,7 +269,7 @@ static int xBusy(void *pArg, int nBusy){ ** Open a database handle and return the string representation of ** the pointer value. */ -static int sqlthread_open( +static int SQLITE_TCLAPI sqlthread_open( ClientData clientData, Tcl_Interp *interp, int objc, @@ -276,7 +280,7 @@ static int sqlthread_open( const char *zFilename; sqlite3 *db; char zBuf[100]; - extern void Md5_Register(sqlite3*); + extern int Md5_Register(sqlite3*,char**,const sqlite3_api_routines*); UNUSED_PARAMETER(clientData); UNUSED_PARAMETER(objc); @@ -299,7 +303,7 @@ static int sqlthread_open( } } #endif - Md5_Register(db); + Md5_Register(db, 0, 0); sqlite3_busy_handler(db, xBusy, 0); if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR; @@ -315,7 +319,7 @@ static int sqlthread_open( ** Return the current thread-id (Tcl_GetCurrentThread()) cast to ** an integer. */ -static int sqlthread_id( +static int SQLITE_TCLAPI sqlthread_id( ClientData clientData, Tcl_Interp *interp, int objc, @@ -333,7 +337,7 @@ static int sqlthread_id( /* ** Dispatch routine for the sub-commands of [sqlthread]. */ -static int sqlthread_proc( +static int SQLITE_TCLAPI sqlthread_proc( ClientData clientData, Tcl_Interp *interp, int objc, @@ -381,7 +385,7 @@ static int sqlthread_proc( ** implemented as a script in Tcl 8.5, it is not usually available to ** testfixture. */ -static int clock_seconds_proc( +static int SQLITE_TCLAPI clock_seconds_proc( ClientData clientData, Tcl_Interp *interp, int objc, @@ -543,7 +547,7 @@ int sqlite3_blocking_prepare_v2( ** ** Advance the statement to the next row. */ -static int blocking_step_proc( +static int SQLITE_TCLAPI blocking_step_proc( void * clientData, Tcl_Interp *interp, int objc, @@ -569,7 +573,7 @@ static int blocking_step_proc( ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar? ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar? */ -static int blocking_prepare_v2_proc( +static int SQLITE_TCLAPI blocking_prepare_v2_proc( void * clientData, Tcl_Interp *interp, int objc, diff --git a/src/test_vfs.c b/src/test_vfs.c index b476a42cb4..fb987a6163 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -28,7 +28,11 @@ #include "sqlite3.h" #include "sqliteInt.h" -#include +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; @@ -1036,7 +1040,7 @@ static int tvfsUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){ return sqlite3OsUnfetch(pFd->pReal, iOfst, p); } -static int testvfs_obj_cmd( +static int SQLITE_TCLAPI testvfs_obj_cmd( ClientData cd, Tcl_Interp *interp, int objc, @@ -1348,7 +1352,7 @@ static int testvfs_obj_cmd( return TCL_OK; } -static void testvfs_obj_del(ClientData cd){ +static void SQLITE_TCLAPI testvfs_obj_del(ClientData cd){ Testvfs *p = (Testvfs *)cd; if( p->pScript ) Tcl_DecrRefCount(p->pScript); sqlite3_vfs_unregister(p->pVfs); @@ -1391,7 +1395,7 @@ static void testvfs_obj_del(ClientData cd){ ** ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive" */ -static int testvfs_cmd( +static int SQLITE_TCLAPI testvfs_cmd( ClientData cd, Tcl_Interp *interp, int objc, diff --git a/src/tokenize.c b/src/tokenize.c index 62e770b79c..9230da2a0f 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -502,14 +502,26 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ assert( pParse->nVar==0 ); assert( pParse->nzVar==0 ); assert( pParse->azVar==0 ); - while( zSql[i]!=0 ){ + while( 1 ){ assert( i>=0 ); - pParse->sLastToken.z = &zSql[i]; - pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType); - i += pParse->sLastToken.n; - if( i>mxSqlLen ){ - pParse->rc = SQLITE_TOOBIG; - break; + if( zSql[i]!=0 ){ + pParse->sLastToken.z = &zSql[i]; + pParse->sLastToken.n = sqlite3GetToken((u8*)&zSql[i],&tokenType); + i += pParse->sLastToken.n; + if( i>mxSqlLen ){ + pParse->rc = SQLITE_TOOBIG; + break; + } + }else{ + /* Upon reaching the end of input, call the parser two more times + ** with tokens TK_SEMI and 0, in that order. */ + if( lastTokenParsed==TK_SEMI ){ + tokenType = 0; + }else if( lastTokenParsed==0 ){ + break; + }else{ + tokenType = TK_SEMI; + } } if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); @@ -530,15 +542,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ } assert( nErr==0 ); pParse->zTail = &zSql[i]; - if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ - assert( zSql[i]==0 ); - if( lastTokenParsed!=TK_SEMI ){ - sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse); - } - if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ - sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); - } - } #ifdef YYTRACKMAXSTACKDEPTH sqlite3_mutex_enter(sqlite3MallocMutex()); sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK, diff --git a/src/treeview.c b/src/treeview.c index 907159c06d..27996d46d7 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -445,6 +445,12 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ break; } #endif + case TK_MATCH: { + sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s", + pExpr->iTable, pExpr->iColumn, zFlgs); + sqlite3TreeViewExpr(pView, pExpr->pRight, 0); + break; + } default: { sqlite3TreeViewLine(pView, "op=%d", pExpr->op); break; diff --git a/src/update.c b/src/update.c index 7b38c5bcc5..1436501264 100644 --- a/src/update.c +++ b/src/update.c @@ -352,7 +352,8 @@ void sqlite3Update( if( HasRowid(pTab) ){ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur + pParse, pTabList, pWhere, 0, 0, + WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE, iIdxCur ); if( pWInfo==0 ) goto update_cleanup; okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); diff --git a/src/vacuum.c b/src/vacuum.c index 3dfa4f67aa..e120632aac 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -121,7 +121,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ int saved_flags; /* Saved value of the db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ - void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */ + u8 saved_mTrace; /* Saved trace settings */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ @@ -142,10 +142,10 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ saved_flags = db->flags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; - saved_xTrace = db->xTrace; + saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin; db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder); - db->xTrace = 0; + db->mTrace = 0; pMain = db->aDb[0].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); @@ -197,6 +197,8 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ } #endif + sqlite3BtreeSetCacheSize(pTemp, db->aDb[0].pSchema->cache_size); + sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF"); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -345,7 +347,7 @@ end_of_vacuum: db->flags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; - db->xTrace = saved_xTrace; + db->mTrace = saved_mTrace; sqlite3BtreeSetPageSize(pMain, -1, -1, 1); /* Currently there is an SQL level transaction open on the vacuum diff --git a/src/vdbe.c b/src/vdbe.c index ccac41c5b1..624ac2197e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -949,8 +949,6 @@ case OP_HaltIfNull: { /* in3 */ ** is the same as executing Halt. */ case OP_Halt: { - const char *zType; - const char *zLogFmt; VdbeFrame *pFrame; int pcx; @@ -979,34 +977,28 @@ case OP_Halt: { p->rc = pOp->p1; p->errorAction = (u8)pOp->p2; p->pc = pcx; + assert( pOp->p5>=0 && pOp->p5<=4 ); if( p->rc ){ if( pOp->p5 ){ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", "FOREIGN KEY" }; - assert( pOp->p5>=1 && pOp->p5<=4 ); testcase( pOp->p5==1 ); testcase( pOp->p5==2 ); testcase( pOp->p5==3 ); testcase( pOp->p5==4 ); - zType = azType[pOp->p5-1]; + sqlite3VdbeError(p, "%s constraint failed", azType[pOp->p5-1]); + if( pOp->p4.z ){ + p->zErrMsg = sqlite3MPrintf(db, "%z: %s", p->zErrMsg, pOp->p4.z); + } }else{ - zType = 0; - } - assert( zType!=0 || pOp->p4.z!=0 ); - zLogFmt = "abort at %d in [%s]: %s"; - if( zType && pOp->p4.z ){ - sqlite3VdbeError(p, "%s constraint failed: %s", zType, pOp->p4.z); - }else if( pOp->p4.z ){ sqlite3VdbeError(p, "%s", pOp->p4.z); - }else{ - sqlite3VdbeError(p, "%s constraint failed", zType); } - sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg); + sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg); } rc = sqlite3VdbeHalt(p); assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); if( rc==SQLITE_BUSY ){ - p->rc = rc = SQLITE_BUSY; + p->rc = SQLITE_BUSY; }else{ assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ); assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 ); @@ -1072,10 +1064,7 @@ case OP_String8: { /* same as TK_STRING, out2 */ #ifndef SQLITE_OMIT_UTF16 if( encoding!=SQLITE_UTF8 ){ rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); - if( rc ){ - assert( rc==SQLITE_TOOBIG ); /* This is the only possible error here */ - goto too_big; - } + assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG ); if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z ); assert( VdbeMemDynamic(pOut)==0 ); @@ -1088,10 +1077,12 @@ case OP_String8: { /* same as TK_STRING, out2 */ pOp->p4.z = pOut->z; pOp->p1 = pOut->n; } + testcase( rc==SQLITE_TOOBIG ); #endif if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } + assert( rc==SQLITE_OK ); /* Fall through to the next case, OP_String */ } @@ -1100,10 +1091,12 @@ case OP_String8: { /* same as TK_STRING, out2 */ ** ** The string value P4 of length P1 (bytes) is stored in register P2. ** -** If P5!=0 and the content of register P3 is greater than zero, then +** If P3 is not zero and the content of register P3 is equal to P5, then ** the datatype of the register P2 is converted to BLOB. The content is ** the same sequence of bytes, it is merely interpreted as a BLOB instead -** of a string, as if it had been CAST. +** of a string, as if it had been CAST. In other words: +** +** if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB) */ case OP_String: { /* out2 */ assert( pOp->p4.z!=0 ); @@ -1114,12 +1107,11 @@ case OP_String: { /* out2 */ pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS - if( pOp->p5 ){ - assert( pOp->p3>0 ); + if( pOp->p3>0 ){ assert( pOp->p3<=(p->nMem+1 - p->nCursor) ); pIn3 = &aMem[pOp->p3]; assert( pIn3->flags & MEM_Int ); - if( pIn3->u.i ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; + if( pIn3->u.i==pOp->p5 ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; } #endif break; @@ -1391,6 +1383,10 @@ case OP_ResultRow: { } if( db->mallocFailed ) goto no_mem; + if( db->mTrace & SQLITE_TRACE_ROW ){ + db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); + } + /* Return SQLITE_ROW */ p->pc = (int)(pOp - aOp) + 1; @@ -2024,6 +2020,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ if( (flags1 | flags3)&MEM_Str ){ if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn1,0); + flags3 = pIn3->flags; } if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn3,0); @@ -2036,6 +2033,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); + flags3 = pIn3->flags; } if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){ testcase( pIn3->flags & MEM_Int ); @@ -2388,7 +2386,6 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { - i64 payloadSize64; /* Number of bytes in the record */ int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ BtCursor *pCrsr; /* The BTree cursor */ @@ -2411,6 +2408,7 @@ case OP_Column: { /* If the cursor cache is stale, bring it up-to-date */ rc = sqlite3VdbeCursorMoveto(&pC, &p2); + if( rc ) goto abort_due_to_error; assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; @@ -2424,8 +2422,7 @@ case OP_Column: { assert( pC->eCurType!=CURTYPE_SORTER ); pCrsr = pC->uc.pCursor; - if( rc ) goto abort_due_to_error; - if( pC->cacheStatus!=p->cacheCtr ){ + if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/ if( pC->nullRow ){ if( pC->eCurType==CURTYPE_PSEUDO ){ assert( pC->uc.pseudoTableReg>0 ); @@ -2441,22 +2438,9 @@ case OP_Column: { }else{ assert( pC->eCurType==CURTYPE_BTREE ); assert( pCrsr ); - if( pC->isTable==0 ){ - assert( sqlite3BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for payloadSize64 to be - ** larger than 32 bits. */ - assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); - pC->aRow = sqlite3BtreeKeyFetch(pCrsr, &avail); - pC->payloadSize = (u32)payloadSize64; - }else{ - assert( sqlite3BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &pC->payloadSize); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - pC->aRow = sqlite3BtreeDataFetch(pCrsr, &avail); - } + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + pC->payloadSize = sqlite3BtreePayloadSize(pCrsr); + pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &avail); assert( avail<=65536 ); /* Maximum page size is 64KiB */ if( pC->payloadSize <= (u32)avail ){ pC->szRow = pC->payloadSize; @@ -2472,7 +2456,7 @@ case OP_Column: { aOffset[0] = offset; - if( availaRow does not have to hold the entire row, but it does at least ** need to cover the header of the record. If pC->aRow does not contain ** the complete header, then set it to zero, forcing the header to be @@ -2493,14 +2477,15 @@ case OP_Column: { rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } + }else if( offset>0 ){ /*OPTIMIZATION-IF-TRUE*/ + /* The following goto is an optimization. It can be omitted and + ** everything will still work. But OP_Column is measurably faster + ** by skipping the subsequent conditional, which is always true. + */ + zData = pC->aRow; + assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */ + goto op_column_read_header; } - - /* The following goto is an optimization. It can be omitted and - ** everything will still work. But OP_Column is measurably faster - ** by skipping the subsequent conditional, which is always true. - */ - assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */ - goto op_column_read_header; } /* Make sure at least the first p2+1 entries of the header have been @@ -2510,7 +2495,6 @@ case OP_Column: { /* If there is more header available for parsing in the record, try ** to extract additional fields up through the p2+1-th field */ - op_column_read_header: if( pC->iHdrOffsetaRow==0 ){ @@ -2523,11 +2507,11 @@ case OP_Column: { } /* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */ + op_column_read_header: i = pC->nHdrParsed; offset64 = aOffset[i]; zHdr = zData + pC->iHdrOffset; zEndHdr = zData + aOffset[0]; - assert( i<=p2 && zHdraType[i++] = t; aOffset[i] = (u32)(offset64 & 0xffffffff); }while( i<=p2 && zHdrnHdrParsed = i; - pC->iHdrOffset = (u32)(zHdr - zData); - + /* The record is corrupt if any of the following are true: ** (1) the bytes of the header extend past the declared header size ** (2) the entire header was used but not all data was used @@ -2554,8 +2536,10 @@ case OP_Column: { rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } - if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem); + pC->nHdrParsed = i; + pC->iHdrOffset = (u32)(zHdr - zData); + if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem); }else{ t = 0; } @@ -2583,9 +2567,10 @@ case OP_Column: { assert( p2nHdrParsed ); assert( rc==SQLITE_OK ); assert( sqlite3VdbeCheckMemInvariants(pDest) ); - if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest); + if( VdbeMemDynamic(pDest) ){ + sqlite3VdbeMemSetNull(pDest); + } assert( t==pC->aType[p2] ); - pDest->enc = encoding; if( pC->szRow>=aOffset[p2+1] ){ /* This is the common case where the desired content fits on the original ** page - where the content is not on an overflow page */ @@ -2599,6 +2584,7 @@ case OP_Column: { */ static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term }; pDest->n = len = (t-12)/2; + pDest->enc = encoding; if( pDest->szMalloc < len+2 ){ pDest->flags = MEM_Null; if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; @@ -2611,6 +2597,7 @@ case OP_Column: { pDest->flags = aFlag[t&1]; } }else{ + pDest->enc = encoding; /* This branch happens only when content is on overflow pages */ if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) @@ -4059,6 +4046,30 @@ case OP_Found: { /* jump, in3 */ break; } +/* Opcode: SeekRowid P1 P2 P3 * * +** Synopsis: intkey=r[P3] +** +** P1 is the index of a cursor open on an SQL table btree (with integer +** keys). If register P3 does not contain an integer or if P1 does not +** contain a record with rowid P3 then jump immediately to P2. +** Or, if P2 is 0, raise an SQLITE_CORRUPT error. If P1 does contain +** a record with rowid P3 then +** leave the cursor pointing at that record and fall through to the next +** instruction. +** +** The OP_NotExists opcode performs the same operation, but with OP_NotExists +** the P3 register must be guaranteed to contain an integer value. With this +** opcode, register P3 might not contain an integer. +** +** The OP_NotFound opcode performs the same operation on index btrees +** (with arbitrary multi-value keys). +** +** This opcode leaves the cursor in a state where it cannot be advanced +** in either direction. In other words, the Next and Prev opcodes will +** not work following this opcode. +** +** See also: Found, NotFound, NoConflict, SeekRowid +*/ /* Opcode: NotExists P1 P2 P3 * * ** Synopsis: intkey=r[P3] ** @@ -4069,6 +4080,10 @@ case OP_Found: { /* jump, in3 */ ** leave the cursor pointing at that record and fall through to the next ** instruction. ** +** The OP_SeekRowid opcode performs the same operation but also allows the +** P3 register to contain a non-integer value, in which case the jump is +** always taken. This opcode requires that P3 always contain an integer. +** ** The OP_NotFound opcode performs the same operation on index btrees ** (with arbitrary multi-value keys). ** @@ -4076,14 +4091,21 @@ case OP_Found: { /* jump, in3 */ ** in either direction. In other words, the Next and Prev opcodes will ** not work following this opcode. ** -** See also: Found, NotFound, NoConflict +** See also: Found, NotFound, NoConflict, SeekRowid */ -case OP_NotExists: { /* jump, in3 */ +case OP_SeekRowid: { /* jump, in3 */ VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; + pIn3 = &aMem[pOp->p3]; + if( (pIn3->flags & MEM_Int)==0 ){ + applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding); + if( (pIn3->flags & MEM_Int)==0 ) goto jump_to_p2; + } + /* Fall through into OP_NotExists */ +case OP_NotExists: /* jump, in3 */ pIn3 = &aMem[pOp->p3]; assert( pIn3->flags & MEM_Int ); assert( pOp->p1>=0 && pOp->p1nCursor ); @@ -4202,8 +4224,7 @@ case OP_NewRowid: { /* out2 */ v = 1; /* IMP: R-61914-48074 */ }else{ assert( sqlite3BtreeCursorIsValid(pC->uc.pCursor) ); - rc = sqlite3BtreeKeySize(pC->uc.pCursor, &v); - assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ + v = sqlite3BtreeIntegerKey(pC->uc.pCursor); if( v>=MAX_ROWID ){ pC->useRandomRowid = 1; }else{ @@ -4286,10 +4307,12 @@ case OP_NewRowid: { /* out2 */ ** sqlite3_last_insert_rowid() function (otherwise it is unmodified). ** ** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of -** the last seek operation (OP_NotExists) was a success, then this +** the last seek operation (OP_NotExists or OP_SeekRowid) was a success, +** then this ** operation will not attempt to find the appropriate row before doing ** the insert but will instead overwrite the row that the cursor is -** currently pointing to. Presumably, the prior OP_NotExists opcode +** currently pointing to. Presumably, the prior OP_NotExists or +** OP_SeekRowid opcode ** has already positioned the cursor correctly. This is an optimization ** that boosts performance by avoiding redundant seeks. ** @@ -4321,13 +4344,12 @@ case OP_Insert: case OP_InsertInt: { Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ - i64 iKey; /* The integer ROWID or key for the record to be inserted */ VdbeCursor *pC; /* Cursor to table into which insert is written */ - int nZero; /* Number of zero-bytes to append */ int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ const char *zDb; /* database name - used by the update hook */ Table *pTab; /* Table structure - used by update and pre-update hooks */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ + BtreePayload x; /* Payload to be inserted */ op = 0; pData = &aMem[pOp->p2]; @@ -4346,10 +4368,10 @@ case OP_InsertInt: { assert( pKey->flags & MEM_Int ); assert( memIsValid(pKey) ); REGISTER_TRACE(pOp->p3, pKey); - iKey = pKey->u.i; + x.nKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); - iKey = pOp->p3; + x.nKey = pOp->p3; } if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ @@ -4370,26 +4392,28 @@ case OP_InsertInt: { && pOp->p4type==P4_TABLE && !(pOp->p5 & OPFLAG_ISUPDATE) ){ - sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, iKey, pOp->p2); + sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2); } #endif if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = iKey; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = x.nKey; if( pData->flags & MEM_Null ){ - pData->z = 0; - pData->n = 0; + x.pData = 0; + x.nData = 0; }else{ assert( pData->flags & (MEM_Blob|MEM_Str) ); + x.pData = pData->z; + x.nData = pData->n; } seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); if( pData->flags & MEM_Zero ){ - nZero = pData->u.nZero; + x.nZero = pData->u.nZero; }else{ - nZero = 0; + x.nZero = 0; } - rc = sqlite3BtreeInsert(pC->uc.pCursor, 0, iKey, - pData->z, pData->n, nZero, + x.pKey = 0; + rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, (pOp->p5 & OPFLAG_APPEND)!=0, seekResult ); pC->deferredMoveto = 0; @@ -4398,7 +4422,7 @@ case OP_InsertInt: { /* Invoke the update-hook if required. */ if( rc ) goto abort_due_to_error; if( db->xUpdateCallback && op ){ - db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, iKey); + db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, x.nKey); } break; } @@ -4457,8 +4481,7 @@ case OP_Delete: { /* If p5 is zero, the seek operation that positioned the cursor prior to ** OP_Delete will have also set the pC->movetoTarget field to the rowid of ** the row that is being deleted */ - i64 iKey = 0; - sqlite3BtreeKeySize(pC->uc.pCursor, &iKey); + i64 iKey = sqlite3BtreeIntegerKey(pC->uc.pCursor); assert( pC->movetoTarget==iKey ); } #endif @@ -4474,7 +4497,7 @@ case OP_Delete: { zDb = db->aDb[pC->iDb].zName; pTab = pOp->p4.pTab; if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){ - sqlite3BtreeKeySize(pC->uc.pCursor, &pC->movetoTarget); + pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor); } }else{ zDb = 0; /* Not needed. Silence a compiler warning. */ @@ -4628,7 +4651,6 @@ case OP_RowData: { VdbeCursor *pC; BtCursor *pCrsr; u32 n; - i64 n64; pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); @@ -4646,8 +4668,9 @@ case OP_RowData: { pCrsr = pC->uc.pCursor; /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or - ** OP_Rewind/Op_Next with no intervening instructions that might invalidate - ** the cursor. If this where not the case, on of the following assert()s + ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions + ** that might invalidate the cursor. + ** If this where not the case, on of the following assert()s ** would fail. Should this ever change (because of changes in the code ** generator) then the fix would be to insert a call to ** sqlite3VdbeCursorMoveto(). @@ -4659,20 +4682,9 @@ case OP_RowData: { if( rc!=SQLITE_OK ) goto abort_due_to_error; #endif - if( pC->isTable==0 ){ - assert( !pC->isTable ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &n64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - if( n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - n = (u32)n64; - }else{ - VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &n); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } + n = sqlite3BtreePayloadSize(pCrsr); + if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } testcase( n==0 ); if( sqlite3VdbeMemClearAndResize(pOut, MAX(n,32)) ){ @@ -4737,8 +4749,7 @@ case OP_Rowid: { /* out2 */ pOut->flags = MEM_Null; break; } - rc = sqlite3BtreeKeySize(pC->uc.pCursor, &v); - assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */ + v = sqlite3BtreeIntegerKey(pC->uc.pCursor); } pOut->u.i = v; break; @@ -5013,8 +5024,7 @@ next_tail: case OP_SorterInsert: /* in2 */ case OP_IdxInsert: { /* in2 */ VdbeCursor *pC; - int nKey; - const char *zKey; + BtreePayload x; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; @@ -5030,9 +5040,12 @@ case OP_IdxInsert: { /* in2 */ if( pOp->opcode==OP_SorterInsert ){ rc = sqlite3VdbeSorterWrite(pC, pIn2); }else{ - nKey = pIn2->n; - zKey = pIn2->z; - rc = sqlite3BtreeInsert(pC->uc.pCursor, zKey, nKey, "", 0, 0, pOp->p3, + x.nKey = pIn2->n; + x.pKey = pIn2->z; + x.nData = 0; + x.nZero = 0; + x.pData = 0; + rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, pOp->p3, ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) ); assert( pC->deferredMoveto==0 ); @@ -6006,21 +6019,6 @@ case OP_DecrJumpZero: { /* jump, in1 */ } -/* Opcode: JumpZeroIncr P1 P2 * * * -** Synopsis: if (r[P1]++)==0 ) goto P2 -** -** The register P1 must contain an integer. If register P1 is initially -** zero, then jump to P2. Increment register P1 regardless of whether or -** not the jump is taken. -*/ -case OP_JumpZeroIncr: { /* jump, in1 */ - pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags&MEM_Int ); - VdbeBranchTaken(pIn1->u.i==0, 2); - if( (pIn1->u.i++)==0 ) goto jump_to_p2; - break; -} - /* Opcode: AggStep0 * P2 P3 P4 P5 ** Synopsis: accum=r[P3] step(r[P2@P5]) ** @@ -6823,16 +6821,34 @@ case OP_MaxPgcnt: { /* out2 */ */ case OP_Init: { /* jump */ char *zTrace; - char *z; + + /* If the P4 argument is not NULL, then it must be an SQL comment string. + ** The "--" string is broken up to prevent false-positives with srcck1.c. + ** + ** This assert() provides evidence for: + ** EVIDENCE-OF: R-50676-09860 The callback can compute the same text that + ** would have been returned by the legacy sqlite3_trace() interface by + ** using the X argument when X begins with "--" and invoking + ** sqlite3_expanded_sql(P) otherwise. + */ + assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 ); #ifndef SQLITE_OMIT_TRACE - if( db->xTrace + if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0 && !p->doingRerun && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ - z = sqlite3VdbeExpandSql(p, zTrace); - db->xTrace(db->pTraceArg, z); - sqlite3DbFree(db, z); +#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); + sqlite3_free(z); + }else +#endif + { + (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); + } } #ifdef SQLITE_USE_FCNTL_TRACE zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); diff --git a/src/vdbe.h b/src/vdbe.h index 3db32c8552..a7bc84cea5 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,8 +15,8 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. */ -#ifndef _SQLITE_VDBE_H_ -#define _SQLITE_VDBE_H_ +#ifndef SQLITE_VDBE_H +#define SQLITE_VDBE_H #include /* @@ -309,4 +309,4 @@ void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); # define sqlite3VdbeScanStatus(a,b,c,d,e) #endif -#endif +#endif /* SQLITE_VDBE_H */ diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 61561e9dce..ecb4fc5f06 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -15,8 +15,8 @@ ** 6000 lines long) it was split up into several smaller files and ** this header information was factored out. */ -#ifndef _VDBEINT_H_ -#define _VDBEINT_H_ +#ifndef SQLITE_VDBEINT_H +#define SQLITE_VDBEINT_H /* ** The maximum number of times that a statement will try to reparse @@ -558,4 +558,4 @@ int sqlite3VdbeMemHandleBom(Mem *pMem); #define ExpandBlob(P) SQLITE_OK #endif -#endif /* !defined(_VDBEINT_H_) */ +#endif /* !defined(SQLITE_VDBEINT_H) */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 1feecb3e11..b17c0e0a40 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -60,12 +60,19 @@ static int vdbeSafetyNotNull(Vdbe *p){ */ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ sqlite3_int64 iNow; + sqlite3_int64 iElapse; assert( p->startTime>0 ); - assert( db->xProfile!=0 ); + assert( db->xProfile!=0 || (db->mTrace & SQLITE_TRACE_PROFILE)!=0 ); assert( db->init.busy==0 ); assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); - db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000); + iElapse = (iNow - p->startTime)*1000000; + if( db->xProfile ){ + db->xProfile(db->pProfileArg, p->zSql, iElapse); + } + if( db->mTrace & SQLITE_TRACE_PROFILE ){ + db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); + } p->startTime = 0; } /* @@ -569,7 +576,8 @@ static int sqlite3Step(Vdbe *p){ ); #ifndef SQLITE_OMIT_TRACE - if( db->xProfile && !db->init.busy && p->zSql ){ + if( (db->xProfile || (db->mTrace & SQLITE_TRACE_PROFILE)!=0) + && !db->init.busy && p->zSql ){ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); }else{ assert( p->startTime==0 ); @@ -1604,6 +1612,39 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ return (int)v; } +/* +** Return the SQL associated with a prepared statement +*/ +const char *sqlite3_sql(sqlite3_stmt *pStmt){ + Vdbe *p = (Vdbe *)pStmt; + return p ? p->zSql : 0; +} + +/* +** Return the SQL associated with a prepared statement with +** bound parameters expanded. Space to hold the returned string is +** obtained from sqlite3_malloc(). The caller is responsible for +** freeing the returned string by passing it to sqlite3_free(). +** +** The SQLITE_TRACE_SIZE_LIMIT puts an upper bound on the size of +** expanded bound parameters. +*/ +char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){ +#ifdef SQLITE_OMIT_TRACE + return 0; +#else + char *z = 0; + const char *zSql = sqlite3_sql(pStmt); + if( zSql ){ + Vdbe *p = (Vdbe *)pStmt; + sqlite3_mutex_enter(p->db->mutex); + z = sqlite3VdbeExpandSql(p, zSql); + sqlite3_mutex_leave(p->db->mutex); + } + return z; +#endif +} + #ifdef SQLITE_ENABLE_PREUPDATE_HOOK /* ** Allocate and populate an UnpackedRecord structure based on the serialized @@ -1650,8 +1691,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ u32 nRec; u8 *aRec; - rc = sqlite3BtreeDataSize(p->pCsr->uc.pCursor, &nRec); - if( rc!=SQLITE_OK ) goto preupdate_old_out; + nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); aRec = sqlite3DbMallocRaw(db, nRec); if( !aRec ) goto preupdate_old_out; rc = sqlite3BtreeData(p->pCsr->uc.pCursor, 0, nRec, aRec); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index aab720249c..5a1c9fa3fb 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -64,14 +64,6 @@ void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){ p->isPrepareV2 = (u8)isPrepareV2; } -/* -** Return the SQL associated with a prepared statement -*/ -const char *sqlite3_sql(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe *)pStmt; - return p ? p->zSql : 0; -} - /* ** Swap all content between two VDBE structures. */ @@ -791,7 +783,7 @@ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ ** the FuncDef is not ephermal, then do nothing. */ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ - if( ALWAYS(pDef) && (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ + if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){ sqlite3DbFree(db, pDef); } } @@ -801,12 +793,20 @@ static void vdbeFreeOpArray(sqlite3 *, Op *, int); /* ** Delete a P4 value if necessary. */ +static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){ + if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); + sqlite3DbFree(db, p); +} +static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){ + freeEphemeralFunction(db, p->pFunc); + sqlite3DbFree(db, p); +} static void freeP4(sqlite3 *db, int p4type, void *p4){ assert( db ); switch( p4type ){ case P4_FUNCCTX: { - freeEphemeralFunction(db, ((sqlite3_context*)p4)->pFunc); - /* Fall through into the next case */ + freeP4FuncCtx(db, (sqlite3_context*)p4); + break; } case P4_REAL: case P4_INT64: @@ -837,9 +837,7 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ if( db->pnBytesFreed==0 ){ sqlite3ValueFree((sqlite3_value*)p4); }else{ - Mem *p = (Mem*)p4; - if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc); - sqlite3DbFree(db, p); + freeP4Mem(db, (Mem*)p4); } break; } @@ -4329,8 +4327,7 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){ ** this code can safely assume that nCellKey is 32-bits */ assert( sqlite3BtreeCursorIsValid(pCur) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); - assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ + nCellKey = sqlite3BtreePayloadSize(pCur); assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey ); /* Read in the complete content of the index entry */ @@ -4407,8 +4404,7 @@ int sqlite3VdbeIdxKeyCompare( assert( pC->eCurType==CURTYPE_BTREE ); pCur = pC->uc.pCursor; assert( sqlite3BtreeCursorIsValid(pCur) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); - assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ + nCellKey = sqlite3BtreePayloadSize(pCur); /* nCellKey will always be between 0 and 0xffffffff because of the way ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ diff --git a/src/vdbeblob.c b/src/vdbeblob.c index f2b3ffef2b..01827f94d0 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -415,7 +415,7 @@ static int blobReadWrite( ** anyhow. */ sqlite3_int64 iKey; - sqlite3BtreeKeySize(p->pCsr, &iKey); + iKey = sqlite3BtreeIntegerKey(p->pCsr); sqlite3VdbePreUpdateHook( v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1 ); diff --git a/src/vdbemem.c b/src/vdbemem.c index f9396e570c..04cb9c5c63 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -991,11 +991,7 @@ int sqlite3VdbeMemFromBtree( /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() ** that both the BtShared and database handle mutexes are held. */ assert( (pMem->flags & MEM_RowSet)==0 ); - if( key ){ - zData = (char *)sqlite3BtreeKeyFetch(pCur, &available); - }else{ - zData = (char *)sqlite3BtreeDataFetch(pCur, &available); - } + zData = (char *)sqlite3BtreePayloadFetch(pCur, &available); assert( zData!=0 ); if( offset+amt<=available ){ diff --git a/src/vdbesort.c b/src/vdbesort.c index 7d5146cf0b..69619802e8 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -1342,19 +1342,18 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){ /* ** Merge the two sorted lists p1 and p2 into a single list. -** Set *ppOut to the head of the new list. */ -static void vdbeSorterMerge( +static SorterRecord *vdbeSorterMerge( SortSubtask *pTask, /* Calling thread context */ SorterRecord *p1, /* First list to merge */ - SorterRecord *p2, /* Second list to merge */ - SorterRecord **ppOut /* OUT: Head of merged list */ + SorterRecord *p2 /* Second list to merge */ ){ SorterRecord *pFinal = 0; SorterRecord **pp = &pFinal; int bCached = 0; - while( p1 && p2 ){ + assert( p1!=0 && p2!=0 ); + for(;;){ int res; res = pTask->xCompare( pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal @@ -1364,15 +1363,22 @@ static void vdbeSorterMerge( *pp = p1; pp = &p1->u.pNext; p1 = p1->u.pNext; + if( p1==0 ){ + *pp = p2; + break; + } }else{ *pp = p2; pp = &p2->u.pNext; p2 = p2->u.pNext; bCached = 0; + if( p2==0 ){ + *pp = p1; + break; + } } } - *pp = p1 ? p1 : p2; - *ppOut = pFinal; + return pFinal; } /* @@ -1425,7 +1431,7 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ p->u.pNext = 0; for(i=0; aSlot[i]; i++){ - vdbeSorterMerge(pTask, p, aSlot[i], &p); + p = vdbeSorterMerge(pTask, p, aSlot[i]); aSlot[i] = 0; } aSlot[i] = p; @@ -1434,7 +1440,8 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ p = 0; for(i=0; i<64; i++){ - vdbeSorterMerge(pTask, p, aSlot[i], &p); + if( aSlot[i]==0 ) continue; + p = p ? vdbeSorterMerge(pTask, p, aSlot[i]) : aSlot[i]; } pList->pList = p; diff --git a/src/vdbetrace.c b/src/vdbetrace.c index 07235c931b..7b47363991 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -81,10 +81,13 @@ char *sqlite3VdbeExpandSql( int i; /* Loop counter */ Mem *pVar; /* Value of a host parameter */ StrAccum out; /* Accumulate the output here */ +#ifndef SQLITE_OMIT_UTF16 + Mem utf8; /* Used to convert UTF16 parameters into UTF8 for display */ +#endif char zBase[100]; /* Initial working space */ db = p->db; - sqlite3StrAccumInit(&out, db, zBase, sizeof(zBase), + sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); if( db->nVdbeExec>1 ){ while( *zRawSql ){ @@ -135,12 +138,14 @@ char *sqlite3VdbeExpandSql( int nOut; /* Number of bytes of the string text to include in output */ #ifndef SQLITE_OMIT_UTF16 u8 enc = ENC(db); - Mem utf8; if( enc!=SQLITE_UTF8 ){ memset(&utf8, 0, sizeof(utf8)); utf8.db = db; sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); - sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8); + if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){ + out.accError = STRACCUM_NOMEM; + out.nAlloc = 0; + } pVar = &utf8; } #endif @@ -182,6 +187,7 @@ char *sqlite3VdbeExpandSql( } } } + if( out.accError ) sqlite3StrAccumReset(&out); return sqlite3StrAccumFinish(&out); } diff --git a/src/vtab.c b/src/vtab.c index 802a8cac3a..45c5e79abd 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -418,7 +418,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ v = sqlite3GetVdbe(pParse); sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); + sqlite3VdbeAddOp0(v, OP_Expire); zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); @@ -754,10 +754,24 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ && (pParse->pNewTable->tabFlags & TF_Virtual)==0 ){ if( !pTab->aCol ){ - pTab->aCol = pParse->pNewTable->aCol; - pTab->nCol = pParse->pNewTable->nCol; - pParse->pNewTable->nCol = 0; - pParse->pNewTable->aCol = 0; + Table *pNew = pParse->pNewTable; + Index *pIdx; + pTab->aCol = pNew->aCol; + pTab->nCol = pNew->nCol; + pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); + pNew->nCol = 0; + pNew->aCol = 0; + assert( pTab->pIndex==0 ); + if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){ + rc = SQLITE_ERROR; + } + pIdx = pNew->pIndex; + if( pIdx ){ + assert( pIdx->pNext==0 ); + pTab->pIndex = pIdx; + pNew->pIndex = 0; + pIdx->pTable = pTab; + } } pCtx->bDeclared = 1; }else{ @@ -793,7 +807,7 @@ int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ + if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){ VTable *p; int (*xDestroy)(sqlite3_vtab *); for(p=pTab->pVTable; p; p=p->pNext){ @@ -933,7 +947,10 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ if( rc==SQLITE_OK ){ int iSvpt = db->nStatement + db->nSavepoint; addToVTrans(db, pVTab); - if( iSvpt ) rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, iSvpt-1); + if( iSvpt && pModule->xSavepoint ){ + pVTab->iSavepoint = iSvpt; + rc = pModule->xSavepoint(pVTab->pVtab, iSvpt-1); + } } } } @@ -1087,7 +1104,7 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ } /* -** Check to see if virtual tale module pMod can be have an eponymous +** Check to see if virtual table module pMod can be have an eponymous ** virtual table instance. If it can, create one if one does not already ** exist. Return non-zero if the eponymous virtual table instance exists ** when this routine returns, and return zero if it does not exist. @@ -1104,17 +1121,18 @@ int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ const sqlite3_module *pModule = pMod->pModule; Table *pTab; char *zErr = 0; - int nName; int rc; sqlite3 *db = pParse->db; if( pMod->pEpoTab ) return 1; if( pModule->xCreate!=0 && pModule->xCreate!=pModule->xConnect ) return 0; - nName = sqlite3Strlen30(pMod->zName) + 1; - pTab = sqlite3DbMallocZero(db, sizeof(Table) + nName); + pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return 0; + pTab->zName = sqlite3DbStrDup(db, pMod->zName); + if( pTab->zName==0 ){ + sqlite3DbFree(db, pTab); + return 0; + } pMod->pEpoTab = pTab; - pTab->zName = (char*)&pTab[1]; - memcpy(pTab->zName, pMod->zName, nName); pTab->nRef = 1; pTab->pSchema = db->aDb[0].pSchema; pTab->tabFlags |= TF_Virtual; @@ -1140,9 +1158,11 @@ int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){ Table *pTab = pMod->pEpoTab; if( pTab!=0 ){ - sqlite3DeleteColumnNames(db, pTab); - sqlite3VtabClear(db, pTab); - sqlite3DbFree(db, pTab); + /* Mark the table as Ephemeral prior to deleting it, so that the + ** sqlite3DeleteTable() routine will know that it is not stored in + ** the schema. */ + pTab->tabFlags |= TF_Ephemeral; + sqlite3DeleteTable(db, pTab); pMod->pEpoTab = 0; } } diff --git a/src/wal.c b/src/wal.c index 808f806a20..4609521d67 100644 --- a/src/wal.c +++ b/src/wal.c @@ -3333,16 +3333,21 @@ int sqlite3WalFrames( ** past the sector boundary is written after the sync. */ if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){ + int bSync = 1; if( pWal->padToSectorBoundary ){ int sectorSize = sqlite3SectorSize(pWal->pWalFd); w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize; + bSync = (w.iSyncPoint==iOffset); + testcase( bSync ); while( iOffsetxExprCallback(pWalker, pExpr); - if( rc==WRC_Continue - && !ExprHasProperty(pExpr,EP_TokenOnly) ){ - if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; - }else{ - if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; - } + if( rc || ExprHasProperty(pExpr,EP_TokenOnly) ) return rc & WRC_Abort; + if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort; + if( pExpr->pRight && walkExpr(pWalker, pExpr->pRight) ) return WRC_Abort; + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort; + }else{ + if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; } - return rc & WRC_Abort; + return WRC_Continue; } int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; diff --git a/src/where.c b/src/where.c index ca810a3b16..c2706dc5dd 100644 --- a/src/where.c +++ b/src/where.c @@ -51,6 +51,18 @@ int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ return pWInfo->nOBSat; } +/* +** Return TRUE if the innermost loop of the WHERE clause implementation +** returns rows in ORDER BY order for complete run of the inner loop. +** +** Across multiple iterations of outer loops, the output rows need not be +** sorted. As long as rows are sorted for just the innermost loop, this +** routine can return TRUE. +*/ +int sqlite3WhereOrderedInnerLoop(WhereInfo *pWInfo){ + return pWInfo->bOrderedInnerLoop; +} + /* ** Return the VDBE address or label to jump to in order to continue ** immediately with the next row of a WHERE clause. @@ -261,7 +273,10 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ ** ** The scanner will be searching the WHERE clause pWC. It will look ** for terms of the form "X " where X is column iColumn of table -** iCur. The must be one of the operators described by opMask. +** iCur. Or if pIdx!=0 then X is column iColumn of index pIdx. pIdx +** must be one of the indexes of table iCur. +** +** The must be one of the operators described by opMask. ** ** If the search is for X and the WHERE clause contains terms of the ** form X=Y then this routine might also return terms of the form @@ -309,11 +324,12 @@ static WhereTerm *whereScanInit( /* ** Search for a term in the WHERE clause that is of the form "X " -** where X is a reference to the iColumn of table iCur and is one of -** the WO_xx operator codes specified by the op parameter. -** Return a pointer to the term. Return 0 if not found. +** where X is a reference to the iColumn of table iCur or of index pIdx +** if pIdx!=0 and is one of the WO_xx operator codes specified by +** the op parameter. Return a pointer to the term. Return 0 if not found. ** -** If pIdx!=0 then search for terms matching the iColumn-th column of pIdx +** If pIdx!=0 then it must be one of the indexes of table iCur. +** Search for terms matching the iColumn-th column of pIdx ** rather than the iColumn-th column of table iCur. ** ** The term returned might by Y= if there is another constraint in @@ -1632,19 +1648,41 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){ sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm); }else{ char zType[4]; + char zLeft[50]; memcpy(zType, "...", 4); if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L'; + if( pTerm->eOperator & WO_SINGLE ){ + sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", + pTerm->leftCursor, pTerm->u.leftColumn); + }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){ + sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld", + pTerm->u.pOrInfo->indexable); + }else{ + sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor); + } sqlite3DebugPrintf( - "TERM-%-3d %p %s cursor=%-3d prob=%-3d op=0x%03x wtFlags=0x%04x\n", - iTerm, pTerm, zType, pTerm->leftCursor, pTerm->truthProb, + "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x\n", + iTerm, pTerm, zType, zLeft, pTerm->truthProb, pTerm->eOperator, pTerm->wtFlags); sqlite3TreeViewExpr(0, pTerm->pExpr, 0); } } #endif +#ifdef WHERETRACE_ENABLED +/* +** Show the complete content of a WhereClause +*/ +void sqlite3WhereClausePrint(WhereClause *pWC){ + int i; + for(i=0; inTerm; i++){ + whereTermPrint(&pWC->a[i], i); + } +} +#endif + #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes @@ -2631,7 +2669,7 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if( !pBuilder->pOrSet /* Not part of an OR optimization */ - && (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0 + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ @@ -2663,6 +2701,7 @@ static int whereLoopAddBtree( pNew->rSetup += 24; } ApplyCostMultiplier(pNew->rSetup, pTab->costMult); + if( pNew->rSetup<0 ) pNew->rSetup = 0; /* TUNING: Each index lookup yields 20 rows in the table. This ** is more than the usual guess of 10 rows, since we have no way ** of knowing how selective the index will ultimately be. It would @@ -2723,6 +2762,7 @@ static int whereLoopAddBtree( /* Full scan via index */ if( b || !HasRowid(pTab) + || pProbe->pPartIdxWhere!=0 || ( m==0 && pProbe->bUnordered==0 && (pProbe->szIdxRowszTabRow) @@ -2735,11 +2775,34 @@ static int whereLoopAddBtree( /* The cost of visiting the index rows is N*K, where K is ** between 1.1 and 3.0, depending on the relative sizes of the - ** index and table rows. If this is a non-covering index scan, - ** also add the cost of visiting table rows (N*3.0). */ + ** index and table rows. */ pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow; if( m!=0 ){ - pNew->rRun = sqlite3LogEstAdd(pNew->rRun, rSize+16); + /* If this is a non-covering index scan, add in the cost of + ** doing table lookups. The cost will be 3x the number of + ** lookups. Take into account WHERE clause terms that can be + ** satisfied using just the index, and that do not require a + ** table lookup. */ + LogEst nLookup = rSize + 16; /* Base cost: N*3 */ + int ii; + int iCur = pSrc->iCursor; + WhereClause *pWC2 = &pWInfo->sWC; + for(ii=0; iinTerm; ii++){ + WhereTerm *pTerm = &pWC2->a[ii]; + if( !sqlite3ExprCoveredByIndex(pTerm->pExpr, iCur, pProbe) ){ + break; + } + /* pTerm can be evaluated using just the index. So reduce + ** the expected number of table lookups accordingly */ + if( pTerm->truthProb<=0 ){ + nLookup += pTerm->truthProb; + }else{ + nLookup--; + if( pTerm->eOperator & (WO_EQ|WO_IS) ) nLookup -= 19; + } + } + + pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup); } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); @@ -3108,9 +3171,7 @@ static int whereLoopAddOr( WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n", (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); if( sqlite3WhereTrace & 0x400 ){ - for(i=0; inTerm; i++){ - whereTermPrint(&sSubBuild.pWC->a[i], i); - } + sqlite3WhereClausePrint(sSubBuild.pWC); } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -3203,6 +3264,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ mPrereq = mPrior; } priorJointype = pItem->fg.jointype; +#ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ struct SrcList_item *p; for(p=&pItem[1]; paLoop[] */ WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */ Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */ @@ -3257,6 +3321,7 @@ static i8 wherePathSatisfiesOrderBy( u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */ u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */ u8 isMatch; /* iColumn matches a term of the ORDER BY clause */ + u16 eqOpMask; /* Allowed equality operators */ u16 nKeyCol; /* Number of key columns in pIndex */ u16 nColumn; /* Total number of ordered columns in the index */ u16 nOrderBy; /* Number terms in the ORDER BY clause */ @@ -3307,9 +3372,16 @@ static i8 wherePathSatisfiesOrderBy( obDone = MASKBIT(nOrderBy)-1; orderDistinctMask = 0; ready = 0; + eqOpMask = WO_EQ | WO_IS | WO_ISNULL; + if( wctrlFlags & WHERE_ORDERBY_LIMIT ) eqOpMask |= WO_IN; for(iLoop=0; isOrderDistinct && obSat0 ) ready |= pLoop->maskSelf; - pLoop = iLoopaLoop[iLoop] : pLast; + if( iLoopaLoop[iLoop]; + if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue; + }else{ + pLoop = pLast; + } if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ if( pLoop->u.vtab.isOrdered ) obSat = obDone; break; @@ -3327,7 +3399,7 @@ static i8 wherePathSatisfiesOrderBy( if( pOBExpr->op!=TK_COLUMN ) continue; if( pOBExpr->iTable!=iCur ) continue; pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, - ~ready, WO_EQ|WO_ISNULL|WO_IS, 0); + ~ready, eqOpMask, 0); if( pTerm==0 ) continue; if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){ const char *z1, *z2; @@ -3367,10 +3439,12 @@ static i8 wherePathSatisfiesOrderBy( for(j=0; ju.btree.nEq && pLoop->nSkip==0 - && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL|WO_IS))!=0 + && ((i = pLoop->aLTerm[j]->eOperator) & eqOpMask)!=0 ){ if( i & WO_ISNULL ){ testcase( isOrderDistinct ); @@ -3894,8 +3968,19 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } }else{ pWInfo->nOBSat = pFrom->isOrdered; - if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0; pWInfo->revMask = pFrom->revLoop; + if( pWInfo->nOBSat<=0 ){ + pWInfo->nOBSat = 0; + if( nLoop>0 && (pFrom->aLoop[nLoop-1]->wsFlags & WHERE_ONEROW)==0 ){ + Bitmask m = 0; + int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom, + WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m); + if( rc==pWInfo->pOrderBy->nExpr ){ + pWInfo->bOrderedInnerLoop = 1; + pWInfo->revMask = m; + } + } + } } if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0 @@ -3943,7 +4028,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ Index *pIdx; pWInfo = pBuilder->pWInfo; - if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0; + if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; pTab = pItem->pTab; @@ -4090,7 +4175,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ ** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. ** ** The iIdxCur parameter is the cursor number of an index. If -** WHERE_ONETABLE_ONLY is set, iIdxCur is the cursor number of an index +** WHERE_OR_SUBCLAUSE is set, iIdxCur is the cursor number of an index ** to use for OR clause processing. The WHERE clause should use this ** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is ** the first cursor in an array of cursors for all indices. iIdxCur should @@ -4104,7 +4189,7 @@ WhereInfo *sqlite3WhereBegin( ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pDistinctSet, /* Try not to output two rows that duplicate these */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ - int iAuxArg /* If WHERE_ONETABLE_ONLY is set, index cursor number + int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ ){ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ @@ -4123,11 +4208,11 @@ WhereInfo *sqlite3WhereBegin( assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || ( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 - && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 + && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 )); - /* Only one of WHERE_ONETABLE_ONLY or WHERE_USE_LIMIT */ - assert( (wctrlFlags & WHERE_ONETABLE_ONLY)==0 + /* Only one of WHERE_OR_SUBCLAUSE or WHERE_USE_LIMIT */ + assert( (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 || (wctrlFlags & WHERE_USE_LIMIT)==0 ); /* Variable initialization */ @@ -4155,11 +4240,11 @@ WhereInfo *sqlite3WhereBegin( } /* This function normally generates a nested loop for all tables in - ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should + ** pTabList. But if the WHERE_OR_SUBCLAUSE flag is set, then we should ** only generate code for the first table in pTabList and assume that ** any cursors associated with subsequent tables are uninitialized. */ - nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc; + nTabList = (wctrlFlags & WHERE_OR_SUBCLAUSE) ? 1 : pTabList->nSrc; /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo @@ -4235,7 +4320,7 @@ WhereInfo *sqlite3WhereBegin( ** Note that bitmasks are created for all pTabList->nSrc tables in ** pTabList, not just the first nTabList tables. nTabList is normally ** equal to pTabList->nSrc but might be shortened to 1 if the - ** WHERE_ONETABLE_ONLY flag is set. + ** WHERE_OR_SUBCLAUSE flag is set. */ for(ii=0; iinSrc; ii++){ createMask(pMaskSet, pTabList->a[ii].iCursor); @@ -4273,10 +4358,7 @@ WhereInfo *sqlite3WhereBegin( sqlite3DebugPrintf(")\n"); } if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ - int i; - for(i=0; inTerm; i++){ - whereTermPrint(&sWLB.pWC->a[i], i); - } + sqlite3WhereClausePrint(sWLB.pWC); } #endif @@ -4418,7 +4500,7 @@ WhereInfo *sqlite3WhereBegin( }else #endif if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 - && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ + && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ int op = OP_OpenRead; if( pWInfo->eOnePass!=ONEPASS_OFF ){ op = OP_OpenWrite; @@ -4457,7 +4539,7 @@ WhereInfo *sqlite3WhereBegin( /* iAuxArg is always set if to a positive value if ONEPASS is possible */ assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx) - && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 + && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ /* This is one term of an OR-optimization using the PRIMARY KEY of a ** WITHOUT ROWID table. No need for a separate index */ @@ -4473,9 +4555,9 @@ WhereInfo *sqlite3WhereBegin( } op = OP_OpenWrite; pWInfo->aiCurOnePass[1] = iIndexCur; - }else if( iAuxArg && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){ + }else if( iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ iIndexCur = iAuxArg; - if( wctrlFlags & WHERE_REOPEN_IDX ) op = OP_ReopenIdx; + op = OP_ReopenIdx; }else{ iIndexCur = pParse->nTab++; } @@ -4537,7 +4619,7 @@ WhereInfo *sqlite3WhereBegin( pLevel->addrBody = sqlite3VdbeCurrentAddr(v); notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady); pWInfo->iContinue = pLevel->addrCont; - if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_ONETABLE_ONLY)==0 ){ + if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0 ){ sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain); } } @@ -4607,13 +4689,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ } #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if( pLevel->addrLikeRep ){ - int op; - if( sqlite3VdbeGetOp(v, pLevel->addrLikeRep-1)->p1 ){ - op = OP_DecrJumpZero; - }else{ - op = OP_JumpZeroIncr; - } - sqlite3VdbeAddOp2(v, op, pLevel->iLikeRepCntr, pLevel->addrLikeRep); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1), + pLevel->addrLikeRep); VdbeCoverage(v); } #endif @@ -4665,12 +4742,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Close all of the cursors that were opened by sqlite3WhereBegin. ** Except, do not close cursors that will be reused by the OR optimization - ** (WHERE_OMIT_OPEN_CLOSE). And do not close the OP_OpenWrite cursors + ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors ** created for the ONEPASS optimization. */ if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 - && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ int ws = pLoop->wsFlags; if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ diff --git a/src/whereInt.h b/src/whereInt.h index 252905aa5a..075c04e5e5 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -70,7 +70,7 @@ struct WhereLevel { int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS - int iLikeRepCntr; /* LIKE range processing counter register */ + u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */ int addrLikeRep; /* LIKE range processing address */ #endif u8 iFrom; /* Which entry in the FROM clause */ @@ -418,8 +418,9 @@ struct WhereInfo { u8 sorted; /* True if really sorted (not just grouped) */ u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ - u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */ + u8 eDistinct; /* One of the WHERE_DISTINCT_* values */ u8 nLevel; /* Number of nested loop */ + u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ @@ -436,6 +437,9 @@ struct WhereInfo { ** where.c: */ Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); +#ifdef WHERETRACE_ENABLED +void sqlite3WhereClausePrint(WhereClause *pWC); +#endif WhereTerm *sqlite3WhereFindTerm( WhereClause *pWC, /* The WHERE clause to be searched */ int iCur, /* Cursor number of LHS */ diff --git a/src/wherecode.c b/src/wherecode.c index 8105e99dde..d78982f5b5 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -126,7 +126,7 @@ int sqlite3WhereExplainOneScan( pLoop = pLevel->pWLoop; flags = pLoop->wsFlags; - if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return 0; + if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0; isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) @@ -560,15 +560,16 @@ static int codeAllEqualityTerms( #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS /* -** If the most recently coded instruction is a constant range contraint -** that originated from the LIKE optimization, then change the P3 to be -** pLoop->iLikeRepCntr and set P5. +** If the most recently coded instruction is a constant range constraint +** (a string literal) that originated from the LIKE optimization, then +** set P3 and P5 on the OP_String opcode so that the string will be cast +** to a BLOB at appropriate times. ** ** The LIKE optimization trys to evaluate "x LIKE 'abc%'" as a range ** expression: "x>='ABC' AND x<'abd'". But this requires that the range ** scan loop run twice, once for strings and a second time for BLOBs. ** The OP_String opcodes on the second pass convert the upper and lower -** bound string contants to blobs. This routine makes the necessary changes +** bound string constants to blobs. This routine makes the necessary changes ** to the OP_String opcodes for that to happen. ** ** Except, of course, if SQLITE_LIKE_DOESNT_MATCH_BLOBS is defined, then @@ -587,8 +588,8 @@ static void whereLikeOptimizationStringFixup( assert( pOp!=0 ); assert( pOp->opcode==OP_String8 || pTerm->pWC->pWInfo->pParse->db->mallocFailed ); - pOp->p3 = pLevel->iLikeRepCntr; - pOp->p5 = 1; + pOp->p3 = (int)(pLevel->iLikeRepCntr>>1); /* Register holding counter */ + pOp->p5 = (u8)(pLevel->iLikeRepCntr&1); /* ASC or DESC */ } } #else @@ -625,6 +626,38 @@ static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } +/* +** Test whether or not expression pExpr, which was part of a WHERE clause, +** should be included in the cursor-hint for a table that is on the rhs +** of a LEFT JOIN. Set Walker.eCode to non-zero before returning if the +** expression is not suitable. +** +** An expression is unsuitable if it might evaluate to non NULL even if +** a TK_COLUMN node that does affect the value of the expression is set +** to NULL. For example: +** +** col IS NULL +** col IS NOT NULL +** coalesce(col, 1) +** CASE WHEN col THEN 0 ELSE 1 END +*/ +static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_IS + || pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT + || pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE + ){ + pWalker->eCode = 1; + }else if( pExpr->op==TK_FUNCTION ){ + int d1; + char d2[3]; + if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){ + pWalker->eCode = 1; + } + } + + return WRC_Continue; +} + /* ** This function is called on every node of an expression tree used as an @@ -677,6 +710,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){ ** Insert an OP_CursorHint instruction if it is appropriate to do so. */ static void codeCursorHint( + struct SrcList_item *pTabItem, /* FROM clause item */ WhereInfo *pWInfo, /* The where clause */ WhereLevel *pLevel, /* Which loop to provide hints for */ WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */ @@ -707,7 +741,42 @@ static void codeCursorHint( pTerm = &pWC->a[i]; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( pTerm->prereqAll & pLevel->notReady ) continue; - if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; + + /* Any terms specified as part of the ON(...) clause for any LEFT + ** JOIN for which the current table is not the rhs are omitted + ** from the cursor-hint. + ** + ** If this table is the rhs of a LEFT JOIN, "IS" or "IS NULL" terms + ** that were specified as part of the WHERE clause must be excluded. + ** This is to address the following: + ** + ** SELECT ... t1 LEFT JOIN t2 ON (t1.a=t2.b) WHERE t2.c IS NULL; + ** + ** Say there is a single row in t2 that matches (t1.a=t2.b), but its + ** t2.c values is not NULL. If the (t2.c IS NULL) constraint is + ** pushed down to the cursor, this row is filtered out, causing + ** SQLite to synthesize a row of NULL values. Which does match the + ** WHERE clause, and so the query returns a row. Which is incorrect. + ** + ** For the same reason, WHERE terms such as: + ** + ** WHERE 1 = (t2.c IS NULL) + ** + ** are also excluded. See codeCursorHintIsOrFunction() for details. + */ + if( pTabItem->fg.jointype & JT_LEFT ){ + Expr *pExpr = pTerm->pExpr; + if( !ExprHasProperty(pExpr, EP_FromJoin) + || pExpr->iRightJoinTable!=pTabItem->iCursor + ){ + sWalker.eCode = 0; + sWalker.xExprCallback = codeCursorHintIsOrFunction; + sqlite3WalkExpr(&sWalker, pTerm->pExpr); + if( sWalker.eCode ) continue; + } + }else{ + if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; + } /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize ** the cursor. These terms are not needed as hints for a pure range @@ -741,7 +810,7 @@ static void codeCursorHint( } } #else -# define codeCursorHint(A,B,C) /* No-op */ +# define codeCursorHint(A,B,C,D) /* No-op */ #endif /* SQLITE_ENABLE_CURSOR_HINTS */ /* @@ -775,7 +844,7 @@ static void codeDeferredSeek( assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 ); sqlite3VdbeAddOp3(v, OP_Seek, iIdxCur, 0, iCur); - if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE) + if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) ){ int i; @@ -830,7 +899,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 - && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0; + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); /* Create labels for the "break" and "continue" instructions @@ -970,8 +1039,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg); addrNxt = pLevel->addrNxt; - sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); + sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); @@ -998,7 +1066,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( pStart = pEnd; pEnd = pTerm; } - codeCursorHint(pWInfo, pLevel, pEnd); + codeCursorHint(pTabItem, pWInfo, pLevel, pEnd); if( pStart ){ Expr *pX; /* The expression that defines the start bound */ int r1, rTemp; /* Registers for holding the start boundary */ @@ -1175,14 +1243,17 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){ assert( pRangeStart!=0 ); /* LIKE opt constraints */ assert( pRangeStart->wtFlags & TERM_LIKEOPT ); /* occur in pairs */ - pLevel->iLikeRepCntr = ++pParse->nMem; - testcase( bRev ); - testcase( pIdx->aSortOrder[nEq]==SQLITE_SO_DESC ); - sqlite3VdbeAddOp2(v, OP_Integer, - bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC), - pLevel->iLikeRepCntr); + pLevel->iLikeRepCntr = (u32)++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 1, (int)pLevel->iLikeRepCntr); VdbeComment((v, "LIKE loop counter")); pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v); + /* iLikeRepCntr actually stores 2x the counter register number. The + ** bottom bit indicates whether the search order is ASC or DESC. */ + testcase( bRev ); + testcase( pIdx->aSortOrder[nEq]==SQLITE_SO_DESC ); + assert( (bRev & ~1)==0 ); + pLevel->iLikeRepCntr <<=1; + pLevel->iLikeRepCntr |= bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC); } #endif if( pRangeStart==0 @@ -1209,7 +1280,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( ** and store the values of those terms in an array of registers ** starting at regBase. */ - codeCursorHint(pWInfo, pLevel, pRangeEnd); + codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd); regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff); assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq ); if( zStartAff ) cEndAff = zStartAff[nEq]; @@ -1248,6 +1319,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( } nConstraint++; testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); + bSeekPastNull = 0; }else if( bSeekPastNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); nConstraint++; @@ -1320,7 +1392,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ - if( pWInfo->eOnePass!=ONEPASS_OFF ){ + if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE)!=0 ){ iRowidReg = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); @@ -1513,10 +1585,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( ** eliminating duplicates from other WHERE clauses, the action for each ** sub-WHERE clause is to to invoke the main loop body as a subroutine. */ - wctrlFlags = WHERE_OMIT_OPEN_CLOSE - | WHERE_FORCE_TABLE - | WHERE_ONETABLE_ONLY - | WHERE_NO_AUTOINDEX; + wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE); for(ii=0; iinTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ @@ -1624,7 +1693,6 @@ Bitmask sqlite3WhereCodeOneLoopStart( ){ assert( pSubWInfo->a[0].iIdxCur==iCovCur ); pCov = pSubLoop->u.btree.pIndex; - wctrlFlags |= WHERE_REOPEN_IDX; }else{ pCov = 0; } @@ -1661,7 +1729,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ - codeCursorHint(pWInfo, pLevel, 0); + codeCursorHint(pTabItem, pWInfo, pLevel, 0); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); @@ -1686,7 +1754,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->prereqAll & pLevel->notReady)!=0 ){ testcase( pWInfo->untestedTerms==0 - && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ); pWInfo->untestedTerms = 1; continue; } @@ -1696,11 +1764,17 @@ Bitmask sqlite3WhereCodeOneLoopStart( continue; } if( pTerm->wtFlags & TERM_LIKECOND ){ + /* If the TERM_LIKECOND flag is set, that means that the range search + ** is sufficient to guarantee that the LIKE operator is true, so we + ** can skip the call to the like(A,B) function. But this only works + ** for strings. So do not skip the call to the function on the pass + ** that compares BLOBs. */ #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS continue; #else - assert( pLevel->iLikeRepCntr>0 ); - skipLikeAddr = sqlite3VdbeAddOp1(v, OP_IfNot, pLevel->iLikeRepCntr); + u32 x = pLevel->iLikeRepCntr; + assert( x>0 ); + skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)? OP_IfNot : OP_If, (int)(x>>1)); VdbeCoverage(v); #endif } diff --git a/src/whereexpr.c b/src/whereexpr.c index ff012281b1..2768428e2d 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -290,7 +290,7 @@ static int isMatchOfColumn( Expr *pExpr, /* Test this expression */ unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */ ){ - struct Op2 { + static const struct Op2 { const char *zOp; unsigned char eOp2; } aOp[] = { @@ -568,7 +568,9 @@ static void exprAnalyzeOrTerm( if( !db->mallocFailed ){ for(j=0, pAndTerm=pAndWC->a; jnTerm; j++, pAndTerm++){ assert( pAndTerm->pExpr ); - if( allowedOp(pAndTerm->pExpr->op) ){ + if( allowedOp(pAndTerm->pExpr->op) + || pAndTerm->eOperator==WO_MATCH + ){ b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor); } } @@ -783,12 +785,10 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){ pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight); if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1; pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - /* Since pLeft and pRight are both a column references, their collating - ** sequence should always be defined. */ - zColl1 = ALWAYS(pColl) ? pColl->zName : 0; + zColl1 = pColl ? pColl->zName : 0; pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight); - zColl2 = ALWAYS(pColl) ? pColl->zName : 0; - return sqlite3StrICmp(zColl1, zColl2)==0; + zColl2 = pColl ? pColl->zName : 0; + return sqlite3_stricmp(zColl1, zColl2)==0; } /* @@ -1122,7 +1122,7 @@ static void exprAnalyze( ** virtual tables. The native query optimizer does not attempt ** to do anything with MATCH functions. */ - if( isMatchOfColumn(pExpr, &eOp2) ){ + if( pWC->op==TK_AND && isMatchOfColumn(pExpr, &eOp2) ){ int idxNew; Expr *pRight, *pLeft; WhereTerm *pNewTerm; @@ -1275,13 +1275,14 @@ void sqlite3WhereClauseClear(WhereClause *pWC){ ** tree. */ Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){ - Bitmask mask = 0; + Bitmask mask; if( p==0 ) return 0; if( p->op==TK_COLUMN ){ mask = sqlite3WhereGetMask(pMaskSet, p->iTable); return mask; } - mask = sqlite3WhereExprUsage(pMaskSet, p->pRight); + assert( !ExprHasProperty(p, EP_TokenOnly) ); + mask = p->pRight ? sqlite3WhereExprUsage(pMaskSet, p->pRight) : 0; if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft); if( ExprHasProperty(p, EP_xIsSelect) ){ mask |= exprSelectUsage(pMaskSet, p->x.pSelect); diff --git a/test/alter3.test b/test/alter3.test index f8ebe056f2..44b31c9833 100644 --- a/test/alter3.test +++ b/test/alter3.test @@ -184,7 +184,7 @@ do_test alter3-3.2 { if {!$has_codec} { do_test alter3-3.3 { get_file_format - } {4} + } {3} } ifcapable schema_version { do_test alter3-3.4 { @@ -220,7 +220,7 @@ do_test alter3-4.2 { if {!$has_codec} { do_test alter3-4.3 { get_file_format - } {4} + } {3} } ifcapable schema_version { do_test alter3-4.4 { @@ -270,7 +270,7 @@ ifcapable attach { if {!$has_codec} { do_test alter3-5.5 { list [get_file_format test2.db] [get_file_format] - } {4 4} + } {3 3} } do_test alter3-5.6 { execsql { @@ -347,19 +347,19 @@ if {!$has_codec} { ALTER TABLE abc ADD d DEFAULT NULL; } get_file_format - } {4} + } {3} do_test alter3-7.3 { execsql { ALTER TABLE abc ADD e DEFAULT 10; } get_file_format - } {4} + } {3} do_test alter3-7.4 { execsql { ALTER TABLE abc ADD f DEFAULT NULL; } get_file_format - } {4} + } {3} do_test alter3-7.5 { execsql { VACUUM; diff --git a/test/alter4.test b/test/alter4.test index ac39d614a5..2cd78777a2 100644 --- a/test/alter4.test +++ b/test/alter4.test @@ -355,4 +355,23 @@ do_execsql_test alter4-9.3 { SELECT typeof(c), c FROM t5; } {real 9.22337203685478e+18} +# Confirm that doing an ALTER TABLE on a legacy format database +# does not corrupt DESC indexes. +# +# Ticket https://www.sqlite.org/src/tktview/f68bf68513a1c +# +do_test alter4-10.1 { + db close + sqlite3 db :memory: + db eval { + PRAGMA legacy_file_format=on; + CREATE TABLE t1(a,b,c); + CREATE INDEX t1a ON t1(a DESC); + INSERT INTO t1 VALUES(1,2,3); + INSERT INTO t1 VALUES(2,3,4); + ALTER TABLE t1 ADD COLUMN d; + PRAGMA integrity_check; + } +} {ok} + finish_test diff --git a/test/auth2.test b/test/auth2.test index a9d64d08af..08d46cac57 100644 --- a/test/auth2.test +++ b/test/auth2.test @@ -98,12 +98,6 @@ SQLITE_UPDATE sqlite_master tbl_name main {} SQLITE_UPDATE sqlite_master rootpage main {} SQLITE_UPDATE sqlite_master sql main {} SQLITE_READ sqlite_master ROWID main {} -SQLITE_READ sqlite_master name main {} -SQLITE_READ sqlite_master rootpage main {} -SQLITE_READ sqlite_master sql main {} -SQLITE_READ sqlite_master tbl_name main {} -SQLITE_READ sqlite_master type main {} -SQLITE_READ sqlite_master ROWID main {} } do_test auth2-2.2 { set ::authargs {} @@ -119,12 +113,6 @@ SQLITE_UPDATE sqlite_master tbl_name main {} SQLITE_UPDATE sqlite_master rootpage main {} SQLITE_UPDATE sqlite_master sql main {} SQLITE_READ sqlite_master ROWID main {} -SQLITE_READ sqlite_master name main {} -SQLITE_READ sqlite_master rootpage main {} -SQLITE_READ sqlite_master sql main {} -SQLITE_READ sqlite_master tbl_name main {} -SQLITE_READ sqlite_master type main {} -SQLITE_READ sqlite_master ROWID main {} } do_test auth2-2.3 { set ::authargs {} diff --git a/test/auth3.test b/test/auth3.test index eef10b398f..ca7e16c84c 100644 --- a/test/auth3.test +++ b/test/auth3.test @@ -12,8 +12,7 @@ # Test that the truncate optimization is disabled if the SQLITE_DELETE # authorization callback returns SQLITE_IGNORE. # -# $Id: auth3.test,v 1.2 2009/05/04 01:58:31 drh Exp $ -# +# Test that authorizer is disabled during schema parsing. set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -108,4 +107,22 @@ do_test auth3-2.2 { set sqlite_search_count } {1} +# 2016-07-28. A problem report from a private client complaining about +# an authorizer failure during an ALTER TABLE. The solution (I think) is +# to disable the authorizer during schema parsing. +# +proc auth {code args} { + if {$code=="SQLITE_READ" && [regexp {DoNotRead} $args]} { + return SQLITE_DENY + } + return SQLITE_OK +} +do_execsql_test auth3-3.0 { + CREATE TEMPORARY TABLE TempTable ( + key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, + value TEXT NOT NULL ON CONFLICT FAIL); + ALTER TABLE TempTable RENAME TO DoNotRead; + SELECT name FROM sqlite_temp_master; +} {DoNotRead sqlite_autoindex_DoNotRead_1} + finish_test diff --git a/test/bestindex3.test b/test/bestindex3.test new file mode 100644 index 0000000000..8baf854254 --- /dev/null +++ b/test/bestindex3.test @@ -0,0 +1,175 @@ +# 2016 May 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 bestindex3 + +ifcapable !vtab { + finish_test + return +} + +#------------------------------------------------------------------------- +# Virtual table callback for a virtual table named $tbl. +# +# The table created is: +# +# "CREATE TABLE t1 (a, b, c)" +# +# This virtual table supports both LIKE and = operators on all columns. +# +proc vtab_cmd {bOmit method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b, c)" + } + + xBestIndex { + foreach {clist orderby mask} $args {} + + set ret [list] + set use use + if {$bOmit} {set use omit} + + for {set i 0} {$i < [llength $clist]} {incr i} { + array unset C + array set C [lindex $clist $i] + if {$C(usable) && ($C(op)=="like" || $C(op)=="eq")} { + lappend ret $use $i + lappend ret idxstr + lappend ret "[lindex {a b c} $C(column)] [string toupper $C(op)] ?" + break + } + } + + if {$ret==""} { + lappend ret cost 1000000 rows 1000000 + } else { + lappend ret cost 100 rows 10 + } + return $ret + } + + xFilter { + foreach {idxnum idxstr param} $args {} + set where "" + if {$bOmit && $idxstr != ""} { + set where " WHERE [string map [list ? '$param' EQ =] $idxstr]" + } + return [list sql "SELECT rowid, * FROM ttt$where"] + } + } + return "" +} + +register_tcl_module db + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING tcl("vtab_cmd 0"); +} + +do_eqp_test 1.1 { + SELECT * FROM t1 WHERE a LIKE 'abc'; +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a LIKE ?} +} + +do_eqp_test 1.2 { + SELECT * FROM t1 WHERE a = 'abc'; +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a EQ ?} +} + +do_eqp_test 1.3 { + SELECT * FROM t1 WHERE a = 'abc' OR b = 'def'; +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a EQ ?} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:b EQ ?} +} + +do_eqp_test 1.4 { + SELECT * FROM t1 WHERE a LIKE 'abc%' OR b = 'def'; +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a LIKE ?} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:b EQ ?} +} + +do_execsql_test 1.5 { + CREATE TABLE ttt(a, b, c); + + INSERT INTO ttt VALUES(1, 'two', 'three'); + INSERT INTO ttt VALUES(2, 'one', 'two'); + INSERT INTO ttt VALUES(3, 'three', 'one'); + INSERT INTO ttt VALUES(4, 'y', 'one'); + INSERT INTO ttt VALUES(5, 'x', 'two'); + INSERT INTO ttt VALUES(6, 'y', 'three'); +} + +foreach omit {0 1} { + do_execsql_test 1.6.$omit.0 " + DROP TABLE t1; + CREATE VIRTUAL TABLE t1 USING tcl('vtab_cmd $omit'); + " + do_execsql_test 1.6.$omit.1 { + SELECT rowid FROM t1 WHERE c LIKE 'o%' + } {3 4} + + do_execsql_test 1.6.$omit.2 { + SELECT rowid FROM t1 WHERE c LIKE 'o%' OR b='y' + } {3 4 6} + + do_execsql_test 1.6.$omit.3 { + SELECT rowid FROM t1 WHERE c = 'three' OR c LIKE 'o%' + } {1 6 3 4} +} + +#------------------------------------------------------------------------- +# Test the same pattern works with ordinary tables. +# +# This test does not work if the ICU extension is enabled. ICU overrides +# LIKE - and this optimization only works with the built-in LIKE function. +# +ifcapable !icu { + do_execsql_test 2.1 { + CREATE TABLE t2(x TEXT COLLATE nocase, y TEXT); + CREATE INDEX t2x ON t2(x COLLATE nocase); + CREATE INDEX t2y ON t2(y); + } + + do_eqp_test 2.2 { + SELECT * FROM t2 WHERE x LIKE 'abc%' OR y = 'def' + } { + 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x>? AND x99; + EXPLAIN QUERY PLAN SELECT a FROM t11 WHERE b<>99; +} {/USING INDEX t11x/} +do_execsql_test index6-11.2 { + EXPLAIN QUERY PLAN SELECT a FROM t11 WHERE b<>99 AND c<>98; +} {/USING INDEX t11x/} + + finish_test diff --git a/test/index8.test b/test/index8.test new file mode 100644 index 0000000000..bb58228527 --- /dev/null +++ b/test/index8.test @@ -0,0 +1,60 @@ +# 2016-07-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. +# +#*********************************************************************** +# +# Test cases for ORDER BY and LIMIT on an index scan. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Performance regression reported at +# http://www.mail-archive.com/sqlite-users@mailinglists.sqlite.org/msg98615.html +# +# Caused by the ORDER BY LIMIT optionation for check-in +# https://sqlite.org/src/info/bf46179d44843769 +# +# Fixed on approximately 2016-07-27 by changes that compute a better score +# for index scans by taking into account WHERE clause constraints that can +# be handled by the index and do not require a table lookup. +# +do_execsql_test 1.0 { + CREATE TABLE t1(a,b,c,d); + WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<100) + INSERT INTO t1(a,b,c,d) + SELECT x/10, x%10, x%19, x FROM c; + CREATE INDEX t1abc ON t1(a,b,c); + SELECT * FROM t1 WHERE c=4 ORDER BY a, b LIMIT 2; +} {0 4 4 4 2 3 4 23} + +# Prior to the fix, the following EQP would show a table scan and a sort +# rather than an index scan. +# +do_execsql_test 1.0eqp { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE c=4 ORDER BY a, b LIMIT 2; +} {/SCAN TABLE t1 USING INDEX t1abc/} + +# If we change the index so that it no longer covers the WHERE clause, +# then we should (correctly) revert to using a table scan. +# +do_execsql_test 1.1 { + DROP INDEX t1abc; + CREATE INDEX t1abd ON t1(a,b,d); + SELECT * FROM t1 WHERE c=4 ORDER BY a, b LIMIT 2; +} {0 4 4 4 2 3 4 23} +do_execsql_test 1.1eqp { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE c=4 ORDER BY a, b LIMIT 2; +} {~/USING INDEX/} + + +finish_test diff --git a/test/intpkey.test b/test/intpkey.test index c6be5418aa..a56cab4c69 100644 --- a/test/intpkey.test +++ b/test/intpkey.test @@ -614,6 +614,32 @@ do_execsql_test intpkey-16.1 { PRAGMA table_info=t16a; } {0 id INTEGER 0 {} 1 1 b TEXT 0 {} 0 2 c INT 0 {} 0} - +# 2016-05-06 ticket https://www.sqlite.org/src/tktview/16c9801ceba4923939085 +# When the schema contains an index on the IPK and no other index +# and a WHERE clause on a delete uses an OR where both sides referencing +# the IPK, then it is possible that the OP_Delete will fail because there +# deferred seek of the OP_Seek is not resolved prior to reaching the OP_Delete. +# +do_execsql_test intpkey-17.0 { + CREATE TABLE t17(x INTEGER PRIMARY KEY, y TEXT); + INSERT INTO t17(x,y) VALUES(123,'elephant'),(248,'giraffe'); + CREATE INDEX t17x ON t17(x); + DELETE FROM t17 WHERE x=99 OR x<130; + SELECT * FROM t17; +} {248 giraffe} +do_execsql_test intpkey-17.1 { + DROP INDEX t17x; + DELETE FROM t17; + INSERT INTO t17(x,y) VALUES(123,'elephant'),(248,'giraffe'); + CREATE UNIQUE INDEX t17x ON t17(abs(x)); + DELETE FROM t17 WHERE abs(x) IS NULL OR abs(x)<130; + SELECT * FROM t17; +} {248 giraffe} +do_execsql_test intpkey-17.2 { + DELETE FROM t17; + INSERT INTO t17(x,y) VALUES(123,'elephant'),(248,'giraffe'); + UPDATE t17 SET y='ostrich' WHERE abs(x)=248; + SELECT * FROM t17 ORDER BY +x; +} {123 elephant 248 ostrich} finish_test diff --git a/test/json101.test b/test/json101.test index 9b780a379b..3ee007c1cb 100644 --- a/test/json101.test +++ b/test/json101.test @@ -356,5 +356,34 @@ do_execsql_test json-8.2 { SELECT a=json_extract(b,'$[0]') FROM t8; } {1} +# The json_quote() function transforms an SQL value into a JSON value. +# String values are quoted and interior quotes are escaped. NULL values +# are rendered as the unquoted string "null". +# +do_execsql_test json-9.1 { + SELECT json_quote('abc"xyz'); +} {{"abc\"xyz"}} +do_execsql_test json-9.2 { + SELECT json_quote(3.14159); +} {3.14159} +do_execsql_test json-9.3 { + SELECT json_quote(12345); +} {12345} +do_execsql_test json-9.4 { + SELECT json_quote(null); +} {"null"} +do_catchsql_test json-9.5 { + SELECT json_quote(x'30313233'); +} {1 {JSON cannot hold BLOB values}} +do_catchsql_test json-9.6 { + SELECT json_quote(123,456) +} {1 {wrong number of arguments to function json_quote()}} +do_catchsql_test json-9.7 { + SELECT json_quote() +} {1 {wrong number of arguments to function json_quote()}} + + + + finish_test diff --git a/test/limit2.test b/test/limit2.test new file mode 100644 index 0000000000..fb4c89644f --- /dev/null +++ b/test/limit2.test @@ -0,0 +1,104 @@ +# 2016-05-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 regression tests for SQLite library. The +# focus of this file is testing the LIMIT in combination with ORDER BY +# and in particular, the optimizations in the inner loop that cause an +# early exit of the inner loop when the LIMIT is reached and the inner +# loop is emitting rows in ORDER BY order. + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test limit2-100 { + CREATE TABLE t1(a,b); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000) + INSERT INTO t1(a,b) SELECT 1, (x*17)%1000 + 1000 FROM c; + INSERT INTO t1(a,b) VALUES(2,2),(3,1006),(4,4),(5,9999); + CREATE INDEX t1ab ON t1(a,b); +} +set sqlite_search_count 0 +do_execsql_test limit2-100.1 { + SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY b LIMIT 5; +} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |} +set fast_count $sqlite_search_count +set sqlite_search_count 0 +do_execsql_test limit2-100.2 { + SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY +b LIMIT 5; +} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |} +do_test limit2-100.3 { + set slow_count $sqlite_search_count + expr {$fast_count < 0.02*$slow_count} +} {1} + +do_execsql_test limit2-110 { + CREATE TABLE t2(x,y); + INSERT INTO t2(x,y) VALUES('a',1),('a',2),('a',3),('a',4); + INSERT INTO t2(x,y) VALUES('b',1),('c',2),('d',3),('e',4); + CREATE INDEX t2xy ON t2(x,y); +} +set sqlite_search_count 0 +do_execsql_test limit2-110.1 { + SELECT a, b, '|' FROM t2, t1 WHERE t2.x='a' AND t1.a=t2.y ORDER BY t1.b LIMIT 5; +} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |} +set fast_count $sqlite_search_count +set sqlite_search_count 0 +do_execsql_test limit2-110.2 { + SELECT a, b, '|' FROM t2, t1 WHERE t2.x='a' AND t1.a=t2.y ORDER BY +t1.b LIMIT 5; +} {2 2 | 4 4 | 1 1000 | 1 1001 | 1 1002 |} +set slow_count $sqlite_search_count +do_test limit2-110.3 { + expr {$fast_count < 0.02*$slow_count} +} {1} + +do_execsql_test limit2-120 { + DROP INDEX t1ab; + CREATE INDEX t1ab ON t1(a,b DESC); +} +set sqlite_search_count 0 +do_execsql_test limit2-120.1 { + SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY b DESC LIMIT 5; +} {5 9999 | 1 1999 | 1 1998 | 1 1997 | 1 1996 |} +set fast_count $sqlite_search_count +set sqlite_search_count 0 +do_execsql_test limit2-120.2 { + SELECT a, b, '|' FROM t1 WHERE a IN (2,4,5,3,1) ORDER BY +b DESC LIMIT 5; +} {5 9999 | 1 1999 | 1 1998 | 1 1997 | 1 1996 |} +do_test limit2-120.3 { + set slow_count $sqlite_search_count + expr {$fast_count < 0.02*$slow_count} +} {1} + +# Bug report against the new ORDER BY LIMIT optimization just prior to +# release. (Unreleased so there is no ticket). +# +# Make sure the optimization is not applied if the inner loop can only +# provide a single row of output. +# +do_execsql_test limit2-200 { + CREATE TABLE t200(a, b); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000) + INSERT INTO t200(a,b) SELECT x, x FROM c; + CREATE TABLE t201(x INTEGER PRIMARY KEY, y); + INSERT INTO t201(x,y) VALUES(2,12345); + + SELECT *, '|' FROM t200, t201 WHERE x=b ORDER BY y LIMIT 3; +} {2 2 2 12345 |} +do_execsql_test limit2-210 { + SELECT *, '|' FROM t200 LEFT JOIN t201 ON x=b ORDER BY y LIMIT 3; +} {1 1 {} {} | 3 3 {} {} | 4 4 {} {} |} + + + + + + +finish_test diff --git a/test/loadext.test b/test/loadext.test index e6ba21e187..24ee7a6802 100644 --- a/test/loadext.test +++ b/test/loadext.test @@ -81,7 +81,7 @@ if {![file exists $testextension]} { set testextsrc $srcdir/test_loadext.c set cmdline [concat exec gcc $gcc_shared] - lappend cmdline -Wall -I$srcdir -I. -g $testextsrc -o $testextension + lappend cmdline -Wall -I$srcdir -I. -I.. -g $testextsrc -o $testextension if {[catch $cmdline msg]} { puts "Skipping loadext tests: Test extension not built..." diff --git a/test/minmax.test b/test/minmax.test index fb9bbb383a..04a3f06dff 100644 --- a/test/minmax.test +++ b/test/minmax.test @@ -628,5 +628,19 @@ do_test_13_noopt 13.7 { SELECT min(c), count(c) FROM t1 WHERE a='a'; } {1 5} +# 2016-07-26. https://www.sqlite.org/src/info/a0bac8b3c3d1bb75 +# Incorrect result on a min() query after a CREATE INDEX. +# +do_execsql_test 14.1 { + CREATE TABLE t14(a INTEGER, b INTEGER); + INSERT INTO t14(a,b) VALUES(100,2),(200,2),(300,2),(400,1),(500,2); + SELECT min(a) FROM t14 WHERE b='2' AND a>'50'; +} {100} +do_execsql_test 14.2 { + CREATE INDEX t14ba ON t14(b,a); + SELECT min(a) FROM t14 WHERE b='2' AND a>'50'; +} {100} + + finish_test diff --git a/test/misc5.test b/test/misc5.test index 30176b8082..0e7e34dd16 100644 --- a/test/misc5.test +++ b/test/misc5.test @@ -571,6 +571,8 @@ ifcapable subquery&&compound { # Overflow the lemon parser stack by providing an overly complex # expression. Make sure that the overflow is detected and reported. # +# This test fails when building with -DYYSTACKDEPTH=0 +# do_test misc5-7.1 { execsql {CREATE TABLE t1(x)} set sql "INSERT INTO t1 VALUES(" diff --git a/test/permutations.test b/test/permutations.test index df3f500cda..f4784cfcee 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -746,6 +746,11 @@ test_suite "inmemory_journal" -description { # WAL mode is different. wal* tkt-2d1a5c67d.test backcompat.test e_wal* rowallock.test + + # This test does not work as the "PRAGMA journal_mode = memory" + # statement switches the database out of wal mode at inopportune + # times. + snapshot_fault.test }] ifcapable mem3 { diff --git a/test/releasetest.tcl b/test/releasetest.tcl index 15f64e3218..f670c8bef4 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -51,10 +51,16 @@ array set ::Configs [strip_comments { "Default" { -O2 --disable-amalgamation --disable-shared + --enable-session } "Sanitize" { CC=clang -fsanitize=undefined -DSQLITE_ENABLE_STAT4 + --enable-session + } + "Stdcall" { + -DUSE_STDCALL=1 + -O2 } "Have-Not" { # The "Have-Not" configuration sets all possible -UHAVE_feature options @@ -106,7 +112,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_STMT_SCANSTATUS - --enable-json1 --enable-fts5 + --enable-json1 --enable-fts5 --enable-session } "Debug-One" { --disable-shared @@ -118,7 +124,6 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_MEMSYS5=1 - -DSQLITE_ENABLE_MEMSYS3=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_HIDDEN_COLUMNS @@ -132,6 +137,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_RBU -DSQLITE_MAX_ATTACHED=125 -DLONGDOUBLE_TYPE=double + --enable-session } "Device-One" { -O2 @@ -168,7 +174,7 @@ array set ::Configs [strip_comments { -DSQLITE_OMIT_TRACE=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_THREADSAFE=2 - --enable-json1 --enable-fts5 + --enable-json1 --enable-fts5 --enable-session } "Locking-Style" { -O2 @@ -296,10 +302,12 @@ array set ::Platforms [strip_comments { "Apple" "threadtest fulltest" } "Windows NT-intel" { + "Stdcall" test "Have-Not" test "Default" "mptest fulltestonly" } "Windows NT-amd64" { + "Stdcall" test "Have-Not" test "Default" "mptest fulltestonly" } @@ -723,6 +731,9 @@ proc makeCommand { targets makeOpts cflags opts } { set nmakeDir [file nativename $::SRCDIR] set nmakeFile [file nativename [file join $nmakeDir Makefile.msc]] lappend result nmake /f $nmakeFile TOP=$nmakeDir + if {[regexp {USE_STDCALL=1} $cflags]} { + lappend result USE_STDCALL=1 + } } else { lappend result make } diff --git a/test/selectA.test b/test/selectA.test index 0338338902..78d04be15d 100644 --- a/test/selectA.test +++ b/test/selectA.test @@ -1434,5 +1434,14 @@ do_catchsql_test 5.4 { SELECT * FROM t8 UNION SELECT * FROM t9 ORDER BY a+b COLLATE NOCASE } {1 {1st ORDER BY term does not match any column in the result set}} +do_execsql_test 6.1 { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + CREATE TABLE t1(a INTEGER); + CREATE TABLE t2(b TEXT); + INSERT INTO t2(b) VALUES('12345'); + SELECT * FROM (SELECT a FROM t1 UNION SELECT b FROM t2) WHERE a=a; +} {12345} + finish_test diff --git a/test/shell1.test b/test/shell1.test index 760c85fadc..9a966fd8db 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -870,6 +870,8 @@ do_test shell1-5.0 { if {$i==0x0D || ($tcl_platform(platform)=="windows" && $i==0x1A)} { continue } + if {$i>=0xE0 && $tcl_platform(os)=="OpenBSD"} continue + if {$i>=0xE0 && $i<=0xEF && $tcl_platform(os)=="Linux"} continue set hex [format %02X $i] set char [subst \\x$hex]; set oldChar $char set escapes [list] @@ -915,6 +917,9 @@ do_test shell1-5.0 { } } {} +# These test cases do not work on MinGW +if 0 { + # The string used here is the word "test" in Chinese. # In UTF-8, it is encoded as: \xE6\xB5\x8B\xE8\xAF\x95 set test \u6D4B\u8BD5 @@ -951,5 +956,6 @@ do_test shell1-6.1 { } forcedelete test3.db } {} +} finish_test diff --git a/test/skipscan1.test b/test/skipscan1.test index ac26711603..6d77e63498 100644 --- a/test/skipscan1.test +++ b/test/skipscan1.test @@ -43,23 +43,23 @@ do_execsql_test skipscan1-1.2 { } {abc 345 7 8 | def 345 9 10 |} do_execsql_test skipscan1-1.2eqp { EXPLAIN QUERY PLAN - SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t1 WHERE d<>99 AND b=345 ORDER BY a; } {/* USING INDEX t1abc (ANY(a) AND b=?)*/} do_execsql_test skipscan1-1.2sort { EXPLAIN QUERY PLAN - SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t1 WHERE d<>99 AND b=345 ORDER BY a; } {~/*ORDER BY*/} do_execsql_test skipscan1-1.3 { - SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a DESC; + SELECT a,b,c,d,'|' FROM t1 WHERE d<>99 AND b=345 ORDER BY a DESC; } {def 345 9 10 | abc 345 7 8 |} do_execsql_test skipscan1-1.3eqp { EXPLAIN QUERY PLAN - SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t1 WHERE d<>99 AND b=345 ORDER BY a DESC; } {/* USING INDEX t1abc (ANY(a) AND b=?)*/} do_execsql_test skipscan1-1.3sort { EXPLAIN QUERY PLAN - SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t1 WHERE d<>99 AND b=345 ORDER BY a DESC; } {~/*ORDER BY*/} do_execsql_test skipscan1-1.4 { @@ -150,15 +150,15 @@ do_execsql_test skipscan1-2.1 { } {} do_execsql_test skipscan1-2.2 { - SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t2 WHERE d<>99 AND b=345 ORDER BY a; } {abc 345 7 8 | def 345 9 10 |} do_execsql_test skipscan1-2.2eqp { EXPLAIN QUERY PLAN - SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t2 WHERE d<>99 AND b=345 ORDER BY a; } {/* USING INDEX sqlite_autoindex_t2_1 (ANY(a) AND b=?)*/} do_execsql_test skipscan1-2.2sort { EXPLAIN QUERY PLAN - SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a; + SELECT a,b,c,d,'|' FROM t2 WHERE d<>99 AND b=345 ORDER BY a; } {~/*ORDER BY*/} diff --git a/test/snapshot.test b/test/snapshot.test index 6dff11bbb9..b2d61941a7 100644 --- a/test/snapshot.test +++ b/test/snapshot.test @@ -352,10 +352,6 @@ do_test 6.4 { db2 eval { SELECT * FROM x1 } } {z zz zzz} -# EVIDENCE-OF: R-55491-50411 A snapshot will fail to open if the -# database connection D has not previously completed at least one read -# operation against the database file. -# do_test 6.5 { db2 close sqlite3 db2 test.db diff --git a/test/sort5.test b/test/sort5.test index 9fb2dff281..6ada81c9cc 100644 --- a/test/sort5.test +++ b/test/sort5.test @@ -78,10 +78,7 @@ execsql { CREATE TABLE t1(x) } # each a bit over 100 bytes in size. In total a little more than 1MiB # of data. # -breakpoint foreach {tn pgsz cachesz bTemp} { - 2 1024 1000 1 - 1 4096 1000 0 2 1024 1000 1 @@ -97,6 +94,12 @@ foreach {tn pgsz cachesz bTemp} { PRAGMA cache_size = $cachesz; " + if {[db one {PRAGMA page_size}]!=$pgsz} { + # SEE is not able to change page sizes and that messes up the + # results that follow. + continue + } + do_test 2.$tn.1 { set ::iTemp 0 catch { array unset F } @@ -113,4 +116,3 @@ foreach {tn pgsz cachesz bTemp} { } finish_test - diff --git a/test/speedtest1.c b/test/speedtest1.c index 2d337d4705..c69c6d5fc5 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -1216,6 +1216,10 @@ static void displayLinuxIoStats(FILE *out){ } #endif +#if SQLITE_VERSION_NUMBER<3006018 +# define sqlite3_sourceid(X) "(before 3.6.18)" +#endif + int main(int argc, char **argv){ int doAutovac = 0; /* True for --autovacuum */ int cacheSize = 0; /* Desired cache size. 0 means default */ diff --git a/test/stat.test b/test/stat.test index d106894d71..66ca5e2f2b 100644 --- a/test/stat.test +++ b/test/stat.test @@ -32,11 +32,27 @@ db func a_string a_string register_dbstat_vtab db do_execsql_test stat-0.0 { + PRAGMA table_info(dbstat); +} {/0 name TEXT .* 1 path TEXT .* 9 pgsize INTEGER/} + +# Attempts to drop an eponymous virtual table are a no-op. +do_execsql_test stat-0.1 { + DROP TABLE dbstat; + PRAGMA table_info=dbstat; +} {/0 name TEXT .* 1 path TEXT .* 9 pgsize INTEGER/} + +db close +forcedelete test.db +sqlite3 db test.db +db func a_string a_string +register_dbstat_vtab db +do_execsql_test stat-0.2 { PRAGMA auto_vacuum = OFF; CREATE VIRTUAL TABLE temp.stat USING dbstat; SELECT * FROM stat; } {} + if {[wal_is_capable]} { do_execsql_test stat-0.1 { PRAGMA journal_mode = WAL; diff --git a/test/tabfunc01.test b/test/tabfunc01.test index 879a045b8e..19d3cc66d5 100644 --- a/test/tabfunc01.test +++ b/test/tabfunc01.test @@ -22,6 +22,7 @@ ifcapable !vtab { return } load_static_extension db series +load_static_extension db carray do_execsql_test tabfunc01-1.1 { SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2; @@ -135,4 +136,61 @@ do_execsql_test tabfunc01-500 { ORDER BY +1; } {1 7 11 17} +# Table-valued functions on the RHS of an IN operator +# +do_execsql_test tabfunc01-600 { + CREATE TABLE t600(a INTEGER PRIMARY KEY, b TEXT); + WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100) + INSERT INTO t600(a,b) SELECT x, printf('(%03d)',x) FROM c; + SELECT b FROM t600 WHERE a IN generate_series(2,52,10); +} {(002) (012) (022) (032) (042) (052)} + + +do_test tabfunc01-700 { + set PTR [intarray_addr 5 7 13 17 23] + db eval { + SELECT b FROM t600, carray($PTR,5) WHERE a=value; + } +} {(005) (007) (013) (017) (023)} +do_test tabfunc01-701 { + db eval { + SELECT b FROM t600 WHERE a IN carray($PTR,5,'int32'); + } +} {(005) (007) (013) (017) (023)} +do_test tabfunc01-702 { + db eval { + SELECT b FROM t600 WHERE a IN carray($PTR,4,'int32'); + } +} {(005) (007) (013) (017)} +do_catchsql_test tabfunc01-710 { + SELECT b FROM t600 WHERE a IN carray($PTR,5,'int33'); +} {1 {unknown datatype: 'int33'}} + +do_test tabfunc01-720 { + set PTR [int64array_addr 5 7 13 17 23] + db eval { + SELECT b FROM t600, carray($PTR,5,'int64') WHERE a=value; + } +} {(005) (007) (013) (017) (023)} + +do_test tabfunc01-730 { + set PTR [doublearray_addr 5.0 7.0 13.0 17.0 23.0] + db eval { + SELECT b FROM t600, carray($PTR,5,'double') WHERE a=value; + } +} {(005) (007) (013) (017) (023)} + +do_test tabfunc01-740 { + set PTR [textarray_addr 5 7 13 17 23] + db eval { + SELECT b FROM t600, carray($PTR,5,'char*') WHERE a=value; + } +} {(005) (007) (013) (017) (023)} + + +intarray_addr +int64array_addr +doublearray_addr +textarray_addr + finish_test diff --git a/test/tclsqlite.test b/test/tclsqlite.test index 44d5c5634b..bdb0fc56d1 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -34,7 +34,7 @@ do_test tcl-1.1 { do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg -} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}} +} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}} do_test tcl-1.2.1 { set v [catch {db cache bogus} msg] lappend v $msg @@ -635,6 +635,39 @@ do_test tcl-14.2 { db one {SELECT x FROM t6 WHERE xCall()!='value'} } {} +# Verify that the "exists" and "onecolumn" methods work when +# a "profile" is registered. +# +catch {db close} +sqlite3 db :memory: +proc noop-profile {args} { + return +} +do_test tcl-15.0 { + db eval {CREATE TABLE t1(a); INSERT INTO t1 VALUES(1),(2),(3);} + db onecolumn {SELECT a FROM t1 WHERE a>2} +} {3} +do_test tcl-15.1 { + db exists {SELECT a FROM t1 WHERE a>2} +} {1} +do_test tcl-15.2 { + db exists {SELECT a FROM t1 WHERE a>3} +} {0} +db profile noop-profile +do_test tcl-15.3 { + db onecolumn {SELECT a FROM t1 WHERE a>2} +} {3} +do_test tcl-15.4 { + db exists {SELECT a FROM t1 WHERE a>2} +} {1} +do_test tcl-15.5 { + db exists {SELECT a FROM t1 WHERE a>3} +} {0} + + + + + finish_test diff --git a/test/tempdb2.test b/test/tempdb2.test new file mode 100644 index 0000000000..bc2822e71e --- /dev/null +++ b/test/tempdb2.test @@ -0,0 +1,77 @@ +# 2016 March 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix tempdb2 + +db close +sqlite3 db "" + +proc int2str {i} { string range [string repeat "$i." 450] 0 899 } +db func int2str int2str + +#------------------------------------------------------------------------- +# +# 1.1: Write a big transaction to the db. One so large that it forces +# the file to be created and the cache flushed to disk on COMMIT. +# +# 1.2: Write a small transaction - one small enough that it remains in +# memory on COMMIT. All the pages of table [t1] are now dirty. +# +# 1.3: Delete the contents of [t1]. This moves all of its leaves to the +# free-list and causes the btree layer to call PagerDontWrite() on +# each of them. +# +# Then do a big update on table [t2]. So big that the former leaves +# of [t1] are forced out of the cache. Then roll back the transaction. +# If the PagerDontWrite() calls are honoured and the data is not written +# to disk, the update made in test 1.2 will be lost at this point. Or, if +# they are ignored (as they should be for temp databases), the update +# will be safely written out to disk before the cache entries are +# discarded. +# +do_execsql_test 1.1 { + PRAGMA page_size=1024; + PRAGMA cache_size=50; + + BEGIN; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, int2str(1)); + INSERT INTO t1 VALUES(2, int2str(1)); + INSERT INTO t1 VALUES(3, int2str(1)); + + CREATE TABLE t2(a INTEGER PRIMARY KEY, b); + WITH c(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100 ) + INSERT INTO t2 SELECT x, int2str(x) FROM c; + COMMIT; + + PRAGMA lock_status; +} {main unlocked temp closed} + +do_execsql_test 1.2 { + UPDATE t1 SET b=int2str(2); + SELECT b=int2str(2) FROM t1 +} {1 1 1} + +do_execsql_test 1.3 { + BEGIN; + DELETE FROM t1; + UPDATE t2 SET b=int2str(a+1); + ROLLBACK; +} + +do_execsql_test 1.4 { + SELECT b=int2str(2) FROM t1 +} {1 1 1} + +finish_test + diff --git a/test/temptable2.test b/test/temptable2.test index 890b3b996d..ffa69b6150 100644 --- a/test/temptable2.test +++ b/test/temptable2.test @@ -230,7 +230,7 @@ do_execsql_test 8.1 { CREATE TABLE t2(a, b); CREATE INDEX i2 ON t2(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<20 ) - INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM x; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM x ORDER BY 1, 2; PRAGMA page_count; } {13} @@ -242,7 +242,7 @@ do_test 8.2 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); WITH x(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM x WHERE i<100 ) - INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x; + INSERT INTO t1 SELECT randomblob(100), randomblob(100) FROM x ORDER BY 1, 2; PRAGMA page_count; } tmp } {10} @@ -343,14 +343,15 @@ do_execsql_test 10.1 { INSERT INTO t2 VALUES(3, 4); } -if {[permutation]!="journaltest"} { - # The journaltest permutation does not support mmap, so this part of - # the test is omitted. - do_execsql_test 10.2 { PRAGMA mmap_size = 512000 } 512000 +ifcapable mmap { + if {[permutation]!="journaltest"} { + # The journaltest permutation does not support mmap, so this part of + # the test is omitted. + do_execsql_test 10.2 { PRAGMA mmap_size = 512000 } 512000 + } } do_execsql_test 10.3 { SELECT * FROM t2 } {1 2 3 4} do_execsql_test 10.4 { PRAGMA integrity_check } ok finish_test - diff --git a/test/temptable3.test b/test/temptable3.test new file mode 100644 index 0000000000..16f51d7832 --- /dev/null +++ b/test/temptable3.test @@ -0,0 +1,41 @@ +# 2016-05-10 +# +# 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 temptable3 + +db close +sqlite3 db {} +do_execsql_test 1.1 { + PRAGMA cache_size = 1; + PRAGMA page_size = 1024; + PRAGMA auto_vacuum = 2; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES( randomblob(800) ); + INSERT INTO t1 VALUES( randomblob(800) ); + CREATE TABLE t2(x); + PRAGMA integrity_check; +} {ok} + +db close +sqlite3 db {} +do_execsql_test 1.2 { + PRAGMA cache_size = 1; + PRAGMA auto_vacuum = 2; + CREATE TABLE t1(x); + CREATE TABLE t2(x UNIQUE); + INSERT INTO t2 VALUES(1), (2), (3); + DROP TABLE t1; + PRAGMA integrity_check; +} {ok} + +finish_test diff --git a/test/tester.tcl b/test/tester.tcl index 23810d09ee..3fe84c641f 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -373,6 +373,7 @@ proc do_not_use_codec {} { set ::do_not_use_codec 1 reset_db } +unset -nocomplain do_not_use_codec # Return true if the "reserved_bytes" integer on database files is non-zero. # @@ -521,7 +522,11 @@ if {[info exists cmdlinearg]==0} { } default { - lappend leftover [file normalize $a] + if {[file tail $a]==$a} { + lappend leftover $a + } else { + lappend leftover [file normalize $a] + } } } } @@ -716,6 +721,17 @@ proc puts {args} { uplevel puts_override $args } # Invoke the do_test procedure to run a single test # +# The $expected parameter is the expected result. The result is the return +# value from the last TCL command in $cmd. +# +# Normally, $expected must match exactly. But if $expected is of the form +# "/regexp/" then regular expression matching is used. If $expected is +# "~/regexp/" then the regular expression must NOT match. If $expected is +# of the form "#/value-list/" then each term in value-list must be numeric +# and must approximately match the corresponding numeric term in $result. +# Values must match within 10%. Or if the $expected term is A..B then the +# $result term must be in between A and B. +# proc do_test {name cmd expected} { global argv cmdlinearg @@ -749,7 +765,7 @@ proc do_test {name cmd expected} { output2 "\nError: $result" fail_test $name } else { - if {[regexp {^~?/.*/$} $expected]} { + if {[regexp {^[~#]?/.*/$} $expected]} { # "expected" is of the form "/PATTERN/" then the result if correct if # regular expression PATTERN matches the result. "~/PATTERN/" means # the regular expression must not match. @@ -763,6 +779,21 @@ proc do_test {name cmd expected} { set ok [regexp $re $result] } set ok [expr {!$ok}] + } elseif {[string index $expected 0]=="#"} { + # Numeric range value comparison. Each term of the $result is matched + # against one term of $expect. Both $result and $expected terms must be + # numeric. The values must match within 10%. Or if $expected is of the + # form A..B then the $result term must be between A and B. + set e2 [string range $expected 2 end-1] + foreach i $result j $e2 { + if {[regexp {^(-?\d+)\.\.(-?\d)$} $j all A B]} { + set ok [expr {$i+0>=$A && $i+0<=$B}] + } else { + set ok [expr {$i+0>=0.9*$j && $i+0<=1.1*$j}] + } + if {!$ok} break + } + if {$ok && [llength $result]!=[llength $e2]} {set ok 0} } else { set re [string range $expected 1 end-1] if {[string index $re 0]=="*"} { diff --git a/test/time-wordcount.sh b/test/time-wordcount.sh new file mode 100644 index 0000000000..df9edc6f2c --- /dev/null +++ b/test/time-wordcount.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# This script runs the wordcount program in different ways and generates +# an output useful for performance comparisons. +# + +# Select the source text to be analyzed. +# +if test "x$1" = "x"; +then echo "Usage: $0 FILENAME [ARGS...]"; exit 1; +fi + +# Do test runs +# +rm -f wcdb1.db +./wordcount --tag A: --timer --summary wcdb1.db $* --insert +rm -f wcdb2.db +./wordcount --tag B: --timer --summary wcdb2.db $* --insert --without-rowid +rm -f wcdb1.db +./wordcount --tag C: --timer --summary wcdb1.db $* --replace +rm -f wcdb2.db +./wordcount --tag D: --timer --summary wcdb2.db $* --replace --without-rowid +rm -f wcdb1.db +./wordcount --tag E: --timer --summary wcdb1.db $* --select +rm -f wcdb2.db +./wordcount --tag F: --timer --summary wcdb2.db $* --select --without-rowid +./wordcount --tag G: --timer --summary wcdb1.db $* --query +./wordcount --tag H: --timer --summary wcdb1.db $* --query --without-rowid +./wordcount --tag I: --timer --summary wcdb1.db $* --delete +./wordcount --tag J: --timer --summary wcdb2.db $* --delete --without-rowid + +# Clean up temporary files created. +# +rm -f wcdb1.db wcdb2.db diff --git a/test/trace3.test b/test/trace3.test new file mode 100644 index 0000000000..0809759d80 --- /dev/null +++ b/test/trace3.test @@ -0,0 +1,233 @@ +# 2016 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. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The focus of +# this test file is the "sqlite3_trace_v2()" and "sqlite3_expanded_sql()" +# APIs. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !trace { finish_test ; return } +set ::testprefix trace3 + +proc trace_v2_error { args } { + lappend ::stmtlist(error) [string trim $args] + error "trace error"; # this will be ignored. +} +proc trace_v2_record { args } { + lappend ::stmtlist(record) [string trim $args] +} +proc trace_v2_nop { args } {}; # do nothing. + +do_test trace3-1.0 { + execsql { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,NULL); + INSERT INTO t1 VALUES(2,-1); + INSERT INTO t1 VALUES(3,0); + INSERT INTO t1 VALUES(4,1); + INSERT INTO t1 VALUES(5,-2147483648); + INSERT INTO t1 VALUES(6,2147483647); + INSERT INTO t1 VALUES(7,-9223372036854775808); + INSERT INTO t1 VALUES(8,9223372036854775807); + INSERT INTO t1 VALUES(9,-1.0); + INSERT INTO t1 VALUES(10,0.0); + INSERT INTO t1 VALUES(11,1.0); + INSERT INTO t1 VALUES(12,''); + INSERT INTO t1 VALUES(13,'1'); + INSERT INTO t1 VALUES(14,'one'); + INSERT INTO t1 VALUES(15,x'abcd0123'); + INSERT INTO t1 VALUES(16,x'4567cdef'); + } +} {} + +do_test trace3-1.1 { + set rc [catch {db trace_v2 1 2 3} msg] + lappend rc $msg +} {1 {wrong # args: should be "db trace_v2 ?CALLBACK? ?MASK?"}} +do_test trace3-1.2 { + set rc [catch {db trace_v2 1 bad} msg] + lappend rc $msg +} {1 {bad trace type "bad": must be statement, profile, row, or close}} + +do_test trace3-2.1 { + db trace_v2 trace_v2_nop + db trace_v2 +} {trace_v2_nop} + +do_test trace3-3.1 { + unset -nocomplain ::stmtlist + db trace_v2 trace_v2_nop + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + array get ::stmtlist +} {} +do_test trace3-3.2 { + set ::stmtlist(error) {} + db trace_v2 trace_v2_error + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(error) +} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/} +do_test trace3-3.3 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/} +do_test trace3-3.4 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record statement + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/} +do_test trace3-3.5 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record 1 + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} {/^\{-?\d+ \{SELECT a, b FROM t1 ORDER BY a;\}\}$/} + +do_test trace3-4.1 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record profile + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} {/^\{-?\d+ -?\d+\}$/} +do_test trace3-4.2 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record 2 + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} {/^\{-?\d+ -?\d+\}$/} + +do_test trace3-5.1 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record row + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} "/^[string trim [string repeat {\d+ } 16]]\$/" +do_test trace3-5.2 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record 4 + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} "/^[string trim [string repeat {\d+ } 16]]\$/" + +do_test trace3-6.1 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record {profile row} + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} "/^[string trim [string repeat {-?\d+ } 16]] \\\{-?\\d+ -?\\d+\\\}\$/" +do_test trace3-6.2 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record {statement profile row} + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + set ::stmtlist(record) +} "/^\\\{-?\\d+ \\\{SELECT a, b FROM t1 ORDER BY a;\\\}\\\} [string trim \ +[string repeat {-?\d+ } 16]] \\\{-?\\d+ -?\\d+\\\}\$/" + +do_test trace3-7.1 { + set DB [sqlite3_connection_pointer db] + + set STMT [sqlite3_prepare_v2 $DB \ + "SELECT a, b FROM t1 WHERE b = ? ORDER BY a;" -1 TAIL] +} {/^[0-9A-Fa-f]+$/} + +do_test trace3-8.1 { + list [sqlite3_bind_null $STMT 1] [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = NULL ORDER BY a;}} +do_test trace3-8.2 { + list [sqlite3_bind_int $STMT 1 123] [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = 123 ORDER BY a;}} +do_test trace3-8.3 { + list [sqlite3_bind_int64 $STMT 1 123] [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = 123 ORDER BY a;}} +do_test trace3-8.4 { + list [sqlite3_bind_text $STMT 1 "some string" 11] \ + [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = 'some string' ORDER BY a;}} +do_test trace3-8.5 { + list [sqlite3_bind_text $STMT 1 "some 'bad' string" 17] \ + [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = 'some ''bad'' string' ORDER BY a;}} +do_test trace3-8.6 { + list [sqlite3_bind_double $STMT 1 123] [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = 123.0 ORDER BY a;}} +do_test trace3-8.7 { + list [sqlite3_bind_text16 $STMT 1 \ + [encoding convertto unicode hi\000yall\000] 16] \ + [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = 'hi' ORDER BY a;}} +do_test trace3-8.8 { + list [sqlite3_bind_blob $STMT 1 "\x12\x34\x56" 3] \ + [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = x'123456' ORDER BY a;}} +do_test trace3-8.9 { + list [sqlite3_bind_blob $STMT 1 "\xAB\xCD\xEF" 3] \ + [sqlite3_expanded_sql $STMT] +} {{} {SELECT a, b FROM t1 WHERE b = x'abcdef' ORDER BY a;}} + +do_test trace3-9.1 { + sqlite3_finalize $STMT +} {SQLITE_OK} + +do_test trace3-10.1 { + db trace_v2 "" + db trace_v2 +} {} +do_test trace3-10.2 { + unset -nocomplain ::stmtlist + db trace_v2 "" {statement profile row} + execsql { + SELECT a, b FROM t1 ORDER BY a; + } + array get ::stmtlist +} {} + +do_test trace3-11.1 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record close + db close + set ::stmtlist(record) +} {/^-?\d+$/} + +reset_db + +do_test trace3-11.2 { + set ::stmtlist(record) {} + db trace_v2 trace_v2_record 8 + db close + set ::stmtlist(record) +} {/^-?\d+$/} + +finish_test diff --git a/test/vacuummem.test b/test/vacuummem.test new file mode 100644 index 0000000000..b214c83b3d --- /dev/null +++ b/test/vacuummem.test @@ -0,0 +1,60 @@ +# 2005 February 15 +# +# 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 file is testing that the VACUUM statement correctly +# frees any memory used for a temporary cache. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix vacuummem + +if {[permutation]=="memsubsys1"} { + finish_test + return +} + + +proc memory_used {} { + set stat [sqlite3_status SQLITE_STATUS_MEMORY_USED 1] + lindex $stat 1 +} + +do_execsql_test 1.0 { + PRAGMA cache_size = -2000; + CREATE TABLE t1(a, b, c); + + WITH r(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM r WHERE i<100000 + ) + INSERT INTO t1 SELECT randomblob(100),randomblob(100),randomblob(100) FROM r; + + CREATE INDEX t1a ON t1(a); + CREATE INDEX t1b ON t1(b); + CREATE INDEX t1c ON t1(c); +} +set ans "#/[memory_used]/" + +do_test 1.1 { memory_used } $ans + +do_execsql_test 1.2 VACUUM + +do_test 1.3 { memory_used } $ans + +do_execsql_test 1.4 { + SELECT count(*) FROM t1 WHERE +a IS NOT NULL +} {100000} + +do_test 1.5 { memory_used } $ans + + + +finish_test diff --git a/test/walcrash4.test b/test/walcrash4.test new file mode 100644 index 0000000000..80839b39e5 --- /dev/null +++ b/test/walcrash4.test @@ -0,0 +1,85 @@ +# 2010 May 25 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/wal_common.tcl +ifcapable !wal {finish_test ; return } +set testprefix walcrash4 +do_not_use_codec + +#------------------------------------------------------------------------- +# At one point, if "PRAGMA synchronous=full" is set and the platform +# does not support POWERSAFE_OVERWRITE, and the last frame written to +# the wal file in a transaction is aligned with a sector boundary, the +# xSync() call was omitted. +# +# The following test verifies that this has been fixed. +# +do_execsql_test 1.0 { + PRAGMA autovacuum = 0; + PRAGMA page_size = 1024; + PRAGMA journal_mode = wal; + PRAGMA main.synchronous = full; +} {wal} + +faultsim_save_and_close + +# The error message is different on unix and windows +# +if {$::tcl_platform(platform)=="windows"} { + set msg "child killed: unknown signal" +} else { + set msg "child process exited abnormally" +} + +for {set nExtra 0} {$nExtra < 10} {incr nExtra} { + for {set i 0} {$i < 10} {incr i} { + do_test 1.nExtra=$nExtra.i=$i.1 { + faultsim_restore_and_reopen + + set fd [open crash.tcl w] + puts $fd [subst -nocommands { + sqlite3_crash_enable 1 + sqlite3_test_control_pending_byte $::sqlite_pending_byte + sqlite3 db test.db -vfs crash + db eval { + PRAGMA main.synchronous=FULL; + BEGIN; + CREATE TABLE t1(x UNIQUE); + } + for {set e 2} {[set e] < ($nExtra+2)} {incr e} { + db eval "CREATE TABLE t[set e] (x)" + } + db eval { + INSERT INTO t1 VALUES( randomblob(170000) ); + COMMIT; + } + sqlite3_crash_now + }] + close $fd + + set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg] + list $r $msg + } "1 {$msg}" + + do_execsql_test 1.nExtra=$nExtra.i=$i.2 { + SELECT count(*) FROM t1; + PRAGMA integrity_check; + } {1 ok} + } +} + + +finish_test diff --git a/test/walprotocol.test b/test/walprotocol.test index d658de9d7c..ee8d0b72a5 100644 --- a/test/walprotocol.test +++ b/test/walprotocol.test @@ -69,7 +69,7 @@ proc lock_callback {method filename handle lock} { if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY } return SQLITE_OK } -puts "# Warning: This next test case causes SQLite to call xSlee(1) 100 times." +puts "# Warning: This next test case causes SQLite to call xSleep(1) 100 times." puts "# Normally this equates to a delay of roughly 10 seconds, but if SQLite" puts "# is built on unix without HAVE_USLEEP defined, it may be much longer." do_test 1.3 { diff --git a/test/where3.test b/test/where3.test index c2804b5579..6edbe2bcad 100644 --- a/test/where3.test +++ b/test/where3.test @@ -45,7 +45,7 @@ do_test where3-1.1 { } } {222 two 2 222 {} {}} -ifcapable explain { +ifcapable explain&&!cursorhints { do_test where3-1.1.1 { explain_no_trace {SELECT * FROM t1, t2 LEFT JOIN t3 ON q=x WHERE p=2 AND a=q} @@ -86,7 +86,7 @@ do_test where3-1.2 { } } {1 {Value for C1.1} {Value for C2.1} 2 {} {Value for C2.2} 3 {Value for C1.3} {Value for C2.3}} -ifcapable explain { +ifcapable explain&&!cursorhints { do_test where3-1.2.1 { explain_no_trace { SELECT parent1.parent1key, child1.value, child2.value diff --git a/test/win32nolock.test b/test/win32nolock.test new file mode 100644 index 0000000000..8128860392 --- /dev/null +++ b/test/win32nolock.test @@ -0,0 +1,126 @@ +# 2016 July 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. +# + +if {$tcl_platform(platform)!="windows"} return + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix win32nolock + +do_test win32nolock-1.0 { + sqlite3 db test.db + execsql { + CREATE TABLE t1(a, b); + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } +} {} + +do_test win32nolock-1.1 { + execsql COMMIT + catchsql { SELECT * FROM t1 } +} {0 {1 2}} + +db close + +do_test win32nolock-1.2 { + sqlite3 db test.db -vfs win32-none + sqlite3 db2 test.db -vfs win32-none + execsql { PRAGMA mmap_size = 0 } db2 + execsql { + BEGIN; + INSERT INTO t1 VALUES(3, 4); + } +} {} + +do_test win32nolock-1.3 { + execsql { SELECT * FROM t1 } +} {1 2 3 4} + +do_test win32nolock-1.4 { + execsql { SELECT * FROM t1; } db2 +} {1 2} + +do_test win32nolock-1.5 { + execsql { + BEGIN; + SELECT * FROM t1; + } db2 +} {1 2} + +do_test win32nolock-1.6 { + execsql COMMIT + execsql {SELECT * FROM t1} db2 +} {1 2} + +ifcapable memorymanage { + do_test win32nolock-1.7 { + sqlite3_release_memory 1000000 + execsql {SELECT * FROM t1} db2 + } {1 2 3 4} +} + +do_test win32nolock-1.8 { + db close + db2 close +} {} + +do_test win32nolock-1.9.1 { + sqlite3 db test.db + sqlite3 db2 test.db + list [catchsql { BEGIN EXCLUSIVE; } db] \ + [catchsql { BEGIN EXCLUSIVE; } db2] +} {{0 {}} {1 {database is locked}}} + +do_test win32nolock-1.9.2 { + db close + db2 close +} {} + +do_test win32nolock-1.10.1 { + sqlite3 db test.db -vfs win32-none + sqlite3 db2 test.db + list [catchsql { BEGIN EXCLUSIVE; } db] \ + [catchsql { BEGIN EXCLUSIVE; } db2] +} {{0 {}} {0 {}}} + +do_test win32nolock-1.10.2 { + db close + db2 close +} {} + +do_test win32nolock-1.11.1 { + sqlite3 db test.db + sqlite3 db2 test.db -vfs win32-none + list [catchsql { BEGIN EXCLUSIVE; } db] \ + [catchsql { BEGIN EXCLUSIVE; } db2] +} {{0 {}} {0 {}}} + +do_test win32nolock-1.11.2 { + db close + db2 close +} {} + +do_test win32nolock-1.12.1 { + sqlite3 db test.db -vfs win32-none + sqlite3 db2 test.db -vfs win32-none + list [catchsql { BEGIN EXCLUSIVE; } db] \ + [catchsql { BEGIN EXCLUSIVE; } db2] +} {{0 {}} {0 {}}} + +do_test win32nolock-1.12.2 { + db close + db2 close +} {} + +finish_test diff --git a/test/wordcount.c b/test/wordcount.c index 72aa6b2f0b..5002918c5e 100644 --- a/test/wordcount.c +++ b/test/wordcount.c @@ -30,6 +30,7 @@ ** --nosync Use PRAGMA synchronous=OFF ** --journal MMMM Use PRAGMA journal_mode=MMMM ** --timer Time the operation of this program +** --tag NAME Tag all output using NAME. Use only stdout. ** ** Modes: ** @@ -82,6 +83,9 @@ #include "sqlite3.h" #define ISALPHA(X) isalpha((unsigned char)(X)) +/* Output tag */ +char *zTag = "--"; + /* Return the current wall-clock time */ static sqlite3_int64 realTime(void){ static sqlite3_vfs *clockVfs = 0; @@ -115,7 +119,7 @@ static void traceCallback(void *NotUsed, const char *zSql){ ** each column separated by a single space. */ static int printResult(void *NotUsed, int nArg, char **azArg, char **azNm){ int i; - printf("--"); + printf("%s", zTag); for(i=0; i +#include +#include +#include +#include +#include +#include "sqlite3.h" + +/* Context for the SHA1 hash */ +typedef struct SHA1Context SHA1Context; +struct SHA1Context { + unsigned int state[5]; + unsigned int count[2]; + unsigned char buffer[64]; +}; + +/* +** All global variables are gathered into the "g" singleton. +*/ +struct GlobalVars { + const char *zArgv0; /* Name of program */ + unsigned fDebug; /* Debug flags */ + sqlite3 *db; /* The database connection */ + SHA1Context cx; /* SHA1 hash context */ +} g; + +/* +** Debugging flags +*/ +#define DEBUG_FULLTRACE 0x00000001 /* Trace hash to stderr */ + +/****************************************************************************** +** The Hash Engine +** +** Modify these routines (and appropriate state fields in global variable 'g') +** in order to compute a different (better?) hash of the database. +*/ +/* + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + * + * blk0le() for little-endian and blk0be() for big-endian. + */ +#if __GNUC__ && (defined(__i386__) || defined(__x86_64__)) +/* + * GCC by itself only generates left rotates. Use right rotates if + * possible to be kinder to dinky implementations with iterative rotate + * instructions. + */ +#define SHA_ROT(op, x, k) \ + ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; }) +#define rol(x,k) SHA_ROT("roll", x, k) +#define ror(x,k) SHA_ROT("rorl", x, k) + +#else +/* Generic C equivalent */ +#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) +#define rol(x,k) SHA_ROT(x,k,32-(k)) +#define ror(x,k) SHA_ROT(x,32-(k),k) +#endif + + +#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ + |(rol(block[i],8)&0x00FF00FF)) +#define blk0be(i) block[i] +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ + ^block[(i+2)&15]^block[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + * + * Rl0() for little-endian and Rb0() for big-endian. Endianness is + * determined at run-time. + */ +#define Rl0(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define Rb0(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define R1(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define R2(v,w,x,y,z,i) \ + z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); +#define R3(v,w,x,y,z,i) \ + z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); +#define R4(v,w,x,y,z,i) \ + z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +#define a qq[0] +#define b qq[1] +#define c qq[2] +#define d qq[3] +#define e qq[4] + +void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){ + unsigned int qq[5]; /* a, b, c, d, e; */ + static int one = 1; + unsigned int block[16]; + memcpy(block, buffer, 64); + memcpy(qq,state,5*sizeof(unsigned int)); + + /* Copy g.cx.state[] to working vars */ + /* + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + */ + + /* 4 rounds of 20 operations each. Loop unrolled. */ + if( 1 == *(unsigned char*)&one ){ + Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); + Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); + Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); + Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); + }else{ + Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); + Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); + Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); + Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); + } + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + + +/* Initialize the SHA1 hash */ +static void hash_init(void){ + /* SHA1 initialization constants */ + g.cx.state[0] = 0x67452301; + g.cx.state[1] = 0xEFCDAB89; + g.cx.state[2] = 0x98BADCFE; + g.cx.state[3] = 0x10325476; + g.cx.state[4] = 0xC3D2E1F0; + g.cx.count[0] = g.cx.count[1] = 0; +} + +/* Add new content to the SHA1 hash */ +static void hash_step(const unsigned char *data, unsigned int len){ + unsigned int i, j; + + j = g.cx.count[0]; + if( (g.cx.count[0] += len << 3) < j ){ + g.cx.count[1] += (len>>29)+1; + } + j = (j >> 3) & 63; + if( (j + len) > 63 ){ + (void)memcpy(&g.cx.buffer[j], data, (i = 64-j)); + SHA1Transform(g.cx.state, g.cx.buffer); + for(; i + 63 < len; i += 64){ + SHA1Transform(g.cx.state, &data[i]); + } + j = 0; + }else{ + i = 0; + } + (void)memcpy(&g.cx.buffer[j], &data[i], len - i); +} + + +/* Add padding and compute and output the message digest. */ +static void hash_finish(const char *zName){ + unsigned int i; + unsigned char finalcount[8]; + unsigned char digest[20]; + static const char zEncode[] = "0123456789abcdef"; + char zOut[41]; + + for (i = 0; i < 8; i++){ + finalcount[i] = (unsigned char)((g.cx.count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + hash_step((const unsigned char *)"\200", 1); + while ((g.cx.count[0] & 504) != 448){ + hash_step((const unsigned char *)"\0", 1); + } + hash_step(finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++){ + digest[i] = (unsigned char)((g.cx.state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + for(i=0; i<20; i++){ + zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; + zOut[i*2+1] = zEncode[digest[i] & 0xf]; + } + zOut[i*2]= 0; + printf("%s %s\n", zOut, zName); +} +/* End of the hashing logic +*******************************************************************************/ + +/* +** Print an error resulting from faulting command-line arguments and +** abort the program. +*/ +static void cmdlineError(const char *zFormat, ...){ + va_list ap; + fprintf(stderr, "%s: ", g.zArgv0); + va_start(ap, zFormat); + vfprintf(stderr, zFormat, ap); + va_end(ap); + fprintf(stderr, "\n\"%s --help\" for more help\n", g.zArgv0); + exit(1); +} + +/* +** Print an error message for an error that occurs at runtime, then +** abort the program. +*/ +static void runtimeError(const char *zFormat, ...){ + va_list ap; + fprintf(stderr, "%s: ", g.zArgv0); + va_start(ap, zFormat); + vfprintf(stderr, zFormat, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} + +/* +** Prepare a new SQL statement. Print an error and abort if anything +** goes wrong. +*/ +static sqlite3_stmt *db_vprepare(const char *zFormat, va_list ap){ + char *zSql; + int rc; + sqlite3_stmt *pStmt; + + zSql = sqlite3_vmprintf(zFormat, ap); + if( zSql==0 ) runtimeError("out of memory"); + rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0); + if( rc ){ + runtimeError("SQL statement error: %s\n\"%s\"", sqlite3_errmsg(g.db), + zSql); + } + sqlite3_free(zSql); + return pStmt; +} +static sqlite3_stmt *db_prepare(const char *zFormat, ...){ + va_list ap; + sqlite3_stmt *pStmt; + va_start(ap, zFormat); + pStmt = db_vprepare(zFormat, ap); + va_end(ap); + return pStmt; +} + +/* +** Compute the hash for all rows of the query formed from the printf-style +** zFormat and its argument. +*/ +static void hash_one_query(const char *zFormat, ...){ + va_list ap; + sqlite3_stmt *pStmt; /* The query defined by zFormat and "..." */ + int nCol; /* Number of columns in the result set */ + int i; /* Loop counter */ + + /* Prepare the query defined by zFormat and "..." */ + va_start(ap, zFormat); + pStmt = db_vprepare(zFormat, ap); + va_end(ap); + nCol = sqlite3_column_count(pStmt); + + /* Compute a hash over the result of the query */ + while( SQLITE_ROW==sqlite3_step(pStmt) ){ + for(i=0; i=0; j--){ + x[j] = u & 0xff; + u >>= 8; + } + hash_step((const unsigned char*)"1",1); + hash_step(x,8); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "INT %s\n", sqlite3_column_text(pStmt,i)); + } + break; + } + case SQLITE_FLOAT: { + sqlite3_uint64 u; + int j; + unsigned char x[8]; + double r = sqlite3_column_double(pStmt,i); + memcpy(&u, &r, 8); + for(j=7; j>=0; j--){ + x[j] = u & 0xff; + u >>= 8; + } + hash_step((const unsigned char*)"2",1); + hash_step(x,8); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "FLOAT %s\n", sqlite3_column_text(pStmt,i)); + } + break; + } + case SQLITE_TEXT: { + int n = sqlite3_column_bytes(pStmt, i); + const unsigned char *z = sqlite3_column_text(pStmt, i); + hash_step((const unsigned char*)"3", 1); + hash_step(z, n); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "TEXT '%s'\n", sqlite3_column_text(pStmt,i)); + } + break; + } + case SQLITE_BLOB: { + int n = sqlite3_column_bytes(pStmt, i); + const unsigned char *z = sqlite3_column_blob(pStmt, i); + hash_step((const unsigned char*)"4", 1); + hash_step(z, n); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "BLOB (%d bytes)\n", n); + } + break; + } + } + } + } + sqlite3_finalize(pStmt); +} + + +/* +** Print sketchy documentation for this utility program +*/ +static void showHelp(void){ + printf("Usage: %s [options] FILE ...\n", g.zArgv0); + printf( +"Compute a SHA1 hash on the content of database FILE. System tables such as\n" +"sqlite_stat1, sqlite_stat4, and sqlite_sequence are omitted from the hash.\n" +"Options:\n" +" --debug N Set debugging flags to N (experts only)\n" +" --like PATTERN Only hash tables whose name is LIKE the pattern\n" +" --schema-only Only hash the schema - omit table content\n" +" --without-schema Only hash table content - omit the schema\n" + ); +} + +int main(int argc, char **argv){ + const char *zDb = 0; /* Name of the database currently being hashed */ + int i; /* Loop counter */ + int rc; /* Subroutine return code */ + char *zErrMsg; /* Error message when opening database */ + sqlite3_stmt *pStmt; /* An SQLite query */ + const char *zLike = 0; /* LIKE pattern of tables to hash */ + int omitSchema = 0; /* True to compute hash on content only */ + int omitContent = 0; /* True to compute hash on schema only */ + int nFile = 0; /* Number of input filenames seen */ + + g.zArgv0 = argv[0]; + sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); + for(i=1; itype = type; newaction->sp = sp; + newaction->spOpt = 0; if( type==SHIFT ){ newaction->x.stp = (struct state *)arg; }else{ @@ -1499,7 +1504,7 @@ static void handle_T_option(char *z){ lemon_strcpy(user_templatename, z); } -/* Merge together to lists of rules order by rule.iRule */ +/* Merge together to lists of rules ordered by rule.iRule */ static struct rule *Rule_merge(struct rule *pA, struct rule *pB){ struct rule *pFirst = 0; struct rule **ppPrev = &pFirst; @@ -1642,7 +1647,10 @@ int main(int argc, char **argv) for(i=1; ISUPPER(lem.symbols[i]->name[0]); i++); lem.nterminal = i; - /* Assign sequential rule numbers */ + /* Assign sequential rule numbers. Start with 0. Put rules that have no + ** reduce action C-code associated with them last, so that the switch() + ** statement that selects reduction actions will have a smaller jump table. + */ for(i=0, rp=lem.rule; rp; rp=rp->next){ rp->iRule = rp->code ? i++ : -1; } @@ -2211,6 +2219,7 @@ to follow the previous rule."); }else{ psp->prevrule->line = psp->tokenlineno; psp->prevrule->code = &x[1]; + psp->prevrule->noCode = 0; } }else if( x[0]=='[' ){ psp->state = PRECEDENCE_MARK_1; @@ -2317,6 +2326,7 @@ to follow the previous rule."); rp->lhsalias = psp->lhsalias; rp->nrhs = psp->nrhs; rp->code = 0; + rp->noCode = 1; rp->precsym = 0; rp->index = psp->gp->nrule++; rp->nextlhs = rp->lhs->rule; @@ -3160,6 +3170,9 @@ int PrintAction( result = 0; break; } + if( result && ap->spOpt ){ + fprintf(fp," /* because %s==%s */", ap->sp->name, ap->spOpt->name); + } return result; } @@ -3527,9 +3540,8 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ } /* -** zCode is a string that is the action associated with a rule. Expand -** the symbols in this string so that the refer to elements of the parser -** stack. +** Write and transform the rp->code string so that symbols are expanded. +** Populate the rp->codePrefix and rp->codeSuffix strings, as appropriate. ** ** Return 1 if the expanded code requires that "yylhsminor" local variable ** to be defined. @@ -3553,17 +3565,17 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){ static char newlinestr[2] = { '\n', '\0' }; rp->code = newlinestr; rp->line = rp->ruleline; + rp->noCode = 1; + }else{ + rp->noCode = 0; } - if( rp->lhsalias==0 ){ - /* There is no LHS value symbol. */ - lhsdirect = 1; - }else if( rp->nrhs==0 ){ + if( rp->nrhs==0 ){ /* If there are no RHS symbols, then writing directly to the LHS is ok */ lhsdirect = 1; }else if( rp->rhsalias[0]==0 ){ - /* The left-most RHS symbol has not value. LHS direct is ok. But + /* The left-most RHS symbol has no value. LHS direct is ok. But ** we have to call the distructor on the RHS symbol first. */ lhsdirect = 1; if( has_destructor(rp->rhs[0],lemp) ){ @@ -3571,7 +3583,11 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){ append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, rp->rhs[0]->index,1-rp->nrhs); rp->codePrefix = Strsafe(append_str(0,0,0,0)); + rp->noCode = 0; } + }else if( rp->lhsalias==0 ){ + /* There is no LHS value symbol. */ + lhsdirect = 1; }else if( strcmp(rp->lhsalias,rp->rhsalias[0])==0 ){ /* The LHS symbol and the left-most RHS symbol are the same, so ** direct writing is allowed */ @@ -3715,7 +3731,10 @@ PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){ /* Suffix code generation complete */ cp = append_str(0,0,0,0); - if( cp ) rp->codeSuffix = Strsafe(cp); + if( cp && cp[0] ){ + rp->codeSuffix = Strsafe(cp); + rp->noCode = 0; + } return rc; } @@ -4134,6 +4153,19 @@ void ReportTable( } free(ax); + /* Mark rules that are actually used for reduce actions after all + ** optimizations have been applied + */ + for(rp=lemp->rule; rp; rp=rp->next) rp->doesReduce = LEMON_FALSE; + for(i=0; inxstate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE || ap->type==SHIFTREDUCE ){ + ap->x.rp->doesReduce = i; + } + } + } + /* Finish rendering the constants now that the action table has ** been computed */ fprintf(out,"#define YYNSTATE %d\n",lemp->nxstate); lineno++; @@ -4199,20 +4231,21 @@ void ReportTable( fprintf(out, "};\n"); lineno++; /* Output the yy_shift_ofst[] table */ - fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; n = lemp->nxstate; while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; - fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; - fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; - fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", lemp->nactiontab); lineno++; + fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++; + fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++; + fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++; fprintf(out, "static const %s yy_shift_ofst[] = {\n", - minimum_size_type(mnTknOfst-1, mxTknOfst, &sz)); lineno++; + minimum_size_type(mnTknOfst, lemp->nterminal+lemp->nactiontab, &sz)); + lineno++; lemp->tablesize += n*sz; for(i=j=0; isorted[i]; ofst = stp->iTknOfst; - if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( ofst==NO_OFFSET ) ofst = lemp->nactiontab; if( j==0 ) fprintf(out," /* %5d */ ", i); fprintf(out, " %4d,", ofst); if( j==9 || i==n-1 ){ @@ -4396,8 +4429,11 @@ void ReportTable( /* First output rules other than the default: rule */ for(rp=lemp->rule; rp; rp=rp->next){ struct rule *rp2; /* Other rules with the same action */ - if( rp->code==0 ) continue; - if( rp->code[0]=='\n' && rp->code[1]==0 ) continue; /* Will be default: */ + if( rp->codeEmitted ) continue; + if( rp->noCode ){ + /* No C code actions, so this will be part of the "default:" rule */ + continue; + } fprintf(out," case %d: /* ", rp->iRule); writeRuleText(out, rp); fprintf(out, " */\n"); lineno++; @@ -4407,22 +4443,27 @@ void ReportTable( fprintf(out," case %d: /* ", rp2->iRule); writeRuleText(out, rp2); fprintf(out," */ yytestcase(yyruleno==%d);\n", rp2->iRule); lineno++; - rp2->code = 0; + rp2->codeEmitted = 1; } } emit_code(out,rp,lemp,&lineno); fprintf(out," break;\n"); lineno++; - rp->code = 0; + rp->codeEmitted = 1; } /* Finally, output the default: rule. We choose as the default: all ** empty actions. */ fprintf(out," default:\n"); lineno++; for(rp=lemp->rule; rp; rp=rp->next){ - if( rp->code==0 ) continue; - assert( rp->code[0]=='\n' && rp->code[1]==0 ); + if( rp->codeEmitted ) continue; + assert( rp->noCode ); fprintf(out," /* (%d) ", rp->iRule); writeRuleText(out, rp); - fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->iRule); lineno++; + if( rp->doesReduce ){ + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->iRule); lineno++; + }else{ + fprintf(out, " (OPTIMIZED OUT) */ assert(yyruleno!=%d);\n", + rp->iRule); lineno++; + } } fprintf(out," break;\n"); lineno++; tplt_xfer(lemp->name,in,out,&lineno); @@ -4493,7 +4534,7 @@ void ReportHeader(struct lemon *lemp) void CompressTables(struct lemon *lemp) { struct state *stp; - struct action *ap, *ap2; + struct action *ap, *ap2, *nextap; struct rule *rp, *rp2, *rbest; int nbest, n; int i; @@ -4570,6 +4611,36 @@ void CompressTables(struct lemon *lemp) } } } + + /* If a SHIFTREDUCE action specifies a rule that has a single RHS term + ** (meaning that the SHIFTREDUCE will land back in the state where it + ** started) and if there is no C-code associated with the reduce action, + ** then we can go ahead and convert the action to be the same as the + ** action for the RHS of the rule. + */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(ap=stp->ap; ap; ap=nextap){ + nextap = ap->next; + if( ap->type!=SHIFTREDUCE ) continue; + rp = ap->x.rp; + if( rp->noCode==0 ) continue; + if( rp->nrhs!=1 ) continue; +#if 1 + /* Only apply this optimization to non-terminals. It would be OK to + ** apply it to terminal symbols too, but that makes the parser tables + ** larger. */ + if( ap->sp->indexnterminal ) continue; +#endif + /* If we reach this point, it means the optimization can be applied */ + nextap = ap; + for(ap2=stp->ap; ap2 && (ap2==ap || ap2->sp!=rp->lhs); ap2=ap2->next){} + assert( ap2!=0 ); + ap->spOpt = ap2->sp; + ap->type = ap2->type; + ap->x = ap2->x; + } + } } diff --git a/tool/lempar.c b/tool/lempar.c index e313904082..61d19451e9 100644 --- a/tool/lempar.c +++ b/tool/lempar.c @@ -116,7 +116,7 @@ ** ** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE ** and YY_MAX_REDUCE - +** ** N == YY_ERROR_ACTION A syntax error has occurred. ** ** N == YY_ACCEPT_ACTION The parser accepts its input. @@ -125,16 +125,20 @@ ** slots in the yy_action[] table. ** ** The action table is constructed as a single large table named yy_action[]. -** Given state S and lookahead X, the action is computed as +** Given state S and lookahead X, the action is computed as either: ** -** yy_action[ yy_shift_ofst[S] + X ] +** (A) N = yy_action[ yy_shift_ofst[S] + X ] +** (B) N = yy_default[S] ** -** If the index value yy_shift_ofst[S]+X is out of range or if the value -** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] -** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table -** and that yy_default[S] should be used instead. +** The (A) formula is preferred. The B formula is used instead if: +** (1) The yy_shift_ofst[S]+X value is out of range, or +** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or +** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT. +** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that +** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X. +** Hence only tests (1) and (2) need to be evaluated.) ** -** The formula above is for computing the action when the lookahead is +** The formulas above are for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after ** a reduce action) then the yy_reduce_ofst[] array is used in place of ** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of @@ -203,9 +207,9 @@ typedef struct yyStackEntry yyStackEntry; /* The state of the parser is completely contained in an instance of ** the following structure */ struct yyParser { - int yyidx; /* Index of top element in stack */ + yyStackEntry *yytos; /* Pointer to top element of the stack */ #ifdef YYTRACKMAXSTACKDEPTH - int yyidxMax; /* Maximum value of yyidx */ + int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ @@ -214,6 +218,7 @@ struct yyParser { #if YYSTACKDEPTH<=0 int yystksz; /* Current side of the stack */ yyStackEntry *yystack; /* The parser's stack */ + yyStackEntry yystk0; /* First stack entry */ #else yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ #endif @@ -271,24 +276,34 @@ static const char *const yyRuleName[] = { #if YYSTACKDEPTH<=0 /* -** Try to increase the size of the parser stack. +** Try to increase the size of the parser stack. Return the number +** of errors. Return 0 on success. */ -static void yyGrowStack(yyParser *p){ +static int yyGrowStack(yyParser *p){ int newSize; + int idx; yyStackEntry *pNew; newSize = p->yystksz*2 + 100; - pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; + if( p->yystack==&p->yystk0 ){ + pNew = malloc(newSize*sizeof(pNew[0])); + if( pNew ) pNew[0] = p->yystk0; + }else{ + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + } if( pNew ){ p->yystack = pNew; - p->yystksz = newSize; + p->yytos = &p->yystack[idx]; #ifndef NDEBUG if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", - yyTracePrompt, p->yystksz); + fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", + yyTracePrompt, p->yystksz, newSize); } #endif + p->yystksz = newSize; } + return pNew==0; } #endif @@ -317,15 +332,24 @@ void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){ yyParser *pParser; pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); if( pParser ){ - pParser->yyidx = -1; #ifdef YYTRACKMAXSTACKDEPTH - pParser->yyidxMax = 0; + pParser->yyhwm = 0; #endif #if YYSTACKDEPTH<=0 + pParser->yytos = NULL; pParser->yystack = NULL; pParser->yystksz = 0; - yyGrowStack(pParser); + if( yyGrowStack(pParser) ){ + pParser->yystack = &pParser->yystk0; + pParser->yystksz = 1; + } #endif +#ifndef YYNOERRORRECOVERY + pParser->yyerrcnt = -1; +#endif + pParser->yytos = pParser->yystack; + pParser->yystack[0].stateno = 0; + pParser->yystack[0].major = 0; } return pParser; } @@ -369,8 +393,9 @@ static void yy_destructor( */ static void yy_pop_parser_stack(yyParser *pParser){ yyStackEntry *yytos; - assert( pParser->yyidx>=0 ); - yytos = &pParser->yystack[pParser->yyidx--]; + assert( pParser->yytos!=0 ); + assert( pParser->yytos > pParser->yystack ); + yytos = pParser->yytos--; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", @@ -397,9 +422,9 @@ void ParseFree( #ifndef YYPARSEFREENEVERNULL if( pParser==0 ) return; #endif - while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); + while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); #if YYSTACKDEPTH<=0 - free(pParser->yystack); + if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); #endif (*freeProc)((void*)pParser); } @@ -410,7 +435,7 @@ void ParseFree( #ifdef YYTRACKMAXSTACKDEPTH int ParseStackPeak(void *p){ yyParser *pParser = (yyParser*)p; - return pParser->yyidxMax; + return pParser->yyhwm; } #endif @@ -423,56 +448,53 @@ static unsigned int yy_find_shift_action( YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; - int stateno = pParser->yystack[pParser->yyidx].stateno; + int stateno = pParser->yytos->stateno; if( stateno>=YY_MIN_REDUCE ) return stateno; assert( stateno <= YY_SHIFT_COUNT ); do{ i = yy_shift_ofst[stateno]; - if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno]; assert( iLookAhead!=YYNOCODE ); i += iLookAhead; if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ - if( iLookAhead>0 ){ #ifdef YYFALLBACK - YYCODETYPE iFallback; /* Fallback token */ - if( iLookAhead %s\n", - yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); - } -#endif - assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ - iLookAhead = iFallback; - continue; + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); } +#endif + assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ + iLookAhead = iFallback; + continue; + } #endif #ifdef YYWILDCARD - { - int j = i - iLookAhead + YYWILDCARD; - if( + { + int j = i - iLookAhead + YYWILDCARD; + if( #if YY_SHIFT_MIN+YYWILDCARD<0 - j>=0 && + j>=0 && #endif #if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT - j0 + ){ #ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", - yyTracePrompt, yyTokenName[iLookAhead], - yyTokenName[YYWILDCARD]); - } -#endif /* NDEBUG */ - return yy_action[j]; + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], + yyTokenName[YYWILDCARD]); } +#endif /* NDEBUG */ + return yy_action[j]; } -#endif /* YYWILDCARD */ } +#endif /* YYWILDCARD */ return yy_default[stateno]; }else{ return yy_action[i]; @@ -516,13 +538,13 @@ static int yy_find_reduce_action( */ static void yyStackOverflow(yyParser *yypParser){ ParseARG_FETCH; - yypParser->yyidx--; + yypParser->yytos--; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); } #endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will execute if the parser ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ @@ -539,11 +561,11 @@ static void yyTraceShift(yyParser *yypParser, int yyNewState){ if( yyTraceFILE ){ if( yyNewStateyystack[yypParser->yyidx].major], + yyTracePrompt,yyTokenName[yypParser->yytos->major], yyNewState); }else{ fprintf(yyTraceFILE,"%sShift '%s'\n", - yyTracePrompt,yyTokenName[yypParser->yystack[yypParser->yyidx].major]); + yyTracePrompt,yyTokenName[yypParser->yytos->major]); } } } @@ -561,27 +583,30 @@ static void yy_shift( ParseTOKENTYPE yyMinor /* The minor token to shift in */ ){ yyStackEntry *yytos; - yypParser->yyidx++; + yypParser->yytos++; #ifdef YYTRACKMAXSTACKDEPTH - if( yypParser->yyidx>yypParser->yyidxMax ){ - yypParser->yyidxMax = yypParser->yyidx; + if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif #if YYSTACKDEPTH>0 - if( yypParser->yyidx>=YYSTACKDEPTH ){ + if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH] ){ yyStackOverflow(yypParser); return; } #else - if( yypParser->yyidx>=yypParser->yystksz ){ - yyGrowStack(yypParser); - if( yypParser->yyidx>=yypParser->yystksz ){ + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ + if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); return; } } #endif - yytos = &yypParser->yystack[yypParser->yyidx]; + if( yyNewState > YY_MAX_SHIFT ){ + yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; + } + yytos = yypParser->yytos; yytos->stateno = (YYACTIONTYPE)yyNewState; yytos->major = (YYCODETYPE)yyMajor; yytos->minor.yy0 = yyMinor; @@ -613,7 +638,7 @@ static void yy_reduce( yyStackEntry *yymsp; /* The top of the parser's stack */ int yysize; /* Amount to pop the stack */ ParseARG_FETCH; - yymsp = &yypParser->yystack[yypParser->yyidx]; + yymsp = yypParser->yytos; #ifndef NDEBUG if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ yysize = yyRuleInfo[yyruleno].nrhs; @@ -627,22 +652,23 @@ static void yy_reduce( ** enough on the stack to push the LHS value */ if( yyRuleInfo[yyruleno].nrhs==0 ){ #ifdef YYTRACKMAXSTACKDEPTH - if( yypParser->yyidx>yypParser->yyidxMax ){ - yypParser->yyidxMax = yypParser->yyidx; + if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif #if YYSTACKDEPTH>0 - if( yypParser->yyidx>=YYSTACKDEPTH-1 ){ + if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH-1] ){ yyStackOverflow(yypParser); return; } #else - if( yypParser->yyidx>=yypParser->yystksz-1 ){ - yyGrowStack(yypParser); - if( yypParser->yyidx>=yypParser->yystksz-1 ){ + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ + if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); return; } + yymsp = yypParser->yytos; } #endif } @@ -665,15 +691,17 @@ static void yy_reduce( yysize = yyRuleInfo[yyruleno].nrhs; yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); if( yyact <= YY_MAX_SHIFTREDUCE ){ - if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; - yypParser->yyidx -= yysize - 1; + if( yyact>YY_MAX_SHIFT ){ + yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; + } yymsp -= yysize-1; + yypParser->yytos = yymsp; yymsp->stateno = (YYACTIONTYPE)yyact; yymsp->major = (YYCODETYPE)yygoto; yyTraceShift(yypParser, yyact); }else{ assert( yyact == YY_ACCEPT_ACTION ); - yypParser->yyidx -= yysize; + yypParser->yytos -= yysize; yy_accept(yypParser); } } @@ -691,7 +719,7 @@ static void yy_parse_failed( fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); } #endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ /************ Begin %parse_failure code ***************************************/ @@ -729,7 +757,10 @@ static void yy_accept( fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); } #endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + assert( yypParser->yytos==yypParser->yystack ); /* Here code is inserted which will be executed whenever the ** parser accepts */ /*********** Begin %parse_accept code *****************************************/ @@ -773,28 +804,8 @@ void Parse( #endif yyParser *yypParser; /* The parser */ - /* (re)initialize the parser, if necessary */ yypParser = (yyParser*)yyp; - if( yypParser->yyidx<0 ){ -#if YYSTACKDEPTH<=0 - if( yypParser->yystksz <=0 ){ - yyStackOverflow(yypParser); - return; - } -#endif - yypParser->yyidx = 0; -#ifndef YYNOERRORRECOVERY - yypParser->yyerrcnt = -1; -#endif - yypParser->yystack[0].stateno = 0; - yypParser->yystack[0].major = 0; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sInitialize. Empty stack. State 0\n", - yyTracePrompt); - } -#endif - } + assert( yypParser->yytos!=0 ); #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) yyendofinput = (yymajor==0); #endif @@ -809,7 +820,6 @@ void Parse( do{ yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); if( yyact <= YY_MAX_SHIFTREDUCE ){ - if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; yy_shift(yypParser,yyact,yymajor,yyminor); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt--; @@ -851,7 +861,7 @@ void Parse( if( yypParser->yyerrcnt<0 ){ yy_syntax_error(yypParser,yymajor,yyminor); } - yymx = yypParser->yystack[yypParser->yyidx].major; + yymx = yypParser->yytos->major; if( yymx==YYERRORSYMBOL || yyerrorhit ){ #ifndef NDEBUG if( yyTraceFILE ){ @@ -862,18 +872,20 @@ void Parse( yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ - while( - yypParser->yyidx >= 0 && - yymx != YYERRORSYMBOL && - (yyact = yy_find_reduce_action( - yypParser->yystack[yypParser->yyidx].stateno, + while( yypParser->yytos >= &yypParser->yystack + && yymx != YYERRORSYMBOL + && (yyact = yy_find_reduce_action( + yypParser->yytos->stateno, YYERRORSYMBOL)) >= YY_MIN_REDUCE ){ yy_pop_parser_stack(yypParser); } - if( yypParser->yyidx < 0 || yymajor==0 ){ + if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif yymajor = YYNOCODE; }else if( yymx!=YYERRORSYMBOL ){ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); @@ -910,18 +922,23 @@ void Parse( yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); if( yyendofinput ){ yy_parse_failed(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif } yymajor = YYNOCODE; #endif } - }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + }while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack ); #ifndef NDEBUG if( yyTraceFILE ){ - int i; + yyStackEntry *i; + char cDiv = '['; fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); - for(i=1; i<=yypParser->yyidx; i++) - fprintf(yyTraceFILE,"%c%s", i==1 ? '[' : ' ', - yyTokenName[yypParser->yystack[i].major]); + for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ + fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); + cDiv = ' '; + } fprintf(yyTraceFILE,"]\n"); } #endif diff --git a/tool/libvers.c b/tool/libvers.c new file mode 100644 index 0000000000..6911dbd07e --- /dev/null +++ b/tool/libvers.c @@ -0,0 +1,15 @@ +/* +** Compile this program against an SQLite library of unknown version +** and then run this program, and it will print out the SQLite version +** information. +*/ +#include + +extern const char *sqlite3_libversion(void); +extern const char *sqlite3_sourceid(void); + +int main(int argc, char **argv){ + printf("SQLite version %s\n", sqlite3_libversion()); + printf("SQLite source %s\n", sqlite3_sourceid()); + return 0; +} diff --git a/tool/mksqlite3c-noext.tcl b/tool/mksqlite3c-noext.tcl index 601b8cce8c..fad6c9e584 100644 --- a/tool/mksqlite3c-noext.tcl +++ b/tool/mksqlite3c-noext.tcl @@ -220,7 +220,7 @@ proc copy_file {filename} { if {[lsearch -exact $cdecllist $funcname] >= 0} { append line SQLITE_CDECL } else { - append line SQLITE_STDCALL + append line SQLITE_APICALL } append line " " $funcname $rest puts $out $line diff --git a/tool/opcodesum.tcl b/tool/opcodesum.tcl new file mode 100644 index 0000000000..47dff32b90 --- /dev/null +++ b/tool/opcodesum.tcl @@ -0,0 +1,34 @@ +#!/usr/bin/tclsh +# +# Run this script, redirecting input from cachegrind output, to compute the +# number of CPU cycles used by each VDBE opcode. +# +# The cachegrind output should be configured so that it reports a single +# column of Ir at the left margin. Ex: +# +# cg_annotation --show=Ir --auto=yes cachegrind.out.* | tclsh opcodesum.tcl +# +set currentop x +set ncycle(x) 0 +while {![eof stdin]} { + set line [string map {\173 x \175 x \042 x} [gets stdin]] + if {[regexp { \. case OP_.*:} $line]} { + regexp {OP_(.+):} $line all currentop + set ncycle($currentop) 0 + } elseif {[lindex $line 1]=="default:" + && [regexp {really OP_Noop and OP_Explain} $line]} { + break + } elseif {[lindex $line 0]!="."} { + regsub -all {[^0-9]} [lindex $line 0] {} n + if {$n!=""} {incr ncycle($currentop) $n} + } +} +unset ncycle(x) +set results {} +foreach op [lsort [array names ncycle]] { + if {$ncycle($op)==0} continue + lappend results [list $ncycle($op) $op] +} +foreach entry [lsort -index 0 -int -decr $results] { + puts [format {%-16s %10d} [lindex $entry 1] [lindex $entry 0]] +} diff --git a/tool/replace.tcl b/tool/replace.tcl index b01a83accb..5a1ac5983c 100644 --- a/tool/replace.tcl +++ b/tool/replace.tcl @@ -4,16 +4,19 @@ # only lines successfully modified with a regular # expression. # +fconfigure stdout -translation binary -encoding binary +fconfigure stderr -translation binary -encoding binary set mode [string tolower [lindex $argv 0]] set from [lindex $argv 1] set to [lindex $argv 2] -if {$mode ni [list exact include]} {exit 1} +if {$mode ni [list exact regsub include]} {exit 1} if {[string length $from]==0} {exit 2} while {![eof stdin]} { set line [gets stdin] if {[eof stdin]} break switch -exact $mode { exact {set line [string map [list $from $to] $line]} + regsub {regsub -all -- $from $line $to line} include {if {[regsub -all -- $from $line $to line]==0} continue} } puts stdout $line diff --git a/tool/spaceanal.tcl b/tool/spaceanal.tcl index 38d954162e..eef1192a1d 100644 --- a/tool/spaceanal.tcl +++ b/tool/spaceanal.tcl @@ -152,6 +152,7 @@ set tabledef {CREATE TABLE space_used( name clob, -- Name of a table or index in the database file tblname clob, -- Name of associated table is_index boolean, -- TRUE if it is an index, false for a table + is_without_rowid boolean, -- TRUE if WITHOUT ROWID table nentry int, -- Number of entries in the BTree leaf_entries int, -- Number of leaf entries depth int, -- Depth of the b-tree @@ -184,7 +185,7 @@ set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 } foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] { set is_index [expr {$name!=$tblname}] - set idx_btree [expr {$is_index || [is_without_rowid $name]}] + set is_without_rowid [is_without_rowid $name] db eval { SELECT sum(ncell) AS nentry, @@ -235,6 +236,7 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] { $name, $tblname, $is_index, + $is_without_rowid, $nentry, $leaf_entries, $depth, @@ -330,12 +332,15 @@ proc subreport {title where showFrag} { # following query returns exactly one row (because it is an aggregate). # # The results of the query are stored directly by SQLite into local - # variables (i.e. $nentry, $nleaf etc.). + # variables (i.e. $nentry, $payload etc.). # mem eval " SELECT - int(sum(nentry)) AS nentry, - int(sum(leaf_entries)) AS nleaf, + int(sum( + CASE WHEN (is_without_rowid OR is_index) THEN nentry + ELSE leaf_entries + END + )) AS nentry, int(sum(payload)) AS payload, int(sum(ovfl_payload)) AS ovfl_payload, max(mx_payload) AS mx_payload, @@ -375,8 +380,8 @@ proc subreport {title where showFrag} { set storage [expr {$total_pages*$pageSize}] set payload_percent [percent $payload $storage {of storage consumed}] set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}] - set avg_payload [divide $payload $nleaf] - set avg_unused [divide $total_unused $nleaf] + set avg_payload [divide $payload $nentry] + set avg_unused [divide $total_unused $nentry] if {$int_pages>0} { # TODO: Is this formula correct? set nTab [mem eval " @@ -390,12 +395,12 @@ proc subreport {title where showFrag} { "] set avg_fanout [format %.2f $avg_fanout] } - set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}] + set ovfl_cnt_percent [percent $ovfl_cnt $nentry {of all entries}] # Print out the sub-report statistics. # statline {Percentage of total database} $total_pages_percent - statline {Number of entries} $nleaf + statline {Number of entries} $nentry statline {Bytes of storage consumed} $storage if {$compressed_size!=$storage} { set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}] diff --git a/tool/speed-check.sh b/tool/speed-check.sh new file mode 100644 index 0000000000..d10f172d59 --- /dev/null +++ b/tool/speed-check.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# +# This is a template for a script used for day-to-day size and +# performance monitoring of SQLite. Typical usage: +# +# sh run-speed-test.sh trunk # Baseline measurement of trunk +# sh run-speed-test.sh x1 # Measure some experimental change +# fossil test-diff --tk cout-trunk.txt cout-x1.txt # View chanages +# +# There are multiple output files, all with a base name given by +# the first argument: +# +# summary-$BASE.txt # Copy of standard output +# cout-$BASE.txt # cachegrind output +# explain-$BASE.txt # EXPLAIN listings (only with --explain) +# +if test "$1" = "" +then + echo "Usage: $0 OUTPUTFILE [OPTIONS]" + exit +fi +NAME=$1 +shift +CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5" +SPEEDTEST_OPTS="--shrink-memory --reprepare --heap 10000000 64" +SIZE=5 +doExplain=0 +doCachegrind=1 +while test "$1" != ""; do + case $1 in + --reprepare) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1" + ;; + --autovacuum) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1" + ;; + --utf16be) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1" + ;; + --stats) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1" + ;; + --without-rowid) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1" + ;; + --nomemstat) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1" + ;; + --temp) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --temp 6" + ;; + --wal) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --journal wal" + ;; + --size) + shift; SIZE=$1 + ;; + --explain) + doExplain=1 + ;; + --vdbeprofile) + rm -f vdbe_profile.out + CC_OPTS="$CC_OPTS -DVDBE_PROFILE" + doCachegrind=0 + ;; + --heap) + CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5" + shift; + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64" + ;; + *) + CC_OPTS="$CC_OPTS $1" + ;; + esac + shift +done +SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE" +echo "NAME = $NAME" | tee summary-$NAME.txt +echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt +echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt +rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o +gcc -g -Os -Wall -I. $CC_OPTS -c sqlite3.c +size sqlite3.o | tee -a summary-$NAME.txt +if test $doExplain -eq 1; then + gcc -g -Os -Wall -I. $CC_OPTS \ + -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ + ./shell.c ./sqlite3.c -o sqlite3 -ldl -lpthread +fi +SRC=./speedtest1.c +gcc -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread +ls -l speedtest1 | tee -a summary-$NAME.txt +if test $doCachegrind -eq 1; then + valgrind --tool=cachegrind ./speedtest1 speedtest1.db \ + $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt +else + ./speedtest1 speedtest1.db $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt +fi +size sqlite3.o | tee -a summary-$NAME.txt +wc sqlite3.c +if test $doCachegrind -eq 1; then + cg_anno.tcl cachegrind.out.* >cout-$NAME.txt +fi +if test $doExplain -eq 1; then + ./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt +fi diff --git a/tool/sqldiff.c b/tool/sqldiff.c index 319acafdbd..b1363bb341 100644 --- a/tool/sqldiff.c +++ b/tool/sqldiff.c @@ -33,6 +33,7 @@ struct GlobalVars { const char *zArgv0; /* Name of program */ int bSchemaOnly; /* Only show schema differences */ int bSchemaPK; /* Use the schema-defined PK, not the true PK */ + int bHandleVtab; /* Handle fts3, fts4, fts5 and rtree vtabs */ unsigned fDebug; /* Debug flags */ sqlite3 *db; /* The database connection */ } g; @@ -683,7 +684,7 @@ static void diff_one_table(const char *zTab, FILE *out){ /* Run the query and output differences */ if( !g.bSchemaOnly ){ - pStmt = db_prepare(sql.z); + pStmt = db_prepare("%s", sql.z); while( SQLITE_ROW==sqlite3_step(pStmt) ){ int iType = sqlite3_column_int(pStmt, nPk); if( iType==1 || iType==2 ){ @@ -1301,7 +1302,7 @@ static void rbudiff_one_table(const char *zTab, FILE *out){ char *zOtaControl; int nOtaControl = sqlite3_column_bytes(pStmt, nCol); - zOtaControl = (char*)sqlite3_malloc(nOtaControl); + zOtaControl = (char*)sqlite3_malloc(nOtaControl+1); memcpy(zOtaControl, sqlite3_column_text(pStmt, nCol), nOtaControl+1); for(i=0; i