diff --git a/Makefile.in b/Makefile.in index 54f7643327..d8f5695e5d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -173,7 +173,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ fts5.lo \ func.lo global.lo hash.lo \ - icu.lo insert.lo journal.lo json1.lo legacy.lo loadext.lo \ + icu.lo insert.lo json1.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ @@ -224,7 +224,6 @@ SRC = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ - $(TOP)/src/journal.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ @@ -376,6 +375,7 @@ TESTSRC = \ $(TOP)/src/test_autoext.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ + $(TOP)/src/test_bestindex.c \ $(TOP)/src/test_blob.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ @@ -549,7 +549,8 @@ TESTOPTS = --verbose=file --output=test-out.txt # Extra compiler options for various shell tools # -SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 +SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4 +SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 @@ -575,9 +576,9 @@ libtclsqlite3.la: tclsqlite.lo libsqlite3.la -version-info "8:6:8" \ -avoid-version -sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h +sqlite3$(TEXE): $(TOP)/src/shell.c sqlite3.c $(LTLINK) $(READLINE_FLAGS) $(SHELL_OPT) -o $@ \ - $(TOP)/src/shell.c libsqlite3.la \ + $(TOP)/src/shell.c sqlite3.c \ $(LIBREADLINE) $(TLIBS) -rpath "$(libdir)" sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h @@ -737,9 +738,6 @@ hash.lo: $(TOP)/src/hash.c $(HDR) insert.lo: $(TOP)/src/insert.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/insert.c -journal.lo: $(TOP)/src/journal.c $(HDR) - $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/journal.c - legacy.lo: $(TOP)/src/legacy.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/legacy.c @@ -1049,6 +1047,8 @@ sqlite3rbu.lo: $(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR) TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE TESTFIXTURE_FLAGS += -DBUILD_sqlite +TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 +TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c @@ -1155,10 +1155,15 @@ 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. +# Build the amalgamation-autoconf package. The amalamgation-tarball target builds +# a tarball named for the version number. Ex: sqlite-autoconf-3110000.tar.gz. +# The snapshot-tarball target builds a tarball named by the SHA1 hash # amalgamation-tarball: sqlite3.c - TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh + TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal + +snapshot-tarball: sqlite3.c + TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot # 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 diff --git a/Makefile.msc b/Makefile.msc index 9620ff6237..15fbf6c65d 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -126,12 +126,12 @@ USE_RC = 1 FOR_WINRT = 0 !ENDIF -# Set this non-0 to compile binaries suitable for the UAP environment. +# Set this non-0 to compile binaries suitable for the UWP environment. # This setting does not apply to any binaries that require Tcl to operate # properly (i.e. the text fixture, etc). # -!IFNDEF FOR_UAP -FOR_UAP = 0 +!IFNDEF FOR_UWP +FOR_UWP = 0 !ENDIF # Set this non-0 to compile binaries suitable for the Windows 10 platform. @@ -219,27 +219,43 @@ SQLITE3H = sqlite3.h # This is the name to use for the SQLite dynamic link library (DLL). # !IFNDEF SQLITE3DLL +!IF $(FOR_WIN10)!=0 +SQLITE3DLL = winsqlite3.dll +!ELSE SQLITE3DLL = sqlite3.dll !ENDIF +!ENDIF # This is the name to use for the SQLite import library (LIB). # !IFNDEF SQLITE3LIB +!IF $(FOR_WIN10)!=0 +SQLITE3LIB = winsqlite3.lib +!ELSE SQLITE3LIB = sqlite3.lib !ENDIF +!ENDIF # This is the name to use for the SQLite shell executable (EXE). # !IFNDEF SQLITE3EXE +!IF $(FOR_WIN10)!=0 +SQLITE3EXE = winsqlite3shell.exe +!ELSE SQLITE3EXE = sqlite3.exe !ENDIF +!ENDIF # This is the argument used to set the program database (PDB) file for the # SQLite shell executable (EXE). # !IFNDEF SQLITE3EXEPDB +!IF $(FOR_WIN10)!=0 +SQLITE3EXEPDB = +!ELSE SQLITE3EXEPDB = /pdb:sqlite3sh.pdb !ENDIF +!ENDIF # These are the "standard" SQLite compilation options used when compiling for # the Windows platform. @@ -290,6 +306,14 @@ PROGRAMFILES_X86 = $(PROGRAMFILES_X86:\\=\) CC = cl.exe !ENDIF +# Check for the predefined command macro CSC. This should point to a working +# C Sharp compiler binary. If it is not defined, simply define it to the +# legacy default value 'csc.exe'. +# +!IFNDEF CSC +CSC = csc.exe +!ENDIF + # Check for the command macro LD. This should point to the linker binary for # the target platform. If it is not defined, simply define it to the legacy # default value 'link.exe'. @@ -414,17 +438,8 @@ TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS) TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS) !ENDIF -TCC = $(TCC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src -fp:precise -RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS) - -# Adjust the names of the primary targets for use with Windows 10. -# -!IF $(FOR_WIN10)!=0 -SQLITE3DLL = winsqlite3.dll -SQLITE3LIB = winsqlite3.lib -SQLITE3EXE = winsqlite3shell.exe -SQLITE3EXEPDB = -!ENDIF +TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise +RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS) # Check if we want to use the "stdcall" calling convention when compiling. # This is not supported by the compilers for non-x86 platforms. It should @@ -465,20 +480,24 @@ CORE_COMPILE_OPTS = $(CORE_CCONV_OPTS) # when linking. # !IFNDEF CORE_LINK_DEP -!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0 +!IF $(DYNAMIC_SHELL)!=0 CORE_LINK_DEP = -!ELSE +!ELSEIF $(FOR_WIN10)==0 || "$(PLATFORM)"=="x86" CORE_LINK_DEP = sqlite3.def +!ELSE +CORE_LINK_DEP = !ENDIF !ENDIF # These are additional linker options used for the core library. # !IFNDEF CORE_LINK_OPTS -!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0 +!IF $(DYNAMIC_SHELL)!=0 CORE_LINK_OPTS = -!ELSE +!ELSEIF $(FOR_WIN10)==0 || "$(PLATFORM)"=="x86" CORE_LINK_OPTS = /DEF:sqlite3.def +!ELSE +CORE_LINK_OPTS = !ENDIF !ENDIF @@ -551,8 +570,8 @@ RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP # C compiler options for the Windows 10 platform (needs MSVC 2015). # !IF $(FOR_WIN10)!=0 -TCC = $(TCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE -BCC = $(BCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE +TCC = $(TCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE +BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE !ENDIF # Also, we need to dynamically link to the correct MSVC runtime @@ -907,10 +926,10 @@ LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelH LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib !ENDIF -# When compiling for UAP or the Windows 10 platform, some extra linker +# When compiling for UWP or the Windows 10 platform, some extra linker # options are also required. # -!IF $(FOR_UAP)!=0 || $(FOR_WIN10)!=0 +!IF $(FOR_UWP)!=0 || $(FOR_WIN10)!=0 LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE /NODEFAULTLIB:kernel32.lib LTLINKOPTS = $(LTLINKOPTS) mincore.lib !IFDEF PSDKLIBPATH @@ -919,7 +938,7 @@ LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(PSDKLIBPATH)" !ENDIF !IF $(FOR_WIN10)!=0 -LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(UCRTLIBPATH)" +LTLINKOPTS = $(LTLINKOPTS) /guard:cf "/LIBPATH:$(UCRTLIBPATH)" !IF $(DEBUG)>1 LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrtd.lib /DEFAULTLIB:ucrtd.lib !ELSE @@ -966,7 +985,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \ fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ fts5.lo \ func.lo global.lo hash.lo \ - icu.lo insert.lo journal.lo legacy.lo loadext.lo \ + icu.lo insert.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ @@ -1005,9 +1024,9 @@ LIBRESOBJS = !ENDIF # <> -# All of the source code files. +# Core source code files, part 1. # -SRC1 = \ +SRC00 = \ $(TOP)\src\alter.c \ $(TOP)\src\analyze.c \ $(TOP)\src\attach.c \ @@ -1016,8 +1035,6 @@ SRC1 = \ $(TOP)\src\bitvec.c \ $(TOP)\src\btmutex.c \ $(TOP)\src\btree.c \ - $(TOP)\src\btree.h \ - $(TOP)\src\btreeInt.h \ $(TOP)\src\build.c \ $(TOP)\src\callback.c \ $(TOP)\src\complete.c \ @@ -1031,10 +1048,7 @@ SRC1 = \ $(TOP)\src\func.c \ $(TOP)\src\global.c \ $(TOP)\src\hash.c \ - $(TOP)\src\hash.h \ - $(TOP)\src\hwtime.h \ $(TOP)\src\insert.c \ - $(TOP)\src\journal.c \ $(TOP)\src\legacy.c \ $(TOP)\src\loadext.c \ $(TOP)\src\main.c \ @@ -1045,29 +1059,22 @@ SRC1 = \ $(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 \ $(TOP)\src\mutex_unix.c \ $(TOP)\src\mutex_w32.c \ $(TOP)\src\notify.c \ $(TOP)\src\os.c \ - $(TOP)\src\os.h \ - $(TOP)\src\os_common.h \ - $(TOP)\src\os_setup.h \ $(TOP)\src\os_unix.c \ - $(TOP)\src\os_win.c \ - $(TOP)\src\os_win.h -SRC2 = \ + $(TOP)\src\os_win.c + +# Core source code files, part 2. +# +SRC01 = \ $(TOP)\src\pager.c \ - $(TOP)\src\pager.h \ - $(TOP)\src\parse.y \ $(TOP)\src\pcache.c \ - $(TOP)\src\pcache.h \ $(TOP)\src\pcache1.c \ $(TOP)\src\pragma.c \ - $(TOP)\src\pragma.h \ $(TOP)\src\prepare.c \ $(TOP)\src\printf.c \ $(TOP)\src\random.c \ @@ -1075,11 +1082,6 @@ SRC2 = \ $(TOP)\src\rowset.c \ $(TOP)\src\select.c \ $(TOP)\src\status.c \ - $(TOP)\src\shell.c \ - $(TOP)\src\sqlite.h.in \ - $(TOP)\src\sqlite3ext.h \ - $(TOP)\src\sqliteInt.h \ - $(TOP)\src\sqliteLimit.h \ $(TOP)\src\table.c \ $(TOP)\src\threads.c \ $(TOP)\src\tclsqlite.c \ @@ -1091,83 +1093,132 @@ SRC2 = \ $(TOP)\src\util.c \ $(TOP)\src\vacuum.c \ $(TOP)\src\vdbe.c \ - $(TOP)\src\vdbe.h \ $(TOP)\src\vdbeapi.c \ $(TOP)\src\vdbeaux.c \ $(TOP)\src\vdbeblob.c \ $(TOP)\src\vdbemem.c \ $(TOP)\src\vdbesort.c \ $(TOP)\src\vdbetrace.c \ - $(TOP)\src\vdbeInt.h \ $(TOP)\src\vtab.c \ - $(TOP)\src\vxworks.h \ $(TOP)\src\wal.c \ - $(TOP)\src\wal.h \ $(TOP)\src\walker.c \ $(TOP)\src\where.c \ $(TOP)\src\wherecode.c \ - $(TOP)\src\whereexpr.c \ + $(TOP)\src\whereexpr.c + +# Shell source code files. +# +SRC02 = \ + $(TOP)\src\shell.c + +# Core miscellaneous files. +# +SRC03 = \ + $(TOP)\src\parse.y + +# Core header files, part 1. +# +SRC04 = \ + $(TOP)\src\btree.h \ + $(TOP)\src\btreeInt.h \ + $(TOP)\src\hash.h \ + $(TOP)\src\hwtime.h \ + $(TOP)\src\msvc.h \ + $(TOP)\src\mutex.h \ + $(TOP)\src\os.h \ + $(TOP)\src\os_common.h \ + $(TOP)\src\os_setup.h \ + $(TOP)\src\os_win.h + +# Core header files, part 2. +# +SRC05 = \ + $(TOP)\src\pager.h \ + $(TOP)\src\pcache.h \ + $(TOP)\src\pragma.h \ + $(TOP)\src\sqlite.h.in \ + $(TOP)\src\sqlite3ext.h \ + $(TOP)\src\sqliteInt.h \ + $(TOP)\src\sqliteLimit.h \ + $(TOP)\src\vdbe.h \ + $(TOP)\src\vdbeInt.h \ + $(TOP)\src\vxworks.h \ + $(TOP)\src\wal.h \ $(TOP)\src\whereInt.h -# Source code for extensions +# Extension source code files, part 1. # -SRC3 = \ +SRC06 = \ $(TOP)\ext\fts1\fts1.c \ - $(TOP)\ext\fts1\fts1.h \ $(TOP)\ext\fts1\fts1_hash.c \ - $(TOP)\ext\fts1\fts1_hash.h \ $(TOP)\ext\fts1\fts1_porter.c \ - $(TOP)\ext\fts1\fts1_tokenizer.h \ $(TOP)\ext\fts1\fts1_tokenizer1.c \ $(TOP)\ext\fts2\fts2.c \ - $(TOP)\ext\fts2\fts2.h \ $(TOP)\ext\fts2\fts2_hash.c \ - $(TOP)\ext\fts2\fts2_hash.h \ $(TOP)\ext\fts2\fts2_icu.c \ $(TOP)\ext\fts2\fts2_porter.c \ - $(TOP)\ext\fts2\fts2_tokenizer.h \ $(TOP)\ext\fts2\fts2_tokenizer.c \ $(TOP)\ext\fts2\fts2_tokenizer1.c -SRC4 = \ + +# Extension source code files, part 2. +# +SRC07 = \ $(TOP)\ext\fts3\fts3.c \ - $(TOP)\ext\fts3\fts3.h \ - $(TOP)\ext\fts3\fts3Int.h \ $(TOP)\ext\fts3\fts3_aux.c \ $(TOP)\ext\fts3\fts3_expr.c \ $(TOP)\ext\fts3\fts3_hash.c \ - $(TOP)\ext\fts3\fts3_hash.h \ $(TOP)\ext\fts3\fts3_icu.c \ $(TOP)\ext\fts3\fts3_porter.c \ $(TOP)\ext\fts3\fts3_snippet.c \ - $(TOP)\ext\fts3\fts3_tokenizer.h \ $(TOP)\ext\fts3\fts3_tokenizer.c \ $(TOP)\ext\fts3\fts3_tokenizer1.c \ $(TOP)\ext\fts3\fts3_tokenize_vtab.c \ $(TOP)\ext\fts3\fts3_unicode.c \ $(TOP)\ext\fts3\fts3_unicode2.c \ $(TOP)\ext\fts3\fts3_write.c \ - $(TOP)\ext\icu\sqliteicu.h \ $(TOP)\ext\icu\icu.c \ - $(TOP)\ext\rtree\rtree.h \ $(TOP)\ext\rtree\rtree.c \ - $(TOP)\ext\rbu\sqlite3rbu.h \ $(TOP)\ext\rbu\sqlite3rbu.c \ $(TOP)\ext\misc\json1.c +# Extension header files, part 1. +# +SRC08 = \ + $(TOP)\ext\fts1\fts1.h \ + $(TOP)\ext\fts1\fts1_hash.h \ + $(TOP)\ext\fts1\fts1_tokenizer.h \ + $(TOP)\ext\fts2\fts2.h \ + $(TOP)\ext\fts2\fts2_hash.h \ + $(TOP)\ext\fts2\fts2_tokenizer.h + +# Extension header files, part 2. +# +SRC09 = \ + $(TOP)\ext\fts3\fts3.h \ + $(TOP)\ext\fts3\fts3Int.h \ + $(TOP)\ext\fts3\fts3_hash.h \ + $(TOP)\ext\fts3\fts3_tokenizer.h \ + $(TOP)\ext\icu\sqliteicu.h \ + $(TOP)\ext\rtree\rtree.h \ + $(TOP)\ext\rbu\sqlite3rbu.h # Generated source code files # -SRC5 = \ - keywordhash.h \ +SRC10 = \ opcodes.c \ + parse.c + +# Generated header files +# +SRC11 = \ + keywordhash.h \ opcodes.h \ - parse.c \ parse.h \ $(SQLITE3H) # All source code files. # -SRC = $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) +SRC = $(SRC00) $(SRC01) $(SRC02) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11) # Source code to the test files. # @@ -1184,6 +1235,7 @@ TESTSRC = \ $(TOP)\src\test_autoext.c \ $(TOP)\src\test_async.c \ $(TOP)\src\test_backup.c \ + $(TOP)\src\test_bestindex.c \ $(TOP)\src\test_blob.c \ $(TOP)\src\test_btree.c \ $(TOP)\src\test_config.c \ @@ -1216,7 +1268,7 @@ TESTSRC = \ $(TOP)\ext\fts3\fts3_test.c \ $(TOP)\ext\rbu\test_rbu.c -# Statically linked extensions +# Statically linked extensions. # TESTEXT = \ $(TOP)\ext\misc\amatch.c \ @@ -1236,56 +1288,14 @@ TESTEXT = \ $(TOP)\ext\misc\totype.c \ $(TOP)\ext\misc\wholenumber.c - # Source code to the library files needed by the test fixture # TESTSRC2 = \ - $(TOP)\src\attach.c \ - $(TOP)\src\backup.c \ - $(TOP)\src\bitvec.c \ - $(TOP)\src\btree.c \ - $(TOP)\src\build.c \ - $(TOP)\src\ctime.c \ - $(TOP)\src\date.c \ - $(TOP)\src\dbstat.c \ - $(TOP)\src\expr.c \ - $(TOP)\src\func.c \ - $(TOP)\src\insert.c \ - $(TOP)\src\wal.c \ - $(TOP)\src\main.c \ - $(TOP)\src\mem5.c \ - $(TOP)\src\os.c \ - $(TOP)\src\os_unix.c \ - $(TOP)\src\os_win.c \ - $(TOP)\src\pager.c \ - $(TOP)\src\pragma.c \ - $(TOP)\src\prepare.c \ - $(TOP)\src\printf.c \ - $(TOP)\src\random.c \ - $(TOP)\src\pcache.c \ - $(TOP)\src\pcache1.c \ - $(TOP)\src\select.c \ - $(TOP)\src\tokenize.c \ - $(TOP)\src\utf.c \ - $(TOP)\src\util.c \ - $(TOP)\src\vdbeapi.c \ - $(TOP)\src\vdbeaux.c \ - $(TOP)\src\vdbe.c \ - $(TOP)\src\vdbemem.c \ - $(TOP)\src\vdbesort.c \ - $(TOP)\src\vdbetrace.c \ - $(TOP)\src\where.c \ - $(TOP)\src\wherecode.c \ - $(TOP)\src\whereexpr.c \ - parse.c \ - $(TOP)\ext\fts3\fts3.c \ - $(TOP)\ext\fts3\fts3_aux.c \ - $(TOP)\ext\fts3\fts3_expr.c \ - $(TOP)\ext\fts3\fts3_tokenizer.c \ - $(TOP)\ext\fts3\fts3_tokenize_vtab.c \ - $(TOP)\ext\fts3\fts3_unicode.c \ - $(TOP)\ext\fts3\fts3_unicode2.c \ - $(TOP)\ext\fts3\fts3_write.c \ + $(SRC00) \ + $(SRC01) \ + $(SRC06) \ + $(SRC07) \ + $(SRC10) \ $(TOP)\ext\async\sqlite3async.c # Header files used by all library source files. @@ -1359,7 +1369,7 @@ FUZZDATA = \ # when the shell is not being dynamically linked. # !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 -SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 +SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS !ENDIF # <> @@ -1389,30 +1399,30 @@ all: dll libsqlite3.lib shell $(ALL_TCL_TARGETS) # Dynamic link library section. # -dll: $(SQLITE3DLL) +dll: $(SQLITE3DLL) # Shell executable. # -shell: $(SQLITE3EXE) +shell: $(SQLITE3EXE) +# <> libsqlite3.lib: $(LIBOBJ) $(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS) -# <> libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS) # <> -$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) +$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) -# <> -sqlite3.def: libsqlite3.lib +# <> +sqlite3.def: libsqlite3.lib echo EXPORTS > sqlite3.def dumpbin /all libsqlite3.lib \ - | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3_.*)$$" \1 \ + | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3_[^@]*)(?:@\d+)?$$" \1 \ | sort >> sqlite3.def -# <> +# <> $(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H) $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c $(SHELL_CORE_SRC) \ @@ -1460,11 +1470,18 @@ mptest: mptester.exe .target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl fts5.c -rmdir /Q/S tsrc 2>NUL -mkdir tsrc - for %i in ($(SRC1)) do copy /Y %i tsrc - for %i in ($(SRC2)) do copy /Y %i tsrc - for %i in ($(SRC3)) do copy /Y %i tsrc - for %i in ($(SRC4)) do copy /Y %i tsrc - for %i in ($(SRC5)) do copy /Y %i tsrc + for %i in ($(SRC00)) do copy /Y %i tsrc + for %i in ($(SRC01)) do copy /Y %i tsrc + for %i in ($(SRC02)) do copy /Y %i tsrc + for %i in ($(SRC03)) do copy /Y %i tsrc + for %i in ($(SRC04)) do copy /Y %i tsrc + for %i in ($(SRC05)) do copy /Y %i tsrc + for %i in ($(SRC06)) do copy /Y %i tsrc + for %i in ($(SRC07)) do copy /Y %i tsrc + for %i in ($(SRC08)) do copy /Y %i tsrc + for %i in ($(SRC09)) do copy /Y %i tsrc + for %i in ($(SRC10)) do copy /Y %i tsrc + for %i in ($(SRC11)) do copy /Y %i tsrc copy /Y fts5.c tsrc copy /Y fts5.h tsrc del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL @@ -1592,9 +1609,6 @@ hash.lo: $(TOP)\src\hash.c $(HDR) insert.lo: $(TOP)\src\insert.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\insert.c -journal.lo: $(TOP)\src\journal.c $(HDR) - $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\journal.c - legacy.lo: $(TOP)\src\legacy.c $(HDR) $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\legacy.c @@ -1779,7 +1793,7 @@ parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl $(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) -sqlite3ext.h: .target_source +sqlite3ext.h: .target_source copy tsrc\sqlite3ext.h . mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c @@ -1880,9 +1894,9 @@ fts5parse.c: $(TOP)\ext\fts5\fts5parse.y lemon.exe del /Q fts5parse.h 2>NUL .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) fts5parse.y -fts5parse.h: fts5parse.c +fts5parse.h: fts5parse.c -fts5.c: $(FTS5_SRC) +fts5.c: $(FTS5_SRC) $(TCLSH_CMD) $(TOP)\ext\fts5\tool\mkfts5c.tcl copy $(TOP)\ext\fts5\fts5.h . @@ -1908,8 +1922,10 @@ sqlite3rbu.lo: $(TOP)\ext\rbu\sqlite3rbu.c $(HDR) $(EXTHDR) TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN) +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 -TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) $(SHELL_CORE_DEP) +TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C) !IF $(USE_AMALGAMATION)==0 TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0) @@ -1923,7 +1939,7 @@ testfixture.exe: $(TESTFIXTURE_SRC) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(TESTFIXTURE_SRC) \ /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) -extensiontest: testfixture.exe testloadext.dll +extensiontest: testfixture.exe testloadext.dll @set PATH=$(LIBTCLPATH);$(PATH) .\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS) @@ -1966,7 +1982,7 @@ smoketest: $(TESTPROGS) @set PATH=$(LIBTCLPATH);$(PATH) .\testfixture.exe $(TOP)\test\main.test $(TESTOPTS) -sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl +sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl echo #define TCLSH 2 > $@ echo #define SQLITE_ENABLE_DBSTAT_VTAB 1 >> $@ copy $@ + $(SQLITE3C) + $(TOP)\src\tclsqlite.c $@ @@ -1982,7 +1998,7 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS) testloadext.lo: $(TOP)\src\test_loadext.c $(LTCOMPILE) $(NO_WARN) -c $(TOP)\src\test_loadext.c -testloadext.dll: testloadext.lo +testloadext.dll: testloadext.lo $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C) $(SQLITE3H) @@ -2020,16 +2036,21 @@ speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ $(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) -rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H) +rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU -Fe$@ \ $(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) + +moreclean: clean + del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL # <> clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL - del /Q *.bsc *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL # <> - del /Q $(SQLITE3C) $(SQLITE3H) opcodes.c opcodes.h 2>NUL + del /Q sqlite3.c sqlite3.h 2>NUL + del /Q opcodes.c opcodes.h 2>NUL del /Q lemon.* lempar.c parse.* 2>NUL del /Q mkkeywordhash.* keywordhash.h 2>NUL del /Q notasharedlib.* 2>NUL @@ -2046,7 +2067,6 @@ clean: del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe 2>NUL del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL del /Q mptester.exe wordcount.exe rbu.exe srcck1.exe 2>NUL - del /Q $(SQLITE3EXE) $(SQLITE3DLL) sqlite3.def 2>NUL del /Q sqlite3.c sqlite3-*.c 2>NUL del /Q sqlite3rc.h 2>NUL del /Q shell.c sqlite3ext.h 2>NUL diff --git a/VERSION b/VERSION index afad818663..92536a9e48 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.11.0 +3.12.0 diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am index 03424a606b..7b6934adde 100644 --- a/autoconf/Makefile.am +++ b/autoconf/Makefile.am @@ -6,15 +6,14 @@ libsqlite3_la_SOURCES = sqlite3.c libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 bin_PROGRAMS = sqlite3 -sqlite3_SOURCES = shell.c sqlite3.h -EXTRA_sqlite3_SOURCES = sqlite3.c -sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@ +sqlite3_SOURCES = shell.c sqlite3.c sqlite3.h +sqlite3_LDADD = @READLINE_LIBS@ sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@ -sqlite3_CFLAGS = $(AM_CFLAGS) +sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS include_HEADERS = sqlite3.h sqlite3ext.h -EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt +EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs pkgconfigdir = ${libdir}/pkgconfig pkgconfig_DATA = sqlite3.pc diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc index 65f2396180..ad829bffa0 100644 --- a/autoconf/Makefile.msc +++ b/autoconf/Makefile.msc @@ -119,12 +119,12 @@ USE_RC = 1 FOR_WINRT = 0 !ENDIF -# Set this non-0 to compile binaries suitable for the UAP environment. +# Set this non-0 to compile binaries suitable for the UWP environment. # This setting does not apply to any binaries that require Tcl to operate # properly (i.e. the text fixture, etc). # -!IFNDEF FOR_UAP -FOR_UAP = 0 +!IFNDEF FOR_UWP +FOR_UWP = 0 !ENDIF # Set this non-0 to compile binaries suitable for the Windows 10 platform. @@ -204,27 +204,43 @@ SQLITE3H = sqlite3.h # This is the name to use for the SQLite dynamic link library (DLL). # !IFNDEF SQLITE3DLL +!IF $(FOR_WIN10)!=0 +SQLITE3DLL = winsqlite3.dll +!ELSE SQLITE3DLL = sqlite3.dll !ENDIF +!ENDIF # This is the name to use for the SQLite import library (LIB). # !IFNDEF SQLITE3LIB +!IF $(FOR_WIN10)!=0 +SQLITE3LIB = winsqlite3.lib +!ELSE SQLITE3LIB = sqlite3.lib !ENDIF +!ENDIF # This is the name to use for the SQLite shell executable (EXE). # !IFNDEF SQLITE3EXE +!IF $(FOR_WIN10)!=0 +SQLITE3EXE = winsqlite3shell.exe +!ELSE SQLITE3EXE = sqlite3.exe !ENDIF +!ENDIF # This is the argument used to set the program database (PDB) file for the # SQLite shell executable (EXE). # !IFNDEF SQLITE3EXEPDB +!IF $(FOR_WIN10)!=0 +SQLITE3EXEPDB = +!ELSE SQLITE3EXEPDB = /pdb:sqlite3sh.pdb !ENDIF +!ENDIF # These are the "standard" SQLite compilation options used when compiling for # the Windows platform. @@ -275,6 +291,14 @@ PROGRAMFILES_X86 = $(PROGRAMFILES_X86:\\=\) CC = cl.exe !ENDIF +# Check for the predefined command macro CSC. This should point to a working +# C Sharp compiler binary. If it is not defined, simply define it to the +# legacy default value 'csc.exe'. +# +!IFNDEF CSC +CSC = csc.exe +!ENDIF + # Check for the command macro LD. This should point to the linker binary for # the target platform. If it is not defined, simply define it to the legacy # default value 'link.exe'. @@ -399,17 +423,8 @@ TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS) TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS) !ENDIF -TCC = $(TCC) -DSQLITE_OS_WIN=1 -I$(TOP) -fp:precise -RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) $(RCOPTS) $(RCCOPTS) - -# Adjust the names of the primary targets for use with Windows 10. -# -!IF $(FOR_WIN10)!=0 -SQLITE3DLL = winsqlite3.dll -SQLITE3LIB = winsqlite3.lib -SQLITE3EXE = winsqlite3shell.exe -SQLITE3EXEPDB = -!ENDIF +TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -fp:precise +RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS) # Check if we want to use the "stdcall" calling convention when compiling. # This is not supported by the compilers for non-x86 platforms. It should @@ -450,8 +465,10 @@ CORE_COMPILE_OPTS = $(CORE_CCONV_OPTS) # when linking. # !IFNDEF CORE_LINK_DEP -!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0 +!IF $(DYNAMIC_SHELL)!=0 CORE_LINK_DEP = +!ELSEIF $(FOR_WIN10)==0 || "$(PLATFORM)"=="x86" +CORE_LINK_DEP = sqlite3.def !ELSE CORE_LINK_DEP = !ENDIF @@ -460,8 +477,10 @@ CORE_LINK_DEP = # These are additional linker options used for the core library. # !IFNDEF CORE_LINK_OPTS -!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0 +!IF $(DYNAMIC_SHELL)!=0 CORE_LINK_OPTS = +!ELSEIF $(FOR_WIN10)==0 || "$(PLATFORM)"=="x86" +CORE_LINK_OPTS = /DEF:sqlite3.def !ELSE CORE_LINK_OPTS = !ENDIF @@ -536,8 +555,8 @@ RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP # C compiler options for the Windows 10 platform (needs MSVC 2015). # !IF $(FOR_WIN10)!=0 -TCC = $(TCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE -BCC = $(BCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE +TCC = $(TCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE +BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE !ENDIF # Also, we need to dynamically link to the correct MSVC runtime @@ -802,10 +821,10 @@ LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelH LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib !ENDIF -# When compiling for UAP or the Windows 10 platform, some extra linker +# When compiling for UWP or the Windows 10 platform, some extra linker # options are also required. # -!IF $(FOR_UAP)!=0 || $(FOR_WIN10)!=0 +!IF $(FOR_UWP)!=0 || $(FOR_WIN10)!=0 LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE /NODEFAULTLIB:kernel32.lib LTLINKOPTS = $(LTLINKOPTS) mincore.lib !IFDEF PSDKLIBPATH @@ -814,7 +833,7 @@ LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(PSDKLIBPATH)" !ENDIF !IF $(FOR_WIN10)!=0 -LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(UCRTLIBPATH)" +LTLINKOPTS = $(LTLINKOPTS) /guard:cf "/LIBPATH:$(UCRTLIBPATH)" !IF $(DEBUG)>1 LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrtd.lib /DEFAULTLIB:ucrtd.lib !ELSE @@ -856,30 +875,35 @@ LIBRESOBJS = # when the shell is not being dynamically linked. # !IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 -SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 +SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS !ENDIF # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # -all: dll libsqlite3.lib shell +all: dll shell # Dynamic link library section. # -dll: $(SQLITE3DLL) +dll: $(SQLITE3DLL) # Shell executable. # -shell: $(SQLITE3EXE) - -libsqlite3.lib: $(LIBOBJ) - $(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS) +shell: $(SQLITE3EXE) -$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) +$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) +Replace.exe: + $(CSC) /target:exe $(TOP)\Replace.cs + +sqlite3.def: Replace.exe $(LIBOBJ) + echo EXPORTS > sqlite3.def + dumpbin /all $(LIBOBJ) \ + | .\Replace.exe "^\s+/EXPORT:_?(sqlite3_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \ + | sort >> sqlite3.def $(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H) $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\shell.c $(SHELL_CORE_SRC) \ @@ -918,4 +942,5 @@ $(LIBRESOBJS): $(TOP)\sqlite3.rc rcver.vc $(SQLITE3H) clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL - del /Q *.bsc *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL + del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL diff --git a/autoconf/README.txt b/autoconf/README.txt index c27c08d0df..6e62a4e138 100644 --- a/autoconf/README.txt +++ b/autoconf/README.txt @@ -1,13 +1,13 @@ This package contains: - * the SQLite library amalgamation (single file) source code distribution, - * the shell.c file used to build the sqlite3 shell too, and - * the sqlite3.h and sqlite3ext.h header files required to link programs - and sqlite extensions against the installed libary. + * the SQLite library amalgamation source code file: sqlite3.c + * the sqlite3.h and sqlite3ext.h header files that define the C-language + interface to the sqlite3.c library file + * the shell.c file used to build the sqlite3 command-line shell program * autoconf/automake installation infrastucture for building on POSIX - compliant systems. - * a Makefile.msc and sqlite3.rc for building with Microsoft Visual C++ on - Windows. + compliant systems + * a Makefile.msc, sqlite3.rc, and Replace.cs for building with Microsoft + Visual C++ on Windows SUMMARY OF HOW TO BUILD ======================= @@ -39,7 +39,7 @@ to produce a smaller installation footprint. Other SQLite compilation parameters can also be set using CFLAGS. For example: - $ CFLAGS="-Os -DSQLITE_OMIT_TRIGGERS" ./configure + $ CFLAGS="-Os -DSQLITE_THREADSAFE=0" ./configure BUILDING WITH MICROSOFT VISUAL C++ @@ -75,10 +75,10 @@ NMAKE command line as well: "NSDKLIBPATH=%WindowsSdkDir%\..\8.1\lib\winv6.3\um\x86" -Building for UAP 10.0 +Building for UWP 10.0 --------------------- - FOR_WINRT=1 FOR_UAP=1 + FOR_WINRT=1 FOR_UWP=1 Using Microsoft Visual C++ 2015 (or later) is required. When using the above, something like the following macros will need to be added to the diff --git a/config.h.in b/config.h.in index 36fd60781c..f2ba7d4fc4 100644 --- a/config.h.in +++ b/config.h.in @@ -45,6 +45,18 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the pread() function. */ +#undef HAVE_PREAD + +/* Define to 1 if you have the pread64() function. */ +#undef HAVE_PREAD64 + +/* Define to 1 if you have the pwrite() function. */ +#undef HAVE_PWRITE + +/* Define to 1 if you have the pwrite64() function. */ +#undef HAVE_PWRITE64 + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H diff --git a/configure b/configure index b2cb4d1308..8363818c6f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite 3.11.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.12.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -726,8 +726,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.11.0' -PACKAGE_STRING='sqlite 3.11.0' +PACKAGE_VERSION='3.12.0' +PACKAGE_STRING='sqlite 3.12.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1460,7 +1460,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.11.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.12.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1525,7 +1525,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.11.0:";; + short | recursive ) echo "Configuration of sqlite 3.12.0:";; esac cat <<\_ACEOF @@ -1646,7 +1646,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.11.0 +sqlite configure 3.12.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2065,7 +2065,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.11.0, which was +It was created by sqlite $as_me 3.12.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -10273,7 +10273,7 @@ done ######### # Figure out whether or not we have these functions # -for ac_func in fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime +for ac_func in fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime pread pread64 pwrite pwrite64 do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -12079,7 +12079,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.11.0, which was +This file was extended by sqlite $as_me 3.12.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12145,7 +12145,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite config.status 3.11.0 +sqlite config.status 3.12.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 19f85dc875..08c8867f06 100644 --- a/configure.ac +++ b/configure.ac @@ -108,7 +108,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([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime]) +AC_CHECK_FUNCS([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime pread pread64 pwrite pwrite64]) ######### # By default, we use the amalgamation (this may be changed below...) diff --git a/doc/lemon.html b/doc/lemon.html index b16e35960e..2b8c613640 100644 --- a/doc/lemon.html +++ b/doc/lemon.html @@ -161,8 +161,9 @@ such as ``identifier'' or ``number'' and the third argument will be the name of the identifier or the value of the number.

The Parse() function may have either three or four arguments, -depending on the grammar. If the grammar specification file request -it, the Parse() function will have a fourth parameter that can be +depending on the grammar. If the grammar specification file requests +it (via the extra_argument directive), +the Parse() function will have a fourth parameter that can be of any type chosen by the programmer. The parser doesn't do anything with this argument except to pass it through to action routines. This is a convenient mechanism for passing state information down @@ -263,6 +264,12 @@ with prior yacc and bison experience. But after years of experience using Lemon, I firmly believe that the Lemon way of doing things is better.

+

Updated as of 2016-02-16: +The text above was written in the 1990s. +We are told that Bison has lately been enhanced to support the +tokenizer-calls-parser paradigm used by Lemon, and to obviate the +need for global variables.

+

Input File Syntax

The main purpose of the grammar specification file for Lemon is @@ -617,6 +624,7 @@ build a parser using Lemon that can be used within a long-running program, such as a GUI, that will not leak memory or other resources. To do the same using yacc or bison is much more difficult.

+

The %extra_argument directive

The %extra_argument directive instructs Lemon to add a 4th parameter diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 06bcc7202e..0c86c4217e 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -18,6 +18,12 @@ # define NDEBUG 1 #endif +/* FTS3/FTS4 require virtual tables */ +#ifdef SQLITE_OMIT_VIRTUALTABLE +# undef SQLITE_ENABLE_FTS3 +# undef SQLITE_ENABLE_FTS4 +#endif + /* ** FTS4 is really an extension for FTS3. It is enabled using the ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all diff --git a/ext/fts3/fts3_test.c b/ext/fts3/fts3_test.c index 36dcc94e6d..dec977b916 100644 --- a/ext/fts3/fts3_test.c +++ b/ext/fts3/fts3_test.c @@ -526,7 +526,8 @@ static int fts3_test_varint_cmd( #ifdef SQLITE_ENABLE_FTS3 char aBuf[24]; int rc; - Tcl_WideInt w, w2; + Tcl_WideInt w; + sqlite3_int64 w2; int nByte, nByte2; if( objc!=2 ){ diff --git a/ext/fts3/fts3_tokenizer.c b/ext/fts3/fts3_tokenizer.c index fcabe5cca2..b7d9d2b3b2 100644 --- a/ext/fts3/fts3_tokenizer.c +++ b/ext/fts3/fts3_tokenizer.c @@ -29,6 +29,18 @@ #include #include +/* +** Return true if the two-argument version of fts3_tokenizer() +** has been activated via a prior call to sqlite3_db_config(db, +** SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0); +*/ +static int fts3TokenizerEnabled(sqlite3_context *context){ + sqlite3 *db = sqlite3_context_db_handle(context); + int isEnabled = 0; + sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,-1,&isEnabled); + return isEnabled; +} + /* ** Implementation of the SQL scalar function for accessing the underlying ** hash table. This function may be called as follows: @@ -49,7 +61,7 @@ ** is a blob containing the pointer stored as the hash data corresponding ** to string (after the hash-table is updated, if applicable). */ -static void scalarFunc( +static void fts3TokenizerFunc( sqlite3_context *context, int argc, sqlite3_value **argv @@ -67,27 +79,23 @@ static void scalarFunc( nName = sqlite3_value_bytes(argv[0])+1; if( argc==2 ){ -#ifdef SQLITE_ENABLE_FTS3_TOKENIZER - void *pOld; - int n = sqlite3_value_bytes(argv[1]); - if( zName==0 || n!=sizeof(pPtr) ){ - sqlite3_result_error(context, "argument type mismatch", -1); + if( fts3TokenizerEnabled(context) ){ + void *pOld; + int n = sqlite3_value_bytes(argv[1]); + if( zName==0 || n!=sizeof(pPtr) ){ + sqlite3_result_error(context, "argument type mismatch", -1); + return; + } + pPtr = *(void **)sqlite3_value_blob(argv[1]); + pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr); + if( pOld==pPtr ){ + sqlite3_result_error(context, "out of memory", -1); + } + }else{ + sqlite3_result_error(context, "fts3tokenize disabled", -1); return; } - pPtr = *(void **)sqlite3_value_blob(argv[1]); - pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr); - if( pOld==pPtr ){ - sqlite3_result_error(context, "out of memory", -1); - return; - } -#else - sqlite3_result_error(context, "fts3tokenize: " - "disabled - rebuild with -DSQLITE_ENABLE_FTS3_TOKENIZER", -1 - ); - return; -#endif /* SQLITE_ENABLE_FTS3_TOKENIZER */ - }else - { + }else{ if( zName ){ pPtr = sqlite3Fts3HashFind(pHash, zName, nName); } @@ -98,7 +106,6 @@ static void scalarFunc( return; } } - sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } @@ -336,7 +343,6 @@ finish: Tcl_DecrRefCount(pRet); } -#ifdef SQLITE_ENABLE_FTS3_TOKENIZER static int registerTokenizer( sqlite3 *db, @@ -358,7 +364,6 @@ int registerTokenizer( return sqlite3_finalize(pStmt); } -#endif /* SQLITE_ENABLE_FTS3_TOKENIZER */ static @@ -431,13 +436,13 @@ static void intTestFunc( assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") ); /* Test the storage function */ -#ifdef SQLITE_ENABLE_FTS3_TOKENIZER - rc = registerTokenizer(db, "nosuchtokenizer", p1); - assert( rc==SQLITE_OK ); - rc = queryTokenizer(db, "nosuchtokenizer", &p2); - assert( rc==SQLITE_OK ); - assert( p2==p1 ); -#endif + if( fts3TokenizerEnabled(context) ){ + rc = registerTokenizer(db, "nosuchtokenizer", p1); + assert( rc==SQLITE_OK ); + rc = queryTokenizer(db, "nosuchtokenizer", &p2); + assert( rc==SQLITE_OK ); + assert( p2==p1 ); + } sqlite3_result_text(context, "ok", -1, SQLITE_STATIC); } @@ -453,7 +458,7 @@ static void intTestFunc( ** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); ** ** This function adds a scalar function (see header comment above -** scalarFunc() in this file for details) and, if ENABLE_TABLE is +** fts3TokenizerFunc() in this file for details) and, if ENABLE_TABLE is ** defined at compilation time, a temporary virtual table (see header ** comment above struct HashTableVtab) to the database schema. Both ** provide read/write access to the contents of *pHash. @@ -482,10 +487,10 @@ int sqlite3Fts3InitHashTable( #endif if( SQLITE_OK==rc ){ - rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0); + rc = sqlite3_create_function(db, zName, 1, any, p, fts3TokenizerFunc, 0, 0); } if( SQLITE_OK==rc ){ - rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0); + rc = sqlite3_create_function(db, zName, 2, any, p, fts3TokenizerFunc, 0, 0); } #ifdef SQLITE_TEST if( SQLITE_OK==rc ){ diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index d5a408222e..24039aef30 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -333,7 +333,8 @@ static int fts3SqlStmt( ** of the oldest level in the db that contains at least ? segments. Or, ** if no level in the FTS index contains more than ? segments, the statement ** returns zero rows. */ -/* 28 */ "SELECT level FROM %Q.'%q_segdir' GROUP BY level HAVING count(*)>=?" +/* 28 */ "SELECT level, count(*) AS cnt FROM %Q.'%q_segdir' " + " GROUP BY level HAVING cnt>=?" " ORDER BY (level %% 1024) ASC LIMIT 1", /* Estimate the upper limit on the number of leaf nodes in a new segment @@ -4836,10 +4837,11 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){ ** set nSeg to -1. */ rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0); - sqlite3_bind_int(pFindLevel, 1, nMin); + sqlite3_bind_int(pFindLevel, 1, MAX(2, nMin)); if( sqlite3_step(pFindLevel)==SQLITE_ROW ){ iAbsLevel = sqlite3_column_int64(pFindLevel, 0); - nSeg = nMin; + nSeg = sqlite3_column_int(pFindLevel, 1); + assert( nSeg>=2 ); }else{ nSeg = -1; } diff --git a/ext/fts3/unicode/mkunicode.tcl b/ext/fts3/unicode/mkunicode.tcl index a2e9b1da29..aafb4e9f9b 100644 --- a/ext/fts3/unicode/mkunicode.tcl +++ b/ext/fts3/unicode/mkunicode.tcl @@ -226,9 +226,9 @@ proc print_isalnum {zFunc lRange} { an_print_range_array $lRange an_print_ascii_bitmap $lRange puts { - if( c<128 ){ + if( (unsigned int)c<128 ){ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); - }else if( c<(1<<22) ){ + }else if( (unsigned int)c<(1<<22) ){ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; int iRes = 0; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 6318af900f..af40412167 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -26,6 +26,7 @@ SQLITE_EXTENSION_INIT1 typedef unsigned char u8; typedef unsigned int u32; typedef unsigned short u16; +typedef short i16; typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; @@ -80,6 +81,16 @@ extern int sqlite3_fts5_may_be_corrupt; # define assert_nc(x) assert(x) #endif +/* Mark a function parameter as unused, to suppress nuisance compiler +** warnings. */ +#ifndef UNUSED_PARAM +# define UNUSED_PARAM(X) (void)(X) +#endif + +#ifndef UNUSED_PARAM2 +# define UNUSED_PARAM2(X, Y) (void)(X), (void)(Y) +#endif + typedef struct Fts5Global Fts5Global; typedef struct Fts5Colset Fts5Colset; @@ -345,16 +356,6 @@ struct Fts5IndexIter { int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); int sqlite3Fts5IndexClose(Fts5Index *p); -/* -** for( -** sqlite3Fts5IndexQuery(p, "token", 5, 0, 0, &pIter); -** 0==sqlite3Fts5IterEof(pIter); -** sqlite3Fts5IterNext(pIter) -** ){ -** i64 iRowid = sqlite3Fts5IterRowid(pIter); -** } -*/ - /* ** Return a simple checksum value based on the arguments. */ @@ -396,7 +397,6 @@ int sqlite3Fts5IndexQuery( */ int sqlite3Fts5IterNext(Fts5IndexIter*); int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch); -i64 sqlite3Fts5IterRowid(Fts5IndexIter*); /* ** Close an iterator opened by sqlite3Fts5IndexQuery(). @@ -681,7 +681,7 @@ int sqlite3Fts5ExprPopulatePoslists( void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); void sqlite3Fts5ExprClearEof(Fts5Expr*); -int sqlite3Fts5ExprClonePhrase(Fts5Config*, Fts5Expr*, int, Fts5Expr**); +int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c index 79bed1c8f7..836de8a0f0 100644 --- a/ext/fts5/fts5_aux.c +++ b/ext/fts5/fts5_aux.c @@ -158,6 +158,8 @@ static int fts5HighlightCb( int rc = SQLITE_OK; int iPos; + UNUSED_PARAM2(pToken, nToken); + if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK; iPos = p->iPos++; @@ -391,6 +393,7 @@ static int fts5CountCb( void *pUserData /* Pointer to sqlite3_int64 variable */ ){ sqlite3_int64 *pn = (sqlite3_int64*)pUserData; + UNUSED_PARAM2(pApi, pFts); (*pn)++; return SQLITE_OK; } diff --git a/ext/fts5/fts5_buffer.c b/ext/fts5/fts5_buffer.c index c552e860eb..0098846be9 100644 --- a/ext/fts5/fts5_buffer.c +++ b/ext/fts5/fts5_buffer.c @@ -236,7 +236,7 @@ int sqlite3Fts5PoslistWriterAppend( Fts5PoslistWriter *pWriter, i64 iPos ){ - int rc; + int rc = 0; /* Initialized only to suppress erroneous warning from Clang */ if( fts5BufferGrow(&rc, pBuf, 5+5+5) ) return rc; sqlite3Fts5PoslistSafeAppend(pBuf, &pWriter->iPrev, iPos); return SQLITE_OK; @@ -390,6 +390,3 @@ void sqlite3Fts5TermsetFree(Fts5Termset *p){ sqlite3_free(p); } } - - - diff --git a/ext/fts5/fts5_config.c b/ext/fts5/fts5_config.c index 70b1229edb..f49cede129 100644 --- a/ext/fts5/fts5_config.c +++ b/ext/fts5/fts5_config.c @@ -206,7 +206,7 @@ static int fts5ConfigSetEnum( const char *zEnum, int *peVal ){ - int nEnum = strlen(zEnum); + int nEnum = (int)strlen(zEnum); int i; int iVal = -1; @@ -943,4 +943,3 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ } return rc; } - diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 5006d88d0f..d3f801b022 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -320,8 +320,6 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){ */ static int fts5ExprSynonymList( Fts5ExprTerm *pTerm, - int bCollist, - Fts5Colset *pColset, i64 iRowid, Fts5Buffer *pBuf, /* Use this buffer for space if required */ u8 **pa, int *pn @@ -405,7 +403,6 @@ static int fts5ExprSynonymList( */ static int fts5ExprPhraseIsMatch( Fts5ExprNode *pNode, /* Node pPhrase belongs to */ - Fts5Colset *pColset, /* Restrict matches to these columns */ Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */ int *pbMatch /* OUT: Set to true if really a match */ ){ @@ -434,9 +431,7 @@ static int fts5ExprPhraseIsMatch( u8 *a = 0; if( pTerm->pSynonym ){ Fts5Buffer buf = {0, 0, 0}; - rc = fts5ExprSynonymList( - pTerm, 0, pColset, pNode->iRowid, &buf, &a, &n - ); + rc = fts5ExprSynonymList(pTerm, pNode->iRowid, &buf, &a, &n); if( rc ){ sqlite3_free(a); goto ismatch_out; @@ -727,7 +722,7 @@ static int fts5ExprNearTest( Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){ int bMatch = 0; - rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch); + rc = fts5ExprPhraseIsMatch(pNode, pPhrase, &bMatch); if( bMatch==0 ) break; }else{ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter; @@ -1476,6 +1471,8 @@ static int fts5ParseTokenize( TokenCtx *pCtx = (TokenCtx*)pContext; Fts5ExprPhrase *pPhrase = pCtx->pPhrase; + UNUSED_PARAM2(iUnused1, iUnused2); + /* If an error has already occurred, this is a no-op */ if( pCtx->rc!=SQLITE_OK ) return pCtx->rc; @@ -1611,7 +1608,6 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm( ** expression passed as the second argument. */ int sqlite3Fts5ExprClonePhrase( - Fts5Config *pConfig, Fts5Expr *pExpr, int iPhrase, Fts5Expr **ppNew @@ -1619,14 +1615,10 @@ int sqlite3Fts5ExprClonePhrase( int rc = SQLITE_OK; /* Return code */ Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */ int i; /* Used to iterate through phrase terms */ - Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ - TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */ - pOrig = pExpr->apExprPhrase[iPhrase]; - pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); if( rc==SQLITE_OK ){ pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, @@ -2399,13 +2391,15 @@ static int fts5ExprPopulatePoslistsCb( int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ - int iStart, /* Byte offset of token within input text */ - int iEnd /* Byte offset of end of token within input text */ + int iUnused1, /* Byte offset of token within input text */ + int iUnused2 /* Byte offset of end of token within input text */ ){ Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx; Fts5Expr *pExpr = p->pExpr; int i; + UNUSED_PARAM2(iUnused1, iUnused2); + if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; for(i=0; inPhrase; i++){ Fts5ExprTerm *pTerm; @@ -2550,7 +2544,7 @@ int sqlite3Fts5ExprPhraseCollist( if( pTerm->pSynonym ){ Fts5Buffer *pBuf = (Fts5Buffer*)&pTerm->pSynonym[1]; rc = fts5ExprSynonymList( - pTerm, 1, 0, pNode->iRowid, pBuf, (u8**)ppCollist, pnCollist + pTerm, pNode->iRowid, pBuf, (u8**)ppCollist, pnCollist ); }else{ *ppCollist = pPhrase->aTerm[0].pIter->pData; diff --git a/ext/fts5/fts5_hash.c b/ext/fts5/fts5_hash.c index 50ca082711..ada8bb16cb 100644 --- a/ext/fts5/fts5_hash.c +++ b/ext/fts5/fts5_hash.c @@ -62,10 +62,10 @@ struct Fts5HashEntry { int nAlloc; /* Total size of allocation */ int iSzPoslist; /* Offset of space for 4-byte poslist size */ int nData; /* Total bytes of data (incl. structure) */ + int nKey; /* Length of zKey[] in bytes */ u8 bDel; /* Set delete-flag @ iSzPoslist */ u8 bContent; /* Set content-flag (detail=none mode) */ - - int iCol; /* Column of last value written */ + i16 iCol; /* Column of last value written */ int iPos; /* Position of last value written */ i64 iRowid; /* Rowid of last value written */ char zKey[8]; /* Nul-terminated entry key */ @@ -245,8 +245,8 @@ int sqlite3Fts5HashWrite( iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ if( p->zKey[0]==bByte + && p->nKey==nToken && memcmp(&p->zKey[1], pToken, nToken)==0 - && p->zKey[nToken+1]==0 ){ break; } @@ -273,6 +273,7 @@ int sqlite3Fts5HashWrite( p->zKey[0] = bByte; memcpy(&p->zKey[1], pToken, nToken); assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) ); + p->nKey = nToken; p->zKey[nToken+1] = '\0'; p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE; p->pHashNext = pHash->aSlot[iHash]; diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 12e18c6445..ac97a7d75f 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -607,17 +607,6 @@ static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ return (res==0 ? (pLeft->n - pRight->n) : res); } -#ifdef SQLITE_DEBUG -static int fts5BlobCompare( - const u8 *pLeft, int nLeft, - const u8 *pRight, int nRight -){ - int nCmp = MIN(nLeft, nRight); - int res = memcmp(pLeft, pRight, nCmp); - return (res==0 ? (nLeft - nRight) : res); -} -#endif - static int fts5LeafFirstTermOff(Fts5Data *pLeaf){ int ret; fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf], ret); @@ -709,6 +698,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ return pRet; } + /* ** Release a reference to data record returned by an earlier call to ** fts5DataRead(). @@ -876,28 +866,37 @@ static int fts5StructureDecode( for(iLvl=0; rc==SQLITE_OK && iLvlaLevel[iLvl]; - int nTotal; + int nTotal = 0; int iSeg; - i += fts5GetVarint32(&pData[i], pLvl->nMerge); - i += fts5GetVarint32(&pData[i], nTotal); - assert( nTotal>=pLvl->nMerge ); - pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, - nTotal * sizeof(Fts5StructureSegment) - ); + if( i>=nData ){ + rc = FTS5_CORRUPT; + }else{ + i += fts5GetVarint32(&pData[i], pLvl->nMerge); + i += fts5GetVarint32(&pData[i], nTotal); + assert( nTotal>=pLvl->nMerge ); + pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, + nTotal * sizeof(Fts5StructureSegment) + ); + } if( rc==SQLITE_OK ){ pLvl->nSeg = nTotal; for(iSeg=0; iSeg=nData ){ + rc = FTS5_CORRUPT; + break; + } i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid); i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst); i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast); } - }else{ - fts5StructureRelease(pRet); - pRet = 0; } } + if( rc!=SQLITE_OK ){ + fts5StructureRelease(pRet); + pRet = 0; + } } *ppOut = pRet; @@ -1564,6 +1563,10 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ int nNew; /* Bytes of new data */ iOff += fts5GetVarint32(&a[iOff], nNew); + if( iOff+nNew>pIter->pLeaf->nn ){ + p->rc = FTS5_CORRUPT; + return; + } pIter->term.n = nKeep; fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]); iOff += nNew; @@ -1770,10 +1773,12 @@ static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5Iter *pIter){ static void fts5SegIterNext_Reverse( Fts5Index *p, /* FTS5 backend object */ Fts5SegIter *pIter, /* Iterator to advance */ - int *pbNewTerm /* OUT: Set for new term */ + int *pbUnused /* Unused */ ){ assert( pIter->flags & FTS5_SEGITER_REVERSE ); assert( pIter->pNextLeaf==0 ); + UNUSED_PARAM(pbUnused); + if( pIter->iRowidOffset>0 ){ u8 *a = pIter->pLeaf->p; int iOff; @@ -2150,6 +2155,10 @@ static void fts5LeafSeek( iPgidx = szLeaf; iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff); iOff = iTermOff; + if( iOff>n ){ + p->rc = FTS5_CORRUPT; + return; + } while( 1 ){ @@ -2250,7 +2259,6 @@ static void fts5LeafSeek( */ static void fts5SegIterSeekInit( Fts5Index *p, /* FTS5 backend */ - Fts5Buffer *pBuf, /* Buffer to use for loading pages */ const u8 *pTerm, int nTerm, /* Term to seek to */ int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5StructureSegment *pSeg, /* Description of segment */ @@ -2637,7 +2645,7 @@ static void fts5SegIterNextFrom( /* ** Free the iterator object passed as the second argument. */ -static void fts5MultiIterFree(Fts5Index *p, Fts5Iter *pIter){ +static void fts5MultiIterFree(Fts5Iter *pIter){ if( pIter ){ int i; for(i=0; inSeg; i++){ @@ -2678,7 +2686,6 @@ static void fts5MultiIterAdvanced( ** that it deals with more complicated cases as well. */ static int fts5MultiIterAdvanceRowid( - Fts5Index *p, /* FTS5 backend to iterate within */ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */ int iChanged, /* Index of sub-iterator just advanced */ Fts5SegIter **ppFirst @@ -2753,7 +2760,7 @@ static void fts5MultiIterNext( } if( pSeg->pLeaf==0 || bNewTerm - || fts5MultiIterAdvanceRowid(p, pIter, iFirst, &pSeg) + || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg) ){ fts5MultiIterAdvanced(p, pIter, iFirst, 1); fts5MultiIterSetEof(pIter); @@ -2786,7 +2793,7 @@ static void fts5MultiIterNext2( assert( p->rc==SQLITE_OK ); pSeg->xNext(p, pSeg, &bNewTerm); if( pSeg->pLeaf==0 || bNewTerm - || fts5MultiIterAdvanceRowid(p, pIter, iFirst, &pSeg) + || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg) ){ fts5MultiIterAdvanced(p, pIter, iFirst, 1); fts5MultiIterSetEof(pIter); @@ -2800,7 +2807,8 @@ static void fts5MultiIterNext2( } } -static void fts5IterSetOutputs_Noop(Fts5Iter *pIter, Fts5SegIter *pSeg){ +static void fts5IterSetOutputs_Noop(Fts5Iter *pUnused1, Fts5SegIter *pUnused2){ + UNUSED_PARAM2(pUnused1, pUnused2); } static Fts5Iter *fts5MultiIterAlloc( @@ -2826,10 +2834,11 @@ static Fts5Iter *fts5MultiIterAlloc( } static void fts5PoslistCallback( - Fts5Index *p, + Fts5Index *pUnused, void *pContext, const u8 *pChunk, int nChunk ){ + UNUSED_PARAM(pUnused); assert_nc( nChunk>=0 ); if( nChunk>0 ){ fts5BufferSafeAppendBlob((Fts5Buffer*)pContext, pChunk, nChunk); @@ -2863,11 +2872,12 @@ static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){ } static void fts5PoslistOffsetsCallback( - Fts5Index *p, + Fts5Index *pUnused, void *pContext, const u8 *pChunk, int nChunk ){ PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext; + UNUSED_PARAM(pUnused); assert_nc( nChunk>=0 ); if( nChunk>0 ){ int i = 0; @@ -2885,11 +2895,12 @@ static void fts5PoslistOffsetsCallback( } static void fts5PoslistFilterCallback( - Fts5Index *p, + Fts5Index *pUnused, void *pContext, const u8 *pChunk, int nChunk ){ PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext; + UNUSED_PARAM(pUnused); assert_nc( nChunk>=0 ); if( nChunk>0 ){ /* Search through to find the first varint with value 1. This is the @@ -3253,7 +3264,6 @@ static void fts5MultiIterNew( int nSeg = 0; /* Number of segment-iters in use */ int iIter = 0; /* */ int iSeg; /* Used to iterate through segments */ - Fts5Buffer buf = {0,0,0}; /* Buffer used by fts5SegIterSeekInit() */ Fts5StructureLevel *pLvl; Fts5Iter *pNew; @@ -3296,7 +3306,7 @@ static void fts5MultiIterNew( if( pTerm==0 ){ fts5SegIterInit(p, pSeg, pIter); }else{ - fts5SegIterSeekInit(p, &buf, pTerm, nTerm, flags, pSeg, pIter); + fts5SegIterSeekInit(p, pTerm, nTerm, flags, pSeg, pIter); } } } @@ -3333,11 +3343,9 @@ static void fts5MultiIterNew( } }else{ - fts5MultiIterFree(p, pNew); + fts5MultiIterFree(pNew); *ppOut = 0; } - fts5BufferFree(&buf); - } /* @@ -3475,15 +3483,14 @@ static void fts5IndexDiscardData(Fts5Index *p){ } /* -** Return the size of the prefix, in bytes, that buffer (nNew/pNew) shares -** with buffer (nOld/pOld). +** Return the size of the prefix, in bytes, that buffer +** (pNew/) shares with buffer (pOld/nOld). +** +** Buffer (pNew/) is guaranteed to be greater +** than buffer (pOld/nOld). */ -static int fts5PrefixCompress( - int nOld, const u8 *pOld, - int nNew, const u8 *pNew -){ +static int fts5PrefixCompress(int nOld, const u8 *pOld, const u8 *pNew){ int i; - assert( fts5BlobCompare(pOld, nOld, pNew, nNew)<0 ); for(i=0; iterm.n ){ - n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, nTerm, pTerm); + n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm); } fts5WriteBtreeTerm(p, pWriter, n, pTerm); pPage = &pWriter->writer; } }else{ - nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, nTerm, pTerm); + nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm); fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix); } @@ -4165,7 +4172,7 @@ static void fts5IndexMergeLevel( pLvl->nMerge = nInput; } - fts5MultiIterFree(p, pIter); + fts5MultiIterFree(pIter); fts5BufferFree(&term); if( pnRem ) *pnRem -= writer.nLeafWritten; } @@ -4497,7 +4504,10 @@ int sqlite3Fts5IndexOptimize(Fts5Index *p){ if( pLvl->aSeg ){ int iLvl, iSeg; int iSegOut = 0; - for(iLvl=0; iLvlnLevel; iLvl++){ + /* Iterate through all segments, from oldest to newest. Add them to + ** the new Fts5Level object so that pLvl->aSeg[0] is the oldest + ** segment in the data structure. */ + for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg]; iSegOut++; @@ -4541,9 +4551,10 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ static void fts5AppendRowid( Fts5Index *p, i64 iDelta, - Fts5Iter *pMulti, + Fts5Iter *pUnused, Fts5Buffer *pBuf ){ + UNUSED_PARAM(pUnused); fts5BufferAppendVarint(&p->rc, pBuf, iDelta); } @@ -4886,7 +4897,7 @@ static void fts5SetupPrefixIter( } fts5BufferFree(&aBuf[i]); } - fts5MultiIterFree(p, p1); + fts5MultiIterFree(p1); pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n); if( pData ){ @@ -5167,7 +5178,7 @@ int sqlite3Fts5IndexQuery( int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; buf.p[0] = FTS5_MAIN_PREFIX; fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet); - assert( pRet->pColset==0 ); + assert( p->rc!=SQLITE_OK || pRet->pColset==0 ); fts5IterSetOutputCb(&p->rc, pRet); if( p->rc==SQLITE_OK ){ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst]; @@ -5250,7 +5261,7 @@ void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ if( pIndexIter ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5Index *pIndex = pIter->pIndex; - fts5MultiIterFree(pIter->pIndex, pIter); + fts5MultiIterFree(pIter); fts5CloseReader(pIndex); } } @@ -5809,7 +5820,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ } fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3); - fts5MultiIterFree(p, pIter); + fts5MultiIterFree(pIter); if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT; fts5StructureRelease(pStruct); @@ -6046,6 +6057,7 @@ static void fts5DecodeFunction( int eDetailNone = (sqlite3_user_data(pCtx)!=0); assert( nArg==2 ); + UNUSED_PARAM(nArg); memset(&s, 0, sizeof(Fts5Buffer)); iRowid = sqlite3_value_int64(apVal[0]); diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c index 4b798314c0..28f3f3e62c 100644 --- a/ext/fts5/fts5_main.c +++ b/ext/fts5/fts5_main.c @@ -1096,7 +1096,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ static int fts5FilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ - const char *idxStr, /* Unused */ + const char *zUnused, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ @@ -1114,6 +1114,9 @@ static int fts5FilterMethod( sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ char **pzErrmsg = pConfig->pzErrmsg; + UNUSED_PARAM(zUnused); + UNUSED_PARAM(nVal); + if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); @@ -1398,8 +1401,7 @@ static int fts5SpecialInsert( static int fts5SpecialDelete( Fts5Table *pTab, - sqlite3_value **apVal, - sqlite3_int64 *piRowid + sqlite3_value **apVal ){ int rc = SQLITE_OK; int eType1 = sqlite3_value_type(apVal[1]); @@ -1475,7 +1477,7 @@ static int fts5UpdateMethod( if( pConfig->eContent!=FTS5_CONTENT_NORMAL && 0==sqlite3_stricmp("delete", z) ){ - rc = fts5SpecialDelete(pTab, apVal, pRowid); + rc = fts5SpecialDelete(pTab, apVal); }else{ rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); } @@ -1576,6 +1578,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ ** Implementation of xBegin() method. */ static int fts5BeginMethod(sqlite3_vtab *pVtab){ + UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0); return SQLITE_OK; } @@ -1586,6 +1589,7 @@ static int fts5BeginMethod(sqlite3_vtab *pVtab){ ** by fts5SyncMethod(). */ static int fts5CommitMethod(sqlite3_vtab *pVtab){ + UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0); return SQLITE_OK; } @@ -1839,12 +1843,14 @@ static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ static int fts5ColumnSizeCb( void *pContext, /* Pointer to int */ int tflags, - const char *pToken, /* Buffer containing token */ - int nToken, /* Size of token in bytes */ - int iStart, /* Start offset of token */ - int iEnd /* End offset of token */ + const char *pUnused, /* Buffer containing token */ + int nUnused, /* Size of token in bytes */ + int iUnused1, /* Start offset of token */ + int iUnused2 /* End offset of token */ ){ int *pCnt = (int*)pContext; + UNUSED_PARAM2(pUnused, nUnused); + UNUSED_PARAM2(iUnused1, iUnused2); if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){ (*pCnt)++; } @@ -1960,10 +1966,11 @@ static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ } static void fts5ApiPhraseNext( - Fts5Context *pCtx, + Fts5Context *pUnused, Fts5PhraseIter *pIter, int *piCol, int *piOff ){ + UNUSED_PARAM(pUnused); if( pIter->a>=pIter->b ){ *piCol = -1; *piOff = -1; @@ -2115,12 +2122,11 @@ static int fts5ApiQueryPhrase( rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew); if( rc==SQLITE_OK ){ - Fts5Config *pConf = pTab->pConfig; pNew->ePlan = FTS5_PLAN_MATCH; pNew->iFirstRowid = SMALLEST_INT64; pNew->iLastRowid = LARGEST_INT64; pNew->base.pVtab = (sqlite3_vtab*)pTab; - rc = sqlite3Fts5ExprClonePhrase(pConf, pCsr->pExpr, iPhrase, &pNew->pExpr); + rc = sqlite3Fts5ExprClonePhrase(pCsr->pExpr, iPhrase, &pNew->pExpr); } if( rc==SQLITE_OK ){ @@ -2333,7 +2339,7 @@ static int fts5ColumnMethod( */ static int fts5FindFunctionMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ - int nArg, /* Number of SQL function arguments */ + int nUnused, /* Number of SQL function arguments */ const char *zName, /* Name of SQL function */ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ void **ppArg /* OUT: User data for *pxFunc */ @@ -2341,6 +2347,7 @@ static int fts5FindFunctionMethod( Fts5Table *pTab = (Fts5Table*)pVtab; Fts5Auxiliary *pAux; + UNUSED_PARAM(nUnused); pAux = fts5FindAuxiliary(pTab, zName); if( pAux ){ *pxFunc = fts5ApiCallback; @@ -2370,6 +2377,7 @@ static int fts5RenameMethod( */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5Table *pTab = (Fts5Table*)pVtab; + UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); fts5TripCursors(pTab); return sqlite3Fts5StorageSync(pTab->pStorage, 0); @@ -2382,6 +2390,7 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ */ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5Table *pTab = (Fts5Table*)pVtab; + UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); fts5TripCursors(pTab); return sqlite3Fts5StorageSync(pTab->pStorage, 0); @@ -2394,6 +2403,7 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ */ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5Table *pTab = (Fts5Table*)pVtab; + UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); return sqlite3Fts5StorageRollback(pTab->pStorage); @@ -2573,10 +2583,11 @@ static void fts5ModuleDestroy(void *pCtx){ static void fts5Fts5Func( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ - sqlite3_value **apVal /* Function arguments */ + sqlite3_value **apUnused /* Function arguments */ ){ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); char buf[8]; + UNUSED_PARAM2(nArg, apUnused); assert( nArg==0 ); assert( sizeof(buf)>=sizeof(pGlobal) ); memcpy(buf, (void*)&pGlobal, sizeof(pGlobal)); @@ -2589,9 +2600,10 @@ static void fts5Fts5Func( static void fts5SourceIdFunc( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ - sqlite3_value **apVal /* Function arguments */ + sqlite3_value **apUnused /* Function arguments */ ){ assert( nArg==0 ); + UNUSED_PARAM2(nArg, apUnused); sqlite3_result_text(pCtx, "--FTS5-SOURCE-ID--", -1, SQLITE_TRANSIENT); } @@ -2653,6 +2665,17 @@ static int fts5Init(sqlite3 *db){ ); } } + + /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file + ** fts5_test_mi.c is compiled and linked into the executable. And call + ** its entry point to enable the matchinfo() demo. */ +#ifdef SQLITE_FTS5_ENABLE_TEST_MI + if( rc==SQLITE_OK ){ + extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*); + rc = sqlite3Fts5TestRegisterMatchinfo(db); + } +#endif + return rc; } diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index da9524dea3..e4f5dd8f51 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -362,11 +362,12 @@ static int fts5StorageInsertCallback( int tflags, const char *pToken, /* Buffer containing token */ int nToken, /* Size of token in bytes */ - int iStart, /* Start offset of token */ - int iEnd /* End offset of token */ + int iUnused1, /* Start offset of token */ + int iUnused2 /* End offset of token */ ){ Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext; Fts5Index *pIdx = pCtx->pStorage->pIndex; + UNUSED_PARAM2(iUnused1, iUnused2); if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ pCtx->szCol++; } @@ -797,8 +798,8 @@ static int fts5StorageIntegrityCallback( int tflags, const char *pToken, /* Buffer containing token */ int nToken, /* Size of token in bytes */ - int iStart, /* Start offset of token */ - int iEnd /* End offset of token */ + int iUnused1, /* Start offset of token */ + int iUnused2 /* End offset of token */ ){ Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; Fts5Termset *pTermset = pCtx->pTermset; @@ -808,6 +809,8 @@ static int fts5StorageIntegrityCallback( int iPos; int iCol; + UNUSED_PARAM2(iUnused1, iUnused2); + if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ pCtx->szCol++; } diff --git a/ext/fts5/fts5_test_mi.c b/ext/fts5/fts5_test_mi.c index 28331773c0..bc6d01f126 100644 --- a/ext/fts5/fts5_test_mi.c +++ b/ext/fts5/fts5_test_mi.c @@ -41,16 +41,17 @@ */ -#ifdef SQLITE_TEST #ifdef SQLITE_ENABLE_FTS5 #include "fts5.h" -#include #include #include typedef struct Fts5MatchinfoCtx Fts5MatchinfoCtx; + +#ifndef SQLITE_AMALGAMATION typedef unsigned int u32; +#endif struct Fts5MatchinfoCtx { int nCol; /* Number of cols in FTS5 table */ @@ -244,11 +245,7 @@ static int fts5MatchinfoLocalCb( iOff>=0; pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) ){ - if( f=='b' ){ - aOut[iPhrase * ((p->nCol+31)/32) + iCol/32] |= ((u32)1 << iCol%32); - }else{ - aOut[nMul * (iCol + iPhrase * p->nCol)]++; - } + aOut[nMul * (iCol + iPhrase * p->nCol)]++; } } @@ -409,7 +406,7 @@ int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db){ ** ** Also check that the fts5_api object is version 2 or newer. */ - if( pApi==0 || pApi->iVersion<1 ){ + if( pApi==0 || pApi->iVersion<2 ){ return SQLITE_ERROR; } @@ -420,5 +417,4 @@ int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db){ } #endif /* SQLITE_ENABLE_FTS5 */ -#endif /* SQLITE_TEST */ diff --git a/ext/fts5/fts5_tokenize.c b/ext/fts5/fts5_tokenize.c index 790adaae47..b72a0c24ab 100644 --- a/ext/fts5/fts5_tokenize.c +++ b/ext/fts5/fts5_tokenize.c @@ -62,12 +62,13 @@ static void fts5AsciiDelete(Fts5Tokenizer *p){ ** Create an "ascii" tokenizer. */ static int fts5AsciiCreate( - void *pCtx, + void *pUnused, const char **azArg, int nArg, Fts5Tokenizer **ppOut ){ int rc = SQLITE_OK; AsciiTokenizer *p = 0; + UNUSED_PARAM(pUnused); if( nArg%2 ){ rc = SQLITE_ERROR; }else{ @@ -116,7 +117,7 @@ static void asciiFold(char *aOut, const char *aIn, int nByte){ static int fts5AsciiTokenize( Fts5Tokenizer *pTokenizer, void *pCtx, - int flags, + int iUnused, const char *pText, int nText, int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) ){ @@ -130,6 +131,8 @@ static int fts5AsciiTokenize( char *pFold = aFold; unsigned char *a = p->aTokenChar; + UNUSED_PARAM(iUnused); + while( isnFold; const char *pEnd = &aFold[nFold-6]; + UNUSED_PARAM(iUnused); + /* Each iteration of this loop gobbles up a contiguous run of separators, ** then the next token. */ while( rc==SQLITE_OK ){ diff --git a/ext/fts5/fts5_unicode2.c b/ext/fts5/fts5_unicode2.c index 8ad709d0fd..1ef56f6156 100644 --- a/ext/fts5/fts5_unicode2.c +++ b/ext/fts5/fts5_unicode2.c @@ -125,9 +125,9 @@ int sqlite3Fts5UnicodeIsalnum(int c){ 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, }; - if( c<128 ){ + if( (unsigned int)c<128 ){ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); - }else if( c<(1<<22) ){ + }else if( (unsigned int)c<(1<<22) ){ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; int iRes = 0; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; diff --git a/ext/fts5/fts5_vocab.c b/ext/fts5/fts5_vocab.c index b17e8974fb..f3a2381cc1 100644 --- a/ext/fts5/fts5_vocab.c +++ b/ext/fts5/fts5_vocab.c @@ -237,7 +237,7 @@ static int fts5VocabCreateMethod( ** Implementation of the xBestIndex method. */ static int fts5VocabBestIndexMethod( - sqlite3_vtab *pVTab, + sqlite3_vtab *pUnused, sqlite3_index_info *pInfo ){ int i; @@ -247,6 +247,8 @@ static int fts5VocabBestIndexMethod( int idxNum = 0; int nArg = 0; + UNUSED_PARAM(pUnused); + for(i=0; inConstraint; i++){ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; if( p->usable==0 ) continue; @@ -488,8 +490,8 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ static int fts5VocabFilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ - const char *idxStr, /* Unused */ - int nVal, /* Number of elements in apVal */ + const char *zUnused, /* Unused */ + int nUnused, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; @@ -504,6 +506,8 @@ static int fts5VocabFilterMethod( sqlite3_value *pGe = 0; sqlite3_value *pLe = 0; + UNUSED_PARAM2(zUnused, nUnused); + fts5VocabResetCursor(pCsr); if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; diff --git a/ext/fts5/fts5parse.y b/ext/fts5/fts5parse.y index 0e81771b4f..2bdf4b09b2 100644 --- a/ext/fts5/fts5parse.y +++ b/ext/fts5/fts5parse.y @@ -28,12 +28,13 @@ // This code runs whenever there is a syntax error // %syntax_error { + UNUSED_PARAM(yymajor); /* Silence a compiler warning */ sqlite3Fts5ParseError( pParse, "fts5: syntax error near \"%.*s\"",TOKEN.n,TOKEN.p ); } %stack_overflow { - assert( 0 ); + sqlite3Fts5ParseError(pParse, "fts5: parser stack overflow"); } // The name of the generated procedure that implements the parser diff --git a/ext/fts5/test/fts5_common.tcl b/ext/fts5/test/fts5_common.tcl index cf688dcc0d..32691d1c81 100644 --- a/ext/fts5/test/fts5_common.tcl +++ b/ext/fts5/test/fts5_common.tcl @@ -16,8 +16,13 @@ if {![info exists testdir]} { source $testdir/tester.tcl ifcapable !fts5 { - finish_test + proc return_if_no_fts5 {} { + finish_test + return -code return + } return +} else { + proc return_if_no_fts5 {} {} } catch { @@ -25,12 +30,6 @@ catch { reset_db } -# If SQLITE_ENABLE_FTS5 is not defined, skip this test. -ifcapable !fts5 { - finish_test - return -} - proc fts5_test_poslist {cmd} { set res [list] for {set i 0} {$i < [$cmd xInstCount]} {incr i} { diff --git a/ext/fts5/test/fts5bigtok.test b/ext/fts5/test/fts5bigtok.test index 2267be5058..9ccaf6c860 100644 --- a/ext/fts5/test/fts5bigtok.test +++ b/ext/fts5/test/fts5bigtok.test @@ -14,6 +14,7 @@ source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5bigtok +return_if_no_fts5 proc rndterm {} { set L [list a b c d e f g h i j k l m n o p q r s t u v w x y z] diff --git a/ext/fts5/test/fts5corrupt3.test b/ext/fts5/test/fts5corrupt3.test index dd70f31a6b..7a8cb5c465 100644 --- a/ext/fts5/test/fts5corrupt3.test +++ b/ext/fts5/test/fts5corrupt3.test @@ -334,12 +334,9 @@ do_catchsql_test 6.3.5 { } {1 {database disk image is malformed}} -} - #------------------------------------------------------------------------ # reset_db -reset_db proc rnddoc {n} { set map [list a b c d] set doc [list] @@ -371,6 +368,41 @@ do_test 7.1 { } } {} +} + +#------------------------------------------------------------------------ +# Corruption within the structure record. +# +reset_db +do_execsql_test 8.1 { + CREATE VIRTUAL TABLE t1 USING fts5(x, y); + INSERT INTO t1 VALUES('one', 'two'); +} + +do_test 9.1.1 { + set blob "12345678" ;# cookie + append blob "0105" ;# 1 level, total of 5 segments + append blob "06" ;# write counter + append blob "0002" ;# first level has 0 segments merging, 2 other. + append blob "450108" ;# first segment + execsql "REPLACE INTO t1_data VALUES(10, X'$blob')" +} {} +do_catchsql_test 9.1.2 { + SELECT * FROM t1('one AND two'); +} {1 {database disk image is malformed}} + +do_test 9.2.1 { + set blob "12345678" ;# cookie + append blob "0205" ;# 2 levels, total of 5 segments + append blob "06" ;# write counter + append blob "0001" ;# first level has 0 segments merging, 1 other. + append blob "450108" ;# first segment + execsql "REPLACE INTO t1_data VALUES(10, X'$blob')" +} {} +do_catchsql_test 9.2.2 { + SELECT * FROM t1('one AND two'); +} {1 {database disk image is malformed}} + sqlite3_fts5_may_be_corrupt 0 finish_test diff --git a/ext/fts5/test/fts5faultB.test b/ext/fts5/test/fts5faultB.test new file mode 100644 index 0000000000..40df8b6043 --- /dev/null +++ b/ext/fts5/test/fts5faultB.test @@ -0,0 +1,83 @@ +# 2016 February 17 +# +# 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 is focused on OOM errors. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +source $testdir/malloc_common.tcl +set testprefix fts5faultB + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +proc mit {blob} { + set scan(littleEndian) i* + set scan(bigEndian) I* + binary scan $blob $scan($::tcl_platform(byteOrder)) r + return $r +} +db func mit mit + + +#------------------------------------------------------------------------- +# Errors while registering the matchinfo() demo function. +# +do_faultsim_test 1 -faults oom* -prep { + sqlite3 db test.db +} -body { + sqlite3_fts5_register_matchinfo db +} -test { + faultsim_test_result {0 {}} {1 SQLITE_ERROR} {1 SQLITE_NOMEM} +} + + +#------------------------------------------------------------------------- +# Errors while executing the matchinfo() demo function. +# +reset_db +sqlite3_fts5_register_matchinfo db +db func mit mit +do_execsql_test 2 { + CREATE VIRTUAL TABLE t1 USING fts5(a, b); + INSERT INTO t1 VALUES('x y z', '1 2 3'); + INSERT INTO t1 VALUES('x', '1 2 3 4 5 6 7'); +} + +do_faultsim_test 2.1 -faults oom* -body { + execsql { SELECT mit(matchinfo(t1, 'a')) FROM t1('x') } +} -test { + faultsim_test_result {0 {{2 5} {2 5}}} +} + +do_faultsim_test 2.2 -faults oom* -body { + execsql { SELECT mit(matchinfo(t1, 'l')) FROM t1('x') } +} -test { + faultsim_test_result {0 {{3 3} {1 7}}} +} + +do_execsql_test 2.3 { + INSERT INTO t1 VALUES('a b c d e f', 'a b d e f c'); + INSERT INTO t1 VALUES('l m b c a', 'n o a b c z'); +} + +do_faultsim_test 2.4 -faults oom* -body { + execsql { SELECT mit(matchinfo(t1, 's')) FROM t1('a b c') } +} -test { + faultsim_test_result {0 {{3 2} {2 3}}} +} + + +finish_test + diff --git a/ext/fts5/test/fts5hash.test b/ext/fts5/test/fts5hash.test index ac8486f070..f3952d6157 100644 --- a/ext/fts5/test/fts5hash.test +++ b/ext/fts5/test/fts5hash.test @@ -66,47 +66,66 @@ proc random_doc {vocab nWord} { foreach_detail_mode $testprefix { -set vocab [build_vocab1] -db func r random_doc - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE eee USING fts5(e, ee, detail=%DETAIL%); - BEGIN; - WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<100) - INSERT INTO eee SELECT r($vocab, 5), r($vocab, 7) FROM ii; - INSERT INTO eee(eee) VALUES('integrity-check'); - COMMIT; - INSERT INTO eee(eee) VALUES('integrity-check'); -} - -set hash [sqlite3_fts5_token_hash 1024 xyz] -set vocab [build_vocab1 -prefix xyz -hash $hash] -lappend vocab xyz - -do_execsql_test 1.1 { - CREATE VIRTUAL TABLE vocab USING fts5vocab(eee, 'row'); - BEGIN; -} -do_test 1.2 { - for {set i 1} {$i <= 100} {incr i} { - execsql { INSERT INTO eee VALUES( r($vocab, 5), r($vocab, 7) ) } - } -} {} + set vocab [build_vocab1] + db func r random_doc -do_test 1.3 { - db eval { SELECT term, doc FROM vocab } { - set nRow [db one {SELECT count(*) FROM eee WHERE eee MATCH $term}] - if {$nRow != $doc} { - error "term=$term fts5vocab=$doc cnt=$nRow" - } + do_execsql_test 1.0 { + CREATE VIRTUAL TABLE eee USING fts5(e, ee, detail=%DETAIL%); + BEGIN; + WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<100) + INSERT INTO eee SELECT r($vocab, 5), r($vocab, 7) FROM ii; + INSERT INTO eee(eee) VALUES('integrity-check'); + COMMIT; + INSERT INTO eee(eee) VALUES('integrity-check'); + } + + set hash [sqlite3_fts5_token_hash 1024 xyz] + set vocab [build_vocab1 -prefix xyz -hash $hash] + lappend vocab xyz + + do_execsql_test 1.1 { + CREATE VIRTUAL TABLE vocab USING fts5vocab(eee, 'row'); + BEGIN; + } + do_test 1.2 { + for {set i 1} {$i <= 100} {incr i} { + execsql { INSERT INTO eee VALUES( r($vocab, 5), r($vocab, 7) ) } + } + } {} + + do_test 1.3 { + db eval { SELECT term, doc FROM vocab } { + set nRow [db one {SELECT count(*) FROM eee WHERE eee MATCH $term}] + if {$nRow != $doc} { + error "term=$term fts5vocab=$doc cnt=$nRow" + } + } + set {} {} + } {} + + do_execsql_test 1.4 { + COMMIT; + INSERT INTO eee(eee) VALUES('integrity-check'); } - set {} {} -} {} -do_execsql_test 1.4 { - COMMIT; - INSERT INTO eee(eee) VALUES('integrity-check'); -} + #----------------------------------------------------------------------- + # Add a small and very large token with the same hash value to an + # empty table. At one point this would provoke an asan error. + # + do_test 2.0 { + set big [string repeat 12345 40] + set hash [sqlite3_fts5_token_hash 1024 $big] + while {1} { + set small [random_token] + if {[sqlite3_fts5_token_hash 1024 $small]==$hash} break + } + + execsql { CREATE VIRTUAL TABLE t2 USING fts5(x, detail=%DETAIL%) } +breakpoint + execsql { + INSERT INTO t2 VALUES($small || ' ' || $big); + } + } {} } ;# foreach_detail_mode diff --git a/ext/fts5/test/fts5matchinfo.test b/ext/fts5/test/fts5matchinfo.test index 06f4550b47..99b07677af 100644 --- a/ext/fts5/test/fts5matchinfo.test +++ b/ext/fts5/test/fts5matchinfo.test @@ -467,5 +467,29 @@ do_execsql_test 12.1 { } ;# foreach_detail_mode +#------------------------------------------------------------------------- +# Test that a bad fts5() return is detected +# +reset_db +proc xyz {} {} +db func fts5 -argcount 0 xyz +do_test 13.1 { + list [catch { sqlite3_fts5_register_matchinfo db } msg] $msg +} {1 SQLITE_ERROR} + +#------------------------------------------------------------------------- +# Test that an invalid matchinfo() flag is detected +# +reset_db +sqlite3_fts5_register_matchinfo db +do_execsql_test 14.1 { + CREATE VIRTUAL TABLE x1 USING fts5(z); + INSERT INTO x1 VALUES('a b c a b c a b c'); +} {} + +do_catchsql_test 14.2 { + SELECT matchinfo(x1, 'd') FROM x1('a b c'); +} {1 {unrecognized matchinfo flag: d}} + finish_test diff --git a/ext/fts5/test/fts5merge2.test b/ext/fts5/test/fts5merge2.test index 87dbf14332..0d3fad8b08 100644 --- a/ext/fts5/test/fts5merge2.test +++ b/ext/fts5/test/fts5merge2.test @@ -13,7 +13,8 @@ # source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5merge +set testprefix fts5merge2 +return_if_no_fts5 proc dump_structure {} { db eval {SELECT fts5_decode(id, block) AS t FROM t1_data WHERE id=10} { @@ -26,8 +27,6 @@ proc dump_structure {} { foreach_detail_mode $testprefix { -if {[detail_is_none]==0} continue - do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%); INSERT INTO t1(t1, rank) VALUES('pgsz', 32); diff --git a/ext/fts5/test/fts5simple.test b/ext/fts5/test/fts5simple.test index b6563fd9d0..464b601548 100644 --- a/ext/fts5/test/fts5simple.test +++ b/ext/fts5/test/fts5simple.test @@ -399,5 +399,53 @@ do_execsql_test 18.1 { SELECT rowid FROM x4('""'); } -finish_test +#------------------------------------------------------------------------- +reset_db +do_execsql_test 19.1 { + CREATE VIRTUAL TABLE x1 USING fts5(a,b,c); +} +do_catchsql_test 19.2 { + SELECT * FROM x1 WHERE x1 MATCH 'c0 AND (c1 AND (c2 AND (c3 AND (c4 AND (c5 AND (c6 AND (c7 AND (c8 AND (c9 AND (c10 AND (c11 AND (c12 AND (c13 AND (c14 AND (c15 AND (c16 AND (c17 AND (c18 AND (c19 AND (c20 AND (c21 AND (c22 AND (c23 AND (c24 AND (c25 AND (c26 AND (c27 AND (c28 AND (c29 AND (c30 AND (c31 AND (c32 AND (c33 AND (c34 AND (c35 AND (c36 AND (c37 AND (c38 AND (c39 AND (c40 AND (c41 AND (c42 AND (c43 AND (c44 AND (c45 AND (c46 AND (c47 AND (c48 AND (c49 AND (c50 AND (c51 AND (c52 AND (c53 AND (c54 AND (c55 AND (c56 AND (c57 AND (c58 AND (c59 AND (c60 AND (c61 AND (c62 AND (c63 AND (c64 AND (c65 AND (c66 AND (c67 AND (c68 AND (c69 AND (c70 AND (c71 AND (c72 AND (c73 AND (c74 AND (c75 AND (c76 AND (c77 AND (c78 AND (c79 AND (c80 AND (c81 AND (c82 AND (c83 AND (c84 AND (c85 AND (c86 AND (c87 AND (c88 AND (c89 AND (c90 AND (c91 AND (c92 AND (c93 AND (c94 AND (c95 AND (c96 AND (c97 AND (c98 AND (c99 AND (c100 AND (c101 AND (c102 AND (c103 AND (c104 AND (c105 AND (c106 AND (c107 AND (c108 AND (c109 AND (c110 AND (c111 AND (c112 AND (c113 AND (c114 AND (c115 AND (c116 AND (c117 AND (c118 AND (c119 AND (c120 AND (c121 AND (c122 AND (c123 AND (c124 AND (c125 AND (c126 AND (c127 AND (c128 AND (c129 AND (c130 AND (c131 AND (c132 AND (c133 AND (c134 AND (c135 AND (c136 AND (c137 AND (c138 AND (c139 AND (c140 AND (c141 AND (c142 AND (c143 AND (c144 AND (c145 AND (c146 AND (c147 AND (c148 AND (c149 AND (c150 AND (c151 AND (c152 AND (c153 AND (c154 AND (c155 AND (c156 AND (c157 AND (c158 AND (c159 AND (c160 AND (c161 AND (c162 AND (c163 AND (c164 AND (c165 AND (c166 AND (c167 AND (c168 AND (c169 AND (c170 AND (c171 AND (c172 AND (c173 AND (c174 AND (c175 AND (c176 AND (c177 AND (c178 AND (c179 AND (c180 AND (c181 AND (c182 AND (c183 AND (c184 AND (c185 AND (c186 AND (c187 AND (c188 AND (c189 AND (c190 AND (c191 AND (c192 AND (c193 AND (c194 AND (c195 AND (c196 AND (c197 AND (c198 AND (c199 AND c200)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))'; +} {1 {fts5: parser stack overflow}} + +#------------------------------------------------------------------------- +reset_db +breakpoint +do_execsql_test 20.0 { + CREATE VIRTUAL TABLE x1 USING fts5(x); + INSERT INTO x1(x1, rank) VALUES('pgsz', 32); + INSERT INTO x1(rowid, x) VALUES(11111, 'onetwothree'); +} +do_test 20.1 { + for {set i 1} {$i <= 200} {incr i} { + execsql { INSERT INTO x1(rowid, x) VALUES($i, 'one two three'); } + } + execsql { INSERT INTO x1(x1) VALUES('optimize'); } + execsql { DELETE FROM x1 WHERE rowid = 4; } +} {} +do_execsql_test 20.2 { + INSERT INTO x1(x1) VALUES('optimize'); + INSERT INTO x1(x1) VALUES('integrity-check'); +} {} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 20.0 { + CREATE VIRTUAL TABLE x1 USING fts5(x); + INSERT INTO x1(x1, rank) VALUES('pgsz', 32); + INSERT INTO x1(rowid, x) VALUES(11111, 'onetwothree'); +} +do_test 20.1 { + for {set i 1} {$i <= 200} {incr i} { + execsql { INSERT INTO x1(rowid, x) VALUES($i, 'one two three'); } + } + execsql { INSERT INTO x1(x1) VALUES('optimize'); } + execsql { DELETE FROM x1 WHERE rowid = 4; } +} {} +do_execsql_test 20.2 { + INSERT INTO x1(x1) VALUES('optimize'); + INSERT INTO x1(x1) VALUES('integrity-check'); +} {} + +finish_test diff --git a/ext/icu/icu.c b/ext/icu/icu.c index a2ff49274c..3f45646194 100644 --- a/ext/icu/icu.c +++ b/ext/icu/icu.c @@ -60,6 +60,38 @@ static void xFree(void *p){ sqlite3_free(p); } +/* +** This lookup table is used to help decode the first byte of +** a multi-byte UTF8 character. It is copied here from SQLite source +** code file utf8.c. +*/ +static const unsigned char icuUtf8Trans1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, +}; + +#define SQLITE_ICU_READ_UTF8(zIn, c) \ + c = *(zIn++); \ + if( c>=0xc0 ){ \ + c = icuUtf8Trans1[c-0xc0]; \ + while( (*zIn & 0xc0)==0x80 ){ \ + c = (c<<6) + (0x3f & *(zIn++)); \ + } \ + } + +#define SQLITE_ICU_SKIP_UTF8(zIn) \ + assert( *zIn ); \ + if( *(zIn++)>=0xc0 ){ \ + while( (*zIn & 0xc0)==0x80 ){zIn++;} \ + } + + /* ** Compare two UTF-8 strings for equality where the first string is ** a "LIKE" expression. Return true (1) if they are the same and @@ -73,16 +105,14 @@ static int icuLikeCompare( static const int MATCH_ONE = (UChar32)'_'; static const int MATCH_ALL = (UChar32)'%'; - int iPattern = 0; /* Current byte index in zPattern */ - int iString = 0; /* Current byte index in zString */ - int prevEscape = 0; /* True if the previous character was uEsc */ - while( zPattern[iPattern]!=0 ){ + while( 1 ){ /* Read (and consume) the next character from the input pattern. */ UChar32 uPattern; - U8_NEXT_UNSAFE(zPattern, iPattern, uPattern); + SQLITE_ICU_READ_UTF8(zPattern, uPattern); + if( uPattern==0 ) break; /* There are now 4 possibilities: ** @@ -99,28 +129,28 @@ static int icuLikeCompare( ** MATCH_ALL. For each MATCH_ONE, skip one character in the ** test string. */ - while( (c=zPattern[iPattern]) == MATCH_ALL || c == MATCH_ONE ){ + while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){ if( c==MATCH_ONE ){ - if( zString[iString]==0 ) return 0; - U8_FWD_1_UNSAFE(zString, iString); + if( *zString==0 ) return 0; + SQLITE_ICU_SKIP_UTF8(zString); } - iPattern++; + zPattern++; } - if( zPattern[iPattern]==0 ) return 1; + if( *zPattern==0 ) return 1; - while( zString[iString] ){ - if( icuLikeCompare(&zPattern[iPattern], &zString[iString], uEsc) ){ + while( *zString ){ + if( icuLikeCompare(zPattern, zString, uEsc) ){ return 1; } - U8_FWD_1_UNSAFE(zString, iString); + SQLITE_ICU_SKIP_UTF8(zString); } return 0; }else if( !prevEscape && uPattern==MATCH_ONE ){ /* Case 2. */ - if( zString[iString]==0 ) return 0; - U8_FWD_1_UNSAFE(zString, iString); + if( *zString==0 ) return 0; + SQLITE_ICU_SKIP_UTF8(zString); }else if( !prevEscape && uPattern==uEsc){ /* Case 3. */ @@ -129,7 +159,7 @@ static int icuLikeCompare( }else{ /* Case 4. */ UChar32 uString; - U8_NEXT_UNSAFE(zString, iString, uString); + SQLITE_ICU_READ_UTF8(zString, uString); uString = u_foldCase(uString, U_FOLD_CASE_DEFAULT); uPattern = u_foldCase(uPattern, U_FOLD_CASE_DEFAULT); if( uString!=uPattern ){ @@ -139,7 +169,7 @@ static int icuLikeCompare( } } - return zString[iString]==0; + return *zString==0; } /* @@ -325,11 +355,11 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){ */ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){ const UChar *zInput; - UChar *zOutput; + UChar *zOutput = 0; int nInput; - int nOutput; - - UErrorCode status = U_ZERO_ERROR; + int nOut; + int cnt; + UErrorCode status; const char *zLocale = 0; assert(nArg==1 || nArg==2); @@ -341,26 +371,34 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){ if( !zInput ){ return; } - nInput = sqlite3_value_bytes16(apArg[0]); - - nOutput = nInput * 2 + 2; - zOutput = sqlite3_malloc(nOutput); - if( !zOutput ){ + nOut = nInput = sqlite3_value_bytes16(apArg[0]); + if( nOut==0 ){ + sqlite3_result_text16(p, "", 0, SQLITE_STATIC); return; } - if( sqlite3_user_data(p) ){ - u_strToUpper(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status); - }else{ - u_strToLower(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status); + for(cnt=0; cnt<2; cnt++){ + UChar *zNew = sqlite3_realloc(zOutput, nOut); + if( zNew==0 ){ + sqlite3_free(zOutput); + sqlite3_result_error_nomem(p); + return; + } + zOutput = zNew; + status = U_ZERO_ERROR; + if( sqlite3_user_data(p) ){ + nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status); + }else{ + nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status); + } + if( !U_SUCCESS(status) ){ + if( status==U_BUFFER_OVERFLOW_ERROR ) continue; + icuFunctionError(p, + sqlite3_user_data(p) ? "u_strToUpper" : "u_strToLower", status); + return; + } } - - if( !U_SUCCESS(status) ){ - icuFunctionError(p, "u_strToLower()/u_strToUpper", status); - return; - } - - sqlite3_result_text16(p, zOutput, -1, xFree); + sqlite3_result_text16(p, zOutput, nOut, xFree); } /* diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 3c63b6e946..99d299c29c 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -31,7 +31,11 @@ SQLITE_EXTENSION_INIT1 #include #include -#define UNUSED_PARAM(X) (void)(X) +/* Mark a function parameter as unused, to suppress nuisance compiler +** warnings. */ +#ifndef UNUSED_PARAM +# define UNUSED_PARAM(X) (void)(X) +#endif #ifndef LARGEST_INT64 # define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) @@ -1552,6 +1556,7 @@ static void jsonArrayStep( sqlite3_value **argv ){ JsonString *pStr; + UNUSED_PARAM(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ @@ -1597,6 +1602,7 @@ static void jsonObjectStep( JsonString *pStr; const char *z; u32 n; + UNUSED_PARAM(argc); pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ diff --git a/ext/misc/series.c b/ext/misc/series.c index 2ae312effa..9f80bb46ca 100644 --- a/ext/misc/series.c +++ b/ext/misc/series.c @@ -217,6 +217,14 @@ static int seriesEof(sqlite3_vtab_cursor *cur){ } } +/* True to cause run-time checking of the start=, stop=, and/or step= +** parameters. The only reason to do this is for testing the +** constraint checking logic for virtual tables in the SQLite core. +*/ +#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY +# define SQLITE_SERIES_CONSTRAINT_VERIFY 0 +#endif + /* ** This method is called to "rewind" the series_cursor object back ** to the first row of output. This method is always called at least @@ -324,20 +332,20 @@ static int seriesBestIndex( } if( startIdx>=0 ){ pIdxInfo->aConstraintUsage[startIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[startIdx].omit = 1; + pIdxInfo->aConstraintUsage[startIdx].omit= !SQLITE_SERIES_CONSTRAINT_VERIFY; } if( stopIdx>=0 ){ pIdxInfo->aConstraintUsage[stopIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[stopIdx].omit = 1; + pIdxInfo->aConstraintUsage[stopIdx].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; } if( stepIdx>=0 ){ pIdxInfo->aConstraintUsage[stepIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[stepIdx].omit = 1; + pIdxInfo->aConstraintUsage[stepIdx].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; } if( (idxNum & 3)==3 ){ /* Both start= and stop= boundaries are available. This is the ** the preferred case */ - pIdxInfo->estimatedCost = (double)1; + pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0)); pIdxInfo->estimatedRows = 1000; if( pIdxInfo->nOrderBy==1 ){ if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8; diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c index cac1edcff8..5734d04f41 100644 --- a/ext/misc/spellfix.c +++ b/ext/misc/spellfix.c @@ -1775,7 +1775,7 @@ static void scriptCodeSqlFunc( */ /* Maximum length of a phonehash used for querying the shadow table */ -#define SPELLFIX_MX_HASH 8 +#define SPELLFIX_MX_HASH 32 /* Maximum number of hash strings to examine per query */ #define SPELLFIX_MX_RUN 1 diff --git a/ext/rbu/rbuC.test b/ext/rbu/rbuC.test new file mode 100644 index 0000000000..89fd01518f --- /dev/null +++ b/ext/rbu/rbuC.test @@ -0,0 +1,142 @@ +# 2016 March 7 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# Tests for RBU focused on the REPLACE operation (rbu_control column +# contains integer value 2). +# + +source [file join [file dirname [info script]] rbu_common.tcl] +set ::testprefix rbuC + +#------------------------------------------------------------------------- +# This test is actually of an UPDATE directive. Just to establish that +# these work with UNIQUE indexes before preceding to REPLACE. +# +do_execsql_test 1.0 { + CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE); + INSERT INTO t1 VALUES(1, 'a', 'b', 'c'); +} + +forcedelete rbu.db +do_execsql_test 1.1 { + ATTACH 'rbu.db' AS rbu; + CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control); + INSERT INTO data_t1 VALUES(1, 'a', 'b', 'c', '.xxx'); +} + +do_test 1.2 { + step_rbu test.db rbu.db +} {SQLITE_DONE} + +do_execsql_test 1.3 { + SELECT * FROM t1 +} { + 1 a b c +} + +#------------------------------------------------------------------------- +# +foreach {tn schema} { + 1 { + CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b, c UNIQUE); + CREATE INDEX t1a ON t1(a); + } + 2 { + CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE); + CREATE INDEX t1a ON t1(a); + } + 3 { + CREATE TABLE t1(i PRIMARY KEY, a, b, c UNIQUE) WITHOUT ROWID; + CREATE INDEX t1a ON t1(a); + } +} { + reset_db + forcedelete rbu.db + execsql $schema + + do_execsql_test 2.$tn.0 { + INSERT INTO t1 VALUES(1, 'a', 'b', 'c'); + INSERT INTO t1 VALUES(2, 'b', 'c', 'd'); + INSERT INTO t1 VALUES(3, 'c', 'd', 'e'); + } + + do_execsql_test 2.$tn.1 { + ATTACH 'rbu.db' AS rbu; + CREATE TABLE rbu.data_t1(i, a, b, c, rbu_control); + INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2); + INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2); + INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2); + } + + do_test 2.$tn.2 { + step_rbu test.db rbu.db + } {SQLITE_DONE} + + do_execsql_test 2.$tn.3 { + SELECT * FROM t1 ORDER BY i + } { + 1 1 2 3 + 2 b c d + 3 c d e + 4 d e f + } + + integrity_check 2.$tn.4 +} + +foreach {tn schema} { + 1 { + CREATE TABLE t1(a, b, c UNIQUE); + CREATE INDEX t1a ON t1(a); + } + + 2 { + CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); + } +} { + if {$tn==2} { ifcapable !fts5 break } + reset_db + forcedelete rbu.db + execsql $schema + + do_execsql_test 3.$tn.0 { + INSERT INTO t1 VALUES('a', 'b', 'c'); + INSERT INTO t1 VALUES('b', 'c', 'd'); + INSERT INTO t1 VALUES('c', 'd', 'e'); + } + + do_execsql_test 3.$tn.1 { + ATTACH 'rbu.db' AS rbu; + CREATE TABLE rbu.data_t1(rbu_rowid, a, b, c, rbu_control); + INSERT INTO data_t1 VALUES(1, 1, 2, 3, 2); + INSERT INTO data_t1 VALUES(3, 'c', 'd', 'e', 2); + INSERT INTO data_t1 VALUES(4, 'd', 'e', 'f', 2); + } + + do_test 3.$tn.2 { + step_rbu test.db rbu.db + } {SQLITE_DONE} + + do_execsql_test 3.$tn.3 { + SELECT rowid, * FROM t1 ORDER BY 1 + } { + 1 1 2 3 + 2 b c d + 3 c d e + 4 d e f + } + + integrity_check 3.$tn.4 +} + + + +finish_test + diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c index 145b446d30..474e39fe8d 100644 --- a/ext/rbu/sqlite3rbu.c +++ b/ext/rbu/sqlite3rbu.c @@ -280,10 +280,11 @@ struct RbuObjIter { */ #define RBU_INSERT 1 /* Insert on a main table b-tree */ #define RBU_DELETE 2 /* Delete a row from a main table b-tree */ -#define RBU_IDX_DELETE 3 /* Delete a row from an aux. index b-tree */ -#define RBU_IDX_INSERT 4 /* Insert on an aux. index b-tree */ -#define RBU_UPDATE 5 /* Update a row in a main table b-tree */ +#define RBU_REPLACE 3 /* Delete and then insert a row */ +#define RBU_IDX_DELETE 4 /* Delete a row from an aux. index b-tree */ +#define RBU_IDX_INSERT 5 /* Insert on an aux. index b-tree */ +#define RBU_UPDATE 6 /* Update a row in a main table b-tree */ /* ** A single step of an incremental checkpoint - frame iWalFrame of the wal @@ -1909,13 +1910,13 @@ static int rbuObjIterPrepareAll( ); }else{ zSql = sqlite3_mprintf( + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " + "UNION ALL " "SELECT %s, rbu_control FROM '%q' " "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 " - "UNION ALL " - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " "ORDER BY %s%s", - zCollist, pIter->zDataTbl, zCollist, p->zStateDb, pIter->zDataTbl, + zCollist, pIter->zDataTbl, zCollist, zLimit ); } @@ -1981,17 +1982,17 @@ static int rbuObjIterPrepareAll( rbuMPrintfExec(p, p->dbMain, "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" " "BEGIN " - " SELECT rbu_tmp_insert(2, %s);" + " SELECT rbu_tmp_insert(3, %s);" "END;" "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" " "BEGIN " - " SELECT rbu_tmp_insert(2, %s);" + " SELECT rbu_tmp_insert(3, %s);" "END;" "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" " "BEGIN " - " SELECT rbu_tmp_insert(3, %s);" + " SELECT rbu_tmp_insert(4, %s);" "END;", zWrite, zTbl, zOldlist, zWrite, zTbl, zOldlist, @@ -2509,14 +2510,12 @@ static int rbuStepType(sqlite3rbu *p, const char **pzMask){ switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){ case SQLITE_INTEGER: { int iVal = sqlite3_column_int(p->objiter.pSelect, iCol); - if( iVal==0 ){ - res = RBU_INSERT; - }else if( iVal==1 ){ - res = RBU_DELETE; - }else if( iVal==2 ){ - res = RBU_IDX_DELETE; - }else if( iVal==3 ){ - res = RBU_IDX_INSERT; + switch( iVal ){ + case 0: res = RBU_INSERT; break; + case 1: res = RBU_DELETE; break; + case 2: res = RBU_REPLACE; break; + case 3: res = RBU_IDX_DELETE; break; + case 4: res = RBU_IDX_INSERT; break; } break; } @@ -2555,6 +2554,67 @@ static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){ # define assertColumnName(x,y,z) #endif +/* +** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or +** RBU_IDX_DELETE. This function performs the work of a single +** sqlite3rbu_step() call for the type of operation specified by eType. +*/ +static void rbuStepOneOp(sqlite3rbu *p, int eType){ + RbuObjIter *pIter = &p->objiter; + sqlite3_value *pVal; + sqlite3_stmt *pWriter; + int i; + + assert( p->rc==SQLITE_OK ); + assert( eType!=RBU_DELETE || pIter->zIdx==0 ); + + if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){ + pWriter = pIter->pDelete; + }else{ + pWriter = pIter->pInsert; + } + + for(i=0; inCol; i++){ + /* If this is an INSERT into a table b-tree and the table has an + ** explicit INTEGER PRIMARY KEY, check that this is not an attempt + ** to write a NULL into the IPK column. That is not permitted. */ + if( eType==RBU_INSERT + && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i] + && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL + ){ + p->rc = SQLITE_MISMATCH; + p->zErrmsg = sqlite3_mprintf("datatype mismatch"); + return; + } + + if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){ + continue; + } + + pVal = sqlite3_column_value(pIter->pSelect, i); + p->rc = sqlite3_bind_value(pWriter, i+1, pVal); + if( p->rc ) return; + } + if( pIter->zIdx==0 + && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) + ){ + /* For a virtual table, or a table with no primary key, the + ** SELECT statement is: + ** + ** SELECT , rbu_control, rbu_rowid FROM .... + ** + ** Hence column_value(pIter->nCol+1). + */ + assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid"); + pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1); + p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal); + } + if( p->rc==SQLITE_OK ){ + sqlite3_step(pWriter); + p->rc = resetAndCollectError(pWriter, &p->zErrmsg); + } +} + /* ** This function does the work for an sqlite3rbu_step() call. ** @@ -2569,78 +2629,32 @@ static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){ static int rbuStep(sqlite3rbu *p){ RbuObjIter *pIter = &p->objiter; const char *zMask = 0; - int i; int eType = rbuStepType(p, &zMask); if( eType ){ + assert( eType==RBU_INSERT || eType==RBU_DELETE + || eType==RBU_REPLACE || eType==RBU_IDX_DELETE + || eType==RBU_IDX_INSERT || eType==RBU_UPDATE + ); assert( eType!=RBU_UPDATE || pIter->zIdx==0 ); if( pIter->zIdx==0 && eType==RBU_IDX_DELETE ){ rbuBadControlError(p); } - else if( - eType==RBU_INSERT - || eType==RBU_DELETE - || eType==RBU_IDX_DELETE - || eType==RBU_IDX_INSERT - ){ - sqlite3_value *pVal; - sqlite3_stmt *pWriter; - - assert( eType!=RBU_UPDATE ); - assert( eType!=RBU_DELETE || pIter->zIdx==0 ); - - if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){ - pWriter = pIter->pDelete; - }else{ - pWriter = pIter->pInsert; - } - - for(i=0; inCol; i++){ - /* If this is an INSERT into a table b-tree and the table has an - ** explicit INTEGER PRIMARY KEY, check that this is not an attempt - ** to write a NULL into the IPK column. That is not permitted. */ - if( eType==RBU_INSERT - && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i] - && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL - ){ - p->rc = SQLITE_MISMATCH; - p->zErrmsg = sqlite3_mprintf("datatype mismatch"); - goto step_out; - } - - if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){ - continue; - } - - pVal = sqlite3_column_value(pIter->pSelect, i); - p->rc = sqlite3_bind_value(pWriter, i+1, pVal); - if( p->rc ) goto step_out; - } - if( pIter->zIdx==0 - && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) - ){ - /* For a virtual table, or a table with no primary key, the - ** SELECT statement is: - ** - ** SELECT , rbu_control, rbu_rowid FROM .... - ** - ** Hence column_value(pIter->nCol+1). - */ - assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid"); - pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1); - p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal); - } - if( p->rc==SQLITE_OK ){ - sqlite3_step(pWriter); - p->rc = resetAndCollectError(pWriter, &p->zErrmsg); - } - }else{ + else if( eType==RBU_REPLACE ){ + if( pIter->zIdx==0 ) rbuStepOneOp(p, RBU_DELETE); + if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT); + } + else if( eType!=RBU_UPDATE ){ + rbuStepOneOp(p, eType); + } + else{ sqlite3_value *pVal; sqlite3_stmt *pUpdate = 0; assert( eType==RBU_UPDATE ); rbuGetUpdateStmt(p, pIter, zMask, &pUpdate); if( pUpdate ){ + int i; for(i=0; p->rc==SQLITE_OK && inCol; i++){ char c = zMask[pIter->aiSrcOrder[i]]; pVal = sqlite3_column_value(pIter->pSelect, i); @@ -2663,8 +2677,6 @@ static int rbuStep(sqlite3rbu *p){ } } } - - step_out: return p->rc; } @@ -3704,7 +3716,7 @@ static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){ static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal){ rbu_file *pDb; sqlite3_mutex_enter(pRbuVfs->mutex); - for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext); + for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){} sqlite3_mutex_leave(pRbuVfs->mutex); return pDb; } diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 4e473a22c2..012c48d6d1 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -1741,7 +1741,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ return SQLITE_NOMEM; } - nRow = pRtree->nRowEst / (iIdx + 1); + nRow = pRtree->nRowEst >> (iIdx/2); pIdxInfo->estimatedCost = (double)6.0 * (double)nRow; setEstimatedRows(pIdxInfo, nRow); diff --git a/main.mk b/main.mk index a10f043695..9763a69edc 100644 --- a/main.mk +++ b/main.mk @@ -61,7 +61,7 @@ LIBOBJ+= vdbe.o parse.o \ fts3_tokenize_vtab.o \ fts3_unicode.o fts3_unicode2.o \ fts3_write.o fts5.o func.o global.o hash.o \ - icu.o insert.o journal.o json1.o legacy.o loadext.o \ + icu.o insert.o json1.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ @@ -104,7 +104,6 @@ SRC = \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ $(TOP)/src/insert.c \ - $(TOP)/src/journal.c \ $(TOP)/src/legacy.c \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ @@ -285,6 +284,7 @@ TESTSRC = \ $(TOP)/src/test_autoext.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ + $(TOP)/src/test_bestindex.c \ $(TOP)/src/test_blob.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ @@ -500,10 +500,9 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c $(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ $(TLIBS) $(THREADLIB) -MPTEST1=./mptester$(EXE) mptest.db $(TOP)/mptest/crash01.test --repeat 20 -MPTEST2=./mptester$(EXE) mptest.db $(TOP)/mptest/multiwrite01.test --repeat 20 +MPTEST1=./mptester$(EXE) mptest1.db $(TOP)/mptest/crash01.test --repeat 20 +MPTEST2=./mptester$(EXE) mptest2.db $(TOP)/mptest/multiwrite01.test --repeat 20 mptest: mptester$(EXE) - rm -f mptest.db $(MPTEST1) --journalmode DELETE $(MPTEST2) --journalmode WAL $(MPTEST1) --journalmode WAL @@ -740,7 +739,9 @@ sqlite3_schemalint$(EXE): $(TESTSRC) sqlite3_schemalint.c # Rules to build the 'testfixture' application. # TESTFIXTURE_FLAGS = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 -TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE +TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE +TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 +TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ @@ -877,10 +878,15 @@ loadfts: $(TOP)/tool/loadfts.c libsqlite3.a checksymbols: sqlite3.o nm -g --defined-only sqlite3.o | grep -v " sqlite3_" ; test $$? -ne 0 -# Build the amalgamation-autoconf package. +# Build the amalgamation-autoconf package. The amalamgation-tarball target builds +# a tarball named for the version number. Ex: sqlite-autoconf-3110000.tar.gz. +# The snapshot-tarball target builds a tarball named by the SHA1 hash # amalgamation-tarball: sqlite3.c - TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh + TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal + +snapshot-tarball: sqlite3.c + TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot # Standard install and cleanup targets diff --git a/manifest b/manifest index 7b7e824272..a6c352d7e8 100644 --- a/manifest +++ b/manifest @@ -1,19 +1,19 @@ -C Fix\sa\sproblem\sin\sthe\sschemalint\scode\sthat\scomes\sup\swhen\sa\ssub-query\suses\sone\sor\smore\sof\sthe\ssame\stables\sas\sits\sparent. -D 2016-03-09T08:07:31.577 -F Makefile.in dac2776c84e0d533b158a9af6e57e05c4a6b19f3 +C Merge\slatest\strunk\schanges\swith\sthis\sbranch. +D 2016-03-09T08:08:51.626 +F Makefile.in f53429fb2f313c099283659d0df6f20f932c861f F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc b0493f10caddb8adf992a4e6f1943141fc7c6816 +F Makefile.msc df0bf9ff7f8b3f4dd9fb4cc43f92fe58f6ec5c66 F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7 -F VERSION 866588d1edf0ccb5b0d33896974338f97564f719 +F VERSION c6b1f51809551d60ad001e6d87cf3ab2c7f54b6f F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903 -F autoconf/Makefile.am 1c1657650775960804945dc392e14d9e43c5ed84 -F autoconf/Makefile.msc a35b2aab24d1603f3f0ae65cf01686c2578d319c +F autoconf/Makefile.am 4b8bd7896fffb6b22a57747f1f98cba1da9108ae +F autoconf/Makefile.msc b8ada5177e6f39c126a88295af5ad0255e08d280 F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 -F autoconf/README.txt e9757a381e5ce2553dbaa6247bb8ad00eb8d87aa +F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1 F autoconf/configure.ac 72a5e42beb090b32bca580285dc0ab3c4670adb8 F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873 @@ -28,12 +28,12 @@ F autoconf/tea/win/makefile.vc f89d0184d0eee5f7e356ea407964dcd139939928 F autoconf/tea/win/nmakehlp.c 247538ad8e8c508f33c03ec1fbd67d3a07ef6291 F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 -F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0 +F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 12d96e3798e612e0ffa53a7a8c4d7fb1090df80e x -F configure.ac a2224b1162f79848982d3618ac1deffcd94e88ec +F configure c01a159fdf7ea0171ad01ce2937283f2c7972bde x +F configure.ac 89e4e02a83d8a1528011f8395621b8c3186b4089 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad -F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 +F doc/lemon.html c30255bea0fd87a81f082d17a72c9dffbc3f6dd9 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1 @@ -72,7 +72,7 @@ F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d F ext/fts3/fts3.c e028eb13432f108d2e22cded019fc980700e4e00 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h c84125c666ee54cef6efce6ff64abb0d0e2f4535 +F ext/fts3/fts3Int.h 89d0bd4595a0de384dac78e94b803de12586e8dd F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1 F ext/fts3/fts3_expr.c dfd571a24412779ac01f25c01d888c6ef7b2d0ef F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60 @@ -81,42 +81,42 @@ F ext/fts3/fts3_icu.c deb46f7020d87ea7a14a433fb7a7f4bef42a9652 F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009 F ext/fts3/fts3_snippet.c 68ae118b0f834ea53d2b89e4087fc0f0b8c4ee4e F ext/fts3/fts3_term.c 88c55a6fa1a51ab494e33dced0401a6c28791fd7 -F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038 +F ext/fts3/fts3_test.c 11e36437b0f3f2266acea5b4787f615e4337a237 F ext/fts3/fts3_tokenize_vtab.c a27593ab19657166f6fa5ec073b678cc29a75860 -F ext/fts3/fts3_tokenizer.c 4bd72f767f61c9ce5a7575c844e8d1ed2c3c561a +F ext/fts3/fts3_tokenizer.c 3cf21cd2212db17a88d4ef7da0fd8a80275979a1 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 f442223e4a1914dc1fc12b65af7e4f2c255fa47c +F ext/fts3/fts3_write.c fb0456fa1407758f82458417c76fe4b06c86be98 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/tool/fts3view.c 5d78b668f4e9598af9147f8999632599fb0d9dd5 F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 -F ext/fts3/unicode/mkunicode.tcl 95cf7ec186e48d4985e433ff8a1c89090a774252 +F ext/fts3/unicode/mkunicode.tcl 2debed3f582d77b3fdd0b8830880250021571fd8 F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95 F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 F ext/fts5/fts5.h ff9c2782e8ed890b0de2f697a8d63971939e70c7 -F ext/fts5/fts5Int.h efb02807dbe5a2bfb0ea592a472d1171cb553d53 -F ext/fts5/fts5_aux.c b9bcce753ef5b451267b2232f0ca153ddeb3951d -F ext/fts5/fts5_buffer.c 5142f73defd7d2ad7419f3a8e487681b38b72097 -F ext/fts5/fts5_config.c 0c384ebdd23fd055e2e50a93277b8d59da538238 -F ext/fts5/fts5_expr.c ff5c451a6d025909639ac0f0d0af0cc595b50feb -F ext/fts5/fts5_hash.c 1b113977296cf4212c6ec667d5e3f2bd18036955 -F ext/fts5/fts5_index.c 28f72130400cb54d179a9a120b7232915e3e7a4e -F ext/fts5/fts5_main.c 6e23df904049edb498538bd3e22e53ec1ab6f4f7 -F ext/fts5/fts5_storage.c 2a1f44deae090cd711f02cec0c2af8e660360d24 +F ext/fts5/fts5Int.h fa7c17e5c3ec9c8690387ff962f9dc6aee75e114 +F ext/fts5/fts5_aux.c daa57fb45216491814520bbb587e97bf81ced458 +F ext/fts5/fts5_buffer.c 4c1502d4c956cd092c89ce4480867f9d8bf325cd +F ext/fts5/fts5_config.c 35c5173cae4eb17e82164a7f5aeef56a48903079 +F ext/fts5/fts5_expr.c 8e8e4635f655133eb39018072fc0f0942a2c4337 +F ext/fts5/fts5_hash.c f3a7217c86eb8f272871be5f6aa1b6798960a337 +F ext/fts5/fts5_index.c 26a4a6112864feb599a6f6144d06a78bb179736a +F ext/fts5/fts5_main.c db24ac714c6c4a1b3c24a1f8c25889f2952148c1 +F ext/fts5/fts5_storage.c f8343db90d8c95a4d4b52f6676e354b4649ffd6e F ext/fts5/fts5_tcl.c f8731e0508299bd43f1a2eff7dbeaac870768966 -F ext/fts5/fts5_test_mi.c 1ec66ffdf7632077fbd773b7a6df5153272ec070 +F ext/fts5/fts5_test_mi.c b8d04816428202b2898d4ca38deb1739ac0110ae F ext/fts5/fts5_test_tok.c db08af63673c3a7d39f053b36fd6e065017706be -F ext/fts5/fts5_tokenize.c 4d5c4f183c7d07d144bc219b92da1ea0e962fae3 -F ext/fts5/fts5_unicode2.c 78273fbd588d1d9bd0a7e4e0ccc9207348bae33c +F ext/fts5/fts5_tokenize.c 2ce7b44183538ec46b7907726262ee43ffdd39a8 +F ext/fts5/fts5_unicode2.c b450b209b157d598f7b9df9f837afb75a14c24bf F ext/fts5/fts5_varint.c a5aceacda04dafcbae725413d7a16818ecd65738 -F ext/fts5/fts5_vocab.c 3ef401a8d6932db56368de32f446eb9fe73aa623 -F ext/fts5/fts5parse.y 1647eba089b9b3fc058b4dc989d9da87d15b9580 +F ext/fts5/fts5_vocab.c dba72ca393d71c2588548b51380387f6b44c77a8 +F ext/fts5/fts5parse.y 86fe6ba094a47e02fe8be2571539e6833d197764 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba -F ext/fts5/test/fts5_common.tcl 61ff0d1a29d98a91c4553b20b3f410d858834ee9 +F ext/fts5/test/fts5_common.tcl b9b1fed811c0390511cef8b254826ea15d380f4d F ext/fts5/test/fts5aa.test 7e814df4a0e6c22a6fe2d84f210fdc0b5068a084 F ext/fts5/test/fts5ab.test 30325a89453280160106be411bba3acf138e6d1b F ext/fts5/test/fts5ac.test 55cad4275a1f5acabfe14d8442a8046b47e49e5f @@ -134,14 +134,14 @@ F ext/fts5/test/fts5auto.test 401c20e89f1114d733b94809be1e6f893e16c09e F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb -F ext/fts5/test/fts5bigtok.test 981b2790f6fa02773c889bd35d42c6b97f80f0f4 +F ext/fts5/test/fts5bigtok.test 017a9397b14e7598883a6328ead4a6539b42d59a F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07 F ext/fts5/test/fts5config.test 8b2bc6dcc0eb06fa2b7dd65b2ce2db09e829e873 F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5 F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1 F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62 F ext/fts5/test/fts5corrupt2.test 26c0a39dd9ff73207e6229f83b50b21d37c7658c -F ext/fts5/test/fts5corrupt3.test a2b537c120bdd43c79c42fe2438d7b8c81fe5599 +F ext/fts5/test/fts5corrupt3.test b9558d5b0ca44a8b6247fbb5d4a47592a8976892 F ext/fts5/test/fts5detail.test ef5c690535a797413acaf5ad9b8ab5d49972df69 F ext/fts5/test/fts5dlidx.test 13871a14641017ae42f6f1055a8067bafd44cb3d F ext/fts5/test/fts5doclist.test 8edb5b57e5f144030ed74ec00ef6fa4294fed79b @@ -157,12 +157,13 @@ F ext/fts5/test/fts5fault7.test cb14ea3c1f42394f06f2284abc58eecee6ff8080 F ext/fts5/test/fts5fault8.test 430837fe6dd0511fd3aea52bd602ac02441bcb58 F ext/fts5/test/fts5fault9.test e10e395428a9ea0596ebe752ff7123d16ab78e08 F ext/fts5/test/fts5faultA.test fa5d59c0ff62b7125cd14eee38ded1c46e15a7ea +F ext/fts5/test/fts5faultB.test 92ae906284062bf081b6c854afa54dcb1aa9ef88 F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741 -F ext/fts5/test/fts5hash.test 00668f6fa9b9bffbd7c1be29f408aa2bdade0451 +F ext/fts5/test/fts5hash.test 06f9309ccb4d5050a131594e9e47d0b21456837d F ext/fts5/test/fts5integrity.test f5e4f8d284385875068ad0f3e894ce43e9de835d -F ext/fts5/test/fts5matchinfo.test 86569026d20f1ed748236587ce798de8a96615f1 +F ext/fts5/test/fts5matchinfo.test f7dde99697bcb310ea8faa8eb2714d9f4dfc0e1b F ext/fts5/test/fts5merge.test 8f3cdba2ec9c5e7e568246e81b700ad37f764367 -F ext/fts5/test/fts5merge2.test c0cb66eb38a41c26cc5848fb9e50093e0f59ac93 +F ext/fts5/test/fts5merge2.test a6da3c16d694235938d1939f503cfa53f0943d75 F ext/fts5/test/fts5near.test b214cddb1c1f1bddf45c75af768f20145f7e71cc F ext/fts5/test/fts5onepass.test 7ed9608e258132cb8d55e7c479b08676ad68810c F ext/fts5/test/fts5optimize.test 42741e7c085ee0a1276140a752d4407d97c2c9f5 @@ -176,7 +177,7 @@ F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17 F ext/fts5/test/fts5rowid.test 16908a99d6efc9ba21081b4f2b86b3fc699839a6 -F ext/fts5/test/fts5simple.test 7fcacfa473a37355af2e60096650c87b5ba8f3ba +F ext/fts5/test/fts5simple.test ac5006cc3d0d08b3538e1e76c7300de9f24fbed1 F ext/fts5/test/fts5simple2.test 98377ae1ff7749a42c21fe1a139c1ed312522c46 F ext/fts5/test/fts5simple3.test 8e71733b3d1b0e695011d02c68ebc5ca40b6124e F ext/fts5/test/fts5synonym.test 6475d189c2e20d60795808f83e36bf9318708d48 @@ -197,7 +198,7 @@ F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093 F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45 F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 -F ext/icu/icu.c b2732aef0b076e4276d9b39b5a33cec7a05e1413 +F ext/icu/icu.c 4bdf4c5daedabcd8e6eb6e6a377657f7b978f9f7 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/misc/amatch.c a1a8f66c29d40bd71b075546ddeddb477b17a2bb F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704 @@ -206,14 +207,14 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767 F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c -F ext/misc/json1.c a27cf1eca6583f9b6e18abab5c2a9a82c4540ca9 +F ext/misc/json1.c b9c88d5c3b6ecd8c731ffdd7f5b3d902857f8c96 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/series.c b8fb7befd85b3a9b4a10e701b30b2b79ca92b6d4 +F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 -F ext/misc/spellfix.c 525190484b7a9dbc6be646c4842274fff4f27d53 +F ext/misc/spellfix.c 194b5fc3a9a63cb6c5680d8f713800012bddca7c F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e @@ -233,6 +234,7 @@ F ext/rbu/rbu8.test 3bbf2c35d71a843c463efe93946f14ad10c3ede0 F ext/rbu/rbu9.test 0806d1772c9f4981774ff028de6656e4183082af F ext/rbu/rbuA.test c1a7b3e2d926b8f8448bb3b4ae787e314ee4b2b3 F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2 +F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831 F ext/rbu/rbu_common.tcl 0398545fed614f807d5f0ba55a85a51f08ba8f1a F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695 F ext/rbu/rbudiff.test 6cc806dc36389292f2a8f5842d0103721df4a07d @@ -240,11 +242,11 @@ F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89 F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06 F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48 -F ext/rbu/sqlite3rbu.c bea954197524631f2691ec272e8a42df8cad01cc +F ext/rbu/sqlite3rbu.c 5956f8bee63b5ab2b04e65c1801ea0f5920dac92 F ext/rbu/sqlite3rbu.h 0bdeb3be211aaba7d85445fa36f4701a25a3dbde F ext/rbu/test_rbu.c 4a4cdcef4ef9379fc2a21f008805c80b27bcf573 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c 0f9b595bd0debcbedf1d7a63d0e0678d619e6c9c +F ext/rtree/rtree.c 0b870ccb7b58b734a2a8e1e2755a7c0ded070920 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba @@ -272,55 +274,54 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk eeff3d12ebe5945dac88147c641407c22a731131 +F main.mk e3b21467eb50c2dff9bf4003a02aea753bfe7e35 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421 F mptest/crash02.subtest f4ef05adcd15d60e5d2bd654204f2c008b519df8 -F mptest/mptest.c 0d3f2eb8e373cb692ab362a6dddedd53e0978502 +F mptest/mptest.c aa41ace6dbc5050d76b02548d3521e6bbccae4f0 F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c d50b7dbb49a4affee951301afb76a008463e3625 -F src/analyze.c fbf0e80d83cc893734e872f932f249a056b86e11 -F src/attach.c c16c2648a577fa3def2adfa48c28901376389bc5 +F src/alter.c 1bb0709b3048e24217b80ec6bd78a3e99a47c01b +F src/analyze.c ab57b6763dd4c6170a20673d14882c033affd188 +F src/attach.c 771153bd1f4ab0b97a44a13dde2c7e5e1efeba22 F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 -F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc -F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf +F src/backup.c f60f0aa55d25d853ffde53d0b0370a7bb7ee41ce +F src/bitvec.c 3ee4c8b2c94ed3a7377256e18199e6ff5cf33f63 F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73 -F src/btree.c 4c8caaeed7878aafdb607c3d2bcbc365bb0d19a1 -F src/btree.h 368ceeb4bd9312dc8df2ffd64b4b7dbcf4db5f8e +F src/btree.c 6eee126fe9d1f57118de9be2be840a4c6e691828 +F src/btree.h a5008b9afe56e8e54ade6c436a910f112defcca9 F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5 -F src/build.c 54866fbafa09d494269bdefc79995eb7207003a6 -F src/callback.c ed6c2a4a712eb7287ff64e20e3c23265dfb8a7ce -F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f +F src/build.c 213cbf84e99dd834e6ea46615633656d7ef79321 +F src/callback.c 2e76147783386374bf01b227f752c81ec872d730 +F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 60e135af364d777a9ab41c97e5e89cd224da6198 -F src/date.c ca17321bc17cca8f40e0843edea4fafff974998e -F src/dbstat.c b2ec6793eef97aebb4d171d490a4ffdfa9f2475c +F src/date.c 0b73e681c11fca867fec554750c07fe0d4e417c1 +F src/dbstat.c c845548d4346e606e2f2b7d2e714ace2b8a7dd1b F src/delete.c 48802aa3ee6339f576d074336d3ae1b5f40e240f -F src/expr.c fbf0706199aea23c54efe36b6932d8307c4eb872 +F src/expr.c c4dad2cd6cec00387b75fef4551aff655430dcd2 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 08edad1fce30f761f14b3997e89bad58f9f7f4e0 -F src/func.c 86e55fee35b9577e485f47d9dd5c1d34cd513288 -F src/global.c bd5a0af3f30b0c01be6db756c626cd3c33a3d260 +F src/fkey.c 5cb42d9a59e2a590776fd3fc8ff6f61d40df3c6e +F src/func.c 552d300265aed09eea21f68ac742a440550c0062 +F src/global.c 884d4c7eba9f5fc25c96a23b21520da19b7713e2 F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5 F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c 046199e085e69e05af7bef197d53c5b4b402b6fa -F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d -F src/legacy.c b1b0880fc474abfab89e737b0ecfde0bd7a60902 -F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b -F src/main.c 816b9a98a6aca0fd643e77f3610d6a4a1a4c7e24 -F src/malloc.c 337e9808b5231855fe28857950f4f60ae42c417f +F src/insert.c 723d5d708cdb61bdd47c00b9f07c75be45aefc09 +F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e +F src/loadext.c 9e2a41adcaff16ebc1ebff1f336cbf33de55396f +F src/main.c 787fc7ccd341ae80695fb3954ab95b84b7b3a146 +F src/malloc.c 1443d1ad95d67c21d77af7ae3f44678252f0efec F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944 -F src/memjournal.c 3eb2c0b51adbd869cb6a44780323f05fa904dc85 +F src/memjournal.c 6423a0817ffd8c7a04ef9e5fb974b6b9dd71f8b6 F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495 F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85 @@ -328,38 +329,38 @@ F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 F src/mutex_unix.c 27bb6cc49485ee46711a6580ab7b3f1402211d23 F src/mutex_w32.c 5e6fe1c298fb5a8a15aaed4161d5759311431c17 F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7 -F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8 +F src/os.c f89e3ca1c2e3d5015b847aec60371c474acbac82 F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf -F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e +F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa -F src/os_unix.c 821ed110197175165cf2f50b0930c7ff9a24504c -F src/os_win.c ccf29ddded3e41e506b6bd98c1171aa0963b23f2 +F src/os_unix.c f5bac8e74aaefc4ea520e43b4540793c3b8a9e8f +F src/os_win.c f0d7aa603eb6262143d7169a222aea07c4fca91d F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca -F src/pager.c 67cd2fbab58d0e35fed5f81432856f4f0af9fc6d -F src/pager.h f3eb324a3ff2408b28bab7e81c1c55c13720f865 -F src/parse.y d7bff41d460f2df96fb890f36700e85cb0fc5634 -F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23 +F src/pager.c 2d950142528e5d9b291a1160443d7ade0d90700d +F src/pager.h e1d38a2f14849e219df0f91f8323504d134c8a56 +F src/parse.y 5ea8c81c5c41b27887f41b4a7e1c58470d7d3821 +F src/pcache.c 647bb53a86b7bbcf55ad88089b3ea5a9170b90df F src/pcache.h 4d0ccaad264d360981ec5e6a2b596d6e85242545 F src/pcache1.c 72f644dc9e1468c72922eff5904048427b817051 -F src/pragma.c cfd521558fccd3864ec664af09a061e9e692583f +F src/pragma.c 3ca4a32189ffa5d0b2a908c9d55bd7fcd2717a62 F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c -F src/prepare.c c12b786713df3e8270c0f85f988c5359d8b4d87c +F src/prepare.c 22df6171aec1d86904ed2ad30c2348a5748aa04e F src/printf.c 63e6fb12bbe702dd664dc3703776c090383a5a26 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 -F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c +F src/resolve.c b8f7174e5f8c33c44ded3a25a973d0bb89228c20 F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e -F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d -F src/shell.c 2cde87e03712204231167c4a6c61b0eb5129e105 +F src/select.c 137b31daa84d57d67847bf621bb54f3353e2077b +F src/shell.c 4aba7844546ffd02b728a9509226a6e3c0462f77 F src/shell_indexes.c 23c4b7f7bb8e9b0a8b912c389a85922939bb0769 -F src/sqlite.h.in c7db059d3b810b70b83d9ed1436fa813eba22462 +F src/sqlite.h.in 9f0fee10494624d03686fabcc270ce8d12fcb65b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d -F src/sqliteInt.h a1d0d9613ed7da3657396795e44991fef188c8ee -F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 +F src/sqliteInt.h dc193eb8a9922528a370eae11e2e66886e0f29a3 +F src/sqliteLimit.h 7b28cf72cbd52f178bfc97ea266445e351f2cd24 F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba -F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e -F src/tclsqlite.c 94ef6e2794220c5b6064d4c78ec7169a8c5cc45d -F src/test1.c 4f1b42699068b7806af3111786f5ad760c2c1ff7 +F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 +F src/tclsqlite.c 54dca4eaf3a9cb0a4b845ef54ffa99225c30a52e +F src/test1.c f14a6f9e2cff6cba4d83e2b0c52857f61886cead F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b F src/test3.c a8887dabbbee3059af338f20d290084a63ed1b0f F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e @@ -371,12 +372,13 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 -F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07 +F src/test_bestindex.c 29af3cc3b963ffe5760c85d142b9b3e5302c1e3d +F src/test_blob.c b2551a9b5573232db5f66f292307c37067937239 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f -F src/test_config.c 7985332c806d1cece793475c75a6abcccde9d331 +F src/test_config.c 0dee90328e3dedf8ba002ee94b6a7e7ea7726fe4 F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc -F src/test_fs.c a61f54247fdb843761d709879c3bcd1989b2050c +F src/test_fs.c f10f840ca4f8c72e4837908bd8347ac4bcab074b F src/test_func.c 37453d346cfcf118774efd5bf6187f7e6a7e3254 F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32 @@ -406,32 +408,32 @@ F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_windirent.c 8f5fada630348558d5745b334702f301da1ffc61 F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 -F src/threads.c bbfb74450643cb5372a43ad4f6cffd7e9dfcecb0 -F src/tokenize.c 813934be70597edfbb685ae08fc4c8b549cf5a1e -F src/treeview.c dc39ccf04e9331237388b9cb73289c9d87ea050b +F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c +F src/tokenize.c 3d338cdd00d916ce8a05c397001d64ed58e6fe1c +F src/treeview.c e4b41a37530a191579d3c53142cc44ee2eb99373 F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280 -F src/update.c 310ca7adb86a7d1f2afae46905b21c83580f3e17 -F src/utf.c 10cc2519e82e3369344d0969ad4b1a333dc86d18 -F src/util.c 49ce0a65306c1c51d61cb5bc214c71cb62452de6 -F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 -F src/vdbe.c c193299e595a13eba247738e22fce25c49346a6c +F src/update.c 56b3db7edff0110360a12b76af97c39ebe3ea8b8 +F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c +F src/util.c 12800a93f0664f41575f96799eb881a786d565e6 +F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52 +F src/vdbe.c 87ae3a5657fefed7875f3eb30e7ababd48013d71 F src/vdbe.h c743791f723049db94f009e3e30958952bc2d512 -F src/vdbeInt.h 4b69d5451bcadd473e745af53ef1e8abfdce0a79 -F src/vdbeapi.c 9324f6baee1a1b2284c6543e98f916888a81e459 -F src/vdbeaux.c deae5d3bd45da0e57c7d9e1d7436333d142dc3bb +F src/vdbeInt.h 49a74fe5ece206d2d8666ba9afaf1abeda5f123b +F src/vdbeapi.c 95b1f8e527240a18a9aea41a655b013bf07a7009 +F src/vdbeaux.c 2c15cf88de4df97428318c8cfac0dea873dae451 F src/vdbeblob.c 3b570b730109e8f653d9d2081649f6e7015113db -F src/vdbemem.c 68fcfac37dc6601d98c32cc5adee4d39f2c1b7b4 -F src/vdbesort.c ef3c6d1f1a7d44cf67bb2bee59ea3d1fe5bad174 +F src/vdbemem.c 9b0cb32cc267ef026515f15a3594d5ff91fe4dfc +F src/vdbesort.c 307460bfa4de4d1c3901fcd42089159131e34062 F src/vdbetrace.c f75c5455d8cf389ef86a8bfdfd3177e0e3692484 -F src/vtab.c bef51b4f693d82b4b0184457faa8625654534091 +F src/vtab.c fd69fd398e23e57ea4ea377d8a44b6998fc569c7 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c d21b99fd1458159d0b1ecdccc8ee6ada4fdc4c54 +F src/wal.c 10deb6b43887662691e5f53d10b3c171c401169b F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 -F src/where.c 59f267a53d29db13880597011323745b69e14f24 -F src/whereInt.h 78b6b4de94db84aecbdc07fe3e38f648eb391e9a -F src/wherecode.c 791a784bbf8749d560fdb0b990b607bc4f44a38d -F src/whereexpr.c de117970b29471177a6901d60ad83a194671dc03 +F src/where.c 2cbcacd9ae9bc8570cb83eaf3400700dbf90ac87 +F src/whereInt.h 93297d56edd137b7ea004490690fb6e2ce028a34 +F src/wherecode.c 863aedf086131743763c1960637fde904eadc442 +F src/whereexpr.c fb87944b1254234e5bba671aaf6dee476241506a F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -440,7 +442,7 @@ F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 F test/alter.test 2facdddf08d0d48e75dc6cc312cd2b030f4835dd F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 -F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d +F test/alter3.test b3568d11c38c4599c92f24242eda34144d78dc10 F test/alter4.test c461150723ac957f3b2214aa0b11552cd72023ec F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f @@ -451,11 +453,11 @@ F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4 F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f F test/analyze8.test c05a461d0a6b05991106467d0c47480f2e709c82 -F test/analyze9.test 88c1f2aa20b614236f03e1cc38c3619e7e8a38b4 +F test/analyze9.test b817b8e798315fc65b820a5463f73ad5f48ed8dd F test/analyzeA.test 3335697f6700c7052295cfd0067fc5b2aacddf9a F test/analyzeB.test a4c1c3048f6d9e090eb76e83eecb18bcf6d31a70 F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93 -F test/analyzeD.test f3d77cd0fefe2849d784897d52df13beee19271d +F test/analyzeD.test 42af58de25a6436502e43006e9e59e2d71bcb0cf F test/analyzeE.test 8684e8ac5722fb97c251887ad97e5d496a98af1d F test/analyzeF.test 5d1fe1024ba2dfea3c18bede8c1ccef8aba1ab34 F test/analyzer1.test 498e2ff4b62740c2751c3a2f8b744fe26689fae9 @@ -475,14 +477,14 @@ F test/auth2.test 264c6af53cad9aba5218c68bbe18036e39007bfa F test/auth3.test 5cfa94ed90c6617c42b7ba4b133fd79678b251c7 F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7 F test/autoindex1.test 14b63a9f1e405fe6d5bfc8c8d00249c2ebaf13ea -F test/autoindex2.test af7e595c6864cc6ef5fc38d5db579a3e34940cb8 +F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df F test/autoindex3.test a3be0d1a53a7d2edff208a5e442312957047e972 F test/autoindex4.test 49d3cd791a9baa16fb461d7ea3de80d019a819cf F test/autoindex5.test 96f084a5e6024ea07cace5888df3223f3ea86990 F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 -F test/backcompat.test 19a1f337c68419b020a7481dd272a472c4ad8ef4 +F test/backcompat.test 28403f43d11624e696c5ef134c7c7d81015cd19e F test/backup.test b79299a536a4c6d919094786595b95be56d02014 F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf F test/backup4.test 2a2e4a64388090b152de753fd9e123f28f6a3bd4 @@ -492,6 +494,8 @@ F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f F test/bc_common.tcl 3eda41ef9cda7d5f6c205462c96228b301da4191 +F test/bestindex1.test e228fe1e3794dbe20271481164e000d695abcd24 +F test/bestindex2.test 10f2c6791f1cd0de414012528cd10a114648fd8f F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc @@ -523,7 +527,7 @@ F test/capi3d.test 485048dc5cd07bc68011e4917ad035ad6047ab82 F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 F test/cffault.test aadc1f61f8811cb600e3e069acbf8796f472a096 -F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763 +F test/check.test 85a84bfc4be0e83f668747211c7cd45a6721d485 F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815 F test/closure01.test b1703ba40639cfc9b295cf478d70739415eec6a4 F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 @@ -539,8 +543,8 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b -F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8 -F test/conflict2.test a82dd3b9b41fceb5dd6ff0707c5c7ffba208d538 +F test/conflict.test 029faa2d81a0d1cafb5f88614beb663d972c01db +F test/conflict2.test bb0b94cf7196c64a3cbd815c66d3ee98c2fecd9c F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4 F test/corrupt.test 141c39ea650c1365e85a49e402fa05cb9617fb97 @@ -564,7 +568,7 @@ F test/corruptI.test 347babbf970e7947e3f91dccf7a1bec28a1bab04 F test/corruptJ.test 9e29e7a81ee3b6ac50f77ea7a9e2f3fa03f32d91 F test/cost.test 1eedbfd868f806f3fa08ff072b04cf270dcf61c8 F test/count.test cb2e0f934c6eb33670044520748d2ecccd46259c -F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62 +F test/coveridxscan.test b629e896b14df2f000a99b8d170d80589c46562c F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651 F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418 @@ -617,7 +621,7 @@ F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9 F test/e_wal.test ae9a593207a77d711443ee69ffe081fda9243625 F test/e_walauto.test 280714ddf14e1a47dcbc59d515cd0b026dfd5567 F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0 -F test/e_walhook.test da3ea8b3483d1af72190337bda50155a91a4b664 +F test/e_walhook.test 4c0613a0c76e7a9d5c4c211e1b4cbcc1143914df F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473 F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6 @@ -625,7 +629,7 @@ F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020 F test/eqp.test 85873fa5816c48915c82c4e74cb5c35a5b48160f F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401 F test/eval.test a64c9105d6ff163df7cf09d6ac29cdad5922078c -F test/exclusive.test c7ebbc756eacf544c108b15eed64d7d4e5f86b75 +F test/exclusive.test f48243eaf40e0957215501a12f510a8644d13a02 F test/exclusive2.test 32798111aae78a5deec980eee383213f189df308 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30 @@ -640,7 +644,7 @@ F test/fkey2.test f3d27ecba480a348c328965d154214719bb158a9 F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49 F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d F test/fkey5.test 5a373303f201ac03c22ba1ef17a733d3f56e611a -F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48 +F test/fkey6.test ce3c39cd4f910184f5fd4a48db6783c59956224f F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13 F test/fkey8.test 7bd1dd0174a0e29a90c62c517b9e2a410a0b345d F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 @@ -698,7 +702,7 @@ F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8 F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18 F test/fts3ao.test 3e4e3d5e75c076520341d0bdf4eb17c00e8cbde2 -F test/fts3atoken.test 76262be798f23a390717d14266f0df551e52a7ee +F test/fts3atoken.test 4b4c16fdcfc972f2cdbba212375a060a86ccf5f1 F test/fts3auto.test b981fea19b132b4e6878f50d7c1f369b28f68eb9 F test/fts3aux1.test f8f287a4a73f381f8fa15b6a70f36245f903d221 F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba @@ -742,16 +746,17 @@ F test/fts4aa.test 10aac8e9d62c7357590acfabe3fad01e9a9ce1cb F test/fts4check.test c3056eab9524232e4c9bdcd119912947e07bcc1c F test/fts4content.test 05716af19a899cd70d5cd916c580043c03f30db4 F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01 -F test/fts4growth.test 60d6bb3f78e25b34f533797dd9f2f9402310a13a +F test/fts4growth.test e5390da74619cacc389711bac9349640b32c4f9a F test/fts4growth2.test 13ad4e76451af6e6906c95cdc725d01b00044269 F test/fts4incr.test 4e353a0bd886ea984e56fce9e77724fc923b8d0d -F test/fts4langid.test 8bd8759e0d4b04d71771544b861193a6841fee84 -F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee +F test/fts4langid.test 65a7332c9bc257919e259a304aa8a38c41655b9d +F test/fts4merge.test d2b39f6b1bd4a9738a13540e2d044cba11c43d47 F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891 -F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7 +F test/fts4merge3.test 8d9ccb4a3d41c4c617a149d6c4b13ad02de797d0 F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b F test/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309 F test/fts4onepass.test 7319d61a2ed1325fc54afd0c060a0513b462303a +F test/fts4opt.test 1901f696214c4e16a995dd7fdd70576fc5c41dbe F test/fts4unicode.test 27378af76394542cf490cf001d8d1505fe55f6a9 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef @@ -765,7 +770,7 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test b47377143f0c80f91ed29d722861077ff34415d5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzcheck.c 3309d793165ca61a9996271cb799694839348f9a +F test/fuzzcheck.c 93bb9d309888634615e21ef98d1c30d51483e942 F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664 F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973 F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -777,7 +782,7 @@ F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98 F test/hexlit.test d7b0a5f41123df1e43985b91b8b2e70f95282d21 F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711 F test/hook.test 162d7cef7a2d2b04839fe14402934e6a1b79442f -F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 +F test/icu.test 73956798bace8982909c00476b216714a6d0559a F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607 F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8 F test/in.test 61a24ae38d4b64ec69f06ccdf022992f68a98176 @@ -856,7 +861,7 @@ F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5 F test/lock7.test 49f1eaff1cdc491cc5dee3669f3c671d9f172431 F test/lock_common.tcl 7ffb45accf6ee91c736df9bafe0806a44358f035 F test/lookaside.test 90052e87282de256d613fcf8c9cbb845e4001d2f -F test/main.test 16131264ea0c2b93b95201f0c92958e85f2ba11a +F test/main.test bb75e406c9b64931f3dc7e7f04626633365bb22f F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 F test/malloc.test 21c213365f2cca95ab2d7dc078dc8525f96065f8 F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a @@ -890,14 +895,14 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f -F test/misc1.test 48ebfb5b22a6a058f7b7e1df211226dd1d21409c +F test/misc1.test 6430dabfb4b4fa480633590118964201f94d3ccc F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3 F test/misc5.test f96428ea95b3820aafc6f1c50cf48a09e4597ee1 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2 -F test/misc8.test fc2754d38892f7dac30c22db3616c2764f117d66 +F test/misc8.test 21ac9d35a5e110279ae9e1588b8914f54de1c60b F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912 F test/mmap1.test 44a5ff1c1bcc7dcf2de50227d1f997e75a8ef1ae F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022 @@ -932,7 +937,7 @@ F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3 F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f -F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa +F test/pager1.test f49df1a8b0e38b9ee3a7dd2ab4d427507b7314ce F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e @@ -945,7 +950,7 @@ F test/parser1.test 222b5cbf3e2e659fec1bf7d723488c8b9c94f1d0 F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442 F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff -F test/permutations.test 382a43c49f49bafe6fddffe904ea33d6bb3ff33e +F test/permutations.test 64abe58d53cde4adcfb881e37aa5ea1b2a980e58 F test/pragma.test 507ac7ef2ea5682241ea0ef041799ca70bb5e0bf F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c @@ -964,8 +969,9 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736 F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8 F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 +F test/regexp2.test aa7ffcc21350007a78361b82bcf3b74d12227144 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 -F test/releasetest.tcl 975449bf742b8bb9025208292208af816a1fcb58 +F test/releasetest.tcl a246ecb14ed594bf44bf77bd21df873971d779bf F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5 @@ -1019,10 +1025,10 @@ 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 ce5e744870387164703bf2dee2cc9753e4a71513 +F test/shell1.test a45b424ec23a4b946848ff9e1af5186c677162ab F test/shell2.test 12b8bf901b0e3a8ac58cf5c0c63a0a388d4d1862 F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29 -F test/shell4.test ddf0a99044e2245a87fc17423e3aaa1445b3243b +F test/shell4.test f43e250139dc5dc5f0f2ec1752c50284a1ede102 F test/shell5.test c04e9f9f948305706b88377c464c7f08ce7479f9 F test/shell6.test 4ce53ce53e1206742e003b5ae609b6c2be438df1 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 @@ -1034,7 +1040,7 @@ F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5 F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2 F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b -F test/snapshot.test 5ec4651d16c3d1eb6c010d102febe32b3614bf56 +F test/snapshot.test c03eb5ba1602df33c1edc1d12393d6ca7a282a46 F test/snapshot_fault.test 25973aeb1b86a280800e0bcf1eb5ce70e9ef57ab F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087 @@ -1052,7 +1058,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/speedtest1.c f8bf04214e7b5f745feea99f7bde68b1c4870666 +F test/speedtest1.c 1478cb3fb64ad30f291ddca87ca9dbd72ff552aa F test/spellfix.test f9c1f431e2c096c8775fec032952320c0e4700db F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test f7bf7b3482971473d32b6b00f6944c5c066cff97 @@ -1061,7 +1067,7 @@ F test/sqllimits1.test a74ee2a3740b9f9c2437c246d8fb77354862a142 F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a F test/stat.test acc91e80517fff447ae8adcfd953cfdaa5efc0af F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1 -F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 +F test/stmt.test 64844332db69cf1a735fcb3e11548557fc95392f F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f F test/subquery2.test 438f8a7da1457277b22e4176510f7659b286995f F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 @@ -1072,15 +1078,15 @@ F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849 F test/sync.test 2f607e1821aa3af3c5c53b58835c05e511c95899 F test/syscall.test f59ba4e25f7ba4a4c031026cc2ef8b6e4b4c639c F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04 -F test/tabfunc01.test cc33684f9480fcf1fd5ce287ac28d22971cad1cc +F test/tabfunc01.test f977868fa8bb7beb4b2072883190411653473906 F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 F test/tclsqlite.test 7fb866443c7deceed22b63948ccd6f76b52ad054 -F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c +F test/tempdb.test bd92eba8f20e16a9136e434e20b280794de3cdb6 F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1 -F test/tester.tcl 462376b478c1429030911b4cb7c8c517ef1fbd9b +F test/tester.tcl 750365ff97047ded5f2d6e28df82a998f7c66ae0 F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -1265,7 +1271,7 @@ F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 -F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825 +F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8 F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32 F test/uri.test 6630ecbdea2aac10df3c89dbae2243f4c2c353e4 @@ -1283,7 +1289,7 @@ F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e F test/vtab4.test 8e73ed268f3d596bc3590f45fc948fb40f28e9c3 F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391 -F test/vtab6.test d2986cf418dc51e7fb81d12366bea2caa8b812df +F test/vtab6.test 8e789f526e6594cf7ae933d1adee0caa87dc9f78 F test/vtab7.test ae560ebea870ed04e9aa4177cc302f910faaabb5 F test/vtab8.test e19fa4a538fcd1bb66c22825fa8f71618fb13583 F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b @@ -1299,7 +1305,7 @@ F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad F test/wal.test 0148c8b3421a25fdb4d9c160e84a681d0646371b -F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada +F test/wal2.test 25ae059e900dbb584e0775627e45415ba5940df1 F test/wal3.test 5dd734147f1f8f958c5261a1f2775d346d7013ce F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c F test/wal5.test 88b5d9a6a3d1532497ee9f4296f010d66f07e33c @@ -1327,7 +1333,7 @@ F test/walro.test 34422d1d95aaff0388f0791ec20edb34e2a3ed57 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e -F test/where.test 9902a3d84e9bc80357a2c54ed0e76c0d6d04a867 +F test/where.test f0c325563acde44f2c4ea6ba348e9e29f7121757 F test/where2.test af78c55589cbc82d793449493adba0dc3d659f23 F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8 @@ -1365,11 +1371,12 @@ F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e F test/wordcount.c 2a0a6c0d0e8e8bbbac1f06d72a6791828c37c0cf F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa -F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac +F test/zerodamage.test 2d725c214b883e25ae6bb85ef228ecdfa03c6a7b F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5 F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372 -F tool/addopcodes.tcl 4ca9c3ef196f08da30add5d07ce0c9458dc8c633 -F tool/build-all-msvc.bat 31866578036cd1d962628059b0760d407c3ce4d8 x +F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91 +F tool/addopcodes.tcl 2b089684eb8b7d0db64cf9d8e6d2fe1b6d279e8d +F tool/build-all-msvc.bat 55be1cf8545dabd69df2ba6b3de6868da0c26f52 x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 @@ -1380,23 +1387,23 @@ F tool/fuzzershell.c 94019b185caceffc9f7c7b678a6489e42bc2aefa F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4 F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce -F tool/lemon.c 799e73e19a33b8dd7767a7fa34618ed2a9c2397d -F tool/lempar.c 3ec1463a034b37d87d782be5f6b8b10a3b1ecbe7 +F tool/lemon.c 251f5c3f21b553240cbdd42dd187a51bb2372cd3 +F tool/lempar.c d5114c7d13aa3af1e27ff3d02e4dea6eadec7ddf F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862 F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6 -F tool/mkautoconfamal.sh a29b14d54302b33fd892958f6895582ea90e4a45 +F tool/mkautoconfamal.sh e855df211ecbcc7131dee817110ff386cfb112f7 F tool/mkkeywordhash.c f7f3b342211ac6a14258b9726d5b97cf4f548f22 -F tool/mkmsvcmin.tcl d57e6efc9428605f5418d0b235721ddf7b5d9c0b +F tool/mkmsvcmin.tcl 2f12f7fa8858bbe61cf81820a2da96c79ed1ca8d F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c F tool/mkopcodeh.tcl 385c62d78c38b2d92146dcb5abd319dbbc33506d F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkpragmatab.tcl f0d5bb266d1d388cf86fce5ba01a891e95d72d41 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 87240b09c20042999b41d5fabe091b7111287835 -F tool/mksqlite3c.tcl b66b4170f693602cd6985aed15d9509fe2f18c84 +F tool/mksqlite3c.tcl 9382100c82a0a08e63265780536a6c8efead4b65 F tool/mksqlite3h.tcl 1d41ab59bffb025121f75b76e183125ce41b3ec8 F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b -F tool/mkvsix.tcl bbe57cd9ae11c6cc70319241101ef8d2b8c3765b +F tool/mkvsix.tcl 4abcaf3267171b2faadaf9b82a0dfbaa6e98f8b7 F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091 F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97 F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b @@ -1419,7 +1426,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/sqldiff.c 5a26205111e6fa856d9b1535b1637744dcdb930b -F tool/srcck1.c 4c39bdfa9a92edd20233ee720df84dbeb2417602 +F tool/srcck1.c 4f65e1a6748e42f24c0ea629dddc934d821c729a F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f @@ -1428,9 +1435,30 @@ F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003 F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 -F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b +F tool/warnings.sh a98af506df552f3b3c0d904f94e4cdc4e1a6d598 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 0b73406595c9a077399b0f4c17af3a826cf3612f -R d77c52a71510d468f62d262501df9c9f +F vsixtest/App.xaml b76d3b48860e7454775c47ea38ffea9c4abe3e85 +F vsixtest/App.xaml.cpp c465147f50871165c60ca16955219f6c5812d6d8 +F vsixtest/App.xaml.h 4a9768e2983d05600ad1e1c2f1b00a132967da9f +F vsixtest/Assets/LockScreenLogo.scale-200.png e820c9a3deb909197081b0bf3216c06e13905f0a +F vsixtest/Assets/SplashScreen.scale-200.png cab70988ca71bebec7bfeb3b6dbafe17b9ab0b4a +F vsixtest/Assets/Square150x150Logo.scale-200.png e17b40817db7a239fc239d83efcc951fb824e3ff +F vsixtest/Assets/Square44x44Logo.scale-200.png 2f166237094dea94d952d10b9eeae81806844f1c +F vsixtest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png 5f6a6d391b95a3061ccca6e6fdd6955ede63b4ed +F vsixtest/Assets/StoreLogo.png 0828b7257db74a4ecd5eeb6b7b4971f0fdc4d9d1 +F vsixtest/Assets/Wide310x150Logo.scale-200.png 04ddefe5bc5f43ae12a7433f6f236ddab101ac42 +F vsixtest/MainPage.xaml 34f49897e3ca533a7e74506ba0759b66eebce151 +F vsixtest/MainPage.xaml.cpp 7f31fc6de751b64676c0924c97a5485d950a91d7 +F vsixtest/MainPage.xaml.h cc05cca10d50a003f6c6e4448b701cdd07f52f29 +F vsixtest/Package.appxmanifest 6b6db1eb7df3a315c5d681059754d5f0e0c47a93 +F vsixtest/pch.cpp cb823cfac36f1a39a7eb0acbd7e9a0b0de8f23af +F vsixtest/pch.h 9cab7980f2ac4baa40807d8b5e52af32a21cf78c +F vsixtest/vsixtest.sln 77cadbe4e96c1fe1bf51cd77de9e9b0a12ada547 +F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 +F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc +F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e +F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 +P fc18cc9293fb0080b7152c16baac49f44e2db7b3 8baa2c2c7698e03418531482a8314a5d7ae2c7d3 +R 450ba648de3c5f9783d9a3140277593a U dan -Z c51c58b805618dc47dc45d8f0cd33733 +Z 1186a7b0c3808314b7a93e4b95a66f1c diff --git a/manifest.uuid b/manifest.uuid index e9344a7af6..3e67f3151e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fc18cc9293fb0080b7152c16baac49f44e2db7b3 \ No newline at end of file +59caca4387e97231834a3bffdbed8636be4e8e19 \ No newline at end of file diff --git a/mptest/mptest.c b/mptest/mptest.c index b29ebdc9e1..5022b009e6 100644 --- a/mptest/mptest.c +++ b/mptest/mptest.c @@ -41,6 +41,7 @@ #else # include #endif +#include #include #include #include @@ -1244,6 +1245,19 @@ static void usage(const char *argv0){ if( isDirSep(argv0[i]) ) zTail = argv0+i+1; } fprintf(stderr,"Usage: %s DATABASE ?OPTIONS? ?SCRIPT?\n", zTail); + fprintf(stderr, + "Options:\n" + " --errlog FILENAME Write errors to FILENAME\n" + " --journalmode MODE Use MODE as the journal_mode\n" + " --log FILENAME Log messages to FILENAME\n" + " --quiet Suppress unnecessary output\n" + " --vfs NAME Use NAME as the VFS\n" + " --repeat N Repeat the test N times\n" + " --sqltrace Enable SQL tracing\n" + " --sync Enable synchronous disk writes\n" + " --timeout MILLISEC Busy timeout is MILLISEC\n" + " --trace BOOLEAN Enable or disable tracing\n" + ); exit(1); } @@ -1275,6 +1289,8 @@ int SQLITE_CDECL main(int argc, char **argv){ const char *zJMode; const char *zNRep; int nRep = 1, iRep; + int iTmout = 0; /* Default: no timeout */ + const char *zTmout; g.argv0 = argv[0]; g.iTrace = 1; @@ -1301,6 +1317,8 @@ int SQLITE_CDECL main(int argc, char **argv){ zTrace = findOption(argv+2, &n, "trace", 1); if( zTrace ) g.iTrace = atoi(zTrace); if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0; + zTmout = findOption(argv+2, &n, "timeout", 1); + if( zTmout ) iTmout = atoi(zTmout); g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0; g.bSync = findOption(argv+2, &n, "sync", 0)!=0; if( g.zErrLog ){ @@ -1321,6 +1339,7 @@ int SQLITE_CDECL main(int argc, char **argv){ sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d", GETPID(), iClient); }else{ + int nTry = 0; if( g.iTrace>0 ){ printf("BEGIN: %s", argv[0]); for(i=1; i5 ? "still " : "", g.zDbFile); + rc = unlink(g.zDbFile); + if( rc && errno==ENOENT ) rc = 0; + }while( rc!=0 && (++nTry)<60 && sqlite3_sleep(1000)>0 ); + if( rc!=0 ){ + fatalError("unable to unlink '%s' after %d attempts\n", + g.zDbFile, nTry); + } openFlags |= SQLITE_OPEN_CREATE; } rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs); if( rc ) fatalError("cannot open [%s]", g.zDbFile); + if( iTmout>0 ) sqlite3_busy_timeout(g.db, iTmout); + if( zJMode ){ #if defined(_WIN32) if( sqlite3_stricmp(zJMode,"persist")==0 diff --git a/src/alter.c b/src/alter.c index 34ca8abd2c..642c1fb677 100644 --- a/src/alter.c +++ b/src/alter.c @@ -229,7 +229,7 @@ static void renameTriggerFunc( ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ - static SQLITE_WSD FuncDef aAlterTableFuncs[] = { + static FuncDef aAlterTableFuncs[] = { FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), #ifndef SQLITE_OMIT_TRIGGER FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), @@ -238,13 +238,7 @@ void sqlite3AlterFunctions(void){ FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), #endif }; - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs); - - for(i=0; iflags = savedDbFlags; } - -/* -** Generate code to make sure the file format number is at least minFormat. -** The generated code will increase the file format number if necessary. -*/ -void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){ - Vdbe *v; - v = sqlite3GetVdbe(pParse); - /* The VDBE should have been allocated before this routine is called. - ** If that allocation failed, we would have quit before reaching this - ** point */ - if( ALWAYS(v) ){ - int r1 = sqlite3GetTempReg(pParse); - int r2 = sqlite3GetTempReg(pParse); - int addr1; - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); - sqlite3VdbeUsesBtree(v, iDb); - sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2); - addr1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1); - sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, minFormat); - sqlite3VdbeJumpHere(v, addr1); - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ReleaseTempReg(pParse, r2); - } -} - /* ** This function is called after an "ALTER TABLE ... ADD" statement ** has been parsed. Argument pColDef contains the text of the new @@ -633,9 +600,11 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ Column *pCol; /* The new column */ Expr *pDflt; /* Default value for the new column */ sqlite3 *db; /* The database connection; */ + Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ db = pParse->db; if( pParse->nErr || db->mallocFailed ) return; + assert( v!=0 ); pNew = pParse->pNewTable; assert( pNew ); @@ -659,7 +628,8 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ ** literal NULL, then set pDflt to 0. This simplifies checking ** for an SQL NULL default below. */ - if( pDflt && pDflt->op==TK_NULL ){ + assert( pDflt==0 || pDflt->op==TK_SPAN ); + if( pDflt && pDflt->pLeft->op==TK_NULL ){ pDflt = 0; } @@ -725,11 +695,16 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ db->flags = savedDbFlags; } - /* If the default value of the new column is NULL, then set the file + /* If the default value of the new column is NULL, then the file ** format to 2. If the default value of the new column is not NULL, - ** the file format becomes 3. + ** the file format be 3. Back when this feature was first added + ** in 2006, we went to the trouble to upgrade the file format to the + ** minimum support values. But 10-years on, we can assume that all + ** extent versions of SQLite support file-format 4, so we always and + ** unconditionally upgrade to 4. */ - sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2); + sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, + SQLITE_MAX_FILE_FORMAT); /* Reload the schema of the modified table. */ reloadTableSchema(pParse, pTab, pTab->zName); @@ -811,9 +786,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ Column *pCol = &pNew->aCol[i]; pCol->zName = sqlite3DbStrDup(db, pCol->zName); pCol->zColl = 0; - pCol->zType = 0; pCol->pDflt = 0; - pCol->zDflt = 0; } pNew->pSchema = db->aDb[iDb].pSchema; pNew->addColOffset = pTab->addColOffset; diff --git a/src/analyze.c b/src/analyze.c index 1e026a7530..cd59ae681d 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -481,8 +481,7 @@ static const FuncDef statInitFuncdef = { statInit, /* xSFunc */ 0, /* xFinalize */ "stat_init", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ + {0} }; #ifdef SQLITE_ENABLE_STAT4 @@ -781,8 +780,7 @@ static const FuncDef statPushFuncdef = { statPush, /* xSFunc */ 0, /* xFinalize */ "stat_push", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ + {0} }; #define STAT_GET_STAT1 0 /* "stat" column of stat1 table */ @@ -927,8 +925,7 @@ static const FuncDef statGetFuncdef = { statGet, /* xSFunc */ 0, /* xFinalize */ "stat_get", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ + {0} }; static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){ @@ -1673,7 +1670,7 @@ static int loadStatTbl( assert( db->lookaside.bDisable ); zSql = sqlite3MPrintf(db, zSql1, zDb); if( !zSql ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); sqlite3DbFree(db, zSql); @@ -1713,7 +1710,7 @@ static int loadStatTbl( pIdx->aSample = sqlite3DbMallocZero(db, nByte); if( pIdx->aSample==0 ){ sqlite3_finalize(pStmt); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pSpace = (tRowcnt*)&pIdx->aSample[nSample]; pIdx->aAvgEq = pSpace; pSpace += nIdxCol; @@ -1729,7 +1726,7 @@ static int loadStatTbl( zSql = sqlite3MPrintf(db, zSql2, zDb); if( !zSql ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); sqlite3DbFree(db, zSql); @@ -1767,7 +1764,7 @@ static int loadStatTbl( pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); if( pSample->p==0 ){ sqlite3_finalize(pStmt); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n); pIdx->nSample++; @@ -1856,7 +1853,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ zSql = sqlite3MPrintf(db, "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); diff --git a/src/attach.c b/src/attach.c index 2288ac9b62..ea378a40a2 100644 --- a/src/attach.c +++ b/src/attach.c @@ -144,7 +144,7 @@ static void attachFunc( Pager *pPager; aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); if( !aNew->pSchema ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ zErrDyn = sqlite3MPrintf(db, "attached databases must use the same text encoding as main database"); @@ -161,10 +161,10 @@ static void attachFunc( #endif sqlite3BtreeLeave(aNew->pBt); } - aNew->safety_level = 3; + aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; aNew->zName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && aNew->zName==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } @@ -392,8 +392,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){ detachFunc, /* xSFunc */ 0, /* xFinalize */ "sqlite_detach", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ + {0} }; codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname); } @@ -412,8 +411,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ attachFunc, /* xSFunc */ 0, /* xFinalize */ "sqlite_attach", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ + {0} }; codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey); } diff --git a/src/backup.c b/src/backup.c index 1c282242d7..455671a1ad 100644 --- a/src/backup.c +++ b/src/backup.c @@ -88,7 +88,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse)); if( pParse==0 ){ sqlite3ErrorWithMsg(pErrorDb, SQLITE_NOMEM, "out of memory"); - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ pParse->db = pDb; if( sqlite3OpenTempDatabase(pParse) ){ @@ -182,7 +182,7 @@ sqlite3_backup *sqlite3_backup_init( ** sqlite3_backup_finish(). */ p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup)); if( !p ){ - sqlite3Error(pDestDb, SQLITE_NOMEM); + sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT); } } @@ -581,7 +581,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ } if( rc==SQLITE_IOERR_NOMEM ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } p->rc = rc; } diff --git a/src/bitvec.c b/src/bitvec.c index f7f544cff7..9d13ba9185 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -177,7 +177,7 @@ int sqlite3BitvecSet(Bitvec *p, u32 i){ i = i%p->iDivisor; if( p->u.apSub[bin]==0 ){ p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor ); - if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM; + if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT; } p = p->u.apSub[bin]; } @@ -212,7 +212,7 @@ bitvec_set_rehash: int rc; u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash)); if( aiValues==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; }else{ memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); memset(p->u.apSub, 0, sizeof(p->u.apSub)); diff --git a/src/btree.c b/src/btree.c index c6f9c34f7b..18e6be9f38 100644 --- a/src/btree.c +++ b/src/btree.c @@ -350,7 +350,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ if( !pLock ){ pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock)); if( !pLock ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pLock->iTable = iTable; pLock->pBtree = p; @@ -553,7 +553,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ assert( pgno<=pBt->nPage ); pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage); if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } } if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ @@ -632,7 +632,7 @@ static int saveCursorKey(BtCursor *pCur){ sqlite3_free(pKey); } }else{ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } } assert( !pCur->curIntKey || !pCur->pKey ); @@ -764,7 +764,7 @@ static int btreeMoveto( pIdxKey = sqlite3VdbeAllocUnpackedRecord( pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree ); - if( pIdxKey==0 ) return SQLITE_NOMEM; + if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); if( pIdxKey->nField==0 ){ sqlite3DbFree(pCur->pKeyInfo->db, pFree); @@ -2176,7 +2176,7 @@ int sqlite3BtreeOpen( } p = sqlite3MallocZero(sizeof(Btree)); if( !p ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } p->inTrans = TRANS_NONE; p->db = db; @@ -2200,7 +2200,7 @@ int sqlite3BtreeOpen( p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } if( isMemdb ){ memcpy(zFullPathname, zFilename, nFilename); @@ -2268,7 +2268,7 @@ int sqlite3BtreeOpen( pBt = sqlite3MallocZero( sizeof(*pBt) ); if( pBt==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto btree_open_out; } rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, @@ -2337,7 +2337,7 @@ int sqlite3BtreeOpen( if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){ pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST); if( pBt->mutex==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto btree_open_out; } } @@ -2619,21 +2619,6 @@ int sqlite3BtreeSetPagerFlags( } #endif -/* -** Return TRUE if the given btree is set to safety level 1. In other -** words, return TRUE if no sync() occurs on the disk files. -*/ -int sqlite3BtreeSyncDisabled(Btree *p){ - BtShared *pBt = p->pBt; - int rc; - assert( sqlite3_mutex_held(p->db->mutex) ); - sqlite3BtreeEnter(p); - assert( pBt && pBt->pPager ); - rc = sqlite3PagerNosync(pBt->pPager); - sqlite3BtreeLeave(p); - return rc; -} - /* ** Change the default pages size and the number of reserved bytes per page. ** Or, if the page size has already been fixed, return SQLITE_READONLY @@ -2879,9 +2864,25 @@ static int lockBtree(BtShared *pBt){ rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; - }else if( isOpen==0 ){ - releasePage(pPage1); - return SQLITE_OK; + }else{ +#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS + sqlite3 *db; + Db *pDb; + if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ + while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } + if( pDb->bSyncSet==0 + && pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1 + ){ + pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1; + sqlite3PagerSetFlags(pBt->pPager, + pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); + } + } +#endif + if( isOpen==0 ){ + releasePage(pPage1); + return SQLITE_OK; + } } rc = SQLITE_NOTADB; } @@ -4114,7 +4115,7 @@ static int btreeCursor( if( wrFlag ){ allocateTempSpace(pBt); - if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM; + if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT; } if( iTable==1 && btreePagecount(pBt)==0 ){ assert( wrFlag==0 ); @@ -4512,7 +4513,7 @@ static int accessPayload( pCur->aOverflow, nOvfl*2*sizeof(Pgno) ); if( aNew==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ pCur->nOvflAlloc = nOvfl*2; pCur->aOverflow = aNew; @@ -5217,7 +5218,7 @@ int sqlite3BtreeMovetoUnpacked( } pCellKey = sqlite3Malloc( nCell+18 ); if( pCellKey==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto moveto_finish; } pCur->aiIdx[pCur->iPage] = (u16)idx; @@ -7036,7 +7037,7 @@ static int balance_nonroot( assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx ); if( !aOvflSpace ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } /* Find the sibling pages to balance. Also locate the cells in pParent @@ -7136,7 +7137,7 @@ static int balance_nonroot( assert( szScratch<=6*(int)pBt->pageSize ); b.apCell = sqlite3ScratchMalloc( szScratch ); if( b.apCell==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto balance_cleanup; } b.szCell = (u16*)&b.apCell[nMaxCells]; @@ -7571,9 +7572,9 @@ static int balance_nonroot( ** any cell). But it is important to pass the correct size to ** insertCell(), so reparse the cell now. ** - ** Note that this can never happen in an SQLite data file, as all - ** cells are at least 4 bytes. It only happens in b-trees used - ** to evaluate "IN (SELECT ...)" and similar clauses. + ** This can only happen for b-trees used to evaluate "IN (SELECT ...)" + ** and WITHOUT ROWID tables with exactly one column which is the + ** primary key. */ if( b.szCell[j]==4 ){ assert(leafCorrection==4); diff --git a/src/btree.h b/src/btree.h index 30522e99e1..9ba233ac33 100644 --- a/src/btree.h +++ b/src/btree.h @@ -68,7 +68,6 @@ int sqlite3BtreeSetSpillSize(Btree*,int); int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); #endif int sqlite3BtreeSetPagerFlags(Btree*,unsigned); -int sqlite3BtreeSyncDisabled(Btree*); int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); int sqlite3BtreeGetPageSize(Btree*); int sqlite3BtreeMaxPageCount(Btree*,int); diff --git a/src/build.c b/src/build.c index 250dc20d20..e89c744461 100644 --- a/src/build.c +++ b/src/build.c @@ -571,8 +571,6 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ for(i=0; inCol; i++, pCol++){ sqlite3DbFree(db, pCol->zName); sqlite3ExprDelete(db, pCol->pDflt); - sqlite3DbFree(db, pCol->zDflt); - sqlite3DbFree(db, pCol->zType); sqlite3DbFree(db, pCol->zColl); } sqlite3DbFree(db, pTable->aCol); @@ -710,12 +708,8 @@ int sqlite3FindDbName(sqlite3 *db, const char *zName){ int i = -1; /* Database number */ if( zName ){ Db *pDb; - int n = sqlite3Strlen30(zName); for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){ - if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) && - 0==sqlite3StrICmp(pDb->zName, zName) ){ - break; - } + if( 0==sqlite3StrICmp(pDb->zName, zName) ) break; } } return i; @@ -927,7 +921,7 @@ void sqlite3StartTable( pTable = sqlite3DbMallocZero(db, sizeof(Table)); if( pTable==0 ){ assert( db->mallocFailed ); - pParse->rc = SQLITE_NOMEM; + pParse->rc = SQLITE_NOMEM_BKPT; pParse->nErr++; goto begin_table_error; } @@ -1043,10 +1037,11 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ ** first to get things going. Then this routine is called for each ** column. */ -void sqlite3AddColumn(Parse *pParse, Token *pName){ +void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ Table *p; int i; char *z; + char *zType; Column *pCol; sqlite3 *db = pParse->db; if( (p = pParse->pNewTable)==0 ) return; @@ -1056,8 +1051,14 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){ return; } #endif - z = sqlite3NameFromToken(db, pName); + z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2); if( z==0 ) return; + memcpy(z, pName->z, pName->n); + z[pName->n] = 0; + sqlite3Dequote(z); + zType = z + sqlite3Strlen30(z) + 1; + memcpy(zType, pType->z, pType->n); + zType[pType->n] = 0; for(i=0; inCol; i++){ if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){ sqlite3ErrorMsg(pParse, "duplicate column name: %s", z); @@ -1079,13 +1080,16 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){ pCol->zName = z; sqlite3ColumnPropertiesFromName(p, pCol); - /* If there is no type specified, columns have the default affinity - ** 'BLOB'. If there is a type specified, then sqlite3AddColumnType() will - ** be called next to set pCol->affinity correctly. - */ - pCol->affinity = SQLITE_AFF_BLOB; - pCol->szEst = 1; + if( pType->n==0 ){ + /* If there is no type specified, columns have the default affinity + ** 'BLOB'. */ + pCol->affinity = SQLITE_AFF_BLOB; + pCol->szEst = 1; + }else{ + pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst); + } p->nCol++; + pParse->constraintName.n = 0; } /* @@ -1131,7 +1135,7 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){ char aff = SQLITE_AFF_NUMERIC; const char *zChar = 0; - if( zIn==0 ) return aff; + assert( zIn!=0 ); while( zIn[0] ){ h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff]; zIn++; @@ -1188,28 +1192,6 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){ return aff; } -/* -** This routine is called by the parser while in the middle of -** parsing a CREATE TABLE statement. The pFirst token is the first -** token in the sequence of tokens that describe the type of the -** column currently under construction. pLast is the last token -** in the sequence. Use this information to construct a string -** that contains the typename of the column and store that string -** in zType. -*/ -void sqlite3AddColumnType(Parse *pParse, Token *pType){ - Table *p; - Column *pCol; - - p = pParse->pNewTable; - if( p==0 || NEVER(p->nCol<1) ) return; - pCol = &p->aCol[p->nCol-1]; - assert( pCol->zType==0 || CORRUPT_DB ); - sqlite3DbFree(pParse->db, pCol->zType); - pCol->zType = sqlite3NameFromToken(pParse->db, pType); - pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst); -} - /* ** The expression is the default value for the most recently added column ** of the table currently under construction. @@ -1235,11 +1217,16 @@ void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){ ** tokens that point to volatile memory. The 'span' of the expression ** is required by pragma table_info. */ + Expr x; sqlite3ExprDelete(db, pCol->pDflt); - pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE); - sqlite3DbFree(db, pCol->zDflt); - pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart, - (int)(pSpan->zEnd - pSpan->zStart)); + memset(&x, 0, sizeof(x)); + x.op = TK_SPAN; + x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart, + (int)(pSpan->zEnd - pSpan->zStart)); + x.pLeft = pSpan->pExpr; + x.flags = EP_Skip; + pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE); + sqlite3DbFree(db, x.u.zToken); } } sqlite3ExprDelete(db, pSpan->pExpr); @@ -1295,7 +1282,7 @@ void sqlite3AddPrimaryKey( int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */ ){ Table *pTab = pParse->pNewTable; - char *zType = 0; + const char *zName = 0; int iCol = -1, i; int nTerm; if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit; @@ -1308,7 +1295,7 @@ void sqlite3AddPrimaryKey( if( pList==0 ){ iCol = pTab->nCol - 1; pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; - zType = pTab->aCol[iCol].zType; + zName = pTab->aCol[iCol].zName; nTerm = 1; }else{ nTerm = pList->nExpr; @@ -1321,7 +1308,7 @@ void sqlite3AddPrimaryKey( for(iCol=0; iColnCol; iCol++){ if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){ pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY; - zType = pTab->aCol[iCol].zType; + zName = pTab->aCol[iCol].zName; break; } } @@ -1329,7 +1316,8 @@ void sqlite3AddPrimaryKey( } } if( nTerm==1 - && zType && sqlite3StrICmp(zType, "INTEGER")==0 + && zName + && sqlite3StrICmp(sqlite3StrNext(zName), "INTEGER")==0 && sortOrder!=SQLITE_SO_DESC ){ pTab->iPKey = iCol; @@ -1608,7 +1596,7 @@ static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){ assert( pIdx->isResized==0 ); nByte = (sizeof(char*) + sizeof(i16) + 1)*N; zExtra = sqlite3DbMallocZero(db, nByte); - if( zExtra==0 ) return SQLITE_NOMEM; + if( zExtra==0 ) return SQLITE_NOMEM_BKPT; memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); pIdx->azColl = (const char**)zExtra; zExtra += sizeof(char*)*N; @@ -3200,6 +3188,20 @@ Index *sqlite3CreateIndex( sqlite3DefaultRowEst(pIndex); if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex); + /* If this index contains every column of its table, then mark + ** it as a covering index */ + assert( HasRowid(pTab) + || pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 ); + if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){ + pIndex->isCovering = 1; + for(j=0; jnCol; j++){ + if( j==pTab->iPKey ) continue; + if( sqlite3ColumnOfIndex(pIndex,j)>=0 ) continue; + pIndex->isCovering = 0; + break; + } + } + if( pTab==pParse->pNewTable ){ /* This routine has been called to create an automatic index as a ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or @@ -3237,7 +3239,7 @@ Index *sqlite3CreateIndex( if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break; z1 = pIdx->azColl[k]; z2 = pIndex->azColl[k]; - if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break; + if( sqlite3StrICmp(z1, z2) ) break; } if( k==pIdx->nKeyCol ){ if( pIdx->onError!=pIndex->onError ){ diff --git a/src/callback.c b/src/callback.c index 2b955fdcdb..2351178867 100644 --- a/src/callback.c +++ b/src/callback.c @@ -284,14 +284,12 @@ static int matchQuality( ** a pointer to the matching FuncDef if found, or 0 if there is no match. */ static FuncDef *functionSearch( - FuncDefHash *pHash, /* Hash table to search */ int h, /* Hash of the name */ - const char *zFunc, /* Name of function */ - int nFunc /* Number of bytes in zFunc */ + const char *zFunc /* Name of function */ ){ FuncDef *p; - for(p=pHash->a[h]; p; p=p->pHash){ - if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){ + for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ + if( sqlite3StrICmp(p->zName, zFunc)==0 ){ return p; } } @@ -301,23 +299,26 @@ static FuncDef *functionSearch( /* ** Insert a new FuncDef into a FuncDefHash hash table. */ -void sqlite3FuncDefInsert( - FuncDefHash *pHash, /* The hash table into which to insert */ - FuncDef *pDef /* The function definition to insert */ +void sqlite3InsertBuiltinFuncs( + FuncDef *aDef, /* List of global functions to be inserted */ + int nDef /* Length of the apDef[] list */ ){ - FuncDef *pOther; - int nName = sqlite3Strlen30(pDef->zName); - u8 c1 = (u8)pDef->zName[0]; - int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a); - pOther = functionSearch(pHash, h, pDef->zName, nName); - if( pOther ){ - assert( pOther!=pDef && pOther->pNext!=pDef ); - pDef->pNext = pOther->pNext; - pOther->pNext = pDef; - }else{ - pDef->pNext = 0; - pDef->pHash = pHash->a[h]; - pHash->a[h] = pDef; + int i; + for(i=0; ipNext!=&aDef[i] ); + aDef[i].pNext = pOther->pNext; + pOther->pNext = &aDef[i]; + }else{ + aDef[i].pNext = 0; + aDef[i].u.pHash = sqlite3BuiltinFunctions.a[h]; + sqlite3BuiltinFunctions.a[h] = &aDef[i]; + } } } @@ -344,8 +345,7 @@ void sqlite3FuncDefInsert( */ FuncDef *sqlite3FindFunction( sqlite3 *db, /* An open database */ - const char *zName, /* Name of the function. Not null-terminated */ - int nName, /* Number of characters in the name */ + const char *zName, /* Name of the function. zero-terminated */ int nArg, /* Number of arguments. -1 means any number */ u8 enc, /* Preferred text encoding */ u8 createFlag /* Create new entry if true and does not otherwise exist */ @@ -354,14 +354,15 @@ FuncDef *sqlite3FindFunction( FuncDef *pBest = 0; /* Best match found so far */ int bestScore = 0; /* Score of best match */ int h; /* Hash value */ + int nName; /* Length of the name */ assert( nArg>=(-2) ); assert( nArg>=(-1) || createFlag==0 ); - h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a); + nName = sqlite3Strlen30(zName); /* First search for a match amongst the application-defined functions. */ - p = functionSearch(&db->aFunc, h, zName, nName); + p = (FuncDef*)sqlite3HashFind(&db->aFunc, zName); while( p ){ int score = matchQuality(p, nArg, enc); if( score>bestScore ){ @@ -384,9 +385,9 @@ FuncDef *sqlite3FindFunction( ** So we must not search for built-ins when creating a new function. */ if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){ - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); bestScore = 0; - p = functionSearch(pHash, h, zName, nName); + h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ; + p = functionSearch(h, zName); while( p ){ int score = matchQuality(p, nArg, enc); if( score>bestScore ){ @@ -403,12 +404,19 @@ FuncDef *sqlite3FindFunction( */ if( createFlag && bestScorezName = (char *)&pBest[1]; + FuncDef *pOther; + pBest->zName = (const char*)&pBest[1]; pBest->nArg = (u16)nArg; pBest->funcFlags = enc; - memcpy(pBest->zName, zName, nName); - pBest->zName[nName] = 0; - sqlite3FuncDefInsert(&db->aFunc, pBest); + memcpy((char*)&pBest[1], zName, nName+1); + pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest); + if( pOther==pBest ){ + sqlite3DbFree(db, pBest); + sqlite3OomFault(db); + return 0; + }else{ + pBest->pNext = pOther; + } } if( pBest && (pBest->xSFunc || createFlag) ){ diff --git a/src/complete.c b/src/complete.c index b120b7e811..bb2c03098e 100644 --- a/src/complete.c +++ b/src/complete.c @@ -281,7 +281,7 @@ int sqlite3_complete16(const void *zSql){ if( zSql8 ){ rc = sqlite3_complete(zSql8); }else{ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } sqlite3ValueFree(pVal); return rc & 0xff; diff --git a/src/date.c b/src/date.c index 0a1d0e0798..ef05d44086 100644 --- a/src/date.c +++ b/src/date.c @@ -1136,7 +1136,7 @@ static void currentTimeFunc( ** external linkage. */ void sqlite3RegisterDateTimeFunctions(void){ - static SQLITE_WSD FuncDef aDateTimeFuncs[] = { + static FuncDef aDateTimeFuncs[] = { #ifndef SQLITE_OMIT_DATETIME_FUNCS DFUNCTION(julianday, -1, 0, 0, juliandayFunc ), DFUNCTION(date, -1, 0, 0, dateFunc ), @@ -1152,11 +1152,5 @@ void sqlite3RegisterDateTimeFunctions(void){ STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc), #endif }; - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aDateTimeFuncs); - - for(i=0; ibase.pVtab = pVTab; @@ -349,7 +349,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt); sqlite3BtreeLeave(pBt); p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell)); - if( p->aCell==0 ) return SQLITE_NOMEM; + if( p->aCell==0 ) return SQLITE_NOMEM_BKPT; memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell)); for(i=0; inCell; i++){ @@ -382,7 +382,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); pCell->nOvfl = nOvfl; pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl); - if( pCell->aOvfl==0 ) return SQLITE_NOMEM; + if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT; pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]); for(j=1; jaPage[0].iCell = 0; pCsr->aPage[0].zPath = z = sqlite3_mprintf("/"); pCsr->iPage = 0; - if( z==0 ) rc = SQLITE_NOMEM; + if( z==0 ) rc = SQLITE_NOMEM_BKPT; }else{ pCsr->isEof = 1; return sqlite3_reset(pCsr->pStmt); @@ -496,7 +496,7 @@ statNextRestart: } pCell->iOvfl++; statSizeAndOffset(pCsr); - return z==0 ? SQLITE_NOMEM : SQLITE_OK; + return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK; } if( p->iRightChildPg ) break; p->iCell++; @@ -520,7 +520,7 @@ statNextRestart: p[1].iCell = 0; p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell); p->iCell++; - if( z==0 ) rc = SQLITE_NOMEM; + if( z==0 ) rc = SQLITE_NOMEM_BKPT; } @@ -554,7 +554,7 @@ statNextRestart: pCsr->nUnused = p->nUnused; pCsr->nMxPayload = p->nMxPayload; pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath); - if( z==0 ) rc = SQLITE_NOMEM; + if( z==0 ) rc = SQLITE_NOMEM_BKPT; nPayload = 0; for(i=0; inCell; i++){ nPayload += p->aCell[i].nLocal; @@ -588,7 +588,7 @@ static int statFilter( if( pCsr->iDb<0 ){ sqlite3_free(pCursor->pVtab->zErrMsg); pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase); - return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM_BKPT; } }else{ pCsr->iDb = pTab->iDb; @@ -604,7 +604,7 @@ static int statFilter( " FROM \"%w\".%s WHERE rootpage!=0" " ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster); if( zSql==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; }else{ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); sqlite3_free(zSql); diff --git a/src/expr.c b/src/expr.c index 8d96ba10cd..8a69732195 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2888,7 +2888,6 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ ExprList *pFarg; /* List of function arguments */ int nFarg; /* Number of function arguments */ FuncDef *pDef; /* The function definition object */ - int nId; /* Length of the function name in bytes */ const char *zId; /* The function name */ u32 constMask = 0; /* Mask of function arguments that are constant */ int i; /* Loop counter */ @@ -2904,10 +2903,9 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ nFarg = pFarg ? pFarg->nExpr : 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); zId = pExpr->u.zToken; - nId = sqlite3Strlen30(zId); - pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0); + pDef = sqlite3FindFunction(db, zId, nFarg, enc, 0); if( pDef==0 || pDef->xFinalize!=0 ){ - sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId); + sqlite3ErrorMsg(pParse, "unknown function: %s()", zId); break; } @@ -3072,6 +3070,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ sqlite3ReleaseTempReg(pParse, r4); break; } + case TK_SPAN: case TK_COLLATE: case TK_UPLUS: { inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); @@ -4132,7 +4131,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ pItem->iMem = ++pParse->nMem; assert( !ExprHasProperty(pExpr, EP_IntValue) ); pItem->pFunc = sqlite3FindFunction(pParse->db, - pExpr->u.zToken, sqlite3Strlen30(pExpr->u.zToken), + pExpr->u.zToken, pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); if( pExpr->flags & EP_Distinct ){ pItem->iDistinct = pParse->nTab++; diff --git a/src/fkey.c b/src/fkey.c index 38fd4f756b..9a1a85fec0 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -1162,6 +1162,10 @@ static Trigger *fkActionTrigger( int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ action = pFKey->aAction[iAction]; + if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){ + return 0; + } + pTrigger = pFKey->apTrigger[iAction]; if( action!=OE_None && !pTrigger ){ diff --git a/src/func.c b/src/func.c index d615cf90b1..662a08f504 100644 --- a/src/func.c +++ b/src/func.c @@ -1611,7 +1611,7 @@ static void groupConcatFinalize(sqlite3_context *context){ ** of the built-in functions above are part of the global function set. ** This routine only deals with those that are not global. */ -void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ +void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){ int rc = sqlite3_overload_function(db, "MATCH", 2); assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); if( rc==SQLITE_NOMEM ){ @@ -1624,8 +1624,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ */ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){ FuncDef *pDef; - pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName), - 2, SQLITE_UTF8, 0); + pDef = sqlite3FindFunction(db, zName, 2, SQLITE_UTF8, 0); if( ALWAYS(pDef) ){ pDef->funcFlags |= flagVal; } @@ -1673,9 +1672,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ return 0; } assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - pDef = sqlite3FindFunction(db, pExpr->u.zToken, - sqlite3Strlen30(pExpr->u.zToken), - 2, SQLITE_UTF8, 0); + pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0); if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){ return 0; } @@ -1699,7 +1696,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ ** ** After this routine runs */ -void sqlite3RegisterGlobalFunctions(void){ +void sqlite3RegisterBuiltinFunctions(void){ /* ** The following array holds FuncDef structures for all of the functions ** defined in this file. @@ -1707,8 +1704,27 @@ void sqlite3RegisterGlobalFunctions(void){ ** The array cannot be constant since changes are made to the ** FuncDef.pHash elements at start-time. The elements of this array ** are read-only after initialization is complete. + ** + ** For peak efficiency, put the most frequently used function last. */ - static SQLITE_WSD FuncDef aBuiltinFunc[] = { + static FuncDef aBuiltinFunc[] = { +#ifdef SQLITE_SOUNDEX + FUNCTION(soundex, 1, 0, 0, soundexFunc ), +#endif +#ifndef SQLITE_OMIT_LOAD_EXTENSION + VFUNCTION(load_extension, 1, 0, 0, loadExt ), + VFUNCTION(load_extension, 2, 0, 0, loadExt ), +#endif +#if SQLITE_USER_AUTHENTICATION + FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ), +#endif +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS + DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), + DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), +#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ + FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), + FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), + FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), FUNCTION(ltrim, 1, 1, 0, trimFunc ), FUNCTION(ltrim, 2, 1, 0, trimFunc ), FUNCTION(rtrim, 1, 2, 0, trimFunc ), @@ -1726,8 +1742,6 @@ void sqlite3RegisterGlobalFunctions(void){ FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH), FUNCTION(instr, 2, 0, 0, instrFunc ), - FUNCTION(substr, 2, 0, 0, substrFunc ), - FUNCTION(substr, 3, 0, 0, substrFunc ), FUNCTION(printf, -1, 0, 0, printfFunc ), FUNCTION(unicode, 1, 0, 0, unicodeFunc ), FUNCTION(char, -1, 0, 0, charFunc ), @@ -1738,40 +1752,22 @@ void sqlite3RegisterGlobalFunctions(void){ #endif FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), - FUNCTION(coalesce, 1, 0, 0, 0 ), - FUNCTION(coalesce, 0, 0, 0, 0 ), - FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), FUNCTION(hex, 1, 0, 0, hexFunc ), FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), - FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), - FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), -#if SQLITE_USER_AUTHENTICATION - FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ), -#endif -#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS - DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), - DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), -#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ FUNCTION(quote, 1, 0, 0, quoteFunc ), VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), VFUNCTION(changes, 0, 0, 0, changes ), VFUNCTION(total_changes, 0, 0, 0, total_changes ), FUNCTION(replace, 3, 0, 0, replaceFunc ), FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), - #ifdef SQLITE_SOUNDEX - FUNCTION(soundex, 1, 0, 0, soundexFunc ), - #endif - #ifndef SQLITE_OMIT_LOAD_EXTENSION - VFUNCTION(load_extension, 1, 0, 0, loadExt ), - VFUNCTION(load_extension, 2, 0, 0, loadExt ), - #endif + FUNCTION(substr, 2, 0, 0, substrFunc ), + FUNCTION(substr, 3, 0, 0, substrFunc ), AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), @@ -1789,20 +1785,32 @@ void sqlite3RegisterGlobalFunctions(void){ LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE), LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE), #endif + FUNCTION(coalesce, 1, 0, 0, 0 ), + FUNCTION(coalesce, 0, 0, 0, 0 ), + FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE), }; - - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc); - - for(i=0; iu.pHash){ + int n = sqlite3Strlen30(p->zName); + int h = p->zName[0] + n; + printf(" %s(%d)", p->zName, h); + } + printf("\n"); + } + } #endif } diff --git a/src/global.c b/src/global.c index 64966b35d7..bd70b38273 100644 --- a/src/global.c +++ b/src/global.c @@ -159,6 +159,18 @@ const unsigned char sqlite3CtypeMap[256] = { # define SQLITE_SORTER_PMASZ 250 #endif +/* Statement journals spill to disk when their size exceeds the following +** threashold (in bytes). 0 means that statement journals are created and +** written to disk immediately (the default behavior for SQLite versions +** before 3.12.0). -1 means always keep the entire statement journal in +** memory. (The statement journal is also always held entirely in memory +** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this +** setting.) +*/ +#ifndef SQLITE_STMTJRNL_SPILL +# define SQLITE_STMTJRNL_SPILL (64*1024) +#endif + /* ** The following singleton contains the global configuration for ** the SQLite library. @@ -173,6 +185,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* neverCorrupt */ 128, /* szLookaside */ 500, /* nLookaside */ + SQLITE_STMTJRNL_SPILL, /* nStmtSpill */ {0,0,0,0,0,0,0,0}, /* m */ {0,0,0,0,0,0,0,0,0}, /* mutex */ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */ @@ -219,7 +232,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = { ** database connections. After initialization, this table is ** read-only. */ -SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; +FuncDefHash sqlite3BuiltinFunctions; /* ** Constant tokens for values 0 and 1. diff --git a/src/insert.c b/src/insert.c index 650f397de0..03fdf09acc 100644 --- a/src/insert.c +++ b/src/insert.c @@ -995,7 +995,7 @@ void sqlite3Insert( { int isReplace; /* Set to true if constraints may cause a replace */ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, - regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace + regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0 ); sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, @@ -1077,6 +1077,59 @@ insert_cleanup: #undef tmask #endif +/* +** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged() +*/ +#define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */ +#define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */ + +/* This is the Walker callback from checkConstraintUnchanged(). Set +** bit 0x01 of pWalker->eCode if +** pWalker->eCode to 0 if this expression node references any of the +** columns that are being modifed by an UPDATE statement. +*/ +static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN ){ + assert( pExpr->iColumn>=0 || pExpr->iColumn==-1 ); + if( pExpr->iColumn>=0 ){ + if( pWalker->u.aiCol[pExpr->iColumn]>=0 ){ + pWalker->eCode |= CKCNSTRNT_COLUMN; + } + }else{ + pWalker->eCode |= CKCNSTRNT_ROWID; + } + } + return WRC_Continue; +} + +/* +** pExpr is a CHECK constraint on a row that is being UPDATE-ed. The +** only columns that are modified by the UPDATE are those for which +** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true. +** +** Return true if CHECK constraint pExpr does not use any of the +** changing columns (or the rowid if it is changing). In other words, +** return true if this CHECK constraint can be skipped when validating +** the new row in the UPDATE statement. +*/ +static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ + Walker w; + memset(&w, 0, sizeof(w)); + w.eCode = 0; + w.xExprCallback = checkConstraintExprNode; + w.u.aiCol = aiChng; + sqlite3WalkExpr(&w, pExpr); + if( !chngRowid ){ + testcase( (w.eCode & CKCNSTRNT_ROWID)!=0 ); + w.eCode &= ~CKCNSTRNT_ROWID; + } + testcase( w.eCode==0 ); + testcase( w.eCode==CKCNSTRNT_COLUMN ); + testcase( w.eCode==CKCNSTRNT_ROWID ); + testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) ); + return !w.eCode; +} + /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE ** on table pTab. @@ -1171,7 +1224,8 @@ void sqlite3GenerateConstraintChecks( u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */ u8 overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ - int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ + int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */ + int *aiChng /* column i is unchanged if aiChng[i]<0 */ ){ Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ @@ -1217,10 +1271,14 @@ void sqlite3GenerateConstraintChecks( */ for(i=0; iiPKey ){ + continue; /* ROWID is never NULL */ + } + if( aiChng && aiChng[i]<0 ){ + /* Don't bother checking for NOT NULL on columns that do not change */ continue; } onError = pTab->aCol[i].notNull; - if( onError==OE_None ) continue; + if( onError==OE_None ) continue; /* This column is allowed to be NULL */ if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ @@ -1269,8 +1327,11 @@ void sqlite3GenerateConstraintChecks( pParse->ckBase = regNewData+1; onError = overrideError!=OE_Default ? overrideError : OE_Abort; for(i=0; inExpr; i++){ - int allOk = sqlite3VdbeMakeLabel(v); - sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL); + int allOk; + Expr *pExpr = pCheck->a[i].pExpr; + if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue; + allOk = sqlite3VdbeMakeLabel(v); + sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); if( onError==OE_Ignore ){ sqlite3VdbeGoto(v, ignoreDest); }else{ @@ -1938,11 +1999,15 @@ static int xferOptimization( return 0; /* tab2 must be NOT NULL if tab1 is */ } /* Default values for second and subsequent columns need to match. */ - if( i>0 - && ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0) - || (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0)) - ){ - return 0; /* Default values must be the same for all columns */ + if( i>0 ){ + assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN ); + assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN ); + if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0) + || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken, + pSrcCol->pDflt->u.zToken)!=0) + ){ + return 0; /* Default values must be the same for all columns */ + } } } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ diff --git a/src/journal.c b/src/journal.c deleted file mode 100644 index fed27be3e3..0000000000 --- a/src/journal.c +++ /dev/null @@ -1,256 +0,0 @@ -/* -** 2007 August 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file implements a special kind of sqlite3_file object used -** by SQLite to create journal files if the atomic-write optimization -** is enabled. -** -** The distinctive characteristic of this sqlite3_file is that the -** actual on disk file is created lazily. When the file is created, -** the caller specifies a buffer size for an in-memory buffer to -** be used to service read() and write() requests. The actual file -** on disk is not created or populated until either: -** -** 1) The in-memory representation grows too large for the allocated -** buffer, or -** 2) The sqlite3JournalCreate() function is called. -*/ -#ifdef SQLITE_ENABLE_ATOMIC_WRITE -#include "sqliteInt.h" - - -/* -** A JournalFile object is a subclass of sqlite3_file used by -** as an open file handle for journal files. -*/ -struct JournalFile { - sqlite3_io_methods *pMethod; /* I/O methods on journal files */ - int nBuf; /* Size of zBuf[] in bytes */ - char *zBuf; /* Space to buffer journal writes */ - int iSize; /* Amount of zBuf[] currently used */ - int flags; /* xOpen flags */ - sqlite3_vfs *pVfs; /* The "real" underlying VFS */ - sqlite3_file *pReal; /* The "real" underlying file descriptor */ - const char *zJournal; /* Name of the journal file */ -}; -typedef struct JournalFile JournalFile; - -/* -** If it does not already exists, create and populate the on-disk file -** for JournalFile p. -*/ -static int createFile(JournalFile *p){ - int rc = SQLITE_OK; - if( !p->pReal ){ - sqlite3_file *pReal = (sqlite3_file *)&p[1]; - rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0); - if( rc==SQLITE_OK ){ - p->pReal = pReal; - if( p->iSize>0 ){ - assert(p->iSize<=p->nBuf); - rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0); - } - if( rc!=SQLITE_OK ){ - /* If an error occurred while writing to the file, close it before - ** returning. This way, SQLite uses the in-memory journal data to - ** roll back changes made to the internal page-cache before this - ** function was called. */ - sqlite3OsClose(pReal); - p->pReal = 0; - } - } - } - return rc; -} - -/* -** Close the file. -*/ -static int jrnlClose(sqlite3_file *pJfd){ - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - sqlite3OsClose(p->pReal); - } - sqlite3_free(p->zBuf); - return SQLITE_OK; -} - -/* -** Read data from the file. -*/ -static int jrnlRead( - sqlite3_file *pJfd, /* The journal file from which to read */ - void *zBuf, /* Put the results here */ - int iAmt, /* Number of bytes to read */ - sqlite_int64 iOfst /* Begin reading at this offset */ -){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); - }else if( (iAmt+iOfst)>p->iSize ){ - rc = SQLITE_IOERR_SHORT_READ; - }else{ - memcpy(zBuf, &p->zBuf[iOfst], iAmt); - } - return rc; -} - -/* -** Write data to the file. -*/ -static int jrnlWrite( - sqlite3_file *pJfd, /* The journal file into which to write */ - const void *zBuf, /* Take data to be written from here */ - int iAmt, /* Number of bytes to write */ - sqlite_int64 iOfst /* Begin writing at this offset into the file */ -){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( !p->pReal && (iOfst+iAmt)>p->nBuf ){ - rc = createFile(p); - } - if( rc==SQLITE_OK ){ - if( p->pReal ){ - rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); - }else{ - memcpy(&p->zBuf[iOfst], zBuf, iAmt); - if( p->iSize<(iOfst+iAmt) ){ - p->iSize = (iOfst+iAmt); - } - } - } - return rc; -} - -/* -** Truncate the file. -*/ -static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsTruncate(p->pReal, size); - }else if( sizeiSize ){ - p->iSize = size; - } - return rc; -} - -/* -** Sync the file. -*/ -static int jrnlSync(sqlite3_file *pJfd, int flags){ - int rc; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsSync(p->pReal, flags); - }else{ - rc = SQLITE_OK; - } - return rc; -} - -/* -** Query the size of the file in bytes. -*/ -static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){ - int rc = SQLITE_OK; - JournalFile *p = (JournalFile *)pJfd; - if( p->pReal ){ - rc = sqlite3OsFileSize(p->pReal, pSize); - }else{ - *pSize = (sqlite_int64) p->iSize; - } - return rc; -} - -/* -** Table of methods for JournalFile sqlite3_file object. -*/ -static struct sqlite3_io_methods JournalFileMethods = { - 1, /* iVersion */ - jrnlClose, /* xClose */ - jrnlRead, /* xRead */ - jrnlWrite, /* xWrite */ - jrnlTruncate, /* xTruncate */ - jrnlSync, /* xSync */ - jrnlFileSize, /* xFileSize */ - 0, /* xLock */ - 0, /* xUnlock */ - 0, /* xCheckReservedLock */ - 0, /* xFileControl */ - 0, /* xSectorSize */ - 0, /* xDeviceCharacteristics */ - 0, /* xShmMap */ - 0, /* xShmLock */ - 0, /* xShmBarrier */ - 0 /* xShmUnmap */ -}; - -/* -** Open a journal file. -*/ -int sqlite3JournalOpen( - sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ - const char *zName, /* Name of the journal file */ - sqlite3_file *pJfd, /* Preallocated, blank file handle */ - int flags, /* Opening flags */ - int nBuf /* Bytes buffered before opening the file */ -){ - JournalFile *p = (JournalFile *)pJfd; - memset(p, 0, sqlite3JournalSize(pVfs)); - if( nBuf>0 ){ - p->zBuf = sqlite3MallocZero(nBuf); - if( !p->zBuf ){ - return SQLITE_NOMEM; - } - }else{ - return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); - } - p->pMethod = &JournalFileMethods; - p->nBuf = nBuf; - p->flags = flags; - p->zJournal = zName; - p->pVfs = pVfs; - return SQLITE_OK; -} - -/* -** If the argument p points to a JournalFile structure, and the underlying -** file has not yet been created, create it now. -*/ -int sqlite3JournalCreate(sqlite3_file *p){ - if( p->pMethods!=&JournalFileMethods ){ - return SQLITE_OK; - } - return createFile((JournalFile *)p); -} - -/* -** The file-handle passed as the only argument is guaranteed to be an open -** file. It may or may not be of class JournalFile. If the file is a -** JournalFile, and the underlying file on disk has not yet been opened, -** return 0. Otherwise, return 1. -*/ -int sqlite3JournalExists(sqlite3_file *p){ - return (p->pMethods!=&JournalFileMethods || ((JournalFile *)p)->pReal!=0); -} - -/* -** Return the number of bytes required to store a JournalFile that uses vfs -** pVfs to create the underlying on-disk files. -*/ -int sqlite3JournalSize(sqlite3_vfs *pVfs){ - return (pVfs->szOsFile+sizeof(JournalFile)); -} -#endif diff --git a/src/legacy.c b/src/legacy.c index 1b5e518d43..bd34512d31 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -131,7 +131,7 @@ exec_out: if( *pzErrMsg ){ memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg); }else{ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; sqlite3Error(db, SQLITE_NOMEM); } }else if( pzErrMsg ){ diff --git a/src/loadext.c b/src/loadext.c index 94298c4763..3469fbb73d 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -478,7 +478,7 @@ static int sqlite3LoadExtension( #if SQLITE_OS_UNIX || SQLITE_OS_WIN for(ii=0; ii=0 && zFile[iFile]!='/'; iFile--){} @@ -557,7 +557,7 @@ static int sqlite3LoadExtension( /* Append the new shared library handle to the db->aExtension array. */ aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1)); if( aHandle==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } if( db->nExtension>0 ){ memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension); @@ -679,7 +679,7 @@ int sqlite3_auto_extension(void (*xInit)(void)){ void (**aNew)(void); aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte); if( aNew==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ wsdAutoext.aExt = aNew; wsdAutoext.aExt[wsdAutoext.nExt] = xInit; diff --git a/src/main.c b/src/main.c index 72f12d2ac2..456219afa1 100644 --- a/src/main.c +++ b/src/main.c @@ -187,7 +187,7 @@ int sqlite3_initialize(void){ sqlite3GlobalConfig.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } } } @@ -218,7 +218,6 @@ int sqlite3_initialize(void){ */ sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex); if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); sqlite3GlobalConfig.inProgress = 1; #ifdef SQLITE_ENABLE_SQLLOG { @@ -226,8 +225,8 @@ int sqlite3_initialize(void){ sqlite3_init_sqllog(); } #endif - memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); - sqlite3RegisterGlobalFunctions(); + memset(&sqlite3BuiltinFunctions, 0, sizeof(sqlite3BuiltinFunctions)); + sqlite3RegisterBuiltinFunctions(); if( sqlite3GlobalConfig.isPCacheInit==0 ){ rc = sqlite3PcacheInitialize(); } @@ -634,6 +633,11 @@ int sqlite3_config(int op, ...){ break; } + case SQLITE_CONFIG_STMTJRNL_SPILL: { + sqlite3GlobalConfig.nStmtSpill = va_arg(ap, int); + break; + } + default: { rc = SQLITE_ERROR; break; @@ -804,8 +808,9 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){ int op; /* The opcode */ u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ } aFlagOp[] = { - { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, - { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, + { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, + { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, + { SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -965,7 +970,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){ ** with SQLITE_ANY as the encoding. */ static void functionDestroy(sqlite3 *db, FuncDef *p){ - FuncDestructor *pDestructor = p->pDestructor; + FuncDestructor *pDestructor = p->u.pDestructor; if( pDestructor ){ pDestructor->nRef--; if( pDestructor->nRef==0 ){ @@ -1147,18 +1152,17 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ */ sqlite3ConnectionClosed(db); - for(j=0; jaFunc.a); j++){ - FuncDef *pNext, *pHash, *p; - for(p=db->aFunc.a[j]; p; p=pHash){ - pHash = p->pHash; - while( p ){ - functionDestroy(db, p); - pNext = p->pNext; - sqlite3DbFree(db, p); - p = pNext; - } - } + for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){ + FuncDef *pNext, *p; + p = sqliteHashData(i); + do{ + functionDestroy(db, p); + pNext = p->pNext; + sqlite3DbFree(db, p); + p = pNext; + }while( p ); } + sqlite3HashClear(&db->aFunc); for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ CollSeq *pColl = (CollSeq *)sqliteHashData(i); /* Invoke any destructors registered for collation sequence user data. */ @@ -1637,7 +1641,7 @@ int sqlite3CreateFunc( ** is being overridden/deleted but there are no active VMs, allow the ** operation to continue but invalidate all precompiled statements. */ - p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0); + p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0); if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){ if( db->nVdbeActive ){ sqlite3ErrorWithMsg(db, SQLITE_BUSY, @@ -1649,10 +1653,10 @@ int sqlite3CreateFunc( } } - p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1); + p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 1); assert(p || db->mallocFailed); if( !p ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } /* If an older version of the function with a configured destructor is @@ -1662,7 +1666,7 @@ int sqlite3CreateFunc( if( pDestructor ){ pDestructor->nRef++; } - p->pDestructor = pDestructor; + p->u.pDestructor = pDestructor; p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags; testcase( p->funcFlags & SQLITE_DETERMINISTIC ); p->xSFunc = xSFunc ? xSFunc : xStep; @@ -1777,7 +1781,6 @@ int sqlite3_overload_function( const char *zName, int nArg ){ - int nName = sqlite3Strlen30(zName); int rc = SQLITE_OK; #ifdef SQLITE_ENABLE_API_ARMOR @@ -1786,7 +1789,7 @@ int sqlite3_overload_function( } #endif sqlite3_mutex_enter(db->mutex); - if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ + if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){ rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, 0, sqlite3InvalidFunction, 0, 0, 0); } @@ -2156,14 +2159,14 @@ int sqlite3TempInMemory(const sqlite3 *db){ const char *sqlite3_errmsg(sqlite3 *db){ const char *z; if( !db ){ - return sqlite3ErrStr(SQLITE_NOMEM); + return sqlite3ErrStr(SQLITE_NOMEM_BKPT); } if( !sqlite3SafetyCheckSickOrOk(db) ){ return sqlite3ErrStr(SQLITE_MISUSE_BKPT); } sqlite3_mutex_enter(db->mutex); if( db->mallocFailed ){ - z = sqlite3ErrStr(SQLITE_NOMEM); + z = sqlite3ErrStr(SQLITE_NOMEM_BKPT); }else{ testcase( db->pErr==0 ); z = (char*)sqlite3_value_text(db->pErr); @@ -2231,7 +2234,7 @@ int sqlite3_errcode(sqlite3 *db){ return SQLITE_MISUSE_BKPT; } if( !db || db->mallocFailed ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } return db->errCode & db->errMask; } @@ -2240,7 +2243,7 @@ int sqlite3_extended_errcode(sqlite3 *db){ return SQLITE_MISUSE_BKPT; } if( !db || db->mallocFailed ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } return db->errCode; } @@ -2320,7 +2323,7 @@ static int createCollation( } pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1); - if( pColl==0 ) return SQLITE_NOMEM; + if( pColl==0 ) return SQLITE_NOMEM_BKPT; pColl->xCmp = xCompare; pColl->pUser = pCtx; pColl->xDel = xDel; @@ -2368,8 +2371,8 @@ static const int aHardLimit[] = { #if SQLITE_MAX_VDBE_OP<40 # error SQLITE_MAX_VDBE_OP must be at least 40 #endif -#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000 -# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000 +#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127 +# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127 #endif #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 # error SQLITE_MAX_ATTACHED must be between 0 and 125 @@ -2499,7 +2502,7 @@ int sqlite3ParseUri( for(iIn=0; iInaCollSeq); @@ -2864,7 +2870,7 @@ static int openDatabase( flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } sqlite3Error(db, rc); goto opendb_out; @@ -2875,13 +2881,13 @@ static int openDatabase( 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 - ** database it is 'NONE'. This matches the pager layer defaults. + /* The default safety_level for the main database is FULL; for the temp + ** database it is OFF. This matches the pager layer defaults. */ db->aDb[0].zName = "main"; - db->aDb[0].safety_level = 3; + db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; db->aDb[1].zName = "temp"; - db->aDb[1].safety_level = 1; + db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; db->magic = SQLITE_MAGIC_OPEN; if( db->mallocFailed ){ @@ -2893,7 +2899,7 @@ static int openDatabase( ** is accessed. */ sqlite3Error(db, SQLITE_OK); - sqlite3RegisterBuiltinFunctions(db); + sqlite3RegisterPerConnectionBuiltinFunctions(db); /* Load automatic extensions - extensions that have been registered ** using the sqlite3_automatic_extension() API. @@ -3067,7 +3073,7 @@ int sqlite3_open16( SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE; } }else{ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } sqlite3ValueFree(pVal); @@ -3212,7 +3218,7 @@ int sqlite3_get_autocommit(sqlite3 *db){ /* ** The following routines are substitutes for constants SQLITE_CORRUPT, -** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_IOERR and possibly other error +** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_NOMEM and possibly other error ** constants. They serve two purposes: ** ** 1. Serve as a convenient place to set a breakpoint in a debugger @@ -3221,28 +3227,33 @@ int sqlite3_get_autocommit(sqlite3 *db){ ** 2. Invoke sqlite3_log() to provide the source code location where ** a low-level error is first detected. */ +static int reportError(int iErr, int lineno, const char *zType){ + sqlite3_log(iErr, "%s at line %d of [%.10s]", + zType, lineno, 20+sqlite3_sourceid()); + return iErr; +} int sqlite3CorruptError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_CORRUPT, - "database corruption at line %d of [%.10s]", - lineno, 20+sqlite3_sourceid()); - return SQLITE_CORRUPT; + return reportError(SQLITE_CORRUPT, lineno, "database corruption"); } int sqlite3MisuseError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_MISUSE, - "misuse at line %d of [%.10s]", - lineno, 20+sqlite3_sourceid()); - return SQLITE_MISUSE; + return reportError(SQLITE_MISUSE, lineno, "misuse"); } int sqlite3CantopenError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); - sqlite3_log(SQLITE_CANTOPEN, - "cannot open file at line %d of [%.10s]", - lineno, 20+sqlite3_sourceid()); - return SQLITE_CANTOPEN; + return reportError(SQLITE_CANTOPEN, lineno, "cannot open file"); } - +#ifdef SQLITE_DEBUG +int sqlite3NomemError(int lineno){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + return reportError(SQLITE_NOMEM, lineno, "OOM"); +} +int sqlite3IoerrnomemError(int lineno){ + testcase( sqlite3GlobalConfig.xLog!=0 ); + return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error"); +} +#endif #ifndef SQLITE_OMIT_DEPRECATED /* @@ -3336,7 +3347,8 @@ int sqlite3_table_column_metadata( ** explicitly declared column. Copy meta information from *pCol. */ if( pCol ){ - zDataType = pCol->zType; + zDataType = sqlite3StrNext(pCol->zName); + if( zDataType[0]==0 ) zDataType = 0; zCollSeq = pCol->zColl; notnull = pCol->notNull!=0; primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0; diff --git a/src/malloc.c b/src/malloc.c index ebe0440350..462d78e68c 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -795,7 +795,7 @@ void sqlite3OomClear(sqlite3 *db){ static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ sqlite3OomClear(db); sqlite3Error(db, SQLITE_NOMEM); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } /* diff --git a/src/memjournal.c b/src/memjournal.c index 6452cecc37..b81682de85 100644 --- a/src/memjournal.c +++ b/src/memjournal.c @@ -21,24 +21,28 @@ typedef struct MemJournal MemJournal; typedef struct FilePoint FilePoint; typedef struct FileChunk FileChunk; -/* Space to hold the rollback journal is allocated in increments of -** this many bytes. -** -** The size chosen is a little less than a power of two. That way, -** the FileChunk object will have a size that almost exactly fills -** a power-of-two allocation. This minimizes wasted space in power-of-two -** memory allocators. -*/ -#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) - /* ** The rollback journal is composed of a linked list of these structures. +** +** The zChunk array is always at least 8 bytes in size - usually much more. +** Its actual size is stored in the MemJournal.nChunkSize variable. */ struct FileChunk { FileChunk *pNext; /* Next chunk in the journal */ - u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */ + u8 zChunk[8]; /* Content of this chunk */ }; +/* +** By default, allocate this many bytes of memory for each FileChunk object. +*/ +#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024 + +/* +** For chunk size nChunkSize, return the number of bytes that should +** be allocated for each FileChunk structure. +*/ +#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8)) + /* ** An instance of this object serves as a cursor into the rollback journal. ** The cursor can be either for reading or writing. @@ -49,14 +53,22 @@ struct FilePoint { }; /* -** This subclass is a subclass of sqlite3_file. Each open memory-journal +** This structure is a subclass of sqlite3_file. Each open memory-journal ** is an instance of this class. */ struct MemJournal { - sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */ + const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */ + int nChunkSize; /* In-memory chunk-size */ + + int nSpill; /* Bytes of data before flushing */ + int nSize; /* Bytes of data currently in memory */ FileChunk *pFirst; /* Head of in-memory chunk-list */ FilePoint endpoint; /* Pointer to the end of the file */ FilePoint readpoint; /* Pointer to the end of the last xRead() */ + + int flags; /* xOpen flags */ + sqlite3_vfs *pVfs; /* The "real" underlying VFS */ + const char *zJournal; /* Name of the journal file */ }; /* @@ -75,26 +87,30 @@ static int memjrnlRead( int iChunkOffset; FileChunk *pChunk; - /* SQLite never tries to read past the end of a rollback journal file */ - assert( iOfst+iAmt<=p->endpoint.iOffset ); +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + if( (iAmt+iOfst)>p->endpoint.iOffset ){ + return SQLITE_IOERR_SHORT_READ; + } +#endif + assert( (iAmt+iOfst)<=p->endpoint.iOffset ); if( p->readpoint.iOffset!=iOfst || iOfst==0 ){ sqlite3_int64 iOff = 0; for(pChunk=p->pFirst; - ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst; + ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst; pChunk=pChunk->pNext ){ - iOff += JOURNAL_CHUNKSIZE; + iOff += p->nChunkSize; } }else{ pChunk = p->readpoint.pChunk; } - iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE); + iChunkOffset = (int)(iOfst%p->nChunkSize); do { - int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset; - int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset)); - memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy); + int iSpace = p->nChunkSize - iChunkOffset; + int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset)); + memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy); zOut += nCopy; nRead -= iSpace; iChunkOffset = 0; @@ -105,6 +121,58 @@ static int memjrnlRead( return SQLITE_OK; } +/* +** Free the list of FileChunk structures headed at MemJournal.pFirst. +*/ +static void memjrnlFreeChunks(MemJournal *p){ + FileChunk *pIter; + FileChunk *pNext; + for(pIter=p->pFirst; pIter; pIter=pNext){ + pNext = pIter->pNext; + sqlite3_free(pIter); + } + p->pFirst = 0; +} + +/* +** Flush the contents of memory to a real file on disk. +*/ +static int memjrnlCreateFile(MemJournal *p){ + int rc; + sqlite3_file *pReal = (sqlite3_file*)p; + MemJournal copy = *p; + + memset(p, 0, sizeof(MemJournal)); + rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0); + if( rc==SQLITE_OK ){ + int nChunk = copy.nChunkSize; + i64 iOff = 0; + FileChunk *pIter; + for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){ + if( iOff + nChunk > copy.endpoint.iOffset ){ + nChunk = copy.endpoint.iOffset - iOff; + } + rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff); + if( rc ) break; + iOff += nChunk; + } + if( rc==SQLITE_OK ){ + /* No error has occurred. Free the in-memory buffers. */ + memjrnlFreeChunks(©); + } + } + if( rc!=SQLITE_OK ){ + /* If an error occurred while creating or writing to the file, restore + ** the original before returning. This way, SQLite uses the in-memory + ** journal data to roll back changes made to the internal page-cache + ** before this function was called. */ + sqlite3OsClose(pReal); + *p = copy; + } + return rc; +} + + /* ** Write data to the file. */ @@ -118,38 +186,62 @@ static int memjrnlWrite( int nWrite = iAmt; u8 *zWrite = (u8 *)zBuf; - /* An in-memory journal file should only ever be appended to. Random - ** access writes are not required by sqlite. - */ - assert( iOfst==p->endpoint.iOffset ); - UNUSED_PARAMETER(iOfst); - - while( nWrite>0 ){ - FileChunk *pChunk = p->endpoint.pChunk; - int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE); - int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset); - - if( iChunkOffset==0 ){ - /* New chunk is required to extend the file. */ - FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk)); - if( !pNew ){ - return SQLITE_IOERR_NOMEM; - } - pNew->pNext = 0; - if( pChunk ){ - assert( p->pFirst ); - pChunk->pNext = pNew; - }else{ - assert( !p->pFirst ); - p->pFirst = pNew; - } - p->endpoint.pChunk = pNew; + /* If the file should be created now, create it and write the new data + ** into the file on disk. */ + if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){ + int rc = memjrnlCreateFile(p); + if( rc==SQLITE_OK ){ + rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst); } + return rc; + } - memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace); - zWrite += iSpace; - nWrite -= iSpace; - p->endpoint.iOffset += iSpace; + /* If the contents of this write should be stored in memory */ + else{ + /* An in-memory journal file should only ever be appended to. Random + ** access writes are not required. The only exception to this is when + ** the in-memory journal is being used by a connection using the + ** atomic-write optimization. In this case the first 28 bytes of the + ** journal file may be written as part of committing the transaction. */ + assert( iOfst==p->endpoint.iOffset || iOfst==0 ); +#ifdef SQLITE_ENABLE_ATOMIC_WRITE + if( iOfst==0 && p->pFirst ){ + assert( p->nChunkSize>iAmt ); + memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt); + }else +#else + assert( iOfst>0 || p->pFirst==0 ); +#endif + { + while( nWrite>0 ){ + FileChunk *pChunk = p->endpoint.pChunk; + int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize); + int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset); + + if( iChunkOffset==0 ){ + /* New chunk is required to extend the file. */ + FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize)); + if( !pNew ){ + return SQLITE_IOERR_NOMEM_BKPT; + } + pNew->pNext = 0; + if( pChunk ){ + assert( p->pFirst ); + pChunk->pNext = pNew; + }else{ + assert( !p->pFirst ); + p->pFirst = pNew; + } + p->endpoint.pChunk = pNew; + } + + memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace); + zWrite += iSpace; + nWrite -= iSpace; + p->endpoint.iOffset += iSpace; + } + p->nSize = iAmt + iOfst; + } } return SQLITE_OK; @@ -157,19 +249,21 @@ static int memjrnlWrite( /* ** Truncate the file. +** +** If the journal file is already on disk, truncate it there. Or, if it +** is still in main memory but is being truncated to zero bytes in size, +** ignore */ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ MemJournal *p = (MemJournal *)pJfd; - FileChunk *pChunk; - assert(size==0); - UNUSED_PARAMETER(size); - pChunk = p->pFirst; - while( pChunk ){ - FileChunk *pTmp = pChunk; - pChunk = pChunk->pNext; - sqlite3_free(pTmp); + if( ALWAYS(size==0) ){ + memjrnlFreeChunks(p); + p->nSize = 0; + p->endpoint.pChunk = 0; + p->endpoint.iOffset = 0; + p->readpoint.pChunk = 0; + p->readpoint.iOffset = 0; } - sqlite3MemJournalOpen(pJfd); return SQLITE_OK; } @@ -177,21 +271,19 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){ ** Close the file. */ static int memjrnlClose(sqlite3_file *pJfd){ - memjrnlTruncate(pJfd, 0); + MemJournal *p = (MemJournal *)pJfd; + memjrnlFreeChunks(p); return SQLITE_OK; } - /* ** Sync the file. ** -** Syncing an in-memory journal is a no-op. And, in fact, this routine -** is never called in a working implementation. This implementation -** exists purely as a contingency, in case some malfunction in some other -** part of SQLite causes Sync to be called by mistake. +** If the real file has been created, call its xSync method. Otherwise, +** syncing an in-memory journal is a no-op. */ -static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){ - UNUSED_PARAMETER2(NotUsed, NotUsed2); +static int memjrnlSync(sqlite3_file *pJfd, int flags){ + UNUSED_PARAMETER2(pJfd, flags); return SQLITE_OK; } @@ -230,26 +322,86 @@ static const struct sqlite3_io_methods MemJournalMethods = { }; /* -** Open a journal file. +** Open a journal file. +** +** The behaviour of the journal file depends on the value of parameter +** nSpill. If nSpill is 0, then the journal file is always create and +** accessed using the underlying VFS. If nSpill is less than zero, then +** all content is always stored in main-memory. Finally, if nSpill is a +** positive value, then the journal file is initially created in-memory +** but may be flushed to disk later on. In this case the journal file is +** flushed to disk either when it grows larger than nSpill bytes in size, +** or when sqlite3JournalCreate() is called. */ -void sqlite3MemJournalOpen(sqlite3_file *pJfd){ - MemJournal *p = (MemJournal *)pJfd; - assert( EIGHT_BYTE_ALIGNMENT(p) ); - memset(p, 0, sqlite3MemJournalSize()); - p->pMethod = (sqlite3_io_methods*)&MemJournalMethods; +int sqlite3JournalOpen( + sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */ + const char *zName, /* Name of the journal file */ + sqlite3_file *pJfd, /* Preallocated, blank file handle */ + int flags, /* Opening flags */ + int nSpill /* Bytes buffered before opening the file */ +){ + MemJournal *p = (MemJournal*)pJfd; + + /* Zero the file-handle object. If nSpill was passed zero, initialize + ** it using the sqlite3OsOpen() function of the underlying VFS. In this + ** case none of the code in this module is executed as a result of calls + ** made on the journal file-handle. */ + memset(p, 0, sizeof(MemJournal)); + if( nSpill==0 ){ + return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0); + } + + if( nSpill>0 ){ + p->nChunkSize = nSpill; + }else{ + p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); + assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); + } + + p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods; + p->nSpill = nSpill; + p->flags = flags; + p->zJournal = zName; + p->pVfs = pVfs; + return SQLITE_OK; } /* -** Return true if the file-handle passed as an argument is -** an in-memory journal +** Open an in-memory journal file. */ -int sqlite3IsMemJournal(sqlite3_file *pJfd){ - return pJfd->pMethods==&MemJournalMethods; +void sqlite3MemJournalOpen(sqlite3_file *pJfd){ + sqlite3JournalOpen(0, 0, pJfd, 0, -1); +} + +#ifdef SQLITE_ENABLE_ATOMIC_WRITE +/* +** If the argument p points to a MemJournal structure that is not an +** in-memory-only journal file (i.e. is one that was opened with a +ve +** nSpill parameter), and the underlying file has not yet been created, +** create it now. +*/ +int sqlite3JournalCreate(sqlite3_file *p){ + int rc = SQLITE_OK; + if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){ + rc = memjrnlCreateFile((MemJournal*)p); + } + return rc; +} +#endif + +/* +** The file-handle passed as the only argument is open on a journal file. +** Return true if this "journal file" is currently stored in heap memory, +** or false otherwise. +*/ +int sqlite3JournalIsInMemory(sqlite3_file *p){ + return p->pMethods==&MemJournalMethods; } /* -** Return the number of bytes required to store a MemJournal file descriptor. +** Return the number of bytes required to store a JournalFile that uses vfs +** pVfs to create the underlying on-disk files. */ -int sqlite3MemJournalSize(void){ - return sizeof(MemJournal); +int sqlite3JournalSize(sqlite3_vfs *pVfs){ + return MAX(pVfs->szOsFile, sizeof(MemJournal)); } diff --git a/src/os.c b/src/os.c index 2a2cf13c5e..eed7828538 100644 --- a/src/os.c +++ b/src/os.c @@ -17,6 +17,28 @@ #include "sqliteInt.h" #undef _SQLITE_OS_C_ +/* +** If we compile with the SQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error. This +** is used for testing the I/O recovery logic. +*/ +#if defined(SQLITE_TEST) +int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ +int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ +int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ +int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ +int sqlite3_io_error_benign = 0; /* True if errors are benign */ +int sqlite3_diskfull_pending = 0; +int sqlite3_diskfull = 0; +#endif /* defined(SQLITE_TEST) */ + +/* +** When testing, also keep a count of the number of open files. +*/ +#if defined(SQLITE_TEST) +int sqlite3_open_file_count = 0; +#endif /* defined(SQLITE_TEST) */ + /* ** The default SQLite sqlite3_vfs implementations do not allocate ** memory (actually, os_unix.c allocates a small amount of memory @@ -24,7 +46,7 @@ ** So we test the effects of a malloc() failing and the sqlite3OsXXX() ** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro. ** -** The following functions are instrumented for malloc() failure +** The following functions are instrumented for malloc() failure ** testing: ** ** sqlite3OsRead() @@ -44,9 +66,9 @@ #if defined(SQLITE_TEST) int sqlite3_memdebug_vfs_oom_test = 1; #define DO_OS_MALLOC_TEST(x) \ - if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \ + if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \ void *pTstAlloc = sqlite3Malloc(10); \ - if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ + if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \ sqlite3_free(pTstAlloc); \ } #else @@ -110,8 +132,8 @@ int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ #ifdef SQLITE_TEST if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite - ** is using a regular VFS, it is called after the corresponding - ** transaction has been committed. Injecting a fault at this point + ** is using a regular VFS, it is called after the corresponding + ** transaction has been committed. Injecting a fault at this point ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM ** but the transaction is committed anyway. ** @@ -180,10 +202,10 @@ int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){ ** VFS methods. */ int sqlite3OsOpen( - sqlite3_vfs *pVfs, - const char *zPath, - sqlite3_file *pFile, - int flags, + sqlite3_vfs *pVfs, + const char *zPath, + sqlite3_file *pFile, + int flags, int *pFlagsOut ){ int rc; @@ -202,18 +224,18 @@ int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ return pVfs->xDelete(pVfs, zPath, dirSync); } int sqlite3OsAccess( - sqlite3_vfs *pVfs, - const char *zPath, - int flags, + sqlite3_vfs *pVfs, + const char *zPath, + int flags, int *pResOut ){ DO_OS_MALLOC_TEST(0); return pVfs->xAccess(pVfs, zPath, flags, pResOut); } int sqlite3OsFullPathname( - sqlite3_vfs *pVfs, - const char *zPath, - int nPathOut, + sqlite3_vfs *pVfs, + const char *zPath, + int nPathOut, char *zPathOut ){ DO_OS_MALLOC_TEST(0); @@ -259,13 +281,13 @@ int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ } int sqlite3OsOpenMalloc( - sqlite3_vfs *pVfs, - const char *zFile, - sqlite3_file **ppFile, + sqlite3_vfs *pVfs, + const char *zFile, + sqlite3_file **ppFile, int flags, int *pOutFlags ){ - int rc = SQLITE_NOMEM; + int rc; sqlite3_file *pFile; pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile); if( pFile ){ @@ -275,6 +297,8 @@ int sqlite3OsOpenMalloc( }else{ *ppFile = pFile; } + }else{ + rc = SQLITE_NOMEM_BKPT; } return rc; } @@ -294,7 +318,7 @@ int sqlite3OsCloseFree(sqlite3_file *pFile){ */ int sqlite3OsInit(void){ void *p = sqlite3_malloc(10); - if( p==0 ) return SQLITE_NOMEM; + if( p==0 ) return SQLITE_NOMEM_BKPT; sqlite3_free(p); return sqlite3_os_init(); } diff --git a/src/os_common.h b/src/os_common.h index d18b95a5ff..1ed4d7a8e1 100644 --- a/src/os_common.h +++ b/src/os_common.h @@ -35,8 +35,8 @@ */ #ifdef SQLITE_PERFORMANCE_TRACE -/* -** hwtime.h contains inline assembler code for implementing +/* +** hwtime.h contains inline assembler code for implementing ** high-performance timing routines. */ #include "hwtime.h" @@ -57,14 +57,14 @@ static sqlite_uint64 g_elapsed; ** of code will give us the ability to simulate a disk I/O error. This ** is used for testing the I/O recovery logic. */ -#ifdef SQLITE_TEST -int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ -int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ -int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ -int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ -int sqlite3_io_error_benign = 0; /* True if errors are benign */ -int sqlite3_diskfull_pending = 0; -int sqlite3_diskfull = 0; +#if defined(SQLITE_TEST) +extern int sqlite3_io_error_hit; +extern int sqlite3_io_error_hardhit; +extern int sqlite3_io_error_pending; +extern int sqlite3_io_error_persist; +extern int sqlite3_io_error_benign; +extern int sqlite3_diskfull_pending; +extern int sqlite3_diskfull; #define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) #define SimulateIOError(CODE) \ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ @@ -90,16 +90,16 @@ static void local_ioerr(){ #define SimulateIOErrorBenign(X) #define SimulateIOError(A) #define SimulateDiskfullError(A) -#endif +#endif /* defined(SQLITE_TEST) */ /* ** When testing, keep a count of the number of open files. */ -#ifdef SQLITE_TEST -int sqlite3_open_file_count = 0; +#if defined(SQLITE_TEST) +extern int sqlite3_open_file_count; #define OpenCounter(X) sqlite3_open_file_count+=(X) #else #define OpenCounter(X) -#endif +#endif /* defined(SQLITE_TEST) */ #endif /* !defined(_OS_COMMON_H_) */ diff --git a/src/os_unix.c b/src/os_unix.c index fe1fc6af19..d6fbd80588 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -71,6 +71,19 @@ # endif #endif +/* Use pread() and pwrite() if they are available */ +#if defined(__APPLE__) +# define HAVE_PREAD 1 +# define HAVE_PWRITE 1 +#endif +#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) +# undef USE_PREAD +# define USE_PREAD64 1 +#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE) +# undef USE_PREAD64 +# define USE_PREAD 1 +#endif + /* ** standard include files. */ @@ -1302,7 +1315,7 @@ static int findInodeInfo( if( pInode==0 ){ pInode = sqlite3_malloc64( sizeof(*pInode) ); if( pInode==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset(pInode, 0, sizeof(*pInode)); memcpy(&pInode->fileId, &fileId, sizeof(fileId)); @@ -4222,7 +4235,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ /* Allocate space for the new unixShm object. */ p = sqlite3_malloc64( sizeof(*p) ); - if( p==0 ) return SQLITE_NOMEM; + if( p==0 ) return SQLITE_NOMEM_BKPT; memset(p, 0, sizeof(*p)); assert( pDbFd->pShm==0 ); @@ -4254,7 +4267,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ #endif pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); if( pShmNode==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); @@ -4272,7 +4285,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ pShmNode->pInode = pDbFd->pInode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } @@ -4445,7 +4458,7 @@ static int unixShmMap( pShmNode->apRegion, nReqRegion*sizeof(char *) ); if( !apNew ){ - rc = SQLITE_IOERR_NOMEM; + rc = SQLITE_IOERR_NOMEM_BKPT; goto shmpage_out; } pShmNode->apRegion = apNew; @@ -4465,7 +4478,7 @@ static int unixShmMap( }else{ pMem = sqlite3_malloc64(szRegion); if( pMem==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto shmpage_out; } memset(pMem, 0, szRegion); @@ -5243,7 +5256,7 @@ static int fillInUnixFile( pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ ctrlFlags |= UNIXFILE_NOLOCK; - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } #endif @@ -5299,7 +5312,7 @@ static int fillInUnixFile( afpLockingContext *pCtx; pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) ); if( pCtx==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ /* NB: zFilename exists and remains valid until the file is closed ** according to requirement F11141. So we do not need to make a @@ -5329,7 +5342,7 @@ static int fillInUnixFile( nFilename = (int)strlen(zFilename) + 6; zLockFile = (char *)sqlite3_malloc64(nFilename); if( zLockFile==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); } @@ -5352,7 +5365,7 @@ static int fillInUnixFile( if( zSemName[n]=='/' ) zSemName[n] = '_'; pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1); if( pNew->pInode->pSem == SEM_FAILED ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; pNew->pInode->aSemName[0] = '\0'; } } @@ -5693,7 +5706,7 @@ static int unixOpen( }else{ pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } } p->pUnused = pUnused; @@ -5779,7 +5792,7 @@ static int unixOpen( zPath = sqlite3_mprintf("%s", zName); if( zPath==0 ){ robust_close(p, fd, __LINE__); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } #else osUnlink(zName); @@ -6011,7 +6024,7 @@ static int unixFullPathname( if( bLink ){ if( zDel==0 ){ zDel = sqlite3_malloc(nOut); - if( zDel==0 ) rc = SQLITE_NOMEM; + if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; }else if( ++nLink>SQLITE_MAX_SYMLINKS ){ rc = SQLITE_CANTOPEN_BKPT; } @@ -6555,7 +6568,7 @@ static int proxyCreateUnixFile( }else{ pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } } if( fd<0 ){ @@ -6588,7 +6601,7 @@ static int proxyCreateUnixFile( pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew)); if( pNew==NULL ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto end_create_proxy; } memset(pNew, 0, sizeof(unixFile)); @@ -7001,7 +7014,7 @@ static int proxyTakeConch(unixFile *pFile){ if( tempLockPath ){ pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath); if( !pCtx->lockProxyPath ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } } } @@ -7066,7 +7079,7 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ ** the name of the original database file. */ *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8); if( conchPath==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memcpy(conchPath, dbPath, len+1); @@ -7182,7 +7195,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { pCtx = sqlite3_malloc64( sizeof(*pCtx) ); if( pCtx==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset(pCtx, 0, sizeof(*pCtx)); @@ -7218,7 +7231,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { if( rc==SQLITE_OK ){ pCtx->dbPath = sqlite3DbStrDup(0, dbPath); if( pCtx->dbPath==NULL ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } } if( rc==SQLITE_OK ){ diff --git a/src/os_win.c b/src/os_win.c index eda6cf59fb..af6b1c814c 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -1222,7 +1222,7 @@ int sqlite3_win32_compact_heap(LPUINT pnLargest){ if( lastErrno==NO_ERROR ){ sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p", (void*)hHeap); - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p", osGetLastError(), (void*)hHeap); @@ -1542,7 +1542,7 @@ static int winMemInit(void *pAppData){ "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu", osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize, dwMaximumSize); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pWinMemData->bOwned = TRUE; assert( pWinMemData->bOwned ); @@ -1552,7 +1552,7 @@ static int winMemInit(void *pAppData){ if( !pWinMemData->hHeap ){ sqlite3_log(SQLITE_NOMEM, "failed to GetProcessHeap (%lu)", osGetLastError()); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pWinMemData->bOwned = FALSE; assert( !pWinMemData->bOwned ); @@ -1789,7 +1789,7 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ if( zValue && zValue[0] ){ zValueUtf8 = winUnicodeToUtf8(zValue); if ( zValueUtf8==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } } sqlite3_free(*ppDirectory); @@ -2066,7 +2066,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){ zName = winUtf8ToUnicode(zFilename); if( zName==0 ){ /* out of memory */ - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } /* Initialize the local lockdata */ @@ -3615,12 +3615,12 @@ static int winOpenSharedMemory(winFile *pDbFd){ ** allocate space for a new winShmNode and filename. */ p = sqlite3MallocZero( sizeof(*p) ); - if( p==0 ) return SQLITE_IOERR_NOMEM; + if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; nName = sqlite3Strlen30(pDbFd->zPath); pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); if( pNew==0 ){ sqlite3_free(p); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } pNew->zFilename = (char*)&pNew[1]; sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); @@ -3647,7 +3647,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ - rc = SQLITE_IOERR_NOMEM; + rc = SQLITE_IOERR_NOMEM_BKPT; goto shm_open_err; } @@ -3952,7 +3952,7 @@ static int winShmMap( pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) ); if( !apNew ){ - rc = SQLITE_IOERR_NOMEM; + rc = SQLITE_IOERR_NOMEM_BKPT; goto shmpage_out; } pShmNode->aRegion = apNew; @@ -4382,7 +4382,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ zBuf = sqlite3MallocZero( nBuf ); if( !zBuf ){ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } /* Figure out the effective temporary directory. First, check if one @@ -4440,7 +4440,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ if( !zConverted ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( winIsDir(zConverted) ){ sqlite3_snprintf(nMax, zBuf, "%s", zDir); @@ -4453,7 +4453,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ if( !zConverted ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( cygwin_conv_path( osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir, @@ -4474,7 +4474,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ sqlite3_free(zConverted); sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); sqlite3_free(zUtf8); @@ -4492,7 +4492,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ if( !zWidePath ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( osGetTempPathW(nMax, zWidePath)==0 ){ sqlite3_free(zWidePath); @@ -4510,7 +4510,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ sqlite3_free(zWidePath); sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } } #ifdef SQLITE_WIN32_HAS_ANSI @@ -4520,7 +4520,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ if( !zMbcsPath ){ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( osGetTempPathA(nMax, zMbcsPath)==0 ){ sqlite3_free(zBuf); @@ -4535,7 +4535,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ }else{ sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } } #endif /* SQLITE_WIN32_HAS_ANSI */ @@ -4727,7 +4727,7 @@ static int winOpen( if( zConverted==0 ){ sqlite3_free(zTmpname); OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( winIsDir(zConverted) ){ @@ -4927,7 +4927,7 @@ static int winDelete( zConverted = winConvertFromUtf8Filename(zFilename); if( zConverted==0 ){ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( osIsNT() ){ do { @@ -5035,7 +5035,7 @@ static int winAccess( zConverted = winConvertFromUtf8Filename(zFilename); if( zConverted==0 ){ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( osIsNT() ){ int cnt = 0; @@ -5162,7 +5162,7 @@ static int winFullPathname( */ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); if( !zOut ){ - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( cygwin_conv_path( (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | @@ -5174,7 +5174,7 @@ static int winFullPathname( char *zUtf8 = winConvertToUtf8Filename(zOut); if( !zUtf8 ){ sqlite3_free(zOut); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", sqlite3_data_directory, winGetDirSep(), zUtf8); @@ -5184,7 +5184,7 @@ static int winFullPathname( }else{ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); if( !zOut ){ - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( cygwin_conv_path( (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), @@ -5196,7 +5196,7 @@ static int winFullPathname( char *zUtf8 = winConvertToUtf8Filename(zOut); if( !zUtf8 ){ sqlite3_free(zOut); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); sqlite3_free(zUtf8); @@ -5256,7 +5256,7 @@ static int winFullPathname( } zConverted = winConvertFromUtf8Filename(zRelative); if( zConverted==0 ){ - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } if( osIsNT() ){ LPWSTR zTemp; @@ -5270,7 +5270,7 @@ static int winFullPathname( zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); if( nByte==0 ){ @@ -5296,7 +5296,7 @@ static int winFullPathname( zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); if( zTemp==0 ){ sqlite3_free(zConverted); - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); if( nByte==0 ){ @@ -5315,7 +5315,7 @@ static int winFullPathname( sqlite3_free(zOut); return SQLITE_OK; }else{ - return SQLITE_IOERR_NOMEM; + return SQLITE_IOERR_NOMEM_BKPT; } #endif } @@ -5390,65 +5390,85 @@ static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ #define winDlClose 0 #endif +/* State information for the randomness gatherer. */ +typedef struct EntropyGatherer EntropyGatherer; +struct EntropyGatherer { + unsigned char *a; /* Gather entropy into this buffer */ + int na; /* Size of a[] in bytes */ + int i; /* XOR next input into a[i] */ + int nXor; /* Number of XOR operations done */ +}; + +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) +/* Mix sz bytes of entropy into p. */ +static void xorMemory(EntropyGatherer *p, unsigned char *x, int sz){ + int j, k; + for(j=0, k=p->i; ja[k++] ^= x[j]; + if( k>=p->na ) k = 0; + } + p->i = k; + p->nXor += sz; +} +#endif /* !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) */ /* ** Write up to nBuf bytes of randomness into zBuf. */ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - int n = 0; - UNUSED_PARAMETER(pVfs); #if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) - n = nBuf; + UNUSED_PARAMETER(pVfs); memset(zBuf, 0, nBuf); + return nBuf; #else - if( sizeof(SYSTEMTIME)<=nBuf-n ){ + EntropyGatherer e; + UNUSED_PARAMETER(pVfs); + memset(zBuf, 0, nBuf); +#if defined(_MSC_VER) && _MSC_VER>=1400 + rand_s((int*)zBuf); /* rand_s() is not available with MinGW */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */ + e.a = (unsigned char*)zBuf; + e.na = nBuf; + e.nXor = 0; + e.i = 0; + { SYSTEMTIME x; osGetSystemTime(&x); - memcpy(&zBuf[n], &x, sizeof(x)); - n += sizeof(x); + xorMemory(&e, (unsigned char*)&x, sizeof(SYSTEMTIME)); } - if( sizeof(DWORD)<=nBuf-n ){ + { DWORD pid = osGetCurrentProcessId(); - memcpy(&zBuf[n], &pid, sizeof(pid)); - n += sizeof(pid); + xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD)); } #if SQLITE_OS_WINRT - if( sizeof(ULONGLONG)<=nBuf-n ){ + { ULONGLONG cnt = osGetTickCount64(); - memcpy(&zBuf[n], &cnt, sizeof(cnt)); - n += sizeof(cnt); + xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG)); } #else - if( sizeof(DWORD)<=nBuf-n ){ + { DWORD cnt = osGetTickCount(); - memcpy(&zBuf[n], &cnt, sizeof(cnt)); - n += sizeof(cnt); + xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD)); } -#endif - if( sizeof(LARGE_INTEGER)<=nBuf-n ){ +#endif /* SQLITE_OS_WINRT */ + { LARGE_INTEGER i; osQueryPerformanceCounter(&i); - memcpy(&zBuf[n], &i, sizeof(i)); - n += sizeof(i); + xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER)); } #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[n], &id, sizeof(UUID)); - n += sizeof(UUID); - } - if( sizeof(UUID)<=nBuf-n ){ - UUID id; + xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); memset(&id, 0, sizeof(UUID)); osUuidCreateSequential(&id); - memcpy(&zBuf[n], &id, sizeof(UUID)); - n += sizeof(UUID); + xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); } -#endif -#endif /* defined(SQLITE_TEST) || defined(SQLITE_ZERO_PRNG_SEED) */ - return n; +#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */ + return e.nXor>nBuf ? nBuf : e.nXor; +#endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */ } diff --git a/src/pager.c b/src/pager.c index 5c61968e36..316a5e8ec0 100644 --- a/src/pager.c +++ b/src/pager.c @@ -428,19 +428,6 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ */ #define MAX_SECTOR_SIZE 0x10000 -/* -** If the option SQLITE_EXTRA_DURABLE option is set at compile-time, then -** SQLite will do extra fsync() operations when synchronous==FULL to help -** ensure that transactions are durable across a power failure. Most -** applications are happy as long as transactions are consistent across -** a power failure, and are perfectly willing to lose the last transaction -** in exchange for the extra performance of avoiding directory syncs. -** And so the default SQLITE_EXTRA_DURABLE setting is off. -*/ -#ifndef SQLITE_EXTRA_DURABLE -# define SQLITE_EXTRA_DURABLE 0 -#endif - /* ** An instance of the following structure is allocated for each active @@ -1343,6 +1330,7 @@ static i64 journalHdrOffset(Pager *pPager){ static int zeroJournalHdr(Pager *pPager, int doTruncate){ int rc = SQLITE_OK; /* Return code */ assert( isOpen(pPager->jfd) ); + assert( !sqlite3JournalIsInMemory(pPager->jfd) ); if( pPager->journalOff ){ const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */ @@ -1724,7 +1712,7 @@ static void releaseAllSavepoints(Pager *pPager){ for(ii=0; iinSavepoint; ii++){ sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint); } - if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){ + if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){ sqlite3OsClose(pPager->sjfd); } sqlite3_free(pPager->aSavepoint); @@ -1962,8 +1950,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ assert( !pagerUseWal(pPager) ); /* Finalize the journal file. */ - if( sqlite3IsMemJournal(pPager->jfd) ){ - assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); + if( sqlite3JournalIsInMemory(pPager->jfd) ){ + /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */ sqlite3OsClose(pPager->jfd); }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ if( pPager->journalOff==0 ){ @@ -1989,9 +1977,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ /* This branch may be executed with Pager.journalMode==MEMORY if ** a hot-journal was just rolled back. In this case the journal ** file should be closed and deleted. If this connection writes to - ** the database file, it will do so using an in-memory journal. + ** the database file, it will do so using an in-memory journal. */ - int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd)); + int bDelete = !pPager->tempFile; + assert( sqlite3JournalIsInMemory(pPager->jfd)==0 ); assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_WAL @@ -2324,9 +2313,9 @@ static int pager_playback_one_page( pPager->dbFileSize = pgno; } if( pPager->pBackup ){ - CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM); + CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); - CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData); + CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); } }else if( !isMainJrnl && pPg==0 ){ /* If this is a rollback of a savepoint and data was not written to @@ -2398,7 +2387,7 @@ static int pager_playback_one_page( } /* Decode the page just read from disk */ - CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM); + CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); sqlite3PcacheRelease(pPg); } return rc; @@ -2464,7 +2453,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile); if( !pMaster ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL); rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0); @@ -2481,7 +2470,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){ nMasterPtr = pVfs->mxPathname+1; zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1); if( !zMasterJournal ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto delmaster_out; } zMasterPtr = &zMasterJournal[nMasterJournal+1]; @@ -2729,7 +2718,7 @@ static int pager_playback(Pager *pPager, int isHot){ ** TODO: Technically the following is an error because it assumes that ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value + ** mxPathname is 512, which is the same as the minimum allowable value ** for pageSize. */ zMaster = pPager->pTmpSpace; @@ -2951,7 +2940,7 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){ memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); } } - CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM); + CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT); PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); @@ -3311,7 +3300,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ if( pSavepoint ){ pDone = sqlite3BitvecCreate(pSavepoint->nOrig); if( !pDone ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } } @@ -3458,7 +3447,7 @@ void sqlite3PagerShrink(Pager *pPager){ ** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness ** of the database to damage due to OS crashes or power failures by ** changing the number of syncs()s when writing the journals. -** There are three levels: +** There are four levels: ** ** OFF sqlite3OsSync() is never called. This is the default ** for temporary and transient files. @@ -3478,6 +3467,10 @@ void sqlite3PagerShrink(Pager *pPager){ ** assurance that the journal will not be corrupted to the ** point of causing damage to the database during rollback. ** +** EXTRA This is like FULL except that is also syncs the directory +** that contains the rollback journal after the rollback +** journal is unlinked. +** ** The above is for a rollback-journal mode. For WAL mode, OFF continues ** to mean that no syncs ever occur. NORMAL means that the WAL is synced ** prior to the start of checkpoint and that the database file is synced @@ -3485,7 +3478,8 @@ void sqlite3PagerShrink(Pager *pPager){ ** was written back into the database. But no sync operations occur for ** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL ** file is synced following each commit operation, in addition to the -** syncs associated with NORMAL. +** syncs associated with NORMAL. There is no difference between FULL +** and EXTRA for WAL mode. ** ** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The ** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync @@ -3674,7 +3668,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ } if( rc==SQLITE_OK ){ pNew = (char *)sqlite3PageMalloc(pageSize); - if( !pNew ) rc = SQLITE_NOMEM; + if( !pNew ) rc = SQLITE_NOMEM_BKPT; } if( rc==SQLITE_OK ){ @@ -3950,7 +3944,7 @@ static int pagerAcquireMapPage( *ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); if( p==0 ){ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } p->pExtra = (void *)&p[1]; p->flags = PGHDR_MMAP; @@ -4308,7 +4302,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ if( pList->pgno==1 ) pager_write_changecounter(pList); /* Encode the database */ - CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData); + CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); /* Write out the page data. */ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); @@ -4353,11 +4347,14 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ static int openSubJournal(Pager *pPager){ int rc = SQLITE_OK; if( !isOpen(pPager->sjfd) ){ + const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE + | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE + | SQLITE_OPEN_DELETEONCLOSE; + int nStmtSpill = sqlite3Config.nStmtSpill; if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){ - sqlite3MemJournalOpen(pPager->sjfd); - }else{ - rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL); + nStmtSpill = -1; } + rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill); } return rc; } @@ -4395,7 +4392,7 @@ static int subjournalPage(PgHdr *pPg){ i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; - CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); + CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); if( rc==SQLITE_OK ){ @@ -4578,18 +4575,8 @@ int sqlite3PagerOpen( int nUri = 0; /* Number of bytes of URI args at *zUri */ /* Figure out how much space is required for each journal file-handle - ** (there are two of them, the main journal and the sub-journal). This - ** is the maximum space required for an in-memory journal file handle - ** and a regular journal file-handle. Note that a "regular journal-handle" - ** may be a wrapper capable of caching the first portion of the journal - ** file in memory to implement the atomic-write optimization (see - ** source file journal.c). - */ - if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){ - journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); - }else{ - journalFileSize = ROUND8(sqlite3MemJournalSize()); - } + ** (there are two of them, the main journal and the sub-journal). */ + journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); /* Set the output variable to NULL in case an error occurs. */ *ppPager = 0; @@ -4599,7 +4586,7 @@ int sqlite3PagerOpen( memDb = 1; if( zFilename && zFilename[0] ){ zPathname = sqlite3DbStrDup(0, zFilename); - if( zPathname==0 ) return SQLITE_NOMEM; + if( zPathname==0 ) return SQLITE_NOMEM_BKPT; nPathname = sqlite3Strlen30(zPathname); zFilename = 0; } @@ -4615,7 +4602,7 @@ int sqlite3PagerOpen( nPathname = pVfs->mxPathname+1; zPathname = sqlite3DbMallocRaw(0, nPathname*2); if( zPathname==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); @@ -4668,7 +4655,7 @@ int sqlite3PagerOpen( assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) ); if( !pPtr ){ sqlite3DbFree(0, zPathname); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pPager = (Pager*)(pPtr); pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager))); @@ -4823,11 +4810,7 @@ act_like_temp_file: assert( pPager->ckptSyncFlags==0 ); }else{ pPager->fullSync = 1; -#if SQLITE_EXTRA_DURABLE - pPager->extraSync = 1; -#else pPager->extraSync = 0; -#endif pPager->syncFlags = SQLITE_SYNC_NORMAL; pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS; pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; @@ -5388,7 +5371,7 @@ int sqlite3PagerGet( if( rc!=SQLITE_OK ) goto pager_acquire_err; if( pBase==0 ){ pPg = *ppPage = 0; - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto pager_acquire_err; } } @@ -5562,7 +5545,7 @@ static int pager_open_journal(Pager *pPager){ if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize); if( pPager->pInJournal==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } /* Open the journal file if it is not already open. */ @@ -5717,7 +5700,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); assert( pPager->journalHdr<=pPager->journalOff ); - CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2); + CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); cksum = pager_cksum(pPager, (u8*)pData2); /* Even if an IO or diskfull error occurs while journalling the @@ -6074,7 +6057,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ if( DIRECT_MODE ){ const void *zBuf; assert( pPager->dbFileSize>0 ); - CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); + CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); pPager->aStat[PAGER_STAT_WRITE]++; @@ -6573,7 +6556,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint ); if( !aNew ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint)); pPager->aSavepoint = aNew; @@ -6589,7 +6572,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){ aNew[ii].iSubRec = pPager->nSubRec; aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize); if( !aNew[ii].pInSavepoint ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } if( pagerUseWal(pPager) ){ sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData); @@ -6667,7 +6650,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){ if( op==SAVEPOINT_RELEASE ){ if( nNew==0 && isOpen(pPager->sjfd) ){ /* Only truncate if it is an in-memory sub-journal. */ - if( sqlite3IsMemJournal(pPager->sjfd) ){ + if( sqlite3JournalIsInMemory(pPager->sjfd) ){ rc = sqlite3OsTruncate(pPager->sjfd, 0); assert( rc==SQLITE_OK ); } @@ -6738,14 +6721,6 @@ const char *sqlite3PagerJournalname(Pager *pPager){ return pPager->zJournal; } -/* -** Return true if fsync() calls are disabled for this pager. Return FALSE -** if fsync()s are executed normally. -*/ -int sqlite3PagerNosync(Pager *pPager){ - return pPager->noSync; -} - #ifdef SQLITE_HAS_CODEC /* ** Set or retrieve the codec for this pager diff --git a/src/pager.h b/src/pager.h index 8d9f08108d..38a4982150 100644 --- a/src/pager.h +++ b/src/pager.h @@ -191,7 +191,6 @@ sqlite3_vfs *sqlite3PagerVfs(Pager*); sqlite3_file *sqlite3PagerFile(Pager*); sqlite3_file *sqlite3PagerJrnlFile(Pager*); const char *sqlite3PagerJournalname(Pager*); -int sqlite3PagerNosync(Pager*); void *sqlite3PagerTempSpace(Pager*); int sqlite3PagerIsMemdb(Pager*); void sqlite3PagerCacheStat(Pager *, int, int, int *); diff --git a/src/parse.y b/src/parse.y index 0bfe4e473a..20492edb80 100644 --- a/src/parse.y +++ b/src/parse.y @@ -35,7 +35,6 @@ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); } %stack_overflow { - UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */ sqlite3ErrorMsg(pParse, "parser stack overflow"); } @@ -139,9 +138,9 @@ trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} -transtype(A) ::= DEFERRED(X). {A = @X;} -transtype(A) ::= IMMEDIATE(X). {A = @X;} -transtype(A) ::= EXCLUSIVE(X). {A = @X;} +transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/} +transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/} +transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/} cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);} cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);} cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);} @@ -164,10 +163,8 @@ cmd ::= create_table create_table_args. create_table ::= createkw temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). { sqlite3StartTable(pParse,&Y,&Z,T,0,0,E); } -createkw(A) ::= CREATE(X). { - disableLookaside(pParse); - A = X; -} +createkw(A) ::= CREATE(A). {disableLookaside(pParse);} + %type ifnotexists {int} ifnotexists(A) ::= . {A = 0;} ifnotexists(A) ::= IF NOT EXISTS. {A = 1;} @@ -193,24 +190,9 @@ table_options(A) ::= WITHOUT nm(X). { sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z); } } -columnlist ::= columnlist COMMA column. -columnlist ::= column. - -// A "column" is a complete description of a single column in a -// CREATE TABLE statement. This includes the column name, its -// datatype, and other keywords such as PRIMARY KEY, UNIQUE, REFERENCES, -// NOT NULL and so forth. -// -column(A) ::= columnid(X) type carglist. { - A.z = X.z; - A.n = (int)(pParse->sLastToken.z-X.z) + pParse->sLastToken.n; -} -columnid(A) ::= nm(X). { - sqlite3AddColumn(pParse,&X); - A = X; - pParse->constraintName.n = 0; -} - +columnlist ::= columnlist COMMA columnname carglist. +columnlist ::= columnname carglist. +columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);} // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. @@ -265,29 +247,26 @@ columnid(A) ::= nm(X). { // The name of a column or table can be any of the following: // %type nm {Token} -nm(A) ::= id(X). {A = X;} -nm(A) ::= STRING(X). {A = X;} -nm(A) ::= JOIN_KW(X). {A = X;} +nm(A) ::= id(A). +nm(A) ::= STRING(A). +nm(A) ::= JOIN_KW(A). -// A typetoken is really one or more tokens that form a type name such +// A typetoken is really zero or more tokens that form a type name such // as can be found after the column name in a CREATE TABLE statement. // Multiple tokens are concatenated to form the value of the typetoken. // %type typetoken {Token} -type ::= . -type ::= typetoken(X). {sqlite3AddColumnType(pParse,&X);} -typetoken(A) ::= typename(X). {A = X;} -typetoken(A) ::= typename(X) LP signed RP(Y). { - A.z = X.z; - A.n = (int)(&Y.z[Y.n] - X.z); +typetoken(A) ::= . {A.n = 0; A.z = 0;} +typetoken(A) ::= typename(A). +typetoken(A) ::= typename(A) LP signed RP(Y). { + A.n = (int)(&Y.z[Y.n] - A.z); } -typetoken(A) ::= typename(X) LP signed COMMA signed RP(Y). { - A.z = X.z; - A.n = (int)(&Y.z[Y.n] - X.z); +typetoken(A) ::= typename(A) LP signed COMMA signed RP(Y). { + A.n = (int)(&Y.z[Y.n] - A.z); } %type typename {Token} -typename(A) ::= ids(X). {A = X;} -typename(A) ::= typename(X) ids(Y). {A.z=X.z; A.n=Y.n+(int)(Y.z-X.z);} +typename(A) ::= ids(A). +typename(A) ::= typename(A) ids(Y). {A.n=Y.n+(int)(Y.z-A.z);} signed ::= plus_num. signed ::= minus_num. @@ -309,7 +288,7 @@ ccons ::= DEFAULT MINUS(A) term(X). { } ccons ::= DEFAULT id(X). { ExprSpan v; - spanExpr(&v, pParse, TK_STRING, &X); + spanExpr(&v, pParse, TK_STRING, X); sqlite3AddDefaultValue(pParse,&v); } @@ -339,7 +318,7 @@ autoinc(X) ::= AUTOINCR. {X = 1;} // %type refargs {int} refargs(A) ::= . { A = OE_None*0x0101; /* EV: R-19803-45884 */} -refargs(A) ::= refargs(X) refarg(Y). { A = (X & ~Y.mask) | Y.value; } +refargs(A) ::= refargs(A) refarg(Y). { A = (A & ~Y.mask) | Y.value; } %type refarg {struct {int value; int mask;}} refarg(A) ::= MATCH nm. { A.value = 0; A.mask = 0x000000; } refarg(A) ::= ON INSERT refact. { A.value = 0; A.mask = 0x000000; } @@ -360,7 +339,7 @@ init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;} init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;} conslist_opt(A) ::= . {A.n = 0; A.z = 0;} -conslist_opt(A) ::= COMMA(X) conslist. {A = X;} +conslist_opt(A) ::= COMMA(A) conslist. conslist ::= conslist tconscomma tcons. conslist ::= tcons. tconscomma ::= COMMA. {pParse->constraintName.n = 0;} @@ -379,7 +358,7 @@ tcons ::= FOREIGN KEY LP eidlist(FA) RP } %type defer_subclause_opt {int} defer_subclause_opt(A) ::= . {A = 0;} -defer_subclause_opt(A) ::= defer_subclause(X). {A = X;} +defer_subclause_opt(A) ::= defer_subclause(A). // The following is a non-standard extension that allows us to declare the // default behavior when there is a constraint conflict. @@ -391,7 +370,7 @@ onconf(A) ::= . {A = OE_Default;} onconf(A) ::= ON CONFLICT resolvetype(X). {A = X;} orconf(A) ::= . {A = OE_Default;} orconf(A) ::= OR resolvetype(X). {A = X;} -resolvetype(A) ::= raisetype(X). {A = X;} +resolvetype(A) ::= raisetype(A). resolvetype(A) ::= IGNORE. {A = OE_Ignore;} resolvetype(A) ::= REPLACE. {A = OE_Replace;} @@ -463,14 +442,14 @@ select(A) ::= with(W) selectnowith(X). { }else{ sqlite3WithDelete(pParse->db, W); } - A = p; + A = p; /*A-overwrites-W*/ } -selectnowith(A) ::= oneselect(X). {A = X;} +selectnowith(A) ::= oneselect(A). %ifndef SQLITE_OMIT_COMPOUND_SELECT -selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). { +selectnowith(A) ::= selectnowith(A) multiselect_op(Y) oneselect(Z). { Select *pRhs = Z; - Select *pLhs = X; + Select *pLhs = A; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -491,12 +470,15 @@ selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). { A = pRhs; } %type multiselect_op {int} -multiselect_op(A) ::= UNION(OP). {A = @OP;} +multiselect_op(A) ::= UNION(OP). {A = @OP; /*A-overwrites-OP*/} multiselect_op(A) ::= UNION ALL. {A = TK_ALL;} -multiselect_op(A) ::= EXCEPT|INTERSECT(OP). {A = @OP;} +multiselect_op(A) ::= EXCEPT|INTERSECT(OP). {A = @OP; /*A-overwrites-OP*/} %endif SQLITE_OMIT_COMPOUND_SELECT oneselect(A) ::= SELECT(S) distinct(D) selcollist(W) from(X) where_opt(Y) groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). { +#if SELECTTRACE_ENABLED + Token s = S; /*A-overwrites-S*/ +#endif A = sqlite3SelectNew(pParse,W,X,Y,P,Q,Z,D,L.pLimit,L.pOffset); #if SELECTTRACE_ENABLED /* Populate the Select.zSelName[] string that is used to help with @@ -509,7 +491,7 @@ oneselect(A) ::= SELECT(S) distinct(D) selcollist(W) from(X) where_opt(Y) ** is an integer that is incremented with each SELECT statement seen. */ if( A!=0 ){ - const char *z = S.z+6; + const char *z = s.z+6; int i; sqlite3_snprintf(sizeof(A->zSelName), A->zSelName, "#%d", ++pParse->nSelect); @@ -523,20 +505,19 @@ oneselect(A) ::= SELECT(S) distinct(D) selcollist(W) from(X) where_opt(Y) } #endif /* SELECTRACE_ENABLED */ } -oneselect(A) ::= values(X). {A = X;} +oneselect(A) ::= values(A). %type values {Select*} %destructor values {sqlite3SelectDelete(pParse->db, $$);} values(A) ::= VALUES LP nexprlist(X) RP. { A = sqlite3SelectNew(pParse,X,0,0,0,0,0,SF_Values,0,0); } -values(A) ::= values(X) COMMA LP exprlist(Y) RP. { - Select *pRight, *pLeft = X; +values(A) ::= values(A) COMMA LP exprlist(Y) RP. { + Select *pRight, *pLeft = A; pRight = sqlite3SelectNew(pParse,Y,0,0,0,0,0,SF_Values|SF_MultiValue,0,0); if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; - pLeft = X; pRight->pPrior = pLeft; A = pRight; }else{ @@ -561,22 +542,22 @@ distinct(A) ::= . {A = 0;} %destructor selcollist {sqlite3ExprListDelete(pParse->db, $$);} %type sclp {ExprList*} %destructor sclp {sqlite3ExprListDelete(pParse->db, $$);} -sclp(A) ::= selcollist(X) COMMA. {A = X;} +sclp(A) ::= selcollist(A) COMMA. sclp(A) ::= . {A = 0;} -selcollist(A) ::= sclp(P) expr(X) as(Y). { - A = sqlite3ExprListAppend(pParse, P, X.pExpr); +selcollist(A) ::= sclp(A) expr(X) as(Y). { + A = sqlite3ExprListAppend(pParse, A, X.pExpr); if( Y.n>0 ) sqlite3ExprListSetName(pParse, A, &Y, 1); sqlite3ExprListSetSpan(pParse,A,&X); } -selcollist(A) ::= sclp(P) STAR. { +selcollist(A) ::= sclp(A) STAR. { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); - A = sqlite3ExprListAppend(pParse, P, p); + A = sqlite3ExprListAppend(pParse, A, p); } -selcollist(A) ::= sclp(P) nm(X) DOT STAR(Y). { +selcollist(A) ::= sclp(A) nm(X) DOT STAR(Y). { Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0, &Y); Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &X); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); - A = sqlite3ExprListAppend(pParse,P, pDot); + A = sqlite3ExprListAppend(pParse,A, pDot); } // An option "AS " phrase that can follow one of the expressions that @@ -584,8 +565,8 @@ selcollist(A) ::= sclp(P) nm(X) DOT STAR(Y). { // %type as {Token} as(X) ::= AS nm(Y). {X = Y;} -as(X) ::= ids(Y). {X = Y;} -as(X) ::= . {X.n = 0;} +as(X) ::= ids(X). +as(X) ::= . {X.n = 0; X.z = 0;} %type seltablist {SrcList*} @@ -606,32 +587,31 @@ from(A) ::= FROM seltablist(X). { // "seltablist" is a "Select Table List" - the content of the FROM clause // in a SELECT statement. "stl_prefix" is a prefix of this list. // -stl_prefix(A) ::= seltablist(X) joinop(Y). { - A = X; +stl_prefix(A) ::= seltablist(A) joinop(Y). { if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].fg.jointype = (u8)Y; } stl_prefix(A) ::= . {A = 0;} -seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) +seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U); sqlite3SrcListIndexedBy(pParse, A, &I); } -seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) +seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,&Y,&D,&Z,0,N,U); sqlite3SrcListFuncArgs(pParse, A, E); } %ifndef SQLITE_OMIT_SUBQUERY - seltablist(A) ::= stl_prefix(X) LP select(S) RP + seltablist(A) ::= stl_prefix(A) LP select(S) RP as(Z) on_opt(N) using_opt(U). { - A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,N,U); } - seltablist(A) ::= stl_prefix(X) LP seltablist(F) RP + seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP as(Z) on_opt(N) using_opt(U). { - if( X==0 && Z.n==0 && N==0 && U==0 ){ + if( A==0 && Z.n==0 && N==0 && U==0 ){ A = F; }else if( F->nSrc==1 ){ - A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,0,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,N,U); if( A ){ struct SrcList_item *pNew = &A->a[A->nSrc-1]; struct SrcList_item *pOld = F->a; @@ -646,7 +626,7 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) Select *pSubquery; sqlite3SrcListShiftJoinType(F); pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0,0); - A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,pSubquery,N,U); + A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,N,U); } } %endif SQLITE_OMIT_SUBQUERY @@ -657,14 +637,17 @@ dbnm(A) ::= DOT nm(X). {A = X;} %type fullname {SrcList*} %destructor fullname {sqlite3SrcListDelete(pParse->db, $$);} -fullname(A) ::= nm(X) dbnm(Y). {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y);} +fullname(A) ::= nm(X) dbnm(Y). + {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/} %type joinop {int} joinop(X) ::= COMMA|JOIN. { X = JT_INNER; } -joinop(X) ::= JOIN_KW(A) JOIN. { X = sqlite3JoinType(pParse,&A,0,0); } -joinop(X) ::= JOIN_KW(A) nm(B) JOIN. { X = sqlite3JoinType(pParse,&A,&B,0); } +joinop(X) ::= JOIN_KW(A) JOIN. + {X = sqlite3JoinType(pParse,&A,0,0); /*X-overwrites-A*/} +joinop(X) ::= JOIN_KW(A) nm(B) JOIN. + {X = sqlite3JoinType(pParse,&A,&B,0); /*X-overwrites-A*/} joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN. - { X = sqlite3JoinType(pParse,&A,&B,&C); } + {X = sqlite3JoinType(pParse,&A,&B,&C);/*X-overwrites-A*/} %type on_opt {Expr*} %destructor on_opt {sqlite3ExprDelete(pParse->db, $$);} @@ -704,12 +687,12 @@ using_opt(U) ::= . {U = 0;} orderby_opt(A) ::= . {A = 0;} orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;} -sortlist(A) ::= sortlist(X) COMMA expr(Y) sortorder(Z). { - A = sqlite3ExprListAppend(pParse,X,Y.pExpr); +sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z). { + A = sqlite3ExprListAppend(pParse,A,Y.pExpr); sqlite3ExprListSetSortOrder(A,Z); } sortlist(A) ::= expr(Y) sortorder(Z). { - A = sqlite3ExprListAppend(pParse,0,Y.pExpr); + A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/ sqlite3ExprListSetSortOrder(A,Z); } @@ -799,8 +782,8 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) %type setlist {ExprList*} %destructor setlist {sqlite3ExprListDelete(pParse->db, $$);} -setlist(A) ::= setlist(Z) COMMA nm(X) EQ expr(Y). { - A = sqlite3ExprListAppend(pParse, Z, Y.pExpr); +setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). { + A = sqlite3ExprListAppend(pParse, A, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } setlist(A) ::= nm(X) EQ expr(Y). { @@ -831,10 +814,10 @@ insert_cmd(A) ::= REPLACE. {A = OE_Replace;} idlist_opt(A) ::= . {A = 0;} idlist_opt(A) ::= LP idlist(X) RP. {A = X;} -idlist(A) ::= idlist(X) COMMA nm(Y). - {A = sqlite3IdListAppend(pParse->db,X,&Y);} +idlist(A) ::= idlist(A) COMMA nm(Y). + {A = sqlite3IdListAppend(pParse->db,A,&Y);} idlist(A) ::= nm(Y). - {A = sqlite3IdListAppend(pParse->db,0,&Y);} + {A = sqlite3IdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/} /////////////////////////// Expression Processing ///////////////////////////// // @@ -858,61 +841,62 @@ idlist(A) ::= nm(Y). ** new Expr to populate pOut. Set the span of pOut to be the identifier ** that created the expression. */ - static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token *pValue){ - pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, pValue); - pOut->zStart = pValue->z; - pOut->zEnd = &pValue->z[pValue->n]; + static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){ + pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, &t); + pOut->zStart = t.z; + pOut->zEnd = &t.z[t.n]; } } -expr(A) ::= term(X). {A = X;} -expr(A) ::= LP(B) expr(X) RP(E). {A.pExpr = X.pExpr; spanSet(&A,&B,&E);} -term(A) ::= NULL(X). {spanExpr(&A, pParse, @X, &X);} -expr(A) ::= id(X). {spanExpr(&A, pParse, TK_ID, &X);} -expr(A) ::= JOIN_KW(X). {spanExpr(&A, pParse, TK_ID, &X);} +expr(A) ::= term(A). +expr(A) ::= LP(B) expr(X) RP(E). + {spanSet(&A,&B,&E); /*A-overwrites-B*/ A.pExpr = X.pExpr;} +term(A) ::= NULL(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} +expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} +expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X); Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y); + spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0); - spanSet(&A,&X,&Y); } expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &X); Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Y); Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &Z); Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0); + spanSet(&A,&X,&Z); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0); - spanSet(&A,&X,&Z); } -term(A) ::= INTEGER|FLOAT|BLOB(X). {spanExpr(&A, pParse, @X, &X);} -term(A) ::= STRING(X). {spanExpr(&A, pParse, @X, &X);} +term(A) ::= INTEGER|FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} +term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X);/*A-overwrites-X*/} expr(A) ::= VARIABLE(X). { - if( X.n>=2 && X.z[0]=='#' && sqlite3Isdigit(X.z[1]) ){ + Token t = X; /*A-overwrites-X*/ + if( t.n>=2 && t.z[0]=='#' && sqlite3Isdigit(t.z[1]) ){ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers ** in the virtual machine. #N is the N-th register. */ + spanSet(&A, &t, &t); if( pParse->nested==0 ){ - sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X); + sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); A.pExpr = 0; }else{ - A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X); - if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable); + A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &t); + if( A.pExpr ) sqlite3GetInt32(&t.z[1], &A.pExpr->iTable); } }else{ - spanExpr(&A, pParse, TK_VARIABLE, &X); + spanExpr(&A, pParse, TK_VARIABLE, t); sqlite3ExprAssignVarNumber(pParse, A.pExpr); } - spanSet(&A, &X, &X); } -expr(A) ::= expr(E) COLLATE ids(C). { - A.pExpr = sqlite3ExprAddCollateToken(pParse, E.pExpr, &C, 1); - A.zStart = E.zStart; +expr(A) ::= expr(A) COLLATE ids(C). { + A.pExpr = sqlite3ExprAddCollateToken(pParse, A.pExpr, &C, 1); A.zEnd = &C.z[C.n]; } %ifndef SQLITE_OMIT_CAST expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). { + spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_CAST, E.pExpr, 0, &T); - spanSet(&A,&X,&Y); } %endif SQLITE_OMIT_CAST expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). { @@ -939,58 +923,56 @@ term(A) ::= CTIME_KW(OP). { ** objects and uses the result to populate a new ExprSpan object. */ static void spanBinaryExpr( - ExprSpan *pOut, /* Write the result here */ Parse *pParse, /* The parsing context. Errors accumulate here */ int op, /* The binary operation */ - ExprSpan *pLeft, /* The left operand */ + ExprSpan *pLeft, /* The left operand, and output */ ExprSpan *pRight /* The right operand */ ){ - pOut->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0); - pOut->zStart = pLeft->zStart; - pOut->zEnd = pRight->zEnd; + pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0); + pLeft->zEnd = pRight->zEnd; } /* If doNot is true, then add a TK_NOT Expr-node wrapper around the ** outside of *ppExpr. */ - static void exprNot(Parse *pParse, int doNot, Expr **ppExpr){ - if( doNot ) *ppExpr = sqlite3PExpr(pParse, TK_NOT, *ppExpr, 0, 0); + static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){ + if( doNot ){ + pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0, 0); + } } } -expr(A) ::= expr(X) AND(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) OR(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) LT|GT|GE|LE(OP) expr(Y). - {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) EQ|NE(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y). - {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y). - {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). - {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} -expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} +expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y). + {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) EQ|NE(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y). + {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) PLUS|MINUS(OP) expr(Y). + {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) STAR|SLASH|REM(OP) expr(Y). + {spanBinaryExpr(pParse,@OP,&A,&Y);} +expr(A) ::= expr(A) CONCAT(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);} %type likeop {struct LikeOp} -likeop(A) ::= LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 0;} +likeop(A) ::= LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 0;/*A-overwrites-X*/} likeop(A) ::= NOT LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 1;} -expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] { +expr(A) ::= expr(A) likeop(OP) expr(Y). [LIKE_KW] { ExprList *pList; pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, X.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, A.pExpr); A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator); - exprNot(pParse, OP.bNot, &A.pExpr); - A.zStart = X.zStart; + exprNot(pParse, OP.bNot, &A); A.zEnd = Y.zEnd; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; } -expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { +expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { ExprList *pList; pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, X.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, A.pExpr); pList = sqlite3ExprListAppend(pParse,pList, E.pExpr); A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator); - exprNot(pParse, OP.bNot, &A.pExpr); - A.zStart = X.zStart; + exprNot(pParse, OP.bNot, &A); A.zEnd = E.zEnd; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; } @@ -999,20 +981,18 @@ expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { /* Construct an expression node for a unary postfix operator */ static void spanUnaryPostfix( - ExprSpan *pOut, /* Write the new expression node here */ Parse *pParse, /* Parsing context to record errors */ int op, /* The operator */ - ExprSpan *pOperand, /* The operand */ + ExprSpan *pOperand, /* The operand, and output */ Token *pPostOp /* The operand token for setting the span */ ){ - pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0); - pOut->zStart = pOperand->zStart; - pOut->zEnd = &pPostOp->z[pPostOp->n]; + pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0); + pOperand->zEnd = &pPostOp->z[pPostOp->n]; } } -expr(A) ::= expr(X) ISNULL|NOTNULL(E). {spanUnaryPostfix(&A,pParse,@E,&X,&E);} -expr(A) ::= expr(X) NOT NULL(E). {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);} +expr(A) ::= expr(A) ISNULL|NOTNULL(E). {spanUnaryPostfix(pParse,@E,&A,&E);} +expr(A) ::= expr(A) NOT NULL(E). {spanUnaryPostfix(pParse,TK_NOTNULL,&A,&E);} %include { /* A routine to convert a binary TK_IS or TK_ISNOT expression into a @@ -1033,12 +1013,12 @@ expr(A) ::= expr(X) NOT NULL(E). {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);} // If expr2 is NULL then code as TK_ISNULL or TK_NOTNULL. If expr2 // is any other expression, code as TK_IS or TK_ISNOT. // -expr(A) ::= expr(X) IS expr(Y). { - spanBinaryExpr(&A,pParse,TK_IS,&X,&Y); +expr(A) ::= expr(A) IS expr(Y). { + spanBinaryExpr(pParse,TK_IS,&A,&Y); binaryToUnaryIfNull(pParse, Y.pExpr, A.pExpr, TK_ISNULL); } -expr(A) ::= expr(X) IS NOT expr(Y). { - spanBinaryExpr(&A,pParse,TK_ISNOT,&X,&Y); +expr(A) ::= expr(A) IS NOT expr(Y). { + spanBinaryExpr(pParse,TK_ISNOT,&A,&Y); binaryToUnaryIfNull(pParse, Y.pExpr, A.pExpr, TK_NOTNULL); } @@ -1052,42 +1032,43 @@ expr(A) ::= expr(X) IS NOT expr(Y). { ExprSpan *pOperand, /* The operand */ Token *pPreOp /* The operand token for setting the span */ ){ - pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0); pOut->zStart = pPreOp->z; + pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0); pOut->zEnd = pOperand->zEnd; } } -expr(A) ::= NOT(B) expr(X). {spanUnaryPrefix(&A,pParse,@B,&X,&B);} -expr(A) ::= BITNOT(B) expr(X). {spanUnaryPrefix(&A,pParse,@B,&X,&B);} +expr(A) ::= NOT(B) expr(X). + {spanUnaryPrefix(&A,pParse,@B,&X,&B);/*A-overwrites-B*/} +expr(A) ::= BITNOT(B) expr(X). + {spanUnaryPrefix(&A,pParse,@B,&X,&B);/*A-overwrites-B*/} expr(A) ::= MINUS(B) expr(X). [BITNOT] - {spanUnaryPrefix(&A,pParse,TK_UMINUS,&X,&B);} + {spanUnaryPrefix(&A,pParse,TK_UMINUS,&X,&B);/*A-overwrites-B*/} expr(A) ::= PLUS(B) expr(X). [BITNOT] - {spanUnaryPrefix(&A,pParse,TK_UPLUS,&X,&B);} + {spanUnaryPrefix(&A,pParse,TK_UPLUS,&X,&B);/*A-overwrites-B*/} %type between_op {int} between_op(A) ::= BETWEEN. {A = 0;} between_op(A) ::= NOT BETWEEN. {A = 1;} -expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { +expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { ExprList *pList = sqlite3ExprListAppend(pParse,0, X.pExpr); pList = sqlite3ExprListAppend(pParse,pList, Y.pExpr); - A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, W.pExpr, 0, 0); + A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, A.pExpr, 0, 0); if( A.pExpr ){ A.pExpr->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - exprNot(pParse, N, &A.pExpr); - A.zStart = W.zStart; + exprNot(pParse, N, &A); A.zEnd = Y.zEnd; } %ifndef SQLITE_OMIT_SUBQUERY %type in_op {int} in_op(A) ::= IN. {A = 0;} in_op(A) ::= NOT IN. {A = 1;} - expr(A) ::= expr(X) in_op(N) LP exprlist(Y) RP(E). [IN] { + expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] { if( Y==0 ){ /* Expressions of the form ** @@ -1097,8 +1078,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ + sqlite3ExprDelete(pParse->db, A.pExpr); A.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[N]); - sqlite3ExprDelete(pParse->db, X.pExpr); }else if( Y->nExpr==1 ){ /* Expressions of the form: ** @@ -1125,21 +1106,21 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { pRHS->flags &= ~EP_Collate; pRHS->flags |= EP_Generic; } - A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, X.pExpr, pRHS, 0); + A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A.pExpr, pRHS, 0); }else{ - A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0); + A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0); if( A.pExpr ){ A.pExpr->x.pList = Y; sqlite3ExprSetHeightAndFlags(pParse, A.pExpr); }else{ sqlite3ExprListDelete(pParse->db, Y); } - exprNot(pParse, N, &A.pExpr); + exprNot(pParse, N, &A); } - A.zStart = X.zStart; A.zEnd = &E.z[E.n]; } expr(A) ::= LP(B) select(X) RP(E). { + spanSet(&A,&B,&E); /*A-overwrites-B*/ A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); if( A.pExpr ){ A.pExpr->x.pSelect = X; @@ -1148,11 +1129,9 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { }else{ sqlite3SelectDelete(pParse->db, X); } - A.zStart = B.z; - A.zEnd = &E.z[E.n]; } - expr(A) ::= expr(X) in_op(N) LP select(Y) RP(E). [IN] { - A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0); + expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] { + A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0); if( A.pExpr ){ A.pExpr->x.pSelect = Y; ExprSetProperty(A.pExpr, EP_xIsSelect|EP_Subquery); @@ -1160,13 +1139,12 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { }else{ sqlite3SelectDelete(pParse->db, Y); } - exprNot(pParse, N, &A.pExpr); - A.zStart = X.zStart; + exprNot(pParse, N, &A); A.zEnd = &E.z[E.n]; } - expr(A) ::= expr(X) in_op(N) nm(Y) dbnm(Z). [IN] { + expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z). [IN] { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z); - A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0); + A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0, 0); if( A.pExpr ){ A.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); ExprSetProperty(A.pExpr, EP_xIsSelect|EP_Subquery); @@ -1174,12 +1152,13 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { }else{ sqlite3SrcListDelete(pParse->db, pSrc); } - exprNot(pParse, N, &A.pExpr); - A.zStart = X.zStart; + exprNot(pParse, N, &A); A.zEnd = Z.z ? &Z.z[Z.n] : &Y.z[Y.n]; } expr(A) ::= EXISTS(B) LP select(Y) RP(E). { - Expr *p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); + Expr *p; + spanSet(&A,&B,&E); /*A-overwrites-B*/ + p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); if( p ){ p->x.pSelect = Y; ExprSetProperty(p, EP_xIsSelect|EP_Subquery); @@ -1187,13 +1166,12 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] { }else{ sqlite3SelectDelete(pParse->db, Y); } - A.zStart = B.z; - A.zEnd = &E.z[E.n]; } %endif SQLITE_OMIT_SUBQUERY /* CASE expressions */ expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). { + spanSet(&A,&C,&E); /*A-overwrites-C*/ A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0, 0); if( A.pExpr ){ A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y; @@ -1202,13 +1180,11 @@ expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). { sqlite3ExprListDelete(pParse->db, Y); sqlite3ExprDelete(pParse->db, Z); } - A.zStart = C.z; - A.zEnd = &E.z[E.n]; } %type case_exprlist {ExprList*} %destructor case_exprlist {sqlite3ExprListDelete(pParse->db, $$);} -case_exprlist(A) ::= case_exprlist(X) WHEN expr(Y) THEN expr(Z). { - A = sqlite3ExprListAppend(pParse,X, Y.pExpr); +case_exprlist(A) ::= case_exprlist(A) WHEN expr(Y) THEN expr(Z). { + A = sqlite3ExprListAppend(pParse,A, Y.pExpr); A = sqlite3ExprListAppend(pParse,A, Z.pExpr); } case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). { @@ -1221,7 +1197,7 @@ case_else(A) ::= ELSE expr(X). {A = X.pExpr;} case_else(A) ::= . {A = 0;} %type case_operand {Expr*} %destructor case_operand {sqlite3ExprDelete(pParse->db, $$);} -case_operand(A) ::= expr(X). {A = X.pExpr;} +case_operand(A) ::= expr(X). {A = X.pExpr; /*A-overwrites-X*/} case_operand(A) ::= . {A = 0;} %type exprlist {ExprList*} @@ -1229,12 +1205,12 @@ case_operand(A) ::= . {A = 0;} %type nexprlist {ExprList*} %destructor nexprlist {sqlite3ExprListDelete(pParse->db, $$);} -exprlist(A) ::= nexprlist(X). {A = X;} +exprlist(A) ::= nexprlist(A). exprlist(A) ::= . {A = 0;} -nexprlist(A) ::= nexprlist(X) COMMA expr(Y). - {A = sqlite3ExprListAppend(pParse,X,Y.pExpr);} +nexprlist(A) ::= nexprlist(A) COMMA expr(Y). + {A = sqlite3ExprListAppend(pParse,A,Y.pExpr);} nexprlist(A) ::= expr(Y). - {A = sqlite3ExprListAppend(pParse,0,Y.pExpr);} + {A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/} ///////////////////////////// The CREATE INDEX command /////////////////////// @@ -1298,11 +1274,11 @@ uniqueflag(A) ::= . {A = OE_None;} eidlist_opt(A) ::= . {A = 0;} eidlist_opt(A) ::= LP eidlist(X) RP. {A = X;} -eidlist(A) ::= eidlist(X) COMMA nm(Y) collate(C) sortorder(Z). { - A = parserAddExprIdListTerm(pParse, X, &Y, C, Z); +eidlist(A) ::= eidlist(A) COMMA nm(Y) collate(C) sortorder(Z). { + A = parserAddExprIdListTerm(pParse, A, &Y, C, Z); } eidlist(A) ::= nm(Y) collate(C) sortorder(Z). { - A = parserAddExprIdListTerm(pParse, 0, &Y, C, Z); + A = parserAddExprIdListTerm(pParse, 0, &Y, C, Z); /*A-overwrites-Y*/ } %type collate {int} @@ -1334,15 +1310,15 @@ cmd ::= PRAGMA nm(X) dbnm(Z) EQ minus_num(Y). cmd ::= PRAGMA nm(X) dbnm(Z) LP minus_num(Y) RP. {sqlite3Pragma(pParse,&X,&Z,&Y,1);} -nmnum(A) ::= plus_num(X). {A = X;} -nmnum(A) ::= nm(X). {A = X;} -nmnum(A) ::= ON(X). {A = X;} -nmnum(A) ::= DELETE(X). {A = X;} -nmnum(A) ::= DEFAULT(X). {A = X;} +nmnum(A) ::= plus_num(A). +nmnum(A) ::= nm(A). +nmnum(A) ::= ON(A). +nmnum(A) ::= DELETE(A). +nmnum(A) ::= DEFAULT(A). %endif SQLITE_OMIT_PRAGMA %token_class number INTEGER|FLOAT. plus_num(A) ::= PLUS number(X). {A = X;} -plus_num(A) ::= number(X). {A = X;} +plus_num(A) ::= number(A). minus_num(A) ::= MINUS number(X). {A = X;} //////////////////////////// The CREATE TRIGGER command ///////////////////// @@ -1359,7 +1335,7 @@ trigger_decl(A) ::= temp(T) TRIGGER ifnotexists(NOERR) nm(B) dbnm(Z) trigger_time(C) trigger_event(D) ON fullname(E) foreach_clause when_clause(G). { sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, E, G, T, NOERR); - A = (Z.n==0?B:Z); + A = (Z.n==0?B:Z); /*A-overwrites-T*/ } %type trigger_time {int} @@ -1370,9 +1346,9 @@ trigger_time(A) ::= . { A = TK_BEFORE; } %type trigger_event {struct TrigEvent} %destructor trigger_event {sqlite3IdListDelete(pParse->db, $$.b);} -trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;} -trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;} -trigger_event(A) ::= UPDATE OF idlist(X). {A.a = TK_UPDATE; A.b = X;} +trigger_event(A) ::= DELETE|INSERT(X). {A.a = @X; /*A-overwrites-X*/ A.b = 0;} +trigger_event(A) ::= UPDATE(X). {A.a = @X; /*A-overwrites-X*/ A.b = 0;} +trigger_event(A) ::= UPDATE OF idlist(X).{A.a = TK_UPDATE; A.b = X;} foreach_clause ::= . foreach_clause ::= FOR EACH ROW. @@ -1384,16 +1360,14 @@ when_clause(A) ::= WHEN expr(X). { A = X.pExpr; } %type trigger_cmd_list {TriggerStep*} %destructor trigger_cmd_list {sqlite3DeleteTriggerStep(pParse->db, $$);} -trigger_cmd_list(A) ::= trigger_cmd_list(Y) trigger_cmd(X) SEMI. { - assert( Y!=0 ); - Y->pLast->pNext = X; - Y->pLast = X; - A = Y; +trigger_cmd_list(A) ::= trigger_cmd_list(A) trigger_cmd(X) SEMI. { + assert( A!=0 ); + A->pLast->pNext = X; + A->pLast = X; } -trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { - assert( X!=0 ); - X->pLast = X; - A = X; +trigger_cmd_list(A) ::= trigger_cmd(A) SEMI. { + assert( A!=0 ); + A->pLast = A; } // Disallow qualified table names on INSERT, UPDATE, and DELETE statements @@ -1401,7 +1375,7 @@ trigger_cmd_list(A) ::= trigger_cmd(X) SEMI. { // the same database as the table that the trigger fires on. // %type trnm {Token} -trnm(A) ::= nm(X). {A = X;} +trnm(A) ::= nm(A). trnm(A) ::= nm DOT nm(X). { A = X; sqlite3ErrorMsg(pParse, @@ -1432,35 +1406,34 @@ tridxby ::= NOT INDEXED. { // UPDATE trigger_cmd(A) ::= UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z). - { A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); } + {A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R);} // INSERT trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S). - {A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);} + {A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);/*A-overwrites-R*/} // DELETE trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y). - {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);} + {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);} // SELECT -trigger_cmd(A) ::= select(X). {A = sqlite3TriggerSelectStep(pParse->db, X); } +trigger_cmd(A) ::= select(X). + {A = sqlite3TriggerSelectStep(pParse->db, X); /*A-overwrites-X*/} // The special RAISE expression that may occur in trigger programs expr(A) ::= RAISE(X) LP IGNORE RP(Y). { + spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); if( A.pExpr ){ A.pExpr->affinity = OE_Ignore; } - A.zStart = X.z; - A.zEnd = &Y.z[Y.n]; } expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). { + spanSet(&A,&X,&Y); /*A-overwrites-X*/ A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &Z); if( A.pExpr ) { A.pExpr->affinity = (char)T; } - A.zStart = X.z; - A.zEnd = &Y.z[Y.n]; } %endif !SQLITE_OMIT_TRIGGER @@ -1512,7 +1485,9 @@ cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);} cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). { sqlite3AlterRenameTable(pParse,X,&Z); } -cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). { +cmd ::= ALTER TABLE add_column_fullname + ADD kwcolumn_opt columnname(Y) carglist. { + Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &Y); } add_column_fullname ::= fullname(X). { @@ -1556,9 +1531,9 @@ with(A) ::= WITH wqlist(W). { A = W; } with(A) ::= WITH RECURSIVE wqlist(W). { A = W; } wqlist(A) ::= nm(X) eidlist_opt(Y) AS LP select(Z) RP. { - A = sqlite3WithAdd(pParse, 0, &X, Y, Z); + A = sqlite3WithAdd(pParse, 0, &X, Y, Z); /*A-overwrites-X*/ } -wqlist(A) ::= wqlist(W) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. { - A = sqlite3WithAdd(pParse, W, &X, Y, Z); +wqlist(A) ::= wqlist(A) COMMA nm(X) eidlist_opt(Y) AS LP select(Z) RP. { + A = sqlite3WithAdd(pParse, A, &X, Y, Z); } %endif SQLITE_OMIT_CTE diff --git a/src/pcache.c b/src/pcache.c index 5ac9d34a1e..61e7587422 100644 --- a/src/pcache.c +++ b/src/pcache.c @@ -191,7 +191,7 @@ int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), pCache->bPurgeable ); - if( pNew==0 ) return SQLITE_NOMEM; + if( pNew==0 ) return SQLITE_NOMEM_BKPT; sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); if( pCache->pCache ){ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); @@ -301,7 +301,7 @@ int sqlite3PcacheFetchStress( } } *ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); - return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK; + return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK; } /* diff --git a/src/pragma.c b/src/pragma.c index 2b4058ca16..b3edd9342e 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -993,6 +993,7 @@ void sqlite3Pragma( int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK; if( iLevel==0 ) iLevel = 1; pDb->safety_level = iLevel; + pDb->bSyncSet = 1; setAllPagerFlags(db); } } @@ -1066,6 +1067,7 @@ void sqlite3Pragma( setAllColumnNames(v, 6, azCol); assert( 6==ArraySize(azCol) ); sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ + const char *zName; if( IsHiddenColumn(pCol) ){ nHidden++; continue; @@ -1077,12 +1079,14 @@ void sqlite3Pragma( }else{ for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} } + assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN ); + zName = pCol->zName; sqlite3VdbeMultiLoad(v, 1, "issisi", i-nHidden, - pCol->zName, - pCol->zType ? pCol->zType : "", + zName, + sqlite3StrNext(zName), pCol->notNull ? 1 : 0, - pCol->zDflt, + pCol->pDflt ? pCol->pDflt->u.zToken : 0, k); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); } @@ -1103,14 +1107,14 @@ void sqlite3Pragma( sqlite3VdbeMultiLoad(v, 1, "ssii", pTab->zName, 0, - (int)sqlite3LogEstToInt(pTab->szTabRow), - (int)sqlite3LogEstToInt(pTab->nRowLogEst)); + pTab->szTabRow, + pTab->nRowLogEst); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3VdbeMultiLoad(v, 2, "sii", pIdx->zName, - (int)sqlite3LogEstToInt(pIdx->szIdxRow), - (int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0])); + pIdx->szIdxRow, + pIdx->aiRowLogEst[0]); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4); } } diff --git a/src/prepare.c b/src/prepare.c index f74aa52e0b..6685dfeaf7 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -33,7 +33,7 @@ static void corruptSchema( sqlite3DbFree(db, *pData->pzErrMsg); *pData->pzErrMsg = z; } - pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT; + pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT; } /* @@ -307,7 +307,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ #endif } if( db->mallocFailed ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; sqlite3ResetAllSchemasOfConnection(db); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ @@ -524,7 +524,7 @@ static int sqlite3Prepare( /* Allocate the parsing context */ pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto end_prepare; } pParse->pReprepare = pReprepare; @@ -601,7 +601,7 @@ static int sqlite3Prepare( schemaIsValid(pParse); } if( db->mallocFailed ){ - pParse->rc = SQLITE_NOMEM; + pParse->rc = SQLITE_NOMEM_BKPT; } if( pzTail ){ *pzTail = pParse->zTail; diff --git a/src/resolve.c b/src/resolve.c index 81bb712a2e..8e290af123 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -656,9 +656,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ notValid(pParse, pNC, "functions", NC_PartIdx); zId = pExpr->u.zToken; nId = sqlite3Strlen30(zId); - pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0); + pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); if( pDef==0 ){ - pDef = sqlite3FindFunction(pParse->db, zId, nId, -2, enc, 0); + pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0); if( pDef==0 ){ no_such_func = 1; }else{ diff --git a/src/select.c b/src/select.c index c3132c2325..c9bc389b2f 100644 --- a/src/select.c +++ b/src/select.c @@ -105,7 +105,7 @@ Select *sqlite3SelectNew( ExprList *pGroupBy, /* the GROUP BY clause */ Expr *pHaving, /* the HAVING clause */ ExprList *pOrderBy, /* the ORDER BY clause */ - u16 selFlags, /* Flag parameters, such as SF_Distinct */ + u32 selFlags, /* Flag parameters, such as SF_Distinct */ Expr *pLimit, /* LIMIT value. NULL means not used */ Expr *pOffset /* OFFSET value. NULL means no offset */ ){ @@ -1429,8 +1429,8 @@ static const char *columnTypeImpl( zType = "INTEGER"; zOrigCol = "rowid"; }else{ - zType = pTab->aCol[iCol].zType; zOrigCol = pTab->aCol[iCol].zName; + zType = sqlite3StrNext(zOrigCol); estWidth = pTab->aCol[iCol].szEst; } zOrigTab = pTab->zName; @@ -1442,7 +1442,7 @@ static const char *columnTypeImpl( if( iCol<0 ){ zType = "INTEGER"; }else{ - zType = pTab->aCol[iCol].zType; + zType = sqlite3StrNext(pTab->aCol[iCol].zName); estWidth = pTab->aCol[iCol].szEst; } #endif @@ -1688,7 +1688,7 @@ int sqlite3ColumnsFromExprList( sqlite3DbFree(db, aCol); *paCol = 0; *pnCol = 0; - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } return SQLITE_OK; } @@ -1727,10 +1727,7 @@ static void selectAddColumnTypeAndCollation( a = pSelect->pEList->a; for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ p = a[i].pExpr; - if( pCol->zType==0 ){ - pCol->zType = sqlite3DbStrDup(db, - columnType(&sNC, p,0,0,0, &pCol->szEst)); - } + columnType(&sNC, p, 0, 0, 0, &pCol->szEst); szAll += pCol->szEst; pCol->affinity = sqlite3ExprAffinity(p); if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB; @@ -1845,8 +1842,9 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeGoto(v, iBreak); - }else if( n>=0 && p->nSelectRow>(u64)n ){ - p->nSelectRow = n; + }else if( n>=0 && p->nSelectRow>sqlite3LogEst((u64)n) ){ + p->nSelectRow = sqlite3LogEst((u64)n); + p->selFlags |= SF_FixedLimit; } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); @@ -2224,7 +2222,6 @@ static int multiSelect( if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); - sqlite3VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } @@ -2287,12 +2284,12 @@ static int multiSelect( testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; - p->nSelectRow += pPrior->nSelectRow; + p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); if( pPrior->pLimit && sqlite3ExprIsInteger(pPrior->pLimit, &nLimit) - && nLimit>0 && p->nSelectRow > (u64)nLimit + && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) ){ - p->nSelectRow = nLimit; + p->nSelectRow = sqlite3LogEst((u64)nLimit); } if( addr ){ sqlite3VdbeJumpHere(v, addr); @@ -2364,7 +2361,9 @@ static int multiSelect( pDelete = p->pPrior; p->pPrior = pPrior; p->pOrderBy = 0; - if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow; + if( p->op==TK_UNION ){ + p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); + } sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; @@ -2499,7 +2498,7 @@ static int multiSelect( nCol = p->pEList->nExpr; pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); if( !pKeyInfo ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto multi_select_end; } for(i=0, apColl=pKeyInfo->aColl; iflags |= EP_IntValue; pNew->u.iValue = i; pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); @@ -3001,7 +3000,7 @@ static int multiSelectOrderBy( addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd); VdbeCoverage(v); sqlite3VdbeGoto(v, addrEofA); - p->nSelectRow += pPrior->nSelectRow; + p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); } /* Generate a subroutine to run when the results from select B @@ -4091,7 +4090,7 @@ static int withExpand( pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0); - if( db->mallocFailed ) return SQLITE_NOMEM; + if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; assert( pFrom->pSelect ); /* Check if this is a recursive CTE. */ @@ -4987,7 +4986,7 @@ int sqlite3Select( sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); - pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow); + pItem->pTab->nRowLogEst = pSub->nSelectRow; pItem->fg.viaCoroutine = 1; pItem->regResult = dest.iSdst; sqlite3VdbeEndCoroutine(v, pItem->regReturn); @@ -5018,7 +5017,7 @@ int sqlite3Select( sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); - pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow); + pItem->pTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); VdbeComment((v, "end %s", pItem->pTab->zName)); @@ -5101,7 +5100,7 @@ int sqlite3Select( /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); - p->nSelectRow = LARGEST_INT64; + p->nSelectRow = 320; /* 4 billion rows */ computeLimitRegisters(pParse, p, iEnd); if( p->iLimit==0 && sSort.addrSortIndex>=0 ){ sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen); @@ -5125,10 +5124,12 @@ int sqlite3Select( if( !isAgg && pGroupBy==0 ){ /* No aggregate functions and no GROUP BY clause */ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0); + assert( WHERE_USE_LIMIT==SF_FixedLimit ); + wctrlFlags |= p->selFlags & SF_FixedLimit; /* Begin the database scan. */ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, - p->pEList, wctrlFlags, 0); + p->pEList, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo); @@ -5188,9 +5189,11 @@ int sqlite3Select( for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->u.x.iAlias = 0; } - if( p->nSelectRow>100 ) p->nSelectRow = 100; + assert( 66==sqlite3LogEst(100) ); + if( p->nSelectRow>66 ) p->nSelectRow = 66; }else{ - p->nSelectRow = 1; + assert( 0==sqlite3LogEst(1) ); + p->nSelectRow = 0; } /* If there is both a GROUP BY and an ORDER BY clause and they are diff --git a/src/shell.c b/src/shell.c index e2fe41291d..675e2ffd19 100644 --- a/src/shell.c +++ b/src/shell.c @@ -593,7 +593,8 @@ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ int echoOn; /* True to echo input commands */ - int autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ + int autoExplain; /* Automatically turn on .explain mode */ + int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ int bRecommend; /* Instead of sqlite3_exec(), recommend indexes */ int statsOn; /* True to display memory stats before each finalize */ int scanstatsOn; /* True to display scan stats before each finalize */ @@ -605,6 +606,8 @@ struct ShellState { FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ + int cMode; /* temporary output mode for the current query */ + int normalMode; /* Output mode before ".explain on" */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ @@ -615,7 +618,6 @@ struct ShellState { int actualWidth[100]; /* Actual width of each column */ 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 */ const char *zDbFilename; /* name of the database file */ char *zFreeOnClose; /* Filename to free when closing */ @@ -884,7 +886,7 @@ static int shell_callback( int i; ShellState *p = (ShellState*)pArg; - switch( p->mode ){ + switch( p->cMode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; @@ -901,11 +903,24 @@ static int shell_callback( } case MODE_Explain: case MODE_Column: { + static const int aExplainWidths[] = {4, 13, 4, 4, 4, 13, 2, 13}; + const int *colWidth; + int showHdr; + char *rowSep; + if( p->cMode==MODE_Column ){ + colWidth = p->colWidth; + showHdr = p->showHeader; + rowSep = p->rowSeparator; + }else{ + colWidth = aExplainWidths; + showHdr = 1; + rowSep = SEP_Row; + } if( p->cnt++==0 ){ for(i=0; icolWidth) ){ - w = p->colWidth[i]; + w = colWidth[i]; }else{ w = 0; } @@ -918,17 +933,17 @@ static int shell_callback( if( iactualWidth) ){ p->actualWidth[i] = w; } - if( p->showHeader ){ + if( showHdr ){ if( w<0 ){ utf8_printf(p->out,"%*.*s%s",-w,-w,azCol[i], - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); }else{ utf8_printf(p->out,"%-*.*s%s",w,w,azCol[i], - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); } } } - if( p->showHeader ){ + if( showHdr ){ for(i=0; iactualWidth) ){ @@ -940,7 +955,7 @@ static int shell_callback( utf8_printf(p->out,"%-*.*s%s",w,w, "----------------------------------------------------------" "----------------------------------------------------------", - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); } } } @@ -952,7 +967,7 @@ static int shell_callback( }else{ w = 10; } - if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ + if( p->cMode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ w = strlen30(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ @@ -964,11 +979,11 @@ static int shell_callback( if( w<0 ){ utf8_printf(p->out,"%*.*s%s",-w,-w, azArg[i] ? azArg[i] : p->nullValue, - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); }else{ utf8_printf(p->out,"%-*.*s%s",w,w, azArg[i] ? azArg[i] : p->nullValue, - i==nArg-1 ? p->rowSeparator : " "); + i==nArg-1 ? rowSep : " "); } } break; @@ -988,7 +1003,7 @@ static int shell_callback( utf8_printf(p->out, "%s", z); if( iout, "%s", p->colSeparator); - }else if( p->mode==MODE_Semi ){ + }else if( p->cMode==MODE_Semi ){ utf8_printf(p->out, ";%s", p->rowSeparator); }else{ utf8_printf(p->out, "%s", p->rowSeparator); @@ -1271,6 +1286,43 @@ static char *save_err_msg( return zErrMsg; } +#ifdef __linux__ +/* +** Attempt to display I/O stats on Linux using /proc/PID/io +*/ +static void displayLinuxIoStats(FILE *out){ + FILE *in; + char z[200]; + sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); + in = fopen(z, "rb"); + if( in==0 ) return; + while( fgets(z, sizeof(z), in)!=0 ){ + static const struct { + const char *zPattern; + const char *zDesc; + } aTrans[] = { + { "rchar: ", "Bytes received by read():" }, + { "wchar: ", "Bytes sent to write():" }, + { "syscr: ", "Read() system calls:" }, + { "syscw: ", "Write() system calls:" }, + { "read_bytes: ", "Bytes read from storage:" }, + { "write_bytes: ", "Bytes written to storage:" }, + { "cancelled_write_bytes: ", "Cancelled write bytes:" }, + }; + int i; + for(i=0; iout, "Virtual Machine Steps: %d\n", iCur); } +#ifdef __linux__ + displayLinuxIoStats(pArg->out); +#endif + /* Do not remove this machine readable comment: extra-stats-output-here */ return 0; @@ -1493,10 +1549,17 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ /* Try to figure out if this is really an EXPLAIN statement. If this ** cannot be verified, return early. */ + if( sqlite3_column_count(pSql)!=8 ){ + p->cMode = p->mode; + return; + } zSql = sqlite3_sql(pSql); if( zSql==0 ) return; for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); - if( sqlite3_strnicmp(z, "explain", 7) ) return; + if( sqlite3_strnicmp(z, "explain", 7) ){ + p->cMode = p->mode; + return; + } for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ int i; @@ -1513,6 +1576,20 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ /* Grow the p->aiIndent array as required */ if( iOp>=nAlloc ){ + if( iOp==0 ){ + /* Do further verfication that this is explain output. Abort if + ** it is not */ + static const char *explainCols[] = { + "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" }; + int jj; + for(jj=0; jjcMode = p->mode; + sqlite3_reset(pSql); + return; + } + } + } nAlloc += 100; p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); @@ -1636,10 +1713,20 @@ static int shell_exec( sqlite3_free(zEQP); } - /* If the shell is currently in ".explain" mode, gather the extra - ** data required to add indents to the output.*/ - if( pArg && pArg->mode==MODE_Explain ){ - explain_data_prepare(pArg, pStmt); + if( pArg ){ + pArg->cMode = pArg->mode; + if( pArg->autoExplain + && sqlite3_column_count(pStmt)==8 + && sqlite3_strlike("%EXPLAIN%", sqlite3_sql(pStmt),0)==0 + ){ + pArg->cMode = MODE_Explain; + } + + /* If the shell is currently in ".explain" mode, gather the extra + ** data required to add indents to the output.*/ + if( pArg->cMode==MODE_Explain ){ + explain_data_prepare(pArg, pStmt); + } } /* perform the first step. this will tell us if we @@ -1669,7 +1756,7 @@ static int shell_exec( /* extract the data and data types */ for(i=0; imode==MODE_Insert ){ + if( x==SQLITE_BLOB && pArg && pArg->cMode==MODE_Insert ){ azVals[i] = ""; }else{ azVals[i] = (char*)sqlite3_column_text(pStmt, i); @@ -1889,8 +1976,7 @@ static char zHelp[] = ".echo on|off Turn command echo on or off\n" ".eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN\n" ".exit Exit this program\n" - ".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n" - " With no args, it turns EXPLAIN on.\n" + ".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n" ".fullschema Show schema and the content of sqlite_stat tables\n" ".headers on|off Turn display of headers on or off\n" ".help Show this message\n" @@ -1934,7 +2020,7 @@ static char zHelp[] = " 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" + ".stats ?on|off? Show stats or turn stats on or off\n" ".system CMD ARGS... Run CMD ARGS... in a system shell\n" ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" @@ -2877,7 +2963,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 1; - data.mode = MODE_Column; + data.cMode = data.mode = MODE_Column; data.colWidth[0] = 3; data.colWidth[1] = 15; data.colWidth[2] = 58; @@ -2972,37 +3058,24 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ - int val = nArg>=2 ? booleanValue(azArg[1]) : 1; - if(val == 1) { - if(!p->normalMode.valid) { - p->normalMode.valid = 1; - p->normalMode.mode = p->mode; - p->normalMode.showHeader = p->showHeader; - memcpy(p->normalMode.colWidth,p->colWidth,sizeof(p->colWidth)); + int val = 1; + if( nArg>=2 ){ + if( strcmp(azArg[1],"auto")==0 ){ + val = 99; + }else{ + val = booleanValue(azArg[1]); } - /* We could put this code under the !p->explainValid - ** condition so that it does not execute if we are already in - ** explain mode. However, always executing it allows us an easy - ** was to reset to explain mode in case the user previously - ** did an .explain followed by a .width, .mode or .header - ** command. - */ + } + if( val==1 && p->mode!=MODE_Explain ){ + p->normalMode = p->mode; p->mode = MODE_Explain; - p->showHeader = 1; - memset(p->colWidth,0,sizeof(p->colWidth)); - p->colWidth[0] = 4; /* addr */ - p->colWidth[1] = 13; /* opcode */ - p->colWidth[2] = 4; /* P1 */ - p->colWidth[3] = 4; /* P2 */ - p->colWidth[4] = 4; /* P3 */ - p->colWidth[5] = 13; /* P4 */ - p->colWidth[6] = 2; /* P5 */ - p->colWidth[7] = 13; /* Comment */ - }else if (p->normalMode.valid) { - p->normalMode.valid = 0; - p->mode = p->normalMode.mode; - p->showHeader = p->normalMode.showHeader; - memcpy(p->colWidth,p->normalMode.colWidth,sizeof(p->colWidth)); + p->autoExplain = 0; + }else if( val==0 ){ + if( p->mode==MODE_Explain ) p->mode = p->normalMode; + p->autoExplain = 0; + }else if( val==99 ){ + if( p->mode==MODE_Explain ) p->mode = p->normalMode; + p->autoExplain = 1; } }else @@ -3018,7 +3091,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; - data.mode = MODE_Semi; + data.cMode = data.mode = MODE_Semi; rc = sqlite3_exec(p->db, "SELECT sql FROM" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" @@ -3043,7 +3116,7 @@ static int do_meta_command(char *zLine, ShellState *p){ raw_printf(p->out, "ANALYZE sqlite_master;\n"); sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", callback, &data, &zErrMsg); - data.mode = MODE_Insert; + data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(p->db, "SELECT * FROM sqlite_stat1", shell_callback, &data,&zErrMsg); @@ -3161,7 +3234,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; while( xRead(&sCtx) ){ - zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); + zCreate = sqlite3_mprintf("%z%c\n \"%w\" TEXT", zCreate, cSep, sCtx.z); cSep = ','; if( sCtx.cTerm!=sCtx.cColSep ) break; } @@ -3275,7 +3348,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; - data.mode = MODE_List; + data.cMode = data.mode = MODE_List; if( nArg==1 ){ rc = sqlite3_exec(p->db, "SELECT name FROM sqlite_master " @@ -3461,6 +3534,7 @@ static int do_meta_command(char *zLine, ShellState *p){ "ascii column csv html insert line list tabs tcl\n"); rc = 1; } + p->cMode = p->mode; }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ @@ -3659,7 +3733,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; - data.mode = MODE_Semi; + data.cMode = data.mode = MODE_Semi; if( nArg==2 ){ int i; for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]); @@ -3807,7 +3881,8 @@ static int do_meta_command(char *zLine, ShellState *p){ } utf8_printf(p->out, "%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); utf8_printf(p->out, "%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); - utf8_printf(p->out,"%9.9s: %s\n","explain",p->normalMode.valid?"on":"off"); + utf8_printf(p->out, "%12.12s: %s\n","explain", + p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); utf8_printf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); utf8_printf(p->out, "%12.12s: ", "nullvalue"); @@ -3832,8 +3907,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){ if( nArg==2 ){ p->statsOn = booleanValue(azArg[1]); + }else if( nArg==1 ){ + display_stats(p->db, p, 0); }else{ - raw_printf(stderr, "Usage: .stats on|off\n"); + raw_printf(stderr, "Usage: .stats ?on|off?\n"); rc = 1; } }else @@ -4606,7 +4683,8 @@ static void usage(int showDetail){ */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); - data->mode = MODE_List; + data->normalMode = data->cMode = data->mode = MODE_List; + data->autoExplain = 1; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; @@ -4942,6 +5020,7 @@ int SQLITE_CDECL main(int argc, char **argv){ raw_printf(stderr,"Use -help for a list of options.\n"); return 1; } + data.cMode = data.mode; } if( !readStdin ){ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 03d3d72f6f..960925ff62 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -347,7 +347,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** from [sqlite3_malloc()] and passed back through the 5th parameter. ** To avoid memory leaks, the application should invoke [sqlite3_free()] ** on error message strings returned through the 5th parameter of -** of sqlite3_exec() after the error message string is no longer needed. +** sqlite3_exec() after the error message string is no longer needed. ** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors ** occur, then sqlite3_exec() sets the pointer in its 5th parameter to ** NULL before returning. @@ -1228,7 +1228,7 @@ struct sqlite3_vfs { const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); /* ** The methods above are in versions 1 through 3 of the sqlite_vfs object. - ** New fields may be appended in figure versions. The iVersion + ** New fields may be appended in future versions. The iVersion ** value will increment whenever this happens. */ }; @@ -1820,6 +1820,20 @@ struct sqlite3_mem_methods { ** 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. +** +** [[SQLITE_CONFIG_STMTJRNL_SPILL]] +**
SQLITE_CONFIG_STMTJRNL_SPILL +**
^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which +** becomes the [statement journal] spill-to-disk threshold. +** [Statement journals] are held in memory until their size (in bytes) +** exceeds this threshold, at which point they are written to disk. +** Or if the threshold is -1, statement journals are always held +** exclusively in memory. +** Since many statement journals never become large, setting the spill +** threshold to a value such as 64KiB can greatly reduce the amount of +** I/O required to support statement rollback. +** The default value for this setting is controlled by the +** [SQLITE_STMTJRNL_SPILL] compile-time option. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1847,6 +1861,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ +#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ /* ** CAPI3REF: Database Connection Configuration Options @@ -1904,11 +1919,25 @@ struct sqlite3_mem_methods { ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back.
** +**
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
+**
^This option is used to enable or disable the two-argument +** version of the [fts3_tokenizer()] function which is part of the +** [FTS3] full-text search engine extension. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable fts3_tokenizer() or +** positive to enable fts3_tokenizer() or negative to leave the setting +** unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the new setting is not reported back.
+** ** */ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ -#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ -#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_WHEREINFO 1004 /* xWhereInfo void* */ #define SQLITE_WHEREINFO_TABLE 1 @@ -7498,7 +7527,7 @@ void sqlite3_log(int iErrCode, const char *zFormat, ...); ** previously registered write-ahead log callback. ^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. +** overwrite any prior [sqlite3_wal_hook()] settings. */ void *sqlite3_wal_hook( sqlite3*, @@ -7963,7 +7992,11 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_get( ** the first operation, apart from other sqlite3_snapshot_open() calls, ** following the [BEGIN] that starts a new read transaction. ** ^A [snapshot] will fail to open if it has been overwritten by a -** [checkpoint]. +** [checkpoint]. +** ^A [snapshot] will fail to open if the database connection D has not +** previously completed at least one read operation against the database +** file. (Hint: Run "[PRAGMA application_id]" against a newly opened +** database connection in order to make it ready to use snapshots.) ** ** The [sqlite3_snapshot_open()] interface is only available when the ** SQLITE_ENABLE_SNAPSHOT compile-time option is used. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 57c58195e0..ed999dc88c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -15,6 +15,14 @@ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ +/* +** Make sure that rand_s() is available on Windows systems with MSVC 2005 +** or higher. +*/ +#if defined(_MSC_VER) && _MSC_VER>=1400 +# define _CRT_RAND_S +#endif + /* ** Include the header file used to customize the compiler options for MSVC. ** This should be done first so that it can successfully prevent spurious @@ -149,7 +157,7 @@ ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** -** The correct "ANSI" way to do this is to use the intptr_t type. +** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. @@ -316,7 +324,7 @@ ** is set. Thus NDEBUG becomes an opt-in rather than an opt-out ** feature. */ -#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif #if defined(NDEBUG) && defined(SQLITE_DEBUG) @@ -331,7 +339,7 @@ #endif /* -** The testcase() macro is used to aid in coverage testing. When +** The testcase() macro is used to aid in coverage testing. When ** doing coverage testing, the condition inside the argument to ** testcase() must be evaluated both true and false in order to ** get full branch coverage. The testcase() macro is inserted @@ -377,7 +385,7 @@ #endif /* -** The ALWAYS and NEVER macros surround boolean expressions which +** The ALWAYS and NEVER macros surround boolean expressions which ** are intended to always be true or false, respectively. Such ** expressions could be omitted from the code completely. But they ** are included in a few cases in order to enhance the resilience @@ -490,7 +498,7 @@ /* ** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 -** afterward. Having this macro allows us to cause the C compiler +** afterward. Having this macro allows us to cause the C compiler ** to omit code used by TEMP tables without messy #ifndef statements. */ #ifdef SQLITE_OMIT_TEMPDB @@ -529,7 +537,7 @@ /* ** If no value has been provided for SQLITE_MAX_WORKER_THREADS, or if -** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it +** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it ** to zero. */ #if SQLITE_TEMP_STORE==3 || SQLITE_THREADSAFE==0 @@ -677,7 +685,7 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ ** 4 -> 20 1000 -> 99 1048576 -> 200 ** 10 -> 33 1024 -> 100 4294967296 -> 320 ** -** The LogEst can be negative to indicate fractional values. +** The LogEst can be negative to indicate fractional values. ** Examples: ** ** 0.5 -> -10 0.1 -> -33 0.0625 -> -40 @@ -743,7 +751,7 @@ typedef INT16_TYPE LogEst; #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) -/* +/* ** Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. */ @@ -837,7 +845,7 @@ typedef INT16_TYPE LogEst; /* ** An instance of the following structure is used to store the busy-handler -** callback for a given sqlite handle. +** callback for a given sqlite handle. ** ** The sqlite.busyHandler member of the sqlite struct contains the busy ** callback for the database handle. Each pager opened via the sqlite @@ -882,9 +890,9 @@ struct BusyHandler { /* ** The following value as a destructor means to use sqlite3DbFree(). -** The sqlite3DbFree() routine requires two parameters instead of the -** one parameter that destructors normally want. So we have to introduce -** this magic value that the code knows to handle differently. Any +** The sqlite3DbFree() routine requires two parameters instead of the +** one parameter that destructors normally want. So we have to introduce +** this magic value that the code knows to handle differently. Any ** pointer will work here as long as it is distinct from SQLITE_STATIC ** and SQLITE_TRANSIENT. */ @@ -911,16 +919,16 @@ struct BusyHandler { int sqlite3_wsd_init(int N, int J); void *sqlite3_wsd_find(void *K, int L); #else - #define SQLITE_WSD + #define SQLITE_WSD #define GLOBAL(t,v) v #define sqlite3GlobalConfig sqlite3Config #endif /* ** The following macros are used to suppress compiler warnings and to -** make it clear to human readers when a function parameter is deliberately +** make it clear to human readers when a function parameter is deliberately ** left unused within the body of a function. This usually happens when -** a function is called via a function pointer. For example the +** a function is called via a function pointer. For example the ** implementation of an SQL aggregate step callback may not use the ** parameter indicating the number of arguments passed to the aggregate, ** if it knows that this is enforced elsewhere. @@ -986,7 +994,7 @@ typedef struct WhereInfo WhereInfo; typedef struct With With; /* -** Defer sourcing vdbe.h and btree.h until after the "u8" and +** Defer sourcing vdbe.h and btree.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ @@ -994,10 +1002,39 @@ typedef struct With With; #include "vdbe.h" #include "pager.h" #include "pcache.h" - #include "os.h" #include "mutex.h" +/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default +** synchronous setting to EXTRA. It is no longer supported. +*/ +#ifdef SQLITE_EXTRA_DURABLE +# warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE +# define SQLITE_DEFAULT_SYNCHRONOUS 3 +#endif + +/* +** Default synchronous levels. +** +** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ +** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1. +** +** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS +** OFF 1 0 +** NORMAL 2 1 +** FULL 3 2 +** EXTRA 4 3 +** +** The "PRAGMA synchronous" statement also uses the zero-based numbers. +** In other words, the zero-based numbers are used for all external interfaces +** and the one-based values are used internally. +*/ +#ifndef SQLITE_DEFAULT_SYNCHRONOUS +# define SQLITE_DEFAULT_SYNCHRONOUS (PAGER_SYNCHRONOUS_FULL-1) +#endif +#ifndef SQLITE_DEFAULT_WAL_SYNCHRONOUS +# define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS +#endif /* ** Each database file to be accessed by the system is an instance @@ -1010,6 +1047,7 @@ struct Db { char *zName; /* Name of this database */ Btree *pBt; /* The B*Tree structure for this database file */ u8 safety_level; /* How aggressive at syncing data to disk */ + u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */ Schema *pSchema; /* Pointer to database schema (possibly shared) */ }; @@ -1020,7 +1058,7 @@ struct Db { ** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. ** In shared cache mode, a single Schema object can be shared by multiple ** Btrees that refer to the same underlying BtShared object. -** +** ** Schema objects are automatically deallocated when the last Btree that ** references them is destroyed. The TEMP Schema is manually freed by ** sqlite3_close(). @@ -1045,7 +1083,7 @@ struct Schema { }; /* -** These macros can be used to test, set, or clear bits in the +** These macros can be used to test, set, or clear bits in the ** Db.pSchema->flags field. */ #define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->schemaFlags&(P))==(P)) @@ -1109,13 +1147,15 @@ struct LookasideSlot { }; /* -** A hash table for function definitions. +** A hash table for built-in function definitions. (Application-defined +** functions use a regular table table from hash.h.) ** ** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. -** Collisions are on the FuncDef.pHash chain. +** Collisions are on the FuncDef.u.pHash chain. */ +#define SQLITE_FUNC_HASH_SZ 23 struct FuncDefHash { - FuncDef *a[23]; /* Hash table for functions */ + FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */ }; #ifdef SQLITE_USER_AUTHENTICATION @@ -1208,9 +1248,9 @@ struct sqlite3 { void *pTraceArg; /* Argument to the trace function */ void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ - void *pCommitArg; /* Argument to xCommitCallback() */ + void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ - void *pRollbackArg; /* Argument to xRollbackCallback() */ + void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); @@ -1243,7 +1283,7 @@ struct sqlite3 { VTable **aVTrans; /* Virtual tables with open transactions */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ #endif - FuncDefHash aFunc; /* Hash table of connection functions */ + Hash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ BusyHandler busyHandler; /* Busy callback */ Db aDbStatic[2]; /* Static space for the 2 default backends */ @@ -1255,8 +1295,8 @@ struct sqlite3 { i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY - /* The following variables are all protected by the STATIC_MASTER - ** mutex, not by sqlite3.mutex. They are used by code in notify.c. + /* The following variables are all protected by the STATIC_MASTER + ** mutex, not by sqlite3.mutex. They are used by code in notify.c. ** ** When X.pUnlockConnection==Y, that means that X is waiting for Y to ** unlock so that it can proceed. @@ -1321,6 +1361,7 @@ struct sqlite3 { #define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ #define SQLITE_Vacuum 0x08000000 /* Currently in a VACUUM */ #define SQLITE_CellSizeCk 0x10000000 /* Check btree cell sizes on load */ +#define SQLITE_Fts3Tokenizer 0x20000000 /* Enable fts3_tokenizer(2) */ /* @@ -1374,27 +1415,33 @@ struct sqlite3 { /* ** Each SQL function is defined by an instance of the following -** structure. A pointer to this structure is stored in the sqlite.aFunc -** hash table. When multiple functions have the same name, the hash table -** points to a linked list of these structures. +** structure. For global built-in functions (ex: substr(), max(), count()) +** a pointer to this structure is held in the sqlite3BuiltinFunctions object. +** For per-connection application-defined functions, a pointer to this +** structure is held in the db->aHash hash table. +** +** The u.pHash field is used by the global built-ins. The u.pDestructor +** field is used by per-connection app-def functions. */ struct FuncDef { - i16 nArg; /* Number of arguments. -1 means unlimited */ + i8 nArg; /* Number of arguments. -1 means unlimited */ u16 funcFlags; /* Some combination of SQLITE_FUNC_* */ void *pUserData; /* User data parameter */ FuncDef *pNext; /* Next function with same name */ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */ void (*xFinalize)(sqlite3_context*); /* Agg finalizer */ - char *zName; /* SQL name of the function. */ - FuncDef *pHash; /* Next with a different name but the same hash */ - FuncDestructor *pDestructor; /* Reference counted destructor function */ + const char *zName; /* SQL name of the function. */ + union { + FuncDef *pHash; /* Next with a different name but the same hash */ + FuncDestructor *pDestructor; /* Reference counted destructor function */ + } u; }; /* ** This structure encapsulates a user-function destructor callback (as ** configured using create_function_v2()) and a reference counter. When ** create_function_v2() is called to create a function with a destructor, -** a single object of this type is allocated. FuncDestructor.nRef is set to +** a single object of this type is allocated. FuncDestructor.nRef is set to ** the number of FuncDef objects created (either 1 or 3, depending on whether ** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor ** member of each of the new FuncDef objects is set to point to the allocated @@ -1436,10 +1483,10 @@ struct FuncDestructor { ** used to create the initializers for the FuncDef structures. ** ** FUNCTION(zName, nArg, iArg, bNC, xFunc) -** Used to create a scalar function definition of a function zName +** Used to create a scalar function definition of a function zName ** implemented by C function xFunc that accepts nArg arguments. The ** value passed as iArg is cast to a (void*) and made available -** as the user-data (sqlite3_user_data()) for the function. If +** as the user-data (sqlite3_user_data()) for the function. If ** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. ** ** VFUNCTION(zName, nArg, iArg, bNC, xFunc) @@ -1458,8 +1505,8 @@ struct FuncDestructor { ** FUNCTION(). ** ** LIKEFUNC(zName, nArg, pArg, flags) -** Used to create a scalar function definition of a function zName -** that accepts nArg arguments and is implemented by a call to C +** Used to create a scalar function definition of a function zName +** that accepts nArg arguments and is implemented by a call to C ** function likeFunc. Argument pArg is cast to a (void *) and made ** available as the function user-data (sqlite3_user_data()). The ** FuncDef.flags variable is set to the value passed as the flags @@ -1467,28 +1514,28 @@ struct FuncDestructor { */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0} + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0} + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0} + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0} + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} } #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \ - pArg, 0, xFunc, 0, #zName, 0, 0} + pArg, 0, xFunc, 0, #zName, } #define LIKEFUNC(zName, nArg, arg, flags) \ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \ - (void *)arg, 0, likeFunc, 0, #zName, 0, 0} + (void *)arg, 0, likeFunc, 0, #zName, {0} } #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName,0,0} + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}} #define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ - SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName,0,0} + SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}} /* ** All current savepoints are stored in a linked list starting at @@ -1530,10 +1577,8 @@ struct Module { ** of this structure. */ struct Column { - char *zName; /* Name of this column */ + char *zName; /* Name of this column, \000, then the type */ Expr *pDflt; /* Default value of this column */ - char *zDflt; /* Original text of the default value */ - char *zType; /* Data type for this column */ char *zColl; /* Collating sequence. If NULL, use the default */ u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ char affinity; /* One of the SQLITE_AFF_... values */ @@ -1575,7 +1620,7 @@ struct CollSeq { ** ** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and ** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve -** the speed a little by numbering the values consecutively. +** the speed a little by numbering the values consecutively. ** ** But rather than start with 0 or 1, we begin with 'A'. That way, ** when multiple affinity types are concatenated into a string and @@ -1594,7 +1639,7 @@ struct CollSeq { /* ** The SQLITE_AFF_MASK values masks off the significant bits of an -** affinity value. +** affinity value. */ #define SQLITE_AFF_MASK 0x47 @@ -1614,20 +1659,20 @@ struct CollSeq { /* ** An object of this type is created for each virtual table present in -** the database schema. +** the database schema. ** ** If the database schema is shared, then there is one instance of this ** structure for each database connection (sqlite3*) that uses the shared ** schema. This is because each database connection requires its own unique -** instance of the sqlite3_vtab* handle used to access the virtual table -** implementation. sqlite3_vtab* handles can not be shared between -** database connections, even when the rest of the in-memory database +** instance of the sqlite3_vtab* handle used to access the virtual table +** implementation. sqlite3_vtab* handles can not be shared between +** database connections, even when the rest of the in-memory database ** schema is shared, as the implementation often stores the database ** connection handle passed to it via the xConnect() or xCreate() method ** during initialization internally. This database connection handle may -** then be used by the virtual table implementation to access real tables -** within the database. So that they appear as part of the callers -** transaction, these accesses need to be made via the same database +** then be used by the virtual table implementation to access real tables +** within the database. So that they appear as part of the callers +** transaction, these accesses need to be made via the same database ** connection as that used to execute SQL operations on the virtual table. ** ** All VTable objects that correspond to a single table in a shared @@ -1639,19 +1684,19 @@ struct CollSeq { ** sqlite3_vtab* handle in the compiled query. ** ** When an in-memory Table object is deleted (for example when the -** schema is being reloaded for some reason), the VTable objects are not -** deleted and the sqlite3_vtab* handles are not xDisconnect()ed +** schema is being reloaded for some reason), the VTable objects are not +** deleted and the sqlite3_vtab* handles are not xDisconnect()ed ** immediately. Instead, they are moved from the Table.pVTable list to ** another linked list headed by the sqlite3.pDisconnect member of the -** corresponding sqlite3 structure. They are then deleted/xDisconnected +** corresponding sqlite3 structure. They are then deleted/xDisconnected ** next time a statement is prepared using said sqlite3*. This is done ** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. ** Refer to comments above function sqlite3VtabUnlockList() for an ** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect ** list without holding the corresponding sqlite3.mutex mutex. ** -** The memory for objects of this type is always allocated by -** sqlite3DbMalloc(), using the connection handle stored in VTable.db as +** The memory for objects of this type is always allocated by +** sqlite3DbMalloc(), using the connection handle stored in VTable.db as ** the first argument. */ struct VTable { @@ -1819,7 +1864,7 @@ struct FKey { ** key is set to NULL. CASCADE means that a DELETE or UPDATE of the ** referenced table row is propagated into the row that holds the ** foreign key. -** +** ** The following symbolic values are used to record which type ** of action to take. */ @@ -1840,7 +1885,7 @@ struct FKey { /* ** An instance of the following structure is passed as the first -** argument to sqlite3VdbeKeyCompare and is used to control the +** argument to sqlite3VdbeKeyCompare and is used to control the ** comparison of the two index keys. ** ** Note that aSortOrder[] and aColl[] have nField+1 slots. There @@ -1881,7 +1926,7 @@ struct KeyInfo { ** The key comparison functions actually return default_rc when they find ** an equals comparison. default_rc can be -1, 0, or +1. If there are ** multiple entries in the b-tree with the same key (when only looking -** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to +** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to ** cause the search to find the last match, or +1 to cause the search to ** find the first match. ** @@ -1918,7 +1963,7 @@ struct UnpackedRecord { ** In the Table structure describing Ex1, nCol==3 because there are ** three columns in the table. In the Index structure describing ** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. -** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the +** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the ** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. ** The second column to be indexed (c1) has an index of 0 in ** Ex1.aCol[], hence Ex2.aiColumn[1]==0. @@ -1926,7 +1971,7 @@ struct UnpackedRecord { ** The Index.onError field determines whether or not the indexed columns ** must be unique and what to do if they are not. When Index.onError=OE_None, ** it means this is not a unique index. Otherwise it is a unique index -** and the value of Index.onError indicate the which conflict resolution +** and the value of Index.onError indicate the which conflict resolution ** algorithm to employ whenever an attempt is made to insert a non-unique ** element. ** @@ -1991,7 +2036,7 @@ struct Index { #define XN_EXPR (-2) /* Indexed column is an expression */ /* -** Each sample stored in the sqlite_stat3 table is represented in memory +** Each sample stored in the sqlite_stat3 table is represented in memory ** using a structure of this type. See documentation at the top of the ** analyze.c source file for additional information. */ @@ -2086,9 +2131,9 @@ typedef int ynVar; ** to represent the greater-than-or-equal-to operator in the expression ** tree. ** -** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, +** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, ** or TK_STRING), then Expr.token contains the text of the SQL literal. If -** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** the expression is a variable (TK_VARIABLE), then Expr.token contains the ** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), ** then Expr.token contains the name of the function. ** @@ -2099,7 +2144,7 @@ typedef int ynVar; ** a CASE expression or an IN expression of the form " IN (, ...)". ** Expr.x.pSelect is used if the expression is a sub-select or an expression of ** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the -** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is +** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is ** valid. ** ** An expression of the form ID or ID.ID refers to a column in a table. @@ -2110,8 +2155,8 @@ typedef int ynVar; ** value is also stored in the Expr.iAgg column in the aggregate so that ** it can be accessed after all aggregates are computed. ** -** If the expression is an unbound variable marker (a question mark -** character '?' in the original SQL) then the Expr.iTable holds the index +** If the expression is an unbound variable marker (a question mark +** character '?' in the original SQL) then the Expr.iTable holds the index ** number for that variable. ** ** If the expression is a subquery then Expr.iColumn holds an integer @@ -2150,7 +2195,7 @@ struct Expr { /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no ** space is allocated for the fields below this point. An attempt to - ** access them will result in a segfault or malfunction. + ** access them will result in a segfault or malfunction. *********************************************************************/ Expr *pLeft; /* Left subnode */ @@ -2216,7 +2261,7 @@ struct Expr { #define EP_Propagate (EP_Collate|EP_Subquery) /* Propagate these bits up tree */ /* -** These macros can be used to test, set, or clear bits in the +** These macros can be used to test, set, or clear bits in the ** Expr.flags field. */ #define ExprHasProperty(E,P) (((E)->flags&(P))!=0) @@ -2235,8 +2280,8 @@ struct Expr { #endif /* -** Macros to determine the number of bytes required by a normal Expr -** struct, an Expr struct with the EP_Reduced flag set in Expr.flags +** Macros to determine the number of bytes required by a normal Expr +** struct, an Expr struct with the EP_Reduced flag set in Expr.flags ** and an Expr struct with the EP_TokenOnly flag set. */ #define EXPR_FULLSIZE sizeof(Expr) /* Full size */ @@ -2244,7 +2289,7 @@ struct Expr { #define EXPR_TOKENONLYSIZE offsetof(Expr,pLeft) /* Fewer features */ /* -** Flags passed to the sqlite3ExprDup() function. See the header comment +** Flags passed to the sqlite3ExprDup() function. See the header comment ** above sqlite3ExprDup() for details. */ #define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */ @@ -2326,7 +2371,11 @@ struct IdList { ** tables in a join to 32 instead of 64. But it also reduces the size ** of the library by 738 bytes on ix86. */ -typedef u64 Bitmask; +#ifdef SQLITE_BITMASK_TYPE + typedef SQLITE_BITMASK_TYPE Bitmask; +#else + typedef u64 Bitmask; +#endif /* ** The number of bits in a Bitmask. "BMS" means "BitMask Size". @@ -2338,6 +2387,7 @@ typedef u64 Bitmask; */ #define MASKBIT(n) (((Bitmask)1)<<(n)) #define MASKBIT32(n) (((unsigned int)1)<<(n)) +#define ALLBITS ((Bitmask)-1) /* ** The following structure describes the FROM clause of a SELECT statement. @@ -2426,6 +2476,7 @@ struct SrcList { #define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */ #define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */ #define WHERE_ONEPASS_MULTIROW 0x2000 /* ONEPASS is ok with multiple rows */ +#define WHERE_USE_LIMIT 0x4000 /* There is a constant LIMIT clause */ /* Allowed return values from sqlite3WhereIsDistinct() */ @@ -2443,12 +2494,12 @@ struct SrcList { ** pEList corresponds to the result set of a SELECT and is NULL for ** other statements. ** -** NameContexts can be nested. When resolving names, the inner-most +** NameContexts can be nested. When resolving names, the inner-most ** context is searched first. If no match is found, the next outer ** context is checked. If there is still no match, the next context ** is checked. This process continues until either a match is found ** or all contexts are check. When a match is found, the nRef member of -** the context containing the match is incremented. +** the context containing the match is incremented. ** ** Each subquery gets a new NameContext. The pNext field points to the ** NameContext in the parent query. Thus the process of scanning the @@ -2471,7 +2522,7 @@ struct NameContext { ** ** Note: NC_MinMaxAgg must have the same value as SF_MinMaxAgg and ** SQLITE_FUNC_MINMAX. -** +** */ #define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */ #define NC_HasAgg 0x0002 /* One or more aggregate functions seen */ @@ -2504,13 +2555,13 @@ struct NameContext { struct Select { ExprList *pEList; /* The fields of the result */ u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ - u16 selFlags; /* Various SF_* values */ + LogEst nSelectRow; /* Estimated number of result rows */ + u32 selFlags; /* Various SF_* values */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ #if SELECTTRACE_ENABLED char zSelName[12]; /* Symbolic name of this SELECT use for debugging */ #endif int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */ - u64 nSelectRow; /* Estimated number of result rows */ SrcList *pSrc; /* The FROM clause */ Expr *pWhere; /* The WHERE clause */ ExprList *pGroupBy; /* The GROUP BY clause */ @@ -2527,22 +2578,23 @@ struct Select { ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ -#define SF_Distinct 0x0001 /* Output should be DISTINCT */ -#define SF_All 0x0002 /* Includes the ALL keyword */ -#define SF_Resolved 0x0004 /* Identifiers have been resolved */ -#define SF_Aggregate 0x0008 /* Contains aggregate functions */ -#define SF_UsesEphemeral 0x0010 /* Uses the OpenEphemeral opcode */ -#define SF_Expanded 0x0020 /* sqlite3SelectExpand() called on this */ -#define SF_HasTypeInfo 0x0040 /* FROM subqueries have Table metadata */ -#define SF_Compound 0x0080 /* Part of a compound query */ -#define SF_Values 0x0100 /* Synthesized from VALUES clause */ -#define SF_MultiValue 0x0200 /* Single VALUES term with multiple rows */ -#define SF_NestedFrom 0x0400 /* Part of a parenthesized FROM clause */ -#define SF_MaybeConvert 0x0800 /* Need convertCompoundSelectToSubquery() */ -#define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ -#define SF_Recursive 0x2000 /* The recursive part of a recursive CTE */ -#define SF_Converted 0x4000 /* By convertCompoundSelectToSubquery() */ -#define SF_IncludeHidden 0x8000 /* Include hidden columns in output */ +#define SF_Distinct 0x00001 /* Output should be DISTINCT */ +#define SF_All 0x00002 /* Includes the ALL keyword */ +#define SF_Resolved 0x00004 /* Identifiers have been resolved */ +#define SF_Aggregate 0x00008 /* Contains aggregate functions */ +#define SF_UsesEphemeral 0x00010 /* Uses the OpenEphemeral opcode */ +#define SF_Expanded 0x00020 /* sqlite3SelectExpand() called on this */ +#define SF_HasTypeInfo 0x00040 /* FROM subqueries have Table metadata */ +#define SF_Compound 0x00080 /* Part of a compound query */ +#define SF_Values 0x00100 /* Synthesized from VALUES clause */ +#define SF_MultiValue 0x00200 /* Single VALUES term with multiple rows */ +#define SF_NestedFrom 0x00400 /* Part of a parenthesized FROM clause */ +#define SF_MaybeConvert 0x00800 /* Need convertCompoundSelectToSubquery() */ +#define SF_MinMaxAgg 0x01000 /* Aggregate containing min() or max() */ +#define SF_Recursive 0x02000 /* The recursive part of a recursive CTE */ +#define SF_FixedLimit 0x04000 /* nSelectRow set by a constant LIMIT */ +#define SF_Converted 0x08000 /* By convertCompoundSelectToSubquery() */ +#define SF_IncludeHidden 0x10000 /* Include hidden columns in output */ /* @@ -2550,7 +2602,7 @@ struct Select { ** by one of the following macros. The "SRT" prefix means "SELECT Result ** Type". ** -** SRT_Union Store results as a key in a temporary index +** SRT_Union Store results as a key in a temporary index ** identified by pDest->iSDParm. ** ** SRT_Except Remove results from the temporary index pDest->iSDParm. @@ -2574,7 +2626,7 @@ struct Select { ** of the query. This destination implies "LIMIT 1". ** ** SRT_Set The result must be a single column. Store each -** row of result as the key in table pDest->iSDParm. +** row of result as the key in table pDest->iSDParm. ** Apply the affinity pDest->affSdst before storing ** results. Used to implement "IN (SELECT ...)". ** @@ -2642,7 +2694,7 @@ struct SelectDest { }; /* -** During code generation of statements that do inserts into AUTOINCREMENT +** During code generation of statements that do inserts into AUTOINCREMENT ** tables, the following information is attached to the Table.u.autoInc.p ** pointer of each autoincrement table to record some side information that ** the code generator needs. We have to keep per-table autoincrement @@ -2665,7 +2717,7 @@ struct AutoincInfo { #endif /* -** At least one instance of the following structure is created for each +** At least one instance of the following structure is created for each ** trigger that may be fired while parsing an INSERT, UPDATE or DELETE ** statement. All such objects are stored in the linked list headed at ** Parse.pTriggerPrg and deleted once statement compilation has been @@ -2678,7 +2730,7 @@ struct AutoincInfo { ** values for both pTrigger and orconf. ** ** The TriggerPrg.aColmask[0] variable is set to a mask of old.* columns -** accessed (or set to 0 for triggers fired as a result of INSERT +** accessed (or set to 0 for triggers fired as a result of INSERT ** statements). Similarly, the TriggerPrg.aColmask[1] variable is set to ** a mask of new.* columns used by the program. */ @@ -2719,7 +2771,7 @@ struct TriggerPrg { ** is constant but the second part is reset at the beginning and end of ** each recursion. ** -** The nTableLock and aTableLock variables are only used if the shared-cache +** The nTableLock and aTableLock variables are only used if the shared-cache ** feature is enabled (if sqlite3Tsd()->useSharedData is true). They are ** used to store the set of table-locks required by the statement being ** compiled. Function sqlite3TableLock() is used to add entries to the @@ -2872,10 +2924,10 @@ struct AuthContext { /* * Each trigger present in the database schema is stored as an instance of - * struct Trigger. + * struct Trigger. * * Pointers to instances of struct Trigger are stored in two ways. - * 1. In the "trigHash" hash table (part of the sqlite3* that represents the + * 1. In the "trigHash" hash table (part of the sqlite3* that represents the * database). This allows Trigger structures to be retrieved by name. * 2. All triggers associated with a single table form a linked list, using the * pNext member of struct Trigger. A pointer to the first element of the @@ -2901,7 +2953,7 @@ struct Trigger { /* ** A trigger is either a BEFORE or an AFTER trigger. The following constants -** determine which. +** determine which. ** ** If there are multiple triggers, you might of some BEFORE and some AFTER. ** In that cases, the constants below can be ORed together. @@ -2911,15 +2963,15 @@ struct Trigger { /* * An instance of struct TriggerStep is used to store a single SQL statement - * that is a part of a trigger-program. + * that is a part of a trigger-program. * * Instances of struct TriggerStep are stored in a singly linked list (linked - * using the "pNext" member) referenced by the "step_list" member of the + * using the "pNext" member) referenced by the "step_list" member of the * associated struct Trigger instance. The first element of the linked list is * the first step of the trigger-program. - * + * * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or - * "SELECT" statement. The meanings of the other members is determined by the + * "SELECT" statement. The meanings of the other members is determined by the * value of "op" as follows: * * (op == TK_INSERT) @@ -2929,7 +2981,7 @@ struct Trigger { * zTarget -> Dequoted name of the table to insert into. * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then * this stores values to be inserted. Otherwise NULL. - * pIdList -> If this is an INSERT INTO ... () VALUES ... + * pIdList -> If this is an INSERT INTO ... () VALUES ... * statement, then this stores the column-names to be * inserted into. * @@ -2937,7 +2989,7 @@ struct Trigger { * zTarget -> Dequoted name of the table to delete from. * pWhere -> The WHERE clause of the DELETE statement if one is specified. * Otherwise NULL. - * + * * (op == TK_UPDATE) * zTarget -> Dequoted name of the table to update. * pWhere -> The WHERE clause of the UPDATE statement if one is specified. @@ -2945,7 +2997,7 @@ struct Trigger { * pExprList -> A list of the columns to update and the expressions to update * them to. See sqlite3Update() documentation of "pChanges" * argument. - * + * */ struct TriggerStep { u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ @@ -2963,7 +3015,7 @@ struct TriggerStep { /* ** The following structure contains information used by the sqliteFix... ** routines as they walk the parse tree to make database references -** explicit. +** explicit. */ typedef struct DbFixer DbFixer; struct DbFixer { @@ -3024,6 +3076,7 @@ struct Sqlite3Config { int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ + int nStmtSpill; /* Stmt-journal spill-to-disk threshold */ sqlite3_mem_methods m; /* Low-level memory allocation interface */ sqlite3_mutex_methods mutex; /* Low-level mutex interface */ sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */ @@ -3104,6 +3157,7 @@ struct Walker { SrcList *pSrcList; /* FROM clause */ struct SrcCount *pSrcCount; /* Counting column references */ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ + int *aiCol; /* array of column indexes */ } u; }; @@ -3172,7 +3226,23 @@ int sqlite3CantopenError(int); #define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__) #define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__) #define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__) +#ifdef SQLITE_DEBUG + int sqlite3NomemError(int); + int sqlite3IoerrnomemError(int); +# define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__) +# define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__) +#else +# define SQLITE_NOMEM_BKPT SQLITE_NOMEM +# define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM +#endif +/* +** FTS3 and FTS4 both require virtual table support +*/ +#if defined(SQLITE_OMIT_VIRTUALTABLE) +# undef SQLITE_ENABLE_FTS3 +# undef SQLITE_ENABLE_FTS4 +#endif /* ** FTS4 is really an extension for FTS3. It is enabled using the @@ -3221,8 +3291,9 @@ int sqlite3IsIdChar(u8); /* ** Internal function prototypes */ -#define sqlite3StrICmp sqlite3_stricmp +int sqlite3StrICmp(const char*,const char*); int sqlite3Strlen30(const char*); +const char *sqlite3StrNext(const char*); #define sqlite3StrNICmp sqlite3_strnicmp int sqlite3MallocInit(void); @@ -3261,7 +3332,7 @@ int sqlite3HeapNearlyFull(void); #ifdef SQLITE_USE_ALLOCA # define sqlite3StackAllocRaw(D,N) alloca(N) # define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N) -# define sqlite3StackFree(D,P) +# define sqlite3StackFree(D,P) #else # define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N) # define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N) @@ -3378,11 +3449,10 @@ void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int); #else # define sqlite3ColumnPropertiesFromName(T,C) /* no-op */ #endif -void sqlite3AddColumn(Parse*,Token*); +void sqlite3AddColumn(Parse*,Token*,Token*); void sqlite3AddNotNull(Parse*, int); void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int); void sqlite3AddCheckConstraint(Parse*, Expr*); -void sqlite3AddColumnType(Parse*,Token*); void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); @@ -3456,7 +3526,7 @@ Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, - Expr*,ExprList*,u16,Expr*,Expr*); + Expr*,ExprList*,u32,Expr*,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, int); @@ -3468,7 +3538,7 @@ void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); void sqlite3WhereEnd(WhereInfo*); -u64 sqlite3WhereOutputRowCount(WhereInfo*); +LogEst sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); int sqlite3WhereIsSorted(WhereInfo*); @@ -3549,7 +3619,7 @@ void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); void sqlite3ResolvePartIdxLabel(Parse*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, - u8,u8,int,int*); + u8,u8,int,int*,int*); void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*); void sqlite3BeginWriteOperation(Parse*, int, int); @@ -3568,11 +3638,11 @@ void sqlite3SelectSetName(Select*,const char*); #else # define sqlite3SelectSetName(A,B) #endif -void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*); -FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,u8); -void sqlite3RegisterBuiltinFunctions(sqlite3*); +void sqlite3InsertBuiltinFuncs(FuncDef*,int); +FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); +void sqlite3RegisterBuiltinFunctions(void); void sqlite3RegisterDateTimeFunctions(void); -void sqlite3RegisterGlobalFunctions(void); +void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); int sqlite3SafetyCheckOk(sqlite3*); int sqlite3SafetyCheckSickOrOk(sqlite3*); void sqlite3ChangeCookie(Parse*, int); @@ -3651,7 +3721,11 @@ LogEst sqlite3LogEstAdd(LogEst,LogEst); #ifndef SQLITE_OMIT_VIRTUALTABLE LogEst sqlite3LogEstFromDouble(double); #endif +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ + defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \ + defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) u64 sqlite3LogEstToInt(LogEst); +#endif /* ** Routines to read and write variable-length integers. These used to @@ -3718,7 +3792,7 @@ u8 sqlite3GetBoolean(const char *z,u8); const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); -void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, +void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueSetNull(sqlite3_value*); void sqlite3ValueFree(sqlite3_value*); @@ -3733,7 +3807,7 @@ extern const unsigned char sqlite3UpperToLower[]; extern const unsigned char sqlite3CtypeMap[]; extern const Token sqlite3IntTokens[]; extern SQLITE_WSD struct Sqlite3Config sqlite3Config; -extern SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; +extern FuncDefHash sqlite3BuiltinFunctions; #ifndef SQLITE_OMIT_WSD extern int sqlite3PendingByte; #endif @@ -3768,7 +3842,6 @@ void sqlite3DeleteIndexSamples(sqlite3*,Index*); void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); -void sqlite3MinimumFileFormat(Parse*, int, int); void sqlite3SchemaClear(void *); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); @@ -3779,7 +3852,7 @@ KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*); #ifdef SQLITE_DEBUG int sqlite3KeyInfoIsWriteable(KeyInfo*); #endif -int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, +int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), FuncDestructor *pDestructor @@ -3842,7 +3915,7 @@ void sqlite3AutoLoadExtensions(sqlite3*); # define sqlite3VtabRollback(X) # define sqlite3VtabCommit(X) # define sqlite3VtabInSync(db) 0 -# define sqlite3VtabLock(X) +# define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabUnlockList(X) # define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK @@ -3900,7 +3973,7 @@ const char *sqlite3JournalModename(int); ** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In -** this case foreign keys are parsed, but no other functionality is +** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) @@ -3961,19 +4034,14 @@ const char *sqlite3JournalModename(int); #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ int sqlite3FindInIndex(Parse *, Expr *, u32, int*); +int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); +int sqlite3JournalSize(sqlite3_vfs *); #ifdef SQLITE_ENABLE_ATOMIC_WRITE - int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); - int sqlite3JournalSize(sqlite3_vfs *); int sqlite3JournalCreate(sqlite3_file *); - int sqlite3JournalExists(sqlite3_file *p); -#else - #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile) - #define sqlite3JournalExists(p) 1 #endif +int sqlite3JournalIsInMemory(sqlite3_file *p); void sqlite3MemJournalOpen(sqlite3_file *); -int sqlite3MemJournalSize(void); -int sqlite3IsMemJournal(sqlite3_file *); void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 @@ -4004,7 +4072,7 @@ void sqlite3Put4byte(u8*, u32); /* ** If the SQLITE_ENABLE IOTRACE exists then the global variable ** sqlite3IoTrace is a pointer to a printf-like routine used to -** print I/O tracing messages. +** print I/O tracing messages. */ #ifdef SQLITE_ENABLE_IOTRACE # define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } @@ -4038,7 +4106,7 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...); ** that allocations that might have been satisfied by lookaside are not ** passed back to non-lookaside free() routines. Asserts such as the ** example above are placed on the non-lookaside free() routines to verify -** this constraint. +** this constraint. ** ** All of this is no-op for a production build. It only comes into ** play when the SQLITE_MEMDEBUG compile-time option is used. diff --git a/src/sqliteLimit.h b/src/sqliteLimit.h index 75cad1274b..4b5ddaadec 100644 --- a/src/sqliteLimit.h +++ b/src/sqliteLimit.h @@ -102,12 +102,12 @@ ** the main database table and for temporary tables. ** ** IMPLEMENTATION-OF: R-31093-59126 The default suggested cache size -** is 2000 pages. +** is 2000*1024 bytes. ** IMPLEMENTATION-OF: R-48205-43578 The default suggested cache size can be ** altered using the SQLITE_DEFAULT_CACHE_SIZE compile-time options. */ #ifndef SQLITE_DEFAULT_CACHE_SIZE -# define SQLITE_DEFAULT_CACHE_SIZE 2000 +# define SQLITE_DEFAULT_CACHE_SIZE -2000 #endif /* @@ -120,8 +120,9 @@ /* ** The maximum number of attached databases. This must be between 0 -** and 62. The upper bound on 62 is because a 64-bit integer bitmap -** is used internally to track attached databases. +** and 125. The upper bound of 125 is because the attached databases are +** counted using a signed 8-bit integer which has a maximum value of 127 +** and we have to allow 2 extra counts for the "main" and "temp" databases. */ #ifndef SQLITE_MAX_ATTACHED # define SQLITE_MAX_ATTACHED 10 @@ -156,7 +157,7 @@ ** The default size of a database page. */ #ifndef SQLITE_DEFAULT_PAGE_SIZE -# define SQLITE_DEFAULT_PAGE_SIZE 1024 +# define SQLITE_DEFAULT_PAGE_SIZE 4096 #endif #if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE # undef SQLITE_DEFAULT_PAGE_SIZE diff --git a/src/table.c b/src/table.c index 153bfb319f..a50d83ce63 100644 --- a/src/table.c +++ b/src/table.c @@ -101,7 +101,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ return 0; malloc_failed: - p->rc = SQLITE_NOMEM; + p->rc = SQLITE_NOMEM_BKPT; return 1; } @@ -142,7 +142,7 @@ int sqlite3_get_table( res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc ); if( res.azResult==0 ){ db->errCode = SQLITE_NOMEM; - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } res.azResult[0] = 0; rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg); @@ -171,7 +171,7 @@ int sqlite3_get_table( if( azNew==0 ){ sqlite3_free_table(&res.azResult[1]); db->errCode = SQLITE_NOMEM; - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } res.azResult = azNew; } diff --git a/src/tclsqlite.c b/src/tclsqlite.c index aa913ca7c7..4e19f92644 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -3151,9 +3151,13 @@ EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } /* Because it accesses the file-system and uses persistent state, SQLite -** is not considered appropriate for safe interpreters. Hence, we deliberately -** omit the _SafeInit() interfaces. +** is not considered appropriate for safe interpreters. Hence, we cause +** the _SafeInit() interfaces return TCL_ERROR. */ +EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; } +EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;} + + #ifndef SQLITE_3_SUFFIX_ONLY int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } @@ -3770,6 +3774,7 @@ static void init_all(Tcl_Interp *interp){ extern int SqlitetestSyscall_Init(Tcl_Interp*); extern int Fts5tcl_Init(Tcl_Interp *); extern int SqliteRbu_Init(Tcl_Interp*); + extern int Sqlitetesttcl_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); #endif @@ -3814,6 +3819,7 @@ static void init_all(Tcl_Interp *interp){ SqlitetestSyscall_Init(interp); Fts5tcl_Init(interp); SqliteRbu_Init(interp); + Sqlitetesttcl_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); diff --git a/src/test1.c b/src/test1.c index c5e71582bc..744b400b2a 100644 --- a/src/test1.c +++ b/src/test1.c @@ -3154,7 +3154,7 @@ static int test_bind_zeroblob64( ){ sqlite3_stmt *pStmt; int idx; - i64 n; + Tcl_WideInt n; int rc; if( objc!=4 ){ @@ -6920,6 +6920,53 @@ static int test_register_dbstat_vtab( #endif /* SQLITE_OMIT_VIRTUALTABLE */ } +/* +** tclcmd: sqlite3_db_config DB SETTING VALUE +** +** Invoke sqlite3_db_config() for one of the setting values. +*/ +static int test_sqlite3_db_config( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + static const struct { + const char *zName; + int eVal; + } aSetting[] = { + { "FKEY", SQLITE_DBCONFIG_ENABLE_FKEY }, + { "TRIGGER", SQLITE_DBCONFIG_ENABLE_TRIGGER }, + { "FTS3_TOKENIZER", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER }, + }; + int i; + int v; + const char *zSetting; + sqlite3 *db; + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB SETTING VALUE"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zSetting = Tcl_GetString(objv[2]); + if( sqlite3_strglob("SQLITE_*", zSetting)==0 ) zSetting += 7; + if( sqlite3_strglob("DBCONFIG_*", zSetting)==0 ) zSetting += 9; + if( sqlite3_strglob("ENABLE_*", zSetting)==0 ) zSetting += 7; + for(i=0; i=ArraySize(aSetting) ){ + Tcl_SetObjResult(interp, + Tcl_NewStringObj("unknown sqlite3_db_config setting", -1)); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[3], &v) ) return TCL_ERROR; + sqlite3_db_config(db, aSetting[i].eVal, v, &v); + Tcl_SetObjResult(interp, Tcl_NewIntObj(v)); + return TCL_OK; +} + /* ** Register commands with the TCL interpreter. */ @@ -6989,6 +7036,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { + { "sqlite3_db_config", test_sqlite3_db_config, 0 }, { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "register_dbstat_vtab", test_register_dbstat_vtab }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, diff --git a/src/test_bestindex.c b/src/test_bestindex.c new file mode 100644 index 0000000000..a955c2782c --- /dev/null +++ b/src/test_bestindex.c @@ -0,0 +1,606 @@ +/* +** 2016-03-01 +** +** 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. +** +************************************************************************* +** Code for testing the virtual table xBestIndex method and the query +** planner. +*/ + + +/* +** INSTRUCTIONS +** +** This module exports a single tcl command - [register_tcl_module]. When +** invoked, it registers a special virtual table module with a database +** connection. +** +** The virtual table is currently read-only. And always returns zero rows. +** It is created with a single argument - the name of a Tcl command - as +** follows: +** +** CREATE VIRTUAL TABLE x1 USING tcl(tcl_command); +** +** The command [tcl_command] is invoked when the table is first created (or +** connected), when the xBestIndex() method is invoked and when the xFilter() +** method is called. When it is created (or connected), it is invoked as +** follows: +** +** tcl_command xConnect +** +** In this case the return value of the script is passed to the +** sqlite3_declare_vtab() function to create the virtual table schema. +** +** When the xBestIndex() method is called by SQLite, the Tcl command is +** invoked as: +** +** tcl_command xBestIndex CONSTRAINTS ORDERBY MASK +** +** where CONSTRAINTS is a tcl representation of the aConstraints[] array, +** ORDERBY is a representation of the contents of the aOrderBy[] array and +** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual +** table is declared as: +** +** CREATE TABLE x1(a, b, c) +** +** and the query is: +** +** SELECT * FROM x1 WHERE a=? AND c module name ("fs") +** argv[1] -> database name +** argv[2] -> table name +** argv[...] -> other module argument fields. +*/ +static int tclConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + Tcl_Interp *interp = (Tcl_Interp*)pAux; + tcl_vtab *pTab = 0; + char *zCmd = 0; + Tcl_Obj *pScript = 0; + int rc = SQLITE_OK; + + if( argc!=4 ){ + *pzErr = sqlite3_mprintf("wrong number of arguments"); + return SQLITE_ERROR; + } + + zCmd = sqlite3_malloc(strlen(argv[3])+1); + pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab)); + if( zCmd && pTab ){ + memcpy(zCmd, argv[3], strlen(argv[3])+1); + tclDequote(zCmd); + memset(pTab, 0, sizeof(tcl_vtab)); + + pTab->pCmd = Tcl_NewStringObj(zCmd, -1); + pTab->interp = interp; + pTab->db = db; + Tcl_IncrRefCount(pTab->pCmd); + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1)); + + rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); + if( rc!=TCL_OK ){ + *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp)); + rc = SQLITE_ERROR; + }else{ + rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp)); + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(pTab); + pTab = 0; + } + }else{ + rc = SQLITE_NOMEM; + } + + sqlite3_free(zCmd); + *ppVtab = &pTab->base; + return rc; +} + +/* The xDisconnect and xDestroy methods are also the same */ +static int tclDisconnect(sqlite3_vtab *pVtab){ + tcl_vtab *pTab = (tcl_vtab*)pVtab; + Tcl_DecrRefCount(pTab->pCmd); + sqlite3_free(pTab); + return SQLITE_OK; +} + +/* +** Open a new tcl cursor. +*/ +static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + tcl_cursor *pCur; + pCur = sqlite3_malloc(sizeof(tcl_cursor)); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(tcl_cursor)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Close a tcl cursor. +*/ +static int tclClose(sqlite3_vtab_cursor *cur){ + tcl_cursor *pCur = (tcl_cursor *)cur; + if( pCur ){ + sqlite3_finalize(pCur->pStmt); + sqlite3_free(pCur); + } + return SQLITE_OK; +} + +static int tclNext(sqlite3_vtab_cursor *pVtabCursor){ + tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor; + if( pCsr->pStmt ){ + tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab); + int rc = sqlite3_step(pCsr->pStmt); + if( rc!=SQLITE_ROW ){ + const char *zErr; + rc = sqlite3_finalize(pCsr->pStmt); + pCsr->pStmt = 0; + if( rc!=SQLITE_OK ){ + zErr = sqlite3_errmsg(pTab->db); + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + } + } + } + return SQLITE_OK; +} + +static int tclFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor; + tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab); + Tcl_Interp *interp = pTab->interp; + Tcl_Obj *pScript; + Tcl_Obj *pArg; + int ii; + int rc; + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xFilter", -1)); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(idxNum)); + if( idxStr ){ + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(idxStr, -1)); + }else{ + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("", -1)); + } + + pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + for(ii=0; iibase.zErrMsg = sqlite3_mprintf("%s", zErr); + }else{ + /* Analyze the scripts return value. The return value should be a tcl + ** list object with an even number of elements. The first element of each + ** pair must be one of: + ** + ** "sql" (SQL statement to return data) + */ + Tcl_Obj *pRes = Tcl_GetObjResult(interp); + Tcl_Obj **apElem = 0; + int nElem; + rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem); + if( rc!=TCL_OK ){ + const char *zErr = Tcl_GetStringResult(interp); + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + }else{ + for(ii=0; rc==SQLITE_OK && iidb, zSql, -1, &pCsr->pStmt, 0); + if( rc!=SQLITE_OK ){ + const char *zErr = sqlite3_errmsg(pTab->db); + pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zErr); + } + }else{ + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd); + } + } + } + } + + if( rc==SQLITE_OK ){ + rc = tclNext(pVtabCursor); + } + return rc; +} + +static int tclColumn( + sqlite3_vtab_cursor *pVtabCursor, + sqlite3_context *ctx, + int i +){ + tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor; + sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1)); + return SQLITE_OK; +} + +static int tclRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ + tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor; + *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); + return SQLITE_OK; +} + +static int tclEof(sqlite3_vtab_cursor *pVtabCursor){ + tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor; + return (pCsr->pStmt==0); +} + +static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + tcl_vtab *pTab = (tcl_vtab*)tab; + Tcl_Interp *interp = pTab->interp; + Tcl_Obj *pArg; + Tcl_Obj *pScript; + int ii; + int rc = SQLITE_OK; + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1)); + + pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + for(ii=0; iinConstraint; ii++){ + struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; + Tcl_Obj *pElem = Tcl_NewObj(); + const char *zOp = "?"; + + Tcl_IncrRefCount(pElem); + + switch( pCons->op ){ + case SQLITE_INDEX_CONSTRAINT_EQ: + zOp = "eq"; break; + case SQLITE_INDEX_CONSTRAINT_GT: + zOp = "gt"; break; + case SQLITE_INDEX_CONSTRAINT_LE: + zOp = "le"; break; + case SQLITE_INDEX_CONSTRAINT_LT: + zOp = "lt"; break; + case SQLITE_INDEX_CONSTRAINT_GE: + zOp = "ge"; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: + zOp = "match"; break; + case SQLITE_INDEX_CONSTRAINT_LIKE: + zOp = "like"; break; + case SQLITE_INDEX_CONSTRAINT_GLOB: + zOp = "glob"; break; + case SQLITE_INDEX_CONSTRAINT_REGEXP: + zOp = "regexp"; break; + } + + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable)); + + Tcl_ListObjAppendElement(0, pArg, pElem); + Tcl_DecrRefCount(pElem); + } + + Tcl_ListObjAppendElement(0, pScript, pArg); + Tcl_DecrRefCount(pArg); + + pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + for(ii=0; iinOrderBy; ii++){ + struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii]; + Tcl_Obj *pElem = Tcl_NewObj(); + Tcl_IncrRefCount(pElem); + + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1)); + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc)); + + Tcl_ListObjAppendElement(0, pArg, pElem); + Tcl_DecrRefCount(pElem); + } + + Tcl_ListObjAppendElement(0, pScript, pArg); + Tcl_DecrRefCount(pArg); + + Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed)); + + rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); + Tcl_DecrRefCount(pScript); + if( rc!=TCL_OK ){ + const char *zErr = Tcl_GetStringResult(interp); + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + }else{ + /* Analyze the scripts return value. The return value should be a tcl + ** list object with an even number of elements. The first element of each + ** pair must be one of: + ** + ** "orderby" (value of orderByConsumed flag) + ** "cost" (value of estimatedCost field) + ** "rows" (value of estimatedRows field) + ** "use" (index of used constraint in aConstraint[]) + ** "idxnum" (value of idxNum field) + ** "idxstr" (value of idxStr field) + ** "omit" (index of omitted constraint in aConstraint[]) + */ + Tcl_Obj *pRes = Tcl_GetObjResult(interp); + Tcl_Obj **apElem = 0; + int nElem; + rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem); + if( rc!=TCL_OK ){ + const char *zErr = Tcl_GetStringResult(interp); + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + }else{ + int iArgv = 1; + for(ii=0; rc==SQLITE_OK && iiestimatedCost); + }else + if( sqlite3_stricmp("orderby", zCmd)==0 ){ + rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed); + }else + if( sqlite3_stricmp("idxnum", zCmd)==0 ){ + rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum); + }else + if( sqlite3_stricmp("idxstr", zCmd)==0 ){ + sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p)); + pIdxInfo->needToFreeIdxStr = 1; + }else + if( sqlite3_stricmp("rows", zCmd)==0 ){ + Tcl_WideInt x = 0; + rc = Tcl_GetWideIntFromObj(interp, p, &x); + pIdxInfo->estimatedRows = (tRowcnt)x; + }else + if( sqlite3_stricmp("use", zCmd)==0 + || sqlite3_stricmp("omit", zCmd)==0 + ){ + int iCons; + rc = Tcl_GetIntFromObj(interp, p, &iCons); + if( rc==SQLITE_OK ){ + if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons); + }else{ + int bOmit = (zCmd[0]=='o' || zCmd[0]=='O'); + pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++; + pIdxInfo->aConstraintUsage[iCons].omit = bOmit; + } + } + }else{ + rc = SQLITE_ERROR; + pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd); + } + if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){ + const char *zErr = Tcl_GetStringResult(interp); + pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr); + } + } + } + } + + return rc; +} + +/* +** A virtual table module that provides read-only access to a +** Tcl global variable namespace. +*/ +static sqlite3_module tclModule = { + 0, /* iVersion */ + tclConnect, + tclConnect, + tclBestIndex, + tclDisconnect, + tclDisconnect, + tclOpen, /* xOpen - open a cursor */ + tclClose, /* xClose - close a cursor */ + tclFilter, /* xFilter - configure scan constraints */ + tclNext, /* xNext - advance a cursor */ + tclEof, /* xEof - check for end of scan */ + tclColumn, /* xColumn - read data */ + tclRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +/* +** Decode a pointer to an sqlite3 object. +*/ +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + +/* +** Register the echo virtual table module. +*/ +static int register_tcl_module( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; +#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3_create_module(db, "tcl", &tclModule, (void *)interp); +#endif + return TCL_OK; +} + +#endif + + +/* +** Register commands with the TCL interpreter. +*/ +int Sqlitetesttcl_Init(Tcl_Interp *interp){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_tcl_module", register_tcl_module, 0 }, + }; + int i; + for(i=0; ixTask = xTask; p->pIn = pIn; @@ -89,7 +89,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ int rc; assert( ppOut!=0 ); - if( NEVER(p==0) ) return SQLITE_NOMEM; + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; if( p->done ){ *ppOut = p->pOut; rc = SQLITE_OK; @@ -154,7 +154,7 @@ int sqlite3ThreadCreate( assert( xTask!=0 ); *ppThread = 0; p = sqlite3Malloc(sizeof(*p)); - if( p==0 ) return SQLITE_NOMEM; + if( p==0 ) return SQLITE_NOMEM_BKPT; /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a ** function that returns SQLITE_ERROR when passed the argument 200, that ** forces worker threads to run sequentially and deterministically @@ -186,7 +186,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ BOOL bRc; assert( ppOut!=0 ); - if( NEVER(p==0) ) return SQLITE_NOMEM; + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; if( p->xTask==0 ){ /* assert( p->id==GetCurrentThreadId() ); */ rc = WAIT_OBJECT_0; @@ -234,7 +234,7 @@ int sqlite3ThreadCreate( assert( xTask!=0 ); *ppThread = 0; p = sqlite3Malloc(sizeof(*p)); - if( p==0 ) return SQLITE_NOMEM; + if( p==0 ) return SQLITE_NOMEM_BKPT; if( (SQLITE_PTR_TO_INT(p)/17)&1 ){ p->xTask = xTask; p->pIn = pIn; @@ -250,7 +250,7 @@ int sqlite3ThreadCreate( int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ assert( ppOut!=0 ); - if( NEVER(p==0) ) return SQLITE_NOMEM; + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT; if( p->xTask ){ *ppOut = p->xTask(p->pIn); }else{ @@ -261,7 +261,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ #if defined(SQLITE_TEST) { void *pTstAlloc = sqlite3Malloc(10); - if (!pTstAlloc) return SQLITE_NOMEM; + if (!pTstAlloc) return SQLITE_NOMEM_BKPT; sqlite3_free(pTstAlloc); } #endif diff --git a/src/tokenize.c b/src/tokenize.c index 9b3444ac82..19a5ddf04d 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -285,7 +285,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ case CC_BANG: { if( z[1]!='=' ){ *tokenType = TK_ILLEGAL; - return 2; + return 1; }else{ *tokenType = TK_NE; return 2; @@ -435,8 +435,8 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ *tokenType = TK_ID; return keywordCode((char*)z, i, tokenType); } -#ifndef SQLITE_OMIT_BLOB_LITERAL case CC_X: { +#ifndef SQLITE_OMIT_BLOB_LITERAL testcase( z[0]=='x' ); testcase( z[0]=='X' ); if( z[1]=='\'' ){ *tokenType = TK_BLOB; @@ -448,10 +448,10 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){ if( z[i] ) i++; return i; } +#endif /* If it is not a BLOB literal, then it must be an ID, since no ** SQL keywords start with the letter 'x'. Fall through */ } -#endif case CC_ID: { i = 1; break; @@ -495,7 +495,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ pEngine = sqlite3ParserAlloc(sqlite3Malloc); if( pEngine==0 ){ sqlite3OomFault(db); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } assert( pParse->pNewTable==0 ); assert( pParse->pNewTrigger==0 ); @@ -523,18 +523,17 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ break; } }else{ - if( tokenType==TK_SEMI ) pParse->zTail = &zSql[i]; sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse); lastTokenParsed = tokenType; if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break; } } assert( nErr==0 ); + pParse->zTail = &zSql[i]; if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ assert( zSql[i]==0 ); if( lastTokenParsed!=TK_SEMI ){ sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse); - pParse->zTail = &zSql[i]; } if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); @@ -549,7 +548,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ #endif /* YYDEBUG */ sqlite3ParserFree(pEngine, sqlite3_free); if( db->mallocFailed ){ - pParse->rc = SQLITE_NOMEM; + pParse->rc = SQLITE_NOMEM_BKPT; } if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); diff --git a/src/treeview.c b/src/treeview.c index 2985804314..907159c06d 100644 --- a/src/treeview.c +++ b/src/treeview.c @@ -132,9 +132,10 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ sqlite3TreeViewPush(pView, 1); } do{ - sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x", + sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d", ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), - ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags, + (int)p->nSelectRow ); if( cnt++ ) sqlite3TreeViewPop(pView); if( p->pPrior ){ @@ -338,6 +339,12 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ case TK_ISNULL: zUniOp = "ISNULL"; break; case TK_NOTNULL: zUniOp = "NOTNULL"; break; + case TK_SPAN: { + sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken); + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); + break; + } + case TK_COLLATE: { sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken); sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); diff --git a/src/update.c b/src/update.c index 20951aade9..9b351662e2 100644 --- a/src/update.c +++ b/src/update.c @@ -268,7 +268,7 @@ void sqlite3Update( ** case, set all bits of the colUsed mask (to ensure that the virtual ** table implementation makes all columns available). */ - pTabList->a[0].colUsed = IsVirtual(pTab) ? (Bitmask)-1 : 0; + pTabList->a[0].colUsed = IsVirtual(pTab) ? ALLBITS : 0; hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey); @@ -572,7 +572,8 @@ void sqlite3Update( /* Do constraint checks. */ assert( regOldRowid>0 ); sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, - regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace); + regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace, + aXRef); /* Do FK constraint checks. */ if( hasFK ){ diff --git a/src/utf.c b/src/utf.c index e42ab418ad..7801306cb4 100644 --- a/src/utf.c +++ b/src/utf.c @@ -231,7 +231,7 @@ SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ rc = sqlite3VdbeMemMakeWriteable(pMem); if( rc!=SQLITE_OK ){ assert( rc==SQLITE_NOMEM ); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } zIn = (u8*)pMem->z; zTerm = &zIn[pMem->n&~1]; @@ -273,7 +273,7 @@ SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ zTerm = &zIn[pMem->n]; zOut = sqlite3DbMallocRaw(pMem->db, len); if( !zOut ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } z = zOut; diff --git a/src/util.c b/src/util.c index 37b585b2ea..6aead47faa 100644 --- a/src/util.c +++ b/src/util.c @@ -109,6 +109,14 @@ int sqlite3Strlen30(const char *z){ return 0x3fffffff & (int)strlen(z); } +/* +** The string z[] is followed immediately by another string. Return +** a poiner to that other string. +*/ +const char *sqlite3StrNext(const char *z){ + return z + strlen(z) + 1; +} + /* ** Set the current error code to err_code and clear any prior error message. */ @@ -256,16 +264,25 @@ void sqlite3TokenInit(Token *p, char *z){ ** independence" that SQLite uses internally when comparing identifiers. */ int sqlite3_stricmp(const char *zLeft, const char *zRight){ - register unsigned char *a, *b; if( zLeft==0 ){ return zRight ? -1 : 0; }else if( zRight==0 ){ return 1; } + return sqlite3StrICmp(zLeft, zRight); +} +int sqlite3StrICmp(const char *zLeft, const char *zRight){ + unsigned char *a, *b; + int c; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; - while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } - return UpperToLower[*a] - UpperToLower[*b]; + for(;;){ + c = (int)UpperToLower[*a] - (int)UpperToLower[*b]; + if( c || *a==0 ) break; + a++; + b++; + } + return c; } int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; @@ -1076,7 +1093,7 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ */ int sqlite3VarintLen(u64 v){ int i; - for(i=1; (v >>= 7)!=0; i++){ assert( i<9 ); } + for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); } return i; } @@ -1107,10 +1124,12 @@ u32 sqlite3Get4byte(const u8 *p){ void sqlite3Put4byte(unsigned char *p, u32 v){ #if SQLITE_BYTEORDER==4321 memcpy(p,&v,4); -#elif SQLITE_BYTEORDER==1234 && defined(__GNUC__) && GCC_VERSION>=4003000 +#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ + && defined(__GNUC__) && GCC_VERSION>=4003000 u32 x = __builtin_bswap32(v); memcpy(p,&x,4); -#elif SQLITE_BYTEORDER==1234 && defined(_MSC_VER) && _MSC_VER>=1300 +#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ + && defined(_MSC_VER) && _MSC_VER>=1300 u32 x = _byteswap_ulong(v); memcpy(p,&x,4); #else @@ -1389,8 +1408,14 @@ LogEst sqlite3LogEstFromDouble(double x){ } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \ + defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \ + defined(SQLITE_EXPLAIN_ESTIMATED_ROWS) /* ** Convert a LogEst into an integer. +** +** Note that this routine is only used when one or more of various +** non-standard compile-time options is enabled. */ u64 sqlite3LogEstToInt(LogEst x){ u64 n; @@ -1404,3 +1429,4 @@ u64 sqlite3LogEstToInt(LogEst x){ } return (n+8)>>(3-x); } +#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */ diff --git a/src/vacuum.c b/src/vacuum.c index adc802e60b..bc7b5831b6 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -38,7 +38,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ sqlite3_stmt *pStmt; VVA_ONLY( int rc; ) if( !zSql ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){ sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db)); @@ -219,7 +219,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0)) || NEVER(db->mallocFailed) ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto end_of_vacuum; } diff --git a/src/vdbe.c b/src/vdbe.c index e7c3e48af5..3351a44551 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -627,7 +627,11 @@ int sqlite3VdbeExec( } sqlite3EndBenignMalloc(); #endif - for(pOp=&aOp[p->pc]; rc==SQLITE_OK; pOp++){ + for(pOp=&aOp[p->pc]; 1; pOp++){ + /* Errors are detected by individual opcodes, with an immediate + ** jumps to abort_due_to_error. */ + assert( rc==SQLITE_OK ); + assert( pOp>=aOp && pOp<&aOp[p->nOp]); #ifdef VDBE_PROFILE start = sqlite3Hwtime(); @@ -774,7 +778,7 @@ check_for_interrupt: nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps); if( db->xProgress(db->pProgressArg) ){ rc = SQLITE_INTERRUPT; - goto vdbe_error_halt; + goto abort_due_to_error; } } #endif @@ -1053,7 +1057,10 @@ case OP_String8: { /* same as TK_STRING, out2 */ #ifndef SQLITE_OMIT_UTF16 if( encoding!=SQLITE_UTF8 ){ rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC); - if( rc==SQLITE_TOOBIG ) goto too_big; + if( rc ){ + assert( rc==SQLITE_TOOBIG ); /* This is the only possible error here */ + goto too_big; + } if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z ); assert( VdbeMemDynamic(pOut)==0 ); @@ -1319,7 +1326,7 @@ case OP_ResultRow: { && db->xProgress(db->pProgressArg)!=0 ){ rc = SQLITE_INTERRUPT; - goto vdbe_error_halt; + goto abort_due_to_error; } #endif @@ -1329,7 +1336,7 @@ case OP_ResultRow: { if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){ assert( db->flags&SQLITE_CountRows ); assert( p->usesStmtJournal ); - break; + goto abort_due_to_error; } /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then @@ -1349,9 +1356,7 @@ case OP_ResultRow: { */ assert( p->iStatement==0 || db->flags&SQLITE_CountRows ); rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE); - if( NEVER(rc!=SQLITE_OK) ){ - break; - } + assert( rc==SQLITE_OK ); /* Invalidate all ephemeral cursor row caches */ p->cacheCtr = (p->cacheCtr + 2)|1; @@ -1674,7 +1679,8 @@ case OP_Function: { sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut)); rc = pCtx->isError; } - sqlite3VdbeDeleteAuxData(p, pCtx->iOp, pOp->p1); + sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1); + if( rc ) goto abort_due_to_error; } /* Copy the result of the function into register P3 */ @@ -1858,6 +1864,7 @@ case OP_Cast: { /* in1 */ rc = ExpandBlob(pIn1); sqlite3VdbeMemCast(pIn1, pOp->p2, encoding); UPDATE_MAX_BLOBSIZE(pIn1); + if( rc ) goto abort_due_to_error; break; } #endif /* SQLITE_OMIT_CAST */ @@ -2467,7 +2474,7 @@ case OP_Column: { */ if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; - goto op_column_error; + goto abort_due_to_error; } } @@ -2492,7 +2499,7 @@ case OP_Column: { if( pC->aRow==0 ){ memset(&sMem, 0, sizeof(sMem)); rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], !pC->isTable, &sMem); - if( rc!=SQLITE_OK ) goto op_column_error; + if( rc!=SQLITE_OK ) goto abort_due_to_error; zData = (u8*)sMem.z; }else{ zData = pC->aRow; @@ -2528,7 +2535,7 @@ case OP_Column: { || (offset64 > pC->payloadSize) ){ rc = SQLITE_CORRUPT_BKPT; - goto op_column_error; + goto abort_due_to_error; } }else{ t = 0; @@ -2601,15 +2608,13 @@ case OP_Column: { }else{ rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable, pDest); - if( rc==SQLITE_OK ){ - sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); - pDest->flags &= ~MEM_Ephem; - } + if( rc!=SQLITE_OK ) goto abort_due_to_error; + sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); + pDest->flags &= ~MEM_Ephem; } } op_column_out: -op_column_error: UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); break; @@ -2814,6 +2819,7 @@ case OP_Count: { /* out2 */ assert( pCrsr ); nEntry = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3BtreeCount(pCrsr, &nEntry); + if( rc ) goto abort_due_to_error; pOut = out2Prerelease(p, pOp); pOut->u.i = nEntry; break; @@ -2991,6 +2997,7 @@ case OP_Savepoint: { } } } + if( rc ) goto abort_due_to_error; break; } @@ -3027,7 +3034,7 @@ case OP_AutoCommit: { sqlite3VdbeError(p, "cannot commit transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; - break; + goto abort_due_to_error; }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; }else{ @@ -3054,6 +3061,7 @@ case OP_AutoCommit: { "cannot commit - no transaction is active")); rc = SQLITE_ERROR; + goto abort_due_to_error; } break; } @@ -3176,6 +3184,7 @@ case OP_Transaction: { p->expired = 1; rc = SQLITE_SCHEMA; } + if( rc ) goto abort_due_to_error; break; } @@ -3245,6 +3254,7 @@ case OP_SetCookie: { sqlite3ExpirePreparedStatements(db); p->expired = 0; } + if( rc ) goto abort_due_to_error; break; } @@ -3342,7 +3352,7 @@ case OP_OpenWrite: if( p->expired ){ rc = SQLITE_ABORT_ROLLBACK; - break; + goto abort_due_to_error; } nField = 0; @@ -3376,10 +3386,7 @@ case OP_OpenWrite: ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ - if( NEVER(p2<2) ) { - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; - } + assert( p2>=2 ); } if( pOp->p4type==P4_KEYINFO ){ pKeyInfo = pOp->p4.pKeyInfo; @@ -3417,6 +3424,7 @@ open_cursor_set_hints: #endif sqlite3BtreeCursorHintFlags(pCur->uc.pCursor, (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); + if( rc ) goto abort_due_to_error; break; } @@ -3493,6 +3501,7 @@ case OP_OpenEphemeral: { pCx->isTable = 1; } } + if( rc ) goto abort_due_to_error; pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); break; } @@ -3518,6 +3527,7 @@ case OP_SorterOpen: { assert( pCx->pKeyInfo->db==db ); assert( pCx->pKeyInfo->enc==ENC(db) ); rc = sqlite3VdbeSorterInit(db, pOp->p3, pCx); + if( rc ) goto abort_due_to_error; break; } @@ -3980,7 +3990,7 @@ case OP_Found: { /* jump, in3 */ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res); sqlite3DbFree(db, pFree); if( rc!=SQLITE_OK ){ - break; + goto abort_due_to_error; } pC->seekResult = res; alreadyExists = (res==0); @@ -4052,6 +4062,7 @@ case OP_NotExists: { /* jump, in3 */ goto jump_to_p2; } } + if( rc ) goto abort_due_to_error; break; } @@ -4194,7 +4205,8 @@ case OP_NewRowid: { /* out2 */ 0, &res))==SQLITE_OK) && (res==0) && (++cnt<100)); - if( rc==SQLITE_OK && res==0 ){ + if( rc ) goto abort_due_to_error; + if( res==0 ){ rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } @@ -4308,7 +4320,8 @@ case OP_InsertInt: { pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ - if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ + if( rc ) goto abort_due_to_error; + if( db->xUpdateCallback && pOp->p4.z ){ zDb = db->aDb[pC->iDb].zName; zTbl = pOp->p4.z; op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); @@ -4398,7 +4411,8 @@ case OP_Delete: { pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ - if( rc==SQLITE_OK && hasUpdateCallback ){ + if( rc ) goto abort_due_to_error; + if( hasUpdateCallback ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, db->aDb[pC->iDb].zName, pOp->p4.z, pC->movetoTarget); assert( pC->iDb>=0 ); @@ -4447,6 +4461,7 @@ case OP_SorterCompare: { res = 0; rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res); VdbeBranchTaken(res!=0,2); + if( rc ) goto abort_due_to_error; if( res ) goto jump_to_p2; break; }; @@ -4472,6 +4487,7 @@ case OP_SorterData: { rc = sqlite3VdbeSorterRowkey(pC, pOut); assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); assert( pOp->p1>=0 && pOp->p1nCursor ); + if( rc ) goto abort_due_to_error; p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; break; } @@ -4560,6 +4576,7 @@ case OP_RowData: { }else{ rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z); } + if( rc ) goto abort_due_to_error; pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); REGISTER_TRACE(pOp->p2, pOut); @@ -4600,6 +4617,7 @@ case OP_Rowid: { /* out2 */ assert( pModule->xRowid ); rc = pModule->xRowid(pC->uc.pVCur, &v); sqlite3VtabImportErrmsg(p, pVtab); + if( rc ) goto abort_due_to_error; #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ assert( pC->eCurType==CURTYPE_BTREE ); @@ -4670,6 +4688,7 @@ case OP_Last: { /* jump */ #ifdef SQLITE_DEBUG pC->seekOp = OP_Last; #endif + if( rc ) goto abort_due_to_error; if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; @@ -4734,6 +4753,7 @@ case OP_Rewind: { /* jump */ pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } + if( rc ) goto abort_due_to_error; pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2nOp ); VdbeBranchTaken(res!=0,2); @@ -4846,6 +4866,7 @@ case OP_Next: /* jump */ next_tail: pC->cacheStatus = CACHE_STALE; VdbeBranchTaken(res==0,2); + if( rc ) goto abort_due_to_error; if( res==0 ){ pC->nullRow = 0; p->aCounter[pOp->p5]++; @@ -4896,19 +4917,19 @@ case OP_IdxInsert: { /* in2 */ assert( pC->eCurType==CURTYPE_BTREE || pOp->opcode==OP_SorterInsert ); assert( pC->isTable==0 ); rc = ExpandBlob(pIn2); - if( rc==SQLITE_OK ){ - if( pOp->opcode==OP_SorterInsert ){ - rc = sqlite3VdbeSorterWrite(pC, pIn2); - }else{ - nKey = pIn2->n; - zKey = pIn2->z; - rc = sqlite3BtreeInsert(pC->uc.pCursor, zKey, nKey, "", 0, 0, pOp->p3, - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) - ); - assert( pC->deferredMoveto==0 ); - pC->cacheStatus = CACHE_STALE; - } + if( rc ) goto abort_due_to_error; + if( pOp->opcode==OP_SorterInsert ){ + rc = sqlite3VdbeSorterWrite(pC, pIn2); + }else{ + nKey = pIn2->n; + zKey = pIn2->z; + rc = sqlite3BtreeInsert(pC->uc.pCursor, zKey, nKey, "", 0, 0, pOp->p3, + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) + ); + assert( pC->deferredMoveto==0 ); + pC->cacheStatus = CACHE_STALE; } + if( rc) goto abort_due_to_error; break; } @@ -4939,8 +4960,10 @@ case OP_IdxDelete: { r.default_rc = 0; r.aMem = &aMem[pOp->p2]; rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); - if( rc==SQLITE_OK && res==0 ){ + if( rc ) goto abort_due_to_error; + if( res==0 ){ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); + if( rc ) goto abort_due_to_error; } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; @@ -5115,6 +5138,7 @@ case OP_IdxGE: { /* jump */ res++; } VdbeBranchTaken(res>0,2); + if( rc ) goto abort_due_to_error; if( res>0 ) goto jump_to_p2; break; } @@ -5150,6 +5174,7 @@ case OP_Destroy: { /* out2 */ if( db->nVdbeRead > db->nVDestroy+1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; + goto abort_due_to_error; }else{ iDb = pOp->p3; assert( DbMaskTest(p->btreeMask, iDb) ); @@ -5157,8 +5182,9 @@ case OP_Destroy: { /* out2 */ rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); pOut->flags = MEM_Int; pOut->u.i = iMoved; + if( rc ) goto abort_due_to_error; #ifndef SQLITE_OMIT_AUTOVACUUM - if( rc==SQLITE_OK && iMoved!=0 ){ + if( iMoved!=0 ){ sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1); /* All OP_Destroy operations occur on the same btree */ assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 ); @@ -5204,6 +5230,7 @@ case OP_Clear: { aMem[pOp->p3].u.i += nChange; } } + if( rc ) goto abort_due_to_error; break; } @@ -5227,6 +5254,7 @@ case OP_ResetSorter: { assert( pC->eCurType==CURTYPE_BTREE ); assert( pC->isEphemeral ); rc = sqlite3BtreeClearTableOfCursor(pC->uc.pCursor); + if( rc ) goto abort_due_to_error; } break; } @@ -5275,6 +5303,7 @@ case OP_CreateTable: { /* out2 */ flags = BTREE_BLOBKEY; } rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags); + if( rc ) goto abort_due_to_error; pOut->u.i = pgno; break; } @@ -5315,7 +5344,7 @@ case OP_ParseSchema: { "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", db->aDb[iDb].zName, zMaster, pOp->p4.z); if( zSql==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ assert( db->init.busy==0 ); db->init.busy = 1; @@ -5327,9 +5356,12 @@ case OP_ParseSchema: { db->init.busy = 0; } } - if( rc ) sqlite3ResetAllSchemasOfConnection(db); - if( rc==SQLITE_NOMEM ){ - goto no_mem; + if( rc ){ + sqlite3ResetAllSchemasOfConnection(db); + if( rc==SQLITE_NOMEM ){ + goto no_mem; + } + goto abort_due_to_error; } break; } @@ -5344,6 +5376,7 @@ case OP_ParseSchema: { case OP_LoadAnalysis: { assert( pOp->p1>=0 && pOp->p1nDb ); rc = sqlite3AnalysisLoad(db, pOp->p1); + if( rc ) goto abort_due_to_error; break; } #endif /* !defined(SQLITE_OMIT_ANALYZE) */ @@ -5603,7 +5636,7 @@ case OP_Program: { /* jump */ if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ rc = SQLITE_ERROR; sqlite3VdbeError(p, "too many levels of trigger recursion"); - break; + goto abort_due_to_error; } /* Register pRt is used to store the memory required to save the state @@ -5663,6 +5696,9 @@ case OP_Program: { /* jump */ pFrame->lastRowid = lastRowid; pFrame->nChange = p->nChange; pFrame->nDbChange = p->db->nChange; + assert( pFrame->pAuxData==0 ); + pFrame->pAuxData = p->pAuxData; + p->pAuxData = 0; p->nChange = 0; p->pFrame = pFrame; p->aMem = aMem = &VdbeFrameMem(pFrame)[-1]; @@ -5967,6 +6003,7 @@ case OP_AggStep: { rc = pCtx->isError; } sqlite3VdbeMemRelease(&t); + if( rc ) goto abort_due_to_error; }else{ assert( t.flags==MEM_Null ); } @@ -5999,6 +6036,7 @@ case OP_AggFinal: { rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); if( rc ){ sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem)); + goto abort_due_to_error; } sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); @@ -6034,7 +6072,8 @@ case OP_Checkpoint: { || pOp->p2==SQLITE_CHECKPOINT_TRUNCATE ); rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]); - if( rc==SQLITE_BUSY ){ + if( rc ){ + if( rc!=SQLITE_BUSY ) goto abort_due_to_error; rc = SQLITE_OK; aRes[0] = 1; } @@ -6107,7 +6146,7 @@ case OP_JournalMode: { /* out2 */ "cannot change %s wal mode from within a transaction", (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") ); - break; + goto abort_due_to_error; }else{ if( eOld==PAGER_JOURNALMODE_WAL ){ @@ -6137,9 +6176,7 @@ case OP_JournalMode: { /* out2 */ } #endif /* ifndef SQLITE_OMIT_WAL */ - if( rc ){ - eNew = eOld; - } + if( rc ) eNew = eOld; eNew = sqlite3PagerSetJournalMode(pPager, eNew); pOut->flags = MEM_Str|MEM_Static|MEM_Term; @@ -6147,6 +6184,7 @@ case OP_JournalMode: { /* out2 */ pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); + if( rc ) goto abort_due_to_error; break; }; #endif /* SQLITE_OMIT_PRAGMA */ @@ -6161,6 +6199,7 @@ case OP_JournalMode: { /* out2 */ case OP_Vacuum: { assert( p->readOnly==0 ); rc = sqlite3RunVacuum(&p->zErrMsg, db); + if( rc ) goto abort_due_to_error; break; } #endif @@ -6181,7 +6220,8 @@ case OP_IncrVacuum: { /* jump */ pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); VdbeBranchTaken(rc==SQLITE_DONE,2); - if( rc==SQLITE_DONE ){ + if( rc ){ + if( rc!=SQLITE_DONE ) goto abort_due_to_error; rc = SQLITE_OK; goto jump_to_p2; } @@ -6232,9 +6272,12 @@ case OP_TableLock: { assert( DbMaskTest(p->btreeMask, p1) ); assert( isWriteLock==0 || isWriteLock==1 ); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlite3VdbeError(p, "database table is locked: %s", z); + if( rc ){ + if( (rc&0xFF)==SQLITE_LOCKED ){ + const char *z = pOp->p4.z; + sqlite3VdbeError(p, "database table is locked: %s", z); + } + goto abort_due_to_error; } } break; @@ -6256,6 +6299,7 @@ case OP_VBegin: { pVTab = pOp->p4.pVtab; rc = sqlite3VtabBegin(db, pVTab); if( pVTab ) sqlite3VtabImportErrmsg(p, pVTab->pVtab); + if( rc ) goto abort_due_to_error; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -6284,6 +6328,7 @@ case OP_VCreate: { rc = sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg); } sqlite3VdbeMemRelease(&sMem); + if( rc ) goto abort_due_to_error; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -6298,6 +6343,7 @@ case OP_VDestroy: { db->nVDestroy++; rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); db->nVDestroy--; + if( rc ) goto abort_due_to_error; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -6321,25 +6367,25 @@ case OP_VOpen: { pVtab = pOp->p4.pVtab->pVtab; if( pVtab==0 || NEVER(pVtab->pModule==0) ){ rc = SQLITE_LOCKED; - break; + goto abort_due_to_error; } pModule = pVtab->pModule; rc = pModule->xOpen(pVtab, &pVCur); sqlite3VtabImportErrmsg(p, pVtab); - if( SQLITE_OK==rc ){ - /* Initialize sqlite3_vtab_cursor base class */ - pVCur->pVtab = pVtab; + if( rc ) goto abort_due_to_error; - /* Initialize vdbe cursor object */ - pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); - if( pCur ){ - pCur->uc.pVCur = pVCur; - pVtab->nRef++; - }else{ - assert( db->mallocFailed ); - pModule->xClose(pVCur); - goto no_mem; - } + /* Initialize sqlite3_vtab_cursor base class */ + pVCur->pVtab = pVtab; + + /* Initialize vdbe cursor object */ + pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB); + if( pCur ){ + pCur->uc.pVCur = pVCur; + pVtab->nRef++; + }else{ + assert( db->mallocFailed ); + pModule->xClose(pVCur); + goto no_mem; } break; } @@ -6401,9 +6447,8 @@ case OP_VFilter: { /* jump */ } rc = pModule->xFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg); sqlite3VtabImportErrmsg(p, pVtab); - if( rc==SQLITE_OK ){ - res = pModule->xEof(pVCur); - } + if( rc ) goto abort_due_to_error; + res = pModule->xEof(pVCur); pCur->nullRow = 0; VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; @@ -6452,6 +6497,7 @@ case OP_VColumn: { if( sqlite3VdbeMemTooBig(pDest) ){ goto too_big; } + if( rc ) goto abort_due_to_error; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -6487,9 +6533,8 @@ case OP_VNext: { /* jump */ */ rc = pModule->xNext(pCur->uc.pVCur); sqlite3VtabImportErrmsg(p, pVtab); - if( rc==SQLITE_OK ){ - res = pModule->xEof(pCur->uc.pVCur); - } + if( rc ) goto abort_due_to_error; + res = pModule->xEof(pCur->uc.pVCur); VdbeBranchTaken(!res,2); if( !res ){ /* If there is data, jump to P2 */ @@ -6521,11 +6566,11 @@ case OP_VRename: { testcase( pName->enc==SQLITE_UTF16BE ); testcase( pName->enc==SQLITE_UTF16LE ); rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); - if( rc==SQLITE_OK ){ - rc = pVtab->pModule->xRename(pVtab, pName->z); - sqlite3VtabImportErrmsg(p, pVtab); - p->expired = 0; - } + if( rc ) goto abort_due_to_error; + rc = pVtab->pModule->xRename(pVtab, pName->z); + sqlite3VtabImportErrmsg(p, pVtab); + p->expired = 0; + if( rc ) goto abort_due_to_error; break; } #endif @@ -6574,7 +6619,7 @@ case OP_VUpdate: { pVtab = pOp->p4.pVtab->pVtab; if( pVtab==0 || NEVER(pVtab->pModule==0) ){ rc = SQLITE_LOCKED; - break; + goto abort_due_to_error; } pModule = pVtab->pModule; nArg = pOp->p2; @@ -6606,6 +6651,7 @@ case OP_VUpdate: { }else{ p->nChange++; } + if( rc ) goto abort_due_to_error; } break; } @@ -6777,8 +6823,12 @@ default: { /* This is really OP_Noop and OP_Explain */ /* If we reach this point, it means that execution is finished with ** an error of some kind. */ -vdbe_error_halt: +abort_due_to_error: + if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; assert( rc ); + if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ + sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); + } p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", @@ -6809,34 +6859,23 @@ vdbe_return: too_big: sqlite3VdbeError(p, "string or blob too big"); rc = SQLITE_TOOBIG; - goto vdbe_error_halt; + goto abort_due_to_error; /* Jump to here if a malloc() fails. */ no_mem: sqlite3OomFault(db); sqlite3VdbeError(p, "out of memory"); - rc = SQLITE_NOMEM; - goto vdbe_error_halt; - - /* Jump to here for any other kind of fatal error. The "rc" variable - ** should hold the error number. - */ -abort_due_to_error: - assert( p->zErrMsg==0 ); - if( db->mallocFailed ) rc = SQLITE_NOMEM; - if( rc!=SQLITE_IOERR_NOMEM ){ - sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); - } - goto vdbe_error_halt; + rc = SQLITE_NOMEM_BKPT; + goto abort_due_to_error; /* Jump to here if the sqlite3_interrupt() API sets the interrupt ** flag. */ abort_due_to_interrupt: assert( db->u1.isInterrupted ); - rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_INTERRUPT; + rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT; p->rc = rc; sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); - goto vdbe_error_halt; + goto abort_due_to_error; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 839c2039be..b53952fd44 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -87,7 +87,7 @@ struct VdbeCursor { #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ - Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ + Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Pgno pgnoRoot; /* Root page of the open btree cursor */ i16 nField; /* Number of fields in the header */ u16 nHdrParsed; /* Number of header fields parsed so far */ @@ -161,6 +161,7 @@ struct VdbeFrame { VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ void *token; /* Copy of SubProgram.token */ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ + AuxData *pAuxData; /* Linked list of auxdata allocations */ int nCursor; /* Number of entries in apCsr */ int pc; /* Program Counter in parent (calling) frame */ int nOp; /* Size of aOp array */ @@ -443,7 +444,7 @@ u8 sqlite3VdbeOneByteSerialTypeLen(u8); u32 sqlite3VdbeSerialType(Mem*, int, u32*); u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); -void sqlite3VdbeDeleteAuxData(Vdbe*, int, int); +void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int); int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index fce43630e7..c2038137d1 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -471,7 +471,7 @@ void sqlite3_result_error_toobig(sqlite3_context *pCtx){ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); - pCtx->isError = SQLITE_NOMEM; + pCtx->isError = SQLITE_NOMEM_BKPT; pCtx->fErrorOrAux = 1; sqlite3OomFault(pCtx->pOut->db); } @@ -547,7 +547,7 @@ static int sqlite3Step(Vdbe *p){ db = p->db; if( db->mallocFailed ){ p->rc = SQLITE_NOMEM; - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } if( p->pc<=0 && p->expired ){ @@ -610,7 +610,7 @@ static int sqlite3Step(Vdbe *p){ db->errCode = rc; if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){ - p->rc = SQLITE_NOMEM; + p->rc = SQLITE_NOMEM_BKPT; } end_of_step: /* At this point local variable rc holds the value that should be @@ -677,7 +677,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){ v->rc = rc2; } else { v->zErrMsg = 0; - v->rc = rc = SQLITE_NOMEM; + v->rc = rc = SQLITE_NOMEM_BKPT; } } rc = sqlite3ApiExit(db, rc); @@ -1301,6 +1301,9 @@ int sqlite3_bind_blob( int nData, void (*xDel)(void*) ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( nData<0 ) return SQLITE_MISUSE_BKPT; +#endif return bindText(pStmt, i, zData, nData, xDel, 0); } int sqlite3_bind_blob64( diff --git a/src/vdbeaux.c b/src/vdbeaux.c index ce98edd0a5..9c75d1e078 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -129,7 +129,7 @@ static int growOpArray(Vdbe *v, int nOp){ p->nOpAlloc = p->szOpAlloc/sizeof(Op); v->aOp = pNew; } - return (pNew ? SQLITE_OK : SQLITE_NOMEM); + return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT); } #ifdef SQLITE_DEBUG @@ -1488,6 +1488,7 @@ void sqlite3VdbeFrameDelete(VdbeFrame *p){ sqlite3VdbeFreeCursor(p->v, apCsr[i]); } releaseMemArray(aMem, p->nChildMem); + sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0); sqlite3DbFree(p->v->db, p); } @@ -1530,7 +1531,7 @@ int sqlite3VdbeList( releaseMemArray(pMem, 8); p->pResultSet = 0; - if( p->rc==SQLITE_NOMEM ){ + if( p->rc==SQLITE_NOMEM_BKPT ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ sqlite3OomFault(db); @@ -2016,6 +2017,9 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ v->db->lastRowid = pFrame->lastRowid; v->nChange = pFrame->nChange; v->db->nChange = pFrame->nDbChange; + sqlite3VdbeDeleteAuxData(v->db, &v->pAuxData, -1, 0); + v->pAuxData = pFrame->pAuxData; + pFrame->pAuxData = 0; return pFrame->pc; } @@ -2047,7 +2051,7 @@ static void closeAllCursors(Vdbe *p){ } /* Delete any auxdata allocations made by the VM */ - if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0); + if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0); assert( p->pAuxData==0 ); } @@ -2119,7 +2123,7 @@ int sqlite3VdbeSetColName( assert( vardb->mallocFailed ){ assert( !zName || xDel!=SQLITE_DYNAMIC ); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } assert( p->aColName!=0 ); pColName = &(p->aColName[idx+var*p->nResColumn]); @@ -2136,7 +2140,9 @@ int sqlite3VdbeSetColName( */ static int vdbeCommit(sqlite3 *db, Vdbe *p){ int i; - int nTrans = 0; /* Number of databases with an active write-transaction */ + int nTrans = 0; /* Number of databases with an active write-transaction + ** that are candidates for a two-phase commit using a + ** master-journal */ int rc = SQLITE_OK; int needXcommit = 0; @@ -2164,10 +2170,28 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ + /* Whether or not a database might need a master journal depends upon + ** its journal mode (among other things). This matrix determines which + ** journal modes use a master journal and which do not */ + static const u8 aMJNeeded[] = { + /* DELETE */ 1, + /* PERSIST */ 1, + /* OFF */ 0, + /* TRUNCATE */ 1, + /* MEMORY */ 0, + /* WAL */ 0 + }; + Pager *pPager; /* Pager associated with pBt */ needXcommit = 1; - if( i!=1 ) nTrans++; sqlite3BtreeEnter(pBt); - rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt)); + pPager = sqlite3BtreePager(pBt); + if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF + && aMJNeeded[sqlite3PagerGetJournalMode(pPager)] + ){ + assert( i!=1 ); + nTrans++; + } + rc = sqlite3PagerExclusiveLock(pPager); sqlite3BtreeLeave(pBt); } } @@ -2225,7 +2249,6 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ #ifndef SQLITE_OMIT_DISKIO else{ sqlite3_vfs *pVfs = db->pVfs; - int needSync = 0; char *zMaster = 0; /* File-name for the master journal */ char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt); sqlite3_file *pMaster = 0; @@ -2237,7 +2260,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* Select a master journal file name */ nMainFile = sqlite3Strlen30(zMainFile); zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile); - if( zMaster==0 ) return SQLITE_NOMEM; + if( zMaster==0 ) return SQLITE_NOMEM_BKPT; do { u32 iRandom; if( retryCount ){ @@ -2285,9 +2308,6 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ continue; /* Ignore TEMP and :memory: databases */ } assert( zFile[0]!=0 ); - if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ - needSync = 1; - } rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); offset += sqlite3Strlen30(zFile)+1; if( rc!=SQLITE_OK ){ @@ -2302,8 +2322,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ /* Sync the master journal file. If the IOCAP_SEQUENTIAL device ** flag is set this is not required. */ - if( needSync - && 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) + if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL) && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL)) ){ sqlite3OsCloseFree(pMaster); @@ -2339,7 +2358,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ ** doing this the directory is synced again before any individual ** transaction files are deleted. */ - rc = sqlite3OsDelete(pVfs, zMaster, needSync); + rc = sqlite3OsDelete(pVfs, zMaster, 1); sqlite3DbFree(db, zMaster); zMaster = 0; if( rc ){ @@ -2527,7 +2546,7 @@ int sqlite3VdbeHalt(Vdbe *p){ */ if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; + p->rc = SQLITE_NOMEM_BKPT; } if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag); closeAllCursors(p); @@ -2688,7 +2707,7 @@ int sqlite3VdbeHalt(Vdbe *p){ p->magic = VDBE_MAGIC_HALT; checkActiveVdbeCnt(db); if( db->mallocFailed ){ - p->rc = SQLITE_NOMEM; + p->rc = SQLITE_NOMEM_BKPT; } /* If the auto-commit flag is set to true, then any locks that were held @@ -2875,8 +2894,7 @@ int sqlite3VdbeFinalize(Vdbe *p){ ** * the corresponding bit in argument mask is clear (where the first ** function parameter corresponds to bit 0 etc.). */ -void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){ - AuxData **pp = &pVdbe->pAuxData; +void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, int mask){ while( *pp ){ AuxData *pAux = *pp; if( (iOp<0) @@ -2887,7 +2905,7 @@ void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){ pAux->xDelete(pAux->pAux); } *pp = pAux->pNext; - sqlite3DbFree(pVdbe->db, pAux); + sqlite3DbFree(db, pAux); }else{ pp= &pAux->pNext; } @@ -3666,7 +3684,7 @@ static int vdbeCompareMemString( v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); n2 = v2==0 ? 0 : c2.n; rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2); - if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM; + if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM_BKPT; sqlite3VdbeMemRelease(&c1); sqlite3VdbeMemRelease(&c2); return rc; diff --git a/src/vdbemem.c b/src/vdbemem.c index 6fb7cebbcc..b5139fe32e 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -138,7 +138,7 @@ SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ sqlite3VdbeMemSetNull(pMem); pMem->z = 0; pMem->szMalloc = 0; - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; }else{ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); } @@ -196,7 +196,7 @@ int sqlite3VdbeMemMakeWriteable(Mem *pMem){ f = pMem->flags; if( (f&(MEM_Str|MEM_Blob)) && (pMem->szMalloc==0 || pMem->z!=pMem->zMalloc) ){ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; @@ -228,7 +228,7 @@ int sqlite3VdbeMemExpandBlob(Mem *pMem){ nByte = 1; } if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset(&pMem->z[pMem->n], 0, pMem->u.nZero); @@ -245,7 +245,7 @@ int sqlite3VdbeMemExpandBlob(Mem *pMem){ */ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){ if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; @@ -294,7 +294,7 @@ int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8 @@ -905,7 +905,7 @@ int sqlite3VdbeMemSetStr( testcase( nAlloc==31 ); testcase( nAlloc==32 ); if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memcpy(pMem->z, z, nAlloc); }else if( xDel==SQLITE_DYNAMIC ){ @@ -925,7 +925,7 @@ int sqlite3VdbeMemSetStr( #ifndef SQLITE_OMIT_UTF16 if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } #endif @@ -1186,7 +1186,6 @@ static int valueFromFunction( FuncDef *pFunc = 0; /* Function definition */ sqlite3_value *pVal = 0; /* New value */ int rc = SQLITE_OK; /* Return code */ - int nName; /* Size of function name in bytes */ ExprList *pList = 0; /* Function arguments */ int i; /* Iterator variable */ @@ -1194,8 +1193,7 @@ static int valueFromFunction( assert( (p->flags & EP_TokenOnly)==0 ); pList = p->x.pList; if( pList ) nVal = pList->nExpr; - nName = sqlite3Strlen30(p->u.zToken); - pFunc = sqlite3FindFunction(db, p->u.zToken, nName, nVal, enc, 0); + pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0); assert( pFunc ); if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) @@ -1206,7 +1204,7 @@ static int valueFromFunction( if( pList ){ apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal); if( apVal==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto value_from_function_out; } for(i=0; iop)==TK_UPLUS ) pExpr = pExpr->pLeft; + while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; /* Compressed expressions only appear when parsing the DEFAULT clause @@ -1390,7 +1388,7 @@ no_mem: #else assert( pCtx==0 ); sqlite3ValueFree(pVal); #endif - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } /* @@ -1457,15 +1455,10 @@ static void recordFunc( ** Register built-in functions used to help read ANALYZE data. */ void sqlite3AnalyzeFunctions(void){ - static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = { + static FuncDef aAnalyzeTableFuncs[] = { FUNCTION(sqlite_record, 1, 0, 0, recordFunc), }; - int i; - FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); - FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs); - for(i=0; inRec ) return SQLITE_CORRUPT_BKPT; if( pMem==0 ){ pMem = *ppVal = sqlite3ValueNew(db); - if( pMem==0 ) return SQLITE_NOMEM; + if( pMem==0 ) return SQLITE_NOMEM_BKPT; } sqlite3VdbeSerialGet(&a[iField-szField], t, pMem); pMem->enc = ENC(db); diff --git a/src/vdbesort.c b/src/vdbesort.c index 380772ee16..5913cda6d0 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -540,7 +540,7 @@ static int vdbePmaReadBlob( int nNew = MAX(128, p->nAlloc*2); while( nByte>nNew ) nNew = nNew*2; aNew = sqlite3Realloc(p->aAlloc, nNew); - if( !aNew ) return SQLITE_NOMEM; + if( !aNew ) return SQLITE_NOMEM_BKPT; p->nAlloc = nNew; p->aAlloc = aNew; } @@ -652,7 +652,7 @@ static int vdbePmaReaderSeek( int iBuf = pReadr->iReadOff % pgsz; if( pReadr->aBuffer==0 ){ pReadr->aBuffer = (u8*)sqlite3Malloc(pgsz); - if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM; + if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM_BKPT; pReadr->nBuffer = pgsz; } if( rc==SQLITE_OK && iBuf ){ @@ -968,7 +968,7 @@ int sqlite3VdbeSorterInit( pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); pCsr->uc.pSorter = pSorter; if( pSorter==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); @@ -1002,7 +1002,7 @@ int sqlite3VdbeSorterInit( assert( pSorter->iMemory==0 ); pSorter->nMemory = pgsz; pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz); - if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM; + if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM_BKPT; } } @@ -1324,7 +1324,7 @@ static int vdbeSortAllocUnpacked(SortSubtask *pTask){ pTask->pSorter->pKeyInfo, 0, 0, &pFree ); assert( pTask->pUnpacked==(UnpackedRecord*)pFree ); - if( pFree==0 ) return SQLITE_NOMEM; + if( pFree==0 ) return SQLITE_NOMEM_BKPT; pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField; pTask->pUnpacked->errCode = 0; } @@ -1399,7 +1399,7 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *)); if( !aSlot ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } while( p ){ @@ -1449,7 +1449,7 @@ static void vdbePmaWriterInit( memset(p, 0, sizeof(PmaWriter)); p->aBuffer = (u8*)sqlite3Malloc(nBuf); if( !p->aBuffer ){ - p->eFWErr = SQLITE_NOMEM; + p->eFWErr = SQLITE_NOMEM_BKPT; }else{ p->iBufEnd = p->iBufStart = (iStart % nBuf); p->iWriteOff = iStart - p->iBufStart; @@ -1737,7 +1737,7 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){ pSorter->nMemory = sqlite3MallocSize(aMem); }else if( pSorter->list.aMemory ){ pSorter->list.aMemory = sqlite3Malloc(pSorter->nMemory); - if( !pSorter->list.aMemory ) return SQLITE_NOMEM; + if( !pSorter->list.aMemory ) return SQLITE_NOMEM_BKPT; } rc = vdbeSorterCreateThread(pTask, vdbeSorterFlushThread, pCtx); @@ -1828,7 +1828,7 @@ int sqlite3VdbeSorterWrite( if( nNew < nMin ) nNew = nMin; aNew = sqlite3Realloc(pSorter->list.aMemory, nNew); - if( !aNew ) return SQLITE_NOMEM; + if( !aNew ) return SQLITE_NOMEM_BKPT; pSorter->list.pList = (SorterRecord*)&aNew[iListOff]; pSorter->list.aMemory = aNew; pSorter->nMemory = nNew; @@ -1842,7 +1842,7 @@ int sqlite3VdbeSorterWrite( }else{ pNew = (SorterRecord *)sqlite3Malloc(nReq); if( pNew==0 ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pNew->u.pNext = pSorter->list.pList; } @@ -1989,7 +1989,7 @@ static int vdbeIncrMergerNew( pTask->file2.iEof += pIncr->mxSz; }else{ vdbeMergeEngineFree(pMerger); - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } return rc; } @@ -2294,10 +2294,10 @@ static int vdbeMergeEngineLevel0( int rc = SQLITE_OK; *ppOut = pNew = vdbeMergeEngineNew(nPMA); - if( pNew==0 ) rc = SQLITE_NOMEM; + if( pNew==0 ) rc = SQLITE_NOMEM_BKPT; for(i=0; iaReadr[i]; rc = vdbePmaReaderInit(pTask, &pTask->file, iOff, pReadr, &nDummy); iOff = pReadr->iEof; @@ -2365,7 +2365,7 @@ static int vdbeSorterAddToTree( if( pReadr->pIncr==0 ){ MergeEngine *pNew = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT); if( pNew==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ rc = vdbeIncrMergerNew(pTask, pNew, &pReadr->pIncr); } @@ -2410,7 +2410,7 @@ static int vdbeSorterMergeTreeBuild( assert( pSorter->bUseThreads || pSorter->nTask==1 ); if( pSorter->nTask>1 ){ pMain = vdbeMergeEngineNew(pSorter->nTask); - if( pMain==0 ) rc = SQLITE_NOMEM; + if( pMain==0 ) rc = SQLITE_NOMEM_BKPT; } #endif @@ -2428,7 +2428,7 @@ static int vdbeSorterMergeTreeBuild( int i; int iSeq = 0; pRoot = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT); - if( pRoot==0 ) rc = SQLITE_NOMEM; + if( pRoot==0 ) rc = SQLITE_NOMEM_BKPT; for(i=0; inPMA && rc==SQLITE_OK; i += SORTER_MAX_MERGE_COUNT){ MergeEngine *pMerger = 0; /* New level-0 PMA merger */ int nReader; /* Number of level-0 PMAs to merge */ @@ -2499,7 +2499,7 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ if( rc==SQLITE_OK ){ pReadr = (PmaReader*)sqlite3DbMallocZero(db, sizeof(PmaReader)); pSorter->pReader = pReadr; - if( pReadr==0 ) rc = SQLITE_NOMEM; + if( pReadr==0 ) rc = SQLITE_NOMEM_BKPT; } if( rc==SQLITE_OK ){ rc = vdbeIncrMergerNew(pLast, pMain, &pReadr->pIncr); @@ -2676,7 +2676,7 @@ int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){ pSorter = pCsr->uc.pSorter; pKey = vdbeSorterRowkey(pSorter, &nKey); if( sqlite3VdbeMemClearAndResize(pOut, nKey) ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pOut->n = nKey; MemSetTypeFlag(pOut, MEM_Blob); @@ -2721,7 +2721,7 @@ int sqlite3VdbeSorterCompare( char *p; r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo,0,0,&p); assert( pSorter->pUnpacked==(UnpackedRecord*)p ); - if( r2==0 ) return SQLITE_NOMEM; + if( r2==0 ) return SQLITE_NOMEM_BKPT; r2->nField = nKeyCol; } assert( r2->nField==nKeyCol ); diff --git a/src/vtab.c b/src/vtab.c index e8794e687d..ad8caef3bc 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -506,13 +506,13 @@ static int vtabCallConstructor( zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); if( !zModuleName ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pVTable = sqlite3DbMallocZero(db, sizeof(VTable)); if( !pVTable ){ sqlite3DbFree(db, zModuleName); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pVTable->db = db; pVTable->pMod = pMod; @@ -564,10 +564,10 @@ static int vtabCallConstructor( pTab->pVTable = pVTable; for(iCol=0; iColnCol; iCol++){ - char *zType = pTab->aCol[iCol].zType; + char *zType = (char*)sqlite3StrNext(pTab->aCol[iCol].zName); int nType; int i = 0; - if( !zType ){ + if( !zType[0] ){ pTab->tabFlags |= oooHidden; continue; } @@ -655,7 +655,7 @@ static int growVTrans(sqlite3 *db){ int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR); db->aVTrans = aVTrans; @@ -747,7 +747,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ pParse->declareVtab = 1; pParse->db = db; @@ -1059,8 +1059,8 @@ FuncDef *sqlite3VtabOverloadFunction( return pDef; } *pNew = *pDef; - pNew->zName = (char *)&pNew[1]; - memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1); + pNew->zName = (const char*)&pNew[1]; + memcpy((char*)&pNew[1], pDef->zName, sqlite3Strlen30(pDef->zName)+1); pNew->xSFunc = xSFunc; pNew->pUserData = pArg; pNew->funcFlags |= SQLITE_FUNC_EPHEM; diff --git a/src/wal.c b/src/wal.c index 7cf2cabffb..bbac1bd6d0 100644 --- a/src/wal.c +++ b/src/wal.c @@ -546,7 +546,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); if( !apNew ){ *ppPage = 0; - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset((void*)&apNew[pWal->nWiData], 0, sizeof(u32*)*(iPage+1-pWal->nWiData)); @@ -558,7 +558,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ if( pWal->apWiData[iPage]==0 ){ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); - if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM; + if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; }else{ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] @@ -1173,7 +1173,7 @@ static int walIndexRecover(Wal *pWal){ szFrame = szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc64(szFrame); if( !aFrame ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; goto recovery_error; } aData = &aFrame[WAL_FRAME_HDRSIZE]; @@ -1311,7 +1311,7 @@ int sqlite3WalOpen( *ppWal = 0; pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile); if( !pRet ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } pRet->pVfs = pVfs; @@ -1575,7 +1575,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ + iLast*sizeof(ht_slot); p = (WalIterator *)sqlite3_malloc64(nByte); if( !p ){ - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memset(p, 0, nByte); p->nSegment = nSegment; @@ -1587,7 +1587,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) ); if( !aTmp ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; } for(i=0; rc==SQLITE_OK && ipData; #endif @@ -2909,7 +2909,7 @@ static int walRewriteChecksums(Wal *pWal, u32 iLast){ i64 iCksumOff; aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE); - if( aBuf==0 ) return SQLITE_NOMEM; + if( aBuf==0 ) return SQLITE_NOMEM_BKPT; /* Find the checksum values to use as input for the recalculating the ** first checksum. If the first frame is frame 1 (implying that the current @@ -3062,10 +3062,16 @@ int sqlite3WalFrames( assert( rc==SQLITE_OK || iWrite==0 ); if( iWrite>=iFirst ){ i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; + void *pData; if( pWal->iReCksum==0 || iWriteiReCksum ){ pWal->iReCksum = iWrite; } - rc = sqlite3OsWrite(pWal->pWalFd, p->pData, szPage, iOff); +#if defined(SQLITE_HAS_CODEC) + if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM; +#else + pData = p->pData; +#endif + rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff); if( rc ) return rc; p->flags &= ~PGHDR_WAL_APPEND; continue; @@ -3379,7 +3385,7 @@ int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){ pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr)); if( pRet==0 ){ - rc = SQLITE_NOMEM; + rc = SQLITE_NOMEM_BKPT; }else{ memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr)); *ppSnapshot = (sqlite3_snapshot*)pRet; diff --git a/src/where.c b/src/where.c index abe1015b95..21f6c1aab6 100644 --- a/src/where.c +++ b/src/where.c @@ -31,8 +31,8 @@ static int whereLoopResize(sqlite3*, WhereLoop*, int); /* ** Return the estimated number of output rows from a WHERE clause */ -u64 sqlite3WhereOutputRowCount(WhereInfo *pWInfo){ - return sqlite3LogEstToInt(pWInfo->nRowOut); +LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){ + return pWInfo->nRowOut; } /* @@ -934,7 +934,6 @@ static sqlite3_index_info *allocateIndexInfo( */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; - int i; int rc; TRACE_IDX_INPUTS(p); @@ -953,12 +952,16 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = 0; +#if 0 + /* This error is now caught by the caller. + ** Search for "xBestIndex malfunction" below */ for(i=0; inConstraint; i++){ if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){ sqlite3ErrorMsg(pParse, "table %s: xBestIndex returned an invalid plan", pTab->zName); } } +#endif return pParse->nErr; } @@ -1561,7 +1564,8 @@ static int whereEqualScanEst( pBuilder->nRecValid = nEq; whereKeyStats(pParse, p, pRec, 0, a); - WHERETRACE(0x10,("equality scan regions: %d\n", (int)a[1])); + WHERETRACE(0x10,("equality scan regions %s(%d): %d\n", + p->zName, nEq-1, (int)a[1])); *pnRow = a[1]; return rc; @@ -1736,7 +1740,7 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ if( p->nLSlot>=n ) return SQLITE_OK; n = (n+7)&~7; paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n); - if( paNew==0 ) return SQLITE_NOMEM; + if( paNew==0 ) return SQLITE_NOMEM_BKPT; memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot); if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm); p->aLTerm = paNew; @@ -1751,7 +1755,7 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ whereLoopClearUnion(db, pTo); if( whereLoopResize(db, pTo, pFrom->nLTerm) ){ memset(&pTo->u, 0, sizeof(pTo->u)); - return SQLITE_NOMEM; + return SQLITE_NOMEM_BKPT; } memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0])); @@ -2033,7 +2037,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ if( p==0 ){ /* Allocate a new WhereLoop to add to the end of the list */ *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop)); - if( p==0 ) return SQLITE_NOMEM; + if( p==0 ) return SQLITE_NOMEM_BKPT; whereLoopInit(p); p->pNextLoop = 0; }else{ @@ -2189,7 +2193,7 @@ static int whereLoopAddBtreeIndex( WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ pNew = pBuilder->pNew; - if( db->mallocFailed ) return SQLITE_NOMEM; + if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); @@ -2553,7 +2557,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ */ static int whereLoopAddBtree( WhereLoopBuilder *pBuilder, /* WHERE clause information */ - Bitmask mExtra /* Extra prerequesites for using this table */ + Bitmask mPrereq /* Extra prerequesites for using this table */ ){ WhereInfo *pWInfo; /* WHERE analysis context */ Index *pProbe; /* An index we are evaluating */ @@ -2653,7 +2657,7 @@ static int whereLoopAddBtree( pNew->nOut = 43; assert( 43==sqlite3LogEst(20) ); pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut); pNew->wsFlags = WHERE_AUTO_INDEX; - pNew->prereq = mExtra | pTerm->prereqRight; + pNew->prereq = mPrereq | pTerm->prereqRight; rc = whereLoopInsert(pBuilder, pNew); } } @@ -2674,7 +2678,7 @@ static int whereLoopAddBtree( pNew->nLTerm = 0; pNew->iSortIdx = 0; pNew->rSetup = 0; - pNew->prereq = mExtra; + pNew->prereq = mPrereq; pNew->nOut = rSize; pNew->u.btree.pIndex = pProbe; b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); @@ -2747,12 +2751,160 @@ static int whereLoopAddBtree( } #ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** Argument pIdxInfo is already populated with all constraints that may +** be used by the virtual table identified by pBuilder->pNew->iTab. This +** function marks a subset of those constraints usable, invokes the +** xBestIndex method and adds the returned plan to pBuilder. +** +** A constraint is marked usable if: +** +** * Argument mUsable indicates that its prerequisites are available, and +** +** * It is not one of the operators specified in the mExclude mask passed +** as the fourth argument (which in practice is either WO_IN or 0). +** +** Argument mPrereq is a mask of tables that must be scanned before the +** virtual table in question. These are added to the plans prerequisites +** before it is added to pBuilder. +** +** Output parameter *pbIn is set to true if the plan added to pBuilder +** uses one or more WO_IN terms, or false otherwise. +*/ +static int whereLoopAddVirtualOne( + WhereLoopBuilder *pBuilder, + Bitmask mPrereq, /* Mask of tables that must be used. */ + Bitmask mUsable, /* Mask of usable tables */ + u16 mExclude, /* Exclude terms using these operators */ + sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ + int *pbIn /* OUT: True if plan uses an IN(...) op */ +){ + WhereClause *pWC = pBuilder->pWC; + struct sqlite3_index_constraint *pIdxCons; + struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; + int i; + int mxTerm; + int rc = SQLITE_OK; + WhereLoop *pNew = pBuilder->pNew; + Parse *pParse = pBuilder->pWInfo->pParse; + struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; + int nConstraint = pIdxInfo->nConstraint; + + assert( (mUsable & mPrereq)==mPrereq ); + *pbIn = 0; + pNew->prereq = mPrereq; + + /* Set the usable flag on the subset of constraints identified by + ** arguments mUsable and mExclude. */ + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; ia[pIdxCons->iTermOffset]; + pIdxCons->usable = 0; + if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight + && (pTerm->eOperator & mExclude)==0 + ){ + pIdxCons->usable = 1; + } + } + + /* Initialize the output fields of the sqlite3_index_info structure */ + memset(pUsage, 0, sizeof(pUsage[0])*nConstraint); + assert( pIdxInfo->needToFreeIdxStr==0 ); + pIdxInfo->idxStr = 0; + pIdxInfo->idxNum = 0; + pIdxInfo->orderByConsumed = 0; + pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; + pIdxInfo->estimatedRows = 25; + pIdxInfo->idxFlags = 0; + pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; + + /* Invoke the virtual table xBestIndex() method */ + rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); + if( rc ) return rc; + + mxTerm = -1; + assert( pNew->nLSlot>=nConstraint ); + for(i=0; iaLTerm[i] = 0; + pNew->u.vtab.omitMask = 0; + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; i=0 ){ + WhereTerm *pTerm; + int j = pIdxCons->iTermOffset; + if( iTerm>=nConstraint + || j<0 + || j>=pWC->nTerm + || pNew->aLTerm[iTerm]!=0 + || pIdxCons->usable==0 + ){ + rc = SQLITE_ERROR; + sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); + return rc; + } + testcase( iTerm==nConstraint-1 ); + testcase( j==0 ); + testcase( j==pWC->nTerm-1 ); + pTerm = &pWC->a[j]; + pNew->prereq |= pTerm->prereqRight; + assert( iTermnLSlot ); + pNew->aLTerm[iTerm] = pTerm; + if( iTerm>mxTerm ) mxTerm = iTerm; + testcase( iTerm==15 ); + testcase( iTerm==16 ); + if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<eOperator & WO_IN)!=0 ){ + /* A virtual table that is constrained by an IN clause may not + ** consume the ORDER BY clause because (1) the order of IN terms + ** is not necessarily related to the order of output terms and + ** (2) Multiple outputs from a single IN value will not merge + ** together. */ + pIdxInfo->orderByConsumed = 0; + pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; + *pbIn = 1; assert( (mExclude & WO_IN)==0 ); + } + } + } + + pNew->nLTerm = mxTerm+1; + assert( pNew->nLTerm<=pNew->nLSlot ); + pNew->u.vtab.idxNum = pIdxInfo->idxNum; + pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; + pIdxInfo->needToFreeIdxStr = 0; + pNew->u.vtab.idxStr = pIdxInfo->idxStr; + pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? + pIdxInfo->nOrderBy : 0); + pNew->rSetup = 0; + pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); + pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); + + /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated + ** that the scan will visit at most one row. Clear it otherwise. */ + if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){ + pNew->wsFlags |= WHERE_ONEROW; + }else{ + pNew->wsFlags &= ~WHERE_ONEROW; + } + whereLoopInsert(pBuilder, pNew); + if( pNew->u.vtab.needFree ){ + sqlite3_free(pNew->u.vtab.idxStr); + pNew->u.vtab.needFree = 0; + } + WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", + *pbIn, (sqlite3_uint64)mPrereq, + (sqlite3_uint64)(pNew->prereq & ~mPrereq))); + + return SQLITE_OK; +} + + /* ** Add all WhereLoop objects for a table of the join identified by ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. ** -** If there are no LEFT or CROSS JOIN joins in the query, both mExtra and -** mUnusable are set to 0. Otherwise, mExtra is a mask of all FROM clause +** If there are no LEFT or CROSS JOIN joins in the query, both mPrereq and +** mUnusable are set to 0. Otherwise, mPrereq is a mask of all FROM clause ** entries that occur before the virtual table in the FROM clause and are ** separated from it by at least one LEFT or CROSS JOIN. Similarly, the ** mUnusable mask contains all FROM clause entries that occur after the @@ -2763,188 +2915,122 @@ static int whereLoopAddBtree( ** ** ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6; ** -** then mExtra corresponds to (t1, t2) and mUnusable to (t5, t6). +** then mPrereq corresponds to (t1, t2) and mUnusable to (t5, t6). ** -** All the tables in mExtra must be scanned before the current virtual +** All the tables in mPrereq must be scanned before the current virtual ** table. So any terms for which all prerequisites are satisfied by -** mExtra may be specified as "usable" in all calls to xBestIndex. +** mPrereq may be specified as "usable" in all calls to xBestIndex. ** Conversely, all tables in mUnusable must be scanned after the current ** virtual table, so any terms for which the prerequisites overlap with ** mUnusable should always be configured as "not-usable" for xBestIndex. */ static int whereLoopAddVirtual( WhereLoopBuilder *pBuilder, /* WHERE clause information */ - Bitmask mExtra, /* Tables that must be scanned before this one */ + Bitmask mPrereq, /* Tables that must be scanned before this one */ Bitmask mUnusable /* Tables that must be scanned after this one */ ){ + int rc = SQLITE_OK; /* Return code */ WhereInfo *pWInfo; /* WHERE analysis context */ Parse *pParse; /* The parsing context */ WhereClause *pWC; /* The WHERE clause */ struct SrcList_item *pSrc; /* The FROM clause term to search */ - Table *pTab; - sqlite3 *db; - sqlite3_index_info *pIdxInfo; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_constraint_usage *pUsage; - WhereTerm *pTerm; - int i, j; - int iTerm, mxTerm; - int nConstraint; - int seenIn = 0; /* True if an IN operator is seen */ - int seenVar = 0; /* True if a non-constant constraint is seen */ - int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */ + sqlite3_index_info *p; /* Object to pass to xBestIndex() */ + int nConstraint; /* Number of constraints in p */ + int bIn; /* True if plan uses IN(...) operator */ WhereLoop *pNew; - int rc = SQLITE_OK; + Bitmask mBest; /* Tables used by best possible plan */ - assert( (mExtra & mUnusable)==0 ); + assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; pParse = pWInfo->pParse; - db = pParse->db; pWC = pBuilder->pWC; pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; - pTab = pSrc->pTab; - assert( IsVirtual(pTab) ); - pIdxInfo = allocateIndexInfo(pParse, pWC, mUnusable, pSrc,pBuilder->pOrderBy); - if( pIdxInfo==0 ) return SQLITE_NOMEM; - pNew->prereq = 0; + assert( IsVirtual(pSrc->pTab) ); + p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy); + if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; pNew->nLTerm = 0; pNew->u.vtab.needFree = 0; - pUsage = pIdxInfo->aConstraintUsage; - nConstraint = pIdxInfo->nConstraint; - if( whereLoopResize(db, pNew, nConstraint) ){ - sqlite3DbFree(db, pIdxInfo); - return SQLITE_NOMEM; + nConstraint = p->nConstraint; + if( whereLoopResize(pParse->db, pNew, nConstraint) ){ + sqlite3DbFree(pParse->db, p); + return SQLITE_NOMEM_BKPT; } - for(iPhase=0; iPhase<=3; iPhase++){ - if( !seenIn && (iPhase&1)!=0 ){ - iPhase++; - if( iPhase>3 ) break; - } - if( !seenVar && iPhase>1 ) break; - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pIdxCons++){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - switch( iPhase ){ - case 0: /* Constants without IN operator */ - pIdxCons->usable = 0; - if( (pTerm->eOperator & WO_IN)!=0 ){ - seenIn = 1; - } - if( (pTerm->prereqRight & ~mExtra)!=0 ){ - seenVar = 1; - }else if( (pTerm->eOperator & WO_IN)==0 ){ - pIdxCons->usable = 1; - } - break; - case 1: /* Constants with IN operators */ - assert( seenIn ); - pIdxCons->usable = (pTerm->prereqRight & ~mExtra)==0; - break; - case 2: /* Variables without IN */ - assert( seenVar ); - pIdxCons->usable = (pTerm->eOperator & WO_IN)==0; - break; - default: /* Variables with IN */ - assert( seenVar && seenIn ); - pIdxCons->usable = 1; - break; - } - } - memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); - if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr); - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->needToFreeIdxStr = 0; - pIdxInfo->orderByConsumed = 0; - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; - pIdxInfo->estimatedRows = 25; - pIdxInfo->idxFlags = 0; - pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; - rc = vtabBestIndex(pParse, pTab, pIdxInfo); - if( rc ) goto whereLoopAddVtab_exit; - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pNew->prereq = mExtra; - mxTerm = -1; - assert( pNew->nLSlot>=nConstraint ); - for(i=0; iaLTerm[i] = 0; - pNew->u.vtab.omitMask = 0; - for(i=0; i=0 ){ - j = pIdxCons->iTermOffset; - if( iTerm>=nConstraint - || j<0 - || j>=pWC->nTerm - || pNew->aLTerm[iTerm]!=0 - ){ - rc = SQLITE_ERROR; - sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName); - goto whereLoopAddVtab_exit; - } - testcase( iTerm==nConstraint-1 ); - testcase( j==0 ); - testcase( j==pWC->nTerm-1 ); - pTerm = &pWC->a[j]; - pNew->prereq |= pTerm->prereqRight; - assert( iTermnLSlot ); - pNew->aLTerm[iTerm] = pTerm; - if( iTerm>mxTerm ) mxTerm = iTerm; - testcase( iTerm==15 ); - testcase( iTerm==16 ); - if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<eOperator & WO_IN)!=0 ){ - if( pUsage[i].omit==0 ){ - /* Do not attempt to use an IN constraint if the virtual table - ** says that the equivalent EQ constraint cannot be safely omitted. - ** If we do attempt to use such a constraint, some rows might be - ** repeated in the output. */ - break; - } - /* A virtual table that is constrained by an IN clause may not - ** consume the ORDER BY clause because (1) the order of IN terms - ** is not necessarily related to the order of output terms and - ** (2) Multiple outputs from a single IN value will not merge - ** together. */ - pIdxInfo->orderByConsumed = 0; - pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; - } - } - } - if( i>=nConstraint ){ - pNew->nLTerm = mxTerm+1; - assert( pNew->nLTerm<=pNew->nLSlot ); - pNew->u.vtab.idxNum = pIdxInfo->idxNum; - pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; - pIdxInfo->needToFreeIdxStr = 0; - pNew->u.vtab.idxStr = pIdxInfo->idxStr; - pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? - pIdxInfo->nOrderBy : 0); - pNew->rSetup = 0; - pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); - pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); + /* First call xBestIndex() with all constraints usable. */ + WHERETRACE(0x40, (" VirtualOne: all usable\n")); + rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, &bIn); - /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated - ** that the scan will visit at most one row. Clear it otherwise. */ - if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){ - pNew->wsFlags |= WHERE_ONEROW; - }else{ - pNew->wsFlags &= ~WHERE_ONEROW; - } - whereLoopInsert(pBuilder, pNew); - if( pNew->u.vtab.needFree ){ - sqlite3_free(pNew->u.vtab.idxStr); - pNew->u.vtab.needFree = 0; + /* If the call to xBestIndex() with all terms enabled produced a plan + ** that does not require any source tables (IOW: a plan with mBest==0), + ** then there is no point in making any further calls to xBestIndex() + ** since they will all return the same result (if the xBestIndex() + ** implementation is sane). */ + if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){ + int seenZero = 0; /* True if a plan with no prereqs seen */ + int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ + Bitmask mPrev = 0; + Bitmask mBestNoIn = 0; + + /* If the plan produced by the earlier call uses an IN(...) term, call + ** xBestIndex again, this time with IN(...) terms disabled. */ + if( bIn ){ + WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); + rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, WO_IN, p, &bIn); + assert( bIn==0 ); + mBestNoIn = pNew->prereq & ~mPrereq; + if( mBestNoIn==0 ){ + seenZero = 1; + seenZeroNoIN = 1; } } - } -whereLoopAddVtab_exit: - if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr); - sqlite3DbFree(db, pIdxInfo); + /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq) + ** in the set of terms that apply to the current virtual table. */ + while( rc==SQLITE_OK ){ + int i; + Bitmask mNext = ALLBITS; + assert( mNext>0 ); + for(i=0; ia[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq + ); + if( mThis>mPrev && mThisprereq==mPrereq ){ + seenZero = 1; + if( bIn==0 ) seenZeroNoIN = 1; + } + } + + /* If the calls to xBestIndex() in the above loop did not find a plan + ** that requires no source tables at all (i.e. one guaranteed to be + ** usable), make a call here with all source tables disabled */ + if( rc==SQLITE_OK && seenZero==0 ){ + WHERETRACE(0x40, (" VirtualOne: all disabled\n")); + rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, 0, p, &bIn); + if( bIn==0 ) seenZeroNoIN = 1; + } + + /* If the calls to xBestIndex() have so far failed to find a plan + ** that requires no source tables at all and does not use an IN(...) + ** operator, make a final call to obtain one here. */ + if( rc==SQLITE_OK && seenZeroNoIN==0 ){ + WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); + rc = whereLoopAddVirtualOne(pBuilder, mPrereq, mPrereq, WO_IN, p, &bIn); + } + } + + if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); + sqlite3DbFree(pParse->db, p); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -2955,7 +3041,7 @@ whereLoopAddVtab_exit: */ static int whereLoopAddOr( WhereLoopBuilder *pBuilder, - Bitmask mExtra, + Bitmask mPrereq, Bitmask mUnusable ){ WhereInfo *pWInfo = pBuilder->pWInfo; @@ -3016,14 +3102,14 @@ static int whereLoopAddOr( #endif #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ - rc = whereLoopAddVirtual(&sSubBuild, mExtra, mUnusable); + rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); }else #endif { - rc = whereLoopAddBtree(&sSubBuild, mExtra); + rc = whereLoopAddBtree(&sSubBuild, mPrereq); } if( rc==SQLITE_OK ){ - rc = whereLoopAddOr(&sSubBuild, mExtra, mUnusable); + rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); } assert( rc==SQLITE_OK || sCur.n==0 ); if( sCur.n==0 ){ @@ -3080,7 +3166,7 @@ static int whereLoopAddOr( */ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ WhereInfo *pWInfo = pBuilder->pWInfo; - Bitmask mExtra = 0; + Bitmask mPrereq = 0; Bitmask mPrior = 0; int iTab; SrcList *pTabList = pWInfo->pTabList; @@ -3101,7 +3187,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){ /* This condition is true when pItem is the FROM clause term on the ** right-hand-side of a LEFT or CROSS JOIN. */ - mExtra = mPrior; + mPrereq = mPrior; } priorJointype = pItem->fg.jointype; if( IsVirtual(pItem->pTab) ){ @@ -3111,12 +3197,12 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } - rc = whereLoopAddVirtual(pBuilder, mExtra, mUnusable); + rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable); }else{ - rc = whereLoopAddBtree(pBuilder, mExtra); + rc = whereLoopAddBtree(pBuilder, mPrereq); } if( rc==SQLITE_OK ){ - rc = whereLoopAddOr(pBuilder, mExtra, mUnusable); + rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable); } mPrior |= pNew->maskSelf; if( rc || db->mallocFailed ) break; @@ -3459,15 +3545,14 @@ static LogEst whereSortingCost( LogEst rScale, rSortCost; assert( nOrderBy>0 && 66==sqlite3LogEst(100) ); rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; - rSortCost = nRow + estLog(nRow) + rScale + 16; + rSortCost = nRow + rScale + 16; - /* TUNING: The cost of implementing DISTINCT using a B-TREE is - ** similar but with a larger constant of proportionality. - ** Multiply by an additional factor of 3.0. */ - if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ - rSortCost += 16; + /* Multiple by log(M) where M is the number of output rows. + ** Use the LIMIT for M if it is smaller */ + if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimitiLimit; } - + rSortCost += estLog(nRow); return rSortCost; } @@ -3530,7 +3615,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; nSpace += sizeof(LogEst) * nOrderBy; pSpace = sqlite3DbMallocRawNN(db, nSpace); - if( pSpace==0 ) return SQLITE_NOMEM; + if( pSpace==0 ) return SQLITE_NOMEM_BKPT; aTo = (WherePath*)pSpace; aFrom = aTo+mxChoice; memset(aFrom, 0, sizeof(aFrom[0])); @@ -3585,6 +3670,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue; if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue; + if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<10 ){ + /* Do not use an automatic index if the this loop is expected + ** to run less than 2 times. */ + assert( 10==sqlite3LogEst(2) ); + continue; + } /* At this point, pWLoop is a candidate to be the next loop. ** Compute its cost */ rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); @@ -4106,7 +4197,8 @@ WhereInfo *sqlite3WhereBegin( ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Result set of the query */ u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ - int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */ + int iAuxArg /* If WHERE_ONETABLE_ONLY is set, index cursor number, + ** If WHERE_USE_LIMIT, then the limit amount */ ){ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ int nTabList; /* Number of elements in pTabList */ @@ -4127,6 +4219,10 @@ WhereInfo *sqlite3WhereBegin( && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 )); + /* Only one of WHERE_ONETABLE_ONLY or WHERE_USE_LIMIT */ + assert( (wctrlFlags & WHERE_ONETABLE_ONLY)==0 + || (wctrlFlags & WHERE_USE_LIMIT)==0 ); + /* Variable initialization */ db = pParse->db; memset(&sWLB, 0, sizeof(sWLB)); @@ -4180,6 +4276,7 @@ WhereInfo *sqlite3WhereBegin( pWInfo->pResultSet = pResultSet; pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v); pWInfo->wctrlFlags = wctrlFlags; + pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ pMaskSet = &pWInfo->sMaskSet; @@ -4260,9 +4357,14 @@ WhereInfo *sqlite3WhereBegin( } /* Construct the WhereLoop objects */ - WHERETRACE(0xffff,("*** Optimizer Start *** (wctrlFlags: 0x%x)\n", - wctrlFlags)); #if defined(WHERETRACE_ENABLED) + if( sqlite3WhereTrace & 0xffff ){ + sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); + if( wctrlFlags & WHERE_USE_LIMIT ){ + sqlite3DebugPrintf(", limit: %d", iAuxArg); + } + sqlite3DebugPrintf(")\n"); + } if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ int i; for(i=0; inTerm; i++){ @@ -4299,7 +4401,7 @@ WhereInfo *sqlite3WhereBegin( } } if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ - pWInfo->revMask = (Bitmask)(-1); + pWInfo->revMask = ALLBITS; } if( pParse->nErr || NEVER(db->mallocFailed) ){ goto whereBeginError; @@ -4448,8 +4550,8 @@ WhereInfo *sqlite3WhereBegin( Index *pIx = pLoop->u.btree.pIndex; int iIndexCur; int op = OP_OpenRead; - /* iIdxCur is always set if to a positive value if ONEPASS is possible */ - assert( iIdxCur!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); + /* iAuxArg is always set if to a positive value if ONEPASS is possible */ + assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx) && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){ @@ -4459,7 +4561,7 @@ WhereInfo *sqlite3WhereBegin( op = 0; }else if( pWInfo->eOnePass!=ONEPASS_OFF ){ Index *pJ = pTabItem->pTab->pIndex; - iIndexCur = iIdxCur; + iIndexCur = iAuxArg; assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); while( ALWAYS(pJ) && pJ!=pIx ){ iIndexCur++; @@ -4467,8 +4569,8 @@ WhereInfo *sqlite3WhereBegin( } op = OP_OpenWrite; pWInfo->aiCurOnePass[1] = iIndexCur; - }else if( iIdxCur && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){ - iIndexCur = iIdxCur; + }else if( iAuxArg && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){ + iIndexCur = iAuxArg; if( wctrlFlags & WHERE_REOPEN_IDX ) op = OP_ReopenIdx; }else{ iIndexCur = pParse->nTab++; diff --git a/src/whereInt.h b/src/whereInt.h index 1a189980ef..eb6ca326f5 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -412,6 +412,7 @@ struct WhereInfo { WhereLoop *pLoops; /* List of all WhereLoop objects */ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ LogEst nRowOut; /* Estimated number of output rows */ + LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */ u8 sorted; /* True if really sorted (not just grouped) */ diff --git a/src/wherecode.c b/src/wherecode.c index bb48e5dc70..3705aab51d 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -874,6 +874,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( int iReg; /* P3 Value for OP_VFilter */ int addrNotFound; int nConstraint = pLoop->nLTerm; + int iIn; /* Counter for IN constraints */ sqlite3ExprCachePush(pParse); iReg = sqlite3GetTempRange(pParse, nConstraint+2); @@ -881,7 +882,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( for(j=0; jaLTerm[j]; - if( pTerm==0 ) continue; + if( NEVER(pTerm==0) ) continue; if( pTerm->eOperator & WO_IN ){ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); addrNotFound = pLevel->addrNxt; @@ -896,14 +897,50 @@ Bitmask sqlite3WhereCodeOneLoopStart( pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC); VdbeCoverage(v); pLoop->u.vtab.needFree = 0; - for(j=0; ju.vtab.omitMask>>j)&1 ){ - disableTerm(pLevel, pLoop->aLTerm[j]); - } - } pLevel->p1 = iCur; pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; pLevel->p2 = sqlite3VdbeCurrentAddr(v); + iIn = pLevel->u.in.nIn; + for(j=nConstraint-1; j>=0; j--){ + pTerm = pLoop->aLTerm[j]; + if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){ + disableTerm(pLevel, pTerm); + }else if( (pTerm->eOperator & WO_IN)!=0 ){ + Expr *pCompare; /* The comparison operator */ + Expr *pRight; /* RHS of the comparison */ + VdbeOp *pOp; /* Opcode to access the value of the IN constraint */ + + /* Reload the constraint value into reg[iReg+j+2]. The same value + ** was loaded into the same register prior to the OP_VFilter, but + ** the xFilter implementation might have changed the datatype or + ** encoding of the value in the register, so it *must* be reloaded. */ + assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed ); + if( !db->mallocFailed ){ + assert( iIn>0 ); + pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[--iIn].addrInTop); + assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid ); + assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 ); + assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 ); + testcase( pOp->opcode==OP_Rowid ); + sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3); + } + + /* Generate code that will continue to the next row if + ** the IN constraint is not satisfied */ + pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0, 0); + assert( pCompare!=0 || db->mallocFailed ); + if( pCompare ){ + pCompare->pLeft = pTerm->pExpr->pLeft; + pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0); + if( pRight ){ + pRight->iTable = iReg+j+2; + sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0); + } + pCompare->pLeft = 0; + sqlite3ExprDelete(db, pCompare); + } + } + } sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); sqlite3ExprCachePop(pParse); }else @@ -1212,16 +1249,22 @@ Bitmask sqlite3WhereCodeOneLoopStart( start_constraints = 1; } codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff); - op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; - assert( op!=0 ); - sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - VdbeCoverage(v); - VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); - VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last ); - VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT ); - VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE ); - VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE ); - VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT ); + if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){ + /* The skip-scan logic inside the call to codeAllEqualityConstraints() + ** above has already left the cursor sitting on the correct row, + ** so no further seeking is needed */ + }else{ + op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; + assert( op!=0 ); + sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); + VdbeCoverage(v); + VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); + VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last ); + VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT ); + VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE ); + VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE ); + VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT ); + } /* Load the value for the inequality constraint at the end of the ** range (if any). diff --git a/src/whereexpr.c b/src/whereexpr.c index c84d2f230a..0ad1f6a0dc 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -533,6 +533,7 @@ static void exprAnalyzeOrTerm( if( pOrInfo==0 ) return; pTerm->wtFlags |= TERM_ORINFO; pOrWc = &pOrInfo->wc; + memset(pOrWc->aStatic, 0, sizeof(pOrWc->aStatic)); sqlite3WhereClauseInit(pOrWc, pWInfo); sqlite3WhereSplit(pOrWc, pExpr, TK_OR); sqlite3WhereExprAnalyze(pSrc, pOrWc); @@ -559,6 +560,7 @@ static void exprAnalyzeOrTerm( pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; pAndWC = &pAndInfo->wc; + memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic)); sqlite3WhereClauseInit(pAndWC, pWC->pWInfo); sqlite3WhereSplit(pAndWC, pOrTerm->pExpr, TK_AND); sqlite3WhereExprAnalyze(pSrc, pAndWC); diff --git a/test/alter3.test b/test/alter3.test index 28d293e24b..f8ebe056f2 100644 --- a/test/alter3.test +++ b/test/alter3.test @@ -184,7 +184,7 @@ do_test alter3-3.2 { if {!$has_codec} { do_test alter3-3.3 { get_file_format - } {3} + } {4} } ifcapable schema_version { do_test alter3-3.4 { @@ -220,7 +220,7 @@ do_test alter3-4.2 { if {!$has_codec} { do_test alter3-4.3 { get_file_format - } {3} + } {4} } ifcapable schema_version { do_test alter3-4.4 { @@ -270,7 +270,7 @@ ifcapable attach { if {!$has_codec} { do_test alter3-5.5 { list [get_file_format test2.db] [get_file_format] - } {2 3} + } {4 4} } do_test alter3-5.6 { execsql { @@ -347,19 +347,19 @@ if {!$has_codec} { ALTER TABLE abc ADD d DEFAULT NULL; } get_file_format - } {2} + } {4} do_test alter3-7.3 { execsql { ALTER TABLE abc ADD e DEFAULT 10; } get_file_format - } {3} + } {4} do_test alter3-7.4 { execsql { ALTER TABLE abc ADD f DEFAULT NULL; } get_file_format - } {3} + } {4} do_test alter3-7.5 { execsql { VACUUM; diff --git a/test/analyze9.test b/test/analyze9.test index e24f3dd49d..918577bd19 100644 --- a/test/analyze9.test +++ b/test/analyze9.test @@ -1180,7 +1180,7 @@ do_execsql_test 26.1.3 { # # There should be no other samples that start with (x=10000). So it knows # that (x=10000 AND y<50) must match somewhere between 0 and 99 rows, but -# know more than that. Guessing less than 20 is therefore unreasonable. +# no more than that. Guessing less than 20 is therefore unreasonable. # # At one point though, due to a problem in whereKeyStats(), the planner was # estimating that (x=10000 AND y<50) would match only 2 rows. diff --git a/test/analyzeD.test b/test/analyzeD.test index 7ef1094f82..4588aaeebc 100644 --- a/test/analyzeD.test +++ b/test/analyzeD.test @@ -1,4 +1,4 @@ -# 2005 July 22 +# 2014-10-04 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: @@ -11,7 +11,6 @@ # This file implements regression tests for SQLite library. # This file implements tests for the ANALYZE command. # -# $Id: analyze.test,v 1.9 2008/08/11 18:44:58 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -75,7 +74,7 @@ do_test 1.3 { } {} # Without stat1, because 3001 is larger than all samples in the stat4 -# table, SQLite things that a=3001 matches just 1 row. So it (incorrectly) +# table, SQLite thinks that a=3001 matches just 1 row. So it (incorrectly) # chooses it over the c=150 index (5 rows). Even with stat1 data, things # worked this way before commit [e6f7f97dbc]. # diff --git a/test/autoindex2.test b/test/autoindex2.test index c8abdf410a..afd4a666b7 100644 --- a/test/autoindex2.test +++ b/test/autoindex2.test @@ -218,10 +218,15 @@ do_execsql_test autoindex2-120 { AND t1.did = t2.did AND t2.uid = t3.uid ORDER BY t1.ptime desc LIMIT 500; -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1x1 (ptime>?)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2x0 (did=?)} 0 2 2 {SEARCH TABLE t3 USING INDEX t3x0 (uid=?)}} +} {~/AUTO/} # # ^^^--- Before being fixed, the above was using an automatic covering # on t3 and reordering the tables so that t3 was in the outer loop and # implementing the ORDER BY clause using a B-Tree. +# +# This test is sanitized data received from a user. The original unsanitized +# data and STAT4 data is found in the th3private test repository. See one of +# the th3private check-ins on 2016-02-25. The test is much more accurate when +# STAT4 data is used. finish_test diff --git a/test/backcompat.test b/test/backcompat.test index ea7e6a9eed..fe8a83f0a6 100644 --- a/test/backcompat.test +++ b/test/backcompat.test @@ -63,7 +63,7 @@ proc do_backcompat_test {rv bin1 bin2 script} { set v [split [db version] .] if {[llength $v]==3} {lappend v 0} set ::sqlite_libversion [format \ - "%d%.2d%.2d%2d" [lindex $v 0] [lindex $v 1] [lindex $v 2] [lindex $v 3] + "%d%.2d%.2d%.2d" [lindex $v 0] [lindex $v 1] [lindex $v 2] [lindex $v 3] ] } } @@ -420,6 +420,12 @@ ifcapable fts3 { if {[code1 { set ::sqlite_libversion }] >=3071200 && [code2 { set ::sqlite_libversion }] >=3071200 } { + if {[code1 { set ::sqlite_libversion }]<3120000} { + set res {0 {0 1} 1 0} + } else { + set res {1 0} + } + do_test backcompat-3.9 { sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); } sql2 { INSERT INTO t2(t2) VALUES('merge=100,4'); } @@ -428,7 +434,7 @@ ifcapable fts3 { sql2 { SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level; } - } {0 {0 1} 1 0} + } $res do_test backcompat-3.10 { sql1 { INSERT INTO t2(t2) VALUES('integrity-check') } diff --git a/test/bestindex1.test b/test/bestindex1.test new file mode 100644 index 0000000000..fffb7aebea --- /dev/null +++ b/test/bestindex1.test @@ -0,0 +1,165 @@ +# 2016-03-01 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix bestindex1 + +register_tcl_module db + +proc vtab_command {method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b, c)" + } + + xBestIndex { + set clist [lindex $args 0] + if {[llength $clist]!=1} { error "unexpected constraint list" } + catch { array unset C } + array set C [lindex $clist 0] + if {$C(usable)} { + return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!" + } else { + return "cost 1000000 rows 0 idxnum 0 idxstr scan..." + } + } + + } + + return {} +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); +} {} + +do_eqp_test 1.1 { + SELECT * FROM x1 WHERE a = 'abc' +} { + 0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!} +} + +do_eqp_test 1.2 { + SELECT * FROM x1 WHERE a IN ('abc', 'def'); +} { + 0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!} + 0 0 0 {EXECUTE LIST SUBQUERY 1} +} + +#------------------------------------------------------------------------- +# +reset_db +register_tcl_module db + +# Parameter $mode may be one of: +# +# "omit" - Implement filtering. Set the omit flag. +# "use" - Implement filtering. Use the constraint, but do not set omit. +# "use2" - Do not implement filtering. Use the constraint anyway. +# +# +proc t1_vtab {mode method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b)" + } + + xBestIndex { + set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'} + set SQL_SCAN {SELECT * FROM t1x} + + set clist [lindex $args 0] + set idx 0 + for {set idx 0} {$idx < [llength $clist]} {incr idx} { + array unset C + array set C [lindex $clist $idx] + if {$C(column)==0 && $C(op)=="eq" && $C(usable)} { + switch -- $mode { + "omit" { + return [list omit $idx rows 10 cost 10 idxstr $SQL_FILTER] + } + "use" { + return [list use $idx rows 10 cost 10 idxstr $SQL_FILTER] + } + "use2" { + return [list use $idx rows 10 cost 10 idxstr $SQL_SCAN] + } + default { + error "Bad mode - $mode" + } + } + } + } + + return [list idxstr {SELECT * FROM t1x}] + } + + xFilter { + set map [list %1% [lindex $args 2 0]] + set sql [string map $map [lindex $args 1]] + return [list sql $sql] + } + } + + return {} +} + +do_execsql_test 2.1 { + CREATE TABLE t1x(i INTEGER PRIMARY KEY, a, b); + INSERT INTO t1x VALUES(1, 'one', 1); + INSERT INTO t1x VALUES(2, 'two', 2); + INSERT INTO t1x VALUES(3, 'three', 3); + INSERT INTO t1x VALUES(4, 'four', 4); +} + +foreach {tn mode} { + 1 use 2 omit 3 use2 +} { + do_execsql_test 2.2.$mode.1 " + DROP TABLE IF EXISTS t1; + CREATE VIRTUAL TABLE t1 USING tcl(t1_vtab $mode); + " + + do_execsql_test 2.2.$mode.2 {SELECT * FROM t1} {one 1 two 2 three 3 four 4} + do_execsql_test 2.2.$mode.3 {SELECT rowid FROM t1} {1 2 3 4} + do_execsql_test 2.2.$mode.4 {SELECT rowid FROM t1 WHERE a='two'} {2} + + do_execsql_test 2.2.$mode.5 { + SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid + } {1 4} + + set plan(use) { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'} + 0 0 0 {EXECUTE LIST SUBQUERY 1} + 0 0 0 {USE TEMP B-TREE FOR ORDER BY} + } + set plan(omit) { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'} + 0 0 0 {EXECUTE LIST SUBQUERY 1} + 0 0 0 {USE TEMP B-TREE FOR ORDER BY} + } + set plan(use2) { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x} + 0 0 0 {EXECUTE LIST SUBQUERY 1} + 0 0 0 {USE TEMP B-TREE FOR ORDER BY} + } + + do_eqp_test 2.2.$mode.6 { + SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid + } $plan($mode) +} + +finish_test + + diff --git a/test/bestindex2.test b/test/bestindex2.test new file mode 100644 index 0000000000..7ccd61640d --- /dev/null +++ b/test/bestindex2.test @@ -0,0 +1,138 @@ +# 2016 March 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix bestindex2 + + +#------------------------------------------------------------------------- +# Virtual table callback for table named $tbl, with the columns specified +# by list argument $cols. e.g. if the function is invoked as: +# +# vtab_cmd t1 {a b c} ... +# +# The table created is: +# +# "CREATE TABLE t1 (a, b, c)" +# +# The tables xBestIndex method behaves as if all possible combinations of +# "=" constraints (but no others) may be optimized. The cost of a full table +# scan is: +# +# "WHERE 1" "cost 1000000 rows 1000000" +# +# If one or more "=" constraints are in use, the cost and estimated number +# of rows returned are both is (11 - nCons)*1000, where nCons is the number +# of constraints used. e.g. +# +# "WHERE a=? AND b=?" -> "cost 900 rows 900" +# "WHERE c=? AND b "cost 1000 rows 1000" +# +proc vtab_cmd {tbl cols method args} { + switch -- $method { + xConnect { + return "CREATE TABLE $tbl ([join $cols ,])" + } + xBestIndex { + foreach {clist orderby mask} $args {} + + set cons [list] + set used [list] + + for {set i 0} {$i < [llength $clist]} {incr i} { + array unset C + array set C [lindex $clist $i] + if {$C(op)=="eq" && $C(usable) && [lsearch $cons $C(column)]<0} { + lappend used use $i + lappend cons $C(column) + } + } + + set nCons [llength $cons] + if {$nCons==0} { + return "cost 1000000 rows 1000000" + } else { + set cost [expr (11-$nCons) * 1000] + set ret [concat $used "cost $cost rows $cost"] + + set txt [list] + foreach c $cons { lappend txt "[lindex $cols $c]=?" } + lappend ret idxstr "indexed([join $txt { AND }])" + + return $ret + } + } + } + return "" +} + +register_tcl_module db + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING tcl("vtab_cmd t1 {a b}"); + CREATE VIRTUAL TABLE t2 USING tcl("vtab_cmd t2 {c d}"); + CREATE VIRTUAL TABLE t3 USING tcl("vtab_cmd t3 {e f}"); +} + +do_eqp_test 1.1 { + SELECT * FROM t1 WHERE a='abc' +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)} +} +do_eqp_test 1.2 { + SELECT * FROM t1 WHERE a='abc' AND b='def' +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=? AND b=?)} +} +do_eqp_test 1.3 { + SELECT * FROM t1 WHERE a='abc' AND a='def' +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)} +} +do_eqp_test 1.4 { + SELECT * FROM t1,t2 WHERE c=a +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:} + 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)} +} + +do_eqp_test 1.5 { + SELECT * FROM t1, t2 CROSS JOIN t3 WHERE t2.c = +t1.b AND t3.e=t2.d +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:} + 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)} + 0 2 2 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)} +} + +do_eqp_test 1.6 { + SELECT * FROM t1, t2, t3 WHERE t2.c = +t1.b AND t3.e = t2.d +} { + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:} + 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)} + 0 2 2 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)} +} + +do_execsql_test 1.7.1 { + CREATE TABLE x1(a, b); +} +do_eqp_test 1.7.2 { + SELECT * FROM x1 CROSS JOIN t1, t2, t3 + WHERE t1.a = t2.c AND t1.b = t3.e +} { + 0 0 0 {SCAN TABLE x1} + 0 1 1 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:} + 0 2 2 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)} + 0 3 3 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)} +} + +finish_test + diff --git a/test/check.test b/test/check.test index 02b99f2ac9..43e447f70d 100644 --- a/test/check.test +++ b/test/check.test @@ -459,4 +459,24 @@ do_execsql_test 8.1 { CREATE TABLE t811(b, CHECK( xyzzy.t811.b BETWEEN 5 AND 10 )); } {} +# Make sure check constraints involving the ROWID are not ignored +# +do_execsql_test 9.1 { + CREATE TABLE t1( + a INTEGER PRIMARY KEY, + b INTEGER NOT NULL CONSTRAINT 'b-check' CHECK( b>a ), + c INTEGER NOT NULL CONSTRAINT 'c-check' CHECK( c>rowid*2 ), + d INTEGER NOT NULL CONSTRAINT 'd-check' CHECK( d BETWEEN b AND c ) + ); + INSERT INTO t1(a,b,c,d) VALUES(1,2,4,3),(2,4,6,5),(3,10,30,20); +} {} +do_catchsql_test 9.2 { + UPDATE t1 SET b=0 WHERE a=1; +} {1 {CHECK constraint failed: b-check}} +do_catchsql_test 9.3 { + UPDATE t1 SET c=a*2 WHERE a=1; +} {1 {CHECK constraint failed: c-check}} + + + finish_test diff --git a/test/conflict.test b/test/conflict.test index af5668ed72..a39988adb0 100644 --- a/test/conflict.test +++ b/test/conflict.test @@ -285,15 +285,17 @@ do_test conflict-6.0 { # t3 Number of temporary files for tables # t4 Number of temporary files for statement journals # -# Update: Since temporary table files are now opened lazily, and none -# of the following tests use large quantities of data, t3 is always 0. +# Update (2007-08-21): Since temporary table files are now opened lazily, +# and none of the following tests use large quantities of data, t3 is always 0. +# +# Update (2016-03-04): Subjournals now also open lazily, so t4 is also always 0. # foreach {i conf1 cmd t0 t1 t2 t3 t4} { - 1 {} UPDATE 1 {6 7 8 9} 1 0 1 + 1 {} UPDATE 1 {6 7 8 9} 1 0 0 2 REPLACE UPDATE 0 {7 6 9} 1 0 0 3 IGNORE UPDATE 0 {6 7 3 9} 1 0 0 4 FAIL UPDATE 1 {6 7 3 4} 1 0 0 - 5 ABORT UPDATE 1 {1 2 3 4} 1 0 1 + 5 ABORT UPDATE 1 {1 2 3 4} 1 0 0 6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0 0 7 REPLACE {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 @@ -303,7 +305,7 @@ foreach {i conf1 cmd t0 t1 t2 t3 t4} { 12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0 0 - 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1 + 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 0 16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0 } { if {$t0} {set t1 {UNIQUE constraint failed: t1.a}} diff --git a/test/conflict2.test b/test/conflict2.test index 6496913849..f33e01d582 100644 --- a/test/conflict2.test +++ b/test/conflict2.test @@ -287,22 +287,25 @@ do_test conflict2-6.0 { # Update: Since temporary table files are now opened lazily, and none # of the following tests use large quantities of data, t3 is always 0. # +# Update (2016-03-04): Subjournals now only open when their size +# exceeds 64KB. +# foreach {i conf1 cmd t0 t1 t2 t3 t4} { - 1 {} UPDATE 1 {6 7 8 9} 1 0 1 - 2 REPLACE UPDATE 0 {7 6 9} 1 0 1 - 3 IGNORE UPDATE 0 {6 7 3 9} 1 0 1 - 4 FAIL UPDATE 1 {6 7 3 4} 1 0 1 - 5 ABORT UPDATE 1 {1 2 3 4} 1 0 1 - 6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0 1 + 1 {} UPDATE 1 {6 7 8 9} 1 0 0 + 2 REPLACE UPDATE 0 {7 6 9} 1 0 0 + 3 IGNORE UPDATE 0 {6 7 3 9} 1 0 0 + 4 FAIL UPDATE 1 {6 7 3 4} 1 0 0 + 5 ABORT UPDATE 1 {1 2 3 4} 1 0 0 + 6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0 0 7 REPLACE {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 - 8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1 + 8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 9 FAIL {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 - 10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1 + 10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 11 ROLLBACK {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 - 13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1 + 13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0 0 - 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1 + 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 0 16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0 } { diff --git a/test/coveridxscan.test b/test/coveridxscan.test index 7b3c0b0be9..a8d1a8ff75 100644 --- a/test/coveridxscan.test +++ b/test/coveridxscan.test @@ -89,5 +89,36 @@ do_test 4.3 { db eval {SELECT b FROM t1} } {2 4 8} +#------------------------------------------------------------------------- +# Test that indexes with large numbers of columns can be correctly +# identified as covering indexes. +reset_db +set L [list] +for {set i 1} {$i<120} {incr i} { + lappend L "c$i" +} +set cols [join $L ,] + +do_execsql_test 5.1.0 " + CREATE TABLE t1(a, b, c, $cols, PRIMARY KEY(a, b, c)) WITHOUT ROWID; + CREATE INDEX i1 ON t1($cols); + + CREATE TABLE t2(i INTEGER PRIMARY KEY, $cols); + CREATE INDEX i2 ON t2($cols); +" + +do_eqp_test 5.1.1 { + SELECT * FROM t1 ORDER BY c1, c2; +} { + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1} +} + +do_eqp_test 5.1.2 { + SELECT * FROM t2 ORDER BY c1, c2; +} { + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i2} +} + + finish_test diff --git a/test/e_walhook.test b/test/e_walhook.test index c8c8819493..c963524a97 100644 --- a/test/e_walhook.test +++ b/test/e_walhook.test @@ -161,10 +161,10 @@ do_test 5.2 { -# EVIDENCE-OF: R-42842-27162 Note that the sqlite3_wal_autocheckpoint() +# EVIDENCE-OF: R-57445-43425 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. +# sqlite3_wal_hook() and will overwrite any prior sqlite3_wal_hook() +# settings. # set ::old_wal_hook 0 proc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 } diff --git a/test/exclusive.test b/test/exclusive.test index c000dfefa4..c7b88cfbca 100644 --- a/test/exclusive.test +++ b/test/exclusive.test @@ -420,9 +420,10 @@ do_test exclusive-5.0 { } {} do_test exclusive-5.1 { # Three files are open: The db, journal and statement-journal. + # (2016-03-04) The statement-journal is now opened lazily set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds -} [expr 3 - ($TEMP_STORE>=2)] +} [expr 2 - ($TEMP_STORE>=2)] do_test exclusive-5.2 { execsql { COMMIT; @@ -446,17 +447,19 @@ do_test exclusive-5.4 { INSERT INTO abc SELECT a+10, b+10, c+10 FROM abc; } # Three files are open: The db, journal and statement-journal. + # 2016-03-04: The statement-journal open is deferred set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds -} [expr 3 - ($TEMP_STORE>=2)] +} [expr 2 - ($TEMP_STORE>=2)] do_test exclusive-5.5 { execsql { COMMIT; } # Three files are still open: The db, journal and statement-journal. + # 2016-03-04: The statement-journal open is deferred set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds -} [expr 3 - ($TEMP_STORE>=2)] +} [expr 2 - ($TEMP_STORE>=2)] do_test exclusive-5.6 { execsql { PRAGMA locking_mode = normal; diff --git a/test/fkey6.test b/test/fkey6.test index 6fc3de211c..45ad1ef2cc 100644 --- a/test/fkey6.test +++ b/test/fkey6.test @@ -23,6 +23,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix fkey6 ifcapable {!foreignkey} { finish_test @@ -171,5 +172,58 @@ do_execsql_test fkey6-2.6 { PRAGMA defer_foreign_keys; } {0} +#-------------------------------------------------------------------------- +# Test that defer_foreign_keys disables RESTRICT. +# +do_execsql_test 3.1 { + CREATE TABLE p2(a PRIMARY KEY, b); + CREATE TABLE c2(x, y REFERENCES p2 ON DELETE RESTRICT ON UPDATE RESTRICT); + INSERT INTO p2 VALUES(1, 'one'); + INSERT INTO p2 VALUES(2, 'two'); + INSERT INTO c2 VALUES('i', 1); +} + +do_catchsql_test 3.2.1 { + BEGIN; + UPDATE p2 SET a=a-1; +} {1 {FOREIGN KEY constraint failed}} +do_execsql_test 3.2.2 { COMMIT } + +do_execsql_test 3.2.3 { + BEGIN; + PRAGMA defer_foreign_keys = 1; + UPDATE p2 SET a=a-1; + COMMIT; +} + +do_execsql_test 3.2.4 { + BEGIN; + PRAGMA defer_foreign_keys = 1; + UPDATE p2 SET a=a-1; +} +do_catchsql_test 3.2.5 { + COMMIT; +} {1 {FOREIGN KEY constraint failed}} +do_execsql_test 3.2.6 { ROLLBACK } + +do_execsql_test 3.3.1 { + CREATE TRIGGER p2t AFTER DELETE ON p2 BEGIN + INSERT INTO p2 VALUES(old.a, 'deleted!'); + END; +} +do_catchsql_test 3.3.2 { + BEGIN; + DELETE FROM p2 WHERE a=1; +} {1 {FOREIGN KEY constraint failed}} +do_execsql_test 3.3.3 { COMMIT } + +do_execsql_test 3.3.4 { + BEGIN; + PRAGMA defer_foreign_keys = 1; + DELETE FROM p2 WHERE a=1; + COMMIT; + SELECT * FROM p2; +} {0 one 1 deleted!} + finish_test diff --git a/test/fts3atoken.test b/test/fts3atoken.test index 2cdea79a98..4ce38762d6 100644 --- a/test/fts3atoken.test +++ b/test/fts3atoken.test @@ -56,40 +56,41 @@ proc escape_string {str} { # # 5: Test that the table created to use tokenizer 'blah' is usable. # -ifcapable fts3_tokenizer { - do_test fts3atoken-1.1 { - catchsql { - CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize blah); - } - } {1 {unknown tokenizer: blah}} - do_test fts3atoken-1.2 { - execsql { - SELECT fts3_tokenizer('blah', fts3_tokenizer('simple')) IS NULL; - } - } {0} - do_test fts3atoken-1.3 { - execsql { - SELECT fts3_tokenizer('blah') == fts3_tokenizer('simple'); - } - } {1} - do_test fts3atoken-1.4 { - catchsql { - CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize blah); - } - } {0 {}} - do_test fts3atoken-1.5 { - execsql { - INSERT INTO t1(content) VALUES('There was movement at the station'); - INSERT INTO t1(content) VALUES('For the word has passed around'); - INSERT INTO t1(content) VALUES('That the colt from ol regret had got'); - SELECT content FROM t1 WHERE content MATCH 'movement' - } - } {{There was movement at the station}} -} else { - do_catchsql_test 1.6 { +sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1 +do_test fts3atoken-1.1 { + catchsql { + CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize blah); + } +} {1 {unknown tokenizer: blah}} +do_test fts3atoken-1.2 { + execsql { SELECT fts3_tokenizer('blah', fts3_tokenizer('simple')) IS NULL; - } {1 {fts3tokenize: disabled - rebuild with -DSQLITE_ENABLE_FTS3_TOKENIZER}} -} + } +} {0} +do_test fts3atoken-1.3 { + execsql { + SELECT fts3_tokenizer('blah') == fts3_tokenizer('simple'); + } +} {1} +do_test fts3atoken-1.4 { + catchsql { + CREATE VIRTUAL TABLE t1 USING fts3(content, tokenize blah); + } +} {0 {}} +do_test fts3atoken-1.5 { + execsql { + INSERT INTO t1(content) VALUES('There was movement at the station'); + INSERT INTO t1(content) VALUES('For the word has passed around'); + INSERT INTO t1(content) VALUES('That the colt from ol regret had got'); + SELECT content FROM t1 WHERE content MATCH 'movement' + } +} {{There was movement at the station}} + +sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 0 +do_catchsql_test 1.6 { + SELECT fts3_tokenizer('blah', fts3_tokenizer('simple')) IS NULL; +} {1 {fts3tokenize disabled}} + #-------------------------------------------------------------------------- # Test cases fts3atoken-2.* test error cases in the scalar function based @@ -212,14 +213,14 @@ do_catchsql_test 6.1.3 { do_catchsql_test 6.2.1 { SELECT fts3_tokenizer(NULL); } {1 {unknown tokenizer: }} -ifcapable fts3_tokenizer { - do_catchsql_test 6.2.2 { - SELECT fts3_tokenizer(NULL, X'1234567812345678'); - } {1 {argument type mismatch}} - do_catchsql_test 6.2.3 { - SELECT fts3_tokenizer(NULL, X'12345678'); - } {1 {argument type mismatch}} -} + +sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1 +do_catchsql_test 6.2.2 { + SELECT fts3_tokenizer(NULL, X'1234567812345678'); +} {1 {argument type mismatch}} +do_catchsql_test 6.2.3 { + SELECT fts3_tokenizer(NULL, X'12345678'); +} {1 {argument type mismatch}} finish_test diff --git a/test/fts4growth.test b/test/fts4growth.test index e4b5f19ecb..4dc2212464 100644 --- a/test/fts4growth.test +++ b/test/fts4growth.test @@ -59,11 +59,11 @@ do_test 1.4 { INSERT INTO x1(x1) VALUES('merge=4,4'); SELECT level, end_block, length(root) FROM x1_segdir; } -} {0 {0 110} 110 0 {0 132} 132 0 {0 129} 129 1 {128 658} 2} +} {1 {224 921} 2} do_execsql_test 1.5 { SELECT length(block) FROM x1_segments; -} {658 {}} +} {921 {}} do_test 1.6 { foreach L { @@ -71,27 +71,33 @@ do_test 1.6 { {That perched above Dead Man's Creek, beside the mountain road.} {He turned the cycle down the hill and mounted for the fray,} {But 'ere he'd gone a dozen yards it bolted clean away.} + {It left the track, and through the trees, just like a silver steak,} {It whistled down the awful slope towards the Dead Man's Creek.} {It shaved a stump by half an inch, it dodged a big white-box:} {The very wallaroos in fright went scrambling up the rocks,} + {The wombats hiding in their caves dug deeper underground,} {As Mulga Bill, as white as chalk, sat tight to every bound.} {It struck a stone and gave a spring that cleared a fallen tree,} {It raced beside a precipice as close as close could be;} + {And then as Mulga Bill let out one last despairing shriek} {It made a leap of twenty feet into the Dead Man's Creek.} + {It shaved a stump by half an inch, it dodged a big white-box:} + {The very wallaroos in fright went scrambling up the rocks,} + {The wombats hiding in their caves dug deeper underground,} } { execsql { INSERT INTO x1 VALUES($L) } } execsql { SELECT level, end_block, length(root) FROM x1_segdir; } -} {1 {128 658} 2 1 {130 1377} 6 0 {0 117} 117} +} {1 {224 921} 2 1 {226 1230} 7 0 {0 98} 98} do_execsql_test 1.7 { - SELECT sum(length(block)) FROM x1_segments WHERE blockid IN (129, 130); -} {1377} + SELECT sum(length(block)) FROM x1_segments WHERE blockid IN (224,225,226) +} {1230} #------------------------------------------------------------------------- # @@ -131,24 +137,24 @@ do_execsql_test 2.5 { SELECT end_block FROM x2_segdir WHERE level=3; INSERT INTO x2(x2) VALUES('merge=4,4'); SELECT end_block FROM x2_segdir WHERE level=3; -} {{3828 -3430} {3828 -10191} {3828 -14109}} +} {{5588 -3950} {5588 -11766} {5588 -15541}} do_execsql_test 2.6 { SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE blockid BETWEEN start_block AND leaves_end_block AND level=3 -} {14109} +} {15541} do_execsql_test 2.7 { INSERT INTO x2(x2) VALUES('merge=1000,4'); SELECT end_block FROM x2_segdir WHERE level=3; -} {{3828 86120}} +} {{5588 127563}} do_execsql_test 2.8 { SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE blockid BETWEEN start_block AND leaves_end_block AND level=3 -} {86120} +} {127563} #-------------------------------------------------------------------------- # Test that delete markers are removed from FTS segments when possible. @@ -391,7 +397,7 @@ do_execsql_test 7.2 { } { 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006} - 1 0 {16014 -51226} + 1 0 {23694 -69477} } do_execsql_test 7.3 { @@ -400,7 +406,7 @@ do_execsql_test 7.3 { } { 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006} - 1 0 16014 + 1 0 23694 } do_execsql_test 7.4 { @@ -409,28 +415,26 @@ do_execsql_test 7.4 { } { 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006} 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006} - 1 0 16014 + 1 0 23694 } do_execsql_test 7.5 { INSERT INTO x6(x6) VALUES('merge=2500,4'); - SELECT level, idx, end_block FROM x6_segdir; + SELECT level, idx, start_block, leaves_end_block, end_block FROM x6_segdir; } { - 0 0 {598 118006} 0 1 {718 118006} 1 0 16014 + 1 0 719 1171 23694 } do_execsql_test 7.6 { INSERT INTO x6(x6) VALUES('merge=2500,2'); SELECT level, idx, start_block, leaves_end_block, end_block FROM x6_segdir; } { - 2 0 23695 24147 {41262 633507} + 1 0 719 1171 23694 } do_execsql_test 7.7 { SELECT sum(length(block)) FROM x6_segments - WHERE blockid BETWEEN 23695 AND 24147 -} {633507} - +} {635247} finish_test diff --git a/test/fts4langid.test b/test/fts4langid.test index a3059931e7..fdb1876be8 100644 --- a/test/fts4langid.test +++ b/test/fts4langid.test @@ -358,31 +358,30 @@ proc build_multilingual_db_2 {db} { } } -ifcapable fts3_tokenizer { - do_test 4.1.0 { - reset_db - set ptr [fts3_test_tokenizer] - execsql { SELECT fts3_tokenizer('testtokenizer', $ptr) } - build_multilingual_db_2 db - } {} - do_execsql_test 4.1.1 { - SELECT docid FROM t4 WHERE t4 MATCH 'quick'; - } {0} - do_execsql_test 4.1.2 { - SELECT docid FROM t4 WHERE t4 MATCH 'quick' AND lid=1; - } {} - do_execsql_test 4.1.3 { - SELECT docid FROM t4 WHERE t4 MATCH 'Quick' AND lid=1; - } {1} - for {set i 0} {$i < 50} {incr i} { - do_execsql_test 4.1.4.$i { - SELECT count(*) FROM t4 WHERE t4 MATCH 'fox' AND lid=$i; - } [expr 0==($i%2)] - } - do_catchsql_test 4.1.5 { - INSERT INTO t4(content, lid) VALUES('hello world', 101) - } {1 {SQL logic error or missing database}} +do_test 4.1.0 { + reset_db + set ptr [fts3_test_tokenizer] + sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1 + execsql { SELECT fts3_tokenizer('testtokenizer', $ptr) } + build_multilingual_db_2 db +} {} +do_execsql_test 4.1.1 { + SELECT docid FROM t4 WHERE t4 MATCH 'quick'; +} {0} +do_execsql_test 4.1.2 { + SELECT docid FROM t4 WHERE t4 MATCH 'quick' AND lid=1; +} {} +do_execsql_test 4.1.3 { + SELECT docid FROM t4 WHERE t4 MATCH 'Quick' AND lid=1; +} {1} +for {set i 0} {$i < 50} {incr i} { + do_execsql_test 4.1.4.$i { + SELECT count(*) FROM t4 WHERE t4 MATCH 'fox' AND lid=$i; + } [expr 0==($i%2)] } +do_catchsql_test 4.1.5 { + INSERT INTO t4(content, lid) VALUES('hello world', 101) +} {1 {SQL logic error or missing database}} #------------------------------------------------------------------------- # Test cases 5.* @@ -482,6 +481,6 @@ foreach lid [list 4 [expr 1<<30]] { do_execsql_test 5.4.$lid.5 { SELECT count(*) FROM t6_segdir; SELECT count(*) FROM t6_segments; - } {4 4} + } {1 2} } finish_test diff --git a/test/fts4merge.test b/test/fts4merge.test index fabb651e64..eac18311d3 100644 --- a/test/fts4merge.test +++ b/test/fts4merge.test @@ -55,8 +55,6 @@ foreach mod {fts3 fts4} { do_execsql_test 1.3 { SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level } { - 0 {0 1 2 3} - 1 {0 1 2 3 4 5 6} 2 {0 1 2 3} } @@ -71,7 +69,6 @@ foreach mod {fts3 fts4} { do_execsql_test 1.5 { SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level } { - 2 {0 1} 3 0 } @@ -89,7 +86,6 @@ foreach mod {fts3 fts4} { 5 {merge=6,%} 6 {merge=6,six} 7 {merge=6,1} - 8 {merge=6,0} } { do_catchsql_test 2.$tn { INSERT INTO t2(t2) VALUES($arg); @@ -119,11 +115,7 @@ foreach mod {fts3 fts4} { INSERT INTO t2(t2) VALUES('merge=1000000,2'); SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level } { - 0 0 - 2 0 - 3 0 4 0 - 6 0 } #------------------------------------------------------------------------- @@ -203,28 +195,27 @@ foreach mod {fts3 fts4} { INSERT INTO t1(t1) VALUES('merge=1,5'); SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level; } { - 0 {0 1 2} 1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14} 2 {0 1 2 3} } - do_execsql_test 5.4 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'0105'} + do_execsql_test 5.4 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'010F'} do_test 5.5 { foreach docid [execsql {SELECT docid FROM t1}] { execsql {INSERT INTO t1 SELECT * FROM t1 WHERE docid=$docid} } } {} - do_execsql_test 5.6 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'0105'} + do_execsql_test 5.6 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'010F'} do_execsql_test 5.7 { SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level; SELECT quote(value) from t1_stat WHERE rowid=1; } { - 0 {0 1 2 3 4 5 6 7 8 9 10} + 0 {0 1 2 3 4 5 6 7} 1 {0 1 2 3 4 5 6 7 8 9 10 11 12} - 2 {0 1 2 3 4 5 6 7} - X'0105' + 2 {0 1 2 3 4 5 6 7} + X'010F' } do_execsql_test 5.8 { @@ -233,9 +224,8 @@ foreach mod {fts3 fts4} { SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level; SELECT quote(value) from t1_stat WHERE rowid=1; } { - 0 {0 1 2 3 4} 1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13} - 2 {0 1 2 3 4 5 6 7 8} X'0106' + 2 {0 1 2 3 4 5 6 7 8} X'010E' } do_test 5.8.1 { fts3_integrity_check t1 } ok @@ -253,7 +243,7 @@ foreach mod {fts3 fts4} { SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level; SELECT quote(value) from t1_stat WHERE rowid=1; } { - 0 0 1 {0 1} 2 0 3 0 X'0106' + 0 {0 1 2 3 4 5 6 7 8 9 10 11} 1 0 2 0 3 0 X'010E' } do_execsql_test 5.11 { @@ -261,7 +251,7 @@ foreach mod {fts3 fts4} { SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level; SELECT quote(value) from t1_stat WHERE rowid=1; } { - 0 0 1 {0 1} 2 0 3 0 X'' + 1 {0 1} 2 0 3 0 X'010E' } #------------------------------------------------------------------------- diff --git a/test/fts4merge3.test b/test/fts4merge3.test index 329b4d2cc4..08b68b97d9 100644 --- a/test/fts4merge3.test +++ b/test/fts4merge3.test @@ -62,7 +62,7 @@ do_all_bc_test { do_test 1.7 { sql2 { SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1 - } } [list 0 1 2 18 3 5] + } } {2 15 3 5} # Using the old connection, insert many rows. do_test 1.8 { @@ -73,7 +73,7 @@ do_all_bc_test { do_test 1.9 { sql2 { SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1 - } } [list 0 13 1 13 2 5 3 6] + } } [list 0 12 1 13 2 4 3 6] # Run a big incr-merge operation on the db. do_test 1.10 { sql1 { INSERT INTO t2(t2) VALUES('merge=2000,2') } } {} @@ -97,7 +97,7 @@ do_all_bc_test { do_test 1.15 { sql2 { SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1 - } } {6 1} + } } {4 1} } } diff --git a/test/fts4opt.test b/test/fts4opt.test new file mode 100644 index 0000000000..1933c57b1b --- /dev/null +++ b/test/fts4opt.test @@ -0,0 +1,169 @@ +# 2016 March 8 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/fts3_common.tcl +set ::testprefix fts4opt + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +# Create the fts_kjv_genesis procedure which fills and FTS3/4 table +# with the complete text of the Book of Genesis. +# +source $testdir/genesis.tcl + +do_execsql_test 1.0 { CREATE TABLE t1(docid, words) } +fts_kjv_genesis + +#------------------------------------------------------------------------- +# Argument $db is an open database handle. $tbl is the name of an FTS3/4 +# table with the database. This command rearranges the contents of the +# %_segdir table so that all segments within each index are on the same +# level. This means that the 'merge' command can then be used for an +# incremental optimize routine. +# +proc prepare_for_optimize {db tbl} { + $db eval [string map [list % $tbl] { + BEGIN; + CREATE TEMP TABLE tmp_segdir( + level, idx, start_block, leaves_end_block, end_block, root + ); + + INSERT INTO temp.tmp_segdir + SELECT + 1024*(o.level / 1024) + 32, -- level + sum(o.leveli.idx)), -- idx + o.start_block, o.leaves_end_block, o.end_block, o.root -- other + FROM %_segdir o, %_segdir i + WHERE (o.level / 1024) = (i.level / 1024) + GROUP BY o.level, o.idx; + + DELETE FROM %_segdir; + INSERT INTO %_segdir SELECT * FROM temp.tmp_segdir; + DROP TABLE temp.tmp_segdir; + + COMMIT; + }] +} + +do_test 1.1 { + execsql { CREATE VIRTUAL TABLE t2 USING fts4(words, prefix="1,2,3") } + foreach {docid words} [db eval { SELECT * FROM t1 }] { + execsql { INSERT INTO t2(docid, words) VALUES($docid, $words) } + } +} {} + +do_execsql_test 1.2 { + SELECT level, count(*) FROM t2_segdir GROUP BY level +} { + 0 13 1 15 2 5 + 1024 13 1025 15 1026 5 + 2048 13 2049 15 2050 5 + 3072 13 3073 15 3074 5 +} + +do_execsql_test 1.3 { INSERT INTO t2(t2) VALUES('integrity-check') } +prepare_for_optimize db t2 +do_execsql_test 1.4 { INSERT INTO t2(t2) VALUES('integrity-check') } + +do_execsql_test 1.5 { + SELECT level, count(*) FROM t2_segdir GROUP BY level +} { + 32 33 + 1056 33 + 2080 33 + 3104 33 +} + +do_test 1.6 { + while 1 { + set tc1 [db total_changes] + execsql { INSERT INTO t2(t2) VALUES('merge=5,2') } + set tc2 [db total_changes] + if {($tc2 - $tc1) < 2} break + } + execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level } +} {33 1 1057 1 2081 1 3105 1} +do_execsql_test 1.7 { INSERT INTO t2(t2) VALUES('integrity-check') } + +do_execsql_test 1.8 { + INSERT INTO t2(words) SELECT words FROM t1; + SELECT level, count(*) FROM t2_segdir GROUP BY level; +} {0 2 1024 2 2048 2 3072 2} + +#------------------------------------------------------------------------- + +do_execsql_test 2.0 { + DELETE FROM t2; +} +do_test 2.1 { + foreach {docid words} [db eval { SELECT * FROM t1 }] { + execsql { INSERT INTO t2(docid, words) VALUES($docid, $words) } + } + + set i 0 + foreach {docid words} [db eval { SELECT * FROM t1 }] { + if {[incr i] % 2} { execsql { DELETE FROM t2 WHERE docid = $docid } } + } + + set i 0 + foreach {docid words} [db eval { SELECT * FROM t1 }] { + if {[incr i] % 3} { + execsql { INSERT OR REPLACE INTO t2(docid, words) VALUES($docid, $words) } + } + } +} {} + +do_execsql_test 2.2 { + SELECT level, count(*) FROM t2_segdir GROUP BY level +} { + 0 10 1 15 2 12 + 1024 10 1025 15 1026 12 + 2048 10 2049 15 2050 12 + 3072 10 3073 15 3074 12 +} + +do_execsql_test 2.3 { INSERT INTO t2(t2) VALUES('integrity-check') } +prepare_for_optimize db t2 +do_execsql_test 2.4 { INSERT INTO t2(t2) VALUES('integrity-check') } + +do_execsql_test 2.5 { + SELECT level, count(*) FROM t2_segdir GROUP BY level +} { + 32 37 + 1056 37 + 2080 37 + 3104 37 +} + +do_test 2.6 { + while 1 { + set tc1 [db total_changes] + execsql { INSERT INTO t2(t2) VALUES('merge=5,2') } + set tc2 [db total_changes] + if {($tc2 - $tc1) < 2} break + } + execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level } +} {33 1 1057 1 2081 1 3105 1} +do_execsql_test 2.7 { INSERT INTO t2(t2) VALUES('integrity-check') } + +do_execsql_test 2.8 { + INSERT INTO t2(words) SELECT words FROM t1; + SELECT level, count(*) FROM t2_segdir GROUP BY level; +} {0 2 1024 2 2048 2 3072 2} + +finish_test diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index 03776f1e70..1e0ce86e66 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -70,6 +70,7 @@ #include #include #include "sqlite3.h" +#include #define ISSPACE(X) isspace((unsigned char)(X)) #define ISDIGIT(X) isdigit((unsigned char)(X)) @@ -255,8 +256,9 @@ static VFile *createVFile(const char *zName, int sz, unsigned char *pData){ if( i>=MX_FILE ) return 0; pNew = &g.aFile[i]; if( zName ){ - pNew->zFilename = safe_realloc(0, strlen(zName)+1); - memcpy(pNew->zFilename, zName, strlen(zName)+1); + int nName = (int)strlen(zName)+1; + pNew->zFilename = safe_realloc(0, nName); + memcpy(pNew->zFilename, zName, nName); }else{ pNew->zFilename = 0; } @@ -620,12 +622,14 @@ static void inmemVfsRegister(void){ */ static void runSql(sqlite3 *db, const char *zSql, unsigned runFlags){ const char *zMore; + const char *zEnd = &zSql[strlen(zSql)]; sqlite3_stmt *pStmt; while( zSql && zSql[0] ){ zMore = 0; pStmt = 0; sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore); + assert( zMore<=zEnd ); if( zMore==zSql ) break; if( runFlags & SQL_TRACE ){ const char *z = zSql; @@ -783,8 +787,7 @@ static void showHelp(void){ " --export-db DIR Write databases to files(s) in DIR. Works with --dbid\n" " --export-sql DIR Write SQL to file(s) in DIR. Also works with --sqlid\n" " --help Show this help text\n" -" -q Reduced output\n" -" --quiet Reduced output\n" +" -q|--quiet Reduced output\n" " --limit-mem N Limit memory used by test SQLite instance to N bytes\n" " --limit-vdbe Panic if an sync SQL runs for more than 100,000 cycles\n" " --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n" @@ -795,8 +798,7 @@ static void showHelp(void){ " --result-trace Show the results of each SQL command\n" " --sqlid N Use only SQL where sqlid=N\n" " --timeout N Abort if any single test case needs more than N seconds\n" -" -v Increased output\n" -" --verbose Increased output\n" +" -v|--verbose Increased output. Repeat for more output.\n" ); } @@ -919,8 +921,8 @@ int main(int argc, char **argv){ }else if( strcmp(z,"verbose")==0 || strcmp(z,"v")==0 ){ quietFlag = 0; - verboseFlag = 1; - runFlags |= SQL_TRACE; + verboseFlag++; + if( verboseFlag>1 ) runFlags |= SQL_TRACE; }else { fatalError("unknown option: %s", argv[i]); @@ -1055,7 +1057,7 @@ int main(int argc, char **argv){ /* Print the description, if there is one */ if( !quietFlag ){ zDbName = azSrcDb[iSrcDb]; - i = strlen(zDbName) - 1; + i = (int)strlen(zDbName) - 1; while( i>0 && zDbName[i-1]!='/' && zDbName[i-1]!='\\' ){ i--; } zDbName += i; sqlite3_prepare_v2(db, "SELECT msg FROM readme", -1, &pStmt, 0); diff --git a/test/icu.test b/test/icu.test index 73cb9b913e..743bcfaea1 100644 --- a/test/icu.test +++ b/test/icu.test @@ -72,6 +72,10 @@ test_expr icu-2.6 {i1=$::OGRAVE} {upper(i1)} $::OGRAVE test_expr icu-2.7 {i1=$::szlig} {upper(i1)} "SS" test_expr icu-2.8 {i1='SS'} {lower(i1)} "ss" +do_execsql_test icu-2.9 { + SELECT upper(char(0xfb04,0xfb04,0xfb04,0xfb04)); +} {FFLFFLFFLFFL} + # In turkish (locale="tr_TR"), the lower case version of I # is "small dotless i" (code point 0x131 (decimal 305)). # @@ -133,4 +137,9 @@ do_catchsql_test icu-5.4 { do_catchsql_test icu-5.4 { SELECT 'abc' REGEXP } {1 {near " ": syntax error}} do_catchsql_test icu-5.5 { SELECT 'abc' REGEXP, 1 } {1 {near ",": syntax error}} + +do_malloc_test icu-6.10 -sqlbody { + SELECT upper(char(0xfb04,0xdf,0xfb04,0xe8,0xfb04)); +} + finish_test diff --git a/test/main.test b/test/main.test index 3f35afe20c..9346cf6ced 100644 --- a/test/main.test +++ b/test/main.test @@ -319,7 +319,7 @@ do_test main-3.1 { sqlite3 db testdb set v [catch {execsql {SELECT * from T1 where x!!5}} msg] lappend v $msg -} {1 {unrecognized token: "!!"}} +} {1 {unrecognized token: "!"}} do_test main-3.2 { catch {db close} foreach f [glob -nocomplain testdb/*] {forcedelete $f} diff --git a/test/misc1.test b/test/misc1.test index 400a4517b4..e646bfd098 100644 --- a/test/misc1.test +++ b/test/misc1.test @@ -699,7 +699,7 @@ do_catchsql_test misc1-23.3 { # do_test misc1-24.0 { list [catch { sqlite3_prepare_v2 db ! -1 dummy } msg] $msg -} {1 {(1) unrecognized token: "!}} +} {1 {(1) unrecognized token: "!"}} # The following query (provided by Kostya Serebryany) used to take 25 # minutes to prepare. This has been speeded up to about 250 milliseconds. diff --git a/test/misc8.test b/test/misc8.test index 3ff52e56f1..3817cadf94 100644 --- a/test/misc8.test +++ b/test/misc8.test @@ -94,5 +94,18 @@ do_execsql_test misc8-2.1 { 0 10 {} 10 {} {} } +# 2016-02-26: An assertion fault found by the libFuzzer project +# +do_execsql_test misc8-3.0 { + SELECT * + FROM + ( + (SELECT 0 AS i) AS x1, + (SELECT 1) AS x2 + ) AS x3, + (SELECT 6 AS j UNION ALL SELECT 7) AS x4 + WHERE i0} { + faultsim_restore_and_reopen $prefix + foreach f [glob ${prefix}-mj*] { forcedelete $f } + } else { + db close + sqlite3 db $prefix + } execsql "ATTACH '${prefix}2' AS aux" + glob -nocomplain ${prefix}-mj* } {} do_execsql_test pager1-4.4.$tn.8 { SELECT * FROM a @@ -678,7 +735,6 @@ db close tv delete forcedelete $dirname - # Set up a VFS to make a copy of the file-system just before deleting a # journal file to commit a transaction. The transaction modifies exactly # two database pages (and page 1 - the change counter). diff --git a/test/permutations.test b/test/permutations.test index c099889067..6c02af8fbe 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -86,7 +86,10 @@ proc test_set {args} { # set alltests [list] foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] } -foreach f [glob -nocomplain $testdir/../ext/rtree/*.test] { +foreach f [glob -nocomplain \ + $testdir/../ext/rtree/*.test \ + $testdir/../ext/fts5/test/*.test \ +] { lappend alltests $f } @@ -157,7 +160,8 @@ test_suite "veryquick" -prefix "" -description { This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ - test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* + test_set $allquicktests -exclude *malloc* *ioerr* *fault* *bigfile* *_err* \ + *fts5corrupt* *fts5big* *fts5aj* ] test_suite "extraquick" -prefix "" -description { @@ -612,6 +616,7 @@ test_suite "utf16" -description { trace.test trigger1.test trigger2.test trigger3.test trigger4.test types2.test types.test unique.test update.test vacuum.test view.test where.test + bestindex1.test } # Run some tests in exclusive locking mode. diff --git a/test/regexp2.test b/test/regexp2.test new file mode 100644 index 0000000000..9928e8ce8e --- /dev/null +++ b/test/regexp2.test @@ -0,0 +1,125 @@ +# 2016 February 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 tests for the REGEXP operator in ext/misc/regexp.c. +# It focuses on the use of the sqlite3_set_auxdata()/get_auxdata() APIs. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix regexp2 + +load_static_extension db regexp + +#------------------------------------------------------------------------- +# Test that triggers do not become confused and use aux-data created by +# a different trigger for a different REGEXP invocation. +# +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c); + CREATE TABLE x1(x, y, z); + CREATE TABLE x2(x, y, z); + + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN + INSERT INTO x1 VALUES( + new.a REGEXP 'abc', + new.b REGEXP 'abc', + new.c REGEXP 'abc' + ); + END; + + CREATE TRIGGER tr2 AFTER INSERT ON t1 BEGIN + INSERT INTO x2 VALUES( + new.a REGEXP 'def', + new.b REGEXP 'def', + new.c REGEXP 'def' + ); + END; + + INSERT INTO t1 VALUES('abc', 'def', 'abc'); + SELECT * FROM t1; +} {abc def abc} + +do_execsql_test 1.1 { SELECT * FROM x1 } {1 0 1} +do_execsql_test 1.2 { SELECT * FROM x2 } {0 1 0} + +#------------------------------------------------------------------------- +# Test that if an exception is thrown several triggers deep, all aux-data +# objects are cleaned up correctly. +# +proc sql_error {} { + error "SQL error!" +} +db func error sql_error +do_execsql_test 2.0 { + CREATE TABLE t2(a, b); + CREATE TABLE t3(c, d); + CREATE TABLE t4(e, f); + + CREATE TRIGGER t2_tr1 AFTER UPDATE ON t2 BEGIN + UPDATE t3 SET d = new.b WHERE c = old.a; + END; + + CREATE TRIGGER t3_tr1 AFTER UPDATE ON t3 BEGIN + UPDATE t4 SET f = new.d WHERE e = old.c AND new.d REGEXP 'a.*'; + END; + + CREATE TRIGGER t4_tr1 AFTER UPDATE ON t4 BEGIN + SELECT CASE WHEN new.f REGEXP '.*y.*' THEN error() ELSE 1 END; + END; + + INSERT INTO t2 VALUES(1, 'a_x_1'); + INSERT INTO t2 VALUES(2, 'a_y_1'); + + INSERT INTO t3 VALUES(1, 'b1'); + INSERT INTO t3 VALUES(2, 'b2'); + + INSERT INTO t4 VALUES(1, 'b1'); + INSERT INTO t4 VALUES(2, 'b2'); +} {} + +do_catchsql_test 2.1 { + UPDATE t2 SET a=a+1 WHERE b REGEXP 'a.*' AND b REGEXP '.*1'; +} {1 {SQL error!}} + +# Test that the triggers used in the test above work as expected. +# +do_execsql_test 2.2 { + UPDATE t2 SET b = 'a_abc_1'; +} {} +do_execsql_test 2.3 { + SELECT * FROM t2; + SELECT * FROM t3; + SELECT * FROM t4; +} {1 a_abc_1 2 a_abc_1 1 a_abc_1 2 a_abc_1 1 a_abc_1 2 a_abc_1} + +#------------------------------------------------------------------------- +# Test that trigger parameters (i.e. new.* and old.*) refs are not +# considered to be constant across separate invocations of the trigger. +# +do_execsql_test 3.0 { + CREATE TABLE t5(a); + CREATE TABLE t6(x); + + CREATE TRIGGER t5tr AFTER DELETE ON t5 BEGIN + DELETE FROM t6 WHERE t6.x REGEXP old.a; + END; + + INSERT INTO t5 VALUES ('^a.*'), ('^b.*'), ('^c.*'); + INSERT INTO t6 VALUES ('eab'), ('abc'), ('bcd'), ('cde'), ('dea'); + + DELETE FROM t5; + SELECT * FROM t6; +} {eab dea} + + +finish_test + diff --git a/test/releasetest.tcl b/test/releasetest.tcl index bb902eec3e..2bcf1aa511 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -203,6 +203,7 @@ array set ::Configs [strip_comments { -DSQLITE_MAX_ATTACHED=30 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_FTS4 + -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_FTS4_PARENTHESIS -DSQLITE_DISABLE_FTS4_DEFERRED -DSQLITE_ENABLE_RTREE diff --git a/test/shell1.test b/test/shell1.test index 2fda62ee13..1b9ea1d383 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -625,9 +625,9 @@ do_test shell1-3.23.2 { } {1 {Usage: .show}} # .stats ON|OFF Turn stats on or off -do_test shell1-3.23b.1 { - catchcmd "test.db" ".stats" -} {1 {Usage: .stats on|off}} +#do_test shell1-3.23b.1 { +# catchcmd "test.db" ".stats" +#} {1 {Usage: .stats on|off}} do_test shell1-3.23b.2 { catchcmd "test.db" ".stats ON" } {0 {}} @@ -637,7 +637,7 @@ do_test shell1-3.23b.3 { do_test shell1-3.23b.4 { # too many arguments catchcmd "test.db" ".stats OFF BAD" -} {1 {Usage: .stats on|off}} +} {1 {Usage: .stats ?on|off?}} # .tables ?TABLE? List names of tables # If TABLE specified, only list tables matching diff --git a/test/shell4.test b/test/shell4.test index fcb0b2b715..18e7d7fdf6 100644 --- a/test/shell4.test +++ b/test/shell4.test @@ -61,9 +61,9 @@ do_test shell4-1.2.2 { } {0} # .stats ON|OFF Turn stats on or off -do_test shell4-1.3.1 { - catchcmd "test.db" ".stats" -} {1 {Usage: .stats on|off}} +#do_test shell4-1.3.1 { +# catchcmd "test.db" ".stats" +#} {1 {Usage: .stats on|off}} do_test shell4-1.3.2 { catchcmd "test.db" ".stats ON" } {0 {}} @@ -73,7 +73,7 @@ do_test shell4-1.3.3 { do_test shell4-1.3.4 { # too many arguments catchcmd "test.db" ".stats OFF BAD" -} {1 {Usage: .stats on|off}} +} {1 {Usage: .stats ?on|off?}} # NB. whitespace is important do_test shell4-1.4.1 { diff --git a/test/snapshot.test b/test/snapshot.test index 63a0e30175..ba3a666796 100644 --- a/test/snapshot.test +++ b/test/snapshot.test @@ -334,6 +334,26 @@ do_test 6.3 { sqlite3_snapshot_open db2 main $::snapshot db2 eval { SELECT * FROM x1 } } {z zz zzz} +do_test 6.4 { + db2 close + sqlite3 db2 test.db + db2 eval "PRAGMA application_id" + db2 eval "BEGIN" + sqlite3_snapshot_open db2 main $::snapshot + db2 eval { SELECT * FROM x1 } +} {z zz zzz} + +# EVIDENCE-OF: R-55491-50411 A snapshot will fail to open if the +# database connection D has not previously completed at least one read +# operation against the database file. +# +do_test 6.5 { + db2 close + sqlite3 db2 test.db + db2 eval "BEGIN" + list [catch {sqlite3_snapshot_open db2 main $::snapshot} msg] $msg +} {1 SQLITE_ERROR} + sqlite3_snapshot_free $snapshot finish_test diff --git a/test/speedtest1.c b/test/speedtest1.c index b41c732053..d7f2af47b8 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -1167,6 +1167,45 @@ void testset_debug1(void){ } } +#ifdef __linux__ +#include +#include + +/* +** Attempt to display I/O stats on Linux using /proc/PID/io +*/ +static void displayLinuxIoStats(FILE *out){ + FILE *in; + char z[200]; + sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); + in = fopen(z, "rb"); + if( in==0 ) return; + while( fgets(z, sizeof(z), in)!=0 ){ + static const struct { + const char *zPattern; + const char *zDesc; + } aTrans[] = { + { "rchar: ", "Bytes received by read():" }, + { "wchar: ", "Bytes sent to write():" }, + { "syscr: ", "Read() system calls:" }, + { "syscw: ", "Write() system calls:" }, + { "read_bytes: ", "Bytes rcvd from storage:" }, + { "write_bytes: ", "Bytes sent to storage:" }, + { "cancelled_write_bytes: ", "Cancelled write bytes:" }, + }; + int i; + for(i=0; i0 ORDER BY a LIMIT 3 } } {1 100 4 2 99 9 3 98 16 nosort} -do_test where-6.7 { +do_test where-6.7.1 { cksort { - SELECT * FROM t3 WHERE b>0 ORDER BY a LIMIT 3 + SELECT * FROM t3 WHERE b>0 ORDER BY a LIMIT 10 } -} {1 100 4 2 99 9 3 98 16 nosort} +} {/1 100 4 2 99 9 3 98 16 .* nosort/} +do_test where-6.7.2 { + cksort { + SELECT * FROM t3 WHERE b>0 ORDER BY a LIMIT 1 + } +} {1 100 4 sort} ifcapable subquery { do_test where-6.8a { cksort { diff --git a/test/zerodamage.test b/test/zerodamage.test index dccaba816e..d781ab89a3 100644 --- a/test/zerodamage.test +++ b/test/zerodamage.test @@ -112,6 +112,7 @@ ifcapable wal { db close sqlite3 db file:test.db?psow=FALSE -uri 1 db eval { + PRAGMA synchronous=FULL; UPDATE t1 SET y=randomblob(50) WHERE x=124; } file size test.db-wal diff --git a/tool/Replace.cs b/tool/Replace.cs new file mode 100644 index 0000000000..3475a47e6e --- /dev/null +++ b/tool/Replace.cs @@ -0,0 +1,223 @@ +/* +** 2016 February 26 +** +** 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 C# code to perform regular expression replacements +** using the standard input and output channels. +*/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +/////////////////////////////////////////////////////////////////////////////// + +#region Assembly Metadata +[assembly: AssemblyTitle("Replace Tool")] +[assembly: AssemblyDescription("Replace text using standard input/output.")] +[assembly: AssemblyCompany("SQLite Development Team")] +[assembly: AssemblyProduct("SQLite")] +[assembly: AssemblyCopyright("Public Domain")] +[assembly: ComVisible(false)] +[assembly: Guid("95a0513f-8863-48cd-a76f-cb80868cb578")] +[assembly: AssemblyVersion("1.0.*")] + +#if DEBUG +[assembly: AssemblyConfiguration("Debug")] +#else +[assembly: AssemblyConfiguration("Release")] +#endif +#endregion + +/////////////////////////////////////////////////////////////////////////////// + +namespace Replace +{ + /// + /// This enumeration is used to represent all the possible exit codes from + /// this tool. + /// + internal enum ExitCode + { + /// + /// The file download was a success. + /// + Success = 0, + + /// + /// The command line arguments are missing (i.e. null). Generally, + /// this should not happen. + /// + MissingArgs = 1, + + /// + /// The wrong number of command line arguments was supplied. + /// + WrongNumArgs = 2, + + /// + /// The "matchingOnly" flag could not be converted to a value of the + /// type. + /// + BadMatchingOnlyFlag = 3, + + /// + /// An exception was caught in . Generally, this + /// should not happen. + /// + Exception = 4 + } + + /////////////////////////////////////////////////////////////////////////// + + internal static class Replace + { + #region Private Support Methods + /// + /// This method displays an error message to the console and/or + /// displays the command line usage information for this tool. + /// + /// + /// The error message to display, if any. + /// + /// + /// Non-zero to display the command line usage information. + /// + private static void Error( + string message, + bool usage + ) + { + if (message != null) + Console.WriteLine(message); + + string fileName = Path.GetFileName( + Process.GetCurrentProcess().MainModule.FileName); + + Console.WriteLine(String.Format( + "usage: {0} ", + fileName)); + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Program Entry Point + /// + /// This is the entry-point for this tool. It handles processing the + /// command line arguments, reading from the standard input channel, + /// replacing any matching lines of text, and writing to the standard + /// output channel. + /// + /// + /// The command line arguments. + /// + /// + /// Zero upon success; non-zero on failure. This will be one of the + /// values from the enumeration. + /// + private static int Main( + string[] args + ) + { + // + // NOTE: Sanity check the command line arguments. + // + if (args == null) + { + Error(null, true); + return (int)ExitCode.MissingArgs; + } + + if (args.Length != 3) + { + Error(null, true); + return (int)ExitCode.WrongNumArgs; + } + + try + { + // + // NOTE: Create a regular expression from the first command + // line argument. Then, grab the replacement string, + // which is the second argument. + // + Regex regEx = new Regex(args[0]); + string replacement = args[1]; + + // + // NOTE: Attempt to convert the third argument to a boolean. + // + bool matchingOnly; + + if (!bool.TryParse(args[2], out matchingOnly)) + { + Error(null, true); + return (int)ExitCode.BadMatchingOnlyFlag; + } + + // + // NOTE: Grab the standard input and output channels from the + // console. + // + TextReader inputTextReader = Console.In; + TextWriter outputTextWriter = Console.Out; + + // + // NOTE: Loop until end-of-file is hit on the standard input + // stream. + // + while (true) + { + // + // NOTE: Read a line from the standard input channel. If + // null is returned here, there is no more input and + // we are done. + // + string inputLine = inputTextReader.ReadLine(); + + if (inputLine == null) + break; + + // + // NOTE: Perform regular expression replacements on this + // line, if any. Then, write the modified line to + // the standard output channel. + // + string outputLine = regEx.Replace(inputLine, replacement); + + if (!matchingOnly || !String.Equals( + inputLine, outputLine, StringComparison.Ordinal)) + { + outputTextWriter.WriteLine(outputLine); + } + } + + // + // NOTE: At this point, everything has succeeded. + // + return (int)ExitCode.Success; + } + catch (Exception e) + { + // + // NOTE: An exception was caught. Report it via the console + // and return failure. + // + Error(e.ToString(), false); + return (int)ExitCode.Exception; + } + } + #endregion + } +} diff --git a/tool/addopcodes.tcl b/tool/addopcodes.tcl index 84e3994ce8..9b34cf2e03 100644 --- a/tool/addopcodes.tcl +++ b/tool/addopcodes.tcl @@ -38,6 +38,7 @@ set extras { UPLUS REGISTER ASTERISK + SPAN SPACE ILLEGAL } diff --git a/tool/build-all-msvc.bat b/tool/build-all-msvc.bat index 84528e37c2..c96708cef5 100755 --- a/tool/build-all-msvc.bat +++ b/tool/build-all-msvc.bat @@ -22,7 +22,7 @@ REM REM Example: REM REM CD /D C:\dev\sqlite\core -REM tool\build-all-msvc.bat C:\Temp +REM CALL tool\build-all-msvc.bat C:\Temp REM REM In the example above, "C:\dev\sqlite\core" represents the root of the REM source tree for SQLite and "C:\Temp" represents the final destination @@ -54,6 +54,11 @@ REM REM There are a few other environment variables that impact the build process REM when set ^(to anything^), they are: REM +REM USE_AUTOCONF_MAKEFILE +REM +REM When set, the "autoconf" Makefile for MSVC will be used instead of the main +REM Makefile for MSVC. It must exist at "%ROOT%\autoconf\Makefile.msc". +REM REM NOCLEAN REM REM When set, the "clean" target will not be used during each build iteration. @@ -69,6 +74,11 @@ REM be skipped and they will not appear in the final destination directory. REM Setting this environment variable is never strictly needed and could cause REM issues in some circumstances; therefore, setting it is not recommended. REM +REM NOMEMDEBUG +REM +REM When set, disables use of MEMDEBUG when building binaries for the "Debug" +REM configuration. +REM REM BUILD_ALL_SHELL REM REM When set, the command line shell will be built for each selected platform @@ -88,17 +98,30 @@ REM on the WindowsSdkDir environment variable. It causes this batch script to REM assume the Windows 10.0 SDK location should be used. REM REM NMAKE_ARGS +REM NMAKE_ARGS_DEBUG +REM NMAKE_ARGS_RETAIL REM -REM When set, the value is expanded and passed to the NMAKE command line, after -REM its other arguments. This is used to specify additional NMAKE options, for -REM example: +REM When set, these values are expanded and passed to the NMAKE command line, +REM after its other arguments. These may be used to specify additional NMAKE +REM options, for example: REM REM SET NMAKE_ARGS=FOR_WINRT=1 +REM SET NMAKE_ARGS_DEBUG=MEMDEBUG=1 +REM SET NMAKE_ARGS_RETAIL=WIN32HEAP=1 REM REM Using the above command before running this tool will cause the compiled REM binaries to target the WinRT environment, which provides a subset of the REM Win32 API. REM +REM DLL_FILE_NAME +REM DLL_PDB_FILE_NAME +REM LIB_FILE_NAME +REM EXE_FILE_NAME +REM EXE_PDB_FILE_NAME +REM +REM When set, these values will override the associated target file name used +REM for the build. +REM SETLOCAL REM SET __ECHO=ECHO @@ -204,11 +227,17 @@ REM NOTE: If the command used to invoke NMAKE is not already set, use the REM default. REM IF NOT DEFINED NMAKE_CMD ( - SET NMAKE_CMD=nmake -B -f Makefile.msc + IF DEFINED USE_AUTOCONF_MAKEFILE ( + SET NMAKE_CMD=nmake -B -f autoconf\Makefile.msc + ) ELSE ( + SET NMAKE_CMD=nmake -B -f Makefile.msc + ) ) %_VECHO% NmakeCmd = '%NMAKE_CMD%' %_VECHO% NmakeArgs = '%NMAKE_ARGS%' +%_VECHO% NmakeArgsDebug = '%NMAKE_ARGS_DEBUG%' +%_VECHO% NmakeArgsRetail = '%NMAKE_ARGS_RETAIL%' REM REM NOTE: Setup environment variables to translate between the MSVC platform @@ -255,6 +284,30 @@ IF NOT DEFINED %TCLSH_FILE%_PATH ( GOTO errors ) +REM +REM NOTE: Setup the default names for the build targets we are creating. Any +REM ^(or all^) of these may end up being overridden. +REM +IF NOT DEFINED DLL_FILE_NAME ( + SET DLL_FILE_NAME=sqlite3.dll +) + +IF NOT DEFINED DLL_PDB_FILE_NAME ( + SET DLL_PDB_FILE_NAME=sqlite3.pdb +) + +IF NOT DEFINED LIB_FILE_NAME ( + SET LIB_FILE_NAME=sqlite3.lib +) + +IF NOT DEFINED EXE_FILE_NAME ( + SET EXE_FILE_NAME=sqlite3.exe +) + +IF NOT DEFINED EXE_PDB_FILE_NAME ( + SET EXE_PDB_FILE_NAME=sqlite3sh.pdb +) + REM REM NOTE: Set the TOOLPATH variable to contain all the directories where the REM external tools were found in the search above. @@ -434,12 +487,20 @@ FOR %%P IN (%PLATFORMS%) DO ( REM NOTE: Setting this to non-zero should enable the SQLITE_MEMDEBUG REM define. REM - SET MEMDEBUG=1 + IF NOT DEFINED NOMEMDEBUG ( + SET MEMDEBUG=1 + ) ) ELSE ( CALL :fn_UnsetVariable DEBUG CALL :fn_UnsetVariable MEMDEBUG ) + REM + REM NOTE: Copy the extra NMAKE arguments for this configuration into the + REM common variable used by the actual commands. + REM + CALL :fn_CopyVariable NMAKE_ARGS_%%B NMAKE_ARGS_CFG + REM REM NOTE: Launch a nested command shell to perform the following steps: REM @@ -546,7 +607,7 @@ FOR %%P IN (%PLATFORMS%) DO ( REM file, etc. REM IF NOT DEFINED NOCLEAN ( - %__ECHO% %NMAKE_CMD% clean + CALL :fn_MakeClean %%D IF ERRORLEVEL 1 ( ECHO Failed to clean for platform %%P. @@ -559,7 +620,7 @@ FOR %%P IN (%PLATFORMS%) DO ( REM specifically wanting to build for each platform. REM %_AECHO% Cleaning final core library output files only... - %__ECHO% DEL /Q *.lo sqlite3.dll sqlite3.lib sqlite3.pdb 2%REDIRECT% NUL + %__ECHO% DEL /Q *.lo "%DLL_FILE_NAME%" "%LIB_FILE_NAME%" "%DLL_PDB_FILE_NAME%" 2%REDIRECT% NUL ) REM @@ -569,10 +630,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM Also, disable looking for and/or linking to the native Tcl REM runtime library. REM - %__ECHO% %NMAKE_CMD% sqlite3.dll XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS% + CALL :fn_MakeDll %%D IF ERRORLEVEL 1 ( - ECHO Failed to build %%B "sqlite3.dll" for platform %%P. + ECHO Failed to build %%B "%DLL_FILE_NAME%" for platform %%P. GOTO errors ) @@ -580,10 +641,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM NOTE: Copy the "sqlite3.dll" file to the appropriate directory for REM the build and platform beneath the binary directory. REM - %__ECHO% XCOPY sqlite3.dll "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% + %__ECHO% XCOPY "%DLL_FILE_NAME%" "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% IF ERRORLEVEL 1 ( - ECHO Failed to copy "sqlite3.dll" to "%BINARYDIRECTORY%\%%B\%%D\". + ECHO Failed to copy "%DLL_FILE_NAME%" to "%BINARYDIRECTORY%\%%B\%%D\". GOTO errors ) @@ -591,10 +652,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM NOTE: Copy the "sqlite3.lib" file to the appropriate directory for REM the build and platform beneath the binary directory. REM - %__ECHO% XCOPY sqlite3.lib "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% + %__ECHO% XCOPY "%LIB_FILE_NAME%" "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% IF ERRORLEVEL 1 ( - ECHO Failed to copy "sqlite3.lib" to "%BINARYDIRECTORY%\%%B\%%D\". + ECHO Failed to copy "%LIB_FILE_NAME%" to "%BINARYDIRECTORY%\%%B\%%D\". GOTO errors ) @@ -604,10 +665,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM are prevented from doing so. REM IF NOT DEFINED NOSYMBOLS ( - %__ECHO% XCOPY sqlite3.pdb "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% + %__ECHO% XCOPY "%DLL_PDB_FILE_NAME%" "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% IF ERRORLEVEL 1 ( - ECHO Failed to copy "sqlite3.pdb" to "%BINARYDIRECTORY%\%%B\%%D\". + ECHO Failed to copy "%DLL_PDB_FILE_NAME%" to "%BINARYDIRECTORY%\%%B\%%D\". GOTO errors ) ) @@ -627,7 +688,7 @@ FOR %%P IN (%PLATFORMS%) DO ( REM specifically wanting to build for each platform. REM %_AECHO% Cleaning final shell executable output files only... - %__ECHO% DEL /Q sqlite3.exe sqlite3sh.pdb 2%REDIRECT% NUL + %__ECHO% DEL /Q "%EXE_FILE_NAME%" "%EXE_PDB_FILE_NAME%" 2%REDIRECT% NUL ) REM @@ -637,10 +698,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM Also, disable looking for and/or linking to the native Tcl REM runtime library. REM - %__ECHO% %NMAKE_CMD% sqlite3.exe XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS% + CALL :fn_MakeExe %%D IF ERRORLEVEL 1 ( - ECHO Failed to build %%B "sqlite3.exe" for platform %%P. + ECHO Failed to build %%B "%EXE_FILE_NAME%" for platform %%P. GOTO errors ) @@ -648,10 +709,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM NOTE: Copy the "sqlite3.exe" file to the appropriate directory REM for the build and platform beneath the binary directory. REM - %__ECHO% XCOPY sqlite3.exe "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% + %__ECHO% XCOPY "%EXE_FILE_NAME%" "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% IF ERRORLEVEL 1 ( - ECHO Failed to copy "sqlite3.exe" to "%BINARYDIRECTORY%\%%B\%%D\". + ECHO Failed to copy "%EXE_FILE_NAME%" to "%BINARYDIRECTORY%\%%B\%%D\". GOTO errors ) @@ -661,10 +722,10 @@ FOR %%P IN (%PLATFORMS%) DO ( REM unless we are prevented from doing so. REM IF NOT DEFINED NOSYMBOLS ( - %__ECHO% XCOPY sqlite3sh.pdb "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% + %__ECHO% XCOPY "%EXE_PDB_FILE_NAME%" "%BINARYDIRECTORY%\%%B\%%D\" %FFLAGS% %DFLAGS% IF ERRORLEVEL 1 ( - ECHO Failed to copy "sqlite3sh.pdb" to "%BINARYDIRECTORY%\%%B\%%D\". + ECHO Failed to copy "%EXE_PDB_FILE_NAME%" to "%BINARYDIRECTORY%\%%B\%%D\". GOTO errors ) ) @@ -696,6 +757,18 @@ REM NOTE: If we get to this point, we have succeeded. REM GOTO no_errors +:fn_MakeClean + %__ECHO% %NMAKE_CMD% clean "PLATFORM=%1" XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS% %NMAKE_ARGS_CFG% + GOTO :EOF + +:fn_MakeDll + %__ECHO% %NMAKE_CMD% "%DLL_FILE_NAME%" "PLATFORM=%1" XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS% %NMAKE_ARGS_CFG% + GOTO :EOF + +:fn_MakeExe + %__ECHO% %NMAKE_CMD% "%EXE_FILE_NAME%" "PLATFORM=%1" XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS% %NMAKE_ARGS_CFG% + GOTO :EOF + :fn_ShowVariable SETLOCAL SET __ECHO_CMD=ECHO %%%2%% diff --git a/tool/lemon.c b/tool/lemon.c index d704deb624..cefdf80174 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -286,6 +286,8 @@ struct rule { const char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ int line; /* Line number at which code begins */ const char *code; /* The code executed when this rule is reduced */ + const char *codePrefix; /* Setup code before code[] above */ + const char *codeSuffix; /* Breakdown code after code[] above */ struct symbol *precsym; /* Precedence symbol for this rule */ int index; /* An index number for this rule */ Boolean canReduce; /* True if this rule is ever reduced */ @@ -3430,6 +3432,7 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ int c; char zInt[40]; if( zText==0 ){ + if( used==0 && z!=0 ) z[0] = 0; used = 0; return z; } @@ -3466,12 +3469,21 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){ ** zCode is a string that is the action associated with a rule. Expand ** the symbols in this string so that the refer to elements of the parser ** stack. +** +** Return 1 if the expanded code requires that "yylhsminor" local variable +** to be defined. */ -PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ +PRIVATE int translate_code(struct lemon *lemp, struct rule *rp){ char *cp, *xp; int i; - char lhsused = 0; /* True if the LHS element has been used */ - char used[MAXRHS]; /* True for each RHS element which is used */ + int rc = 0; /* True if yylhsminor is used */ + int dontUseRhs0 = 0; /* If true, use of left-most RHS label is illegal */ + const char *zSkip = 0; /* The zOvwrt comment within rp->code, or NULL */ + char lhsused = 0; /* True if the LHS element has been used */ + char lhsdirect; /* True if LHS writes directly into stack */ + char used[MAXRHS]; /* True for each RHS element which is used */ + char zLhs[50]; /* Convert the LHS symbol into this string */ + char zOvwrt[900]; /* Comment that to allow LHS to overwrite RHS */ for(i=0; inrhs; i++) used[i] = 0; lhsused = 0; @@ -3482,23 +3494,83 @@ PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ rp->line = rp->ruleline; } + + if( rp->lhsalias==0 ){ + /* There is no LHS value symbol. */ + lhsdirect = 1; + }else if( rp->nrhs==0 ){ + /* If there are no RHS symbols, then writing directly to the LHS is ok */ + lhsdirect = 1; + }else if( rp->rhsalias[0]==0 ){ + /* The left-most RHS symbol has not value. LHS direct is ok. But + ** we have to call the distructor on the RHS symbol first. */ + lhsdirect = 1; + if( has_destructor(rp->rhs[0],lemp) ){ + append_str(0,0,0,0); + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[0]->index,1-rp->nrhs); + rp->codePrefix = Strsafe(append_str(0,0,0,0)); + } + }else if( strcmp(rp->lhsalias,rp->rhsalias[0])==0 ){ + /* The LHS symbol and the left-most RHS symbol are the same, so + ** direct writing is allowed */ + lhsdirect = 1; + lhsused = 1; + used[0] = 1; + if( rp->lhs->dtnum!=rp->rhs[0]->dtnum ){ + ErrorMsg(lemp->filename,rp->ruleline, + "%s(%s) and %s(%s) share the same label but have " + "different datatypes.", + rp->lhs->name, rp->lhsalias, rp->rhs[0]->name, rp->rhsalias[0]); + lemp->errorcnt++; + } + }else{ + lemon_sprintf(zOvwrt, "/*%s-overwrites-%s*/", + rp->lhsalias, rp->rhsalias[0]); + zSkip = strstr(rp->code, zOvwrt); + if( zSkip!=0 ){ + /* The code contains a special comment that indicates that it is safe + ** for the LHS label to overwrite left-most RHS label. */ + lhsdirect = 1; + }else{ + lhsdirect = 0; + } + } + if( lhsdirect ){ + sprintf(zLhs, "yymsp[%d].minor.yy%d",1-rp->nrhs,rp->lhs->dtnum); + }else{ + rc = 1; + sprintf(zLhs, "yylhsminor.yy%d",rp->lhs->dtnum); + } + append_str(0,0,0,0); /* This const cast is wrong but harmless, if we're careful. */ for(cp=(char *)rp->code; *cp; cp++){ + if( cp==zSkip ){ + append_str(zOvwrt,0,0,0); + cp += lemonStrlen(zOvwrt)-1; + dontUseRhs0 = 1; + continue; + } if( ISALPHA(*cp) && (cp==rp->code || (!ISALNUM(cp[-1]) && cp[-1]!='_')) ){ char saved; for(xp= &cp[1]; ISALNUM(*xp) || *xp=='_'; xp++); saved = *xp; *xp = 0; if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ - append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + append_str(zLhs,0,0,0); cp = xp; lhsused = 1; }else{ for(i=0; inrhs; i++){ if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ - if( cp!=rp->code && cp[-1]=='@' ){ + if( i==0 && dontUseRhs0 ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s used after '%s'.", + rp->rhsalias[0], zOvwrt); + lemp->errorcnt++; + }else if( cp!=rp->code && cp[-1]=='@' ){ /* If the argument is of the form @X then substituted ** the token number of X, not the value of X */ append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); @@ -3523,6 +3595,11 @@ PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ append_str(cp, 1, 0, 0); } /* End loop */ + /* Main code generation completed */ + cp = append_str(0,0,0,0); + if( cp && cp[0] ) rp->code = Strsafe(cp); + append_str(0,0,0,0); + /* Check to make sure the LHS has been used */ if( rp->lhsalias && !lhsused ){ ErrorMsg(lemp->filename,rp->ruleline, @@ -3531,27 +3608,55 @@ PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ lemp->errorcnt++; } - /* Generate destructor code for RHS symbols which are not used in the - ** reduce code */ + /* Generate destructor code for RHS minor values which are not referenced. + ** Generate error messages for unused labels and duplicate labels. + */ for(i=0; inrhs; i++){ - if( rp->rhsalias[i] && !used[i] ){ - ErrorMsg(lemp->filename,rp->ruleline, - "Label %s for \"%s(%s)\" is never used.", - rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); - lemp->errorcnt++; - }else if( rp->rhsalias[i]==0 ){ - if( has_destructor(rp->rhs[i],lemp) ){ - append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, - rp->rhs[i]->index,i-rp->nrhs+1); - }else{ - /* No destructor defined for this term */ + if( rp->rhsalias[i] ){ + if( i>0 ){ + int j; + if( rp->lhsalias && strcmp(rp->lhsalias,rp->rhsalias[i])==0 ){ + ErrorMsg(lemp->filename,rp->ruleline, + "%s(%s) has the same label as the LHS but is not the left-most " + "symbol on the RHS.", + rp->rhs[i]->name, rp->rhsalias); + lemp->errorcnt++; + } + for(j=0; jrhsalias[j] && strcmp(rp->rhsalias[j],rp->rhsalias[i])==0 ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s used for multiple symbols on the RHS of a rule.", + rp->rhsalias[i]); + lemp->errorcnt++; + break; + } + } } + if( !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + } + }else if( i>0 && has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); } } - if( rp->code ){ - cp = append_str(0,0,0,0); - rp->code = Strsafe(cp?cp:""); + + /* If unable to write LHS values directly into the stack, write the + ** saved LHS value now. */ + if( lhsdirect==0 ){ + append_str(" yymsp[%d].minor.yy%d = ", 0, 1-rp->nrhs, rp->lhs->dtnum); + append_str(zLhs, 0, 0, 0); + append_str(";\n", 0, 0, 0); } + + /* Suffix code generation complete */ + cp = append_str(0,0,0,0); + if( cp ) rp->codeSuffix = Strsafe(cp); + + return rc; } /* @@ -3566,6 +3671,12 @@ PRIVATE void emit_code( ){ const char *cp; + /* Setup code prior to the #line directive */ + if( rp->codePrefix && rp->codePrefix[0] ){ + fprintf(out, "{%s", rp->codePrefix); + for(cp=rp->codePrefix; *cp; cp++){ if( *cp=='\n' ) (*lineno)++; } + } + /* Generate code to do the reduce action */ if( rp->code ){ if( !lemp->nolinenosflag ){ @@ -3573,15 +3684,23 @@ PRIVATE void emit_code( tplt_linedir(out,rp->line,lemp->filename); } fprintf(out,"{%s",rp->code); - for(cp=rp->code; *cp; cp++){ - if( *cp=='\n' ) (*lineno)++; - } /* End loop */ + for(cp=rp->code; *cp; cp++){ if( *cp=='\n' ) (*lineno)++; } fprintf(out,"}\n"); (*lineno)++; if( !lemp->nolinenosflag ){ (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } - } /* End if( rp->code ) */ + } + + /* Generate breakdown code that occurs after the #line directive */ + if( rp->codeSuffix && rp->codeSuffix[0] ){ + fprintf(out, "%s", rp->codeSuffix); + for(cp=rp->codeSuffix; *cp; cp++){ if( *cp=='\n' ) (*lineno)++; } + } + + if( rp->codePrefix ){ + fprintf(out, "}\n"); (*lineno)++; + } return; } @@ -4206,8 +4325,12 @@ void ReportTable( tplt_xfer(lemp->name,in,out,&lineno); /* Generate code which execution during each REDUCE action */ + i = 0; for(rp=lemp->rule; rp; rp=rp->next){ - translate_code(lemp, rp); + i += translate_code(lemp, rp); + } + if( i ){ + fprintf(out," YYMINORTYPE yylhsminor;\n"); lineno++; } /* First output rules other than the default: rule */ for(rp=lemp->rule; rp; rp=rp->next){ diff --git a/tool/lempar.c b/tool/lempar.c index 312507e5f1..db7f2dbf1b 100644 --- a/tool/lempar.c +++ b/tool/lempar.c @@ -87,10 +87,6 @@ %% /************* End control #defines *******************************************/ -/* The yyzerominor constant is used to initialize instances of -** YYMINORTYPE objects to zero. */ -static const YYMINORTYPE yyzerominor = { 0 }; - /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** @@ -211,7 +207,9 @@ struct yyParser { #ifdef YYTRACKMAXSTACKDEPTH int yyidxMax; /* Maximum value of yyidx */ #endif +#ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ +#endif ParseARG_SDECL /* A place to hold %extra_argument */ #if YYSTACKDEPTH<=0 int yystksz; /* Current side of the stack */ @@ -516,7 +514,7 @@ static int yy_find_reduce_action( /* ** The following routine is called if the stack overflows. */ -static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){ +static void yyStackOverflow(yyParser *yypParser){ ParseARG_FETCH; yypParser->yyidx--; #ifndef NDEBUG @@ -560,7 +558,7 @@ static void yy_shift( yyParser *yypParser, /* The parser to be shifted */ int yyNewState, /* The new state to shift in */ int yyMajor, /* The major token to shift in */ - YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ + ParseTOKENTYPE yyMinor /* The minor token to shift in */ ){ yyStackEntry *yytos; yypParser->yyidx++; @@ -571,14 +569,14 @@ static void yy_shift( #endif #if YYSTACKDEPTH>0 if( yypParser->yyidx>=YYSTACKDEPTH ){ - yyStackOverflow(yypParser, yypMinor); + yyStackOverflow(yypParser); return; } #else if( yypParser->yyidx>=yypParser->yystksz ){ yyGrowStack(yypParser); if( yypParser->yyidx>=yypParser->yystksz ){ - yyStackOverflow(yypParser, yypMinor); + yyStackOverflow(yypParser); return; } } @@ -586,7 +584,7 @@ static void yy_shift( yytos = &yypParser->yystack[yypParser->yyidx]; yytos->stateno = (YYACTIONTYPE)yyNewState; yytos->major = (YYCODETYPE)yyMajor; - yytos->minor = *yypMinor; + yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState); } @@ -612,7 +610,6 @@ static void yy_reduce( ){ int yygoto; /* The next state */ int yyact; /* The next action */ - YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ yyStackEntry *yymsp; /* The top of the parser's stack */ int yysize; /* Amount to pop the stack */ ParseARG_FETCH; @@ -625,7 +622,31 @@ static void yy_reduce( yyRuleName[yyruleno], yymsp[-yysize].stateno); } #endif /* NDEBUG */ - yygotominor = yyzerominor; + + /* Check that the stack is large enough to grow by a single entry + ** if the RHS of the rule is empty. This ensures that there is room + ** enough on the stack to push the LHS value */ + if( yyRuleInfo[yyruleno].nrhs==0 ){ +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH-1 ){ + yyStackOverflow(yypParser); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz-1 ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz-1 ){ + yyStackOverflow(yypParser); + return; + } + } +#endif + } switch( yyruleno ){ /* Beginning here are the reduction cases. A typical example @@ -643,26 +664,17 @@ static void yy_reduce( assert( yyruleno>=0 && yyrulenoyyidx -= yysize; yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); if( yyact <= YY_MAX_SHIFTREDUCE ){ if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; - /* If the reduce action popped at least - ** one element off the stack, then we can push the new element back - ** onto the stack here, and skip the stack overflow test in yy_shift(). - ** That gives a significant speed improvement. */ - if( yysize ){ - yypParser->yyidx++; - yymsp -= yysize-1; - yymsp->stateno = (YYACTIONTYPE)yyact; - yymsp->major = (YYCODETYPE)yygoto; - yymsp->minor = yygotominor; - yyTraceShift(yypParser, yyact); - }else{ - yy_shift(yypParser,yyact,yygoto,&yygotominor); - } + yypParser->yyidx -= yysize - 1; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yyTraceShift(yypParser, yyact); }else{ assert( yyact == YY_ACCEPT_ACTION ); + yypParser->yyidx -= yysize; yy_accept(yypParser); } } @@ -696,10 +708,10 @@ static void yy_parse_failed( static void yy_syntax_error( yyParser *yypParser, /* The parser */ int yymajor, /* The major type of the error token */ - YYMINORTYPE yyminor /* The minor type of the error token */ + ParseTOKENTYPE yyminor /* The minor type of the error token */ ){ ParseARG_FETCH; -#define TOKEN (yyminor.yy0) +#define TOKEN yyminor /************ Begin %syntax_error code ****************************************/ %% /************ End %syntax_error code ******************************************/ @@ -767,14 +779,14 @@ void Parse( if( yypParser->yyidx<0 ){ #if YYSTACKDEPTH<=0 if( yypParser->yystksz <=0 ){ - /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ - yyminorunion = yyzerominor; - yyStackOverflow(yypParser, &yyminorunion); + yyStackOverflow(yypParser); return; } #endif yypParser->yyidx = 0; +#ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; +#endif yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; #ifndef NDEBUG @@ -784,7 +796,6 @@ void Parse( } #endif } - yyminorunion.yy0 = yyminor; #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) yyendofinput = (yymajor==0); #endif @@ -800,13 +811,16 @@ void Parse( yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); if( yyact <= YY_MAX_SHIFTREDUCE ){ if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; - yy_shift(yypParser,yyact,yymajor,&yyminorunion); + yy_shift(yypParser,yyact,yymajor,yyminor); +#ifndef YYNOERRORRECOVERY yypParser->yyerrcnt--; +#endif yymajor = YYNOCODE; }else if( yyact <= YY_MAX_REDUCE ){ yy_reduce(yypParser,yyact-YY_MIN_REDUCE); }else{ assert( yyact == YY_ERROR_ACTION ); + yyminorunion.yy0 = yyminor; #ifdef YYERRORSYMBOL int yymx; #endif @@ -836,7 +850,7 @@ void Parse( ** */ if( yypParser->yyerrcnt<0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_syntax_error(yypParser,yymajor,yyminor); } yymx = yypParser->yystack[yypParser->yyidx].major; if( yymx==YYERRORSYMBOL || yyerrorhit ){ @@ -846,10 +860,10 @@ void Parse( yyTracePrompt,yyTokenName[yymajor]); } #endif - yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ - while( + while( yypParser->yyidx >= 0 && yymx != YYERRORSYMBOL && (yyact = yy_find_reduce_action( @@ -863,9 +877,7 @@ void Parse( yy_parse_failed(yypParser); yymajor = YYNOCODE; }else if( yymx!=YYERRORSYMBOL ){ - YYMINORTYPE u2; - u2.YYERRSYMDT = 0; - yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); } } yypParser->yyerrcnt = 3; @@ -878,7 +890,7 @@ void Parse( ** Applications can set this macro (for example inside %include) if ** they intend to abandon the parse upon the first syntax error seen. */ - yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_syntax_error(yypParser,yymajor, yyminor); yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yymajor = YYNOCODE; @@ -893,7 +905,7 @@ void Parse( ** three input tokens have been successfully shifted. */ if( yypParser->yyerrcnt<=0 ){ - yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_syntax_error(yypParser,yymajor, yyminor); } yypParser->yyerrcnt = 3; yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh index 6081a7b0ec..4864ee85c3 100644 --- a/tool/mkautoconfamal.sh +++ b/tool/mkautoconfamal.sh @@ -22,16 +22,27 @@ set -u TMPSPACE=./mkpkg_tmp_dir VERSION=`cat $TOP/VERSION` +HASH=`sed 's/^\(..........\).*/\1/' $TOP/manifest.uuid` +DATETIME=`grep '^D' $TOP/manifest | sed -e 's/[^0-9]//g' -e 's/\(............\).*/\1/'` -# Set global variable $ARTIFACT to the "3xxyyzz" string incorporated -# into artifact filenames. And $VERSION2 to the "3.x.y[.z]" form. -xx=`echo $VERSION|sed 's/3\.\([0-9]*\)\..*/\1/'` -yy=`echo $VERSION|sed 's/3\.[^.]*\.\([0-9]*\).*/\1/'` -zz=0 -set +e - zz=`echo $VERSION|sed 's/3\.[^.]*\.[^.]*\.\([0-9]*\).*/\1/'|grep -v '\.'` -set -e -ARTIFACT=`printf "3%.2d%.2d%.2d" $xx $yy $zz` +# If this script is given an argument of --snapshot, then generate a +# snapshot tarball named for the current checkout SHA1 hash, rather than +# the version number. +# +if test "$#" -ge 1 -a x$1 != x--snapshot +then + # Set global variable $ARTIFACT to the "3xxyyzz" string incorporated + # into artifact filenames. And $VERSION2 to the "3.x.y[.z]" form. + xx=`echo $VERSION|sed 's/3\.\([0-9]*\)\..*/\1/'` + yy=`echo $VERSION|sed 's/3\.[^.]*\.\([0-9]*\).*/\1/'` + zz=0 + set +e + zz=`echo $VERSION|sed 's/3\.[^.]*\.[^.]*\.\([0-9]*\).*/\1/'|grep -v '\.'` + set -e + TARBALLNAME=`printf "sqlite-autoconf-3%.2d%.2d%.2d" $xx $yy $zz` +else + TARBALLNAME=sqlite-snapshot-$DATETIME +fi rm -rf $TMPSPACE cp -R $TOP/autoconf $TMPSPACE @@ -42,6 +53,7 @@ cp $TOP/sqlite3.1 $TMPSPACE cp $TOP/sqlite3.pc.in $TMPSPACE cp $TOP/src/shell.c $TMPSPACE cp $TOP/src/sqlite3.rc $TMPSPACE +cp $TOP/tool/Replace.cs $TMPSPACE cat $TMPSPACE/configure.ac | sed "s/--SQLITE-VERSION--/$VERSION/" > $TMPSPACE/tmp @@ -73,6 +85,8 @@ rm -rf autom4te.cache cd ../ ./configure && make dist 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 .. +mv sqlite-$VERSION $TARBALLNAME +tar -czf $TARBALLNAME.tar.gz $TARBALLNAME +mv $TARBALLNAME.tar.gz .. +cd .. +ls -l $TARBALLNAME.tar.gz diff --git a/tool/mkmsvcmin.tcl b/tool/mkmsvcmin.tcl index da0cd6283c..88e7f9184e 100644 --- a/tool/mkmsvcmin.tcl +++ b/tool/mkmsvcmin.tcl @@ -72,6 +72,21 @@ $(LIBRESOBJS): $(TOP)\sqlite3.rc rcver.vc $(SQLITE3H) $(LTRCOMPILE) -fo $(LIBRESOBJS) -DRC_VERONLY $(TOP)\sqlite3.rc }]] +# +# NOTE: This block is used to replace the section marked <> in +# the Makefile, if it exists. +# +set blocks(2) [string trimleft [string map [list \\\\ \\] { +Replace.exe: + $(CSC) /target:exe $(TOP)\Replace.cs + +sqlite3.def: Replace.exe $(LIBOBJ) + echo EXPORTS > sqlite3.def + dumpbin /all $(LIBOBJ) \\ + | .\Replace.exe "^\s+/EXPORT:_?(sqlite3_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \\ + | sort >> sqlite3.def +}]] + set data "#### DO NOT EDIT ####\n" append data "# This makefile is automatically " append data "generated from the [file tail $fromFileName] at\n" @@ -90,8 +105,7 @@ foreach i [lsort -integer [array names blocks]] { } set data [string map [list " -I\$(TOP)\\src" ""] $data] -set data [string map [list " /DEF:sqlite3.def" ""] $data] -set data [string map [list " sqlite3.def" ""] $data] +set data [string map [list " libsqlite3.lib" ""] $data] set data [string map [list " \$(ALL_TCL_TARGETS)" ""] $data] set data [string map [list "\$(TOP)\\src\\" "\$(TOP)\\"] $data] diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 23241e27a6..7bc05d2f75 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -323,7 +323,6 @@ foreach file { vdbe.c vdbeblob.c vdbesort.c - journal.c memjournal.c walker.c diff --git a/tool/mkvsix.tcl b/tool/mkvsix.tcl index b7c5983dbe..b7350f9c84 100644 --- a/tool/mkvsix.tcl +++ b/tool/mkvsix.tcl @@ -389,7 +389,7 @@ set shortNames(WP80,2013) SQLite.WP80.2013 set shortNames(WP81,2013) SQLite.WP81 set shortNames(Win32,2012) SQLite.Win32 set shortNames(Win32,2013) SQLite.Win32.2013 -set shortNames(UAP,2015) SQLite.UAP.2015 +set shortNames(UWP,2015) SQLite.UWP.2015 set displayNames(WinRT,2012) "SQLite for Windows Runtime" set displayNames(WinRT,2013) "SQLite for Windows Runtime" @@ -399,7 +399,7 @@ set displayNames(WP80,2013) "SQLite for Windows Phone" set displayNames(WP81,2013) "SQLite for Windows Phone 8.1" set displayNames(Win32,2012) "SQLite for Windows" set displayNames(Win32,2013) "SQLite for Windows" -set displayNames(UAP,2015) "SQLite for Universal App Platform" +set displayNames(UWP,2015) "SQLite for Universal Windows Platform" if {[string equal $packageFlavor WinRT]} then { set shortName $shortNames($packageFlavor,$vsVersion) @@ -455,7 +455,7 @@ if {[string equal $packageFlavor WinRT]} then { set extraSdkPath "\\..\\$targetPlatformIdentifier" set extraFileListAttributes \ [getExtraFileListXmlChunk $packageFlavor $vsVersion] -} elseif {[string equal $packageFlavor UAP]} then { +} elseif {[string equal $packageFlavor UWP]} then { if {$vsVersion ne "2015"} then { fail [appendArgs \ "unsupported combination, package flavor " $packageFlavor \ @@ -463,7 +463,7 @@ if {[string equal $packageFlavor WinRT]} then { } set shortName $shortNames($packageFlavor,$vsVersion) set displayName $displayNames($packageFlavor,$vsVersion) - set targetPlatformIdentifier UAP + set targetPlatformIdentifier UAP; # NOTE: Not "UWP". set targetPlatformVersion v0.8.0.0 set minVsVersion [getMinVsVersionXmlChunk $vsVersion] set maxPlatformVersion \ @@ -485,7 +485,7 @@ if {[string equal $packageFlavor WinRT]} then { } else { fail [appendArgs \ "unsupported package flavor, must be one of: " \ - [list WinRT WinRT81 WP80 WP81 UAP Win32]] + [list WinRT WinRT81 WP80 WP81 UWP Win32]] } ############################################################################### diff --git a/tool/srcck1.c b/tool/srcck1.c index e95765185f..f92610f544 100644 --- a/tool/srcck1.c +++ b/tool/srcck1.c @@ -111,11 +111,11 @@ static unsigned int findCloseParen(const char *z){ ** Print error messages whenever a side effect is found. Return the number ** of problems seen. */ -static unsigned int findAllSideEffects(const char *z){ +static unsigned int findAllSideEffects(const unsigned char *z){ unsigned int lineno = 1; /* Line number */ unsigned int i; unsigned int nErr = 0; - char c, prevC = 0; + unsigned char c, prevC = 0; for(i=0; (c = z[i])!=0; prevC=c, i++){ if( c=='\n' ){ lineno++; continue; } if( isalpha(c) && !isalpha(prevC) ){ @@ -125,7 +125,7 @@ static unsigned int findAllSideEffects(const char *z){ || strncmp(&z[i],"testcase(",9)==0 ){ unsigned int n; - const char *z2 = &z[i+5]; + unsigned const char *z2 = &z[i+5]; while( z2[0]!='(' ){ z2++; } z2++; n = findCloseParen(z2); @@ -141,7 +141,7 @@ static unsigned int findAllSideEffects(const char *z){ } int main(int argc, char **argv){ - char *z; + unsigned char *z; unsigned int nErr = 0; if( argc!=2 ){ fprintf(stderr, "Usage: %s FILENAME\n", argv[0]); diff --git a/tool/warnings.sh b/tool/warnings.sh index d69cbaf5db..cee639dc45 100644 --- a/tool/warnings.sh +++ b/tool/warnings.sh @@ -5,10 +5,12 @@ # rm -f sqlite3.c make sqlite3.c -echo '********** No optimizations. Includes FTS4 and RTREE *********' +echo '********** No optimizations. Includes FTS4/5, RTREE, JSON1 ***' gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \ -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1 \ sqlite3.c +if test x`uname` = 'xLinux'; then echo '********** Android configuration ******************************' gcc -c \ -DHAVE_USLEEP=1 \ @@ -31,11 +33,13 @@ gcc -c \ -DUSE_PREAD64 \ -Wshadow -Wall -Wextra \ -Os sqlite3.c shell.c +fi echo '********** No optimizations. ENABLE_STAT4. THREADSAFE=0 *******' gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \ -ansi -DSQLITE_ENABLE_STAT4 -DSQLITE_THREADSAFE=0 \ sqlite3.c -echo '********** Optimized -O3. Includes FTS4 and RTREE ************' +echo '********** Optimized -O3. Includes FTS4/5, RTREE, JSON1 ******' gcc -O3 -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \ -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \ + -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1 \ sqlite3.c diff --git a/vsixtest/App.xaml b/vsixtest/App.xaml new file mode 100644 index 0000000000..80889024ca --- /dev/null +++ b/vsixtest/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/vsixtest/App.xaml.cpp b/vsixtest/App.xaml.cpp new file mode 100644 index 0000000000..da8f327fa0 --- /dev/null +++ b/vsixtest/App.xaml.cpp @@ -0,0 +1,120 @@ +// +// App.xaml.cpp +// Implementation of the App class. +// + +#include "pch.h" +#include "MainPage.xaml.h" + +using namespace vsixtest; + +using namespace Platform; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::UI::Xaml::Data; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Interop; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Navigation; + +/// +/// Initializes the singleton application object. This is the first line of authored code +/// executed, and as such is the logical equivalent of main() or WinMain(). +/// +App::App() +{ + InitializeComponent(); + Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending); +} + +/// +/// Invoked when the application is launched normally by the end user. Other entry points +/// will be used such as when the application is launched to open a specific file. +/// +/// Details about the launch request and process. +void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) +{ + +#if _DEBUG + // Show graphics profiling information while debugging. + if (IsDebuggerPresent()) + { + // Display the current frame rate counters + DebugSettings->EnableFrameRateCounter = true; + } +#endif + + auto rootFrame = dynamic_cast(Window::Current->Content); + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == nullptr) + { + // Create a Frame to act as the navigation context and associate it with + // a SuspensionManager key + rootFrame = ref new Frame(); + + rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed); + + if (e->PreviousExecutionState == ApplicationExecutionState::Terminated) + { + // TODO: Restore the saved session state only when appropriate, scheduling the + // final launch steps after the restore is complete + + } + + if (rootFrame->Content == nullptr) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments); + } + // Place the frame in the current Window + Window::Current->Content = rootFrame; + // Ensure the current window is active + Window::Current->Activate(); + } + else + { + if (rootFrame->Content == nullptr) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments); + } + // Ensure the current window is active + Window::Current->Activate(); + } +} + +/// +/// Invoked when application execution is being suspended. Application state is saved +/// without knowing whether the application will be terminated or resumed with the contents +/// of memory still intact. +/// +/// The source of the suspend request. +/// Details about the suspend request. +void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e) +{ + (void) sender; // Unused parameter + (void) e; // Unused parameter + + //TODO: Save application state and stop any background activity +} + +/// +/// Invoked when Navigation to a certain page fails +/// +/// The Frame which failed navigation +/// Details about the navigation failure +void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e) +{ + throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name); +} \ No newline at end of file diff --git a/vsixtest/App.xaml.h b/vsixtest/App.xaml.h new file mode 100644 index 0000000000..5fa8837d38 --- /dev/null +++ b/vsixtest/App.xaml.h @@ -0,0 +1,27 @@ +// +// App.xaml.h +// Declaration of the App class. +// + +#pragma once + +#include "App.g.h" + +namespace vsixtest +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + ref class App sealed + { + protected: + virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override; + + internal: + App(); + + private: + void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e); + void OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e); + }; +} diff --git a/vsixtest/Assets/LockScreenLogo.scale-200.png b/vsixtest/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000..735f57adb5 Binary files /dev/null and b/vsixtest/Assets/LockScreenLogo.scale-200.png differ diff --git a/vsixtest/Assets/SplashScreen.scale-200.png b/vsixtest/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000..023e7f1fed Binary files /dev/null and b/vsixtest/Assets/SplashScreen.scale-200.png differ diff --git a/vsixtest/Assets/Square150x150Logo.scale-200.png b/vsixtest/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000..af49fec1a5 Binary files /dev/null and b/vsixtest/Assets/Square150x150Logo.scale-200.png differ diff --git a/vsixtest/Assets/Square44x44Logo.scale-200.png b/vsixtest/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000..ce342a2ec8 Binary files /dev/null and b/vsixtest/Assets/Square44x44Logo.scale-200.png differ diff --git a/vsixtest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/vsixtest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000000..f6c02ce97e Binary files /dev/null and b/vsixtest/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/vsixtest/Assets/StoreLogo.png b/vsixtest/Assets/StoreLogo.png new file mode 100644 index 0000000000..7385b56c0e Binary files /dev/null and b/vsixtest/Assets/StoreLogo.png differ diff --git a/vsixtest/Assets/Wide310x150Logo.scale-200.png b/vsixtest/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000..288995b397 Binary files /dev/null and b/vsixtest/Assets/Wide310x150Logo.scale-200.png differ diff --git a/vsixtest/MainPage.xaml b/vsixtest/MainPage.xaml new file mode 100644 index 0000000000..7472ad8626 --- /dev/null +++ b/vsixtest/MainPage.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/vsixtest/MainPage.xaml.cpp b/vsixtest/MainPage.xaml.cpp new file mode 100644 index 0000000000..e67dcb83b2 --- /dev/null +++ b/vsixtest/MainPage.xaml.cpp @@ -0,0 +1,53 @@ +// +// MainPage.xaml.cpp +// Implementation of the MainPage class. +// + +#include "pch.h" +#include "MainPage.xaml.h" +#include "sqlite3.h" + +using namespace vsixtest; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::UI::Xaml::Data; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Navigation; + +// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +MainPage::MainPage() +{ + InitializeComponent(); + UseSQLite(); +} + +void MainPage::UseSQLite(void) +{ + int rc = SQLITE_OK; + sqlite3 *pDb = nullptr; + + rc = sqlite3_open_v2("test.db", &pDb, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); + + if (rc != SQLITE_OK) + throw ref new FailureException("Failed to open database."); + + rc = sqlite3_exec(pDb, "VACUUM;", nullptr, nullptr, nullptr); + + if (rc != SQLITE_OK) + throw ref new FailureException("Failed to vacuum database."); + + rc = sqlite3_close(pDb); + + if (rc != SQLITE_OK) + throw ref new FailureException("Failed to close database."); + + pDb = nullptr; +} diff --git a/vsixtest/MainPage.xaml.h b/vsixtest/MainPage.xaml.h new file mode 100644 index 0000000000..ea327a3e4c --- /dev/null +++ b/vsixtest/MainPage.xaml.h @@ -0,0 +1,22 @@ +// +// MainPage.xaml.h +// Declaration of the MainPage class. +// + +#pragma once + +#include "MainPage.g.h" + +namespace vsixtest +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public ref class MainPage sealed + { + public: + MainPage(); + void UseSQLite(void); + + }; +} diff --git a/vsixtest/Package.appxmanifest b/vsixtest/Package.appxmanifest new file mode 100644 index 0000000000..106b3f1e41 --- /dev/null +++ b/vsixtest/Package.appxmanifest @@ -0,0 +1,49 @@ + + + + + + + + + + vsixtest + mistachkin + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vsixtest/pch.cpp b/vsixtest/pch.cpp new file mode 100644 index 0000000000..97b544ec11 --- /dev/null +++ b/vsixtest/pch.cpp @@ -0,0 +1,6 @@ +// +// pch.cpp +// Include the standard header and generate the precompiled header. +// + +#include "pch.h" diff --git a/vsixtest/pch.h b/vsixtest/pch.h new file mode 100644 index 0000000000..b793236d02 --- /dev/null +++ b/vsixtest/pch.h @@ -0,0 +1,11 @@ +// +// pch.h +// Header for standard system include files. +// + +#pragma once + +#include +#include + +#include "App.xaml.h" diff --git a/vsixtest/vsixtest.sln b/vsixtest/vsixtest.sln new file mode 100644 index 0000000000..1ab6e064f9 --- /dev/null +++ b/vsixtest/vsixtest.sln @@ -0,0 +1,39 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsixtest", "vsixtest.vcxproj", "{60BB14A5-0871-4656-BC38-4F0958230F9A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|ARM.ActiveCfg = Debug|ARM + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|ARM.Build.0 = Debug|ARM + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|ARM.Deploy.0 = Debug|ARM + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|x64.ActiveCfg = Debug|x64 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|x64.Build.0 = Debug|x64 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|x64.Deploy.0 = Debug|x64 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|x86.ActiveCfg = Debug|Win32 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|x86.Build.0 = Debug|Win32 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Debug|x86.Deploy.0 = Debug|Win32 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|ARM.ActiveCfg = Release|ARM + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|ARM.Build.0 = Release|ARM + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|ARM.Deploy.0 = Release|ARM + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|x64.ActiveCfg = Release|x64 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|x64.Build.0 = Release|x64 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|x64.Deploy.0 = Release|x64 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|x86.ActiveCfg = Release|Win32 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|x86.Build.0 = Release|Win32 + {60BB14A5-0871-4656-BC38-4F0958230F9A}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vsixtest/vsixtest.tcl b/vsixtest/vsixtest.tcl new file mode 100644 index 0000000000..5dce821dc4 --- /dev/null +++ b/vsixtest/vsixtest.tcl @@ -0,0 +1,373 @@ +#!/usr/bin/tclsh +# +# This script is used to quickly test a VSIX (Visual Studio Extension) file +# with Visual Studio 2015 on Windows. +# +# PREREQUISITES +# +# 1. This tool is Windows only. +# +# 2. This tool must be executed with "elevated administrator" privileges. +# +# 3. Tcl 8.4 and later are supported, earlier versions have not been tested. +# +# 4. The "sqlite-UWP-output.vsix" file is assumed to exist in the parent +# directory of the directory containing this script. The [optional] first +# command line argument to this script may be used to specify an alternate +# file. However, currently, the file must be compatible with both Visual +# Studio 2015 and the Universal Windows Platform. +# +# 5. The "VERSION" file is assumed to exist in the parent directory of the +# directory containing this script. It must contain a version number that +# matches the VSIX file being tested. +# +# 6. The temporary directory specified in the TEMP or TMP environment variables +# must refer to an existing directory writable by the current user. +# +# 7. The VS140COMNTOOLS environment variable must refer to the Visual Studio +# 2015 common tools directory. +# +# USAGE +# +# The first argument to this script is optional. If specified, it must be the +# name of the VSIX file to test. +# +package require Tcl 8.4 + +proc fail { {error ""} {usage false} } { + if {[string length $error] > 0} then { + puts stdout $error + if {!$usage} then {exit 1} + } + + puts stdout "usage:\ +[file tail [info nameofexecutable]]\ +[file tail [info script]] \[vsixFile\]" + + exit 1 +} + +proc isWindows {} { + # + # NOTE: Returns non-zero only when running on Windows. + # + return [expr {[info exists ::tcl_platform(platform)] && \ + $::tcl_platform(platform) eq "windows"}] +} + +proc isAdministrator {} { + # + # NOTE: Returns non-zero only when running as "elevated administrator". + # + if {[isWindows]} then { + if {[catch {exec -- whoami /groups} groups] == 0} then { + set groups [string map [list \r\n \n] $groups] + + foreach group [split $groups \n] { + # + # NOTE: Match this group line against the "well-known" SID for + # the "Administrators" group on Windows. + # + if {[regexp -- {\sS-1-5-32-544\s} $group]} then { + # + # NOTE: Match this group line against the attributes column + # sub-value that should be present when running with + # elevated administrator credentials. + # + if {[regexp -- {\sEnabled group(?:,|\s)} $group]} then { + return true + } + } + } + } + } + + return false +} + +proc getEnvironmentVariable { name } { + # + # NOTE: Returns the value of the specified environment variable or an empty + # string for environment variables that do not exist in the current + # process environment. + # + return [expr {[info exists ::env($name)] ? $::env($name) : ""}] +} + +proc getTemporaryPath {} { + # + # NOTE: Returns the normalized path to the first temporary directory found + # in the typical set of environment variables used for that purpose + # or an empty string to signal a failure to locate such a directory. + # + set names [list] + + foreach name [list TEMP TMP] { + lappend names [string toupper $name] [string tolower $name] \ + [string totitle $name] + } + + foreach name $names { + set value [getEnvironmentVariable $name] + + if {[string length $value] > 0} then { + return [file normalize $value] + } + } + + return "" +} + +proc appendArgs { args } { + # + # NOTE: Returns all passed arguments joined together as a single string + # with no intervening spaces between arguments. + # + eval append result $args +} + +proc readFile { fileName } { + # + # NOTE: Reads and returns the entire contents of the specified file, which + # may contain binary data. + # + set file_id [open $fileName RDONLY] + fconfigure $file_id -encoding binary -translation binary + set result [read $file_id] + close $file_id + return $result +} + +proc writeFile { fileName data } { + # + # NOTE: Writes the entire contents of the specified file, which may contain + # binary data. + # + set file_id [open $fileName {WRONLY CREAT TRUNC}] + fconfigure $file_id -encoding binary -translation binary + puts -nonewline $file_id $data + close $file_id + return "" +} + +proc putsAndEval { command } { + # + # NOTE: Outputs a command to the standard output channel and then evaluates + # it in the callers context. + # + catch { + puts stdout [appendArgs "Running: " [lrange $command 1 end] ...\n] + } + + return [uplevel 1 $command] +} + +proc isBadDirectory { directory } { + # + # NOTE: Returns non-zero if the directory is empty, does not exist, -OR- is + # not a directory. + # + catch { + puts stdout [appendArgs "Checking directory \"" $directory \"...\n] + } + + return [expr {[string length $directory] == 0 || \ + ![file exists $directory] || ![file isdirectory $directory]}] +} + +proc isBadFile { fileName } { + # + # NOTE: Returns non-zero if the file name is empty, does not exist, -OR- is + # not a regular file. + # + catch { + puts stdout [appendArgs "Checking file \"" $fileName \"...\n] + } + + return [expr {[string length $fileName] == 0 || \ + ![file exists $fileName] || ![file isfile $fileName]}] +} + +# +# NOTE: This is the entry point for this script. +# +set script [file normalize [info script]] + +if {[string length $script] == 0} then { + fail "script file currently being evaluated is unknown" true +} + +if {![isWindows]} then { + fail "this tool only works properly on Windows" +} + +if {![isAdministrator]} then { + fail "this tool must run with \"elevated administrator\" privileges" +} + +set path [file normalize [file dirname $script]] +set argc [llength $argv]; if {$argc > 1} then {fail "" true} + +if {$argc == 1} then { + set vsixFileName [lindex $argv 0] +} else { + set vsixFileName [file join \ + [file dirname $path] sqlite-UWP-output.vsix] +} + +############################################################################### + +if {[isBadFile $vsixFileName]} then { + fail [appendArgs \ + "VSIX file \"" $vsixFileName "\" does not exist"] +} + +set versionFileName [file join [file dirname $path] VERSION] + +if {[isBadFile $versionFileName]} then { + fail [appendArgs \ + "Version file \"" $versionFileName "\" does not exist"] +} + +set projectTemplateFileName [file join $path vsixtest.vcxproj.data] + +if {[isBadFile $projectTemplateFileName]} then { + fail [appendArgs \ + "Project template file \"" $projectTemplateFileName \ + "\" does not exist"] +} + +set envVarName VS140COMNTOOLS +set vsDirectory [getEnvironmentVariable $envVarName] + +if {[isBadDirectory $vsDirectory]} then { + fail [appendArgs \ + "Visual Studio 2015 directory \"" $vsDirectory \ + "\" from environment variable \"" $envVarName \ + "\" does not exist"] +} + +set vsixInstaller [file join \ + [file dirname $vsDirectory] IDE VSIXInstaller.exe] + +if {[isBadFile $vsixInstaller]} then { + fail [appendArgs \ + "Visual Studio 2015 VSIX installer \"" $vsixInstaller \ + "\" does not exist"] +} + +set envVarName ProgramFiles +set programFiles [getEnvironmentVariable $envVarName] + +if {[isBadDirectory $programFiles]} then { + fail [appendArgs \ + "Program Files directory \"" $programFiles \ + "\" from environment variable \"" $envVarName \ + "\" does not exist"] +} + +set msBuild [file join $programFiles MSBuild 14.0 Bin MSBuild.exe] + +if {[isBadFile $msBuild]} then { + fail [appendArgs \ + "MSBuild v14.0 executable file \"" $msBuild \ + "\" does not exist"] +} + +set temporaryDirectory [getTemporaryPath] + +if {[isBadDirectory $temporaryDirectory]} then { + fail [appendArgs \ + "Temporary directory \"" $temporaryDirectory \ + "\" does not exist"] +} + +############################################################################### + +set installLogFileName [appendArgs \ + [file rootname [file tail $vsixFileName]] \ + -install- [pid] .log] + +set commands(1) [list exec [file nativename $vsixInstaller]] + +lappend commands(1) /quiet /norepair +lappend commands(1) [appendArgs /logFile: $installLogFileName] +lappend commands(1) [file nativename $vsixFileName] + +############################################################################### + +set buildLogFileName [appendArgs \ + [file rootname [file tail $vsixFileName]] \ + -build-%configuration%-%platform%- [pid] .log] + +set commands(2) [list exec [file nativename $msBuild]] + +lappend commands(2) [file nativename [file join $path vsixtest.sln]] +lappend commands(2) /target:Rebuild +lappend commands(2) /property:Configuration=%configuration% +lappend commands(2) /property:Platform=%platform% + +lappend commands(2) [appendArgs \ + /logger:FileLogger,Microsoft.Build.Engine\;Logfile= \ + [file nativename [file join $temporaryDirectory \ + $buildLogFileName]] \;Verbosity=diagnostic] + +############################################################################### + +set uninstallLogFileName [appendArgs \ + [file rootname [file tail $vsixFileName]] \ + -uninstall- [pid] .log] + +set commands(3) [list exec [file nativename $vsixInstaller]] + +lappend commands(3) /quiet /norepair +lappend commands(3) [appendArgs /logFile: $uninstallLogFileName] +lappend commands(3) [appendArgs /uninstall:SQLite.UWP.2015] + +############################################################################### + +if {1} then { + catch { + puts stdout [appendArgs \ + "Install log: \"" [file nativename [file join \ + $temporaryDirectory $installLogFileName]] \"\n] + } + + catch { + puts stdout [appendArgs \ + "Build logs: \"" [file nativename [file join \ + $temporaryDirectory $buildLogFileName]] \"\n] + } + + catch { + puts stdout [appendArgs \ + "Uninstall log: \"" [file nativename [file join \ + $temporaryDirectory $uninstallLogFileName]] \"\n] + } +} + +############################################################################### + +if {1} then { + putsAndEval $commands(1) + + set versionNumber [string trim [readFile $versionFileName]] + set data [readFile $projectTemplateFileName] + set data [string map [list %versionNumber% $versionNumber] $data] + + set projectFileName [file join $path vsixtest.vcxproj] + writeFile $projectFileName $data + + set platforms [list x86 x64 ARM] + set configurations [list Debug Release] + + foreach platform $platforms { + foreach configuration $configurations { + putsAndEval [string map [list \ + %platform% $platform %configuration% $configuration] \ + $commands(2)] + } + } + + putsAndEval $commands(3) +} diff --git a/vsixtest/vsixtest.vcxproj.data b/vsixtest/vsixtest.vcxproj.data new file mode 100644 index 0000000000..a64584c3a2 --- /dev/null +++ b/vsixtest/vsixtest.vcxproj.data @@ -0,0 +1,198 @@ + + + + {60bb14a5-0871-4656-bc38-4f0958230f9a} + vsixtest + en-US + 14.0 + true + Windows Store + 10.0.10586.0 + 10.0.10586.0 + 10.0 + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + Application + true + v140 + + + Application + true + v140 + + + Application + true + v140 + + + Application + false + true + v140 + true + + + Application + false + true + v140 + true + + + Application + false + true + v140 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vsixtest_TemporaryKey.pfx + + + + /bigobj %(AdditionalOptions) + 4453;28204 + + + + + /bigobj %(AdditionalOptions) + 4453;28204 + + + + + /bigobj %(AdditionalOptions) + 4453;28204 + + + + + /bigobj %(AdditionalOptions) + 4453;28204 + + + + + /bigobj %(AdditionalOptions) + 4453;28204 + + + + + /bigobj %(AdditionalOptions) + 4453;28204 + + + + + + App.xaml + + + MainPage.xaml + + + + + Designer + + + Designer + + + + + Designer + + + + + + + + + + + + + + + App.xaml + + + MainPage.xaml + + + Create + Create + Create + Create + Create + Create + + + + + + + + + \ No newline at end of file diff --git a/vsixtest/vsixtest.vcxproj.filters b/vsixtest/vsixtest.vcxproj.filters new file mode 100644 index 0000000000..ac1dfca421 --- /dev/null +++ b/vsixtest/vsixtest.vcxproj.filters @@ -0,0 +1,57 @@ + + + + + 60bb14a5-0871-4656-bc38-4f0958230f9a + + + e6271362-8f96-476d-907f-4da227b02435 + bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png + + + + + + + + + + + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + + + + + + + + + + \ No newline at end of file diff --git a/vsixtest/vsixtest_TemporaryKey.pfx b/vsixtest/vsixtest_TemporaryKey.pfx new file mode 100644 index 0000000000..e6787bcad0 Binary files /dev/null and b/vsixtest/vsixtest_TemporaryKey.pfx differ