diff --git a/Makefile.in b/Makefile.in index 4929ce313c..3d6e0f250d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -15,18 +15,22 @@ # The toplevel directory of the source tree. This is the directory # that contains this "Makefile.in" and the "configure.in" script. # -TOP = @srcdir@ +TOP = @abs_srcdir@ # C Compiler and options for use in building executables that # will run on the platform that is doing the build. # BCC = @BUILD_CC@ @BUILD_CFLAGS@ -# C Compile and options for use in building executables that +# TCC is the C Compile and options for use in building executables that # will run on the target platform. (BCC and TCC are usually the -# same unless your are cross-compiling.) +# same unless your are cross-compiling.) Separate CC and CFLAGS macros +# are provide so that these aspects of the build process can be changed +# on the "make" command-line. Ex: "make CC=clang CFLAGS=-fsanitize=undefined" # -TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree +CC = @CC@ +CFLAGS = @CPPFLAGS@ @CFLAGS@ +TCC = $(CC) $(CFLAGS) -I. -I${TOP}/src -I${TOP}/ext/rtree -I${TOP}/ext/fts3 # Define this for the autoconf-based build, so that the code knows it can # include the generated config.h @@ -37,7 +41,7 @@ TCC += -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite # Omitting the define will cause extra debugging code to be inserted and # includes extra comments when "EXPLAIN stmt" is used. # -TCC += @TARGET_DEBUG@ @XTHREADCONNECT@ +TCC += @TARGET_DEBUG@ # Compiler options needed for programs that use the TCL library. # @@ -230,6 +234,7 @@ SRC = \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/memjournal.c \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ @@ -459,6 +464,7 @@ HDR = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ @@ -530,6 +536,11 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c $(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ $(TLIBS) -rpath "$(libdir)" +mptest: mptester$(EXE) + rm -f mptest1.db + ./mptester$(EXE) mptest1.db $(TOP)/mptest/crash01.test + rm -f mptest2.db + ./mptester$(EXE) mptest2.db $(TOP)/mptest/multiwrite01.test # This target creates a directory named "tsrc" and fills it with # copies of all of the C source code and header files needed to @@ -921,19 +932,37 @@ testfixture$(TEXE): $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) - +# A very detailed test running most or all test cases fulltest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test +# Really really long testing soaktest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test -soak=1 +# Do extra testing but not aeverything. fulltestonly: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/full.test +# This is the common case. Run many tests but not those that take +# a really long time. +# test: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/veryquick.test +# Run a test using valgrind. This can take a really long time +# because valgrind is so much slower than a native machine. +# +valgrindtest: testfixture$(TEXE) sqlite3$(TEXE) + OMIT_MISUSE=1 valgrind -v ./testfixture$(TEXE) $(TOP)/test/permutations.test valgrind + +# A very fast test that checks basic sanity. The name comes from +# the 60s-era electronics testing: "Turn it on and see if smoke +# comes out." +# +smoketest: testfixture$(TEXE) + ./testfixture$(TEXE) $(TOP)/test/main.test + sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl echo "#define TCLSH 2" > $@ cat sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c >> $@ @@ -969,6 +998,39 @@ wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c speedtest1$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo $(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS) +# This target will fail if the SQLite amalgamation contains any exported +# symbols that do not begin with "sqlite3_". It is run as part of the +# releasetest.tcl script. +# +checksymbols: sqlite3.lo + nm -g --defined-only sqlite3.o | grep -v " sqlite3_" ; test $$? -ne 0 + echo '0 errors out of 1 tests' + +# Build the amalgamation-autoconf package. +# +amalgamation-tarball: sqlite3.c + TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh + +# The next two rules are used to support the "threadtest" target. Building +# threadtest runs a few thread-safety tests that are implemented in C. This +# target is invoked by the releasetest.tcl script. +# +THREADTEST3_SRC = $(TOP)/test/threadtest3.c \ + $(TOP)/test/tt3_checkpoint.c \ + $(TOP)/test/tt3_index.c \ + $(TOP)/test/tt3_vacuum.c \ + $(TOP)/test/tt3_stress.c \ + $(TOP)/test/tt3_lookaside1.c + +threadtest3$(TEXE): sqlite3.lo $(THREADTEST3_SRC) + $(LTLINK) $(TOP)/test/threadtest3.c sqlite3.lo -o $@ $(TLIBS) + +threadtest: threadtest3$(TEXE) + ./threadtest3$(TEXE) + +releasetest: + $(TCLSH_CMD) $(TOP)/test/releasetest.tcl + # Standard install and cleanup targets # lib_install: libsqlite3.la diff --git a/Makefile.msc b/Makefile.msc index 64175a0679..1908794840 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -16,6 +16,23 @@ TOP = . USE_AMALGAMATION = 1 !ENDIF +# Set this non-0 to enable full warnings (-W4, etc) when compiling. +# +!IFNDEF USE_FULLWARN +USE_FULLWARN = 0 +!ENDIF + +# If necessary, create a list of harmless compiler warnings to disable when +# compiling the build tools. For the SQLite source code itself, warnings, +# if any, will be disabled from within it. +# +!IFNDEF NO_WARN +!IF $(USE_FULLWARN)!=0 +NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4152 -wd4189 -wd4206 -wd4210 +NO_WARN = $(NO_WARN) -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706 +!ENDIF +!ENDIF + # Set this non-0 to use the library paths and other options necessary for # Windows Phone 8.1. # @@ -42,6 +59,12 @@ USE_ICU = 0 USE_CRT_DLL = 0 !ENDIF +# Set this non-0 to link to the RPCRT4 library. +# +!IFNDEF USE_RPCRT4_LIB +USE_RPCRT4_LIB = 0 +!ENDIF + # Set this non-0 to generate assembly code listings for the source code # files. # @@ -108,11 +131,12 @@ WIN32HEAP = 0 # levels. Currently, the recognized values for DEBUG are: # # 0 == NDEBUG: Disables assert() and other runtime diagnostics. -# 1 == Disables NDEBUG and all optimizations and then enables PDBs. -# 2 == SQLITE_DEBUG: Enables various diagnostics messages and code. -# 3 == SQLITE_WIN32_MALLOC_VALIDATE: Validate the Win32 native heap per call. -# 4 == SQLITE_DEBUG_OS_TRACE: Enables output from the OSTRACE() macros. -# 5 == SQLITE_ENABLE_IOTRACE: Enables output from the IOTRACE() macros. +# 1 == SQLITE_ENABLE_API_ARMOR: extra attempts to detect misuse of the API. +# 2 == Disables NDEBUG and all optimizations and then enables PDBs. +# 3 == SQLITE_DEBUG: Enables various diagnostics messages and code. +# 4 == SQLITE_WIN32_MALLOC_VALIDATE: Validate the Win32 native heap per call. +# 5 == SQLITE_DEBUG_OS_TRACE: Enables output from the OSTRACE() macros. +# 6 == SQLITE_ENABLE_IOTRACE: Enables output from the IOTRACE() macros. # !IFNDEF DEBUG DEBUG = 0 @@ -231,7 +255,11 @@ NSDKLIBPATH = $(NSDKLIBPATH:\\=\) # C compiler and options for use in building executables that # will run on the platform that is doing the build. # +!IF $(USE_FULLWARN)!=0 +BCC = $(NCC) -W4 +!ELSE BCC = $(NCC) -W3 +!ENDIF # Check if assembly code listings should be generated for the source # code files to be compiled. @@ -252,7 +280,13 @@ NLTLIBPATHS = "/LIBPATH:$(NCRTLIBPATH)" "/LIBPATH:$(NSDKLIBPATH)" # will run on the target platform. (BCC and TCC are usually the # same unless your are cross-compiling.) # -TCC = $(CC) -W3 -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise +!IF $(USE_FULLWARN)!=0 +TCC = $(CC) -W4 -DINCLUDE_MSVC_H=1 +!ELSE +TCC = $(CC) -W3 +!ENDIF + +TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src # Check if assembly code listings should be generated for the source @@ -280,7 +314,7 @@ RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP # MSVC runtime library. # !IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0 -!IF $(DEBUG)>0 +!IF $(DEBUG)>1 TCC = $(TCC) -MDd BCC = $(BCC) -MDd !ELSE @@ -288,7 +322,7 @@ TCC = $(TCC) -MD BCC = $(BCC) -MD !ENDIF !ELSE -!IF $(DEBUG)>0 +!IF $(DEBUG)>1 TCC = $(TCC) -MTd BCC = $(BCC) -MTd !ELSE @@ -313,7 +347,7 @@ RCC = $(RCC) -I$(TOP)\ext\rtree # options are necessary in order to allow debugging symbols to # work correctly with Visual Studio when using the amalgamation. # -!IF $(DEBUG)>0 +!IF $(DEBUG)>1 MKSQLITE3C_ARGS = --linemacros !ELSE MKSQLITE3C_ARGS = @@ -329,17 +363,22 @@ BCC = $(BCC) -DNDEBUG RCC = $(RCC) -DNDEBUG !ENDIF -!IF $(DEBUG)>1 +!IF $(DEBUG)>0 +TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR +RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR +!ENDIF + +!IF $(DEBUG)>2 TCC = $(TCC) -DSQLITE_DEBUG RCC = $(RCC) -DSQLITE_DEBUG !ENDIF -!IF $(DEBUG)>3 +!IF $(DEBUG)>4 TCC = $(TCC) -DSQLITE_DEBUG_OS_TRACE=1 RCC = $(RCC) -DSQLITE_DEBUG_OS_TRACE=1 !ENDIF -!IF $(DEBUG)>4 +!IF $(DEBUG)>5 TCC = $(TCC) -DSQLITE_ENABLE_IOTRACE RCC = $(RCC) -DSQLITE_ENABLE_IOTRACE !ENDIF @@ -371,7 +410,7 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC=1 # Validate the heap on every call into the native Win32 heap subsystem? # -!IF $(DEBUG)>2 +!IF $(DEBUG)>3 TCC = $(TCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1 RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1 !ENDIF @@ -468,6 +507,12 @@ RCC = $(RCC) -DSQLITE_TEMP_STORE=1 # REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100 +# If we are linking to the RPCRT4 library, enable features that need it. +# +!IF $(USE_RPCRT4_LIB)!=0 +REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS) -DSQLITE_WIN32_USE_UUID=1 +!ENDIF + # Add the required and optional SQLite compilation options into the command # lines used to invoke the MSVC code and resource compilers. # @@ -482,7 +527,7 @@ RCC = $(RCC) $(OPTS) # If compiling for debugging, add some defines. # -!IF $(DEBUG)>0 +!IF $(DEBUG)>1 TCC = $(TCC) -D_DEBUG BCC = $(BCC) -D_DEBUG RCC = $(RCC) -D_DEBUG @@ -491,7 +536,7 @@ RCC = $(RCC) -D_DEBUG # If optimizations are enabled or disabled (either implicitly or # explicitly), add the necessary flags. # -!IF $(DEBUG)>0 || $(OPTIMIZATIONS)==0 +!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0 TCC = $(TCC) -Od BCC = $(BCC) -Od !ELSEIF $(OPTIMIZATIONS)>=3 @@ -507,7 +552,7 @@ BCC = $(BCC) -O1 # If symbols are enabled (or compiling for debugging), enable PDBs. # -!IF $(DEBUG)>0 || $(SYMBOLS)!=0 +!IF $(DEBUG)>1 || $(SYMBOLS)!=0 TCC = $(TCC) -Zi BCC = $(BCC) -Zi !ENDIF @@ -531,6 +576,12 @@ LTRCOMPILE = $(RCC) -r LTLIB = lib.exe LTLINK = $(TCC) -Fe$@ +# If requested, link to the RPCRT4 library. +# +!IF $(USE_RPCRT4_LIB)!=0 +LTLINK = $(LTLINK) rpcrt4.lib +!ENDIF + # If a platform was set, force the linker to target that. # Note that the vcvars*.bat family of batch files typically # set this for you. Otherwise, the linker will attempt @@ -592,7 +643,7 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib # If either debugging or symbols are enabled, enable PDBs. # -!IF $(DEBUG)>0 || $(SYMBOLS)!=0 +!IF $(DEBUG)>1 || $(SYMBOLS)!=0 LDFLAGS = /DEBUG !ENDIF @@ -700,6 +751,7 @@ SRC = \ $(TOP)\src\mem3.c \ $(TOP)\src\mem5.c \ $(TOP)\src\memjournal.c \ + $(TOP)\src\msvc.h \ $(TOP)\src\mutex.c \ $(TOP)\src\mutex.h \ $(TOP)\src\mutex_noop.c \ @@ -932,6 +984,7 @@ HDR = \ $(TOP)\src\hash.h \ $(TOP)\src\hwtime.h \ keywordhash.h \ + $(TOP)\src\msvc.h \ $(TOP)\src\mutex.h \ opcodes.h \ $(TOP)\src\os.h \ @@ -983,8 +1036,7 @@ libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS) sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h - $(LTLINK) $(READLINE_FLAGS) \ - $(TOP)\src\shell.c \ + $(LTLINK) $(READLINE_FLAGS) $(TOP)\src\shell.c \ /link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) mptester.exe: $(TOP)\mptest\mptest.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h @@ -1034,7 +1086,8 @@ lempar.c: $(TOP)\src\lempar.c copy $(TOP)\src\lempar.c . lemon.exe: $(TOP)\tool\lemon.c lempar.c - $(BCC) -Daccess=_access -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) + $(BCC) $(NO_WARN) -Daccess=_access \ + -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) # Rules to build individual *.lo files from generated *.c files. This # applies to: @@ -1305,7 +1358,8 @@ sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c - $(BCC) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) + $(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) \ + $(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe .\mkkeywordhash.exe > keywordhash.h @@ -1386,7 +1440,8 @@ rtree.lo: $(TOP)\ext\rtree\rtree.c $(HDR) $(EXTHDR) # hidden when the library is built via the amalgamation). # TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 -TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN) TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) libsqlite3.lib TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C) diff --git a/Makefile.vxworks b/Makefile.vxworks index 0d9c27f635..706261fc00 100644 --- a/Makefile.vxworks +++ b/Makefile.vxworks @@ -253,6 +253,7 @@ SRC = \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/memjournal.c \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ @@ -414,6 +415,7 @@ HDR = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ diff --git a/README.md b/README.md index 5e52dea507..ff8dc34aef 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ to the "sqlite3.dll" command line above. When debugging into the SQLite code, adding the "DEBUG=1" argument to one of the above command lines is recommended. -SQLite does not require Tcl to run, but a Tcl installation is required -by the makefiles (including those for MSVC). SQLite contains a lot of -generated code and Tcl is used to do much of that code generation. The -makefiles also require AWK. +SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation +is required by the makefiles (including those for MSVC). SQLite contains +a lot of generated code and Tcl is used to do much of that code generation. +The makefiles also require AWK. ## Source Code Tour @@ -95,14 +95,14 @@ manually-edited files and automatically-generated files. The SQLite interface is defined by the **sqlite3.h** header file, which is generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The -Tcl script at tool/mksqlite3h.tcl does the conversion. The manifest.uuid -file contains the SHA1 hash of the particular check-in and is used to generate -the SQLITE_SOURCE_ID macro. The VERSION file contains the current SQLite -version number. The sqlite3.h header is really just a copy of src/sqlite.h.in -with the source-id and version number inserted at just the right spots. -Note that comment text in the sqlite3.h file is used to generate much of -the SQLite API documentation. The Tcl scripts used to generate that -documentation are in a separate source repository. +[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion. +The manifest.uuid file contains the SHA1 hash of the particular check-in +and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file +contains the current SQLite version number. The sqlite3.h header is really +just a copy of src/sqlite.h.in with the source-id and version number inserted +at just the right spots. Note that comment text in the sqlite3.h file is +used to generate much of the SQLite API documentation. The Tcl scripts +used to generate that documentation are in a separate source repository. The SQL language parser is **parse.c** which is generate from a grammar in the src/parse.y file. The conversion of "parse.y" into "parse.c" is done diff --git a/autoconf/README.first b/autoconf/README.first index 6676228ad6..5c2ea0a70f 100644 --- a/autoconf/README.first +++ b/autoconf/README.first @@ -1,57 +1,11 @@ +This directory contains components use to build an autoconf-ready package +of the SQLite amalgamation: sqlite-autoconf-30XXXXXX.tar.gz -This file describes how to use the files in this directory to create a new -version of the "autoconf-amalgamation" package. - -1. The following files should have executable permission: - - chmod 755 install-sh - chmod 755 missing - chmod 755 depcomp - chmod 755 config.sub - chmod 755 config.guess - -2. Copy new versions of the following SQLite files into this directory: - - sqlite3.c - sqlite3.h - sqlite3ext.h - sqlite3.1 - sqlite3.pc.in - shell.c - -3. Update the SQLite version number in the AC_INIT macro in file - configure.ac: - - AC_INIT(sqlite, 3.6.3, http://www.sqlite.org) - -4. Run the following commands to push the version number change through - to the generated files. - - aclocal - autoconf - automake - -5. Create the tclsqlite3.c file in the tea/generic directory. As follows: - - mkdir -p tea/generic - echo "#ifdef USE_SYSTEM_SQLITE" > tea/generic/tclsqlite3.c - echo "# include " >> tea/generic/tclsqlite3.c - echo "#else" >> tea/generic/tclsqlite3.c - echo "#include \"../../sqlite3.c\"" >> tea/generic/tclsqlite3.c - echo "#endif" >> tea/generic/tclsqlite3.c - cat ../src/tclsqlite.c >> tea/generic/tclsqlite3.c - -6. Update the SQLite version in the AC_INIT macro in file tea/configure.in: - - AC_INIT([sqlite], [3.6.3]) - -7. From the 'tea' directory, run the following commands: - - autoconf - rm -rf autom4te.cache - -8. Run "./configure && make dist". This builds a distribution package - named something like "sqlite-3.6.3.tar.gz". Rename to - "sqlite-amalgamation-3.6.3.tar.gz" and use. +To build the autoconf amalgamation, run from the top-level: + ./configure + make amalgamation-tarball +The amalgamation-tarball target (also available in "main.mk") runs the +script tool/mkautoconfamal.sh which does the work. Refer to that script +for details. diff --git a/autoconf/tea/configure.in b/autoconf/tea/configure.ac similarity index 100% rename from autoconf/tea/configure.in rename to autoconf/tea/configure.ac diff --git a/config.h.in b/config.h.in index efc1d47bc6..36fd60781c 100644 --- a/config.h.in +++ b/config.h.in @@ -27,6 +27,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the `isnan' function. */ +#undef HAVE_ISNAN + /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R @@ -48,6 +51,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H +/* Define to 1 if you have the strchrnul() function */ +#undef HAVE_STRCHRNUL + /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H diff --git a/configure b/configure index c0f751db62..9e639e7f75 100755 --- a/configure +++ b/configure @@ -868,7 +868,6 @@ RELEASE VERSION_NUMBER BUILD_CC SQLITE_THREADSAFE -XTHREADCONNECT ALLOWRELEASE TEMP_STORE BUILD_EXEEXT @@ -906,9 +905,7 @@ enable_fast_install with_gnu_ld enable_libtool_lock enable_largefile -with_hints enable_threadsafe -enable_cross_thread_connections enable_releasemode enable_tempstore enable_tcl @@ -1562,9 +1559,7 @@ Optional Features: optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files - --enable-threadsafe Support threadsafe operation - --enable-cross-thread-connections - Allow connection sharing across threads + --disable-threadsafe Disable mutexing --enable-releasemode Support libtool link to release mode --enable-tempstore Use an in-ram database for temporary tables (never,no,yes,always) @@ -1573,7 +1568,8 @@ Optional Features: --enable-debug enable debugging & verbose explain --disable-amalgamation Disable the amalgamation and instead build all files separately - --enable-load-extension Enable loading of external extensions + --disable-load-extension + Disable loading of external extensions --enable-gcov Enable coverage testing using gcov Optional Packages: @@ -1582,7 +1578,6 @@ Optional Packages: --with-pic try to use only PIC/non-PIC objects [default=use both] --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-hints=FILE Read configuration options from FILE --with-tcl=DIR directory containing tcl configuration (tclConfig.sh) --with-readline-lib specify readline library @@ -2058,9 +2053,6 @@ please regen with autoconf" >&2;} { (exit 1); exit 1; }; } fi -# The following RCS revision string applies to configure.in -# $Revision: 1.56 $ - ######### # Programs needed # @@ -3732,13 +3724,13 @@ if test "${lt_cv_nm_interface+set}" = set; then else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3735: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3727: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3738: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3730: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3741: output\"" >&5) + (eval echo "\"\$as_me:3733: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -4960,7 +4952,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4963 "configure"' > conftest.$ac_ext + echo '#line 4955 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6829,11 +6821,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:6832: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6824: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6836: \$? = $ac_status" >&5 + echo "$as_me:6828: \$? = $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. @@ -7168,11 +7160,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:7171: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7163: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7175: \$? = $ac_status" >&5 + echo "$as_me:7167: \$? = $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. @@ -7273,11 +7265,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:7276: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7268: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7280: \$? = $ac_status" >&5 + echo "$as_me:7272: \$? = $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 @@ -7328,11 +7320,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:7331: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7323: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7335: \$? = $ac_status" >&5 + echo "$as_me:7327: \$? = $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 @@ -10141,7 +10133,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10144 "configure" +#line 10136 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10237,7 +10229,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10240 "configure" +#line 10232 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12146,7 +12138,9 @@ done -for ac_func in usleep fdatasync localtime_r gmtime_r localtime_s utime malloc_usable_size + + +for ac_func in fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -12342,45 +12336,6 @@ VERSION_NUMBER=`cat $srcdir/VERSION \ $as_echo "$as_me: Version number set to $VERSION_NUMBER" >&6;} -######### -# Check to see if the --with-hints=FILE option is used. If there is none, -# then check for a files named "$host.hints" and ../$hosts.hints where -# $host is the hostname of the build system. If still no hints are -# found, try looking in $system.hints and ../$system.hints where -# $system is the result of uname -s. -# - -# Check whether --with-hints was given. -if test "${with_hints+set}" = set; then - withval=$with_hints; hints=$withval -fi - -if test "$hints" = ""; then - host=`hostname | sed 's/\..*//'` - if test -r $host.hints; then - hints=$host.hints - else - if test -r ../$host.hints; then - hints=../$host.hints - fi - fi -fi -if test "$hints" = ""; then - sys=`uname -s` - if test -r $sys.hints; then - hints=$sys.hints - else - if test -r ../$sys.hints; then - hints=../$sys.hints - fi - fi -fi -if test "$hints" != ""; then - { $as_echo "$as_me:$LINENO: result: reading hints from $hints" >&5 -$as_echo "reading hints from $hints" >&6; } - . $hints -fi - ######### # Locate a compiler for the build machine. This compiler should # generate command-line programs that run on the build machine. @@ -12552,31 +12507,6 @@ fi fi -########## -# Do we want to allow a connection created in one thread to be used -# in another thread. This does not work on many Linux systems (ex: RedHat 9) -# due to bugs in the threading implementations. This is thus off by default. -# -# Check whether --enable-cross-thread-connections was given. -if test "${enable_cross_thread_connections+set}" = set; then - enableval=$enable_cross_thread_connections; -else - enable_xthreadconnect=no -fi - -{ $as_echo "$as_me:$LINENO: checking whether to allow connections to be shared across threads" >&5 -$as_echo_n "checking whether to allow connections to be shared across threads... " >&6; } -if test "$enable_xthreadconnect" = "no"; then - XTHREADCONNECT='' - { $as_echo "$as_me:$LINENO: result: no" >&5 -$as_echo "no" >&6; } -else - XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1' - { $as_echo "$as_me:$LINENO: result: yes" >&5 -$as_echo "yes" >&6; } -fi - - ########## # Do we want to support release # @@ -13427,7 +13357,7 @@ fi if test "${enable_load_extension+set}" = set; then enableval=$enable_load_extension; use_loadextension=$enableval else - use_loadextension=no + use_loadextension=yes fi if test "${use_loadextension}" = "yes" ; then diff --git a/configure.ac b/configure.ac index 2e70f2c235..00ecf453af 100644 --- a/configure.ac +++ b/configure.ac @@ -69,19 +69,6 @@ # The filename extension for executables on the # target platform. "" for Unix and ".exe" for windows. # -# The generated configure script will make an attempt to guess -# at all of the above parameters. You can override any of -# the guesses by setting the environment variable named -# "config_AAAA" where "AAAA" is the name of the parameter -# described above. (Exception: srcdir cannot be set this way.) -# If you have a file that sets one or more of these environment -# variables, you can invoke configure as follows: -# -# configure --with-hints=FILE -# -# where FILE is the name of the file that sets the environment -# variables. FILE should be an absolute pathname. -# # This configure.in file is easy to reuse on other projects. Just # change the argument to AC_INIT(). And disable any features that # you don't need (for example BLT) by erasing or commenting out @@ -98,11 +85,6 @@ AC_MSG_ERROR([configure script is out of date: please regen with autoconf]) fi -dnl Put the RCS revision string after AC_INIT so that it will also -dnl show in in configure. -# The following RCS revision string applies to configure.in -# $Revision: 1.56 $ - ######### # Programs needed # @@ -127,7 +109,7 @@ AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h malloc.h]) ######### # Figure out whether or not we have these functions # -AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s utime malloc_usable_size]) +AC_CHECK_FUNCS([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime]) ######### # By default, we use the amalgamation (this may be changed below...) @@ -180,41 +162,6 @@ VERSION_NUMBER=[`cat $srcdir/VERSION \ AC_MSG_NOTICE(Version number set to $VERSION_NUMBER) AC_SUBST(VERSION_NUMBER) -######### -# Check to see if the --with-hints=FILE option is used. If there is none, -# then check for a files named "$host.hints" and ../$hosts.hints where -# $host is the hostname of the build system. If still no hints are -# found, try looking in $system.hints and ../$system.hints where -# $system is the result of uname -s. -# -AC_ARG_WITH(hints, - AC_HELP_STRING([--with-hints=FILE],[Read configuration options from FILE]), - hints=$withval) -if test "$hints" = ""; then - host=`hostname | sed 's/\..*//'` - if test -r $host.hints; then - hints=$host.hints - else - if test -r ../$host.hints; then - hints=../$host.hints - fi - fi -fi -if test "$hints" = ""; then - sys=`uname -s` - if test -r $sys.hints; then - hints=$sys.hints - else - if test -r ../$sys.hints; then - hints=../$sys.hints - fi - fi -fi -if test "$hints" != ""; then - AC_MSG_RESULT(reading hints from $hints) - . $hints -fi - ######### # Locate a compiler for the build machine. This compiler should # generate command-line programs that run on the build machine. @@ -236,7 +183,7 @@ AC_SUBST(BUILD_CC) # Do we want to support multithreaded use of sqlite # AC_ARG_ENABLE(threadsafe, -AC_HELP_STRING([--enable-threadsafe],[Support threadsafe operation]),,enable_threadsafe=yes) +AC_HELP_STRING([--disable-threadsafe],[Disable mutexing]),,enable_threadsafe=yes) AC_MSG_CHECKING([whether to support threadsafe operation]) if test "$enable_threadsafe" = "no"; then SQLITE_THREADSAFE=0 @@ -251,23 +198,6 @@ if test "$SQLITE_THREADSAFE" = "1"; then AC_SEARCH_LIBS(pthread_create, pthread) fi -########## -# Do we want to allow a connection created in one thread to be used -# in another thread. This does not work on many Linux systems (ex: RedHat 9) -# due to bugs in the threading implementations. This is thus off by default. -# -AC_ARG_ENABLE(cross-thread-connections, -AC_HELP_STRING([--enable-cross-thread-connections],[Allow connection sharing across threads]),,enable_xthreadconnect=no) -AC_MSG_CHECKING([whether to allow connections to be shared across threads]) -if test "$enable_xthreadconnect" = "no"; then - XTHREADCONNECT='' - AC_MSG_RESULT([no]) -else - XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1' - AC_MSG_RESULT([yes]) -fi -AC_SUBST(XTHREADCONNECT) - ########## # Do we want to support release # @@ -605,9 +535,9 @@ AC_SUBST(USE_AMALGAMATION) ######### # See whether we should allow loadable extensions -AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--enable-load-extension], - [Enable loading of external extensions]), - [use_loadextension=$enableval],[use_loadextension=no]) +AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--disable-load-extension], + [Disable loading of external extensions]), + [use_loadextension=$enableval],[use_loadextension=yes]) if test "${use_loadextension}" = "yes" ; then OPT_FEATURE_FLAGS="" AC_SEARCH_LIBS(dlopen, dl) diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 2b93c62715..a1ea533a89 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -1853,7 +1853,7 @@ static int fts3SelectLeaf( sqlite3_int64 *piLeaf, /* Selected leaf node */ sqlite3_int64 *piLeaf2 /* Selected leaf node */ ){ - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ int iHeight; /* Height of this node in tree */ assert( piLeaf || piLeaf2 ); @@ -1864,7 +1864,7 @@ static int fts3SelectLeaf( if( rc==SQLITE_OK && iHeight>1 ){ char *zBlob = 0; /* Blob read from %_segments table */ - int nBlob; /* Size of zBlob in bytes */ + int nBlob = 0; /* Size of zBlob in bytes */ if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); @@ -3086,7 +3086,7 @@ static int fts3FilterMethod( int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ - int rc; + int rc = SQLITE_OK; char *zSql; /* SQL statement used to access %_content */ int eSearch; Fts3Table *p = (Fts3Table *)pCursor->pVtab; @@ -5020,6 +5020,22 @@ static void fts3EvalNextRow( } pExpr->iDocid = pLeft->iDocid; pExpr->bEof = (pLeft->bEof || pRight->bEof); + if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){ + if( pRight->pPhrase && pRight->pPhrase->doclist.aAll ){ + Fts3Doclist *pDl = &pRight->pPhrase->doclist; + while( *pRc==SQLITE_OK && pRight->bEof==0 ){ + memset(pDl->pList, 0, pDl->nList); + fts3EvalNextRow(pCsr, pRight, pRc); + } + } + if( pLeft->pPhrase && pLeft->pPhrase->doclist.aAll ){ + Fts3Doclist *pDl = &pLeft->pPhrase->doclist; + while( *pRc==SQLITE_OK && pLeft->bEof==0 ){ + memset(pDl->pList, 0, pDl->nList); + fts3EvalNextRow(pCsr, pLeft, pRc); + } + } + } } break; } @@ -5392,6 +5408,7 @@ static void fts3EvalRestart( } pPhrase->doclist.pNextDocid = 0; pPhrase->doclist.iDocid = 0; + pPhrase->pOrPoslist = 0; } pExpr->iDocid = 0; @@ -5637,8 +5654,8 @@ int sqlite3Fts3EvalPhrasePoslist( iDocid = pExpr->iDocid; pIter = pPhrase->doclist.pList; if( iDocid!=pCsr->iPrevId || pExpr->bEof ){ + int rc = SQLITE_OK; int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */ - int iMul; /* +1 if csr dir matches index dir, else -1 */ int bOr = 0; u8 bEof = 0; u8 bTreeEof = 0; @@ -5662,72 +5679,43 @@ int sqlite3Fts3EvalPhrasePoslist( ** an incremental phrase. Load the entire doclist for the phrase ** into memory in this case. */ if( pPhrase->bIncr ){ - int rc = SQLITE_OK; - int bEofSave = pExpr->bEof; - fts3EvalRestart(pCsr, pExpr, &rc); - while( rc==SQLITE_OK && !pExpr->bEof ){ - fts3EvalNextRow(pCsr, pExpr, &rc); - if( bEofSave==0 && pExpr->iDocid==iDocid ) break; + int bEofSave = pNear->bEof; + fts3EvalRestart(pCsr, pNear, &rc); + while( rc==SQLITE_OK && !pNear->bEof ){ + fts3EvalNextRow(pCsr, pNear, &rc); + if( bEofSave==0 && pNear->iDocid==iDocid ) break; } - pIter = pPhrase->doclist.pList; assert( rc!=SQLITE_OK || pPhrase->bIncr==0 ); - if( rc!=SQLITE_OK ) return rc; } - - iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1); - while( bTreeEof==1 - && pNear->bEof==0 - && (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0 - ){ - int rc = SQLITE_OK; - fts3EvalNextRow(pCsr, pExpr, &rc); - if( rc!=SQLITE_OK ) return rc; - iDocid = pExpr->iDocid; - pIter = pPhrase->doclist.pList; - } - - bEof = (pPhrase->doclist.nAll==0); - assert( bDescDoclist==0 || bDescDoclist==1 ); - assert( pCsr->bDesc==0 || pCsr->bDesc==1 ); - - if( bEof==0 ){ - if( pCsr->bDesc==bDescDoclist ){ - int dummy; - if( pNear->bEof ){ - /* This expression is already at EOF. So position it to point to the - ** last entry in the doclist at pPhrase->doclist.aAll[]. Variable - ** iDocid is already set for this entry, so all that is required is - ** to set pIter to point to the first byte of the last position-list - ** in the doclist. - ** - ** It would also be correct to set pIter and iDocid to zero. In - ** this case, the first call to sqltie3Fts4DoclistPrev() below - ** would also move the iterator to point to the last entry in the - ** doclist. However, this is expensive, as to do so it has to - ** iterate through the entire doclist from start to finish (since - ** it does not know the docid for the last entry). */ - pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1]; - fts3ReversePoslist(pPhrase->doclist.aAll, &pIter); - } - while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ - sqlite3Fts3DoclistPrev( - bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, - &pIter, &iDocid, &dummy, &bEof - ); - } - }else{ - if( pNear->bEof ){ - pIter = 0; - iDocid = 0; - } - while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ - sqlite3Fts3DoclistNext( - bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, - &pIter, &iDocid, &bEof - ); - } + if( bTreeEof ){ + while( rc==SQLITE_OK && !pNear->bEof ){ + fts3EvalNextRow(pCsr, pNear, &rc); } } + if( rc!=SQLITE_OK ) return rc; + + pIter = pPhrase->pOrPoslist; + iDocid = pPhrase->iOrDocid; + if( pCsr->bDesc==bDescDoclist ){ + bEof = (pIter >= (pPhrase->doclist.aAll + pPhrase->doclist.nAll)); + while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ + sqlite3Fts3DoclistNext( + bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, + &pIter, &iDocid, &bEof + ); + } + }else{ + bEof = !pPhrase->doclist.nAll || (pIter && pIter<=pPhrase->doclist.aAll); + while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){ + int dummy; + sqlite3Fts3DoclistPrev( + bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, + &pIter, &iDocid, &dummy, &bEof + ); + } + } + pPhrase->pOrPoslist = pIter; + pPhrase->iOrDocid = iDocid; if( bEof || iDocid!=pCsr->iPrevId ) pIter = 0; } @@ -5741,10 +5729,13 @@ int sqlite3Fts3EvalPhrasePoslist( } while( iThispExpr, fts3SnippetFindPositions, (void *)&sIter); + rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sIter); + if( rc==SQLITE_OK ){ - /* Set the *pmSeen output variable. */ - for(i=0; iiCol = iCol; - while( !fts3SnippetNextCandidate(&sIter) ){ - int iPos; - int iScore; - u64 mCover; - u64 mHighlight; - fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover, &mHighlight); - assert( iScore>=0 ); - if( iScore>iBestScore ){ - pFragment->iPos = iPos; - pFragment->hlmask = mHighlight; - pFragment->covered = mCover; - iBestScore = iScore; + /* Loop through all candidate snippets. Store the best snippet in + ** *pFragment. Store its associated 'score' in iBestScore. + */ + pFragment->iCol = iCol; + while( !fts3SnippetNextCandidate(&sIter) ){ + int iPos; + int iScore; + u64 mCover; + u64 mHighlite; + fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover,&mHighlite); + assert( iScore>=0 ); + if( iScore>iBestScore ){ + pFragment->iPos = iPos; + pFragment->hlmask = mHighlite; + pFragment->covered = mCover; + iBestScore = iScore; + } } - } + *piScore = iBestScore; + } sqlite3_free(sIter.aPhrase); - *piScore = iBestScore; - return SQLITE_OK; + return rc; } @@ -680,8 +682,12 @@ static int fts3SnippetText( ** required. They are required if (a) this is not the first fragment, ** or (b) this fragment does not begin at position 0 of its column. */ - if( rc==SQLITE_OK && (iPos>0 || iFragment>0) ){ - rc = fts3StringAppend(pOut, zEllipsis, -1); + if( rc==SQLITE_OK ){ + if( iPos>0 || iFragment>0 ){ + rc = fts3StringAppend(pOut, zEllipsis, -1); + }else if( iBegin ){ + rc = fts3StringAppend(pOut, zDoc, iBegin); + } } if( rc!=SQLITE_OK || iCurrentzKey)==pOld ); */ ppParent = amatchAvlFromPtr(pOld, ppHead); if( pOld->pBefore==0 && pOld->pAfter==0 ){ @@ -998,6 +998,23 @@ static void amatchWriteCost(amatch_word *pWord){ pWord->zCost[8] = 0; } +/* Circumvent compiler warnings about the use of strcpy() by supplying +** our own implementation. +*/ +#if defined(__OpenBSD__) +static void amatchStrcpy(char *dest, const char *src){ + while( (*(dest++) = *(src++))!=0 ){} +} +static void amatchStrcat(char *dest, const char *src){ + while( *dest ) dest++; + amatchStrcpy(dest, src); +} +#else +# define amatchStrcpy strcpy +# define amatchStrcat strcat +#endif + + /* ** Add a new amatch_word object to the queue. ** @@ -1073,7 +1090,7 @@ static void amatchAddWord( assert( pOther==0 ); (void)pOther; pWord->sWord.zKey = pWord->zWord; pWord->sWord.pWord = pWord; - strcpy(pWord->zWord, pCur->zBuf); + amatchStrcpy(pWord->zWord, pCur->zBuf); pOther = amatchAvlInsert(&pCur->pWord, &pWord->sWord); assert( pOther==0 ); (void)pOther; #ifdef AMATCH_TRACE_1 @@ -1083,6 +1100,7 @@ static void amatchAddWord( #endif } + /* ** Advance a cursor to its next row of output */ @@ -1148,7 +1166,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){ zBuf = sqlite3_realloc(zBuf, nBuf); if( zBuf==0 ) return SQLITE_NOMEM; } - strcpy(zBuf, pWord->zWord+2); + amatchStrcpy(zBuf, pWord->zWord+2); zNext[0] = 0; zNextIn[0] = pCur->zInput[pWord->nMatch]; if( zNextIn[0] ){ @@ -1163,7 +1181,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){ if( zNextIn[0] && zNextIn[0]!='*' ){ sqlite3_reset(p->pVCheck); - strcat(zBuf, zNextIn); + amatchStrcat(zBuf, zNextIn); sqlite3_bind_text(p->pVCheck, 1, zBuf, nWord+nNextIn, SQLITE_STATIC); rc = sqlite3_step(p->pVCheck); if( rc==SQLITE_ROW ){ @@ -1176,13 +1194,13 @@ static int amatchNext(sqlite3_vtab_cursor *cur){ } while( 1 ){ - strcpy(zBuf+nWord, zNext); + amatchStrcpy(zBuf+nWord, zNext); sqlite3_reset(p->pVCheck); sqlite3_bind_text(p->pVCheck, 1, zBuf, -1, SQLITE_TRANSIENT); rc = sqlite3_step(p->pVCheck); if( rc!=SQLITE_ROW ) break; zW = (const char*)sqlite3_column_text(p->pVCheck, 0); - strcpy(zBuf+nWord, zNext); + amatchStrcpy(zBuf+nWord, zNext); if( strncmp(zW, zBuf, nWord)!=0 ) break; if( (zNextIn[0]=='*' && zNextIn[1]==0) || (zNextIn[0]==0 && zW[nWord]==0) diff --git a/ext/misc/fuzzer.c b/ext/misc/fuzzer.c index fe41cda8c2..dc03161aaf 100644 --- a/ext/misc/fuzzer.c +++ b/ext/misc/fuzzer.c @@ -342,7 +342,8 @@ static int fuzzerLoadOneRule( rc = SQLITE_NOMEM; }else{ memset(pRule, 0, sizeof(*pRule)); - pRule->zFrom = &pRule->zTo[nTo+1]; + pRule->zFrom = pRule->zTo; + pRule->zFrom += nTo + 1; pRule->nFrom = nFrom; memcpy(pRule->zFrom, zFrom, nFrom+1); memcpy(pRule->zTo, zTo, nTo+1); diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c index 2a26e08391..a6f780584c 100644 --- a/ext/misc/spellfix.c +++ b/ext/misc/spellfix.c @@ -356,7 +356,7 @@ static int substituteCost(char cPrev, char cFrom, char cTo){ static int editdist1(const char *zA, const char *zB, int *pnMatch){ int nA, nB; /* Number of characters in zA[] and zB[] */ int xA, xB; /* Loop counters for zA[] and zB[] */ - char cA, cB; /* Current character of zA and zB */ + char cA = 0, cB; /* Current character of zA and zB */ char cAprev, cBprev; /* Previous character of zA and zB */ char cAnext, cBnext; /* Next character in zA and zB */ int d; /* North-west cost value */ diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 8150538d45..201d3cfff2 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -369,13 +369,12 @@ static int readInt16(u8 *p){ return (p[0]<<8) + p[1]; } static void readCoord(u8 *p, RtreeCoord *pCoord){ - u32 i = ( + pCoord->u = ( (((u32)p[0]) << 24) + (((u32)p[1]) << 16) + (((u32)p[2]) << 8) + (((u32)p[3]) << 0) ); - *(u32 *)pCoord = i; } static i64 readInt64(u8 *p){ return ( @@ -404,7 +403,7 @@ static int writeCoord(u8 *p, RtreeCoord *pCoord){ u32 i; assert( sizeof(RtreeCoord)==4 ); assert( sizeof(u32)==4 ); - i = *(u32 *)pCoord; + i = pCoord->u; p[0] = (i>>24)&0xFF; p[1] = (i>>16)&0xFF; p[2] = (i>> 8)&0xFF; @@ -735,14 +734,13 @@ static void nodeGetCell( RtreeCell *pCell /* OUT: Write the cell contents here */ ){ u8 *pData; - u8 *pEnd; RtreeCoord *pCoord; + int ii; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); - pEnd = pData + pRtree->nDim*8; pCoord = pCell->aCoord; - for(; pDatanDim*2; ii++){ + readCoord(&pData[ii*4], &pCoord[ii]); } } @@ -1182,7 +1180,7 @@ static RtreeSearchPoint *rtreeEnqueue( pNew = pCur->aPoint + i; pNew->rScore = rScore; pNew->iLevel = iLevel; - assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH ); + assert( iLevel<=RTREE_MAX_DEPTH ); while( i>0 ){ RtreeSearchPoint *pParent; j = (i-1)/2; @@ -2806,6 +2804,8 @@ static int rtreeUpdate( rtreeReference(pRtree); assert(nData>=1); + cell.iRowid = 0; /* Used only to suppress a compiler warning */ + /* Constraint handling. A write operation on an r-tree table may return ** SQLITE_CONSTRAINT for two reasons: ** diff --git a/main.mk b/main.mk index bf3204bf25..9a8dc87cb2 100644 --- a/main.mk +++ b/main.mk @@ -112,6 +112,7 @@ SRC = \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/memjournal.c \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ @@ -346,6 +347,7 @@ HDR = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ @@ -626,9 +628,15 @@ test: testfixture$(EXE) sqlite3$(EXE) # threadtest runs a few thread-safety tests that are implemented in C. This # target is invoked by the releasetest.tcl script. # -threadtest3$(EXE): sqlite3.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c - $(TCCX) -O2 sqlite3.o $(TOP)/test/threadtest3.c \ - -o threadtest3$(EXE) $(THREADLIB) +THREADTEST3_SRC = $(TOP)/test/threadtest3.c \ + $(TOP)/test/tt3_checkpoint.c \ + $(TOP)/test/tt3_index.c \ + $(TOP)/test/tt3_vacuum.c \ + $(TOP)/test/tt3_stress.c \ + $(TOP)/test/tt3_lookaside1.c + +threadtest3$(EXE): sqlite3.o $(THREADTEST3_SRC) + $(TCCX) $(TOP)/test/threadtest3.c sqlite3.o -o $@ $(THREADLIB) threadtest: threadtest3$(EXE) ./threadtest3$(EXE) @@ -688,7 +696,7 @@ checksymbols: sqlite3.o # Build the amalgamation-autoconf package. # -dist: sqlite3.c +amalgamation-tarball: sqlite3.c TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh diff --git a/manifest b/manifest index 96dcf87dd3..e60c8e7d07 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Update\sthis\sbranch\swith\slatest\strunk\schanges. -D 2014-12-08T07:50:31.222 +C Merge\sin\sall\schanges\sfrom\strunk. +D 2015-01-28T12:00:40.078 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in 6c4f961fa91d0b4fa121946a19f9e5eac2f2f809 +F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 -F Makefile.msc 10720782f88648bf2b5dcedf4c1524b067d43e47 -F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0 -F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8 +F Makefile.msc 2b1cb8881bdefcb0a8ed41c34c81cfa630374222 +F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858 +F README.md d58e3bebc0a4145e0f2a87994015fdb575a8e866 F VERSION d846487aff892625eb8e75960234e7285f0462fe F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811 @@ -15,7 +15,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903 F autoconf/Makefile.am 8fc2972d92769cf20ab8e4a73ea901b84d69bf44 F autoconf/README 14458f1046c118efa721aadec5f227e876d3cd38 -F autoconf/README.first 47dd53221023b18c836ab00dba6e00bd86132453 +F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 F autoconf/config.guess 94cc57e2a3fdb9c235b362ace86d77e89d188cad x F autoconf/config.sub 1efb390a8fb4bfafd74783a15a8fb5311c84300e x F autoconf/configure.ac ba3e99ba1a8171d0682b68443517088fc5d6f13a @@ -26,7 +26,7 @@ F autoconf/missing d7c9981a81af13370d4ed152b24c0a82b7028585 x F autoconf/tea/Makefile.in d55bcc63832caf0309c2ff80358756116618cfca F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873 F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43 -F autoconf/tea/configure.in 93d43c79e936fb16556e22498177d7e8571efa04 +F autoconf/tea/configure.ac 93d43c79e936fb16556e22498177d7e8571efa04 w autoconf/tea/configure.in F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523 F autoconf/tea/pkgIndex.tcl.in 3ef61715cf1c7bdcff56947ffadb26bc991ca39d @@ -36,10 +36,10 @@ F autoconf/tea/win/makefile.vc f89d0184d0eee5f7e356ea407964dcd139939928 F autoconf/tea/win/nmakehlp.c 2070e086f39866b353a482d3a14dedaf26196506 F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 -F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de +F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 4343c810cc772571210af75d1a8f7c2eb711d75a x -F configure.ac 4cf9f60785143fa141b10962ccc885d973792e9a +F configure b2882796ddebd33ac4e9d4ccf5c5ca11888fc30e x +F configure.ac 6a8d145aea6d81f0b90013340780e43ed74fd5f4 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 @@ -78,25 +78,25 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 8b6cceb3e0be22da26d83a3cec0e0e337e6b8ec6 +F ext/fts3/fts3.c 3b2f792afc04d01d387455932428c8f9ae861cc5 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h 53d4eca1fb23eab00681fb028fb82eb5705c1e21 +F ext/fts3/fts3Int.h 394858c12a17740f7a1f6bd372c4606d4425a8d1 F ext/fts3/fts3_aux.c 5c211e17a64885faeb16b9ba7772f9d5445c2365 F ext/fts3/fts3_expr.c 40123785eaa3ebd4c45c9b23407cc44ac0c49905 F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60 F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5 F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009 -F ext/fts3/fts3_snippet.c 51beb5c1498176fd9caccaf1c75b55cb803a985a +F ext/fts3/fts3_snippet.c f16ef6425f92339a8fecc87d9aaf2b12355c78e4 F ext/fts3/fts3_term.c a521f75132f9a495bdca1bdd45949b3191c52763 F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038 -F ext/fts3/fts3_tokenize_vtab.c 011170fe9eba5ff062f1a31d3188e00267716706 +F ext/fts3/fts3_tokenize_vtab.c becc661223db7898b213f9e8a23d75bac02408c9 F ext/fts3/fts3_tokenizer.c bbdc731bc91338050675c6d1da9ab82147391e16 F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3 F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004 F ext/fts3/fts3_unicode.c a93f5edc0aff44ef8b06d7cb55b52026541ca145 F ext/fts3/fts3_unicode2.c c3d01968d497bd7001e7dc774ba75b372738c057 -F ext/fts3/fts3_write.c 8260388626516a7005d06a9dce94f9e55c6c2a41 +F ext/fts3/fts3_write.c 9b3a32cbecf40a1f41cb08c00df8c066c23c7a25 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/tool/fts3view.c 3986531f2fc0ceca0c89c31ec7d0589b6adb19d6 @@ -106,19 +106,19 @@ F ext/fts3/unicode/mkunicode.tcl a2567f9d6ad6779879a2e394c120ad8718557e65 F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 -F ext/misc/amatch.c 678056a4bfcd83c4e82dea81d37543cd1d6dbee1 +F ext/misc/amatch.c 27b9b601fb1453084e18a3432ea0240d7af8decb F ext/misc/closure.c 636024302cde41b2bf0c542f81c40c624cfb7012 F ext/misc/compress.c 76e45655f4046e756064ab10c62e18f2eb846b9f F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f -F ext/misc/fuzzer.c 136533c53cfce0957f0b48fa11dba27e21c5c01d +F ext/misc/fuzzer.c e3e18f47252c151b5553d7e806f38e757d37c4cc F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 -F ext/misc/spellfix.c 56739fab8c2ed6a9e2dac5592a88d281a999c43b +F ext/misc/spellfix.c 25810dda37fc904b0772a13efd8ca072fb09e355 F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e @@ -139,7 +139,7 @@ F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b F ext/ota/sqlite3ota.c 84cab0f965144772068ec0183252ae5e5278f0be F ext/ota/sqlite3ota.h ce378c0c503f625611713133f9c79704ea4ee7a4 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b +F ext/rtree/rtree.c 14e6239434d4e3f65d3e90320713f26aa24e167f F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -167,7 +167,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 4b47576110101e349ebe152008ad7e87dd6764b3 +F main.mk 9d3b0b164e5e1eda8af9ac60520c315699dccbc3 F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 @@ -175,33 +175,33 @@ F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 F mptest/crash01.test cce8e306d8596d5a2e497e27112dae1f6e5e3538 F mptest/crash02.subtest f4ef05adcd15d60e5d2bd654204f2c008b519df8 -F mptest/mptest.c 499a74af4be293b7c1c7c3d40f332b67227dd739 +F mptest/mptest.c 24c5f72415df2eab7088ef8c9f99f163aed590c8 F mptest/multiwrite01.test 499ad0310da8dff8e8f98d2e272fc2a8aa741b2e F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c ba266a779bc7ce10e52e59e7d3dc79fa342e8fdb -F src/analyze.c 7a2986e6ea8247e5f21aca3d0b584598f58d84fe -F src/attach.c f4e94df2d1826feda65eb0939f7f6f5f923a0ad9 +F src/analyze.c 91540f835163d5369ccbae78e2e6c74d0dd53c1d +F src/attach.c 7f6b3fafa2290b407e4a94dcf1afda7ec0fe394b F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c 7ddee9c7d505e07e959a575b18498f17c71e53ea F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5 -F src/btree.c 7071995e9ab92173f43e9d1b8560a8db64a31e9a -F src/btree.h e31a3a3ebdedb1caf9bda3ad5dbab3db9b780f6e -F src/btreeInt.h 3363e18fd76f69a27a870b25221b2345b3fd4d21 -F src/build.c 67bb05b1077e0cdaccb2e36bfcbe7a5df9ed31e8 +F src/btree.c 73cfa1752138438088a20f17f7bc501ba3b999b4 +F src/btree.h 94277c1d30c0b75705974bcc8b0c05e79c03d474 +F src/btreeInt.h a3d0ae1d511365e1a2b76ad10960dbe55c286f34 +F src/build.c f5cfd7b32216f695b995bbc7c1a395f6d451d11f F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 -F src/complete.c c4ba6e0626bb94bc77a0861735f3382fcf7cc818 -F src/ctime.c df19848891c8a553c80e6f5a035e768280952d1a -F src/date.c 93594514aae68de117ca4a2a0d6cc63eddf26744 -F src/delete.c 2d2c4ff24bda5d28000d0aeb05960ee2883a2d3a -F src/expr.c 00da3072f362b06f39ce4052baa1d4ce2bb36d1c +F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463 +F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887 +F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac +F src/delete.c e68b70ac41dcf6e92a813d860fa984fcd9aec042 +F src/expr.c abe930897ccafae3819fd2855cbc1b00c262fd12 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c da985ae673efef2c712caef825a5d2edb087ead7 +F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12 F src/func.c 6d3c4ebd72aa7923ce9b110a7dc15f9b8c548430 -F src/global.c 6ded36dda9466fc1c9a3c5492ded81d79bf3977d +F src/global.c 12561d70a1b25f67b21154622bb1723426724f75 F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5 F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 @@ -209,65 +209,66 @@ F src/insert.c fef86ab8218cf0d926db93280b9eb5b583981353 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770 -F src/loadext.c de741e66e5ddc1598d904d7289239696e40ed994 -F src/main.c eac81ee5cb0f94b496c15f20fcbabe4530b9a8c1 +F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660 +F src/main.c 296f12a2a3f5f97c814c9ca02772dbfc3d4c7275 F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 -F src/mem1.c faf615aafd8be74a71494dfa027c113ea5c6615f +F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987 F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 F src/mem5.c 61eeb90134f9a5be6c2e68d8daae7628b25953fb F src/memjournal.c 3eb2c0b51adbd869cb6a44780323f05fa904dc85 +F src/msvc.h e78002098966e39b2fd9915bd70b7bc3ec8398b7 F src/mutex.c 19bf9acba69ca2f367c3761080f8a9f0cf4670a8 F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85 F src/mutex_noop.c f3f09fd7a2eb4287cfc799753ffc30380e7b71a1 F src/mutex_unix.c 551e2f25f0fa0ee8fd7a43f50fc3d8be00e95dde -F src/mutex_w32.c 06bfff9a3a83b53389a51a967643db3967032e1e +F src/mutex_w32.c df48fe07562a45c5c927c45b8d5873a27f98bbb0 F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7 F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8 F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa -F src/os_unix.c fb587121840f690101336879adfa6d0b2cd0e8c7 -F src/os_win.c a9e500dd963fb1f67d7860e58b5772abe6123862 -F src/os_win.h 09e751b20bbc107ffbd46e13555dc73576d88e21 -F src/pager.c 47f13c194a980ed55dd4825f286d40d49c4a7093 -F src/pager.h c6157af66a9999797629968921133f67716f8f9f -F src/parse.y 5dfead8aed90cb0c7c1115898ee2266804daff45 -F src/pcache.c ace1b67632deeaa84859b4c16c27711dfb7db3d4 +F src/os_unix.c aefeaf915aaef9f81aa2645e0d5d06fa1bd83beb +F src/os_win.c 8223e7db5b7c4a81d8b161098ac3959400434cdb +F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca +F src/pager.c 90b164ac8fefed940cd50fad6938cd18b55af8f3 +F src/pager.h 19d83e2782fe978976cb1acf474d09d9a6124ac3 +F src/parse.y c5d0d964f9ac023e8154cad512e54b0b6058e086 +F src/pcache.c d210cf90d04365a74f85d21374dded65af67b0cb F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8 -F src/pcache1.c facbdd3ecc09c8f750089d941305694301328e98 -F src/pragma.c 294c31d79dfcb6f9cea49528b19e5f8b25e3d5ec -F src/prepare.c b7b7bf020bd4c962f7c8aed5a3c542c7dfe9f9c7 -F src/printf.c 9e75a6a0b55bf61cfff7d7e19d89834a1b938236 +F src/pcache1.c 1e77432b40b7d3288327d9cdf399dcdfd2b6d3bf +F src/pragma.c 92eba8211c341cbaee420dc1b91087096199befa +F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9 +F src/printf.c 05edc41450d0eb2c05ef7db113bf32742ae65325 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c f6c46d3434439ab2084618d603e6d6dbeb0d6ada F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e -F src/select.c f377fb8a5c73c10678ea74f3400f7913943e3d75 -F src/shell.c 45d9c9bd7cde07845af957f2d849933b990773cf -F src/sqlite.h.in 8f704473c8301f3c9cc044d10020bb3d5955dfc3 +F src/select.c 1f2087523007c42900ffcbdeaef06a23ad9329fc +F src/shell.c efd35900484377d2159189968c3445afefee3e41 +F src/sqlite.h.in a16fe2a826ee58e7cb0cd19bc68d33ba29ac941c F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d -F src/sqliteInt.h 9d7b1d5adfcc026971957d440b796f2b26b82d0c +F src/sqliteInt.h 25b73f7edb325b827522f7d9c3273f7c7d6d4cd6 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 81712116e826b0089bb221b018929536b2b5406f -F src/table.c f142bba7903e93ca8d113a5b8877a108ad1a27dc -F src/tclsqlite.c 8cf7d53aa1e1393b79457e4d49a29c18fa8403bd -F src/test1.c fed17ded5498378fea274f2de0fa7b0b89f855f7 -F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712 -F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c -F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df +F src/table.c e7a09215315a978057fb42c640f890160dbcc45e +F src/tclsqlite.c b321464aba1fff1ed9317ebc82a1a94887f97af8 +F src/test1.c bc1e8e0ea360ebd4be247f041432130f9d480c54 +F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d +F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622 +F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e F src/test5.c 5a34feec76d9b3a86aab30fd4f6cc9c48cbab4c1 F src/test6.c 41cacf3b0dd180823919bf9e1fbab287c9266723 -F src/test7.c 72b732baa5642f795655ba1126ea032af46ecfd2 -F src/test8.c 54ccd7b1df5062f0ecbf50a8f7b618f8b1f13b20 +F src/test7.c 9c89a4f1ed6bb13af0ed805b8d782bd83fcd57e3 +F src/test8.c 610e3d523018ca63b08081795e76794a2121ec38 F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e F src/test_blob.c 1f2e3e25255b731c4fcf15ee7990d06347cb6c09 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f -F src/test_config.c 7d50e35f5e94235863c9bac448f63b0d141119e5 +F src/test_config.c a55a18bbbb117eab92e4343f7ee753b25e0aee49 F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f @@ -276,50 +277,50 @@ F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32 F src/test_intarray.c 6c610a21ab8edde85a3a2c7f2b069244ecf4d834 F src/test_intarray.h 9dc57417fb65bc7835cc18548852cc08cc062202 -F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64 +F src/test_journal.c 5360fbe1d1e4416ca36290562fd5a2e3f70f32aa F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4 -F src/test_malloc.c ba34143f941a9d74b30bbffc8818389bb73a1ca2 -F src/test_multiplex.c caadb62cc777268b4f8fb94d5b27b80156c8f7c0 +F src/test_malloc.c b9495384e74923aefde8311de974bf9b0f5ba570 +F src/test_multiplex.c 72c0ad1e97af3d6d19975bbd81813072b40c7290 F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3 F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25 F src/test_osinst.c 3d0340bc31a9f3d8a3547e0272373e80f78dde25 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 -F src/test_quota.c 65f6348fec0f2b3020c907247fb47556b214abb9 +F src/test_quota.c 180813f43683be5725458fc1ff13ac455d8e722d F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d F src/test_rtree.c fdd8d29ca5165c7857987a2ba263fac5c69e231f F src/test_schema.c 2bdba21b82f601da69793e1f1d11bf481a79b091 F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe -F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e +F src/test_sqllog.c b690c12933f50ff46491e0d56a251f84ae16e914 F src/test_stat.c 9898687a6c2beca733b0dd6fe19163d987826d31 F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd F src/test_syscall.c 2e21ca7f7dc54a028f1967b63f1e76155c356f9b F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa -F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb -F src/test_vfs.c f84075a388527892ff184988f43b69ce69b8083c +F src/test_thread.c af391ec03d23486dffbcc250b7e58e073f172af9 +F src/test_vfs.c 5a14c63da9579ba148138c1fb233100f2eb58ebb F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 -F src/threads.c 6de09362b657f19ba83e5fa521ee715787ce9fee -F src/tokenize.c cc9016e5007fc5e76789079616d2f26741bcc689 +F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481 +F src/tokenize.c e00458c9938072b0ea711c850b8dcf4ddcb5fe18 F src/trigger.c 6dcdf46a21acf4d4e011c809b2c971e63f797a1a F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13 F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c -F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73 +F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e F src/vacuum.c 9b30ec729337dd012ed88d4c292922c8ef9cf00c -F src/vdbe.c 1a9e671c9cfc259e4d2affc71f7df4a4c00a842c +F src/vdbe.c ddfc977981cd6324668aa6b114045eb1c677421a F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3 F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78 -F src/vdbeapi.c 07acb615d1e4170e71fc1b0d087f3c53a1ad8e83 -F src/vdbeaux.c 6f7f39c3fcf0f5923758df8561bb5d843908a553 +F src/vdbeapi.c 4bc511a46b9839392ae0e90844a71dc96d9dbd71 +F src/vdbeaux.c 97911edb61074b871ec4aa2d6bb779071643dee5 F src/vdbeblob.c 317c71482ed73b0966db2d1c4e20839be3e9fe79 F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f -F src/vdbesort.c 42c166f7ca78cb643c7f4e4bdfa83c59d363d1a6 +F src/vdbesort.c 6d64c5448b64851b99931ede980addc3af70d5e2 F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010 F src/vtab.c c08ec66f45919eaa726bf88aa53eb08379d607f9 -F src/wal.c 632d9afe19e11cc49a8b74ff52ec2a415568b958 +F src/wal.c 0d9591fdec673f8402cc604b81dfeec4a150b3d0 F src/wal.h 0d3ba0c3f1b4c25796cb213568a84b9f9063f465 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c e914fdb9159bb36af4a673193bbda08aaf9e5a73 +F src/where.c d46de821bc604a4fd36fa3928c086950e91aafb1 F src/whereInt.h d3633e9b592103241b74b0ec76185f3e5b8b62e0 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -381,7 +382,7 @@ F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747 -F test/bigsort.test 835478d0ce83bd1e5b05c90571dedd9871a09196 +F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2 F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0 F test/bitvec.test 75894a880520164d73b1305c1c3f96882615e142 @@ -419,7 +420,7 @@ F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868 F test/collate8.test df26649cfcbddf109c04122b340301616d3a88f6 F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 -F test/colmeta.test 087c42997754b8c648819832241daf724f813322 +F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8 F test/conflict2.test 0d3af4fb534fa1bd020c79960bb56e4d52655f09 @@ -493,7 +494,9 @@ F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52 F test/e_uri.test 5ae33760fb2039c61aa2d90886f1664664173585 F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9 F test/e_wal.test 0967f0b8f1dfda871dc7b9b5574198f1f4f7d69a -F test/e_walckpt.test de5a8d86c5b95569309c6da796dbea870c22e003 +F test/e_walauto.test ca70cf75c07a6cb1874ced101dd426da76625649 +F test/e_walckpt.test 65e29b6631e51f210f83e4ff11571e647ba93608 +F test/e_walhook.test da3ea8b3483d1af72190337bda50155a91a4b664 F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473 F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40 @@ -514,9 +517,10 @@ F test/fkey1.test e1d1fa84cde579185ea01358436839703e415a5b F test/fkey2.test 1db212cda86b0d3ce72714001f7b6381c321341c F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49 F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d -F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d +F test/fkey5.test 488601fbda8350619b3029487e56830447056fd2 F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48 F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13 +F test/fkey8.test 8f08203458321e6c19a263829de4cfc936274ab0 F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c @@ -604,7 +608,7 @@ F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce F test/fts3query.test 4fefd43ff24993bc2c9b2778f2bec0cc7629e7ed F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0 F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e -F test/fts3snippet.test d524af6bcef4714e059ef559113dbdc924cd33d1 +F test/fts3snippet.test 03c2f3be7d3b7c8bb105ed237f204833392bd57f F test/fts3sort.test ed34c716a11cc2009a35210e84ad5f9c102362ca F test/fts3tok1.test c551043de056b0b1582a54e878991f57bad074bc F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d @@ -631,7 +635,7 @@ F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f F test/func5.test cdd224400bc3e48d891827cc913a57051a426fa4 F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74 F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1 -F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 +F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test efd384b896c647b61a2c1848ba70d42aad60a7b3 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 @@ -711,7 +715,7 @@ F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5 F test/lock7.test 49f1eaff1cdc491cc5dee3669f3c671d9f172431 F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95 F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2 -F test/main.test 39c4bb8a157f57298ed1659d6df89d9f35aaf2c8 +F test/main.test 16131264ea0c2b93b95201f0c92958e85f2ba11a F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 F test/malloc.test 96939d2d1a6f39667bbebe5bc27c6525f2ab614e F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a @@ -732,20 +736,20 @@ F test/mallocG.test 0ff91b65c50bdaba680fb75d87fe4ad35bb7934f F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6 F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e -F test/mallocK.test 3cff7c0f64735f6883bacdd294e45a6ed5714817 +F test/mallocK.test 223cc80c870c80d4a9c2014e94133efdf0123f82 F test/mallocL.test 252ddc7eb4fbf75364eab17b938816085ff1fc17 F test/malloc_common.tcl 3663f9001ce3e29bbaa9677ffe15cd468e3ec7e3 F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f F test/memdb.test fcb5297b321b562084fc79d64d5a12a1cd2b639b F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 -F test/memsubsys1.test bf270964ab83bc2da5927960f78304a866fb9a9d +F test/memsubsys1.test 690d142525a7d31efb638b0d232e25fac3afeb1a F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9 F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f -F test/misc1.test 1201a037c24f982cc0e956cdaa34fcaf6439c417 +F test/misc1.test 4864f2834b203cad7f688df8a5f725e4bab08029 F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 @@ -767,7 +771,7 @@ F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a F test/nolock.test 0540dd96f39b8876e3ffdd8814fad0ea425efeee F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf -F test/notify2.test ce23eb522c9e1fff6443f96376fe67872202061c +F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161 F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934 F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62 F test/null.test a8b09b8ed87852742343b33441a9240022108993 @@ -780,7 +784,8 @@ F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4 F test/orderby5.test 8f08a54836d21fb7c70245360751aedd1c2286fb F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859 F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da -F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 +F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd +F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7 F test/ota.test 3a8d97cbf8f7210dc6a638797c4e4cd674036927 F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799 F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa @@ -794,10 +799,11 @@ F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6 F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 -F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54 -F test/permutations.test 7828a776c70fccf83d2e35d0e1efc191b3e0c646 -F test/pragma.test 49ac8a73c0daa574824538fed28727d1259fe735 +F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff +F test/permutations.test 1c2fd1e06f1000c93400bb49df4fff97bccff9e6 +F test/pragma.test aa16dedfe01c02c8895169012f7dfde9c163f0d5 F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13 +F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 F test/printf2.test b4acd4bf8734243257f01ddefa17c4fb090acc8a F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d @@ -813,8 +819,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rdonly.test dd30a4858d8e0fbad2304c2bd74a33d4df36412a F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 -F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a -F test/releasetest.tcl a4279c890698584feb2ffc86735857a4e4474180 +F test/releasetest.tcl b290782d0697b4e83d671da192cd9a7f71e2f6c1 F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14 @@ -853,6 +858,7 @@ F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977 F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394 F test/selectE.test fc02a1eb04c8eb537091482644b7d778ae8759b7 F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3 +F test/selectG.test e8600e379589e85e9fefd2fe4d44a4cdd63f6982 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879 @@ -866,11 +872,11 @@ 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 ab6025d941f9c84c5b83412c6b4d8b57f78dfa3a +F test/shell1.test cdeb849acc2c37aada70d084564b0cc0a2c7df08 F test/shell2.test 12b8bf901b0e3a8ac58cf5c0c63a0a388d4d1862 F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29 F test/shell4.test 8a9c08976291e6c6c808b4d718f4a8b299f339f5 -F test/shell5.test 15a419cc1df21c892ed64f5596ae7a501f2816f2 +F test/shell5.test 81aba4793fa7441b1300daae1aec4f7e4b5741c1 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868 @@ -882,12 +888,12 @@ F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2 F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24 -F test/sort.test c4400e7533748f6bd7413851ff148645e82b9e2d -F test/sort2.test 84a92eebf697feee9c6697746918c7d67373eea1 +F test/sort.test 3f492e5b7be1d3f756728d2ff6edf4f6091e84cb +F test/sort2.test 37afbc03f5559f2eb0f18940b55d38dfbb5172ac F test/sort3.test 6178ade30810ac9166fcdf14b7065e49c0f534e2 -F test/sort4.test 6c37d85f7cd28d50cce222fcab84ccd771e105cb +F test/sort4.test d5e8903194ae551551349ce25dc8d0b40ca2b9c3 F test/sort5.test a448240a42b49239edc00f85d6d7ac7a1b261e1f -F test/sortfault.test b8e35177f97438b930ee87c9419ca2599e8073e1 +F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66 F test/speed1.test f2974a91d79f58507ada01864c0e323093065452 F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8 @@ -927,7 +933,8 @@ F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46 F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9 -F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef +F test/threadtest3.c 9ab4b168681c3a6f70f6c833ba08e0d48dd4af9b +F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925 F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 F test/tkt-2a5629202f.test 0521bd25658428baa26665aa53ffed9367d33af2 @@ -1090,7 +1097,11 @@ F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe F test/triggerC.test a68980c5955d62ee24be6f97129d824f199f9a4c F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650 F test/triggerE.test 355e9c5cbaed5cd039a60baad1fb2197caeb8e52 -F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af +F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1 +F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a +F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9 +F test/tt3_stress.c c57d804716165811d979d4a719e05baccd79277f +F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776 F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a @@ -1131,7 +1142,7 @@ F test/wal.test 885f32b2b390b30b4aa3dbb0e568f8f78d40f5cc F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0 F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c -F test/wal5.test 174cc1512e304a7dfa28ac30527e28ea02fc37df +F test/wal5.test 11b8658dd4d5448f4604124bebd9b68be5bc3e66 F test/wal6.test 527581f5527bf9c24394991e2be83000aace5f9e F test/wal64k.test 163655ecd2cb8afef4737cac2a40fdd2eeaf20b8 F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd @@ -1186,6 +1197,7 @@ F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 F test/without_rowid3.test 1081aabf60a1e1123b7f9a8f6ae19954351843b0 F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a F test/without_rowid5.test 61256715b686359df48ca1742db50cc7e3e7b862 +F test/without_rowid6.test deddb78ef539c355bddec00cdfaea6c56efd8b3f F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac @@ -1199,18 +1211,18 @@ F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439 F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4 F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce -F tool/lemon.c 3ff0fec22f92dfb54e62eeb48772eddffdbeb0d6 +F tool/lemon.c 1864c4fe4a72b1bb28f1792b60504804fe82c5d2 F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6 -F tool/mkautoconfamal.sh 5dc5010e2e748a9e1bba67baca5956a2c2deda7b +F tool/mkautoconfamal.sh d1a2da0e15b2ed33d60af35c7e9d483f13a8eb9f F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e -F tool/mkpragmatab.tcl 7c9f48bfe61ba0e4018868bf34b2450026c24ae1 +F tool/mkpragmatab.tcl b80cb9d1faf69e7757a6a66f7188c0f84d06ee8f F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 -F tool/mksqlite3c-noext.tcl 88a1e3b0c769773fb7a9ebb363ffc603a4ac21d8 -F tool/mksqlite3c.tcl e72c0c97fe1a105fa9616483e652949be2199fe6 +F tool/mksqlite3c-noext.tcl 9ef48e1748dce7b844f67e2450ff9dfeb0fb4ab5 +F tool/mksqlite3c.tcl cfde806851c413db7689b9cb74a4eeb92539c601 F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12 -F tool/mksqlite3internalh.tcl b6514145a7d5321b47e64e19b8116cc44f973eb1 +F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mkvsix.tcl 52a4c613707ac34ae9c226e5ccc69cb948556105 F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091 F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97 @@ -1241,7 +1253,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 088a41eb8c18886a260cf53fa0cca3bd1958dc05 6aeece19a235344be2537e66a3fe08b1febfb5a0 -R 22c55b7623330ec4e5635caf9d27c83c -U dan -Z 93c0d69763bbc98aa3e78f69481d4c3f +P 69a312ad3fe5b39bc394b9ce958cb63d734518c7 e7d2ec048c88237c124fbe598f8f7e950d43d90f +R 8ac69e11eda6b72259b7582e363c58d6 +U drh +Z a99a35d018da82adaec9a86ad728c987 diff --git a/manifest.uuid b/manifest.uuid index 3420b8955c..2bbf8096d1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -69a312ad3fe5b39bc394b9ce958cb63d734518c7 \ No newline at end of file +17c69be80542c5f84e21d60df3edc49422b087d9 \ No newline at end of file diff --git a/mptest/mptest.c b/mptest/mptest.c index 059ae102fa..7b56b61902 100644 --- a/mptest/mptest.c +++ b/mptest/mptest.c @@ -1395,7 +1395,7 @@ int main(int argc, char **argv){ maybeClose(g.pLog); maybeClose(g.pErrLog); if( iClient==0 ){ - printf("Summary: %d errors in %d tests\n", g.nError, g.nTest); + printf("Summary: %d errors out of %d tests\n", g.nError, g.nTest); } return g.nError>0; } diff --git a/src/analyze.c b/src/analyze.c index e483807116..fec2bdb39d 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -448,7 +448,7 @@ static void statInit( p->mxSample = mxSample; p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); p->current.anLt = &p->current.anEq[nColUp]; - p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565; + p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); /* Set up the Stat4Accum.a[] and aBest[] arrays */ p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; diff --git a/src/attach.c b/src/attach.c index cf52bb24b1..de8742938b 100644 --- a/src/attach.c +++ b/src/attach.c @@ -150,6 +150,7 @@ static void attachFunc( "attached databases must use the same text encoding as main database"); rc = SQLITE_ERROR; } + sqlite3BtreeEnter(aNew->pBt); pPager = sqlite3BtreePager(aNew->pBt); sqlite3PagerLockingMode(pPager, db->dfltLockMode); sqlite3BtreeSecureDelete(aNew->pBt, @@ -157,6 +158,7 @@ static void attachFunc( #ifndef SQLITE_OMIT_PAGER_PRAGMAS sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK)); #endif + sqlite3BtreeLeave(aNew->pBt); } aNew->safety_level = 3; aNew->zName = sqlite3DbStrDup(db, zName); diff --git a/src/btree.c b/src/btree.c index 5942085147..550629d616 100644 --- a/src/btree.c +++ b/src/btree.c @@ -3551,6 +3551,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ sqlite3BtreeLeave(p); return rc; } + p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ pBt->inTransaction = TRANS_READ; btreeClearHasContent(pBt); } @@ -3914,7 +3915,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){ releasePage(pCur->apPage[i]); } unlockBtreeIfUnused(pBt); - sqlite3DbFree(pBtree->db, pCur->aOverflow); + sqlite3_free(pCur->aOverflow); /* sqlite3_free(pCur); */ sqlite3BtreeLeave(pBtree); } @@ -4209,6 +4210,7 @@ static int accessPayload( offset -= pCur->info.nLocal; } + if( rc==SQLITE_OK && amt>0 ){ const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ Pgno nextPage; @@ -4226,8 +4228,8 @@ static int accessPayload( if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; if( nOvfl>pCur->nOvflAlloc ){ - Pgno *aNew = (Pgno*)sqlite3DbRealloc( - pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno) + Pgno *aNew = (Pgno*)sqlite3Realloc( + pCur->aOverflow, nOvfl*2*sizeof(Pgno) ); if( aNew==0 ){ rc = SQLITE_NOMEM; @@ -4274,6 +4276,7 @@ static int accessPayload( */ assert( eOp!=2 ); assert( pCur->curFlags & BTCF_ValidOvfl ); + assert( pCur->pBtree->db==pBt->db ); if( pCur->aOverflow[iIdx+1] ){ nextPage = pCur->aOverflow[iIdx+1]; }else{ @@ -6786,7 +6789,7 @@ static int balance_nonroot( /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer ** that is more than 6 times the database page size. */ - assert( szScratch<=6*pBt->pageSize ); + assert( szScratch<=6*(int)pBt->pageSize ); apCell = sqlite3ScratchMalloc( szScratch ); if( apCell==0 ){ rc = SQLITE_NOMEM; @@ -6863,8 +6866,8 @@ static int balance_nonroot( /* Do not allow any cells smaller than 4 bytes. If a smaller cell ** does exist, pad it with 0x00 bytes. */ assert( szCell[nCell]==3 ); - assert( apCell[nCell]==&pTemp[iSpace1-3] ); - pTemp[iSpace1++] = 0x00; + assert( apCell[nCell]==&aSpace1[iSpace1-3] ); + aSpace1[iSpace1++] = 0x00; szCell[nCell] = 4; } } @@ -8176,6 +8179,13 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ ** The schema layer numbers meta values differently. At the schema ** layer (and the SetCookie and ReadCookie opcodes) the number of ** free pages is not visible. So Cookie[0] is the same as Meta[1]. +** +** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead +** of reading the value out of the header, it instead loads the "DataVersion" +** from the pager. The BTREE_DATA_VERSION value is not actually stored in the +** database file. It is a number computed by the pager. But its access +** pattern is the same as header meta values, and so it is convenient to +** read it from this routine. */ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ BtShared *pBt = p->pBt; @@ -8186,7 +8196,11 @@ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ assert( pBt->pPage1 ); assert( idx>=0 && idx<=15 ); - *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); + if( idx==BTREE_DATA_VERSION ){ + *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; + }else{ + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); + } /* If auto-vacuum is disabled in this build and this is an auto-vacuum ** database, mark the database as read-only. */ @@ -8277,7 +8291,7 @@ int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){ if( pCur->iPage==0 ){ /* All pages of the b-tree have been visited. Return successfully. */ *pnEntry = nEntry; - return SQLITE_OK; + return moveToRoot(pCur); } moveToParent(pCur); }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); @@ -9133,4 +9147,4 @@ int sqlite3BtreeIsReadonly(Btree *p){ /* ** Return the size of the header added to each page by this module. */ -int sqlite3HeaderSizeBtree(void){ return sizeof(MemPage); } +int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } diff --git a/src/btree.h b/src/btree.h index 281e57dafd..b57d500c77 100644 --- a/src/btree.h +++ b/src/btree.h @@ -19,7 +19,7 @@ /* TODO: This definition is just included so other modules compile. It ** needs to be revisited. */ -#define SQLITE_N_BTREE_META 10 +#define SQLITE_N_BTREE_META 16 /* ** If defined as non-zero, auto-vacuum is enabled by default. Otherwise @@ -134,6 +134,11 @@ int sqlite3BtreeNewDb(Btree *p); ** For example, the free-page-count field is located at byte offset 36 of ** the database file header. The incr-vacuum-flag field is located at ** byte offset 64 (== 36+4*7). +** +** The BTREE_DATA_VERSION value is not really a value stored in the header. +** It is a read-only number computed by the pager. But we merge it with +** the header value access routines since its access pattern is the same. +** Call it a "virtual meta value". */ #define BTREE_FREE_PAGE_COUNT 0 #define BTREE_SCHEMA_VERSION 1 @@ -144,6 +149,7 @@ int sqlite3BtreeNewDb(Btree *p); #define BTREE_USER_VERSION 6 #define BTREE_INCR_VACUUM 7 #define BTREE_APPLICATION_ID 8 +#define BTREE_DATA_VERSION 15 /* A virtual meta-value */ /* ** Values that may be OR'd together to form the second argument of an diff --git a/src/btreeInt.h b/src/btreeInt.h index a28a6a297e..ed4d75ee9f 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -351,6 +351,7 @@ struct Btree { u8 locked; /* True if db currently has pBt locked */ int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ int nBackup; /* Number of backup operations reading this btree */ + u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ Btree *pNext; /* List of other sharable Btrees from the same db */ Btree *pPrev; /* Back pointer of the same list */ #ifndef SQLITE_OMIT_SHARED_CACHE diff --git a/src/build.c b/src/build.c index 0b4affc664..f02989bffe 100644 --- a/src/build.c +++ b/src/build.c @@ -435,7 +435,6 @@ static void freeIndex(sqlite3 *db, Index *p){ #ifndef SQLITE_OMIT_ANALYZE sqlite3DeleteIndexSamples(db, p); #endif - if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo); sqlite3ExprDelete(db, p->pPartIdxWhere); sqlite3DbFree(db, p->zColAff); if( p->isResized ) sqlite3DbFree(db, p->azColl); @@ -1714,6 +1713,19 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ pTab->iPKey = -1; }else{ pPk = sqlite3PrimaryKeyIndex(pTab); + /* + ** Remove all redundant columns from the PRIMARY KEY. For example, change + ** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later + ** code assumes the PRIMARY KEY contains no repeated columns. + */ + for(i=j=1; inKeyCol; i++){ + if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){ + pPk->nColumn--; + }else{ + pPk->aiColumn[j++] = pPk->aiColumn[i]; + } + } + pPk->nKeyCol = j; } pPk->isCovering = 1; assert( pPk!=0 ); @@ -4190,40 +4202,31 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ ** when it has finished using it. */ KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ + int i; + int nCol = pIdx->nColumn; + int nKey = pIdx->nKeyCol; + KeyInfo *pKey; if( pParse->nErr ) return 0; -#ifndef SQLITE_OMIT_SHARED_CACHE - if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){ - sqlite3KeyInfoUnref(pIdx->pKeyInfo); - pIdx->pKeyInfo = 0; + if( pIdx->uniqNotNull ){ + pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); + }else{ + pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); } -#endif - if( pIdx->pKeyInfo==0 ){ - int i; - int nCol = pIdx->nColumn; - int nKey = pIdx->nKeyCol; - KeyInfo *pKey; - if( pIdx->uniqNotNull ){ - pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); - }else{ - pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); + if( pKey ){ + assert( sqlite3KeyInfoIsWriteable(pKey) ); + for(i=0; iazColl[i]; + assert( zColl!=0 ); + pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : + sqlite3LocateCollSeq(pParse, zColl); + pKey->aSortOrder[i] = pIdx->aSortOrder[i]; } - if( pKey ){ - assert( sqlite3KeyInfoIsWriteable(pKey) ); - for(i=0; iazColl[i]; - assert( zColl!=0 ); - pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : - sqlite3LocateCollSeq(pParse, zColl); - pKey->aSortOrder[i] = pIdx->aSortOrder[i]; - } - if( pParse->nErr ){ - sqlite3KeyInfoUnref(pKey); - }else{ - pIdx->pKeyInfo = pKey; - } + if( pParse->nErr ){ + sqlite3KeyInfoUnref(pKey); + pKey = 0; } } - return sqlite3KeyInfoRef(pIdx->pKeyInfo); + return pKey; } #ifndef SQLITE_OMIT_CTE diff --git a/src/complete.c b/src/complete.c index c439cfe181..f7a35cc6f3 100644 --- a/src/complete.c +++ b/src/complete.c @@ -105,13 +105,6 @@ int sqlite3_complete(const char *zSql){ u8 state = 0; /* Current state, using numbers defined in header comment */ u8 token; /* Value of the next token */ -#ifdef SQLITE_ENABLE_API_ARMOR - if( zSql==0 ){ - (void)SQLITE_MISUSE_BKPT; - return 0; - } -#endif - #ifndef SQLITE_OMIT_TRIGGER /* A complex statement machine used to detect the end of a CREATE TRIGGER ** statement. This is the normal case. @@ -141,6 +134,13 @@ int sqlite3_complete(const char *zSql){ }; #endif /* SQLITE_OMIT_TRIGGER */ +#ifdef SQLITE_ENABLE_API_ARMOR + if( zSql==0 ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif + while( *zSql ){ switch( *zSql ){ case ';': { /* A semicolon */ diff --git a/src/ctime.c b/src/ctime.c index 59dc972d8d..4f98ffef61 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -33,91 +33,91 @@ static const char * const azCompileOpt[] = { #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) -#ifdef SQLITE_32BIT_ROWID +#if SQLITE_32BIT_ROWID "32BIT_ROWID", #endif -#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +#if SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif -#ifdef SQLITE_CASE_SENSITIVE_LIKE +#if SQLITE_CASE_SENSITIVE_LIKE "CASE_SENSITIVE_LIKE", #endif -#ifdef SQLITE_CHECK_PAGES +#if SQLITE_CHECK_PAGES "CHECK_PAGES", #endif -#ifdef SQLITE_COVERAGE_TEST +#if SQLITE_COVERAGE_TEST "COVERAGE_TEST", #endif -#ifdef SQLITE_DEBUG +#if SQLITE_DEBUG "DEBUG", #endif -#ifdef SQLITE_DEFAULT_LOCKING_MODE +#if SQLITE_DEFAULT_LOCKING_MODE "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), #endif #if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc) "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), #endif -#ifdef SQLITE_DISABLE_DIRSYNC +#if SQLITE_DISABLE_DIRSYNC "DISABLE_DIRSYNC", #endif -#ifdef SQLITE_DISABLE_LFS +#if SQLITE_DISABLE_LFS "DISABLE_LFS", #endif -#ifdef SQLITE_ENABLE_API_ARMOR +#if SQLITE_ENABLE_API_ARMOR "ENABLE_API_ARMOR", #endif -#ifdef SQLITE_ENABLE_ATOMIC_WRITE +#if SQLITE_ENABLE_ATOMIC_WRITE "ENABLE_ATOMIC_WRITE", #endif -#ifdef SQLITE_ENABLE_CEROD +#if SQLITE_ENABLE_CEROD "ENABLE_CEROD", #endif -#ifdef SQLITE_ENABLE_COLUMN_METADATA +#if SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", #endif -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT +#if SQLITE_ENABLE_EXPENSIVE_ASSERT "ENABLE_EXPENSIVE_ASSERT", #endif -#ifdef SQLITE_ENABLE_FTS1 +#if SQLITE_ENABLE_FTS1 "ENABLE_FTS1", #endif -#ifdef SQLITE_ENABLE_FTS2 +#if SQLITE_ENABLE_FTS2 "ENABLE_FTS2", #endif -#ifdef SQLITE_ENABLE_FTS3 +#if SQLITE_ENABLE_FTS3 "ENABLE_FTS3", #endif -#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS +#if SQLITE_ENABLE_FTS3_PARENTHESIS "ENABLE_FTS3_PARENTHESIS", #endif -#ifdef SQLITE_ENABLE_FTS4 +#if SQLITE_ENABLE_FTS4 "ENABLE_FTS4", #endif -#ifdef SQLITE_ENABLE_ICU +#if SQLITE_ENABLE_ICU "ENABLE_ICU", #endif -#ifdef SQLITE_ENABLE_IOTRACE +#if SQLITE_ENABLE_IOTRACE "ENABLE_IOTRACE", #endif -#ifdef SQLITE_ENABLE_LOAD_EXTENSION +#if SQLITE_ENABLE_LOAD_EXTENSION "ENABLE_LOAD_EXTENSION", #endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_ENABLE_LOCKING_STYLE "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), #endif -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +#if SQLITE_ENABLE_MEMORY_MANAGEMENT "ENABLE_MEMORY_MANAGEMENT", #endif -#ifdef SQLITE_ENABLE_MEMSYS3 +#if SQLITE_ENABLE_MEMSYS3 "ENABLE_MEMSYS3", #endif -#ifdef SQLITE_ENABLE_MEMSYS5 +#if SQLITE_ENABLE_MEMSYS5 "ENABLE_MEMSYS5", #endif -#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK +#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif -#ifdef SQLITE_ENABLE_RTREE +#if SQLITE_ENABLE_RTREE "ENABLE_RTREE", #endif #if defined(SQLITE_ENABLE_STAT4) @@ -125,31 +125,31 @@ static const char * const azCompileOpt[] = { #elif defined(SQLITE_ENABLE_STAT3) "ENABLE_STAT3", #endif -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY +#if SQLITE_ENABLE_UNLOCK_NOTIFY "ENABLE_UNLOCK_NOTIFY", #endif -#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT +#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT "ENABLE_UPDATE_DELETE_LIMIT", #endif -#ifdef SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC "HAS_CODEC", #endif -#ifdef SQLITE_HAVE_ISNAN +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN "HAVE_ISNAN", #endif -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX +#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX "HOMEGROWN_RECURSIVE_MUTEX", #endif -#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS +#if SQLITE_IGNORE_AFP_LOCK_ERRORS "IGNORE_AFP_LOCK_ERRORS", #endif -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS +#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS "IGNORE_FLOCK_LOCK_ERRORS", #endif #ifdef SQLITE_INT64_TYPE "INT64_TYPE", #endif -#ifdef SQLITE_LOCK_TRACE +#if SQLITE_LOCK_TRACE "LOCK_TRACE", #endif #if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc) @@ -158,226 +158,226 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_MAX_SCHEMA_RETRY "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), #endif -#ifdef SQLITE_MEMDEBUG +#if SQLITE_MEMDEBUG "MEMDEBUG", #endif -#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT "MIXED_ENDIAN_64BIT_FLOAT", #endif -#ifdef SQLITE_NO_SYNC +#if SQLITE_NO_SYNC "NO_SYNC", #endif -#ifdef SQLITE_OMIT_ALTERTABLE +#if SQLITE_OMIT_ALTERTABLE "OMIT_ALTERTABLE", #endif -#ifdef SQLITE_OMIT_ANALYZE +#if SQLITE_OMIT_ANALYZE "OMIT_ANALYZE", #endif -#ifdef SQLITE_OMIT_ATTACH +#if SQLITE_OMIT_ATTACH "OMIT_ATTACH", #endif -#ifdef SQLITE_OMIT_AUTHORIZATION +#if SQLITE_OMIT_AUTHORIZATION "OMIT_AUTHORIZATION", #endif -#ifdef SQLITE_OMIT_AUTOINCREMENT +#if SQLITE_OMIT_AUTOINCREMENT "OMIT_AUTOINCREMENT", #endif -#ifdef SQLITE_OMIT_AUTOINIT +#if SQLITE_OMIT_AUTOINIT "OMIT_AUTOINIT", #endif -#ifdef SQLITE_OMIT_AUTOMATIC_INDEX +#if SQLITE_OMIT_AUTOMATIC_INDEX "OMIT_AUTOMATIC_INDEX", #endif -#ifdef SQLITE_OMIT_AUTORESET +#if SQLITE_OMIT_AUTORESET "OMIT_AUTORESET", #endif -#ifdef SQLITE_OMIT_AUTOVACUUM +#if SQLITE_OMIT_AUTOVACUUM "OMIT_AUTOVACUUM", #endif -#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION +#if SQLITE_OMIT_BETWEEN_OPTIMIZATION "OMIT_BETWEEN_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_BLOB_LITERAL +#if SQLITE_OMIT_BLOB_LITERAL "OMIT_BLOB_LITERAL", #endif -#ifdef SQLITE_OMIT_BTREECOUNT +#if SQLITE_OMIT_BTREECOUNT "OMIT_BTREECOUNT", #endif -#ifdef SQLITE_OMIT_BUILTIN_TEST +#if SQLITE_OMIT_BUILTIN_TEST "OMIT_BUILTIN_TEST", #endif -#ifdef SQLITE_OMIT_CAST +#if SQLITE_OMIT_CAST "OMIT_CAST", #endif -#ifdef SQLITE_OMIT_CHECK +#if SQLITE_OMIT_CHECK "OMIT_CHECK", #endif -#ifdef SQLITE_OMIT_COMPLETE +#if SQLITE_OMIT_COMPLETE "OMIT_COMPLETE", #endif -#ifdef SQLITE_OMIT_COMPOUND_SELECT +#if SQLITE_OMIT_COMPOUND_SELECT "OMIT_COMPOUND_SELECT", #endif -#ifdef SQLITE_OMIT_CTE +#if SQLITE_OMIT_CTE "OMIT_CTE", #endif -#ifdef SQLITE_OMIT_DATETIME_FUNCS +#if SQLITE_OMIT_DATETIME_FUNCS "OMIT_DATETIME_FUNCS", #endif -#ifdef SQLITE_OMIT_DECLTYPE +#if SQLITE_OMIT_DECLTYPE "OMIT_DECLTYPE", #endif -#ifdef SQLITE_OMIT_DEPRECATED +#if SQLITE_OMIT_DEPRECATED "OMIT_DEPRECATED", #endif -#ifdef SQLITE_OMIT_DISKIO +#if SQLITE_OMIT_DISKIO "OMIT_DISKIO", #endif -#ifdef SQLITE_OMIT_EXPLAIN +#if SQLITE_OMIT_EXPLAIN "OMIT_EXPLAIN", #endif -#ifdef SQLITE_OMIT_FLAG_PRAGMAS +#if SQLITE_OMIT_FLAG_PRAGMAS "OMIT_FLAG_PRAGMAS", #endif -#ifdef SQLITE_OMIT_FLOATING_POINT +#if SQLITE_OMIT_FLOATING_POINT "OMIT_FLOATING_POINT", #endif -#ifdef SQLITE_OMIT_FOREIGN_KEY +#if SQLITE_OMIT_FOREIGN_KEY "OMIT_FOREIGN_KEY", #endif -#ifdef SQLITE_OMIT_GET_TABLE +#if SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif -#ifdef SQLITE_OMIT_INCRBLOB +#if SQLITE_OMIT_INCRBLOB "OMIT_INCRBLOB", #endif -#ifdef SQLITE_OMIT_INTEGRITY_CHECK +#if SQLITE_OMIT_INTEGRITY_CHECK "OMIT_INTEGRITY_CHECK", #endif -#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION +#if SQLITE_OMIT_LIKE_OPTIMIZATION "OMIT_LIKE_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_LOAD_EXTENSION +#if SQLITE_OMIT_LOAD_EXTENSION "OMIT_LOAD_EXTENSION", #endif -#ifdef SQLITE_OMIT_LOCALTIME +#if SQLITE_OMIT_LOCALTIME "OMIT_LOCALTIME", #endif -#ifdef SQLITE_OMIT_LOOKASIDE +#if SQLITE_OMIT_LOOKASIDE "OMIT_LOOKASIDE", #endif -#ifdef SQLITE_OMIT_MEMORYDB +#if SQLITE_OMIT_MEMORYDB "OMIT_MEMORYDB", #endif -#ifdef SQLITE_OMIT_OR_OPTIMIZATION +#if SQLITE_OMIT_OR_OPTIMIZATION "OMIT_OR_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_PAGER_PRAGMAS +#if SQLITE_OMIT_PAGER_PRAGMAS "OMIT_PAGER_PRAGMAS", #endif -#ifdef SQLITE_OMIT_PRAGMA +#if SQLITE_OMIT_PRAGMA "OMIT_PRAGMA", #endif -#ifdef SQLITE_OMIT_PROGRESS_CALLBACK +#if SQLITE_OMIT_PROGRESS_CALLBACK "OMIT_PROGRESS_CALLBACK", #endif -#ifdef SQLITE_OMIT_QUICKBALANCE +#if SQLITE_OMIT_QUICKBALANCE "OMIT_QUICKBALANCE", #endif -#ifdef SQLITE_OMIT_REINDEX +#if SQLITE_OMIT_REINDEX "OMIT_REINDEX", #endif -#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS +#if SQLITE_OMIT_SCHEMA_PRAGMAS "OMIT_SCHEMA_PRAGMAS", #endif -#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS +#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS "OMIT_SCHEMA_VERSION_PRAGMAS", #endif -#ifdef SQLITE_OMIT_SHARED_CACHE +#if SQLITE_OMIT_SHARED_CACHE "OMIT_SHARED_CACHE", #endif -#ifdef SQLITE_OMIT_SUBQUERY +#if SQLITE_OMIT_SUBQUERY "OMIT_SUBQUERY", #endif -#ifdef SQLITE_OMIT_TCL_VARIABLE +#if SQLITE_OMIT_TCL_VARIABLE "OMIT_TCL_VARIABLE", #endif -#ifdef SQLITE_OMIT_TEMPDB +#if SQLITE_OMIT_TEMPDB "OMIT_TEMPDB", #endif -#ifdef SQLITE_OMIT_TRACE +#if SQLITE_OMIT_TRACE "OMIT_TRACE", #endif -#ifdef SQLITE_OMIT_TRIGGER +#if SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif -#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION +#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_UTF16 +#if SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif -#ifdef SQLITE_OMIT_VACUUM +#if SQLITE_OMIT_VACUUM "OMIT_VACUUM", #endif -#ifdef SQLITE_OMIT_VIEW +#if SQLITE_OMIT_VIEW "OMIT_VIEW", #endif -#ifdef SQLITE_OMIT_VIRTUALTABLE +#if SQLITE_OMIT_VIRTUALTABLE "OMIT_VIRTUALTABLE", #endif -#ifdef SQLITE_OMIT_WAL +#if SQLITE_OMIT_WAL "OMIT_WAL", #endif -#ifdef SQLITE_OMIT_WSD +#if SQLITE_OMIT_WSD "OMIT_WSD", #endif -#ifdef SQLITE_OMIT_XFER_OPT +#if SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif -#ifdef SQLITE_PERFORMANCE_TRACE +#if SQLITE_PERFORMANCE_TRACE "PERFORMANCE_TRACE", #endif -#ifdef SQLITE_PROXY_DEBUG +#if SQLITE_PROXY_DEBUG "PROXY_DEBUG", #endif -#ifdef SQLITE_RTREE_INT_ONLY +#if SQLITE_RTREE_INT_ONLY "RTREE_INT_ONLY", #endif -#ifdef SQLITE_SECURE_DELETE +#if SQLITE_SECURE_DELETE "SECURE_DELETE", #endif -#ifdef SQLITE_SMALL_STACK +#if SQLITE_SMALL_STACK "SMALL_STACK", #endif -#ifdef SQLITE_SOUNDEX +#if SQLITE_SOUNDEX "SOUNDEX", #endif -#ifdef SQLITE_SYSTEM_MALLOC +#if SQLITE_SYSTEM_MALLOC "SYSTEM_MALLOC", #endif -#ifdef SQLITE_TCL +#if SQLITE_TCL "TCL", #endif #if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc) "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), #endif -#ifdef SQLITE_TEST +#if SQLITE_TEST "TEST", #endif #if defined(SQLITE_THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), #endif -#ifdef SQLITE_USE_ALLOCA +#if SQLITE_USE_ALLOCA "USE_ALLOCA", #endif -#ifdef SQLITE_USER_AUTHENTICATION +#if SQLITE_USER_AUTHENTICATION "USER_AUTHENTICATION", #endif -#ifdef SQLITE_WIN32_MALLOC +#if SQLITE_WIN32_MALLOC "WIN32_MALLOC", #endif -#ifdef SQLITE_ZERO_MALLOC +#if SQLITE_ZERO_MALLOC "ZERO_MALLOC" #endif }; @@ -392,7 +392,7 @@ static const char * const azCompileOpt[] = { int sqlite3_compileoption_used(const char *zOptName){ int i, n; -#ifdef SQLITE_ENABLE_API_ARMOR +#if SQLITE_ENABLE_API_ARMOR if( zOptName==0 ){ (void)SQLITE_MISUSE_BKPT; return 0; diff --git a/src/date.c b/src/date.c index 10d9006263..5f3f247ca9 100644 --- a/src/date.c +++ b/src/date.c @@ -412,8 +412,9 @@ static void clearYMD_HMS_TZ(DateTime *p){ ** already, check for an MSVC build environment that provides ** localtime_s(). */ -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ - defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \ + && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#undef HAVE_LOCALTIME_S #define HAVE_LOCALTIME_S 1 #endif @@ -433,8 +434,7 @@ static void clearYMD_HMS_TZ(DateTime *p){ */ static int osLocaltime(time_t *t, struct tm *pTm){ int rc; -#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ - && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S struct tm *pX; #if SQLITE_THREADSAFE>0 sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); @@ -451,7 +451,7 @@ static int osLocaltime(time_t *t, struct tm *pTm){ #ifndef SQLITE_OMIT_BUILTIN_TEST if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; #endif -#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R +#if HAVE_LOCALTIME_R rc = localtime_r(t, pTm)==0; #else rc = localtime_s(pTm, t); @@ -895,8 +895,10 @@ static void strftimeFunc( size_t i,j; char *z; sqlite3 *db; - const char *zFmt = (const char*)sqlite3_value_text(argv[0]); + const char *zFmt; char zBuf[100]; + if( argc==0 ) return; + zFmt = (const char*)sqlite3_value_text(argv[0]); if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; db = sqlite3_context_db_handle(context); for(i=0, n=1; zFmt[i]; i++, n++){ @@ -1090,7 +1092,7 @@ static void currentTimeFunc( iT = sqlite3StmtCurrentTime(context); if( iT<=0 ) return; t = iT/1000 - 10000*(sqlite3_int64)21086676; -#ifdef HAVE_GMTIME_R +#if HAVE_GMTIME_R pTm = gmtime_r(&t, &sNow); #else sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); diff --git a/src/delete.c b/src/delete.c index 07f46e4e10..17f7a87459 100644 --- a/src/delete.c +++ b/src/delete.c @@ -226,8 +226,8 @@ void sqlite3DeleteFrom( WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int iTabCur; /* Cursor number for the table */ - int iDataCur; /* VDBE cursor for the canonical data source */ - int iIdxCur; /* Cursor number of the first index */ + int iDataCur = 0; /* VDBE cursor for the canonical data source */ + int iIdxCur = 0; /* Cursor number of the first index */ int nIdx; /* Number of indices */ sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ diff --git a/src/expr.c b/src/expr.c index 817975ab3a..25bd958ceb 100644 --- a/src/expr.c +++ b/src/expr.c @@ -132,9 +132,9 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } - if( p->pTab!=0 - && (op==TK_AGG_COLUMN || op==TK_COLUMN + if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER) + && p->pTab!=0 ){ /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally ** a TK_COLUMN but was previously evaluated and cached in a register */ @@ -515,7 +515,7 @@ Expr *sqlite3PExpr( const Token *pToken /* Argument token */ ){ Expr *p; - if( op==TK_AND && pLeft && pRight ){ + if( op==TK_AND && pLeft && pRight && pParse->nErr==0 ){ /* Take advantage of short-circuit false optimization for AND */ p = sqlite3ExprAnd(pParse->db, pLeft, pRight); }else{ @@ -2256,7 +2256,8 @@ void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){ int idxLru; struct yColCache *p; - assert( iReg>0 ); /* Register numbers are always positive */ + /* Unless an error has occurred, register numbers are always positive. */ + assert( iReg>0 || pParse->nErr || pParse->db->mallocFailed ); assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */ /* The SQLITE_ColumnCache flag disables the column cache. This is used @@ -4069,10 +4070,11 @@ static int exprSrcCount(Walker *pWalker, Expr *pExpr){ int i; struct SrcCount *p = pWalker->u.pSrcCount; SrcList *pSrc = p->pSrc; - for(i=0; inSrc; i++){ + int nSrc = pSrc ? pSrc->nSrc : 0; + for(i=0; iiTable==pSrc->a[i].iCursor ) break; } - if( inSrc ){ + if( inThis++; }else{ p->nOther++; diff --git a/src/fkey.c b/src/fkey.c index e816bd95da..fa148ba6a3 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -437,7 +437,7 @@ static void fkLookupParent( OE_Abort, 0, P4_STATIC, P5_ConstraintFK); }else{ if( nIncr>0 && pFKey->isDeferred==0 ){ - sqlite3ParseToplevel(pParse)->mayAbort = 1; + sqlite3MayAbort(pParse); } sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); } @@ -509,6 +509,10 @@ static Expr *exprTableColumn( ** code for an SQL UPDATE operation, this function may be called twice - ** once to "delete" the old row and once to "insert" the new row. ** +** Parameter nIncr is passed -1 when inserting a row (as this may decrease +** the number of FK violations in the db) or +1 when deleting one (as this +** may increase the number of FK constraint problems). +** ** The code generated by this function scans through the rows in the child ** table that correspond to the parent table row being deleted or inserted. ** For each child row found, one of the following actions is taken: @@ -625,13 +629,9 @@ static void fkScanChildren( sqlite3ResolveExprNames(&sNameContext, pWhere); /* Create VDBE to loop through the entries in pSrc that match the WHERE - ** clause. If the constraint is not deferred, throw an exception for - ** each row found. Otherwise, for deferred constraints, increment the - ** deferred constraint counter by nIncr for each row selected. */ + ** clause. For each row found, increment either the deferred or immediate + ** foreign key constraint counter. */ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); - if( nIncr>0 && pFKey->isDeferred==0 ){ - sqlite3ParseToplevel(pParse)->mayAbort = 1; - } sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ sqlite3WhereEnd(pWInfo); @@ -810,6 +810,24 @@ static int fkParentIsModified( return 0; } +/* +** Return true if the parser passed as the first argument is being +** used to code a trigger that is really a "SET NULL" action belonging +** to trigger pFKey. +*/ +static int isSetNullAction(Parse *pParse, FKey *pFKey){ + Parse *pTop = sqlite3ParseToplevel(pParse); + if( pTop->pTriggerPrg ){ + Trigger *p = pTop->pTriggerPrg->pTrigger; + if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) + || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) + ){ + return 1; + } + } + return 0; +} + /* ** This function is called when inserting, deleting or updating a row of ** table pTab to generate VDBE code to perform foreign key constraint @@ -862,7 +880,7 @@ void sqlite3FkCheck( int *aiCol; int iCol; int i; - int isIgnore = 0; + int bIgnore = 0; if( aChange && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0 @@ -921,7 +939,7 @@ void sqlite3FkCheck( int rcauth; char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); - isIgnore = (rcauth==SQLITE_IGNORE); + bIgnore = (rcauth==SQLITE_IGNORE); } #endif } @@ -936,12 +954,18 @@ void sqlite3FkCheck( /* A row is being removed from the child table. Search for the parent. ** If the parent does not exist, removing the child row resolves an ** outstanding foreign key constraint violation. */ - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore); } - if( regNew!=0 ){ + if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){ /* A row is being added to the child table. If a parent row cannot - ** be found, adding the child row has violated the FK constraint. */ - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); + ** be found, adding the child row has violated the FK constraint. + ** + ** If this operation is being performed as part of a trigger program + ** that is actually a "SET NULL" action belonging to this very + ** foreign key, then omit this scan altogether. As all child key + ** values are guaranteed to be NULL, it is not possible for adding + ** this row to cause an FK violation. */ + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore); } sqlite3DbFree(db, aiFree); @@ -962,8 +986,8 @@ void sqlite3FkCheck( && !pParse->pToplevel && !pParse->isMultiWrite ){ assert( regOld==0 && regNew!=0 ); - /* Inserting a single row into a parent table cannot cause an immediate - ** foreign key violation. So do nothing in this case. */ + /* Inserting a single row into a parent table cannot cause (or fix) + ** an immediate foreign key violation. So do nothing in this case. */ continue; } @@ -987,13 +1011,28 @@ void sqlite3FkCheck( fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); } if( regOld!=0 ){ - /* If there is a RESTRICT action configured for the current operation - ** on the parent table of this FK, then throw an exception - ** immediately if the FK constraint is violated, even if this is a - ** deferred trigger. That's what RESTRICT means. To defer checking - ** the constraint, the FK should specify NO ACTION (represented - ** using OE_None). NO ACTION is the default. */ + int eAction = pFKey->aAction[aChange!=0]; fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); + /* If this is a deferred FK constraint, or a CASCADE or SET NULL + ** action applies, then any foreign key violations caused by + ** removing the parent key will be rectified by the action trigger. + ** So do not set the "may-abort" flag in this case. + ** + ** Note 1: If the FK is declared "ON UPDATE CASCADE", then the + ** may-abort flag will eventually be set on this statement anyway + ** (when this function is called as part of processing the UPDATE + ** within the action trigger). + ** + ** Note 2: At first glance it may seem like SQLite could simply omit + ** all OP_FkCounter related scans when either CASCADE or SET NULL + ** applies. The trouble starts if the CASCADE or SET NULL action + ** trigger causes other triggers or action rules attached to the + ** child table to fire. In these cases the fk constraint counters + ** might be set incorrectly if any OP_FkCounter related scans are + ** omitted. */ + if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){ + sqlite3MayAbort(pParse); + } } pItem->zName = 0; sqlite3SrcListDelete(db, pSrc); diff --git a/src/global.c b/src/global.c index 4bc8edb3bc..c7043bba48 100644 --- a/src/global.c +++ b/src/global.c @@ -152,6 +152,13 @@ const unsigned char sqlite3CtypeMap[256] = { # define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 #endif +/* The minimum PMA size is set to this value multiplied by the database +** page size in bytes. +*/ +#ifndef SQLITE_SORTER_PMASZ +# define SQLITE_SORTER_PMASZ 250 +#endif + /* ** The following singleton contains the global configuration for ** the SQLite library. @@ -182,6 +189,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* nPage */ 0, /* mxParserStack */ 0, /* sharedCacheEnabled */ + SQLITE_SORTER_PMASZ, /* szPma */ /* All the rest should always be initialized to zero */ 0, /* isInit */ 0, /* inProgress */ diff --git a/src/loadext.c b/src/loadext.c index 2a2afd8654..7b39ff1671 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -34,7 +34,6 @@ # define sqlite3_column_table_name16 0 # define sqlite3_column_origin_name 0 # define sqlite3_column_origin_name16 0 -# define sqlite3_table_column_metadata 0 #endif #ifdef SQLITE_OMIT_AUTHORIZATION diff --git a/src/main.c b/src/main.c index 9027a2a4a0..b83771d3e5 100644 --- a/src/main.c +++ b/src/main.c @@ -62,7 +62,7 @@ int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } ** I/O active are written using this function. These messages ** are intended for debugging activity only. */ -void (*sqlite3IoTrace)(const char*, ...) = 0; +/* not-private */ void (*sqlite3IoTrace)(const char*, ...) = 0; #endif /* @@ -271,6 +271,13 @@ int sqlite3_initialize(void){ ** when this routine is invoked, then this routine is a harmless no-op. */ int sqlite3_shutdown(void){ +#ifdef SQLITE_OMIT_WSD + int rc = sqlite3_wsd_init(4096, 24); + if( rc!=SQLITE_OK ){ + return rc; + } +#endif + if( sqlite3GlobalConfig.isInit ){ #ifdef SQLITE_EXTRA_SHUTDOWN void SQLITE_EXTRA_SHUTDOWN(void); @@ -587,6 +594,11 @@ int sqlite3_config(int op, ...){ } #endif + case SQLITE_CONFIG_PMASZ: { + sqlite3GlobalConfig.szPma = va_arg(ap, unsigned int); + break; + } + default: { rc = SQLITE_ERROR; break; @@ -1032,14 +1044,6 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ for(j=0; jnDb; j++){ struct Db *pDb = &db->aDb[j]; if( pDb->pBt ){ - if( pDb->pSchema ){ - /* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */ - for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){ - Index *pIdx = sqliteHashData(i); - sqlite3KeyInfoUnref(pIdx->pKeyInfo); - pIdx->pKeyInfo = 0; - } - } sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ @@ -1346,7 +1350,7 @@ static int sqliteDefaultBusyCallback( void *ptr, /* Database connection */ int count /* Number of times table has been busy */ ){ -#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP) +#if SQLITE_OS_WIN || HAVE_USLEEP static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = @@ -2194,32 +2198,6 @@ const char *sqlite3_errstr(int rc){ return sqlite3ErrStr(rc); } -/* -** Invalidate all cached KeyInfo objects for database connection "db" -*/ -static void invalidateCachedKeyInfo(sqlite3 *db){ - Db *pDb; /* A single database */ - int iDb; /* The database index number */ - HashElem *k; /* For looping over tables in pDb */ - Table *pTab; /* A table in the database */ - Index *pIdx; /* Each index */ - - for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ - if( pDb->pBt==0 ) continue; - sqlite3BtreeEnter(pDb->pBt); - for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ - pTab = (Table*)sqliteHashData(k); - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){ - sqlite3KeyInfoUnref(pIdx->pKeyInfo); - pIdx->pKeyInfo = 0; - } - } - } - sqlite3BtreeLeave(pDb->pBt); - } -} - /* ** Create a new collating function for database "db". The name is zName ** and the encoding is enc. @@ -2263,7 +2241,6 @@ static int createCollation( return SQLITE_BUSY; } sqlite3ExpirePreparedStatements(db); - invalidateCachedKeyInfo(db); /* If collation sequence pColl was created directly by a call to ** sqlite3_create_collation, and not generated by synthCollSeq(), @@ -2767,6 +2744,9 @@ static int openDatabase( #endif #if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS | SQLITE_ForeignKeys +#endif +#if defined(SQLITE_REVERSE_UNORDERED_SELECTS) + | SQLITE_ReverseOrder #endif ; sqlite3HashInit(&db->aCollSeq); @@ -2815,7 +2795,10 @@ static int openDatabase( sqlite3Error(db, rc); goto opendb_out; } + sqlite3BtreeEnter(db->aDb[0].pBt); db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); + if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db); + sqlite3BtreeLeave(db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); /* The default safety_level for the main database is 'full'; for the temp @@ -2973,7 +2956,7 @@ int sqlite3_open16( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); assert( *ppDb || rc==SQLITE_NOMEM ); if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){ - ENC(*ppDb) = SQLITE_UTF16NATIVE; + SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE; } }else{ rc = SQLITE_NOMEM; @@ -3169,7 +3152,6 @@ void sqlite3_thread_cleanup(void){ ** Return meta information about a specific column of a database table. ** See comment in sqlite3.h (sqlite.h.in) for details. */ -#ifdef SQLITE_ENABLE_COLUMN_METADATA int sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ @@ -3185,7 +3167,7 @@ int sqlite3_table_column_metadata( char *zErrMsg = 0; Table *pTab = 0; Column *pCol = 0; - int iCol; + int iCol = 0; char const *zDataType = 0; char const *zCollSeq = 0; @@ -3209,11 +3191,8 @@ int sqlite3_table_column_metadata( } /* Find the column for which info is requested */ - if( sqlite3IsRowid(zColumnName) ){ - iCol = pTab->iPKey; - if( iCol>=0 ){ - pCol = &pTab->aCol[iCol]; - } + if( zColumnName==0 ){ + /* Query for existance of table only */ }else{ for(iCol=0; iColnCol; iCol++){ pCol = &pTab->aCol[iCol]; @@ -3222,8 +3201,13 @@ int sqlite3_table_column_metadata( } } if( iCol==pTab->nCol ){ - pTab = 0; - goto error_out; + if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){ + iCol = pTab->iPKey; + pCol = iCol>=0 ? &pTab->aCol[iCol] : 0; + }else{ + pTab = 0; + goto error_out; + } } } @@ -3276,7 +3260,6 @@ error_out: sqlite3_mutex_leave(db->mutex); return rc; } -#endif /* ** Sleep for a little while. Return the amount of time slept. @@ -3715,13 +3698,14 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ ** connection. */ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ + Btree *pBt; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif - Btree *pBt = sqlite3DbNameToBtree(db, zDbName); + pBt = sqlite3DbNameToBtree(db, zDbName); return pBt ? sqlite3BtreeGetFilename(pBt) : 0; } @@ -3730,12 +3714,13 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ ** no such database exists. */ int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ + Btree *pBt; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return -1; } #endif - Btree *pBt = sqlite3DbNameToBtree(db, zDbName); + pBt = sqlite3DbNameToBtree(db, zDbName); return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; } diff --git a/src/mem1.c b/src/mem1.c index 11fc1771ed..ec9a4e3a61 100644 --- a/src/mem1.c +++ b/src/mem1.c @@ -79,9 +79,9 @@ static malloc_zone_t* _sqliteZone_; ** The malloc.h header file is needed for malloc_usable_size() function ** on some systems (e.g. Linux). */ -#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE) -# define SQLITE_USE_MALLOC_H -# define SQLITE_USE_MALLOC_USABLE_SIZE +#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE +# define SQLITE_USE_MALLOC_H 1 +# define SQLITE_USE_MALLOC_USABLE_SIZE 1 /* ** The MSVCRT has malloc_usable_size(), but it is called _msize(). The ** use of _msize() is automatic, but can be disabled by compiling with diff --git a/src/msvc.h b/src/msvc.h new file mode 100644 index 0000000000..4508e6941f --- /dev/null +++ b/src/msvc.h @@ -0,0 +1,35 @@ +/* +** 2015 January 12 +** +** 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 code that is specific to MSVC. +*/ +#ifndef _MSVC_H_ +#define _MSVC_H_ + +#if defined(_MSC_VER) +#pragma warning(disable : 4054) +#pragma warning(disable : 4055) +#pragma warning(disable : 4100) +#pragma warning(disable : 4127) +#pragma warning(disable : 4152) +#pragma warning(disable : 4189) +#pragma warning(disable : 4206) +#pragma warning(disable : 4210) +#pragma warning(disable : 4232) +#pragma warning(disable : 4244) +#pragma warning(disable : 4305) +#pragma warning(disable : 4306) +#pragma warning(disable : 4702) +#pragma warning(disable : 4706) +#endif /* defined(_MSC_VER) */ + +#endif /* _MSVC_H_ */ diff --git a/src/mutex_w32.c b/src/mutex_w32.c index da7d73f7c5..a799c86159 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -209,6 +209,12 @@ static sqlite3_mutex *winMutexAlloc(int iType){ break; } default: { +#ifdef SQLITE_ENABLE_API_ARMOR + if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif assert( iType-2 >= 0 ); assert( iType-2 < ArraySize(winMutex_staticMutexes) ); assert( winMutex_isInit==1 ); diff --git a/src/os_unix.c b/src/os_unix.c index a9344ee830..ddd6a802eb 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3386,9 +3386,9 @@ int sqlite3_fullsync_count = 0; ** We do not trust systems to provide a working fdatasync(). Some do. ** Others do no. To be safe, we will stick with the (slightly slower) ** fsync(). If you know that your system does support fdatasync() correctly, -** then simply compile with -Dfdatasync=fdatasync +** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC */ -#if !defined(fdatasync) +#if !defined(fdatasync) && !HAVE_FDATASYNC # define fdatasync fsync #endif @@ -3709,24 +3709,28 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ }while( err==EINTR ); if( err ) return SQLITE_IOERR_WRITE; #else - /* If the OS does not have posix_fallocate(), fake it. First use - ** ftruncate() to set the file size, then write a single byte to - ** the last byte in each block within the extended region. This - ** is the same technique used by glibc to implement posix_fallocate() - ** on systems that do not have a real fallocate() system call. + /* If the OS does not have posix_fallocate(), fake it. Write a + ** single byte to the last byte in each block that falls entirely + ** within the extended region. Then, if required, a single byte + ** at offset (nSize-1), to set the size of the file correctly. + ** This is a similar technique to that used by glibc on systems + ** that do not have a real fallocate() call. */ int nBlk = buf.st_blksize; /* File-system block size */ + int nWrite = 0; /* Number of bytes written by seekAndWrite */ i64 iWrite; /* Next offset to write to */ - if( robust_ftruncate(pFile->h, nSize) ){ - pFile->lastErrno = errno; - return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); - } iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; - while( iWrite=buf.st_size ); + assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) ); + assert( ((iWrite+1)%nBlk)==0 ); + for(/*no-op*/; iWriteh)); return SQLITE_FULL; @@ -2583,13 +2600,13 @@ static int winWrite( } #endif -#if SQLITE_OS_WINCE +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) rc = winSeekFile(pFile, offset); if( rc==0 ){ #else { #endif -#if !SQLITE_OS_WINCE +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) OVERLAPPED overlapped; /* The offset for WriteFile. */ #endif u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ @@ -2597,14 +2614,14 @@ static int winWrite( DWORD nWrite; /* Bytes written by each WriteFile() call */ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ -#if !SQLITE_OS_WINCE +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) memset(&overlapped, 0, sizeof(OVERLAPPED)); overlapped.Offset = (LONG)(offset & 0xffffffff); overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); #endif while( nRem>0 ){ -#if SQLITE_OS_WINCE +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ #else if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ @@ -2617,7 +2634,7 @@ static int winWrite( lastErrno = osGetLastError(); break; } -#if !SQLITE_OS_WINCE +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) offset += nWrite; overlapped.Offset = (LONG)(offset & 0xffffffff); overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); @@ -3814,16 +3831,16 @@ static int winShmMap( void volatile **pp /* OUT: Mapped memory */ ){ winFile *pDbFd = (winFile*)fd; - winShm *p = pDbFd->pShm; + winShm *pShm = pDbFd->pShm; winShmNode *pShmNode; int rc = SQLITE_OK; - if( !p ){ + if( !pShm ){ rc = winOpenSharedMemory(pDbFd); if( rc!=SQLITE_OK ) return rc; - p = pDbFd->pShm; + pShm = pDbFd->pShm; } - pShmNode = p->pShmNode; + pShmNode = pShm->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); @@ -5344,6 +5361,22 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ memcpy(&zBuf[n], &i, sizeof(i)); n += sizeof(i); } +#endif +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + if( sizeof(UUID)<=nBuf-n ){ + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreate(&id); + memcpy(zBuf, &id, sizeof(UUID)); + n += sizeof(UUID); + } + if( sizeof(UUID)<=nBuf-n ){ + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreateSequential(&id); + memcpy(zBuf, &id, sizeof(UUID)); + n += sizeof(UUID); + } #endif return n; } @@ -5522,7 +5555,7 @@ int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==77 ); + assert( ArraySize(aSyscall)==79 ); /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); diff --git a/src/os_win.h b/src/os_win.h index 5174ac7781..17d6a2bef4 100644 --- a/src/os_win.h +++ b/src/os_win.h @@ -74,4 +74,15 @@ # define SQLITE_WIN32_VOLATILE volatile #endif +/* +** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() +** functions are not available (e.g. those not using MSVC, Cygwin, etc). +*/ +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) +# define SQLITE_OS_WIN_THREADS 1 +#else +# define SQLITE_OS_WIN_THREADS 0 +#endif + #endif /* _OS_WIN_H_ */ diff --git a/src/pager.c b/src/pager.c index a5bba528ee..7cc35505f6 100644 --- a/src/pager.c +++ b/src/pager.c @@ -661,6 +661,8 @@ struct Pager { u8 setMaster; /* True if a m-j name has been written to jrnl */ u8 doNotSpill; /* Do not spill the cache when non-zero */ u8 subjInMemory; /* True to use in-memory sub-journals */ + u8 bUseFetch; /* True to use xFetch() */ + u8 hasBeenUsed; /* True if any content previously read from this pager*/ Pgno dbSize; /* Number of pages in the database */ Pgno dbOrigSize; /* dbSize before the current transaction */ Pgno dbFileSize; /* Number of pages in the database file */ @@ -678,9 +680,9 @@ struct Pager { sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ PagerSavepoint *aSavepoint; /* Array of active savepoints */ int nSavepoint; /* Number of elements in aSavepoint[] */ + u32 iDataVersion; /* Changes whenever database content changes */ char dbFileVers[16]; /* Changes whenever database file changes */ - u8 bUseFetch; /* True to use xFetch() */ int nMmapOut; /* Number of mmap pages currently outstanding */ sqlite3_int64 szMmap; /* Desired maximum mmap size */ PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ @@ -1708,10 +1710,19 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ ** Discard the entire contents of the in-memory page-cache. */ static void pager_reset(Pager *pPager){ + pPager->iDataVersion++; sqlite3BackupRestart(pPager->pBackup); sqlite3PcacheClear(pPager->pPCache); } +/* +** Return the pPager->iDataVersion value +*/ +u32 sqlite3PagerDataVersion(Pager *pPager){ + assert( pPager->eState>PAGER_OPEN ); + return pPager->iDataVersion; +} + /* ** Free all structures in the Pager.aSavepoint[] array and set both ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal @@ -3914,7 +3925,7 @@ static int pagerAcquireMapPage( PgHdr **ppPage /* OUT: Acquired page object */ ){ PgHdr *p; /* Memory mapped page to return */ - + if( pPager->pMmapFreelist ){ *ppPage = p = pPager->pMmapFreelist; pPager->pMmapFreelist = p->pDirty; @@ -5147,16 +5158,12 @@ int sqlite3PagerSharedLock(Pager *pPager){ ); } - if( !pPager->tempFile && ( - pPager->pBackup - || sqlite3PcachePagecount(pPager->pPCache)>0 - || USEFETCH(pPager) - )){ - /* The shared-lock has just been acquired on the database file - ** and there are already pages in the cache (from a previous - ** read or write transaction). Check to see if the database - ** has been modified. If the database has changed, flush the - ** cache. + if( !pPager->tempFile && pPager->hasBeenUsed ){ + /* The shared-lock has just been acquired then check to + ** see if the database has been modified. If the database has changed, + ** flush the cache. The pPager->hasBeenUsed flag prevents this from + ** occurring on the very first access to a file, in order to save a + ** single unnecessary sqlite3OsRead() call at the start-up. ** ** Database changes is detected by looking at 15 bytes beginning ** at offset 24 into the file. The first 4 of these 16 bytes are @@ -5338,6 +5345,7 @@ int sqlite3PagerAcquire( if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; } + pPager->hasBeenUsed = 1; /* If the pager is in the error state, return an error immediately. ** Otherwise, request the page from the PCache layer. */ @@ -5487,6 +5495,7 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ assert( pgno!=0 ); assert( pPager->pPCache!=0 ); pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0); + assert( pPage==0 || pPager->hasBeenUsed ); return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); } @@ -6353,6 +6362,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ } PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); + pPager->iDataVersion++; rc = pager_end_transaction(pPager, pPager->setMaster, 1); return pager_error(pPager, rc); } diff --git a/src/pager.h b/src/pager.h index 5b2440a58d..289ef9d42b 100644 --- a/src/pager.h +++ b/src/pager.h @@ -172,6 +172,7 @@ int sqlite3PagerSharedLock(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); +u32 sqlite3PagerDataVersion(Pager*); int sqlite3PagerRefcount(Pager*); int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*, int); diff --git a/src/parse.y b/src/parse.y index 877827e68d..544888a228 100644 --- a/src/parse.y +++ b/src/parse.y @@ -415,13 +415,19 @@ select(A) ::= with(W) selectnowith(X). { int cnt = 0, mxSelect; p->pWith = W; if( p->pPrior ){ + u16 allValues = SF_Values; pNext = 0; for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ pLoop->pNext = pNext; pLoop->selFlags |= SF_Compound; + allValues &= pLoop->selFlags; } - mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; - if( mxSelect && cnt>mxSelect ){ + if( allValues ){ + p->selFlags |= SF_AllValues; + }else if( + (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 + && cnt>mxSelect + ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); } } diff --git a/src/pcache.c b/src/pcache.c index 13551872d1..467e2b3dee 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -31,18 +31,6 @@ struct PCache { PgHdr *pPage1; /* Reference to page 1 */ }; -/* -** Some of the assert() macros in this code are too expensive to run -** even during normal debugging. Use them only rarely on long-running -** tests. Enable the expensive asserts using the -** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. -*/ -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT -# define expensive_assert(X) assert(X) -#else -# define expensive_assert(X) -#endif - /********************************** Linked List Management ********************/ /* Allowed values for second argument to pcacheManageDirtyList() */ @@ -196,7 +184,8 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ if( pCache->szPage ){ sqlite3_pcache *pNew; pNew = sqlite3GlobalConfig.pcache2.xCreate( - szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable + szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), + pCache->bPurgeable ); if( pNew==0 ) return SQLITE_NOMEM; sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); @@ -655,7 +644,7 @@ void sqlite3PcacheShrink(PCache *pCache){ ** Return the size of the header added by this middleware layer ** in the page-cache hierarchy. */ -int sqlite3HeaderSizePcache(void){ return sizeof(PgHdr); } +int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) diff --git a/src/pcache1.c b/src/pcache1.c index cad41b1b73..f5f7893714 100644 --- a/src/pcache1.c +++ b/src/pcache1.c @@ -296,7 +296,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ pPg = 0; } #else - pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra); + pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra); p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; #endif pcache1EnterMutex(pCache->pGroup); @@ -984,7 +984,7 @@ void sqlite3PCacheSetDefault(void){ /* ** Return the size of the header on each page of this PCACHE implementation. */ -int sqlite3HeaderSizePcache1(void){ return sizeof(PgHdr1); } +int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* diff --git a/src/pragma.c b/src/pragma.c index 1eaa98ae29..51c8d6feed 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -72,6 +72,7 @@ #define PragTyp_LOCK_STATUS 41 #define PragTyp_PARSER_TRACE 42 #define PragFlag_NeedSchema 0x01 +#define PragFlag_ReadOnly 0x02 static const struct sPragmaNames { const char *const zName; /* Name of pragma */ u8 ePragTyp; /* PragTyp_XXX value */ @@ -88,7 +89,7 @@ static const struct sPragmaNames { { /* zName: */ "application_id", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlag: */ 0, - /* iArg: */ 0 }, + /* iArg: */ BTREE_APPLICATION_ID }, #endif #if !defined(SQLITE_OMIT_AUTOVACUUM) { /* zName: */ "auto_vacuum", @@ -154,6 +155,12 @@ static const struct sPragmaNames { /* ePragFlag: */ 0, /* iArg: */ 0 }, #endif +#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) + { /* zName: */ "data_version", + /* ePragTyp: */ PragTyp_HEADER_VALUE, + /* ePragFlag: */ PragFlag_ReadOnly, + /* iArg: */ BTREE_DATA_VERSION }, +#endif #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) { /* zName: */ "database_list", /* ePragTyp: */ PragTyp_DATABASE_LIST, @@ -209,8 +216,8 @@ static const struct sPragmaNames { #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) { /* zName: */ "freelist_count", /* ePragTyp: */ PragTyp_HEADER_VALUE, - /* ePragFlag: */ 0, - /* iArg: */ 0 }, + /* ePragFlag: */ PragFlag_ReadOnly, + /* iArg: */ BTREE_FREE_PAGE_COUNT }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) { /* zName: */ "full_column_names", @@ -376,7 +383,7 @@ static const struct sPragmaNames { { /* zName: */ "schema_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlag: */ 0, - /* iArg: */ 0 }, + /* iArg: */ BTREE_SCHEMA_VERSION }, #endif #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) { /* zName: */ "secure_delete", @@ -442,7 +449,7 @@ static const struct sPragmaNames { { /* zName: */ "user_version", /* ePragTyp: */ PragTyp_HEADER_VALUE, /* ePragFlag: */ 0, - /* iArg: */ 0 }, + /* iArg: */ BTREE_USER_VERSION }, #endif #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if defined(SQLITE_DEBUG) @@ -485,7 +492,7 @@ static const struct sPragmaNames { /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, #endif }; -/* Number of pragmas: 57 on by default, 72 total. */ +/* Number of pragmas: 58 on by default, 73 total. */ /* End of the automatically generated pragma table. ***************************************************************************/ @@ -735,7 +742,7 @@ void sqlite3Pragma( Token *pId; /* Pointer to token */ char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ int iDb; /* Database index for */ - int lwr, upr, mid; /* Binary search bounds */ + int lwr, upr, mid = 0; /* Binary search bounds */ int rc; /* return value form SQLITE_FCNTL_PRAGMA */ sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* The specific database being pragmaed */ @@ -2142,7 +2149,8 @@ void sqlite3Pragma( ){ for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ - ENC(pParse->db) = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; + SCHEMA_ENC(db) = ENC(db) = + pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; break; } } @@ -2187,24 +2195,9 @@ void sqlite3Pragma( ** applications for any purpose. */ case PragTyp_HEADER_VALUE: { - int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */ + int iCookie = aPragmaNames[mid].iArg; /* Which cookie to read or write */ sqlite3VdbeUsesBtree(v, iDb); - switch( zLeft[0] ){ - case 'a': case 'A': - iCookie = BTREE_APPLICATION_ID; - break; - case 'f': case 'F': - iCookie = BTREE_FREE_PAGE_COUNT; - break; - case 's': case 'S': - iCookie = BTREE_SCHEMA_VERSION; - break; - default: - iCookie = BTREE_USER_VERSION; - break; - } - - if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){ + if( zRight && (aPragmaNames[mid].mPragFlag & PragFlag_ReadOnly)==0 ){ /* Write the specified cookie value */ static const VdbeOpList setCookie[] = { { OP_Transaction, 0, 1, 0}, /* 0 */ diff --git a/src/prepare.c b/src/prepare.c index ca9c64b441..97be900d68 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -394,9 +394,11 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ int commit_internal = !(db->flags&SQLITE_InternChanges); assert( sqlite3_mutex_held(db->mutex) ); + assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) ); assert( db->init.busy==0 ); rc = SQLITE_OK; db->init.busy = 1; + ENC(db) = SCHEMA_ENC(db); for(i=0; rc==SQLITE_OK && inDb; i++){ if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; rc = sqlite3InitOne(db, i, pzErrMsg); diff --git a/src/printf.c b/src/printf.c index ac7b051631..8291002db8 100644 --- a/src/printf.c +++ b/src/printf.c @@ -14,17 +14,6 @@ */ #include "sqliteInt.h" -/* -** If the strchrnul() library function is available, then set -** HAVE_STRCHRNUL. If that routine is not available, this module -** will supply its own. The built-in version is slower than -** the glibc version so the glibc version is definitely preferred. -*/ -#if !defined(HAVE_STRCHRNUL) -# define HAVE_STRCHRNUL 0 -#endif - - /* ** Conversion types fall into various categories as defined by the ** following enumeration. @@ -223,13 +212,6 @@ void sqlite3VXPrintf( PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ -#ifdef SQLITE_ENABLE_API_ARMOR - if( ap==0 ){ - (void)SQLITE_MISUSE_BKPT; - sqlite3StrAccumReset(pAccum); - return; - } -#endif bufpt = 0; if( bFlags ){ if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){ diff --git a/src/select.c b/src/select.c index 070ac00410..8fbd4bb261 100644 --- a/src/select.c +++ b/src/select.c @@ -58,20 +58,25 @@ struct SortCtx { #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ /* -** Delete all the content of a Select structure but do not deallocate -** the select structure itself. +** Delete all the content of a Select structure. Deallocate the structure +** itself only if bFree is true. */ -static void clearSelect(sqlite3 *db, Select *p){ - sqlite3ExprListDelete(db, p->pEList); - sqlite3SrcListDelete(db, p->pSrc); - sqlite3ExprDelete(db, p->pWhere); - sqlite3ExprListDelete(db, p->pGroupBy); - sqlite3ExprDelete(db, p->pHaving); - sqlite3ExprListDelete(db, p->pOrderBy); - sqlite3SelectDelete(db, p->pPrior); - sqlite3ExprDelete(db, p->pLimit); - sqlite3ExprDelete(db, p->pOffset); - sqlite3WithDelete(db, p->pWith); +static void clearSelect(sqlite3 *db, Select *p, int bFree){ + while( p ){ + Select *pPrior = p->pPrior; + sqlite3ExprListDelete(db, p->pEList); + sqlite3SrcListDelete(db, p->pSrc); + sqlite3ExprDelete(db, p->pWhere); + sqlite3ExprListDelete(db, p->pGroupBy); + sqlite3ExprDelete(db, p->pHaving); + sqlite3ExprListDelete(db, p->pOrderBy); + sqlite3ExprDelete(db, p->pLimit); + sqlite3ExprDelete(db, p->pOffset); + sqlite3WithDelete(db, p->pWith); + if( bFree ) sqlite3DbFree(db, p); + p = pPrior; + bFree = 1; + } } /* @@ -130,8 +135,7 @@ Select *sqlite3SelectNew( pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; if( db->mallocFailed ) { - clearSelect(db, pNew); - if( pNew!=&standin ) sqlite3DbFree(db, pNew); + clearSelect(db, pNew, pNew!=&standin); pNew = 0; }else{ assert( pNew->pSrc!=0 || pParse->nErr>0 ); @@ -156,10 +160,7 @@ void sqlite3SelectSetName(Select *p, const char *zName){ ** Delete the given Select structure and all of its substructures. */ void sqlite3SelectDelete(sqlite3 *db, Select *p){ - if( p ){ - clearSelect(db, p); - sqlite3DbFree(db, p); - } + clearSelect(db, p, 1); } /* @@ -542,7 +543,9 @@ static void pushOntoSorter( pKI = pOp->p4.pKeyInfo; memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */ sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); - pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1); + testcase( pKI->nXField>2 ); + pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, + pKI->nXField-1); addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); pSort->labelBkOut = sqlite3VdbeMakeLabel(v); @@ -1053,7 +1056,7 @@ static KeyInfo *keyInfoFromExprList( int i; nExpr = pList->nExpr; - pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1); + pInfo = sqlite3KeyInfoAlloc(db, nExpr-iStart, nExtra+1); if( pInfo ){ assert( sqlite3KeyInfoIsWriteable(pInfo) ); for(i=iStart, pItem=pList->a+iStart; iselFlags & SF_Values ){ + sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); + }else{ + sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" + " do not have the same number of result columns", selectOpName(p->op)); + } +} + +/* +** Handle the special case of a compound-select that originates from a +** VALUES clause. By handling this as a special case, we avoid deep +** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT +** on a VALUES clause. +** +** Because the Select object originates from a VALUES clause: +** (1) It has no LIMIT or OFFSET +** (2) All terms are UNION ALL +** (3) There is no ORDER BY clause +*/ +static int multiSelectValues( + Parse *pParse, /* Parsing context */ + Select *p, /* The right-most of SELECTs to be coded */ + SelectDest *pDest /* What to do with query results */ +){ + Select *pPrior; + int nExpr = p->pEList->nExpr; + int nRow = 1; + int rc = 0; + assert( p->pNext==0 ); + assert( p->selFlags & SF_AllValues ); + do{ + assert( p->selFlags & SF_Values ); + assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); + assert( p->pLimit==0 ); + assert( p->pOffset==0 ); + if( p->pEList->nExpr!=nExpr ){ + selectWrongNumTermsError(pParse, p); + return 1; + } + if( p->pPrior==0 ) break; + assert( p->pPrior->pNext==p ); + p = p->pPrior; + nRow++; + }while(1); + while( p ){ + pPrior = p->pPrior; + p->pPrior = 0; + rc = sqlite3Select(pParse, p, pDest); + p->pPrior = pPrior; + if( rc ) break; + p->nSelectRow = nRow; + p = p->pNext; + } + return rc; +} /* ** This routine is called to process a compound query form from @@ -2156,17 +2219,19 @@ static int multiSelect( dest.eDest = SRT_Table; } + /* Special handling for a compound-select that originates as a VALUES clause. + */ + if( p->selFlags & SF_AllValues ){ + rc = multiSelectValues(pParse, p, &dest); + goto multi_select_end; + } + /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ - if( p->selFlags & SF_Values ){ - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); - }else{ - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" - " do not have the same number of result columns", selectOpName(p->op)); - } + selectWrongNumTermsError(pParse, p); rc = 1; goto multi_select_end; } @@ -4052,7 +4117,9 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; - sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); + if( pWalker->xSelectCallback2==selectPopWith ){ + sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); + } /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -4086,7 +4153,7 @@ static int selectExpander(Walker *pWalker, Select *p){ /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); assert( pFrom->pTab==0 ); - sqlite3WalkSelect(pWalker, pSel); + if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; @@ -4343,7 +4410,9 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - w.xSelectCallback2 = selectPopWith; + if( (pSelect->selFlags & SF_AllValues)==0 ){ + w.xSelectCallback2 = selectPopWith; + } sqlite3WalkSelect(&w, pSelect); } @@ -4857,7 +4926,7 @@ int sqlite3Select( */ if( sSort.pOrderBy ){ KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0); + pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr); sSort.iECursor = pParse->nTab++; sSort.addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, @@ -5031,7 +5100,7 @@ int sqlite3Select( ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0); + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 0, (char*)pKeyInfo, P4_KEYINFO); diff --git a/src/shell.c b/src/shell.c index 94d20d3549..f9a360c7b5 100644 --- a/src/shell.c +++ b/src/shell.c @@ -17,6 +17,13 @@ #define _CRT_SECURE_NO_WARNINGS #endif +/* +** If requested, include the SQLite compiler options file for MSVC. +*/ +#if defined(INCLUDE_MSVC_H) +#include "msvc.h" +#endif + /* ** Enable large-file support for fopen() and friends on unix. */ @@ -48,17 +55,16 @@ # include #endif -#if defined(HAVE_READLINE) && HAVE_READLINE!=0 +#if HAVE_READLINE # include # include -#else -# undef HAVE_READLINE #endif -#if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE) +#if HAVE_EDITLINE +# undef HAVE_READLINE # define HAVE_READLINE 1 # include #endif -#if !defined(HAVE_READLINE) +#if !HAVE_READLINE # define add_history(X) # define read_history(X) # define write_history(X) @@ -100,6 +106,26 @@ extern int pclose(FILE*); #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) +/* On Windows, we normally run with output mode of TEXT so that \n characters +** are automatically translated into \r\n. However, this behavior needs +** to be disabled in some cases (ex: when generating CSV output and when +** rendering quoted strings that contain \n characters). The following +** routines take care of that. +*/ +#if defined(_WIN32) || defined(WIN32) +static void setBinaryMode(FILE *out){ + fflush(out); + _setmode(_fileno(out), _O_BINARY); +} +static void setTextMode(FILE *out){ + fflush(out); + _setmode(_fileno(out), _O_TEXT); +} +#else +# define setBinaryMode(X) +# define setTextMode(X) +#endif + /* True if the timer is enabled */ static int enableTimer = 0; @@ -425,7 +451,7 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; -#if defined(HAVE_READLINE) +#if HAVE_READLINE free(zPrior); zResult = readline(zPrompt); if( zResult && *zResult ) add_history(zResult); @@ -471,11 +497,11 @@ struct ShellState { int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ char *zDestTable; /* Name of destination table when MODE_Insert */ - char separator[20]; /* Separator character for MODE_List */ - char newline[20]; /* Record separator in MODE_Csv */ + char colSeparator[20]; /* Column separator character for several modes */ + char rowSeparator[20]; /* Row separator character for MODE_Ascii */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ - char nullvalue[20]; /* The text to print when a NULL comes back from + char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ @@ -508,6 +534,7 @@ struct ShellState { #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 7 /* Quote strings, numbers are plain */ #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ +#define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ static const char *modeDescr[] = { "line", @@ -519,8 +546,22 @@ static const char *modeDescr[] = { "tcl", "csv", "explain", + "ascii", }; +/* +** These are the column/row/line separators used by the various +** import/export modes. +*/ +#define SEP_Column "|" +#define SEP_Row "\n" +#define SEP_Tab "\t" +#define SEP_Space " " +#define SEP_Comma "," +#define SEP_CrLf "\r\n" +#define SEP_Unit "\x1F" +#define SEP_Record "\x1E" + /* ** Number of elements in an array */ @@ -563,6 +604,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ static void output_quoted_string(FILE *out, const char *z){ int i; int nSingle = 0; + setBinaryMode(out); for(i=0; z[i]; i++){ if( z[i]=='\'' ) nSingle++; } @@ -585,6 +627,7 @@ static void output_quoted_string(FILE *out, const char *z){ } fprintf(out,"'"); } + setTextMode(out); } /* @@ -677,22 +720,22 @@ static const char needCsvQuote[] = { }; /* -** Output a single term of CSV. Actually, p->separator is used for -** the separator, which may or may not be a comma. p->nullvalue is +** Output a single term of CSV. Actually, p->colSeparator is used for +** the separator, which may or may not be a comma. p->nullValue is ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ - fprintf(out,"%s",p->nullvalue); + fprintf(out,"%s",p->nullValue); }else{ int i; - int nSep = strlen30(p->separator); + int nSep = strlen30(p->colSeparator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] - || (z[i]==p->separator[0] && - (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ + || (z[i]==p->colSeparator[0] && + (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ i = 0; break; } @@ -709,7 +752,7 @@ static void output_csv(ShellState *p, const char *z, int bSep){ } } if( bSep ){ - fprintf(p->out, "%s", p->separator); + fprintf(p->out, "%s", p->colSeparator); } } @@ -747,10 +790,10 @@ static int shell_callback( int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } - if( p->cnt++>0 ) fprintf(p->out,"\n"); + if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); for(i=0; iout,"%*s = %s\n", w, azCol[i], - azArg[i] ? azArg[i] : p->nullvalue); + fprintf(p->out,"%*s = %s%s", w, azCol[i], + azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } @@ -767,7 +810,7 @@ static int shell_callback( if( w==0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; - n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); + n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); if( wactualWidth) ){ @@ -775,9 +818,11 @@ static int shell_callback( } if( p->showHeader ){ if( w<0 ){ - fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); + fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], + i==nArg-1 ? p->rowSeparator : " "); }else{ - fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); + fprintf(p->out,"%-*.*s%s",w,w,azCol[i], + i==nArg-1 ? p->rowSeparator : " "); } } } @@ -792,7 +837,7 @@ static int shell_callback( } fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" "----------------------------------------------------------", - i==nArg-1 ? "\n": " "); + i==nArg-1 ? p->rowSeparator : " "); } } } @@ -815,10 +860,12 @@ static int shell_callback( } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + azArg[i] ? azArg[i] : p->nullValue, + i==nArg-1 ? p->rowSeparator : " "); }else{ fprintf(p->out,"%-*.*s%s",w,w, - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + azArg[i] ? azArg[i] : p->nullValue, + i==nArg-1 ? p->rowSeparator : " "); } } break; @@ -827,20 +874,21 @@ static int shell_callback( case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); + fprintf(p->out,"%s%s",azCol[i], + i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; inullvalue; + if( z==0 ) z = p->nullValue; fprintf(p->out, "%s", z); if( iout, "%s", p->separator); + fprintf(p->out, "%s", p->colSeparator); }else if( p->mode==MODE_Semi ){ - fprintf(p->out, ";\n"); + fprintf(p->out, ";%s", p->rowSeparator); }else{ - fprintf(p->out, "\n"); + fprintf(p->out, "%s", p->rowSeparator); } } break; @@ -859,7 +907,7 @@ static int shell_callback( fprintf(p->out,""); for(i=0; iout,""); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); + output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); fprintf(p->out,"\n"); } fprintf(p->out,"\n"); @@ -869,39 +917,33 @@ static int shell_callback( if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, "%s", p->separator); + if(iout, "%s", p->colSeparator); } - fprintf(p->out,"\n"); + fprintf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullvalue); - if(iout, "%s", p->separator); + output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); + if(iout, "%s", p->colSeparator); } - fprintf(p->out,"\n"); + fprintf(p->out, "%s", p->rowSeparator); break; } case MODE_Csv: { -#if defined(WIN32) || defined(_WIN32) - fflush(p->out); - _setmode(_fileno(p->out), _O_BINARY); -#endif + setBinaryMode(p->out); if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s",p->newline); + fprintf(p->out, "%s", p->rowSeparator); } if( nArg>0 ){ for(i=0; iout,"%s",p->newline); + fprintf(p->out, "%s", p->rowSeparator); } -#if defined(WIN32) || defined(_WIN32) - fflush(p->out); - _setmode(_fileno(p->out), _O_TEXT); -#endif + setTextMode(p->out); break; } case MODE_Insert: { @@ -933,6 +975,22 @@ static int shell_callback( fprintf(p->out,");\n"); break; } + case MODE_Ascii: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); + fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); + } + fprintf(p->out, "%s", p->rowSeparator); + } + if( azArg==0 ) break; + for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); + fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + } + fprintf(p->out, "%s", p->rowSeparator); + break; + } } return 0; } @@ -1698,12 +1756,13 @@ static char zHelp[] = #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" + " ascii Columns/rows delimited by 0x1F and 0x1E\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" - " list Values delimited by .separator string\n" + " list Values delimited by .separator strings\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" @@ -1720,8 +1779,8 @@ static char zHelp[] = ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" - ".separator STRING ?NL? Change separator used by output mode and .import\n" - " NL is the end-of-line mark for CSV\n" + ".separator COL ?ROW? Change the column separator and optionally the row\n" + " separator for both the output mode and .import\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats on|off Turn stats on or off\n" @@ -2002,10 +2061,10 @@ static void test_breakpoint(void){ } /* -** An object used to read a CSV file +** An object used to read a CSV and other files for import. */ -typedef struct CSVReader CSVReader; -struct CSVReader { +typedef struct ImportCtx ImportCtx; +struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ @@ -2013,11 +2072,12 @@ struct CSVReader { int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int cTerm; /* Character that terminated the most recent field */ - int cSeparator; /* The separator character. (Usually ",") */ + int cColSep; /* The column separator character. (Usually ",") */ + int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ -static void csv_append_char(CSVReader *p, int c){ +static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc(p->z, p->nAlloc); @@ -2035,15 +2095,17 @@ static void csv_append_char(CSVReader *p, int c){ ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc(). -** + Use p->cSep as the separator. The default is ",". +** + Use p->cSep as the column separator. The default is ",". +** + Use p->rSep as the row separator. The default is "\n". ** + 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. ** + Report syntax errors on stderr */ -static char *csv_read_one_field(CSVReader *p){ - int c, pc, ppc; - int cSep = p->cSeparator; +static char *csv_read_one_field(ImportCtx *p){ + int c; + int cSep = p->cColSep; + int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ @@ -2051,12 +2113,13 @@ static char *csv_read_one_field(CSVReader *p){ return 0; } if( c=='"' ){ + int pc, ppc; int startLine = p->nLine; int cQuote = c; pc = ppc = 0; while( 1 ){ c = fgetc(p->in); - if( c=='\n' ) p->nLine++; + if( c==rSep ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ pc = 0; @@ -2064,8 +2127,8 @@ static char *csv_read_one_field(CSVReader *p){ } } if( (c==cSep && pc==cQuote) - || (c=='\n' && pc==cQuote) - || (c=='\n' && pc=='\r' && ppc==cQuote) + || (c==rSep && pc==cQuote) + || (c==rSep && pc=='\r' && ppc==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); @@ -2079,19 +2142,19 @@ static char *csv_read_one_field(CSVReader *p){ if( c==EOF ){ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); - p->cTerm = EOF; + p->cTerm = c; break; } - csv_append_char(p, c); + import_append_char(p, c); ppc = pc; pc = c; } }else{ - while( c!=EOF && c!=cSep && c!='\n' ){ - csv_append_char(p, c); + while( c!=EOF && c!=cSep && c!=rSep ){ + import_append_char(p, c); c = fgetc(p->in); } - if( c=='\n' ){ + if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } @@ -2101,6 +2164,40 @@ static char *csv_read_one_field(CSVReader *p){ return p->z; } +/* Read a single field of ASCII delimited text. +** +** + Input comes from p->in. +** + Store results in p->z of length p->n. Space to hold p->z comes +** from sqlite3_malloc(). +** + Use p->cSep as the column separator. The default is "\x1F". +** + Use p->rSep as the row separator. The default is "\x1E". +** + Keep track of the row number in p->nLine. +** + Store the character that terminates the field in p->cTerm. Store +** EOF on end-of-file. +** + Report syntax errors on stderr +*/ +static char *ascii_read_one_field(ImportCtx *p){ + int c; + int cSep = p->cColSep; + int rSep = p->cRowSep; + p->n = 0; + c = fgetc(p->in); + if( c==EOF || seenInterrupt ){ + p->cTerm = EOF; + return 0; + } + while( c!=EOF && c!=cSep && c!=rSep ){ + import_append_char(p, c); + c = fgetc(p->in); + } + if( c==rSep ){ + p->nLine++; + } + p->cTerm = c; + if( p->z ) p->z[p->n] = 0; + return p->z; +} + /* ** Try to transfer data for table zTable. If an error is seen while ** moving forward, try to go backwards. The backwards movement won't @@ -2655,9 +2752,10 @@ static int do_meta_command(char *zLine, ShellState *p){ int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ - int nSep; /* Number of bytes in p->separator[] */ + int nSep; /* Number of bytes in p->colSeparator[] */ char *zSql; /* An SQL statement */ - CSVReader sCsv; /* Reader context */ + ImportCtx sCtx; /* Reader context */ + char *(*xRead)(ImportCtx*); /* Procedure to read one value */ int (*xCloser)(FILE*); /* Procedure to close th3 connection */ if( nArg!=3 ){ @@ -2667,55 +2765,79 @@ static int do_meta_command(char *zLine, ShellState *p){ zFile = azArg[1]; zTable = azArg[2]; seenInterrupt = 0; - memset(&sCsv, 0, sizeof(sCsv)); + memset(&sCtx, 0, sizeof(sCtx)); open_db(p, 0); - nSep = strlen30(p->separator); + nSep = strlen30(p->colSeparator); if( nSep==0 ){ - fprintf(stderr, "Error: non-null separator required for import\n"); + fprintf(stderr, "Error: non-null column separator required for import\n"); return 1; } if( nSep>1 ){ - fprintf(stderr, "Error: multi-character separators not allowed" + fprintf(stderr, "Error: multi-character column separators not allowed" " for import\n"); return 1; } - sCsv.zFile = zFile; - sCsv.nLine = 1; - if( sCsv.zFile[0]=='|' ){ - sCsv.in = popen(sCsv.zFile+1, "r"); - sCsv.zFile = ""; + nSep = strlen30(p->rowSeparator); + if( nSep==0 ){ + fprintf(stderr, "Error: non-null row separator required for import\n"); + return 1; + } + if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ + /* When importing CSV (only), if the row separator is set to the + ** default output row separator, change it to the default input + ** row separator. This avoids having to maintain different input + ** and output row separators. */ + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); + nSep = strlen30(p->rowSeparator); + } + if( nSep>1 ){ + fprintf(stderr, "Error: multi-character row separators not allowed" + " for import\n"); + return 1; + } + sCtx.zFile = zFile; + sCtx.nLine = 1; + if( sCtx.zFile[0]=='|' ){ + sCtx.in = popen(sCtx.zFile+1, "r"); + sCtx.zFile = ""; xCloser = pclose; }else{ - sCsv.in = fopen(sCsv.zFile, "rb"); + sCtx.in = fopen(sCtx.zFile, "rb"); xCloser = fclose; } - if( sCsv.in==0 ){ + if( p->mode==MODE_Ascii ){ + xRead = ascii_read_one_field; + }else{ + xRead = csv_read_one_field; + } + if( sCtx.in==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } - sCsv.cSeparator = p->separator[0]; + sCtx.cColSep = p->colSeparator[0]; + sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */ + import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; - while( csv_read_one_field(&sCsv) ){ - zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); + while( xRead(&sCtx) ){ + zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); cSep = ','; - if( sCsv.cTerm!=sCsv.cSeparator ) break; + if( sCtx.cTerm!=sCtx.cColSep ) break; } if( cSep=='(' ){ sqlite3_free(zCreate); - sqlite3_free(sCsv.z); - xCloser(sCsv.in); - fprintf(stderr,"%s: empty file\n", sCsv.zFile); + sqlite3_free(sCtx.z); + xCloser(sCtx.in); + fprintf(stderr,"%s: empty file\n", sCtx.zFile); return 1; } zCreate = sqlite3_mprintf("%z\n)", zCreate); @@ -2724,8 +2846,8 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc ){ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(db)); - sqlite3_free(sCsv.z); - xCloser(sCsv.in); + sqlite3_free(sCtx.z); + xCloser(sCtx.in); return 1; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); @@ -2734,7 +2856,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc ){ if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } nCol = sqlite3_column_count(pStmt); @@ -2744,7 +2866,7 @@ static int do_meta_command(char *zLine, ShellState *p){ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); @@ -2760,46 +2882,56 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); if (pStmt) sqlite3_finalize(pStmt); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } needCommit = sqlite3_get_autocommit(db); if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); do{ - int startLine = sCsv.nLine; + int startLine = sCtx.nLine; for(i=0; imode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); - if( i=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ - fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, + fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(db)); } } - }while( sCsv.cTerm!=EOF ); + }while( sCtx.cTerm!=EOF ); - xCloser(sCsv.in); - sqlite3_free(sCsv.z); + xCloser(sCtx.in); + sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); }else @@ -2917,28 +3049,32 @@ static int do_meta_command(char *zLine, ShellState *p){ p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; - sqlite3_snprintf(sizeof(p->separator), p->separator, " "); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; - sqlite3_snprintf(sizeof(p->separator), p->separator, ","); - sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; - sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); + }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ + p->mode = MODE_Ascii; + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else { fprintf(stderr,"Error: mode should be one of: " - "column csv html insert line list tabs tcl\n"); + "ascii column csv html insert line list tabs tcl\n"); rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ - sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, - "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); + sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, + "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ fprintf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; @@ -3193,7 +3329,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ extern int sqlite3SelectTrace; - sqlite3SelectTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff; + sqlite3SelectTrace = integerValue(azArg[1]); }else #endif @@ -3223,14 +3359,16 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ - fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); + fprintf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ - sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, + "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ - sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, + "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else @@ -3261,23 +3399,24 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; goto meta_command_exit; } - fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); - fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off"); + fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); + fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); - fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); - fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); - fprintf(p->out,"%9.9s: ", "nullvalue"); - output_c_string(p->out, p->nullvalue); + fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); + fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); + fprintf(p->out,"%12.12s: ", "nullvalue"); + output_c_string(p->out, p->nullValue); fprintf(p->out, "\n"); - fprintf(p->out,"%9.9s: %s\n","output", + fprintf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); - fprintf(p->out,"%9.9s: ", "separator"); - output_c_string(p->out, p->separator); - fprintf(p->out," "); - output_c_string(p->out, p->newline); + fprintf(p->out,"%12.12s: ", "colseparator"); + output_c_string(p->out, p->colSeparator); fprintf(p->out, "\n"); - fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); - fprintf(p->out,"%9.9s: ","width"); + fprintf(p->out,"%12.12s: ", "rowseparator"); + output_c_string(p->out, p->rowSeparator); + fprintf(p->out, "\n"); + fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off"); + fprintf(p->out,"%12.12s: ","width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { fprintf(p->out,"%d ",p->colWidth[i]); } @@ -3396,6 +3535,7 @@ static int do_meta_command(char *zLine, ShellState *p){ { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, + { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, }; int testctrl = -1; int rc = 0; @@ -3462,7 +3602,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* sqlite3_test_control(int, int) */ case SQLITE_TESTCTRL_ASSERT: - case SQLITE_TESTCTRL_ALWAYS: + case SQLITE_TESTCTRL_ALWAYS: + case SQLITE_TESTCTRL_NEVER_CORRUPT: if( nArg==3 ){ int opt = booleanValue(azArg[2]); rc = sqlite3_test_control(testctrl, opt); @@ -3938,6 +4079,7 @@ static int process_sqliterc( ** Show available command line options */ static const char zOptions[] = + " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" @@ -3959,11 +4101,11 @@ static const char zOptions[] = #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif - " -newline SEP set newline character(s) for CSV\n" + " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" - " -separator SEP set output field separator. Default: '|'\n" + " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" @@ -3990,8 +4132,8 @@ static void usage(int showDetail){ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; - memcpy(data->separator,"|", 2); - memcpy(data->newline,"\r\n", 3); + memcpy(data->colSeparator,SEP_Column, 2); + memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); @@ -4052,6 +4194,8 @@ int main(int argc, char **argv){ exit(1); } #endif + setBinaryMode(stdin); + setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ Argv0 = argv[0]; main_init(&data); stdin_is_interactive = isatty(0); @@ -4230,15 +4374,21 @@ int main(int argc, char **argv){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; - memcpy(data.separator,",",2); + memcpy(data.colSeparator,",",2); + }else if( strcmp(z,"-ascii")==0 ){ + data.mode = MODE_Ascii; + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, + SEP_Unit); + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, + SEP_Record); }else if( strcmp(z,"-separator")==0 ){ - sqlite3_snprintf(sizeof(data.separator), data.separator, + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-newline")==0 ){ - sqlite3_snprintf(sizeof(data.newline), data.newline, + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ - sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, + sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; @@ -4358,7 +4508,7 @@ int main(int argc, char **argv){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } -#if defined(HAVE_READLINE) +#if HAVE_READLINE if( zHistory ) read_history(zHistory); #endif rc = process_input(&data, 0); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 5d4c821789..e4a8f0794e 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -196,7 +196,7 @@ const char *sqlite3_compileoption_get(int N); ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but ** can be fully or partially disabled using a call to [sqlite3_config()] ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], -** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the +** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the ** sqlite3_threadsafe() function shows only the compile-time setting of ** thread safety, not any run-time changes to that setting made by ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() @@ -1570,7 +1570,7 @@ struct sqlite3_mem_methods { ** 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page -** (a power of two between 512 and 32768) plus some extra bytes for each +** (a power of two between 512 and 65536) plus some extra bytes for each ** page header. ^The number of extra bytes needed by the page header ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option ** to [sqlite3_config()]. @@ -1750,6 +1750,17 @@ struct sqlite3_mem_methods { ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. ** The amount of extra space required can change depending on the compiler, ** target platform, and SQLite version. +** +** [[SQLITE_CONFIG_PMASZ]] +**
SQLITE_CONFIG_PMASZ +**
^The SQLITE_CONFIG_PMASZ option takes a single parameter which +** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded +** sorter to that integer. The default minimum PMA Size is set by the +** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched +** to help with sort operations when multithreaded sorting +** is enabled (using the [PRAGMA threads] command) and the amount of content +** to be sorted exceeds the page size times the minimum of the +** [PRAGMA cache_size] setting and this value. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1776,6 +1787,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ +#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ /* ** CAPI3REF: Database Connection Configuration Options @@ -5157,20 +5169,27 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table ** -** ^This routine returns metadata about a specific column of a specific -** database table accessible using the [database connection] handle -** passed as the first function argument. +** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns +** information about column C of table T in database D +** on [database connection] X.)^ ^The sqlite3_table_column_metadata() +** interface returns SQLITE_OK and fills in the non-NULL pointers in +** the final five arguments with appropriate values if the specified +** 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 +** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it +** does not. ** ** ^The column is identified by the second, third and fourth parameters to -** this function. ^The second parameter is either the name of the database +** this function. ^(The second parameter is either the name of the database ** (i.e. "main", "temp", or an attached database) containing the specified -** table or NULL. ^If it is NULL, then all attached databases are searched +** table or NULL.)^ ^If it is NULL, then all attached databases are searched ** for the table using the same algorithm used by the database engine to ** resolve unqualified table references. ** ** ^The third and fourth parameters to this function are the table and column -** name of the desired column, respectively. Neither of these parameters -** may be NULL. +** name of the desired column, respectively. ** ** ^Metadata is returned by writing to the memory locations passed as the 5th ** and subsequent parameters to this function. ^Any of these arguments may be @@ -5189,16 +5208,17 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** )^ ** ** ^The memory pointed to by the character pointers returned for the -** declaration type and collation sequence is valid only until the next +** declaration type and collation sequence is valid until the next ** call to any SQLite API function. ** ** ^If the specified table is actually a view, an [error code] is returned. ** -** ^If the specified column is "rowid", "oid" or "_rowid_" and an +** ^If the specified column is "rowid", "oid" or "_rowid_" and the table +** is not a [WITHOUT ROWID] table and an ** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output ** parameters are set for the explicitly declared column. ^(If there is no -** explicitly declared [INTEGER PRIMARY KEY] column, then the output -** parameters are set as follows: +** [INTEGER PRIMARY KEY] column, then the outputs +** for the [rowid] are set as follows: ** **
 **     data type: "INTEGER"
@@ -5208,13 +5228,9 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
 **     auto increment: 0
 ** 
)^ ** -** ^(This function may load one or more schemas from database files. If an -** error occurs during this process, or if the requested table or column -** cannot be found, an [error code] is returned and an error message left -** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ -** -** ^This API is only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +** ^This function causes all database schemas to be read from disk and +** parsed, if that has not already been done, and returns an error if +** any errors are encountered while loading the schema. */ int sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ @@ -7181,12 +7197,10 @@ void sqlite3_log(int iErrCode, const char *zFormat, ...); ** CAPI3REF: Write-Ahead Log Commit Hook ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that -** will be invoked each time a database connection commits data to a -** [write-ahead log] (i.e. whenever a transaction is committed in -** [journal_mode | journal_mode=WAL mode]). +** is invoked each time data is committed to a database in wal mode. ** -** ^The callback is invoked by SQLite after the commit has taken place and -** the associated write-lock on the database released, so the implementation +** ^(The callback is invoked by SQLite after the commit has taken place and +** the associated write-lock on the database released)^, so the implementation ** may read, write or [checkpoint] the database as required. ** ** ^The first parameter passed to the callback function when it is invoked @@ -7477,6 +7491,10 @@ int sqlite3_vtab_on_conflict(sqlite3 *); ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a ** different metric for sqlite3_stmt_scanstatus() to return. ** +** When the value returned to V is a string, space to hold that string is +** managed by the prepared statement S and will be automatically freed when +** S is finalized. +** **
** [[SQLITE_SCANSTAT_NLOOP]]
SQLITE_SCANSTAT_NLOOP
**
^The [sqlite3_int64] variable pointed to by the T parameter will be @@ -7522,7 +7540,14 @@ int sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Prepared Statement Scan Status ** -** Return status data for a single loop within query pStmt. +** This interface returns information about the predicted and measured +** performance for pStmt. Advanced applications can use this +** interface to compare the predicted and the measured performance and +** issue warnings and/or rerun [ANALYZE] if discrepancies are found. +** +** Since this interface is expected to be rarely used, it is only +** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] +** compile-time option. ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior @@ -7540,9 +7565,6 @@ int sqlite3_vtab_on_conflict(sqlite3 *); ** as if the loop did not exist - it returns non-zero and leave the variable ** that pOut points to unchanged. ** -** This API is only available if the library is built with pre-processor -** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. -** ** See also: [sqlite3_stmt_scanstatus_reset()] */ SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 40bdbe5d3f..a904fc62c7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -15,6 +15,14 @@ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ +/* +** Include the header file used to customize the compiler options for MSVC. +** This should be done first so that it can successfully prevent spurious +** compiler warnings due to subsequent content in this file and other files +** that are included by this file. +*/ +#include "msvc.h" + /* ** These #defines should enable >2GB file support on POSIX if the ** underlying operating system supports it. If the OS lacks @@ -1059,6 +1067,7 @@ struct sqlite3 { int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u16 dbOptFlags; /* Flags to enable/disable optimizations */ + u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ @@ -1160,7 +1169,8 @@ struct sqlite3 { /* ** A macro to discover the encoding of a database. */ -#define ENC(db) ((db)->aDb[0].pSchema->enc) +#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc) +#define ENC(db) ((db)->enc) /* ** Possible values for the sqlite3.flags. @@ -1789,7 +1799,6 @@ struct Index { u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ Expr *pPartIdxWhere; /* WHERE clause for partial indices */ - KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */ int tnum; /* DB Page containing root of this index */ LogEst szIdxRow; /* Estimated average row size in bytes */ u16 nKeyCol; /* Number of columns forming the key */ @@ -2353,7 +2362,7 @@ struct Select { #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ #define SF_Compound 0x0040 /* Part of a compound query */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ - /* 0x0100 NOT USED */ +#define SF_AllValues 0x0100 /* All terms of compound are VALUES */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ @@ -2843,6 +2852,7 @@ struct Sqlite3Config { int nPage; /* Number of pages in pPage[] */ int mxParserStack; /* maximum depth of the parser stack */ int sharedCacheEnabled; /* true if shared-cache mode enabled */ + u32 szPma; /* Maximum Sorter PMA size */ /* The above might be initialized to non-zero. The following need to always ** initially be zero, however. */ int isInit; /* True after initialization has finished */ @@ -2980,7 +2990,7 @@ int sqlite3CantopenError(int); ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. */ #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) -# define SQLITE_ENABLE_FTS3 +# define SQLITE_ENABLE_FTS3 1 #endif /* diff --git a/src/table.c b/src/table.c index 6e1df30643..235d8dd3df 100644 --- a/src/table.c +++ b/src/table.c @@ -127,7 +127,7 @@ int sqlite3_get_table( TabResult res; #ifdef SQLITE_ENABLE_API_ARMOR - if( pazResult==0 ) return SQLITE_MISUSE_BKPT; + if( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT; #endif *pazResult = 0; if( pnColumn ) *pnColumn = 0; diff --git a/src/tclsqlite.c b/src/tclsqlite.c index bb6b668000..5cdaa8cbb7 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -25,6 +25,14 @@ ** hundreds of new commands used for testing ** SQLite. This option implies -DSQLITE_TCLMD5. */ + +/* +** If requested, include the SQLite compiler options file for MSVC. +*/ +#if defined(INCLUDE_MSVC_H) +#include "msvc.h" +#endif + #include "tcl.h" #include @@ -635,6 +643,7 @@ static int DbWalHandler( Tcl_Interp *interp = pDb->interp; assert(pDb->pWalHook); + assert( db==pDb->db ); p = Tcl_DuplicateObj(pDb->pWalHook); Tcl_IncrRefCount(p); Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1)); @@ -652,9 +661,9 @@ static int DbWalHandler( #if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY) static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){ char zBuf[64]; - sprintf(zBuf, "%d", iArg); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iArg); Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); - sprintf(zBuf, "%d", nArg); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nArg); Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY); } #else @@ -1084,10 +1093,10 @@ static int dbPrepareAndBind( SqlPreparedStmt **ppPreStmt /* OUT: Object used to cache statement */ ){ const char *zSql = zIn; /* Pointer to first SQL statement in zIn */ - sqlite3_stmt *pStmt; /* Prepared statement object */ + sqlite3_stmt *pStmt = 0; /* Prepared statement object */ SqlPreparedStmt *pPreStmt; /* Pointer to cached statement */ int nSql; /* Length of zSql in bytes */ - int nVar; /* Number of variables in statement */ + int nVar = 0; /* Number of variables in statement */ int iParm = 0; /* Next free entry in apParm */ char c; int i; @@ -3101,7 +3110,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** The EXTERN macros are required by TCL in order to work on windows. */ EXTERN int Sqlite3_Init(Tcl_Interp *interp){ - int rc = Tcl_InitStubs(interp, "8.4", 0)==0 ? TCL_ERROR : TCL_OK; + int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR; if( rc==TCL_OK ){ Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0); #ifndef SQLITE_3_SUFFIX_ONLY @@ -3420,7 +3429,7 @@ static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ for(i=j=0; i<16; i+=2){ x = digest[i]*256 + digest[i+1]; if( i>0 ) zDigest[j++] = '-'; - sprintf(&zDigest[j], "%05u", x); + sqlite3_snprintf(16-j, &zDigest[j], "%05u", x); j += 5; } zDigest[j] = 0; @@ -3747,8 +3756,9 @@ static void init_all(Tcl_Interp *interp){ extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); - +#if SQLITE_ENABLE_OTA extern int SqliteOta_Init(Tcl_Interp*); +#endif #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); @@ -3792,7 +3802,9 @@ static void init_all(Tcl_Interp *interp){ Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); +#if SQLITE_ENABLE_OTA SqliteOta_Init(interp); +#endif #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); @@ -3815,6 +3827,11 @@ static void init_all(Tcl_Interp *interp){ #endif } +/* Needed for the setrlimit() system call on unix */ +#if defined(unix) +#include +#endif + #define TCLSH_MAIN main /* Needed to fake out mktclapp */ int TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; @@ -3828,6 +3845,17 @@ int TCLSH_MAIN(int argc, char **argv){ } #endif + /* Since the primary use case for this binary is testing of SQLite, + ** be sure to generate core files if we crash */ +#if defined(SQLITE_TEST) && defined(unix) + { struct rlimit x; + getrlimit(RLIMIT_CORE, &x); + x.rlim_cur = x.rlim_max; + setrlimit(RLIMIT_CORE, &x); + } +#endif /* SQLITE_TEST && unix */ + + /* Call sqlite3_shutdown() once before doing anything else. This is to ** test that sqlite3_shutdown() can be safely called by a process before ** sqlite3_initialize() is. */ diff --git a/src/test1.c b/src/test1.c index fb601badff..7f9d045dc8 100644 --- a/src/test1.c +++ b/src/test1.c @@ -94,10 +94,7 @@ static int get_sqlite_pointer( return TCL_ERROR; } p = (struct SqliteDb*)cmdInfo.objClientData; - sprintf(zBuf, "%p", p->db); - if( strncmp(zBuf,"0x",2) ){ - sprintf(zBuf, "0x%p", p->db); - } + sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p->db); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } @@ -145,7 +142,8 @@ int sqlite3TestErrCode(Tcl_Interp *interp, sqlite3 *db, int rc){ && sqlite3_errcode(db)!=rc ){ char zBuf[200]; int r2 = sqlite3_errcode(db); - sprintf(zBuf, "error code %s (%d) does not match sqlite3_errcode %s (%d)", + sqlite3_snprintf(sizeof(zBuf), zBuf, + "error code %s (%d) does not match sqlite3_errcode %s (%d)", t1ErrorName(rc), rc, t1ErrorName(r2), r2); Tcl_ResetResult(interp); Tcl_AppendResult(interp, zBuf, 0); @@ -261,6 +259,8 @@ static int test_io_trace( ** ** Returns true if the program was compiled using clang with the ** -fsanitize=address switch on the command line. False otherwise. +** +** Also return true if the OMIT_MISUSE environment variable exists. */ static int clang_sanitize_address( void *NotUsed, @@ -274,6 +274,7 @@ static int clang_sanitize_address( res = 1; # endif #endif + if( res==0 && getenv("OMIT_MISUSE")!=0 ) res = 1; Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); return TCL_OK; } @@ -307,7 +308,7 @@ static int test_exec_printf( zSql = sqlite3_mprintf(argv[2], argv[3]); rc = sqlite3_exec(db, zSql, exec_printf_cb, &str, &zErr); sqlite3_free(zSql); - sprintf(zBuf, "%d", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); Tcl_AppendElement(interp, zBuf); Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr); Tcl_DStringFree(&str); @@ -354,7 +355,7 @@ static int test_exec_hex( zSql[i] = 0; Tcl_DStringInit(&str); rc = sqlite3_exec(db, zSql, exec_printf_cb, &str, &zErr); - sprintf(zBuf, "%d", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); Tcl_AppendElement(interp, zBuf); Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr); Tcl_DStringFree(&str); @@ -439,7 +440,7 @@ static int test_exec( zSql[j] = 0; rc = sqlite3_exec(db, zSql, exec_printf_cb, &str, &zErr); sqlite3_free(zSql); - sprintf(zBuf, "%d", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); Tcl_AppendElement(interp, zBuf); Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr); Tcl_DStringFree(&str); @@ -564,7 +565,7 @@ static int test_get_table_printf( Tcl_DString str; int rc; char *zErr = 0; - int nRow, nCol; + int nRow = 0, nCol = 0; char **aResult; int i; char zBuf[30]; @@ -588,13 +589,13 @@ static int test_get_table_printf( resCount = (nRow+1)*nCol; } sqlite3_free(zSql); - sprintf(zBuf, "%d", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); Tcl_AppendElement(interp, zBuf); if( rc==SQLITE_OK ){ if( argc==4 ){ - sprintf(zBuf, "%d", nRow); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nRow); Tcl_AppendElement(interp, zBuf); - sprintf(zBuf, "%d", nCol); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nCol); Tcl_AppendElement(interp, zBuf); } for(i=0; i=5 ){ + if( rc==SQLITE_OK && zTail && objc>=5 ){ if( bytes>=0 ){ bytes = bytes - (int)(zTail-zSql); } @@ -3680,7 +3679,7 @@ static int test_prepare_v2( } if( rc!=SQLITE_OK ){ assert( pStmt==0 ); - sprintf(zBuf, "(%d) ", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "(%d) ", rc); Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); return TCL_ERROR; } @@ -3721,7 +3720,7 @@ static int test_prepare_tkt3134( if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( rc!=SQLITE_OK ){ assert( pStmt==0 ); - sprintf(zBuf, "(%d) ", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "(%d) ", rc); Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); return TCL_ERROR; } @@ -4536,7 +4535,7 @@ static int get_autocommit( return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; - sprintf(zBuf, "%d", sqlite3_get_autocommit(db)); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", sqlite3_get_autocommit(db)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } @@ -5172,7 +5171,7 @@ static int file_control_lockproxy_test( Tcl_AppendResult(interp, "PWD too big", (void*)0); return TCL_ERROR; } - sprintf(proxyPath, "%s/test.proxy", zPwd); + sqlite3_snprintf(sizeof(proxyPath), proxyPath, "%s/test.proxy", zPwd); rc = sqlite3_file_control(db, NULL, SQLITE_SET_LOCKPROXYFILE, proxyPath); if( rc ){ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); @@ -5457,7 +5456,7 @@ static int test_limit( { "SQLITE_LIMIT_TOOSMALL", -1, }, { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_WORKER_THREADS+1 }, }; - int i, id; + int i, id = 0; int val; const char *zId; @@ -5714,6 +5713,7 @@ static int test_wal_checkpoint_v2( rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ const char *zErrCode = sqlite3ErrName(rc); + Tcl_ResetResult(interp); Tcl_AppendResult(interp, zErrCode, " - ", (char *)sqlite3_errmsg(db), 0); return TCL_ERROR; } @@ -5727,6 +5727,43 @@ static int test_wal_checkpoint_v2( return TCL_OK; } +/* +** tclcmd: sqlite3_wal_autocheckpoint db VALUE +*/ +static int test_wal_autocheckpoint( + ClientData clientData, /* Unused */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + int rc; + int iVal; + + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB VALUE"); + return TCL_ERROR; + } + + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) + || Tcl_GetIntFromObj(0, objv[2], &iVal) + ){ + return TCL_ERROR; + } + + rc = sqlite3_wal_autocheckpoint(db, iVal); + Tcl_ResetResult(interp); + if( rc!=SQLITE_OK ){ + const char *zErrCode = sqlite3ErrName(rc); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zErrCode, -1)); + return TCL_ERROR; + } + + return TCL_OK; +} + + /* ** tclcmd: test_sqlite3_log ?SCRIPT? */ @@ -5939,7 +5976,8 @@ static int test_getrusage( memset(&r, 0, sizeof(r)); getrusage(RUSAGE_SELF, &r); - sprintf(buf, "ru_utime=%d.%06d ru_stime=%d.%06d ru_minflt=%d ru_majflt=%d", + sqlite3_snprintf(sizeof(buf), buf, + "ru_utime=%d.%06d ru_stime=%d.%06d ru_minflt=%d ru_majflt=%d", (int)r.ru_utime.tv_sec, (int)r.ru_utime.tv_usec, (int)r.ru_stime.tv_sec, (int)r.ru_stime.tv_usec, (int)r.ru_minflt, (int)r.ru_majflt @@ -6568,6 +6606,65 @@ static int test_user_delete( } #endif /* SQLITE_USER_AUTHENTICATION */ +/* +** tclcmd: bad_behavior TYPE +** +** Do some things that should trigger a valgrind or -fsanitize=undefined +** warning. This is used to verify that errors and warnings output by those +** tools are detected by the test scripts. +** +** TYPE BEHAVIOR +** 1 Overflow a signed integer +** 2 Jump based on an uninitialized variable +** 3 Read after free +** 4 Panic +*/ +static int 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 */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int iType; + int xyz; + int i = *(int*)clientData; + int j; + int w[10]; + int *a; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "TYPE"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &iType) ) return TCL_ERROR; + switch( iType ){ + case 1: { + xyz = 0x7fffff00 - i; + xyz += 0x100; + Tcl_SetObjResult(interp, Tcl_NewIntObj(xyz)); + break; + } + case 2: { + w[1] = 5; + if( w[i]>0 ) w[1]++; + Tcl_SetObjResult(interp, Tcl_NewIntObj(w[1])); + break; + } + case 3: { + a = malloc( sizeof(int)*10 ); + for(j=0; j<10; j++) a[j] = j; + free(a); + Tcl_SetObjResult(interp, Tcl_NewIntObj(a[i])); + break; + } + case 4: { + Tcl_Panic("Deliberate panic"); + break; + } + } + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ @@ -6584,6 +6681,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_max_blobsize; extern int sqlite3BtreeSharedCacheReport(void*, Tcl_Interp*,int,Tcl_Obj*CONST*); + static int iZero = 0; static struct { char *zName; Tcl_CmdProc *xProc; @@ -6636,6 +6734,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { + { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, @@ -6778,9 +6877,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_shared_cache_report", sqlite3BtreeSharedCacheReport, 0}, #endif { "sqlite3_libversion_number", test_libversion_number, 0 }, -#ifdef SQLITE_ENABLE_COLUMN_METADATA { "sqlite3_table_column_metadata", test_table_column_metadata, 0 }, -#endif #ifndef SQLITE_OMIT_INCRBLOB { "sqlite3_blob_reopen", test_blob_reopen, 0 }, #endif @@ -6790,6 +6887,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ #endif { "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 }, { "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 }, + { "sqlite3_wal_autocheckpoint",test_wal_autocheckpoint, 0 }, { "test_sqlite3_log", test_sqlite3_log, 0 }, #ifndef SQLITE_OMIT_EXPLAIN { "print_explain_query_plan", test_print_eqp, 0 }, diff --git a/src/test2.c b/src/test2.c index 58f271ff27..7192ddfffb 100644 --- a/src/test2.c +++ b/src/test2.c @@ -310,7 +310,7 @@ static int page_get( ){ Pager *pPager; char zBuf[100]; - DbPage *pPage; + DbPage *pPage = 0; int pgno; int rc; if( argc!=3 ){ diff --git a/src/test3.c b/src/test3.c index e3ed310c81..07d12d28c0 100644 --- a/src/test3.c +++ b/src/test3.c @@ -445,18 +445,21 @@ static int btree_varint_test( char zErr[200]; n1 = putVarint(zBuf, in); if( n1>9 || n1<1 ){ - sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1); + sqlite3_snprintf(sizeof(zErr), zErr, + "putVarint returned %d - should be between 1 and 9", n1); Tcl_AppendResult(interp, zErr, 0); return TCL_ERROR; } n2 = getVarint(zBuf, &out); if( n1!=n2 ){ - sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2); + sqlite3_snprintf(sizeof(zErr), zErr, + "putVarint returned %d and getVarint returned %d", n1, n2); Tcl_AppendResult(interp, zErr, 0); return TCL_ERROR; } if( in!=out ){ - sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out); + sqlite3_snprintf(sizeof(zErr), zErr, + "Wrote 0x%016llx and got back 0x%016llx", in, out); Tcl_AppendResult(interp, zErr, 0); return TCL_ERROR; } @@ -465,13 +468,15 @@ static int btree_varint_test( n2 = getVarint32(zBuf, out32); out = out32; if( n1!=n2 ){ - sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d", + sqlite3_snprintf(sizeof(zErr), zErr, + "putVarint returned %d and GetVarint32 returned %d", n1, n2); Tcl_AppendResult(interp, zErr, 0); return TCL_ERROR; } if( in!=out ){ - sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32", + sqlite3_snprintf(sizeof(zErr), zErr, + "Wrote 0x%016llx and got back 0x%016llx from GetVarint32", in, out); Tcl_AppendResult(interp, zErr, 0); return TCL_ERROR; diff --git a/src/test4.c b/src/test4.c index a6375c7cc4..d689030303 100644 --- a/src/test4.c +++ b/src/test4.c @@ -270,7 +270,7 @@ static int tcl_thread_argc( return TCL_ERROR; } thread_wait(&threadset[i]); - sprintf(zBuf, "%d", threadset[i].argc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } diff --git a/src/test7.c b/src/test7.c index 93bf1e4898..6ba3631b41 100644 --- a/src/test7.c +++ b/src/test7.c @@ -315,7 +315,7 @@ static int tcl_client_argc( return TCL_ERROR; } client_wait(&threadset[i]); - sprintf(zBuf, "%d", threadset[i].argc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } diff --git a/src/test8.c b/src/test8.c index 8bc835d638..2107710a99 100644 --- a/src/test8.c +++ b/src/test8.c @@ -206,8 +206,8 @@ static int getColumnNames( zSpace = (char *)(&aCol[nCol]); for(ii=0; iiinterp; - int nRow; + int nRow = 0; int useIdx = 0; int rc = SQLITE_OK; int useCost = 0; - double cost; + double cost = 0; int isIgnoreUsable = 0; if( Tcl_GetVar(interp, "echo_module_ignore_usable", TCL_GLOBAL_ONLY) ){ isIgnoreUsable = 1; @@ -927,7 +927,7 @@ int echoUpdate( sqlite3 *db = pVtab->db; int rc = SQLITE_OK; - sqlite3_stmt *pStmt; + sqlite3_stmt *pStmt = 0; char *z = 0; /* SQL statement to execute */ int bindArgZero = 0; /* True to bind apData[0] to sql var no. nData */ int bindArgOne = 0; /* True to bind apData[1] to sql var no. 1 */ diff --git a/src/test_config.c b/src/test_config.c index 93f6083838..6f5e14ee68 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -41,7 +41,7 @@ ** procedures use this to determine when tests should be omitted. */ static void set_options(Tcl_Interp *interp){ -#ifdef HAVE_MALLOC_USABLE_SIZE +#if HAVE_MALLOC_USABLE_SIZE Tcl_SetVar2(interp, "sqlite_options", "malloc_usable_size", "1", TCL_GLOBAL_ONLY); #else diff --git a/src/test_journal.c b/src/test_journal.c index e8701a4eea..6e320b7abb 100644 --- a/src/test_journal.c +++ b/src/test_journal.c @@ -409,7 +409,9 @@ static int openTransaction(jt_file *pMain, jt_file *pJournal){ if( iOff==PENDING_BYTE ) continue; rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff); pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize); - if( ii+1==pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ) rc = SQLITE_OK; + if( ii+1==(int)pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; + } } } @@ -550,7 +552,8 @@ static int jtWrite( */ }else{ u32 pgno = (u32)(iOfst/p->nPagesize + 1); - assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); + assert( (iAmt==1||iAmt==(int)p->nPagesize) && + ((iOfst+iAmt)%p->nPagesize)==0 ); assert( pgno<=p->nPage || p->nSync>0 ); assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); } diff --git a/src/test_malloc.c b/src/test_malloc.c index bd0a3d1ffd..1ea4de5063 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -1260,6 +1260,34 @@ static int test_config_cis( return TCL_OK; } +/* +** Usage: sqlite3_config_pmasz INTEGER +** +** Set the minimum PMA size. +*/ +static int test_config_pmasz( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + int iPmaSz; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &iPmaSz) ){ + return TCL_ERROR; + } + + rc = sqlite3_config(SQLITE_CONFIG_PMASZ, iPmaSz); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); + + return TCL_OK; +} + /* ** Usage: sqlite3_dump_memsys3 FILENAME @@ -1310,7 +1338,7 @@ static int test_status( Tcl_Obj *CONST objv[] ){ int rc, iValue, mxValue; - int i, op, resetFlag; + int i, op = 0, resetFlag; const char *zOpName; static const struct { const char *zName; @@ -1367,7 +1395,7 @@ static int test_db_status( Tcl_Obj *CONST objv[] ){ int rc, iValue, mxValue; - int i, op, resetFlag; + int i, op = 0, resetFlag; const char *zOpName; sqlite3 *db; extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); @@ -1514,6 +1542,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_config_uri", test_config_uri ,0 }, { "sqlite3_config_cis", test_config_cis ,0 }, + { "sqlite3_config_pmasz", test_config_pmasz ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, diff --git a/src/test_multiplex.c b/src/test_multiplex.c index 99819371ce..8f204c6694 100644 --- a/src/test_multiplex.c +++ b/src/test_multiplex.c @@ -406,7 +406,7 @@ static void multiplexControlFunc( ){ int rc = SQLITE_OK; sqlite3 *db = sqlite3_context_db_handle(context); - int op; + int op = 0; int iVal; if( !db || argc!=2 ){ @@ -535,7 +535,7 @@ static int multiplexOpen( /* assign pointers to extra space allocated */ memset(pGroup, 0, sz); pMultiplexOpen->pGroup = pGroup; - pGroup->bEnabled = -1; + pGroup->bEnabled = (unsigned char)-1; pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate", (flags & SQLITE_OPEN_MAIN_DB)==0); pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize", diff --git a/src/test_quota.c b/src/test_quota.c index 80ebd0589e..e8e0b34072 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -889,7 +889,7 @@ int sqlite3_quota_set( ** management, update its size. */ int sqlite3_quota_file(const char *zFilename){ - char *zFull; + char *zFull = 0; sqlite3_file *fd; int rc; int outFlags = 0; diff --git a/src/test_sqllog.c b/src/test_sqllog.c index 4aa68b7c42..6c0bf954ba 100644 --- a/src/test_sqllog.c +++ b/src/test_sqllog.c @@ -365,8 +365,10 @@ static void sqllogOpenlog(struct SLConn *p){ FILE *fd; char *zVar = getenv(ENVIRONMENT_VARIABLE1_NAME); if( zVar==0 || strlen(zVar)+10>=(sizeof(sqllogglobal.zPrefix)) ) return; - sprintf(sqllogglobal.zPrefix, "%s/sqllog_%d", zVar, getProcessId()); - sprintf(sqllogglobal.zIdx, "%s.idx", sqllogglobal.zPrefix); + sqlite3_snprintf(sizeof(sqllogglobal.zPrefix), sqllogglobal.zPrefix, + "%s/sqllog_%d", zVar, getProcessId()); + sqlite3_snprintf(sizeof(sqllogglobal.zIdx), sqllogglobal.zIdx, + "%s.idx", sqllogglobal.zPrefix); if( getenv(ENVIRONMENT_VARIABLE2_NAME) ){ sqllogglobal.bReuse = atoi(getenv(ENVIRONMENT_VARIABLE2_NAME)); } diff --git a/src/test_thread.c b/src/test_thread.c index 2f9363b750..a4d96e1942 100644 --- a/src/test_thread.c +++ b/src/test_thread.c @@ -608,7 +608,7 @@ static int blocking_prepare_v2_proc( } if( rc!=SQLITE_OK ){ assert( pStmt==0 ); - sprintf(zBuf, "%s ", (char *)sqlite3ErrName(rc)); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%s ", (char *)sqlite3ErrName(rc)); Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); return TCL_ERROR; } diff --git a/src/test_vfs.c b/src/test_vfs.c index 7ee2a93453..561addfcc5 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -421,7 +421,7 @@ static int tvfsSync(sqlite3_file *pFile, int flags){ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){ - char *zFlags; + char *zFlags = 0; switch( flags ){ case SQLITE_SYNC_NORMAL: @@ -823,11 +823,12 @@ static int tvfsShmOpen(sqlite3_file *pFile){ if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; } if( !pBuffer ){ - int nByte = sizeof(TestvfsBuffer) + (int)strlen(pFd->zFilename) + 1; + int szName = (int)strlen(pFd->zFilename); + int nByte = sizeof(TestvfsBuffer) + szName + 1; pBuffer = (TestvfsBuffer *)ckalloc(nByte); memset(pBuffer, 0, nByte); pBuffer->zFile = (char *)&pBuffer[1]; - strcpy(pBuffer->zFile, pFd->zFilename); + memcpy(pBuffer->zFile, pFd->zFilename, szName+1); pBuffer->pNext = p->pBuffer; p->pBuffer = pBuffer; } @@ -1225,7 +1226,7 @@ static int testvfs_obj_cmd( case CMD_CANTOPENERR: case CMD_IOERR: case CMD_FULLERR: { - TestFaultInject *pTest; + TestFaultInject *pTest = 0; int iRet; switch( aSubcmd[i].eCmd ){ diff --git a/src/threads.c b/src/threads.c index 18d7320a12..4ce6122274 100644 --- a/src/threads.c +++ b/src/threads.c @@ -26,6 +26,9 @@ ** single-threaded if desired. */ #include "sqliteInt.h" +#if SQLITE_OS_WIN +# include "os_win.h" +#endif #if SQLITE_MAX_WORKER_THREADS>0 @@ -98,7 +101,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ /********************************* Win32 Threads ****************************/ -#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 +#if SQLITE_OS_WIN_THREADS #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ #include @@ -191,7 +194,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; } -#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */ +#endif /* SQLITE_OS_WIN_THREADS */ /******************************** End Win32 Threads *************************/ diff --git a/src/tokenize.c b/src/tokenize.c index 5bb9155460..f0360eef61 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -391,6 +391,9 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ int mxSqlLen; /* Max length of an SQL string */ +#ifdef SQLITE_ENABLE_API_ARMOR + if( zSql==0 || pzErrMsg==0 ) return SQLITE_MISUSE_BKPT; +#endif mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->nVdbeActive==0 ){ db->u1.isInterrupted = 0; diff --git a/src/util.c b/src/util.c index ab409fa256..6e64242e87 100644 --- a/src/util.c +++ b/src/util.c @@ -17,7 +17,7 @@ */ #include "sqliteInt.h" #include -#ifdef SQLITE_HAVE_ISNAN +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN # include #endif @@ -58,7 +58,7 @@ int sqlite3FaultSim(int iTest){ */ int sqlite3IsNaN(double x){ int rc; /* The value return */ -#if !defined(SQLITE_HAVE_ISNAN) +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN /* ** Systems that support the isnan() library function should probably ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have @@ -88,9 +88,9 @@ int sqlite3IsNaN(double x){ volatile double y = x; volatile double z = y; rc = (y!=z); -#else /* if defined(SQLITE_HAVE_ISNAN) */ +#else /* if HAVE_ISNAN */ rc = isnan(x); -#endif /* SQLITE_HAVE_ISNAN */ +#endif /* HAVE_ISNAN */ testcase( rc ); return rc; } diff --git a/src/vdbe.c b/src/vdbe.c index 3dac74dfd4..1e0ff96af4 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3823,8 +3823,8 @@ case OP_Found: { /* jump, in3 */ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ - for(ii=0; iinField; ii++){ + if( pIdxKey->aMem[ii].flags & MEM_Null ){ pc = pOp->p2 - 1; VdbeBranchTaken(1,2); break; } diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 5744c28632..21c537d776 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -400,7 +400,10 @@ static int doWalCallbacks(sqlite3 *db){ for(i=0; inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); + int nEntry; + sqlite3BtreeEnter(pBt); + nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); + sqlite3BtreeLeave(pBt); if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); } @@ -580,7 +583,6 @@ int sqlite3_step(sqlite3_stmt *pStmt){ ** sqlite3_errmsg() and sqlite3_errcode(). */ const char *zErr = (const char *)sqlite3_value_text(db->pErr); - assert( zErr!=0 || db->mallocFailed ); sqlite3DbFree(db, v->zErrMsg); if( !db->mallocFailed ){ v->zErrMsg = sqlite3DbStrDup(db, zErr); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 7900bd52ac..cd9ecaa691 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -396,6 +396,7 @@ static Op *opIterNext(VdbeOpIter *p){ */ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ int hasAbort = 0; + int hasFkCounter = 0; Op *pOp; VdbeOpIter sIter; memset(&sIter, 0, sizeof(sIter)); @@ -404,15 +405,17 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ while( (pOp = opIterNext(&sIter))!=0 ){ int opcode = pOp->opcode; if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename -#ifndef SQLITE_OMIT_FOREIGN_KEY - || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) -#endif || ((opcode==OP_Halt || opcode==OP_HaltIfNull) && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) ){ hasAbort = 1; break; } +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){ + hasFkCounter = 1; + } +#endif } sqlite3DbFree(v->db, sIter.apSub); @@ -421,7 +424,7 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ ** through all opcodes and hasAbort may be set incorrectly. Return ** true for this case to prevent the assert() in the callers frame ** from failing. */ - return ( v->db->mallocFailed || hasAbort==mayAbort ); + return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter ); } #endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ @@ -3346,6 +3349,42 @@ debugCompareEnd: } #endif +#if SQLITE_DEBUG +/* +** Count the number of fields (a.k.a. columns) in the record given by +** pKey,nKey. The verify that this count is less than or equal to the +** limit given by pKeyInfo->nField + pKeyInfo->nXField. +** +** If this constraint is not satisfied, it means that the high-speed +** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will +** not work correctly. If this assert() ever fires, it probably means +** that the KeyInfo.nField or KeyInfo.nXField values were computed +** incorrectly. +*/ +static void vdbeAssertFieldCountWithinLimits( + int nKey, const void *pKey, /* The record to verify */ + const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ +){ + int nField = 0; + u32 szHdr; + u32 idx; + u32 notUsed; + const unsigned char *aKey = (const unsigned char*)pKey; + + if( CORRUPT_DB ) return; + idx = getVarint32(aKey, szHdr); + assert( nKey>=0 ); + assert( szHdr<=(u32)nKey ); + while( idxnField+pKeyInfo->nXField ); +} +#else +# define vdbeAssertFieldCountWithinLimits(A,B,C) +#endif + /* ** Both *pMem1 and *pMem2 contain string values. Compare the two values ** using the collation sequence pColl. As usual, return a negative , zero @@ -3757,6 +3796,7 @@ static int vdbeRecordCompareInt( i64 v = pPKey2->aMem[0].u.i; i64 lhs; + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); switch( serial_type ){ case 1: { /* 1-byte signed integer */ @@ -3844,6 +3884,7 @@ static int vdbeRecordCompareString( int serial_type; int res; + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); getVarint32(&aKey1[1], serial_type); if( serial_type<12 ){ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ diff --git a/src/vdbesort.c b/src/vdbesort.c index 7c736adefe..5a43a10542 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -152,7 +152,7 @@ ** to a level 0 PMA. The purpose of this limit is to prevent various integer ** overflows. 512MiB. */ -#define SQLITE_MAX_MXPMASIZE (1<<29) +#define SQLITE_MAX_PMASZ (1<<29) /* ** Private objects used by the sorter @@ -448,9 +448,6 @@ struct SorterRecord { */ #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) -/* The minimum PMA size is set to this value multiplied by the database -** page size in bytes. */ -#define SORTER_MIN_WORKING 10 /* Maximum number of PMAs that a single MergeEngine can merge */ #define SORTER_MAX_MERGE_COUNT 16 @@ -849,10 +846,11 @@ int sqlite3VdbeSorterInit( } if( !sqlite3TempInMemory(db) ){ - pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz; + u32 szPma = sqlite3GlobalConfig.szPma; + pSorter->mnPmaSize = szPma * pgsz; mxCache = db->aDb[0].pSchema->cache_size; - if( mxCachemxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE); + if( mxCache<(int)szPma ) mxCache = (int)szPma; + pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ); /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary @@ -1130,12 +1128,12 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){ */ static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){ if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){ - int rc = sqlite3OsTruncate(pFd, nByte); - if( rc==SQLITE_OK ){ - void *p = 0; - sqlite3OsFetch(pFd, 0, (int)nByte, &p); - sqlite3OsUnfetch(pFd, 0, p); - } + void *p = 0; + int chunksize = 4*1024; + sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); + sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); + sqlite3OsFetch(pFd, 0, (int)nByte, &p); + sqlite3OsUnfetch(pFd, 0, p); } } #else @@ -2416,6 +2414,7 @@ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){ }else #endif /*if( !pSorter->bUseThreads )*/ { + assert( pSorter->pMerger!=0 ); assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); rc = vdbeMergeEngineStep(pSorter->pMerger, pbEof); } diff --git a/src/wal.c b/src/wal.c index 726afbeb2a..fcffa0f281 100644 --- a/src/wal.c +++ b/src/wal.c @@ -2529,7 +2529,7 @@ int sqlite3WalFindFrame( for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ u32 iFrame = aHash[iKey] + iZero; if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ - /* assert( iFrame>iRead ); -- not true if there is corruption */ + assert( iFrame>iRead || CORRUPT_DB ); iRead = iFrame; } if( (nCollide--)==0 ){ @@ -3295,6 +3295,7 @@ int sqlite3WalCheckpointStart( *ppCkpt = (sqlite3_ckpt*)p; return rc; } +#endif /* SQLITE_ENABLE_OTA */ /* ** Unless the wal file is empty, check that the 8 bytes of salt stored in @@ -3313,7 +3314,6 @@ int sqlite3WalCheckSalt(Wal *pWal, sqlite3_file *pFd){ } return rc; } -#endif /* SQLITE_ENABLE_OTA */ /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since diff --git a/src/where.c b/src/where.c index ee42534f34..183a8cb667 100644 --- a/src/where.c +++ b/src/where.c @@ -3941,7 +3941,6 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ p->u.vtab.idxStr = 0; }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ sqlite3DbFree(db, p->u.btree.pIndex->zColAff); - sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo); sqlite3DbFree(db, p->u.btree.pIndex); p->u.btree.pIndex = 0; } diff --git a/test/bigsort.test b/test/bigsort.test index 259adc3747..c711515973 100644 --- a/test/bigsort.test +++ b/test/bigsort.test @@ -20,6 +20,15 @@ set testprefix bigsort # loop if the product was also an integer multiple of 2^32, or # inefficiency otherwise. # +# This test causes thrashing on machines with smaller amounts of +# memory. Make sure the host has at least 8GB available before running +# this test. +# +if {[catch {exec free | grep Mem:} out] || [lindex $out 1]<8000000} { + finish_test + return +} + do_execsql_test 1.0 { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); @@ -39,5 +48,3 @@ do_execsql_test 1.1 { finish_test - - diff --git a/test/colmeta.test b/test/colmeta.test index 3939f8228a..21b0e0f38a 100644 --- a/test/colmeta.test +++ b/test/colmeta.test @@ -17,17 +17,14 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !columnmetadata { - finish_test - return -} - # Set up a schema in the main and temp test databases. do_test colmeta-0 { execsql { CREATE TABLE abc(a, b, c); CREATE TABLE abc2(a PRIMARY KEY COLLATE NOCASE, b VARCHAR(32), c); CREATE TABLE abc3(a NOT NULL, b INTEGER PRIMARY KEY, c); + CREATE TABLE abc5(w,x,y,z,PRIMARY KEY(x,z)) WITHOUT ROWID; + CREATE TABLE abc6(rowid TEXT COLLATE rtrim, oid REAL, _rowid_ BLOB); } ifcapable autoinc { execsql { @@ -57,19 +54,27 @@ set tests { 13 {main abc rowid} {0 {INTEGER BINARY 0 1 0}} 14 {main abc3 rowid} {0 {INTEGER BINARY 0 1 0}} 16 {main abc d} {1 {no such table column: abc.d}} + 20 {main abc5 w} {0 {{} BINARY 0 0 0}} + 21 {main abc5 x} {0 {{} BINARY 1 1 0}} + 22 {main abc5 y} {0 {{} BINARY 0 0 0}} + 23 {main abc5 z} {0 {{} BINARY 1 1 0}} + 24 {main abc5 rowid} {1 {no such table column: abc5.rowid}} + 30 {main abc6 rowid} {0 {TEXT rtrim 0 0 0}} + 31 {main abc6 oid} {0 {REAL BINARY 0 0 0}} + 32 {main abc6 _rowid_} {0 {BLOB BINARY 0 0 0}} } -ifcapable view { +ifcapable autoinc { set tests [concat $tests { - 8 {{} abc4 b} {0 {INTEGER BINARY 0 1 1}} - 15 {main abc4 rowid} {0 {INTEGER BINARY 0 1 1}} + 100 {{} abc4 b} {0 {INTEGER BINARY 0 1 1}} + 101 {main abc4 rowid} {0 {INTEGER BINARY 0 1 1}} }] } ifcapable view { set tests [concat $tests { - 9 {{} v1 a} {1 {no such table column: v1.a}} - 10 {main v1 b} {1 {no such table column: v1.b}} - 11 {main v1 badname} {1 {no such table column: v1.badname}} - 12 {main v1 rowid} {1 {no such table column: v1.rowid}} + 200 {{} v1 a} {1 {no such table column: v1.a}} + 201 {main v1 b} {1 {no such table column: v1.b}} + 202 {main v1 badname} {1 {no such table column: v1.badname}} + 203 {main v1 rowid} {1 {no such table column: v1.rowid}} }] } @@ -91,4 +96,15 @@ foreach {tn params results} $tests { } $results } +# Calling sqlite3_table_column_metadata with a NULL column name merely +# checks for the existance of the table. +# +do_test colmeta-300 { + catch {sqlite3_table_column_metadata $::DB main xyzzy} res +} {1} +do_test colmeta-301 { + catch {sqlite3_table_column_metadata $::DB main abc} res +} {0} + + finish_test diff --git a/test/e_walauto.test b/test/e_walauto.test new file mode 100644 index 0000000000..b624b2469c --- /dev/null +++ b/test/e_walauto.test @@ -0,0 +1,202 @@ +# 2014 December 04 +# +# 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/wal_common.tcl +set testprefix e_walauto + + +proc read_nbackfill {} { + seek $::shmfd 96 + binary scan [read $::shmfd 4] n nBackfill + set nBackfill +} +proc read_mxframe {} { + seek $::shmfd 16 + binary scan [read $::shmfd 4] n mxFrame + set mxFrame +} + +# Assuming that the main db for database handle +# +proc do_autocommit_threshold_test {tn value} { + + set nBackfillSaved [read_nbackfill] + while {1} { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + if {[read_mxframe] >= $value} break + } + + set nBackfillNew [read_nbackfill] + uplevel [list do_test $tn "expr $nBackfillNew > $nBackfillSaved" 1] +} + +# EVIDENCE-OF: R-30135-06439 The wal_autocheckpoint pragma can be used +# to invoke this interface from SQL. +# +# All tests in this file are run twice - once using the +# sqlite3_wal_autocheckpoint() API, and once using "PRAGMA +# wal_autocheckpoint". +# +foreach {tn code} { + 1 { + proc autocheckpoint {db value} { + uplevel [list $db eval "PRAGMA wal_autocheckpoint = $value"] + } + } + + 2 { + proc autocheckpoint {db value} { + uplevel [list sqlite3_wal_autocheckpoint $db $value] + return $value + } + } +} { + + eval $code + + reset_db + execsql { PRAGMA auto_vacuum = 0 } + do_execsql_test 1.$tn.0 { PRAGMA journal_mode = WAL } {wal} + do_execsql_test 1.$tn.1 { CREATE TABLE t1(a, b) } + set shmfd [open "test.db-shm" rb] + + # EVIDENCE-OF: R-41531-51083 Every new database connection defaults to + # having the auto-checkpoint enabled with a threshold of 1000 or + # SQLITE_DEFAULT_WAL_AUTOCHECKPOINT pages. + # + do_autocommit_threshold_test 1.$tn.2 1000 + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + do_autocommit_threshold_test 1.$tn.3 1000 + + # EVIDENCE-OF: R-38128-34102 The sqlite3_wal_autocheckpoint(D,N) is a + # wrapper around sqlite3_wal_hook() that causes any database on database + # connection D to automatically checkpoint after committing a + # transaction if there are N or more frames in the write-ahead log file. + # + do_test 1.$tn.4 { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + autocheckpoint db 100 + } {100} + do_autocommit_threshold_test 1.$tn.5 100 + + do_test 1.$tn.6 { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + autocheckpoint db 500 + } {500} + do_autocommit_threshold_test 1.$tn.7 500 + + # EVIDENCE-OF: R-26993-43540 Passing zero or a negative value as the + # nFrame parameter disables automatic checkpoints entirely. + # + do_test 1.$tn.7 { + autocheckpoint db 0 ;# Set to zero + for {set i 0} {$i < 10000} {incr i} { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + } + expr {[file size test.db-wal] > (5 * 1024 * 1024)} + } 1 + do_test 1.$tn.8 { + sqlite3_wal_checkpoint_v2 db truncate + file size test.db-wal + } 0 + do_test 1.$tn.9 { + autocheckpoint db -4 ;# Set to a negative value + for {set i 0} {$i < 10000} {incr i} { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + } + expr {[file size test.db-wal] > (5 * 1024 * 1024)} + } 1 + + # EVIDENCE-OF: R-10203-42688 The callback registered by this function + # replaces any existing callback registered using sqlite3_wal_hook(). + # + set ::wal_hook_callback 0 + proc wal_hook_callback {args} { incr ::wal_hook_callback ; return 0 } + do_test 1.$tn.10.1 { + db wal_hook wal_hook_callback + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + set ::wal_hook_callback + } 2 + do_test 1.$tn.10.2 { + autocheckpoint db 100 + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + set ::wal_hook_callback + } 2 + + # EVIDENCE-OF: R-17497-43474 Likewise, registering a callback using + # sqlite3_wal_hook() disables the automatic checkpoint mechanism + # configured by this function. + do_test 1.$tn.11.1 { + sqlite3_wal_checkpoint_v2 db truncate + file size test.db-wal + } 0 + do_test 1.$tn.11.2 { + autocheckpoint db 100 + for {set i 0} {$i < 1000} {incr i} { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + } + expr {[file size test.db-wal] < (1 * 1024 * 1024)} + } 1 + do_test 1.$tn.11.3 { + db wal_hook wal_hook_callback + for {set i 0} {$i < 1000} {incr i} { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + } + expr {[file size test.db-wal] < (1 * 1024 * 1024)} + } 0 + + # EVIDENCE-OF: R-33080-59193 Checkpoints initiated by this mechanism + # are PASSIVE. + # + set ::busy_callback_count 0 + proc busy_callback {args} { + puts Hello + incr ::busy_callback_count + return 0 + } + do_test 1.$tn.12.1 { + sqlite3_wal_checkpoint_v2 db truncate + autocheckpoint db 100 + db busy busy_callback + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + } {} + do_test 1.$tn.12.2 { + sqlite3 db2 test.db + db2 eval { BEGIN; SELECT * FROM t1 LIMIT 10; } + read_nbackfill + } {0} + do_test 1.$tn.12.3 { + for {set i 0} {$i < 1000} {incr i} { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + } + read_nbackfill + } {2} + do_test 1.$tn.12.4 { + set ::busy_callback_count + } {0} + db2 close + + do_test 1.$tn.12.5 { + db eval { INSERT INTO t1 VALUES(randomblob(100), randomblob(100)) } + read_nbackfill + } {1559} + + db close + close $shmfd +} + +finish_test diff --git a/test/e_walckpt.test b/test/e_walckpt.test index 8b0aae798a..e022f840cf 100644 --- a/test/e_walckpt.test +++ b/test/e_walckpt.test @@ -42,6 +42,38 @@ proc compare_db_hashes {} { set ret } +#------------------------------------------------------------------------- +# All calls to the [sqlite3_wal_checkpoint_v2] command made within this +# file use this wrapper. It's sole purpose is to throw an error if the +# following requirement is violated: +# +# EVIDENCE-OF: R-60567-47780 Unless it returns SQLITE_MISUSE, the +# sqlite3_wal_checkpoint_v2() interface sets the error information that +# is queried by sqlite3_errcode() and sqlite3_errmsg(). +# +proc wal_checkpoint_v2 {db args} { + set rc [catch { + uplevel sqlite3_wal_checkpoint_v2 $db $args + } msg] + + set errcode "SQLITE_OK" + if {$rc} { + set errcode [lindex [split $msg " "] 0] + } elseif { [lindex $msg 0] } { + set errcode "SQLITE_BUSY" + } + + if {$errcode != "SQLITE_MISUSE" && [sqlite3_errcode $db] != $errcode} { + error "sqlite3_errcode mismatch! (1) $errcode!=[sqlite3_errcode $db]" + } + + if {$rc==0} { + return $msg + } else { + error $msg + } +} + # The following tests are run 3 times, each using a different method of # invoking a checkpoint: @@ -63,15 +95,15 @@ proc compare_db_hashes {} { foreach {tn script} { 1 { proc checkpoint {db mode args} { - eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args + eval wal_checkpoint_v2 [list $db] [list $mode] $args } } 2 { proc checkpoint {db mode args} { - set sql "PRAGMA wal_checkpoint" + set sql "PRAGMA wal_checkpoint = $mode" if {[llength $args] && [lindex $args 0]!=""} { - set sql "PRAGMA [lindex $args 0].wal_checkpoint" + set sql "PRAGMA [lindex $args 0].wal_checkpoint = $mode" } set rc [catch { $db eval $sql } msg] if {$rc} { @@ -90,7 +122,7 @@ foreach {tn script} { error "$rc - [sqlite3_errmsg $db]" } } else { - eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args + eval wal_checkpoint_v2 [list $db] [list $mode] $args } } } @@ -215,6 +247,7 @@ foreach {tn script} { do_test $tn.3.2.1 { db2 eval { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1,2); @@ -261,6 +294,306 @@ foreach {tn script} { db2 close tvfs delete + proc busy_handler {mode busy_handler_mode n} { + incr ::busy_handler_counter + switch -- $busy_handler_mode { + 1 { + # Do nothing. Do not block. + return 1 + } + + 2 { + # Close first the reader, then later the writer. Give up before + # closing the [db6] reader. + if {$n==5} { catch {db2 eval commit} } + if {$n==10} { catch {db3 eval commit} } + if {$n==15} { return 1 } + return 0 + } + + 3 { + # Close first the writer, then later the reader. And finally the + # [db6] reader. + if {$n==5} { catch {db2 eval commit} } + if {$n==10} { catch {db3 eval commit} } + if {$n==15} { catch {db6 eval commit} } + return 0 + } + } + } + + foreach {mode busy_handler_mode} { + passive 1 + full 1 full 2 full 3 + restart 1 restart 2 restart 3 + truncate 1 truncate 2 truncate 3 + } { + set tp "$tn.$mode.$busy_handler_mode" + + set ::sync_counter 0 + + # Set up a callback function for xSync and xWrite calls made during + # the checkpoint. + # + set ::checkpoint_ongoing 0 + proc tvfs_callback {method args} { + if {$::checkpoint_ongoing==0} return + + set tail [file tail [lindex $args 0]] + if {$method == "xSync" && $tail == "test.db"} { + incr ::sync_counter + } + if {$method == "xWrite" && $tail=="test.db"} { + if {$::write_ok < 0} { + set ::write_ok [expr ![catch {db5 eval { BEGIN IMMEDIATE }}]] + catch { db5 eval ROLLBACK } + } + if {$::read_ok < 0} { + set ::read_ok [expr ![catch {db5 eval { SELECT * FROM t1 }}]] + } + + # If one has not already been opened, open a read-transaction using + # connection [db6] + catch { db6 eval { BEGIN ; SELECT * FROM sqlite_master } } msg + } + if {$method == "xShmLock" } { + set details [lindex $args 2] + if {$details == "0 1 lock exclusive"} { set ::seen_writer_lock 1 } + } + } + + catch { db close } + forcedelete test.db + testvfs tvfs + sqlite3 db test.db -vfs tvfs + #tvfs filter xSync + tvfs script tvfs_callback + + do_execsql_test $tp.0 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + INSERT INTO t1 VALUES(5, 6); + } {wal} + + # Open a reader on the current database snapshot. + do_test $tp.1 { + sqlite3 db2 test.db -vfs tvfs + execsql { + BEGIN; + SELECT * FROM t1 UNION ALL SELECT * FROM t2; + } db2 + } {1 2 3 4 5 6} + + # Open a writer. Write a transaction. Then begin, but do not commit, + # a second transaction. + do_test $tp.2 { + sqlite3 db3 test.db -vfs tvfs + execsql { + INSERT INTO t2 VALUES(7, 8); + BEGIN; + INSERT INTO t2 VALUES(9, 10); + SELECT * FROM t1 UNION ALL SELECT * FROM t2; + } db3 + } {1 2 3 4 5 6 7 8 9 10} + + sqlite3 db5 test.db -vfs tvfs + sqlite3 db6 test.db -vfs tvfs + + # Register a busy-handler with connection [db]. + # + db busy [list busy_handler $mode $busy_handler_mode] + set ::sync_counter 0 + set ::busy_handler_counter 0 + set ::read_ok -1 + set ::write_ok -1 + set ::seen_writer_lock 0 + + set ::checkpoint_ongoing 1 + do_test $tp.3 { + checkpoint db $mode main + set {} {} + } {} + set ::checkpoint_ongoing 0 + set ::did_restart_blocking [expr {[catch {db6 eval commit}]}] + + if { $mode=="passive" } { + # EVIDENCE-OF: R-16333-64433 Checkpoint as many frames as possible + # without waiting for any database readers or writers to finish, then + # sync the database file if all frames in the log were checkpointed. + # + # "As many frames as possible" means all but the last two transactions + # (the two that write to table t2, of which the scond is unfinished). + # So copying the db file only we see the t1 change, but not the t2 + # modifications. + # + # The busy handler is not invoked (see below) and the db reader and + # writer are still active - so the checkpointer did not wait for either + # readers or writers. As a result the checkpoint was not finished and + # so the db file is not synced. + # + # EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked + # in the SQLITE_CHECKPOINT_PASSIVE mode. + # + # It's not. Test case "$tp.6". + # + do_test $tp.4 { + forcecopy test.db abc.db + sqlite3 db4 abc.db + db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } + } {1 2 3 4 5 6} + do_test $tp.5 { set ::sync_counter } 0 + do_test $tp.6 { set ::busy_handler_counter } 0 + db4 close + + db2 eval COMMIT + db3 eval COMMIT + + # EVIDENCE-OF: R-65499-53765 On the other hand, passive mode might leave + # the checkpoint unfinished if there are concurrent readers or writers. + # + # The reader and writer have now dropped their locks. And so a + # checkpoint now is able to checkpoint more frames. Showing that the + # attempt above was left "unfinished". + # + # Also, because the checkpoint finishes this time, the db is synced. + # Which is part of R-16333-64433 above. + # + set ::checkpoint_ongoing 1 + do_test $tp.7 { + checkpoint db $mode main + forcecopy test.db abc.db + sqlite3 db4 abc.db + db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } + } {1 2 3 4 5 6 7 8 9 10} + set ::checkpoint_ongoing 0 + do_test $tp.7 { set ::sync_counter } 1 + do_test $tp.8 { set ::busy_handler_counter } 0 + db4 close + } + + if { $mode=="full" || $mode=="restart" || $mode=="truncate" } { + + # EVIDENCE-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and + # TRUNCATE modes also obtain the exclusive "writer" lock on the + # database file. + # + # Or at least attempts to obtain. + # + do_test $tp.9 { + set ::seen_writer_lock + } {1} + + if {$busy_handler_mode==2 || $busy_handler_mode==3} { + # EVIDENCE-OF: R-59171-47567 This mode blocks (it invokes the + # busy-handler callback) until there is no database writer and all + # readers are reading from the most recent database snapshot. + # + # The test below shows that both the reader and writer have + # finished: + # + # Also restated by the following two. That both busy_handler_mode + # values 2 and 3 work show that both of the following are true - as + # they release the reader and writer transactions in different + # orders. + # + # EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained + # immediately, and a busy-handler is configured, it is invoked and the + # writer lock retried until either the busy-handler returns 0 or the + # lock is successfully obtained. + # + # EVIDENCE-OF: R-48107-00250 The busy-handler is also invoked while + # waiting for database readers as described above. + # + do_test $tp.7 { + list [catchsql COMMIT db2] [catchsql COMMIT db3] + } [list \ + {1 {cannot commit - no transaction is active}} \ + {1 {cannot commit - no transaction is active}} \ + ] + + # EVIDENCE-OF: R-29177-48281 It then checkpoints all frames in the log + # file and syncs the database file. + # + do_test $tp.8 { + forcecopy test.db abc.db + sqlite3 db4 abc.db + db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } + } {1 2 3 4 5 6 7 8 9 10} + do_test $tp.9 { set ::sync_counter } 1 + db4 close + + # EVIDENCE-OF: R-51867-44713 This mode blocks new database writers + # while it is pending, but new database readers are allowed to continue + # unimpeded. + # + # EVIDENCE-OF: R-47276-58266 Like SQLITE_CHECKPOINT_FULL, this mode + # blocks new database writer attempts while it is pending, but does not + # impede readers. + # + # The first of the above two refers to "full" mode. The second + # to "restart". + # + do_test $tp.10.1 { + list $::write_ok $::read_ok + } {0 1} + + # EVIDENCE-OF: R-12410-31217 This mode works the same way as + # SQLITE_CHECKPOINT_FULL with the addition that after checkpointing the + # log file it blocks (calls the busy-handler callback) until all + # readers are reading from the database file only. + # + # The stuff above passed, so the first part of this requirement + # is met. The second part is tested below. If the checkpoint mode + # was "restart" or "truncate", then the busy-handler will have + # been called to block on wal-file readers. + # + do_test $tp.11 { + set ::did_restart_blocking + } [expr {($mode=="restart"||$mode=="truncate")&&$busy_handler_mode==3}] + + # EVIDENCE-OF: R-44699-57140 This mode works the same way as + # SQLITE_CHECKPOINT_RESTART with the addition that it also truncates + # the log file to zero bytes just prior to a successful return. + if {$mode=="truncate" && $busy_handler_mode==3} { + do_test $tp.12 { + file size test.db-wal + } 0 + } + } elseif {$busy_handler_mode==1} { + + # EVIDENCE-OF: R-34519-06271 SQLITE_BUSY is returned in this case. + if {$tn!=2} { + # ($tn==2) is the loop that uses "PRAGMA wal_checkpoint" + do_test $tp.13 { sqlite3_errcode db } {SQLITE_BUSY} + } + + # EVIDENCE-OF: R-49155-63541 If the busy-handler returns 0 before the + # writer lock is obtained or while waiting for database readers, the + # checkpoint operation proceeds from that point in the same way as + # SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible + # without blocking any further. + do_test $tp.14 { + forcecopy test.db abc.db + sqlite3 db4 abc.db + db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } + } {1 2 3 4 5 6} + do_test $tp.15 { set ::sync_counter } 0 + do_test $tp.16 { set ::busy_handler_counter } 1 + db4 close + } + } + + db2 close + db3 close + db5 close + db6 close + } + + db close + tvfs delete } #----------------------------------------------------------------------- @@ -282,9 +615,140 @@ foreach {tn mode res} { 8 1000000 {1 {SQLITE_MISUSE - not an error}} } { do_test 4.$tn { - list [catch "sqlite3_wal_checkpoint_v2 db $mode" msg] $msg + list [catch "wal_checkpoint_v2 db $mode" msg] $msg } $res } +db close + +foreach tn {1 2 3} { + forcedelete test.db test.db2 test.db3 + testvfs tvfs + + sqlite3 db test.db -vfs tvfs + execsql { + ATTACH 'test.db2' AS aux2; + ATTACH 'test.db3' AS aux3; + PRAGMA main.journal_mode = WAL; + PRAGMA aux2.journal_mode = WAL; + PRAGMA aux3.journal_mode = WAL; + + CREATE TABLE main.t1(x,y); + CREATE TABLE aux2.t2(x,y); + CREATE TABLE aux3.t3(x,y); + + INSERT INTO t1 VALUES('a', 'b'); + INSERT INTO t2 VALUES('a', 'b'); + INSERT INTO t3 VALUES('a', 'b'); + } + sqlite3 db2 test.db2 -vfs tvfs + + switch -- $tn { + 1 { + # EVIDENCE-OF: R-41299-52117 If no error (SQLITE_BUSY or otherwise) is + # encountered while processing the attached databases, SQLITE_OK is + # returned. + do_test 5.$tn.1 { + lindex [wal_checkpoint_v2 db truncate] 0 + } {0} ;# 0 -> SQLITE_OK + do_test 5.$tn.2 { + list [expr [file size test.db-wal]==0] \ + [expr [file size test.db2-wal]==0] \ + [expr [file size test.db3-wal]==0] + } {1 1 1} + } + + 2 { + # EVIDENCE-OF: R-38578-34175 If an SQLITE_BUSY error is encountered when + # processing one or more of the attached WAL databases, the operation is + # still attempted on any remaining attached databases and SQLITE_BUSY is + # returned at the end. + db2 eval { BEGIN; INSERT INTO t2 VALUES('d', 'e'); } + do_test 5.$tn.1 { + lindex [wal_checkpoint_v2 db truncate] 0 + } {1} ;# 1 -> SQLITE_BUSY + do_test 5.$tn.2 { + list [expr [file size test.db-wal]==0] \ + [expr [file size test.db2-wal]==0] \ + [expr [file size test.db3-wal]==0] + } {1 0 1} + db2 eval ROLLBACK + } + + 3 { + # EVIDENCE-OF: R-38049-07913 If any other error occurs while processing + # an attached database, processing is abandoned and the error code is + # returned to the caller immediately. + tvfs filter xWrite + tvfs script inject_ioerr + proc inject_ioerr {method file args} { + if {[file tail $file]=="test.db2"} { + return "SQLITE_IOERR" + } + return 0 + } + do_test 5.$tn.1 { + list [catch { wal_checkpoint_v2 db truncate } msg] $msg + } {1 {SQLITE_IOERR - disk I/O error}} + do_test 5.$tn.2 { + list [expr [file size test.db-wal]==0] \ + [expr [file size test.db2-wal]==0] \ + [expr [file size test.db3-wal]==0] + } {1 0 0} + tvfs script "" + } + } + + db close + db2 close +} + +reset_db +sqlite3 db2 test.db + +do_test 6.1 { + execsql { + PRAGMA auto_vacuum = 0; + PRAGMA journal_mode = WAL; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + file size test.db-wal +} [wal_file_size 3 1024] + +do_test 6.2 { + db2 eval { BEGIN; SELECT * FROM t1; } + db eval { INSERT INTO t1 VALUES(3, 4) } + file size test.db-wal +} [wal_file_size 4 1024] + +# At this point the log file contains 4 frames. 3 of which it should +# be possible to checkpoint. +# +# EVIDENCE-OF: R-16642-42503 If pnLog is not NULL, then *pnLog is set to +# the total number of frames in the log file or to -1 if the checkpoint +# could not run because of an error or because the database is not in +# WAL mode. +# +# EVIDENCE-OF: R-10514-25250 If pnCkpt is not NULL,then *pnCkpt is set +# to the total number of checkpointed frames in the log file (including +# any that were already checkpointed before the function was called) or +# to -1 if the checkpoint could not run due to an error or because the +# database is not in WAL mode. +# +do_test 6.4 { + lrange [wal_checkpoint_v2 db passive] 1 2 +} {4 3} + +# EVIDENCE-OF: R-37257-17813 Note that upon successful completion of an +# SQLITE_CHECKPOINT_TRUNCATE, the log file will have been truncated to +# zero bytes and so both *pnLog and *pnCkpt will be set to zero. +# +do_test 6.5 { + db2 eval COMMIT + wal_checkpoint_v2 db truncate +} {0 0 0} + finish_test + diff --git a/test/e_walhook.test b/test/e_walhook.test new file mode 100644 index 0000000000..c8c8819493 --- /dev/null +++ b/test/e_walhook.test @@ -0,0 +1,200 @@ +# 2014 December 04 +# +# 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/wal_common.tcl +set testprefix e_walhook + + +# EVIDENCE-OF: R-00752-43975 The sqlite3_wal_hook() function is used to +# register a callback that is invoked each time data is committed to a +# database in wal mode. +# +# 1.1: shows that the wal-hook is not invoked in rollback mode. +# 1.2: but is invoked in wal mode. +# +set ::wal_hook_count 0 +proc my_wal_hook {args} { + incr ::wal_hook_count + return 0 +} + +do_test 1.1.1 { + db wal_hook my_wal_hook + execsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + } + set ::wal_hook_count +} 0 +do_test 1.1.2 { + execsql { PRAGMA journal_mode = wal } + set ::wal_hook_count +} 0 + +do_test 1.3 { + execsql { INSERT INTO t1 VALUES(2) } + set wal_hook_count +} 1 + +do_test 1.4 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(3); + INSERT INTO t1 VALUES(4); + COMMIT; + } + set wal_hook_count +} 2 + +# EVIDENCE-OF: R-65366-15139 The callback is invoked by SQLite after the +# commit has taken place and the associated write-lock on the database +# released +# +set ::read_ok 0 +proc my_wal_hook {args} { + sqlite3 db2 test.db + if {[db2 eval { SELECT * FROM t1 }] == "1 2 3 4 5"} { + set ::read_ok 1 + } + db2 close +} +do_test 2.1 { + execsql { INSERT INTO t1 VALUES(5) } + set ::read_ok +} 1 + +# EVIDENCE-OF: R-44294-52863 The third parameter is the name of the +# database that was written to - either "main" or the name of an +# ATTACH-ed database. +# +# EVIDENCE-OF: R-18913-19355 The fourth parameter is the number of pages +# currently in the write-ahead log file, including those that were just +# committed. +# +set ::wal_hook_args [list] +proc my_wal_hook {dbname nEntry} { + set ::wal_hook_args [list $dbname $nEntry] +} +forcedelete test.db2 +do_test 3.0 { + execsql { + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t2(x); + PRAGMA aux.journal_mode = wal; + } +} {wal} + +# Database "aux" +do_test 3.1.1 { + set wal_hook_args [list] + execsql { INSERT INTO t2 VALUES('a') } +} {} +do_test 3.1.2 { + set wal_hook_args +} [list aux [wal_frame_count test.db2-wal 1024]] + +# Database "main" +do_test 3.2.1 { + set wal_hook_args [list] + execsql { INSERT INTO t1 VALUES(6) } +} {} +do_test 3.1.2 { + set wal_hook_args +} [list main [wal_frame_count test.db-wal 1024]] + +# EVIDENCE-OF: R-14034-00929 If an error code is returned, that error +# will propagate back up through the SQLite code base to cause the +# statement that provoked the callback to report an error, though the +# commit will have still occurred. +# +proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR } +do_catchsql_test 4.1 { + INSERT INTO t1 VALUES(7) +} {1 {SQL logic error or missing database}} + +proc my_wal_hook {args} { return 5 ;# SQLITE_BUSY } +do_catchsql_test 4.2 { + INSERT INTO t1 VALUES(8) +} {1 {database is locked}} + +proc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN } +do_catchsql_test 4.3 { + INSERT INTO t1 VALUES(9) +} {1 {unable to open database file}} + +do_execsql_test 4.4 { + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8 9} + +# EVIDENCE-OF: R-10466-53920 Calling sqlite3_wal_hook() replaces any +# previously registered write-ahead log callback. +set ::old_wal_hook 0 +proc my_old_wal_hook {args} { + incr ::old_wal_hook + return 0 +} +db wal_hook my_old_wal_hook +do_test 5.1 { + execsql { INSERT INTO t1 VALUES(10) } + set ::old_wal_hook +} {1} + +# Replace old_wal_hook. Observe that it is not invoked after it has +# been replaced. +proc my_new_wal_hook {args} { return 0 } +db wal_hook my_new_wal_hook +do_test 5.2 { + execsql { INSERT INTO t1 VALUES(11) } + set ::old_wal_hook +} {1} + + + +# EVIDENCE-OF: R-42842-27162 Note that the sqlite3_wal_autocheckpoint() +# interface and the wal_autocheckpoint pragma both invoke +# sqlite3_wal_hook() and will those overwrite any prior +# sqlite3_wal_hook() settings. +# +set ::old_wal_hook 0 +proc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 } +db wal_hook my_old_wal_hook +do_test 6.1.1 { + execsql { INSERT INTO t1 VALUES(12) } + set ::old_wal_hook +} {1} +do_test 6.1.2 { + execsql { PRAGMA wal_autocheckpoint = 1000 } + execsql { INSERT INTO t1 VALUES(12) } + set ::old_wal_hook +} {1} + +# EVIDENCE-OF: R-52629-38967 The first parameter passed to the callback +# function when it is invoked is a copy of the third parameter passed to +# sqlite3_wal_hook() when registering the callback. +# +# This is tricky to test using the tcl interface. However, the +# mechanism used to invoke the tcl script registered as a wal-hook +# depends on the context pointer being correctly passed through. And +# since multiple different wal-hook scripts have been successfully +# invoked by this test script, consider this tested. +# +# EVIDENCE-OF: R-23378-42536 The second is a copy of the database +# handle. +# +# There is an assert() in the C wal-hook used by tclsqlite.c to +# prove this. And that hook has been invoked multiple times when +# running this script. So consider this requirement tested as well. +# + +finish_test diff --git a/test/fkey5.test b/test/fkey5.test index 5aa8b1d4b7..dc866cbdb8 100644 --- a/test/fkey5.test +++ b/test/fkey5.test @@ -252,13 +252,17 @@ do_test fkey5-6.5 { } {c12 1 p4 0 c12 3 p4 0 c12 6 p4 0} do_test fkey5-7.1 { + set res {} db eval { INSERT OR IGNORE INTO c13 SELECT * FROM c12; INSERT OR IGNORE INTO C14 SELECT * FROM c12; DELETE FROM c12; PRAGMA foreign_key_check; + } { + lappend res [list $table $rowid $fkid $parent] } -} {c14 1 p4 0 c14 3 p4 0 c14 6 p4 0 c13 1 p3 0 c13 2 p3 0 c13 3 p3 0 c13 4 p3 0 c13 5 p3 0 c13 6 p3 0} + lsort $res +} {{c13 1 0 p3} {c13 2 0 p3} {c13 3 0 p3} {c13 4 0 p3} {c13 5 0 p3} {c13 6 0 p3} {c14 1 0 p4} {c14 3 0 p4} {c14 6 0 p4}} do_test fkey5-7.2 { db eval { PRAGMA foreign_key_check(c14); diff --git a/test/fkey8.test b/test/fkey8.test new file mode 100644 index 0000000000..60bb8e4640 --- /dev/null +++ b/test/fkey8.test @@ -0,0 +1,106 @@ +# 2001 September 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. +# +# This file implements tests for foreign keys. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix fkey8 + +ifcapable {!foreignkey} { + finish_test + return +} +do_execsql_test 1.0 { PRAGMA foreign_keys = 1; } + + +foreach {tn use_stmt sql schema} { + 1 1 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1); + } + + 2.1 0 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE); + } + 2.2 0 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE SET NULL); + } + 2.3 1 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE SET DEFAULT); + } + + 3 1 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE); + CREATE TRIGGER ct1 AFTER DELETE ON c1 BEGIN + INSERT INTO p1 VALUES('x'); + END; + } + + 4 1 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY); + CREATE TABLE cc1(d REFERENCES c1); + } + + 5.1 0 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY); + CREATE TABLE cc1(d REFERENCES c1 ON DELETE CASCADE); + } + 5.2 0 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY); + CREATE TABLE cc1(d REFERENCES c1 ON DELETE SET NULL); + } + 5.3 1 "DELETE FROM p1" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY); + CREATE TABLE cc1(d REFERENCES c1 ON DELETE SET DEFAULT); + } + + 6.1 1 "UPDATE p1 SET a = ?" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON UPDATE SET NULL, c); + } + 6.2 0 "UPDATE OR IGNORE p1 SET a = ?" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON UPDATE SET NULL, c); + } + 6.3 1 "UPDATE OR IGNORE p1 SET a = ?" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b REFERENCES p1 ON UPDATE CASCADE, c); + } + 6.4 1 "UPDATE OR IGNORE p1 SET a = ?" { + CREATE TABLE p1(a PRIMARY KEY); + CREATE TABLE c1(b NOT NULL REFERENCES p1 ON UPDATE SET NULL, c); + } + +} { + drop_all_tables + do_test 1.$tn { + execsql $schema + set stmt [sqlite3_prepare_v2 db $sql -1 dummy] + set ret [uses_stmt_journal $stmt] + sqlite3_finalize $stmt + set ret + } $use_stmt +} + + +finish_test + diff --git a/test/fts3snippet.test b/test/fts3snippet.test index 415251dcce..e97db586e6 100644 --- a/test/fts3snippet.test +++ b/test/fts3snippet.test @@ -520,5 +520,24 @@ do_execsql_test 2.6 { {[one] two three [four] five} } +#------------------------------------------------------------------------- +do_execsql_test 3 { + CREATE VIRTUAL TABLE t3 USING fts4; + INSERT INTO t3 VALUES('[one two three]'); +} +do_execsql_test 3.1 { + SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'one'; +} {{[one two three]}} +do_execsql_test 3.2 { + SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'two'; +} {{[one two three]}} +do_execsql_test 3.3 { + SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'three'; +} {{[one two three]}} +do_execsql_test 3.4 { + SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'one OR two OR three'; +} {{[one two three]}} + set sqlite_fts3_enable_parentheses 0 finish_test + diff --git a/test/fuzz2.test b/test/fuzz2.test index 989b00f056..51dfce140b 100644 --- a/test/fuzz2.test +++ b/test/fuzz2.test @@ -12,7 +12,6 @@ # # This file checks error recovery from malformed SQL strings. # -# $Id: fuzz2.test,v 1.3 2007/05/15 16:51:37 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -105,4 +104,36 @@ do_test fuzz2-5.5 { fuzzcatch {SELECT ALL * GROUP BY EXISTS ( SELECT "AAAAAA" . * , AAAAAA ( * ) AS AAAAAA FROM "AAAAAA" . "AAAAAA" AS "AAAAAA" USING ( AAAAAA , "AAAAAA" , "AAAAAA" ) WHERE AAAAAA ( DISTINCT ) - RAISE ( FAIL , "AAAAAA" ) HAVING "AAAAAA" . "AAAAAA" . AAAAAA ORDER BY #182 , #55 ) BETWEEN EXISTS ( SELECT ALL * FROM ( ( } } {1} +# Test cases discovered by Michal Zalewski on 2015-01-03 and reported on the +# sqlite-users mailing list. All of these cases cause segfaults in +# SQLite 3.8.7.4 and earlier. +# +do_test fuzz2-6.1 { + catchsql {SELECT n()AND+#0;} +} {1 {near "#0": syntax error}} +do_test fuzz2-6.2 { + catchsql {SELECT strftime()} +} {0 {{}}} +do_test fuzz2-6.3 { + catchsql {DETACH(SELECT group_concat(q));} +} {1 {no such column: q}} +do_test fuzz2-6.4a { + db eval {DROP TABLE IF EXISTS t0; CREATE TABLE t0(t);} + catchsql {INSERT INTO t0 SELECT strftime();} +} {0 {}} +do_test fuzz2-6.4b { + db eval {SELECT quote(t) FROM t0} +} {NULL} + +# Another test case discovered by Michal Zalewski, this on on 2015-01-22. +# Ticket 32b63d542433ca6757cd695aca42addf8ed67aa6 +# +do_test fuzz2-7.1 { + catchsql {select e.*,0 from(s,(L))e;} +} {1 {no such table: s}} +do_test fuzz2-7.2 { + catchsql {SELECT c.* FROM (a,b) AS c} +} {1 {no such table: a}} + + finish_test diff --git a/test/main.test b/test/main.test index 5bbc52b845..3f35afe20c 100644 --- a/test/main.test +++ b/test/main.test @@ -514,5 +514,25 @@ if {$::tcl_platform(platform)=="unix" } {1 {no such vfs: async}} } } + +# Print the version number so that it can be picked up by releasetest.tcl. +# +puts [db one {SELECT 'VERSION: ' || + sqlite_version() || ' ' || + sqlite_source_id();}] + +# Do deliberate failures if the TEST_FAILURE environment variable is set. +# This is done to verify that failure notifications are detected by the +# releasetest.tcl script, or possibly by other scripts involved in automatic +# testing. +# +if {[info exists ::env(TEST_FAILURE)]} { + set res 123 + if {$::env(TEST_FAILURE)==0} {set res 234} + do_test main-99.1 { + bad_behavior $::env(TEST_FAILURE) + set x 123 + } $res +} finish_test diff --git a/test/mallocK.test b/test/mallocK.test index dcf00da9aa..0a21b9fa0a 100644 --- a/test/mallocK.test +++ b/test/mallocK.test @@ -134,5 +134,15 @@ do_faultsim_test 6 -faults oom* -body { faultsim_test_result {0 {12 13 14 15}} } +do_execsql_test 7.1 { + CREATE TABLE x1(a INTEGER PRIMARY KEY, b); +} +do_faultsim_test 7.2 -faults oom* -body { + execsql { SELECT * FROM x1 WHERE a = (SELECT 1) } +} -test { + faultsim_test_result [list 0 {}] +} + + finish_test diff --git a/test/memsubsys1.test b/test/memsubsys1.test index 8cc7c2afc1..891558fd52 100644 --- a/test/memsubsys1.test +++ b/test/memsubsys1.test @@ -176,20 +176,20 @@ do_test memsubsys1-4.6 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 1 -# Test 5: Activate both PAGECACHE and SCRATCH. But make the page size +# Test 5: Activate both PAGECACHE and SCRATCH. But make the page size is # such that the SCRATCH allocations are too small. # db close sqlite3_shutdown sqlite3_config_pagecache [expr 4096+$xtra_size] 24 -sqlite3_config_scratch 6000 2 +sqlite3_config_scratch 4000 2 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-5 {PRAGMA page_size=4096} #show_memstats do_test memsubsys1-5.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] -} 24 +} {/^2[34]$/} do_test memsubsys1-5.4 { set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2] expr {$maxreq>4096} @@ -215,7 +215,7 @@ build_test_db memsubsys1-6 {PRAGMA page_size=4096} #show_memstats do_test memsubsys1-6.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] -} 24 +} {/^2[34]$/} #do_test memsubsys1-6.4 { # set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2] # expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)} diff --git a/test/misc1.test b/test/misc1.test index 173b77d637..d18223e67b 100644 --- a/test/misc1.test +++ b/test/misc1.test @@ -621,4 +621,14 @@ do_test misc1-19.2 { set fault_callbacks } {0} +# 2015-01-26: Valgrind-detected over-read. +# Reported on sqlite-users@sqlite.org by Michal Zalewski. Found by afl-fuzz +# presumably. +# +do_execsql_test misc1-20.1 { + CREATE TABLE t0(x INTEGER DEFAULT(0==0) NOT NULL); + REPLACE INTO t0(x) VALUES(''); + SELECT rowid, quote(x) FROM t0; +} {1 ''} + finish_test diff --git a/test/notify2.test b/test/notify2.test index 9e40ed695b..12c6a537ef 100644 --- a/test/notify2.test +++ b/test/notify2.test @@ -101,6 +101,8 @@ set sql $zSql # This loop runs for ~20 seconds. # set iStart [clock_seconds] + set nOp 0 + set nAttempt 0 while { ([clock_seconds]-$iStart) < $nSecond } { # Each transaction does 3 operations. Each operation is either a read @@ -128,6 +130,7 @@ set sql $zSql # Execute the SQL transaction. # + incr nAttempt set rc [catch { execsql_blocking $::DB " BEGIN; $SQL(1); @@ -154,13 +157,14 @@ set sql $zSql # returned "1". Otherwise, the invariant was false, indicating that # some malfunction has occurred. foreach r $msg { if {$r != 1} { puts "Invariant check failed: $msg" } } + incr nOp } } # Close the database connection and return 0. # sqlite3_close $::DB - expr 0 + list $nOp $nAttempt } foreach {iTest xStep xPrepare} { @@ -204,7 +208,9 @@ foreach {iTest xStep xPrepare} { for {set ii 0} {$ii < $nThread} {incr ii} { do_test notify2-$iTest.2.$ii { if {![info exists finished($ii)]} { vwait finished($ii) } - set finished($ii) + incr anSuccess($xStep) [lindex $finished($ii) 0] + incr anAttempt($xStep) [lindex $finished($ii) 1] + expr 0 } {0} } @@ -225,17 +231,36 @@ foreach {iTest xStep xPrepare} { } # The following tests checks to make sure sqlite3_blocking_step() is -# faster than sqlite3_step(). blocking_step() is always faster on -# multi-core and is usually faster on single-core. But sometimes, by -# chance, step() will be faster on a single core, in which case the +# faster than sqlite3_step(). "Faster" in this case means uses fewer +# CPU cycles. This is not always the same as faster in wall-clock time +# for this type of test. The number of CPU cycles per transaction is +# roughly proportional to the number of attempts made (i.e. one plus the +# number of SQLITE_BUSY or SQLITE_LOCKED errors that require the transaction +# to be retried). So this test just measures that a greater percentage of +# transactions attempted using blocking_step() succeed. +# +# The blocking_step() function is almost always faster on multi-core and is +# usually faster on single-core. But sometimes, by chance, step() will be +# faster on a single core, in which case the # following test will fail. # puts "The following test seeks to demonstrate that the sqlite3_unlock_notify()" -puts "interface helps multi-core systems to run faster. This test sometimes" -puts "fails on single-core machines." +puts "interface helps multi-core systems to run more efficiently. This test" +puts "sometimes fails on single-core machines." puts [array get anWrite] do_test notify2-3 { - expr {$anWrite(sqlite3_blocking_step) > $anWrite(sqlite3_step)} + set blocking [expr { + double($anSuccess(sqlite3_blocking_step)) / + double($anAttempt(sqlite3_blocking_step)) + }] + set non [expr { + double($anSuccess(sqlite3_step)) / + double($anAttempt(sqlite3_step)) + }] + puts -nonewline [format " blocking: %.1f%% non-blocking %.1f%% ..." \ + [expr $blocking*100.0] [expr $non*100.0]] + + expr {$blocking > $non} } {1} sqlite3_enable_shared_cache $::enable_shared_cache diff --git a/test/orderby8.test b/test/orderby8.test new file mode 100644 index 0000000000..53da971b12 --- /dev/null +++ b/test/orderby8.test @@ -0,0 +1,41 @@ +# 2015-01-19 +# +# 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 ORDER BY and LIMIT on tables with +# many columns. +# +# These tests verify that ticket [f97c4637102a3ae72b7911167e1d4da12ce60722] +# from 2015-01-19 has been fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby8 + +do_test 1.0 { + db eval { + CREATE TABLE t1(x); + INSERT INTO t1(x) VALUES(1),(5),(9),(7),(3),(2),(4),(6),(8); + } + set ::result_set "x" +} {x} +for {set i 1} {$i<200} {incr i} { + append ::result_set ", x+$i" + do_test 1.$i { + set res {} + db eval "SELECT $::result_set FROM t1 ORDER BY x LIMIT -1" { + lappend res $x + } + set res + } {1 2 3 4 5 6 7 8 9} +} + +finish_test diff --git a/test/oserror.test b/test/oserror.test index 40d2966bcc..1bfa37cd6c 100644 --- a/test/oserror.test +++ b/test/oserror.test @@ -51,19 +51,20 @@ proc do_re_test {tn script expression} { # a call to getcwd() may fail if there are no free file descriptors. So # an error may be reported for either open() or getcwd() here. # -puts "Possible valgrind error about invalid file descriptor follows:" -do_test 1.1.1 { - set ::log [list] - list [catch { - for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } - } msg] $msg -} {1 {unable to open database file}} -do_test 1.1.2 { - catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } -} {1} -do_re_test 1.1.3 { - lindex $::log 0 -} {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } +if {![clang_sanitize_address]} { + do_test 1.1.1 { + set ::log [list] + list [catch { + for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } + } msg] $msg + } {1 {unable to open database file}} + do_test 1.1.2 { + catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } + } {1} + do_re_test 1.1.3 { + lindex $::log 0 + } {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } +} # Test a failure in open() due to the path being a directory. diff --git a/test/percentile.test b/test/percentile.test index 9d471dfcde..b2bd061e86 100644 --- a/test/percentile.test +++ b/test/percentile.test @@ -200,7 +200,7 @@ ifcapable vtab { } { do_test percentile-2.1.$in { execsql { - SELECT percentile(x, $in) from t3; + SELECT round(percentile(x, $in),1) from t3; } } $out } diff --git a/test/permutations.test b/test/permutations.test index 20e253273e..ecf3cdde21 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -118,6 +118,19 @@ set allquicktests [test_set $alltests -exclude { if {[info exists ::env(QUICKTEST_INCLUDE)]} { set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)] } +if {[info exists ::env(QUICKTEST_OMIT)]} { + foreach x [split $::env(QUICKTEST_OMIT) ,] { + regsub -all \\y$x\\y $allquicktests {} allquicktests + } +} + +# If the TEST_FAILURE environment variable is set, it means that we what to +# deliberately provoke test failures in order to test the test infrastructure. +# Only the main.test module is needed for this. +# +if {[info exists ::env(TEST_FAILURE)]} { + set allquicktests main.test +} ############################################################################# # Start of tests @@ -141,7 +154,7 @@ test_suite "veryquick" -prefix "" -description { ] test_suite "mmap" -prefix "mm-" -description { - Similar to veryquick. Except with memory mapping disabled. + Similar to veryquick. Except with memory mapping enabled. } -presql { pragma mmap_size = 268435456; } -files [ @@ -152,7 +165,9 @@ test_suite "valgrind" -prefix "" -description { Run the "veryquick" test suite with a couple of multi-process tests (that fail under valgrind) omitted. } -files [ - test_set $allquicktests -exclude *malloc* *ioerr* *fault* wal.test atof1.test + test_set $allquicktests -exclude *malloc* *ioerr* *fault* wal.test \ + shell*.test crash8.test atof1.test selectG.test \ + tkt-fc62af4523.test ] -initialize { set ::G(valgrind) 1 } -shutdown { @@ -690,7 +705,7 @@ test_suite "inmemory_journal" -description { zerodamage.test # WAL mode is different. - wal* tkt-2d1a5c67d.test backcompat.test + wal* tkt-2d1a5c67d.test backcompat.test e_wal* }] ifcapable mem3 { diff --git a/test/pragma.test b/test/pragma.test index e660ab0fe7..09b9b6c14f 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -454,10 +454,34 @@ do_execsql_test pragma-3.21 { do_execsql_test pragma-3.22 { PRAGMA integrity_check(2); } {{non-unique entry in index t1a} {NULL value in t1x.a}} -do_execsql_test pragma-3.21 { +do_execsql_test pragma-3.23 { PRAGMA integrity_check(1); } {{non-unique entry in index t1a}} +# PRAGMA integrity check (or more specifically the sqlite3BtreeCount() +# interface) used to leave index cursors in an inconsistent state +# which could result in an assertion fault in sqlite3BtreeKey() +# called from saveCursorPosition() if content is removed from the +# index while the integrity_check is still running. This test verifies +# that problem has been fixed. +# +do_test pragma-3.30 { + db close + delete_file test.db + sqlite3 db test.db + db eval { + CREATE TABLE t1(a,b,c); + WITH RECURSIVE + c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<100) + INSERT INTO t1(a,b,c) SELECT i, printf('xyz%08x',i), 2000-i FROM c; + CREATE INDEX t1a ON t1(a); + CREATE INDEX t1bc ON t1(b,c); + } + db eval {PRAGMA integrity_check} { + db eval {DELETE FROM t1} + } +} {} + # Test modifying the cache_size of an attached database. ifcapable pager_pragmas&&attach { do_test pragma-4.1 { diff --git a/test/pragma3.test b/test/pragma3.test new file mode 100644 index 0000000000..b7ea4d3fc6 --- /dev/null +++ b/test/pragma3.test @@ -0,0 +1,253 @@ +# 2014-12-19 +# +# 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. +# +# This file implements tests for PRAGMA data_version command. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test pragma3-100 { + PRAGMA data_version; +} {1} +do_execsql_test pragma3-101 { + PRAGMA temp.data_version; +} {1} + +# Writing to the pragma is a no-op +do_execsql_test pragma3-102 { + PRAGMA main.data_version=1234; + PRAGMA main.data_version; +} {1 1} + +# EVIDENCE-OF: R-27726-60934 The "PRAGMA data_version" command provides +# an indication that the database file has been modified. +# +# EVIDENCE-OF: R-47505-58569 The "PRAGMA data_version" value is +# unchanged for commits made on the same database connection. +# +do_execsql_test pragma3-110 { + PRAGMA data_version; + BEGIN IMMEDIATE; + PRAGMA data_version; + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(100),(200),(300); + PRAGMA data_version; + COMMIT; + SELECT * FROM t1; + PRAGMA data_version; +} {1 1 1 100 200 300 1} + +sqlite3 db2 test.db +do_test pragma3-120 { + db2 eval { + SELECT * FROM t1; + PRAGMA data_version; + } +} {100 200 300 1} + +do_execsql_test pragma3-130 { + PRAGMA data_version; + BEGIN IMMEDIATE; + PRAGMA data_version; + INSERT INTO t1 VALUES(400),(500); + PRAGMA data_version; + COMMIT; + SELECT * FROM t1; + PRAGMA data_version; + PRAGMA shrink_memory; +} {1 1 1 100 200 300 400 500 1} + +# EVIDENCE-OF: R-63005-41812 The integer values returned by two +# invocations of "PRAGMA data_version" from the same connection will be +# different if changes were committed to the database by any other +# connection in the interim. +# +# Value went from 1 in pragma3-120 to 2 here. +# +do_test pragma3-140 { + db2 eval { + SELECT * FROM t1; + PRAGMA data_version; + BEGIN IMMEDIATE; + PRAGMA data_version; + UPDATE t1 SET a=a+1; + COMMIT; + SELECT * FROM t1; + PRAGMA data_version; + } +} {100 200 300 400 500 2 2 101 201 301 401 501 2} +do_execsql_test pragma3-150 { + SELECT * FROM t1; + PRAGMA data_version; +} {101 201 301 401 501 2} + +# +do_test pragma3-160 { + db eval { + BEGIN; + PRAGMA data_version; + UPDATE t1 SET a=555 WHERE a=501; + PRAGMA data_version; + SELECT * FROM t1 ORDER BY a; + PRAGMA data_version; + } +} {2 2 101 201 301 401 555 2} +do_test pragma3-170 { + db2 eval { + PRAGMA data_version; + } +} {2} +do_test pragma3-180 { + db eval { + COMMIT; + PRAGMA data_version; + } +} {2} +do_test pragma3-190 { + db2 eval { + PRAGMA data_version; + } +} {3} + +# EVIDENCE-OF: R-19326-44825 The "PRAGMA data_version" value is a local +# property of each database connection and so values returned by two +# concurrent invocations of "PRAGMA data_version" on separate database +# connections are often different even though the underlying database is +# identical. +# +do_test pragma3-195 { + expr {[db eval {PRAGMA data_version}]!=[db2 eval {PRAGMA data_version}]} +} {1} + +# EVIDENCE-OF: R-54562-06892 The behavior of "PRAGMA data_version" is +# the same for all database connections, including database connections +# in separate processes and shared cache database connections. +# +# The next block checks the behavior for separate processes. +# +do_test pragma3-200 { + db eval {PRAGMA data_version; SELECT * FROM t1;} +} {2 101 201 301 401 555} +do_test pragma3-201 { + set fd [open pragma3.txt wb] + puts $fd { + sqlite3 db test.db; + db eval {DELETE FROM t1 WHERE a>300}; + db close; + exit; + } + close $fd + exec [info nameofexec] pragma3.txt + forcedelete pragma3.txt + db eval { + PRAGMA data_version; + SELECT * FROM t1; + } +} {3 101 201} +db2 close +db close + +# EVIDENCE-OF: R-54562-06892 The behavior of "PRAGMA data_version" is +# the same for all database connections, including database connections +# in separate processes and shared cache database connections. +# +# The next block checks that behavior is the same for shared-cache. +# +ifcapable shared_cache { + set ::enable_shared_cache [sqlite3_enable_shared_cache 1] + sqlite3 db test.db + sqlite3 db2 test.db + do_test pragma3-300 { + db eval { + PRAGMA data_version; + BEGIN; + CREATE TABLE t3(a,b,c); + CREATE TABLE t4(x,y,z); + INSERT INTO t4 VALUES(123,456,789); + PRAGMA data_version; + COMMIT; + PRAGMA data_version; + } + } {1 1 1} + do_test pragma3-310 { + db2 eval { + PRAGMA data_version; + BEGIN; + INSERT INTO t3(a,b,c) VALUES('abc','def','ghi'); + SELECT * FROM t3; + PRAGMA data_version; + } + } {2 abc def ghi 2} + # The transaction in db2 has not yet committed, so the data_version in + # db is unchanged. + do_test pragma3-320 { + db eval { + PRAGMA data_version; + SELECT * FROM t4; + } + } {1 123 456 789} + do_test pragma3-330 { + db2 eval { + COMMIT; + PRAGMA data_version; + SELECT * FROM t4; + } + } {2 123 456 789} + do_test pragma3-340 { + db eval { + PRAGMA data_version; + SELECT * FROM t3; + SELECT * FROM t4; + } + } {2 abc def ghi 123 456 789} + db2 close + db close + sqlite3_enable_shared_cache $::enable_shared_cache +} + +# Make sure this also works in WAL mode +# +# This will not work with the in-memory journal permutation, as opening +# [db2] switches the journal mode back to "memory" +# +ifcapable wal { +if {[permutation]!="inmemory_journal"} { + + sqlite3 db test.db + db eval {PRAGMA journal_mode=WAL} + sqlite3 db2 test.db + do_test pragma3-400 { + db eval { + PRAGMA data_version; + PRAGMA journal_mode; + SELECT * FROM t1; + } + } {2 wal 101 201} + do_test pragma3-410 { + db2 eval { + PRAGMA data_version; + PRAGMA journal_mode; + SELECT * FROM t1; + } + } {2 wal 101 201} + do_test pragma3-420 { + db eval {UPDATE t1 SET a=111*(a/100); PRAGMA data_version; SELECT * FROM t1} + } {2 111 222} + do_test pragma3-430 { + db2 eval {PRAGMA data_version; SELECT * FROM t1;} + } {3 111 222} + db2 close +} +} + +finish_test diff --git a/test/releasetest.mk b/test/releasetest.mk deleted file mode 100644 index 5d217c645b..0000000000 --- a/test/releasetest.mk +++ /dev/null @@ -1,14 +0,0 @@ -######################################################## -TOP=/home/drh/sqlite/sqlite - -TCL_FLAGS=-I/home/drh/tcltk/86linux -LIBTCL=/home/drh/tcltk/86linux/libtcl8.6.a -lm -ldl -lpthread - -BCC = gcc -TCC = gcc -ansi -g $(CFLAGS) -NAWK = awk -AR = ar cr -RANLIB = ranlib -THREADLIB = -lpthread -ldl -lz -include $(TOP)/main.mk -######################################################## diff --git a/test/releasetest.tcl b/test/releasetest.tcl index d2a1bd2bb0..aa12433afe 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -1,70 +1,63 @@ - -set rcsid {$Id: $} - +#!/usr/bin/tclsh +# # Documentation for this script. This may be output to stderr # if the script is invoked incorrectly. See the [process_options] # proc below. # set ::USAGE_MESSAGE { This Tcl script is used to test the various configurations required -before releasing a new version. Supported command line options (all +before releasing a new version. Supported command line options (all optional) are: - -makefile PATH-TO-MAKEFILE (default "releasetest.mk") - -platform PLATFORM (see below) - -quick BOOLEAN (default "0") - -config CONFIGNAME (Run only CONFIGNAME) + --srcdir TOP-OF-SQLITE-TREE (see below) + --platform PLATFORM (see below) + --config CONFIGNAME (Run only CONFIGNAME) + --quick (Run "veryquick.test" only) + --veryquick (Run "make smoketest" only) + --buildonly (Just build testfixture - do not run) + --dryrun (Print what would have happened) + --info (Show diagnostic info) -The default value for -makefile is "./releasetest.mk". +The default value for --srcdir is the parent of the directory holding +this script. -The script determines the default value for -platform using the -$tcl_platform(os) and $tcl_platform(machine) variables. Supported +The script determines the default value for --platform using the +$tcl_platform(os) and $tcl_platform(machine) variables. Supported platforms are "Linux-x86", "Linux-x86_64" and "Darwin-i386". -If the -quick option is set to true, then the "veryquick.test" script -is run for all compilation configurations. Otherwise, sometimes "all.test" -is run, sometimes "veryquick.test". - -Almost any SQLite makefile (except those generated by configure - see below) -should work. The following properties are required: - - * The makefile should support the "fulltest" target. - * The makefile should support the variable "OPTS" as a way to pass - options from the make command line to lemon and the C compiler. - -More precisely, the following invocation must be supported: - - make -f $::MAKEFILE fulltest OPTS="-DSQLITE_SECURE_DELETE=1 -DSQLITE_DEBUG=1" - -Makefiles generated by the sqlite configure program cannot be used as -they do not respect the OPTS variable. - -Example Makefile contents: - - ######################################################## - TOP=/home/dan/work/sqlite/sqlite - - TCL_FLAGS=-I/home/dan/tcl/include - LIBTCL=-L/home/dan/tcl/lib -ltcl - - BCC = gcc - TCC = gcc -ansi -g $(CFLAGS) - NAWK = awk - AR = ar cr - RANLIB = ranlib - THREADLIB = -lpthread -ldl - include $(TOP)/main.mk - ######################################################## +Every test begins with a fresh run of the configure script at the top +of the SQLite source tree. } -array set ::Configs { +# Omit comments (text between # and \n) in a long multi-line string. +# +proc strip_comments {in} { + regsub -all {#[^\n]*\n} $in {} out + return $out +} + +array set ::Configs [strip_comments { "Default" { -O2 + --disable-amalgamation --disable-shared } - "Ftrapv" { - -O2 -ftrapv - -DSQLITE_MAX_ATTACHED=55 - -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + "Sanitize" { + CC=clang -fsanitize=undefined + -DSQLITE_ENABLE_STAT4 + } + "Have-Not" { + # The "Have-Not" configuration sets all possible -UHAVE_feature options + # in order to verify that the code works even on platforms that lack + # these support services. + -DHAVE_FDATASYNC=0 + -DHAVE_GMTIME_R=0 + -DHAVE_ISNAN=0 + -DHAVE_LOCALTIME_R=0 + -DHAVE_LOCALTIME_S=0 + -DHAVE_MALLOC_USABLE_SIZE=0 + -DHAVE_STRCHRNUL=0 + -DHAVE_USLEEP=0 + -DHAVE_UTIME=0 } "Unlock-Notify" { -O2 @@ -81,6 +74,7 @@ array set ::Configs { -O2 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_ENABLE_STMT_SCANSTATUS } "Check-Symbols" { -DSQLITE_MEMDEBUG=1 @@ -94,11 +88,13 @@ array set ::Configs { -DSQLITE_SECURE_DELETE=1 -DSQLITE_SOUNDEX=1 -DSQLITE_ENABLE_ATOMIC_WRITE=1 - -DSQLITE_ENABLE_IOTRACE=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_STMT_SCANSTATUS } "Debug-One" { + --disable-shared -O2 -DSQLITE_DEBUG=1 -DSQLITE_MEMDEBUG=1 @@ -109,6 +105,9 @@ array set ::Configs { -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_ENABLE_MEMSYS3=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_OTA + -DSQLITE_MAX_ATTACHED=125 } "Device-One" { -O2 @@ -149,6 +148,7 @@ array set ::Configs { -DSQLITE_ENABLE_LOCKING_STYLE=1 } "OS-X" { + -O1 # Avoid a compiler bug in gcc 4.2.1 build 5658 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_THREADSAFE=2 @@ -161,8 +161,9 @@ array set ::Configs { -DSQLITE_DEFAULT_CACHE_SIZE=1000 -DSQLITE_MAX_LENGTH=2147483645 -DSQLITE_MAX_VARIABLE_NUMBER=500000 - -DSQLITE_DEBUG=1 + -DSQLITE_DEBUG=1 -DSQLITE_PREFER_PROXY_LOCKING=1 + -DSQLITE_ENABLE_API_ARMOR=1 } "Extra-Robustness" { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 @@ -177,41 +178,80 @@ array set ::Configs { -DSQLITE_DISABLE_FTS4_DEFERRED -DSQLITE_ENABLE_RTREE } - "No-lookaside" { -DSQLITE_TEST_REALLOC_STRESS=1 -DSQLITE_OMIT_LOOKASIDE=1 -DHAVE_USLEEP=1 } -} + "Valgrind" { + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_FTS4 + -DSQLITE_ENABLE_RTREE + } -array set ::Platforms { + # The next group of configurations are used only by the + # Failure-Detection platform. They are all the same, but we need + # different names for them all so that they results appear in separate + # subdirectories. + # + Fail0 {-O0} + Fail2 {-O0} + Fail3 {-O0} + Fail4 {-O0} +}] + +array set ::Platforms [strip_comments { Linux-x86_64 { "Check-Symbols" checksymbols - "Debug-One" test + "Debug-One" "mptest test" + "Have-Not" test "Secure-Delete" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test "Extra-Robustness" test "Device-Two" test - "Ftrapv" test "No-lookaside" test "Devkit" test + "Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test} + "Valgrind" valgrindtest "Default" "threadtest fulltest" "Device-One" fulltest } Linux-i686 { "Devkit" test + "Have-Not" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Device-One" test "Device-Two" test "Default" "threadtest fulltest" } Darwin-i386 { - "Locking-Style" test + "Locking-Style" "mptest test" + "Have-Not" test "OS-X" "threadtest fulltest" } -} + Darwin-x86_64 { + "Locking-Style" "mptest test" + "Have-Not" test + "OS-X" "threadtest fulltest" + } + "Windows NT-intel" { + "Default" "mptest fulltestonly" + "Have-Not" test + } + + # The Failure-Detection platform runs various tests that deliberately + # fail. This is used as a test of this script to verify that this script + # correctly identifies failures. + # + Failure-Detection { + Fail0 "TEST_FAILURE=0 test" + Sanitize "TEST_FAILURE=1 test" + Fail2 "TEST_FAILURE=2 valgrindtest" + Fail3 "TEST_FAILURE=3 valgrindtest" + Fail4 "TEST_FAILURE=4 test" + } +}] # End of configuration section. @@ -227,18 +267,82 @@ foreach {key value} [array get ::Platforms] { } } +# Open the file $logfile and look for a report on the number of errors +# and the number of test cases run. Add these values to the global +# $::NERRCASE and $::NTESTCASE variables. +# +# If any errors occur, then write into $errmsgVar the text of an appropriate +# one-line error message to show on the output. +# +proc count_tests_and_errors {logfile rcVar errmsgVar} { + if {$::DRYRUN} return + upvar 1 $rcVar rc $errmsgVar errmsg + set fd [open $logfile rb] + set seen 0 + while {![eof $fd]} { + set line [gets $fd] + if {[regexp {(\d+) errors out of (\d+) tests} $line all nerr ntest]} { + incr ::NERRCASE $nerr + incr ::NTESTCASE $ntest + set seen 1 + if {$nerr>0} { + set rc 1 + set errmsg $line + } + } + if {[regexp {runtime error: +(.*)} $line all msg]} { + incr ::NERRCASE + if {$rc==0} { + set rc 1 + set errmsg $msg + } + } + if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} { + incr ::NERRCASE + if {$rc==0} { + set rc 1 + set errmsg $all + } + } + if {[regexp {^VERSION: 3\.\d+.\d+} $line]} { + set v [string range $line 9 end] + if {$::SQLITE_VERSION eq ""} { + set ::SQLITE_VERSION $v + } elseif {$::SQLITE_VERSION ne $v} { + set rc 1 + set errmsg "version conflict: {$::SQLITE_VERSION} vs. {$v}" + } + } + } + close $fd + if {!$seen} { + set rc 1 + set errmsg "Test did not complete" + if {[file readable core]} { + append errmsg " - core file exists" + } + } +} + proc run_test_suite {name testtarget config} { - - # Tcl variable $opts is used to build up the value used to set the + # Tcl variable $opts is used to build up the value used to set the # OPTS Makefile variable. Variable $cflags holds the value for # CFLAGS. The makefile will pass OPTS to both gcc and lemon, but # CFLAGS is only passed to gcc. # - set cflags "" + set cflags "-g" set opts "" + set title ${name}($testtarget) + set configOpts "" + + regsub -all {#[^\n]*\n} $config \n config foreach arg $config { - if {[string match -D* $arg]} { + if {[regexp {^-[UD]} $arg]} { lappend opts $arg + } elseif {[regexp {^[A-Z]+=} $arg]} { + lappend testtarget $arg + } elseif {[regexp {^--(enable|disable)-} $arg]} { + lappend configOpts $arg } else { lappend cflags $arg } @@ -258,30 +362,76 @@ proc run_test_suite {name testtarget config} { append opts " -DSQLITE_OS_UNIX=1" } - # Run the test. - # - set makefile [file normalize $::MAKEFILE] - file mkdir $dir - puts -nonewline "Testing configuration \"$name\" (logfile=$dir/test.log)..." - flush stdout + if {!$::TRACE} { + set n [string length $title] + puts -nonewline "${title}[string repeat . [expr {63-$n}]]" + flush stdout + } - set makecmd [concat \ - [list exec make -C $dir -f $makefile clean] \ - $testtarget \ - [list CFLAGS=$cflags OPTS=$opts >& $dir/test.log] \ - ] - - set tm1 [clock seconds] - set rc [catch $makecmd] + set rc 0 + set tm1 [clock seconds] + set origdir [pwd] + trace_cmd file mkdir $dir + trace_cmd cd $dir + set errmsg {} + catch {file delete core} + set rc [catch [configureCommand $configOpts]] + if {!$rc} { + set rc [catch [makeCommand $testtarget $cflags $opts]] + count_tests_and_errors test.log rc errmsg + } + trace_cmd cd $origdir set tm2 [clock seconds] - set minutes [expr {($tm2-$tm1)/60}] - set seconds [expr {($tm2-$tm1)%60}] - puts -nonewline [format " (%d:%.2d) " $minutes $seconds] - if {$rc} { - puts "FAILED." - } else { - puts "Ok." + if {!$::TRACE} { + set hours [expr {($tm2-$tm1)/3600}] + set minutes [expr {(($tm2-$tm1)/60)%60}] + set seconds [expr {($tm2-$tm1)%60}] + set tm [format (%02d:%02d:%02d) $hours $minutes $seconds] + if {$rc} { + puts " FAIL $tm" + incr ::NERR + if {$errmsg!=""} {puts " $errmsg"} + } else { + puts " Ok $tm" + } + } +} + +# The following procedure returns the "configure" command to be exectued for +# the current platform, which may be Windows (via MinGW, etc). +# +proc configureCommand {opts} { + set result [list trace_cmd exec] + if {$::tcl_platform(platform)=="windows"} { + lappend result sh + } + lappend result $::SRCDIR/configure --enable-load-extension + foreach x $opts {lappend result $x} + lappend result >& test.log +} + +# The following procedure returns the "make" command to be executed for the +# specified targets, compiler flags, and options. +# +proc makeCommand { targets cflags opts } { + set result [list trace_cmd exec make clean] + foreach target $targets { + lappend result $target + } + lappend result CFLAGS=$cflags OPTS=$opts >>& test.log +} + +# The following procedure prints its arguments if ::TRACE is true. +# And it executes the command of its arguments in the calling context +# if ::DRYRUN is false. +# +proc trace_cmd {args} { + if {$::TRACE} { + puts $args + } + if {!$::DRYRUN} { + uplevel 1 $args } } @@ -292,16 +442,22 @@ proc run_test_suite {name testtarget config} { # option. # proc process_options {argv} { - set ::MAKEFILE releasetest.mk ;# Default value - set ::QUICK 0 ;# Default value + set ::SRCDIR [file normalize [file dirname [file dirname $::argv0]]] + set ::QUICK 0 + set ::BUILDONLY 0 + set ::DRYRUN 0 + set ::EXEC exec + set ::TRACE 0 set config {} set platform $::tcl_platform(os)-$::tcl_platform(machine) for {set i 0} {$i < [llength $argv]} {incr i} { - switch -- [lindex $argv $i] { - -makefile { + set x [lindex $argv $i] + if {[regexp {^--[a-z]} $x]} {set x [string range $x 1 end]} + switch -glob -- $x { + -srcdir { incr i - set ::MAKEFILE [lindex $argv $i] + set ::SRCDIR [file normalize [lindex $argv $i]] } -platform { @@ -310,15 +466,57 @@ proc process_options {argv} { } -quick { - incr i - set ::QUICK [lindex $argv $i] + set ::QUICK 1 + } + -veryquick { + set ::QUICK 2 } -config { incr i set config [lindex $argv $i] } - + + -buildonly { + set ::BUILDONLY 1 + } + + -dryrun { + set ::DRYRUN 1 + } + + -trace { + set ::TRACE 1 + } + + -info { + puts "Command-line Options:" + puts " --srcdir $::SRCDIR" + puts " --platform [list $platform]" + puts " --config [list $config]" + if {$::QUICK} {puts " --quick"} + if {$::BUILDONLY} {puts " --buildonly"} + if {$::DRYRUN} {puts " --dryrun"} + if {$::TRACE} {puts " --trace"} + puts "\nAvailable --platform options:" + foreach y [lsort [array names ::Platforms]] { + puts " [list $y]" + } + puts "\nAvailable --config options:" + foreach y [lsort [array names ::Configs]] { + puts " [list $y]" + } + exit + } + -g - + -D* - + -O* - + -enable-* - + -disable-* - + *=* { + lappend ::EXTRACONFIG [lindex $argv $i] + } + default { puts stderr "" puts stderr [string trim $::USAGE_MESSAGE] @@ -327,8 +525,6 @@ proc process_options {argv} { } } - set ::MAKEFILE [file normalize $::MAKEFILE] - if {0==[info exists ::Platforms($platform)]} { puts "Unknown platform: $platform" puts -nonewline "Set the -platform option to " @@ -347,8 +543,16 @@ proc process_options {argv} { } else { set ::CONFIGLIST $::Platforms($platform) } - puts "Running the following configurations for $platform:" + puts "Running the following test configurations for $platform:" puts " [string trim $::CONFIGLIST]" + puts -nonewline "Flags:" + if {$::DRYRUN} {puts -nonewline " --dryrun"} + if {$::BUILDONLY} {puts -nonewline " --buildonly"} + switch -- $::QUICK { + 1 {puts -nonewline " --quick"} + 2 {puts -nonewline " --veryquick"} + } + puts "" } # Main routine. @@ -356,30 +560,59 @@ proc process_options {argv} { proc main {argv} { # Process any command line options. + set ::EXTRACONFIG {} process_options $argv + puts [string repeat * 79] + set ::NERR 0 + set ::NTEST 0 + set ::NTESTCASE 0 + set ::NERRCASE 0 + set ::SQLITE_VERSION {} + set STARTTIME [clock seconds] foreach {zConfig target} $::CONFIGLIST { - if {$::QUICK} {set target test} - set config_options $::Configs($zConfig) + if {$target ne "checksymbols"} { + switch -- $::QUICK { + 1 {set target test} + 2 {set target smoketest} + } + if {$::BUILDONLY} {set target testfixture} + } + set config_options [concat $::Configs($zConfig) $::EXTRACONFIG] + incr NTEST run_test_suite $zConfig $target $config_options # If the configuration included the SQLITE_DEBUG option, then remove # it and run veryquick.test. If it did not include the SQLITE_DEBUG option # add it and run veryquick.test. - if {$target!="checksymbols"} { + if {$target!="checksymbols" && $target!="valgrindtest" + && !$::BUILDONLY && $::QUICK<2} { set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] + set xtarget $target + regsub -all {fulltest[a-z]*} $xtarget test xtarget if {$debug_idx < 0} { - run_test_suite "${zConfig}_debug" test [ - concat $config_options -DSQLITE_DEBUG=1 - ] + incr NTEST + append config_options " -DSQLITE_DEBUG=1" + run_test_suite "${zConfig}_debug" $xtarget $config_options } else { - run_test_suite "${zConfig}_ndebug" test [ - lreplace $config_options $debug_idx $debug_idx - ] + incr NTEST + regsub { *-DSQLITE_MEMDEBUG[^ ]* *} $config_options { } config_options + regsub { *-DSQLITE_DEBUG[^ ]* *} $config_options { } config_options + run_test_suite "${zConfig}_ndebug" $xtarget $config_options } } + } + set elapsetime [expr {[clock seconds]-$STARTTIME}] + set hr [expr {$elapsetime/3600}] + set min [expr {($elapsetime/60)%60}] + set sec [expr {$elapsetime%60}] + set etime [format (%02d:%02d:%02d) $hr $min $sec] + puts [string repeat * 79] + puts "$::NERRCASE failures out of $::NTESTCASE tests in $etime" + if {$::SQLITE_VERSION ne ""} { + puts "SQLite $::SQLITE_VERSION" } } diff --git a/test/selectG.test b/test/selectG.test new file mode 100644 index 0000000000..86d89b121b --- /dev/null +++ b/test/selectG.test @@ -0,0 +1,39 @@ +# 2015-01-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 verifies that INSERT operations with a very large number of +# VALUE terms works and does not hit the SQLITE_LIMIT_COMPOUND_SELECT limit. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix selectG + +# Do an INSERT with a VALUES clause that contains 100,000 entries. Verify +# that this insert happens quickly (in less than 10 seconds). Actually, the +# insert will normally happen in less than 0.5 seconds on a workstation, but +# we allow plenty of overhead for slower machines. The speed test checks +# for an O(N*N) inefficiency that was once in the code and that would make +# the insert run for over a minute. +# +do_test 100 { + set sql "CREATE TABLE t1(x);\nINSERT INTO t1(x) VALUES" + for {set i 1} {$i<100000} {incr i} { + append sql "($i)," + } + append sql "($i);" + set microsec [lindex [time {db eval $sql}] 0] + db eval { + SELECT count(x), sum(x), avg(x), $microsec<10000000 FROM t1; + } +} {100000 5000050000 50000.5 1} + +finish_test diff --git a/test/shell1.test b/test/shell1.test index 27bbe87259..f24b00d494 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -207,10 +207,10 @@ do_test shell1-2.2.4 { } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { @@ -238,7 +238,7 @@ do_test shell1-2.3.7 { # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -421,20 +421,21 @@ do_test shell1-3.12.3 { } {1 {Usage: .indices ?LIKE-PATTERN?}} # .mode MODE ?TABLE? Set output mode where MODE is one of: +# ascii Columns/rows delimited by 0x1F and 0x1E # csv Comma-separated values # column Left-aligned columns. (See .width) # html HTML
code # insert SQL insert statements for TABLE # line One value per line -# list Values delimited by .separator string +# list Values delimited by .separator strings # tabs Tab-separated values # tcl TCL list elements do_test shell1-3.13.1 { catchcmd "test.db" ".mode" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} @@ -467,10 +468,10 @@ do_test shell1-3.13.11 { # don't allow partial mode type matches do_test shell1-3.13.12 { catchcmd "test.db" ".mode l" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.13 { catchcmd "test.db" ".mode li" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.14 { catchcmd "test.db" ".mode lin" } {0 {}} @@ -586,10 +587,10 @@ CREATE VIEW v2 AS SELECT x+1 AS y FROM t1; CREATE VIEW v1 AS SELECT y+1 FROM v2;}} db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;} -# .separator STRING Change separator used by output mode and .import +# .separator STRING Change column separator used by output and .import do_test shell1-3.22.1 { catchcmd "test.db" ".separator" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} do_test shell1-3.22.2 { catchcmd "test.db" ".separator FOO" } {0 {}} @@ -599,7 +600,7 @@ do_test shell1-3.22.3 { do_test shell1-3.22.4 { # too many arguments catchcmd "test.db" ".separator FOO BAD BAD2" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} # .show Show the current values for various settings do_test shell1-3.23.1 { @@ -610,10 +611,11 @@ do_test shell1-3.23.1 { [regexp {mode:} $res] \ [regexp {nullvalue:} $res] \ [regexp {output:} $res] \ - [regexp {separator:} $res] \ + [regexp {colseparator:} $res] \ + [regexp {rowseparator:} $res] \ [regexp {stats:} $res] \ [regexp {width:} $res] -} {1 1 1 1 1 1 1 1 1} +} {1 1 1 1 1 1 1 1 1 1} do_test shell1-3.23.2 { # too many arguments catchcmd "test.db" ".show BAD" diff --git a/test/shell5.test b/test/shell5.test index 8d740cb980..e22db04d80 100644 --- a/test/shell5.test +++ b/test/shell5.test @@ -55,7 +55,7 @@ do_test shell5-1.1.3 { # .separator STRING Change separator used by output mode and .import do_test shell5-1.2.1 { catchcmd "test.db" ".separator" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} do_test shell5-1.2.2 { catchcmd "test.db" ".separator ONE" } {0 {}} @@ -65,12 +65,18 @@ do_test shell5-1.2.3 { do_test shell5-1.2.4 { # too many arguments catchcmd "test.db" ".separator ONE TWO THREE" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} -# separator should default to "|" -do_test shell5-1.3.1 { +# column separator should default to "|" +do_test shell5-1.3.1.1 { set res [catchcmd "test.db" ".show"] - list [regexp {separator: \"\|\"} $res] + list [regexp {colseparator: \"\|\"} $res] +} {1} + +# row separator should default to "\n" +do_test shell5-1.3.1.2 { + set res [catchcmd "test.db" ".show"] + list [regexp {rowseparator: \"\\n\"} $res] } {1} # set separator to different value. @@ -372,5 +378,31 @@ CREATE TABLE t4(a, b); db eval { SELECT * FROM t4 } } {xy\" hello one 2 {} {}} +#---------------------------------------------------------------------------- +# Tests for the shell "ascii" import/export mode. +# +do_test shell5-3.1 { + set fd [open shell5.csv w] + fconfigure $fd -encoding binary -translation binary + puts -nonewline $fd "\"test 1\"\x1F,test 2\r\n\x1E" + puts -nonewline $fd "test 3\x1Ftest 4\n" + close $fd + catchcmd test.db { +.mode ascii +CREATE TABLE t5(a, b); +.import shell5.csv t5 + } + db eval { SELECT * FROM t5 } +} "\{\"test 1\"} \{,test 2\r\n\} \{test 3\} \{test 4\n\}" + +do_test shell5-3.2 { + set x [catchcmd test.db { +.mode ascii +SELECT * FROM t5; + }] + # Handle platform end-of-line differences + regsub -all {[\n\r]?\n} $x x + set x +} "0 \{\"test 1\"\x1F,test 2\x1Etest 3\x1Ftest 4\x1E\}" finish_test diff --git a/test/sort.test b/test/sort.test index be2a6f531c..21b11d6c88 100644 --- a/test/sort.test +++ b/test/sort.test @@ -16,6 +16,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sort +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db # Create a bunch of data to sort against # diff --git a/test/sort2.test b/test/sort2.test index a4c55c9f24..890025a5d0 100644 --- a/test/sort2.test +++ b/test/sort2.test @@ -17,6 +17,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sort2 +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db foreach {tn script} { 1 { } diff --git a/test/sort4.test b/test/sort4.test index 01fcbfee95..7b913610da 100644 --- a/test/sort4.test +++ b/test/sort4.test @@ -17,6 +17,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sort4 +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db + # Configure the sorter to use 3 background threads. db eval {PRAGMA threads=3} diff --git a/test/sortfault.test b/test/sortfault.test index a1983ac1c0..f9fe2be48a 100644 --- a/test/sortfault.test +++ b/test/sortfault.test @@ -17,6 +17,12 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sortfault +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db + do_execsql_test 1.0 { PRAGMA cache_size = 5; diff --git a/test/threadtest3.c b/test/threadtest3.c index cb7e2fa41b..25caeb89f9 100644 --- a/test/threadtest3.c +++ b/test/threadtest3.c @@ -1,41 +1,41 @@ - /* +** 2010-07-22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** ** The code in this file runs a few multi-threaded test cases using the ** SQLite library. It can be compiled to an executable on unix using the ** following command: ** ** gcc -O2 threadtest3.c sqlite3.c -ldl -lpthread -lm ** -** Then run the compiled program. The exit status is non-zero if any tests -** failed (hopefully there is also some output to stdout to clarify what went -** wrong). +** Even though threadtest3.c is the only C source code file mentioned on +** the compiler command-line, #include macros are used to pull in additional +** C code files named "tt3_*.c". ** -** There are three parts to the code in this file, in the following order: +** After compiling, run this program with an optional argument telling +** which test to run. All tests are run if no argument is given. The +** argument can be a glob pattern to match multiple tests. Examples: ** -** 1. Code for the SQL aggregate function md5sum() copied from -** tclsqlite.c in the SQLite distribution. The names of all the -** types and functions in this section begin with "MD5" or "md5". +** ./a.out -- Run all tests +** ./a.out walthread3 -- Run the "walthread3" test +** ./a.out 'wal*' -- Run all of the wal* tests +** ./a.out --help -- List all available tests ** -** 2. A set of utility functions that may be used to implement -** multi-threaded test cases. These are all called by test code -** via macros that help with error reporting. The macros are defined -** immediately below this comment. -** -** 3. The test code itself. And a main() routine to drive the test -** code. +** The exit status is non-zero if any test fails. */ -/************************************************************************* -** Start of test code/infrastructure interface macros. -** -** The following macros constitute the interface between the test -** programs and the test infrastructure. Test infrastructure code -** does not itself use any of these macros. Test code should not -** call any of the macroname_x() functions directly. -** -** See the header comments above the corresponding macroname_x() -** function for a description of each interface. +/* +** The "Set Error Line" macro. */ +#define SEL(e) ((e)->iLine = ((e)->rc ? (e)->iLine : __LINE__)) /* Database functions */ #define opendb(w,x,y,z) (SEL(w), opendb_x(w,x,y,z)) @@ -47,10 +47,13 @@ #define execsql_i64(x,y,...) (SEL(x), execsql_i64_x(x,y,__VA_ARGS__)) #define execsql_text(x,y,z,...) (SEL(x), execsql_text_x(x,y,z,__VA_ARGS__)) #define execsql(x,y,...) (SEL(x), (void)execsql_i64_x(x,y,__VA_ARGS__)) +#define sql_script_printf(x,y,z,...) ( \ + SEL(x), sql_script_printf_x(x,y,z,__VA_ARGS__) \ +) /* Thread functions */ -#define launch_thread(w,x,y,z) (SEL(w), launch_thread_x(w,x,y,z)) -#define join_all_threads(y,z) (SEL(y), join_all_threads_x(y,z)) +#define launch_thread(w,x,y,z) (SEL(w), launch_thread_x(w,x,y,z)) +#define join_all_threads(y,z) (SEL(y), join_all_threads_x(y,z)) /* Timer functions */ #define setstoptime(y,z) (SEL(y), setstoptime_x(y,z)) @@ -64,6 +67,9 @@ #define filesize(y,z) (SEL(y), filesize_x(y,z)) #define filecopy(x,y,z) (SEL(x), filecopy_x(x,y,z)) +#define PTR2INT(x) ((int)((intptr_t)x)) +#define INT2PTR(x) ((void*)((intptr_t)x)) + /* ** End of test code/infrastructure interface macros. *************************************************************************/ @@ -115,7 +121,10 @@ struct MD5Context { int isInit; uint32 buf[4]; uint32 bits[2]; - unsigned char in[64]; + union { + unsigned char in[64]; + uint32 in32[16]; + } u; }; typedef struct MD5Context MD5Context; @@ -264,7 +273,7 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ /* Handle any leading odd-sized chunks */ if ( t ) { - unsigned char *p = (unsigned char *)ctx->in + t; + unsigned char *p = (unsigned char *)ctx->u.in + t; t = 64-t; if (len < t) { @@ -272,8 +281,8 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ return; } memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse(ctx->u.in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->u.in); buf += t; len -= t; } @@ -281,16 +290,16 @@ void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){ /* Process data in 64-byte chunks */ while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); + memcpy(ctx->u.in, buf, 64); + byteReverse(ctx->u.in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->u.in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ - memcpy(ctx->in, buf, len); + memcpy(ctx->u.in, buf, len); } /* @@ -306,7 +315,7 @@ static void MD5Final(unsigned char digest[16], MD5Context *ctx){ /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ - p = ctx->in + count; + p = ctx->u.in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ @@ -316,25 +325,25 @@ static void MD5Final(unsigned char digest[16], MD5Context *ctx){ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse(ctx->u.in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->u.in); /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); + memset(ctx->u.in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count-8); } - byteReverse(ctx->in, 14); + byteReverse(ctx->u.in, 14); /* Append length in bits and transform */ - ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; - ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + ctx->u.in32[14] = ctx->bits[0]; + ctx->u.in32[15] = ctx->bits[1]; - MD5Transform(ctx->buf, (uint32 *)ctx->in); + MD5Transform(ctx->buf, (uint32 *)ctx->u.in); byteReverse((unsigned char *)ctx->buf, 4); memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */ + memset(ctx, 0, sizeof(*ctx)); /* In case it is sensitive */ } /* @@ -382,9 +391,9 @@ static void md5finalize(sqlite3_context *context){ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } -/************************************************************************* +/* ** End of copied md5sum() code. -*/ +**************************************************************************/ typedef sqlite3_int64 i64; @@ -398,9 +407,6 @@ typedef struct Thread Thread; /* Total number of errors in this process so far. */ static int nGlobalErr = 0; -/* Set to true to run in "process" instead of "thread" mode. */ -static int bProcessMode = 0; - struct Error { int rc; int iLine; @@ -421,10 +427,10 @@ struct Statement { struct Thread { int iTid; /* Thread number within test */ - int iArg; /* Integer argument passed by caller */ + void* pArg; /* Pointer argument passed by caller */ pthread_t tid; /* Thread id */ - char *(*xProc)(int, int); /* Thread main proc */ + char *(*xProc)(int, void*); /* Thread main proc */ Thread *pNext; /* Next in this list of threads */ }; @@ -441,8 +447,13 @@ static void free_err(Error *p){ static void print_err(Error *p){ if( p->rc!=SQLITE_OK ){ - printf("Error: (%d) \"%s\" at line %d\n", p->rc, p->zErr, p->iLine); - nGlobalErr++; + int isWarn = 0; + if( p->rc==SQLITE_SCHEMA ) isWarn = 1; + if( sqlite3_strglob("* - no such table: *",p->zErr)==0 ) isWarn = 1; + printf("%s: (%d) \"%s\" at line %d\n", isWarn ? "Warning" : "Error", + p->rc, p->zErr, p->iLine); + if( !isWarn ) nGlobalErr++; + fflush(stdout); } } @@ -506,8 +517,9 @@ static void opendb_x( ){ if( pErr->rc==SQLITE_OK ){ int rc; + int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI; if( bDelete ) unlink(zFile); - rc = sqlite3_open(zFile, &pDb->db); + rc = sqlite3_open_v2(zFile, &pDb->db, flags, 0); if( rc ){ sqlite_error(pErr, pDb, "open"); sqlite3_close(pDb->db); @@ -556,6 +568,22 @@ static void sql_script_x( } } +static void sql_script_printf_x( + Error *pErr, /* IN/OUT: Error code */ + Sqlite *pDb, /* Database handle */ + const char *zFormat, /* SQL printf format string */ + ... /* Printf args */ +){ + va_list ap; /* ... printf arguments */ + va_start(ap, zFormat); + if( pErr->rc==SQLITE_OK ){ + char *zSql = sqlite3_vmprintf(zFormat, ap); + pErr->rc = sqlite3_exec(pDb->db, zSql, 0, 0, &pErr->zErr); + sqlite3_free(zSql); + } + va_end(ap); +} + static Statement *getSqlStatement( Error *pErr, /* IN/OUT: Error code */ Sqlite *pDb, /* Database handle */ @@ -624,11 +652,9 @@ static i64 execsql_i64_x( if( pErr->rc==SQLITE_OK ){ sqlite3_stmt *pStmt; /* SQL statement to execute */ va_list ap; /* ... arguments */ - int i; /* Used to iterate through parameters */ va_start(ap, pDb); pStmt = getAndBindSqlStatement(pErr, pDb, ap); if( pStmt ){ - int rc; int first = 1; while( SQLITE_ROW==sqlite3_step(pStmt) ){ if( first && sqlite3_column_count(pStmt)>0 ){ @@ -663,11 +689,9 @@ static char * execsql_text_x( if( pErr->rc==SQLITE_OK ){ sqlite3_stmt *pStmt; /* SQL statement to execute */ va_list ap; /* ... arguments */ - int i; /* Used to iterate through parameters */ va_start(ap, iSlot); pStmt = getAndBindSqlStatement(pErr, pDb, ap); if( pStmt ){ - int rc; int first = 1; while( SQLITE_ROW==sqlite3_step(pStmt) ){ if( first && sqlite3_column_count(pStmt)>0 ){ @@ -693,14 +717,13 @@ static void integrity_check_x( ){ if( pErr->rc==SQLITE_OK ){ Statement *pStatement; /* Statement to execute */ - int rc; /* Return code */ char *zErr = 0; /* Integrity check error */ pStatement = getSqlStatement(pErr, pDb, "PRAGMA integrity_check"); if( pStatement ){ sqlite3_stmt *pStmt = pStatement->pStmt; while( SQLITE_ROW==sqlite3_step(pStmt) ){ - const char *z = sqlite3_column_text(pStmt, 0); + const char *z = (const char*)sqlite3_column_text(pStmt, 0); if( strcmp(z, "ok") ){ if( zErr==0 ){ zErr = sqlite3_mprintf("%s", z); @@ -721,14 +744,14 @@ static void integrity_check_x( static void *launch_thread_main(void *pArg){ Thread *p = (Thread *)pArg; - return (void *)p->xProc(p->iTid, p->iArg); + return (void *)p->xProc(p->iTid, p->pArg); } static void launch_thread_x( Error *pErr, /* IN/OUT: Error code */ Threadset *pThreads, /* Thread set */ - char *(*xProc)(int, int), /* Proc to run */ - int iArg /* Argument passed to thread proc */ + char *(*xProc)(int, void*), /* Proc to run */ + void *pArg /* Argument passed to thread proc */ ){ if( pErr->rc==SQLITE_OK ){ int iTid = ++pThreads->iMaxTid; @@ -738,7 +761,7 @@ static void launch_thread_x( p = (Thread *)sqlite3_malloc(sizeof(Thread)); memset(p, 0, sizeof(Thread)); p->iTid = iTid; - p->iArg = iArg; + p->pArg = pArg; p->xProc = xProc; rc = pthread_create(&p->tid, NULL, launch_thread_main, (void *)p); @@ -767,6 +790,7 @@ static void join_all_threads_x( if( pErr->rc==SQLITE_OK ) system_error(pErr, rc); }else{ printf("Thread %d says: %s\n", p->iTid, (ret==0 ? "..." : (char *)ret)); + fflush(stdout); } sqlite3_free(p); } @@ -880,11 +904,6 @@ static int timetostop_x( return ret; } -/* -** The "Set Error Line" macro. -*/ -#define SEL(e) ((e)->iLine = ((e)->rc ? (e)->iLine : __LINE__)) - /************************************************************************* ************************************************************************** @@ -895,7 +914,7 @@ static int timetostop_x( #define WALTHREAD1_NTHREAD 10 #define WALTHREAD3_NTHREAD 6 -static char *walthread1_thread(int iTid, int iArg){ +static char *walthread1_thread(int iTid, void *pArg){ Error err = {0}; /* Error code and message */ Sqlite db = {0}; /* SQLite database connection */ int nIter = 0; /* Iterations so far */ @@ -934,7 +953,7 @@ static char *walthread1_thread(int iTid, int iArg){ return sqlite3_mprintf("%d iterations", nIter); } -static char *walthread1_ckpt_thread(int iTid, int iArg){ +static char *walthread1_ckpt_thread(int iTid, void *pArg){ Error err = {0}; /* Error code and message */ Sqlite db = {0}; /* SQLite database connection */ int nCkpt = 0; /* Checkpoints so far */ @@ -966,6 +985,7 @@ static void walthread1(int nMs){ "INSERT INTO t1 VALUES(randomblob(100));" "INSERT INTO t1 SELECT md5sum(x) FROM t1;" ); + closedb(&err, &db); setstoptime(&err, nMs); for(i=0; i2 ) goto usage; - if( argc==2 ){ - zTest = argv[1]; - nTest = strlen(zTest); - if( zTest[nTest-1]=='*' ){ - nTest--; - bPrefix = 1; - } - } + static char *substArgv[] = { 0, "*", 0 }; + int i, iArg; + int nTestfound = 0; sqlite3_config(SQLITE_CONFIG_MULTITHREAD); - - for(i=0; i=sizeof(aTest)/sizeof(aTest[0]) ) goto usage; + } + for(iArg=1; iArg0 ? 255 : 0); usage: - printf("Usage: %s [testname|testprefix*]\n", argv[0]); + printf("Usage: %s [testname|testprefix*]...\n", argv[0]); printf("Available tests are:\n"); for(i=0; i +#include +#include +#include +#include +#include +#include + +/* +** An instance of the following structure is passed into each worker +** thread. +*/ +typedef struct WorkerInfo WorkerInfo; +struct WorkerInfo { + int tid; /* Thread ID */ + int nWorker; /* Total number of workers */ + unsigned wkrFlags; /* Flags */ + sqlite3 *mainDb; /* Database connection of the main thread */ + sqlite3 *db; /* Database connection of this thread */ + int nErr; /* Number of errors seen by this thread */ + int nTest; /* Number of tests run by this thread */ + char *zMsg; /* Message returned by this thread */ + pthread_t id; /* Thread id */ + pthread_mutex_t *pWrMutex; /* Hold this mutex while writing */ +}; + +/* +** Allowed values for WorkerInfo.wkrFlags +*/ +#define TT4_SERIALIZED 0x0000001 /* The --serialized option is used */ +#define TT4_WAL 0x0000002 /* WAL mode in use */ +#define TT4_TRACE 0x0000004 /* Trace activity */ + + +/* +** Report an OOM error and die if the argument is NULL +*/ +static void check_oom(void *x){ + if( x==0 ){ + fprintf(stderr, "out of memory\n"); + exit(1); + } +} + +/* +** Allocate memory. If the allocation fails, print an error message and +** kill the process. +*/ +static void *safe_malloc(int sz){ + void *x = sqlite3_malloc(sz>0?sz:1); + check_oom(x); + return x; +} + +/* +** Print a trace message for a worker +*/ +static void worker_trace(WorkerInfo *p, const char *zFormat, ...){ + va_list ap; + char *zMsg; + if( (p->wkrFlags & TT4_TRACE)==0 ) return; + va_start(ap, zFormat); + zMsg = sqlite3_vmprintf(zFormat, ap); + check_oom(zMsg); + va_end(ap); + fprintf(stderr, "TRACE(%02d): %s\n", p->tid, zMsg); + sqlite3_free(zMsg); +} + +/* +** Prepare a single SQL query +*/ +static sqlite3_stmt *prep_sql(sqlite3 *db, const char *zFormat, ...){ + va_list ap; + char *zSql; + int rc; + sqlite3_stmt *pStmt = 0; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + check_oom(zSql); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + fprintf(stderr, "SQL error (%d,%d): %s\nWhile preparing: [%s]\n", + rc, sqlite3_extended_errcode(db), sqlite3_errmsg(db), zSql); + exit(1); + } + sqlite3_free(zSql); + return pStmt; +} + +/* +** Run a SQL statements. Panic if unable. +*/ +static void run_sql(WorkerInfo *p, const char *zFormat, ...){ + va_list ap; + char *zSql; + int rc; + sqlite3_stmt *pStmt = 0; + int nRetry = 0; + + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + check_oom(zSql); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + fprintf(stderr, "SQL error (%d,%d): %s\nWhile preparing: [%s]\n", + rc, sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zSql); + exit(1); + } + worker_trace(p, "running [%s]", zSql); + while( (rc = sqlite3_step(pStmt))!=SQLITE_DONE ){ + if( (rc&0xff)==SQLITE_BUSY || (rc&0xff)==SQLITE_LOCKED ){ + sqlite3_reset(pStmt); + nRetry++; + if( nRetry<10 ){ + worker_trace(p, "retry %d for [%s]", nRetry, zSql); + sched_yield(); + continue; + }else{ + fprintf(stderr, "Deadlock in thread %d while running [%s]\n", + p->tid, zSql); + exit(1); + } + } + if( rc!=SQLITE_ROW ){ + fprintf(stderr, "SQL error (%d,%d): %s\nWhile running [%s]\n", + rc, sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zSql); + exit(1); + } + } + sqlite3_free(zSql); + sqlite3_finalize(pStmt); +} + + +/* +** Open the database connection for WorkerInfo. The order in which +** the files are opened is a function of the tid value. +*/ +static void worker_open_connection(WorkerInfo *p, int iCnt){ + char *zFile; + int x; + int rc; + static const unsigned char aOrder[6][3] = { + { 1, 2, 3}, + { 1, 3, 2}, + { 2, 1, 3}, + { 2, 3, 1}, + { 3, 1, 2}, + { 3, 2, 1} + }; + x = (p->tid + iCnt) % 6; + zFile = sqlite3_mprintf("tt4-test%d.db", aOrder[x][0]); + check_oom(zFile); + worker_trace(p, "open %s", zFile); + rc = sqlite3_open_v2(zFile, &p->db, + SQLITE_OPEN_READWRITE|SQLITE_OPEN_SHAREDCACHE, 0); + if( rc!=SQLITE_OK ){ + fprintf(stderr, "sqlite_open_v2(%s) failed on thread %d\n", + zFile, p->tid); + exit(1); + } + sqlite3_free(zFile); + run_sql(p, "PRAGMA read_uncommitted=ON;"); + sqlite3_busy_timeout(p->db, 10000); + run_sql(p, "PRAGMA synchronous=OFF;"); + run_sql(p, "ATTACH 'tt4-test%d.db' AS aux1", aOrder[x][1]); + run_sql(p, "ATTACH 'tt4-test%d.db' AS aux2", aOrder[x][2]); +} + +/* +** Close the worker database connection +*/ +static void worker_close_connection(WorkerInfo *p){ + if( p->db ){ + worker_trace(p, "close"); + sqlite3_close(p->db); + p->db = 0; + } +} + +/* +** Delete all content in the three databases associated with a +** single thread. Make this happen all in a single transaction if +** inTrans is true, or separately for each database if inTrans is +** false. +*/ +static void worker_delete_all_content(WorkerInfo *p, int inTrans){ + if( inTrans ){ + pthread_mutex_lock(p->pWrMutex); + run_sql(p, "BEGIN"); + run_sql(p, "DELETE FROM t1 WHERE tid=%d", p->tid); + run_sql(p, "DELETE FROM t2 WHERE tid=%d", p->tid); + run_sql(p, "DELETE FROM t3 WHERE tid=%d", p->tid); + run_sql(p, "COMMIT"); + pthread_mutex_unlock(p->pWrMutex); + p->nTest++; + }else{ + pthread_mutex_lock(p->pWrMutex); + run_sql(p, "DELETE FROM t1 WHERE tid=%d", p->tid); + pthread_mutex_unlock(p->pWrMutex); + p->nTest++; + pthread_mutex_lock(p->pWrMutex); + run_sql(p, "DELETE FROM t2 WHERE tid=%d", p->tid); + pthread_mutex_unlock(p->pWrMutex); + p->nTest++; + pthread_mutex_lock(p->pWrMutex); + run_sql(p, "DELETE FROM t3 WHERE tid=%d", p->tid); + pthread_mutex_unlock(p->pWrMutex); + p->nTest++; + } +} + +/* +** Create rows mn through mx in table iTab for the given worker +*/ +static void worker_add_content(WorkerInfo *p, int mn, int mx, int iTab){ + char *zTabDef; + switch( iTab ){ + case 1: zTabDef = "t1(tid,sp,a,b,c)"; break; + case 2: zTabDef = "t2(tid,sp,d,e,f)"; break; + case 3: zTabDef = "t3(tid,sp,x,y,z)"; break; + } + pthread_mutex_lock(p->pWrMutex); + run_sql(p, + "WITH RECURSIVE\n" + " c(i) AS (VALUES(%d) UNION ALL SELECT i+1 FROM c WHERE i<%d)\n" + "INSERT INTO %s SELECT %d, zeroblob(3000), i, printf('%%d',i), i FROM c;", + mn, mx, zTabDef, p->tid + ); + pthread_mutex_unlock(p->pWrMutex); + p->nTest++; +} + +/* +** Set an error message on a worker +*/ +static void worker_error(WorkerInfo *p, const char *zFormat, ...){ + va_list ap; + p->nErr++; + sqlite3_free(p->zMsg); + va_start(ap, zFormat); + p->zMsg = sqlite3_vmprintf(zFormat, ap); + va_end(ap); +} + +/* +** Each thread runs the following function. +*/ +static void *worker_thread(void *pArg){ + WorkerInfo *p = (WorkerInfo*)pArg; + int iOuter; + int i; + int rc; + sqlite3_stmt *pStmt; + + printf("worker %d startup\n", p->tid); fflush(stdout); + for(iOuter=1; iOuter<=p->nWorker; iOuter++){ + worker_open_connection(p, iOuter); + for(i=0; i<4; i++){ + worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter)%3 + 1); + worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+1)%3 + 1); + worker_add_content(p, i*100+1, (i+1)*100, (p->tid+iOuter+2)%3 + 1); + } + + pStmt = prep_sql(p->db, "SELECT count(a) FROM t1 WHERE tid=%d", p->tid); + worker_trace(p, "query [%s]", sqlite3_sql(pStmt)); + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + worker_error(p, "Failed to step: %s", sqlite3_sql(pStmt)); + }else if( sqlite3_column_int(pStmt, 0)!=400 ){ + worker_error(p, "Wrong result: %d", sqlite3_column_int(pStmt,0)); + } + sqlite3_finalize(pStmt); + if( p->nErr ) break; + + if( ((iOuter+p->tid)%3)==0 ){ + sqlite3_db_release_memory(p->db); + p->nTest++; + } + + pthread_mutex_lock(p->pWrMutex); + run_sql(p, "BEGIN;"); + run_sql(p, "UPDATE t1 SET c=NULL WHERE a=55"); + run_sql(p, "UPDATE t2 SET f=NULL WHERE d=42"); + run_sql(p, "UPDATE t3 SET z=NULL WHERE x=31"); + run_sql(p, "ROLLBACK;"); + p->nTest++; + pthread_mutex_unlock(p->pWrMutex); + + + if( iOuter==p->tid ){ + pthread_mutex_lock(p->pWrMutex); + run_sql(p, "VACUUM"); + pthread_mutex_unlock(p->pWrMutex); + } + + pStmt = prep_sql(p->db, + "SELECT t1.rowid, t2.rowid, t3.rowid" + " FROM t1, t2, t3" + " WHERE t1.tid=%d AND t2.tid=%d AND t3.tid=%d" + " AND t1.a<>t2.d AND t2.d<>t3.x" + " ORDER BY 1, 2, 3" + ,p->tid, p->tid, p->tid); + worker_trace(p, "query [%s]", sqlite3_sql(pStmt)); + for(i=0; inWorker; i++){ + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + worker_error(p, "Failed to step: %s", sqlite3_sql(pStmt)); + break; + } + sched_yield(); + } + sqlite3_finalize(pStmt); + if( p->nErr ) break; + + worker_delete_all_content(p, (p->tid+iOuter)%2); + worker_close_connection(p); + p->db = 0; + } + worker_close_connection(p); + printf("worker %d finished\n", p->tid); fflush(stdout); + return 0; +} + +int main(int argc, char **argv){ + int nWorker = 0; /* Number of worker threads */ + int i; /* Loop counter */ + WorkerInfo *aInfo; /* Information for each worker */ + unsigned wkrFlags = 0; /* Default worker flags */ + int nErr = 0; /* Number of errors */ + int nTest = 0; /* Number of tests */ + int rc; /* Return code */ + sqlite3 *db = 0; /* Main database connection */ + pthread_mutex_t wrMutex; /* The write serialization mutex */ + WorkerInfo infoTop; /* WorkerInfo for the main thread */ + WorkerInfo *p; /* Pointer to infoTop */ + + sqlite3_config(SQLITE_CONFIG_MULTITHREAD); + for(i=1; i='1' && z[0]<='9' && nWorker==0 ){ + nWorker = atoi(z); + if( nWorker<2 ){ + fprintf(stderr, "minimum of 2 threads\n"); + exit(1); + } + }else{ + fprintf(stderr, "extra command-line argument: \"%s\"\n", argv[i]); + exit(1); + } + } + if( nWorker==0 ){ + fprintf(stderr, + "usage: %s ?OPTIONS? N\n" + "N is the number of threads and must be at least 2.\n" + "Options:\n" + " --serialized\n" + " --multithread\n" + " --wal\n" + " --trace\n" + ,argv[0] + ); + exit(1); + } + if( !sqlite3_threadsafe() ){ + fprintf(stderr, "requires a threadsafe build of SQLite\n"); + exit(1); + } + sqlite3_initialize(); + sqlite3_enable_shared_cache(1); + pthread_mutex_init(&wrMutex, 0); + + /* Initialize the test database files */ + (void)unlink("tt4-test1.db"); + (void)unlink("tt4-test2.db"); + (void)unlink("tt4-test3.db"); + rc = sqlite3_open("tt4-test1.db", &db); + if( rc!=SQLITE_OK ){ + fprintf(stderr, "Unable to open test database: tt4-test2.db\n"); + exit(1); + } + memset(&infoTop, 0, sizeof(infoTop)); + infoTop.db = db; + infoTop.wkrFlags = wkrFlags; + p = &infoTop; + if( wkrFlags & TT4_WAL ){ + run_sql(p, "PRAGMA journal_mode=WAL"); + } + run_sql(p, "PRAGMA synchronous=OFF"); + run_sql(p, "CREATE TABLE IF NOT EXISTS t1(tid INTEGER, sp, a, b, c)"); + run_sql(p, "CREATE INDEX t1tid ON t1(tid)"); + run_sql(p, "CREATE INDEX t1ab ON t1(a,b)"); + run_sql(p, "ATTACH 'tt4-test2.db' AS 'test2'"); + run_sql(p, "CREATE TABLE IF NOT EXISTS test2.t2(tid INTEGER, sp, d, e, f)"); + run_sql(p, "CREATE INDEX test2.t2tid ON t2(tid)"); + run_sql(p, "CREATE INDEX test2.t2de ON t2(d,e)"); + run_sql(p, "ATTACH 'tt4-test3.db' AS 'test3'"); + run_sql(p, "CREATE TABLE IF NOT EXISTS test3.t3(tid INTEGER, sp, x, y, z)"); + run_sql(p, "CREATE INDEX test3.t3tid ON t3(tid)"); + run_sql(p, "CREATE INDEX test3.t3xy ON t3(x,y)"); + aInfo = safe_malloc( sizeof(*aInfo)*nWorker ); + memset(aInfo, 0, sizeof(*aInfo)*nWorker); + for(i=0; izDb, 0); + for(cnt=0; err.rc==SQLITE_OK && cntxProc(&err, &db, i1); + i2 += (err.rc==SQLITE_OK); + clear_error(&err, SQLITE_LOCKED); + i1++; + } + closedb(&err, &db); + } + + print_and_free_err(&err); + return sqlite3_mprintf("ok %d/%d", i2, i1); +} + +static void stress2_launch_thread_loop( + Error *pErr, /* IN/OUT: Error code */ + Threadset *pThreads, /* Thread set */ + const char *zDb, /* Database name */ + void (*x)(Error*,Sqlite*,int) /* Run this until error or timeout */ +){ + Stress2Ctx *pCtx = sqlite3_malloc(sizeof(Stress2Ctx)); + pCtx->zDb = zDb; + pCtx->xProc = x; + launch_thread(pErr, pThreads, stress2_thread_wrapper, (void*)pCtx); +} + +static void stress2(int nMs){ + struct Stress2Task { + void (*x)(Error*,Sqlite*,int); + } aTask[] = { + { stress2_workload1 }, + { stress2_workload2 }, + { stress2_workload3 }, + { stress2_workload4 }, + { stress2_workload5 }, + { stress2_workload6 }, + { stress2_workload7 }, + { stress2_workload8 }, + { stress2_workload9 }, + { stress2_workload10 }, + { stress2_workload11 }, + { stress2_workload14 }, + { stress2_workload17 }, + }; + const char *zDb = "test.db"; + + int i; + Error err = {0}; + Sqlite db = {0}; + Threadset threads = {0}; + + /* To make sure the db file is empty before commencing */ + opendb(&err, &db, zDb, 1); + sql_script(&err, &db, + "CREATE TABLE IF NOT EXISTS t0(x PRIMARY KEY, y, z);" + "CREATE INDEX IF NOT EXISTS i0 ON t0(y);" + ); + closedb(&err, &db); + + setstoptime(&err, nMs); + sqlite3_enable_shared_cache(1); + + for(i=0; i%*s %s\n",op[i].label, + fprintf(errstream," -%s%*s %s\n",op[i].label, (int)(max-lemonStrlen(op[i].label)-9),"",op[i].message); break; case OPT_DBL: case OPT_FDBL: - fprintf(errstream," %s=%*s %s\n",op[i].label, + fprintf(errstream," -%s%*s %s\n",op[i].label, (int)(max-lemonStrlen(op[i].label)-6),"",op[i].message); break; case OPT_STR: case OPT_FSTR: - fprintf(errstream," %s=%*s %s\n",op[i].label, + fprintf(errstream," -%s%*s %s\n",op[i].label, (int)(max-lemonStrlen(op[i].label)-8),"",op[i].message); break; } @@ -2436,7 +2442,7 @@ to follow the previous rule."); if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){ const char *zOld, *zNew; char *zBuf, *z; - int nOld, n, nLine, nNew, nBack; + int nOld, n, nLine = 0, nNew, nBack; int addLineMacro; char zLine[50]; zNew = x; @@ -2635,7 +2641,7 @@ void Parse(struct lemon *gp) struct pstate ps; FILE *fp; char *filebuf; - int filesize; + unsigned int filesize; int lineno; int c; char *cp, *nextcp; @@ -2769,7 +2775,7 @@ void Parse(struct lemon *gp) c = *cp; *cp = 0; /* Null terminate the token */ parseonetoken(&ps); /* Parse the token */ - *cp = c; /* Restore the buffer */ + *cp = (char)c; /* Restore the buffer */ cp = nextcp; } free(filebuf); /* Release the buffer after parsing */ @@ -3392,7 +3398,7 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ zText++; n--; }else{ - z[used++] = c; + z[used++] = (char)c; } } z[used] = 0; diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh index 4829277234..0c2668c8b7 100644 --- a/tool/mkautoconfamal.sh +++ b/tool/mkautoconfamal.sh @@ -66,9 +66,9 @@ echo "#include \"sqlite3.c\"" >> tea/generic/tclsqlite3.c echo "#endif" >> tea/generic/tclsqlite3.c cat $TOP/src/tclsqlite.c >> tea/generic/tclsqlite3.c -cat tea/configure.in | +cat tea/configure.ac | sed "s/AC_INIT(\[sqlite\], .*)/AC_INIT([sqlite], [$VERSION])/" > tmp -mv tmp tea/configure.in +mv tmp tea/configure.ac cd tea autoconf @@ -80,4 +80,3 @@ tar -xzf sqlite-$VERSION.tar.gz mv sqlite-$VERSION sqlite-autoconf-$ARTIFACT tar -czf sqlite-autoconf-$ARTIFACT.tar.gz sqlite-autoconf-$ARTIFACT mv sqlite-autoconf-$ARTIFACT.tar.gz .. - diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl index 3c36a6c011..4e3786b8b2 100644 --- a/tool/mkpragmatab.tcl +++ b/tool/mkpragmatab.tcl @@ -246,18 +246,29 @@ set pragma_def { NAME: schema_version TYPE: HEADER_VALUE + ARG: BTREE_SCHEMA_VERSION IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: user_version TYPE: HEADER_VALUE + ARG: BTREE_USER_VERSION + IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) + + NAME: data_version + TYPE: HEADER_VALUE + ARG: BTREE_DATA_VERSION + FLAG: ReadOnly IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: freelist_count TYPE: HEADER_VALUE + ARG: BTREE_FREE_PAGE_COUNT + FLAG: ReadOnly IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: application_id TYPE: HEADER_VALUE + ARG: BTREE_APPLICATION_ID IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) NAME: compile_options diff --git a/tool/mksqlite3c-noext.tcl b/tool/mksqlite3c-noext.tcl index f54b347be1..27522265bb 100644 --- a/tool/mksqlite3c-noext.tcl +++ b/tool/mksqlite3c-noext.tcl @@ -96,6 +96,7 @@ foreach hdr { hash.h hwtime.h keywordhash.h + msvc.h mutex.h opcodes.h os_common.h diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 0e979234f3..1d597a51a0 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -100,6 +100,7 @@ foreach hdr { hash.h hwtime.h keywordhash.h + msvc.h mutex.h opcodes.h os_common.h @@ -212,7 +213,7 @@ proc copy_file {filename} { } } elseif {[regexp {^(SQLITE_EXTERN )?void \(\*sqlite3IoTrace\)} $line]} { regsub {^SQLITE_EXTERN } $line {} line - puts $out "SQLITE_PRIVATE $line" + puts $out $line } elseif {[regexp {^void \(\*sqlite3Os} $line]} { puts $out "SQLITE_PRIVATE $line" } else { diff --git a/tool/mksqlite3internalh.tcl b/tool/mksqlite3internalh.tcl index 7e92b3ad7d..8db593fe75 100644 --- a/tool/mksqlite3internalh.tcl +++ b/tool/mksqlite3internalh.tcl @@ -58,6 +58,7 @@ foreach hdr { hash.h hwtime.h keywordhash.h + msvc.h opcodes.h os_common.h os_setup.h