diff --git a/Makefile.arm-wince-mingw32ce-gcc b/Makefile.arm-wince-mingw32ce-gcc deleted file mode 100644 index 323b7ea482..0000000000 --- a/Makefile.arm-wince-mingw32ce-gcc +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/make -# -# Makefile for SQLITE -# -# This is a template makefile for SQLite. Most people prefer to -# use the autoconf generated "configure" script to generate the -# makefile automatically. But that does not work for everybody -# and in every situation. If you are having problems with the -# "configure" script, you might want to try this makefile as an -# alternative. Create a copy of this file, edit the parameters -# below and type "make". -# - -#### The directory where to find the mingw32ce tools -MINGW32CE = /opt/mingw32ce/bin - -#### The target prefix of the mingw32ce tools -TARGET = arm-wince-mingw32ce - -#### The toplevel directory of the source tree. This is the directory -# that contains this "Makefile.in" and the "configure.in" script. -# -TOP = ../sqlite - -#### C Compiler and options for use in building executables that -# will run on the platform that is doing the build. -# -BCC = gcc -g -O2 -#BCC = /opt/ancic/bin/c89 -0 - -#### If the target operating system supports the "usleep()" system -# call, then define the HAVE_USLEEP macro for all C modules. -# -USLEEP = -#USLEEP = -DHAVE_USLEEP=1 - -#### If you want the SQLite library to be safe for use within a -# multi-threaded program, then define the following macro -# appropriately: -# -THREADSAFE = -DTHREADSAFE=1 -#THREADSAFE = -DTHREADSAFE=0 - -#### Specify any extra linker options needed to make the library -# thread safe -# -#THREADLIB = -lpthread -THREADLIB = - -#### Specify any extra libraries needed to access required functions. -# -#TLIBS = -lrt # fdatasync on Solaris 8 -TLIBS = - -#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 -# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all -# malloc()s and free()s in order to track down memory leaks. -# -# SQLite uses some expensive assert() statements in the inner loop. -# You can make the library go almost twice as fast if you compile -# with -DNDEBUG=1 -# -#OPTS = -DSQLITE_DEBUG=2 -#OPTS = -DSQLITE_DEBUG=1 -#OPTS = -OPTS = -DNDEBUG=1 -DSQLITE_OS_WIN=1 -D_WIN32_WCE=1 -#OPTS += -DHAVE_FDATASYNC=1 - -#### The suffix to add to executable files. ".exe" for windows. -# Nothing for unix. -# -EXE = .exe -#EXE = - -#### C Compile and options for use in building executables that -# will run on the target platform. This is usually the same -# as BCC, unless you are cross-compiling. -# -#TCC = gcc -O6 -#TCC = gcc -g -O0 -Wall -#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage -#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6 -TCC = $(MINGW32CE)/$(TARGET)-gcc -O2 -#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive - -#### Tools used to build a static library. -# -#AR = ar cr -#AR = /opt/mingw/bin/i386-mingw32-ar cr -AR = $(MINGW32CE)/$(TARGET)-ar cr -#RANLIB = ranlib -#RANLIB = /opt/mingw/bin/i386-mingw32-ranlib -RANLIB = $(MINGW32CE)/$(TARGET)-ranlib - -#MKSHLIB = gcc -shared -#SO = so -#SHPREFIX = lib -MKSHLIB = $(MINGW32CE)/$(TARGET)-gcc -shared -SO = dll -SHPREFIX = - -#### Extra compiler options needed for programs that use the TCL library. -# -#TCL_FLAGS = -#TCL_FLAGS = -DSTATIC_BUILD=1 -TCL_FLAGS = -I/home/drh/tcltk/8.5linux -#TCL_FLAGS = -I/home/drh/tcltk/8.5win -DSTATIC_BUILD=1 -#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux - -#### Linker options needed to link against the TCL library. -# -#LIBTCL = -ltcl -lm -ldl -LIBTCL = /home/drh/tcltk/8.5linux/libtcl8.5g.a -lm -ldl -#LIBTCL = /home/drh/tcltk/8.5win/libtcl85s.a -lmsvcrt -#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc - -#### Additional objects for SQLite library when TCL support is enabled. -TCLOBJ = -#TCLOBJ = tclsqlite.o - -#### Compiler options needed for programs that use the readline() library. -# -READLINE_FLAGS = -#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline - -#### Linker options needed by programs using readline() must link against. -# -LIBREADLINE = -#LIBREADLINE = -static -lreadline -ltermcap - -#### Which "awk" program provides nawk compatibilty -# -# NAWK = nawk -NAWK = awk - -# You should not have to change anything below this line -############################################################################### -include $(TOP)/main.mk diff --git a/Makefile.in b/Makefile.in index 424d839750..c6b090ba43 100644 --- a/Makefile.in +++ b/Makefile.in @@ -65,7 +65,7 @@ TCC += -DSQLITE_THREADSAFE=@SQLITE_THREADSAFE@ # Any target libraries which libsqlite must be linked against # -TLIBS = @LIBS@ +TLIBS = @LIBS@ $(LIBS) # Flags controlling use of the in memory btree implementation # @@ -155,9 +155,6 @@ LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(TCC) $(LTCOMPILE_EXTRAS) LTLINK = $(LIBTOOL) --mode=link $(TCC) $(LTCOMPILE_EXTRAS) @LDFLAGS@ $(LTLINK_EXTRAS) LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL) -# nawk compatible awk. -NAWK = @AWK@ - # You should not have to change anything below this line ############################################################################### @@ -175,7 +172,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 legacy.lo loadext.lo \ + icu.lo insert.lo journal.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 \ @@ -272,8 +269,8 @@ SRC = \ $(TOP)/src/sqliteInt.h \ $(TOP)/src/sqliteLimit.h \ $(TOP)/src/table.c \ - $(TOP)/src/threads.c \ $(TOP)/src/tclsqlite.c \ + $(TOP)/src/threads.c \ $(TOP)/src/tokenize.c \ $(TOP)/src/treeview.c \ $(TOP)/src/trigger.c \ @@ -347,6 +344,9 @@ SRC += \ SRC += \ $(TOP)/ext/rbu/sqlite3rbu.h \ $(TOP)/ext/rbu/sqlite3rbu.c +SRC += \ + $(TOP)/ext/misc/json1.c + # Generated source code files @@ -417,7 +417,6 @@ TESTSRC += \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/misc/ieee754.c \ - $(TOP)/ext/misc/json1.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ @@ -541,16 +540,16 @@ FUZZDATA = \ $(TOP)/test/fuzzdata3.db \ $(TOP)/test/fuzzdata4.db -# Extra arguments for including json1 in the build of tools -# -JSON1_DEP = $(TOP)/ext/misc/json1.c sqlite3ext.h -JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE -JSON1_SRC = $(TOP)/ext/misc/json1.c - # Standard options to testfixture # 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 +FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 +FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 + # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # @@ -573,20 +572,20 @@ libtclsqlite3.la: tclsqlite.lo libsqlite3.la -version-info "8:6:8" \ -avoid-version -sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h $(JSON1_DEP) - $(LTLINK) $(READLINE_FLAGS) $(JSON1_OPT) -o $@ \ - $(TOP)/src/shell.c $(JSON1_SRC) libsqlite3.la \ +sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h + $(LTLINK) $(READLINE_FLAGS) $(SHELL_OPT) -o $@ \ + $(TOP)/src/shell.c libsqlite3.la \ $(LIBREADLINE) $(TLIBS) -rpath "$(libdir)" sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) -fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP) - $(LTLINK) -o $@ $(JSON1_OPT) \ - $(TOP)/tool/fuzzershell.c $(JSON1_SRC) sqlite3.c $(TLIBS) +fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h + $(LTLINK) -o $@ $(FUZZERSHELL_OPT) \ + $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) -fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP) - $(LTLINK) -o $@ $(JSON1_OPT) $(TOP)/test/fuzzcheck.c $(JSON1_SRC) sqlite3.c $(TLIBS) +fuzzcheck$(TEXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h + $(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) mptester$(TEXE): sqlite3.c $(TOP)/mptest/mptest.c $(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ @@ -612,19 +611,23 @@ mptest: mptester$(TEXE) # files are automatically generated. This target takes care of # all that automatic generation. # -.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl +.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c rm -rf tsrc mkdir tsrc cp -f $(SRC) tsrc rm tsrc/sqlite.h.in tsrc/parse.y $(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl $(OPTS) vdbe.new mv vdbe.new tsrc/vdbe.c + cp fts5.c fts5.h tsrc touch .target_source sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl cp tsrc/shell.c tsrc/sqlite3ext.h . +sqlite3ext.h: .target_source + cp tsrc/sqlite3ext.h . + tclsqlite3.c: sqlite3.c echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c cat sqlite3.c >>tclsqlite3.c @@ -896,22 +899,22 @@ tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la # Rules to build opcodes.c and opcodes.h # -opcodes.c: opcodes.h $(TOP)/mkopcodec.awk - $(NAWK) -f $(TOP)/mkopcodec.awk opcodes.h >opcodes.c +opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl + $(TCLSH_CMD) $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c -opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk - cat parse.h $(TOP)/src/vdbe.c | $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h +opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl + cat parse.h $(TOP)/src/vdbe.c | $(TCLSH_CMD) $(TOP)/tool/mkopcodeh.tcl >opcodes.h # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c -parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/addopcodes.awk +parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/tool/addopcodes.tcl cp $(TOP)/src/parse.y . rm -f parse.h ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y mv parse.h parse.h.temp - $(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h + $(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h @@ -987,6 +990,9 @@ fts3_write.lo: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR) rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c +json1.lo: $(TOP)/ext/misc/json1.c + $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c + # FTS5 things # FTS5_SRC = \ @@ -1093,7 +1099,7 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl cat sqlite3.c $(TOP)/src/tclsqlite.c >> $@ echo "static const char *tclsh_main_loop(void){" >> $@ echo "static const char *zMainloop = " >> $@ - $(NAWK) -f $(TOP)/tool/tostr.awk $(TOP)/tool/spaceanal.tcl >> $@ + $(TCLSH_CMD) $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@ echo "; return zMainloop; }" >> $@ sqlite3_analyzer$(TEXE): sqlite3_analyzer.c diff --git a/Makefile.linux-gcc b/Makefile.linux-gcc index 1c9f24f4ba..1491a4b02a 100644 --- a/Makefile.linux-gcc +++ b/Makefile.linux-gcc @@ -118,11 +118,6 @@ READLINE_FLAGS = LIBREADLINE = #LIBREADLINE = -static -lreadline -ltermcap -#### Which "awk" program provides nawk compatibilty -# -# NAWK = nawk -NAWK = awk - # You should not have to change anything below this line ############################################################################### include $(TOP)/main.mk diff --git a/Makefile.msc b/Makefile.msc index 7e69589705..8bbd6cf126 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -390,9 +390,9 @@ CORE_LINK_OPTS = /DEF:sqlite3.def # !IFNDEF SHELL_COMPILE_OPTS !IF $(DYNAMIC_SHELL)!=0 -SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport) +SHELL_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport) !ELSE -SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 $(SHELL_CCONV_OPTS) +SHELL_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 $(SHELL_CCONV_OPTS) !ENDIF !ENDIF @@ -569,6 +569,10 @@ TCLLIBDIR = c:\tcl\lib LIBTCL = tcl85.lib !ENDIF +!IFNDEF LIBTCLSTUB +LIBTCLSTUB = tclstub85.lib +!ENDIF + # The locations of the ICU header and library files. These variables # (ICUINCDIR, ICULIBDIR, and LIBICU) may be overridden via the environment # prior to running nmake in order to match the actual installed location on @@ -809,12 +813,6 @@ LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ICULIBDIR) LTLIBS = $(LTLIBS) $(LIBICU) !ENDIF -# nawk compatible awk. -# -!IFNDEF NAWK -NAWK = gawk.exe -!ENDIF - # You should not have to change anything below this line ############################################################################### @@ -1008,7 +1006,8 @@ SRC4 = \ $(TOP)\ext\rtree\rtree.h \ $(TOP)\ext\rtree\rtree.c \ $(TOP)\ext\rbu\sqlite3rbu.h \ - $(TOP)\ext\rbu\sqlite3rbu.c + $(TOP)\ext\rbu\sqlite3rbu.c \ + $(TOP)\ext\misc\json1.c # Generated source code files @@ -1210,11 +1209,11 @@ FUZZDATA = \ $(TOP)\test\fuzzdata3.db \ $(TOP)\test\fuzzdata4.db -# Extra arguments for including json1 in the build of tools +# Extra compiler options for various shell tools # -JSON1_DEP = sqlite3ext.h $(TOP)\ext\misc\json1.c -JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE -JSON1_SRC = $(TOP)\ext\misc\json1.c +SHELL_COMPILE_OPTS = -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 +FUZZERSHELL_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 +FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 # Standard options to testfixture # @@ -1229,25 +1228,25 @@ libsqlite3.lib: $(LIBOBJ) $(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS) libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib - $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS) + $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS) -sqlite3.exe: $(TOP)\src\shell.c $(JSON1_DEP) $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h - $(LTLINK) $(SHELL_COMPILE_OPTS) $(JSON1_OPT) $(READLINE_FLAGS) $(TOP)\src\shell.c $(JSON1_SRC) \ - /link /pdb:sqlite3sh.pdb $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) +sqlite3.exe: $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h + $(LTLINK) $(SHELL_COMPILE_OPTS) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c \ + /link /pdb:sqlite3sh.pdb $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) sqldiff.exe: $(TOP)\tool\sqldiff.c sqlite3.c sqlite3.h - $(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c sqlite3.c + $(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c sqlite3.c /link $(LDFLAGS) $(LTLINKOPTS) -fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP) - $(LTLINK) $(NO_WARN) $(JSON1_OPT) \ - $(TOP)\tool\fuzzershell.c $(JSON1_SRC) sqlite3.c +fuzzershell.exe: $(TOP)\tool\fuzzershell.c sqlite3.c sqlite3.h + $(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) \ + $(TOP)\tool\fuzzershell.c sqlite3.c /link $(LDFLAGS) $(LTLINKOPTS) -fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP) - $(LTLINK) $(NO_WARN) $(JSON1_OPT) $(TOP)\test\fuzzcheck.c $(JSON1_SRC) sqlite3.c +fuzzcheck.exe: $(TOP)\test\fuzzcheck.c sqlite3.c sqlite3.h + $(LTLINK) $(NO_WARN) $(FUZZCHECK_COMPILE_OPTS) $(TOP)\test\fuzzcheck.c sqlite3.c /link $(LDFLAGS) $(LTLINKOPTS) mptester.exe: $(TOP)\mptest\mptest.c $(SHELL_CORE_DEP) $(LIBRESOBJS) sqlite3.h $(LTLINK) $(NO_WARN) $(SHELL_COMPILE_OPTS) $(TOP)\mptest\mptest.c \ - /link $(LTLINKOPTS) $(LTLIBPATHS) $(SHELL_LINK_OPTS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) + /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(SHELL_LINK_OPTS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) MPTEST1 = mptester mptest.db $(TOP)\mptest\crash01.test --repeat 20 MPTEST2 = mptester mptest.db $(TOP)\mptest\multiwrite01.test --repeat 20 @@ -1269,7 +1268,7 @@ mptest: mptester.exe # files are automatically generated. This target takes care of # all that automatic generation. # -.target_source: $(SRC) $(TOP)\tool\vdbe-compress.tcl +.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 @@ -1277,6 +1276,8 @@ mptest: mptester.exe 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 + copy /Y fts5.c tsrc + copy /Y fts5.h tsrc del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL $(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new move vdbe.new tsrc\vdbe.c @@ -1331,7 +1332,7 @@ $(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(HDR) echo #ifndef SQLITE_RESOURCE_VERSION > sqlite3rc.h for /F %%V in ('type "$(TOP)\VERSION"') do ( \ echo #define SQLITE_RESOURCE_VERSION %%V \ - | $(NAWK) "/.*/ { gsub(/[.]/,\",\");print }" >> sqlite3rc.h \ + | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact . ^, >> sqlite3rc.h \ ) echo #endif >> sqlite3rc.h $(LTRCOMPILE) -fo $(LIBRESOBJS) $(TOP)\src\sqlite3.rc @@ -1568,26 +1569,26 @@ tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(LTCOMPILE) $(NO_WARN) -DTCLSH=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(LIBRESOBJS) - $(LTLINK) $(SQLITE3C) /link $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(LTLIBS) $(TLIBS) + $(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(LTLIBS) $(TLIBS) # Rules to build opcodes.c and opcodes.h # -opcodes.c: opcodes.h $(TOP)\mkopcodec.awk - $(NAWK) -f $(TOP)\mkopcodec.awk opcodes.h > opcodes.c +opcodes.c: opcodes.h $(TOP)\tool\mkopcodec.tcl + $(TCLSH_CMD) $(TOP)\tool\mkopcodec.tcl opcodes.h > opcodes.c -opcodes.h: parse.h $(TOP)\src\vdbe.c $(TOP)\mkopcodeh.awk - type parse.h $(TOP)\src\vdbe.c | $(NAWK) -f $(TOP)\mkopcodeh.awk > opcodes.h +opcodes.h: parse.h $(TOP)\src\vdbe.c $(TOP)\tool\mkopcodeh.tcl + type parse.h $(TOP)\src\vdbe.c | $(TCLSH_CMD) $(TOP)\tool\mkopcodeh.tcl > opcodes.h # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c -parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk +parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl del /Q parse.y parse.h parse.h.temp 2>NUL copy $(TOP)\src\parse.y . .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y move parse.h parse.h.temp - $(NAWK) -f $(TOP)\addopcodes.awk parse.h.temp > parse.h + $(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h @@ -1731,7 +1732,7 @@ testfixture.exe: $(TESTFIXTURE_SRC) $(LIBRESOBJS) $(HDR) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \ -DBUILD_sqlite -I$(TCLINCDIR) \ $(TESTFIXTURE_SRC) \ - /link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) + /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) extensiontest: testfixture.exe testloadext.dll .\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS) @@ -1774,12 +1775,12 @@ sqlite3_analyzer.c: $(SQLITE3C) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl copy $@ + $(SQLITE3C) + $(TOP)\src\tclsqlite.c $@ echo static const char *tclsh_main_loop(void){ >> $@ echo static const char *zMainloop = >> $@ - $(NAWK) -f $(TOP)\tool\tostr.awk $(TOP)\tool\spaceanal.tcl >> $@ + $(TCLSH_CMD) $(TOP)\tool\tostr.tcl $(TOP)\tool\spaceanal.tcl >> $@ echo ; return zMainloop; } >> $@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS) $(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \ - /link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) + /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) testloadext.lo: $(TOP)\src\test_loadext.c $(LTCOMPILE) $(NO_WARN) -c $(TOP)\src\test_loadext.c @@ -1789,38 +1790,38 @@ testloadext.dll: testloadext.lo showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\tool\showdb.c $(SQLITE3C) + $(TOP)\tool\showdb.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) showstat4.exe: $(TOP)\tool\showstat4.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\tool\showstat4.c $(SQLITE3C) + $(TOP)\tool\showstat4.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) showjournal.exe: $(TOP)\tool\showjournal.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\tool\showjournal.c $(SQLITE3C) + $(TOP)\tool\showjournal.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\tool\showwal.c $(SQLITE3C) + $(TOP)\tool\showwal.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) fts3view.exe: $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) + $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) rollback-test.exe: $(TOP)\tool\rollback-test.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\tool\rollback-test.c $(SQLITE3C) + $(TOP)\tool\rollback-test.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) LogEst.exe: $(TOP)\tool\logest.c sqlite3.h - $(LTLINK) $(NO_WARN) -Fe$@ $(TOP)\tool\LogEst.c + $(LTLINK) $(NO_WARN) -Fe$@ $(TOP)\tool\LogEst.c /link $(LDFLAGS) $(LTLINKOPTS) wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\test\wordcount.c $(SQLITE3C) + $(TOP)\test\wordcount.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(LTLINK) $(NO_WARN) -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \ - $(TOP)\test\speedtest1.c $(SQLITE3C) + $(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL @@ -1858,7 +1859,7 @@ dll: sqlite3.dll sqlite3.def: libsqlite3.lib echo EXPORTS > sqlite3.def dumpbin /all libsqlite3.lib \ - | $(NAWK) "/ 1 _?sqlite3_/ { sub(/^.* _?/,\"\");print }" \ + | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3_.*)$$" \1 \ | sort >> sqlite3.def sqlite3.dll: $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) diff --git a/Makefile.vxworks b/Makefile.vxworks deleted file mode 100644 index 706261fc00..0000000000 --- a/Makefile.vxworks +++ /dev/null @@ -1,673 +0,0 @@ -#!/usr/make -# -# Makefile for SQLITE on VxWorks - -ifeq ($(FORCPU),) - FORCPU = SH32gnule -endif - -TOOL_FAMILY = gnu - -include $(WIND_USR)/tool/gnu/make.$(FORCPU) - -#### The toplevel directory of the source tree. This is the directory -# that contains this "Makefile.in" and the "configure.in" script. -# -TOP = . - -#### C Compiler and options for use in building executables that -# will run on the platform that is doing the build. -# -BCC = gcc -g -O2 -#BCC = /opt/ancic/bin/c89 -0 - -#### If the target operating system supports the "usleep()" system -# call, then define the HAVE_USLEEP macro for all C modules. -# -USLEEP = -#USLEEP = -DHAVE_USLEEP=1 - -#### If you want the SQLite library to be safe for use within a -# multi-threaded program, then define the following macro -# appropriately: -# -THREADSAFE = -DSQLITE_THREADSAFE=1 -#THREADSAFE = -DSQLITE_THREADSAFE=0 - -#### Specify any extra linker options needed to make the library -# thread safe -# -#THREADLIB = -lpthread -THREADLIB = - -#### Specify any extra libraries needed to access required functions. -# -ifeq ($(CPU),SH32) - # for SH4 shared library - TLIBS_SHARED += -L$(WIND_USR)/lib/sh/SH32/commonle/PIC -else - # for all other CPUs shared library - TLIBS_SHARED += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS) -endif -# for static library -TLIBS += $(LD_LINK_PATH_ATEND) $(LD_PARTIAL_LAST_FLAGS) - -#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 -# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all -# malloc()s and free()s in order to track down memory leaks. -# -# SQLite uses some expensive assert() statements in the inner loop. -# You can make the library go almost twice as fast if you compile -# with -DNDEBUG=1 -# -#OPTS = -DSQLITE_DEBUG=2 -#OPTS = -DSQLITE_DEBUG=1 -#OPTS = -OPTS = -DNDEBUG=1 -DSQLITE_OS_UNIX=1 $(THREADSAFE) -OPTS += -DSQLITE_OMIT_LOAD_EXTENSION=1 -OPTS += -DSQLITE_ENABLE_LOCKING_STYLE=1 -OPTS += -DSQLITE_THREAD_OVERRIDE_LOCK=0 -OPTS += -DSQLITE_ENABLE_COLUMN_METADATA=1 -OPTS += -DHAVE_FDATASYNC=1 - -#### The suffix to add to executable files. ".exe" for windows. -# Nothing for unix. -# -EXE = .vxe -#EXE = - -#### C Compile and options for use in building executables that -# will run on the target platform. This is usually the same -# as BCC, unless you are cross-compiling. -# -#TCC = gcc -O6 -#TCC = gcc -g -O0 -Wall -#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage -#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6 -TCC = $(CC) $(DEFINE_CC) -O2 -g -mrtp $(CC_ARCH_SPEC) -D_REENTRANT=1 -D_VX_CPU=_VX_$(CPU) -D_VX_TOOL_FAMILY=$(TOOL_FAMILY) -D_VX_TOOL=$(TOOL) -TCC += -I$(WIND_USR)/h -I$(WIND_USR)/h/wrn/coreip -#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive - -#TCC_SHARED = $(TCC) -fPIC -TCC_SHARED = $(TCC) - -#### Tools used to build a static library. -# -#ARX = ar cr -#ARX = /opt/mingw/bin/i386-mingw32-ar cr -AR += cr -#RANLIB = ranlib -#RANLIB = /opt/mingw/bin/i386-mingw32-ranlib - -#MKSHLIB = gcc -shared -#SO = so -#SHPREFIX = lib -MKSHLIB = $(CC) $(DEFINE_CC) -mrtp -shared $(CC_ARCH_SPEC) -D_VX_CPU=_VX_$(CPU) -D_VX_TOOL_FAMILY=$(TOOL_FAMILY) -D_VX_TOOL=$(TOOL) -SO = so -SHPREFIX = lib - -#### Extra compiler options needed for programs that use the TCL library. -# -#TCL_FLAGS = -#TCL_FLAGS = -DSTATIC_BUILD=1 -TCL_FLAGS = -I/home/drh/tcltk/8.5linux -#TCL_FLAGS = -I/home/drh/tcltk/8.5win -DSTATIC_BUILD=1 -#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux - -#### Linker options needed to link against the TCL library. -# -#LIBTCL = -ltcl -lm -ldl -LIBTCL = /home/drh/tcltk/8.5linux/libtcl8.5g.a -lm -ldl -#LIBTCL = /home/drh/tcltk/8.5win/libtcl85s.a -lmsvcrt -#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc - -#### Additional objects for SQLite library when TCL support is enabled. -TCLOBJ = -#TCLOBJ = tclsqlite.o - -#### Compiler options needed for programs that use the readline() library. -# -READLINE_FLAGS = -#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline - -#### Linker options needed by programs using readline() must link against. -# -LIBREADLINE = -#LIBREADLINE = -static -lreadline -ltermcap - -#### Which "awk" program provides nawk compatibilty -# -# NAWK = nawk -NAWK = awk - - -#### Pasted and adapted main.mk file -############################################################################### -# The following macros should be defined before this script is -# invoked: -# -# TOP The toplevel directory of the source tree. This is the -# directory that contains this "Makefile.in" and the -# "configure.in" script. -# -# BCC C Compiler and options for use in building executables that -# will run on the platform that is doing the build. -# -# THREADLIB Specify any extra linker options needed to make the library -# thread safe -# -# OPTS Extra compiler command-line options. -# -# EXE The suffix to add to executable files. ".exe" for windows -# and "" for Unix. -# -# TCC C Compiler and options for use in building executables that -# will run on the target platform. This is usually the same -# as BCC, unless you are cross-compiling. -# -# AR Tools used to build a static library. -# RANLIB -# -# TCL_FLAGS Extra compiler options needed for programs that use the -# TCL library. -# -# LIBTCL Linker options needed to link against the TCL library. -# -# READLINE_FLAGS Compiler options needed for programs that use the -# readline() library. -# -# LIBREADLINE Linker options needed by programs using readline() must -# link against. -# -# NAWK Nawk compatible awk program. Older (obsolete?) solaris -# systems need this to avoid using the original AT&T AWK. -# -# Once the macros above are defined, the rest of this make script will -# build the SQLite library and testing tools. -################################################################################ - -# This is how we compile -# -TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) -TCCX_SHARED = $(TCC_SHARED) $(OPTS) -I. -I$(TOP)/src -I$(TOP) \ - -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3 \ - -I$(TOP)/ext/async - -# Object files for the SQLite library. -# -LIBOBJ+= alter.o analyze.o attach.o auth.o \ - backup.o bitvec.o btmutex.o btree.o build.o \ - callback.o complete.o date.o delete.o expr.o fault.o \ - fts3.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ - fts3_tokenizer.o fts3_tokenizer1.o \ - func.o global.o hash.o \ - icu.o insert.o journal.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 \ - notify.o opcodes.o os.o os_unix.o os_win.o \ - pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \ - random.o resolve.o rowset.o rtree.o select.o status.o \ - table.o tokenize.o trigger.o \ - update.o util.o vacuum.o \ - vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o \ - walker.o where.o utf.o vtab.o - - - -# All of the source code files. -# -SRC = \ - $(TOP)/src/alter.c \ - $(TOP)/src/analyze.c \ - $(TOP)/src/attach.c \ - $(TOP)/src/auth.c \ - $(TOP)/src/backup.c \ - $(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 \ - $(TOP)/src/ctime.c \ - $(TOP)/src/date.c \ - $(TOP)/src/delete.c \ - $(TOP)/src/expr.c \ - $(TOP)/src/fault.c \ - $(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 \ - $(TOP)/src/malloc.c \ - $(TOP)/src/mem0.c \ - $(TOP)/src/mem1.c \ - $(TOP)/src/mem2.c \ - $(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 \ - $(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/prepare.c \ - $(TOP)/src/printf.c \ - $(TOP)/src/random.c \ - $(TOP)/src/resolve.c \ - $(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/tclsqlite.c \ - $(TOP)/src/tokenize.c \ - $(TOP)/src/trigger.c \ - $(TOP)/src/utf.c \ - $(TOP)/src/update.c \ - $(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/vdbeInt.h \ - $(TOP)/src/vtab.c \ - $(TOP)/src/walker.c \ - $(TOP)/src/where.c - -# Source code for extensions -# -SRC += \ - $(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 -SRC += \ - $(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 -SRC += \ - $(TOP)/ext/fts3/fts3.c \ - $(TOP)/ext/fts3/fts3.h \ - $(TOP)/ext/fts3/fts3_expr.c \ - $(TOP)/ext/fts3/fts3_expr.h \ - $(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_tokenizer.h \ - $(TOP)/ext/fts3/fts3_tokenizer.c \ - $(TOP)/ext/fts3/fts3_tokenizer1.c -SRC += \ - $(TOP)/ext/icu/sqliteicu.h \ - $(TOP)/ext/icu/icu.c -SRC += \ - $(TOP)/ext/rtree/rtree.h \ - $(TOP)/ext/rtree/rtree.c - - -# Generated source code files -# -SRC += \ - keywordhash.h \ - opcodes.c \ - opcodes.h \ - parse.c \ - parse.h \ - sqlite3.h - - -# Source code to the test files. -# -TESTSRC = \ - $(TOP)/src/test1.c \ - $(TOP)/src/test2.c \ - $(TOP)/src/test3.c \ - $(TOP)/src/test4.c \ - $(TOP)/src/test5.c \ - $(TOP)/src/test6.c \ - $(TOP)/src/test7.c \ - $(TOP)/src/test8.c \ - $(TOP)/src/test9.c \ - $(TOP)/src/test_autoext.c \ - $(TOP)/src/test_async.c \ - $(TOP)/src/test_backup.c \ - $(TOP)/src/test_btree.c \ - $(TOP)/src/test_config.c \ - $(TOP)/src/test_devsym.c \ - $(TOP)/src/test_func.c \ - $(TOP)/src/test_hexio.c \ - $(TOP)/src/test_journal.c \ - $(TOP)/src/test_malloc.c \ - $(TOP)/src/test_md5.c \ - $(TOP)/src/test_mutex.c \ - $(TOP)/src/test_onefile.c \ - $(TOP)/src/test_osinst.c \ - $(TOP)/src/test_pcache.c \ - $(TOP)/src/test_schema.c \ - $(TOP)/src/test_server.c \ - $(TOP)/src/test_tclvar.c \ - $(TOP)/src/test_thread.c \ - $(TOP)/src/test_vfs.c \ - $(TOP)/src/test_wsd.c \ - -#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c -#TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c - -TESTSRC2 = \ - $(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \ - $(TOP)/src/build.c $(TOP)/src/ctime.c $(TOP)/src/date.c \ - $(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.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/where.c parse.c \ - $(TOP)/ext/fts3/fts3.c $(TOP)/ext/fts3/fts3_expr.c \ - $(TOP)/ext/fts3/fts3_tokenizer.c \ - $(TOP)/ext/async/sqlite3async.c - -# Header files used by all library source files. -# -HDR = \ - $(TOP)/src/btree.h \ - $(TOP)/src/btreeInt.h \ - $(TOP)/src/hash.h \ - $(TOP)/src/hwtime.h \ - keywordhash.h \ - $(TOP)/src/msvc.h \ - $(TOP)/src/mutex.h \ - opcodes.h \ - $(TOP)/src/os.h \ - $(TOP)/src/os_common.h \ - $(TOP)/src/os_setup.h \ - $(TOP)/src/os_win.h \ - $(TOP)/src/pager.h \ - $(TOP)/src/pcache.h \ - parse.h \ - sqlite3.h \ - $(TOP)/src/sqlite3ext.h \ - $(TOP)/src/sqliteInt.h \ - $(TOP)/src/sqliteLimit.h \ - $(TOP)/src/vdbe.h \ - $(TOP)/src/vdbeInt.h - -# Header files used by extensions -# -EXTHDR += \ - $(TOP)/ext/fts1/fts1.h \ - $(TOP)/ext/fts1/fts1_hash.h \ - $(TOP)/ext/fts1/fts1_tokenizer.h -EXTHDR += \ - $(TOP)/ext/fts2/fts2.h \ - $(TOP)/ext/fts2/fts2_hash.h \ - $(TOP)/ext/fts2/fts2_tokenizer.h -EXTHDR += \ - $(TOP)/ext/fts3/fts3.h \ - $(TOP)/ext/fts3/fts3_expr.h \ - $(TOP)/ext/fts3/fts3_hash.h \ - $(TOP)/ext/fts3/fts3_tokenizer.h -EXTHDR += \ - $(TOP)/ext/rtree/rtree.h -EXTHDR += \ - $(TOP)/ext/icu/sqliteicu.h - -# This is the default Makefile target. The objects listed here -# are what get build when you type just "make" with no arguments. -# -all: sqlite3.h libsqlite3.a sqlite3$(EXE) - -libsqlite3.a: $(LIBOBJ) - $(AR) libsqlite3.a $(LIBOBJ) - $(RANLIB) libsqlite3.a - -$(SHPREFIX)sqlite3.$(SO): $(LIBOBJ) - $(MKSHLIB) -o $(SHPREFIX)sqlite3.$(SO) $(LIBOBJ) $(TLIBS_SHARED) - -sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h - $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \ - $(TOP)/src/shell.c \ - $(LIBREADLINE) $(TLIBS) $(THREADLIB) -L. -lsqlite3 - -# This target creates a directory named "tsrc" and fills it with -# copies of all of the C source code and header files needed to -# build on the target system. Some of the C source code and header -# files are automatically generated. This target takes care of -# all that automatic generation. -# -target_source: $(SRC) - rm -rf tsrc - mkdir tsrc - cp -f $(SRC) tsrc - rm tsrc/sqlite.h.in tsrc/parse.y - touch target_source - -sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl - tclsh $(TOP)/tool/mksqlite3c.tcl - cp sqlite3.c tclsqlite3.c - cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c - -fts2amal.c: target_source $(TOP)/ext/fts2/mkfts2amal.tcl - tclsh $(TOP)/ext/fts2/mkfts2amal.tcl - -fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl - tclsh $(TOP)/ext/fts3/mkfts3amal.tcl - -# Rules to build the LEMON compiler generator -# -lemon: $(TOP)/tool/lemon.c $(TOP)/src/lempar.c - $(BCC) -o lemon $(TOP)/tool/lemon.c - cp $(TOP)/src/lempar.c . - -# Rules to build individual *.o files from generated *.c files. This -# applies to: -# -# parse.o -# opcodes.o -# -%.o: %.c $(HDR) - $(TCCX_SHARED) -c $< - -# Rules to build individual *.o files from files in the src directory. -# -%.o: $(TOP)/src/%.c $(HDR) - $(TCCX_SHARED) -c $< - -tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR) - $(TCCX_SHARED) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c - - - -# Rules to build opcodes.c and opcodes.h -# -opcodes.c: opcodes.h $(TOP)/mkopcodec.awk - $(NAWK) -f $(TOP)/mkopcodec.awk opcodes.h >opcodes.c - -opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk - cat parse.h $(TOP)/src/vdbe.c | \ - $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h - -# Rules to build parse.c and parse.h - the outputs of lemon. -# -parse.h: parse.c - -parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk - cp $(TOP)/src/parse.y . - rm -f parse.h - ./lemon $(OPTS) parse.y - mv parse.h parse.h.temp - awk -f $(TOP)/addopcodes.awk parse.h.temp >parse.h - -sqlite3.h: $(TOP)/src/sqlite.h.in - sed -e s/--VERS--/`cat ${TOP}/VERSION`/ \ - -e s/--VERSION-NUMBER--/`cat ${TOP}/VERSION | sed 's/[^0-9]/ /g' | $(NAWK) '{printf "%d%03d%03d",$$1,$$2,$$3}'`/ \ - $(TOP)/src/sqlite.h.in >sqlite3.h - -keywordhash.h: $(TOP)/tool/mkkeywordhash.c - $(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c - ./mkkeywordhash >keywordhash.h - - - -# Rules to build the extension objects. -# -icu.o: $(TOP)/ext/icu/icu.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/icu/icu.c - -fts2.o: $(TOP)/ext/fts2/fts2.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2.c - -fts2_hash.o: $(TOP)/ext/fts2/fts2_hash.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_hash.c - -fts2_icu.o: $(TOP)/ext/fts2/fts2_icu.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_icu.c - -fts2_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_porter.c - -fts2_tokenizer.o: $(TOP)/ext/fts2/fts2_tokenizer.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c - -fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c - -fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c - -fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c - -fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c - -fts3_icu.o: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_icu.c - -fts3_porter.o: $(TOP)/ext/fts3/fts3_porter.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_porter.c - -fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer.c - -fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c - -rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) - $(TCCX_SHARED) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c - - -# Rules for building test programs and for running tests -# -tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a - $(TCCX_SHARED) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \ - $(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB) - - -# Rules to build the 'testfixture' application. -# -TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 -TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE - -testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c - $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ - $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ - -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) libsqlite3.a - -amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c - $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ - $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \ - -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) - -fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c - $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ - -DSQLITE_ENABLE_FTS3=1 \ - $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c \ - -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) - -fulltest: testfixture$(EXE) sqlite3$(EXE) - ./testfixture$(EXE) $(TOP)/test/all.test - -soaktest: testfixture$(EXE) sqlite3$(EXE) - ./testfixture$(EXE) $(TOP)/test/all.test -soak=1 - -fulltestonly: testfixture$(EXE) sqlite3$(EXE) - ./testfixture$(EXE) $(TOP)/test/full.test - -test: testfixture$(EXE) sqlite3$(EXE) - ./testfixture$(EXE) $(TOP)/test/veryquick.test - -sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \ - $(TOP)/tool/spaceanal.tcl - sed \ - -e '/^#/d' \ - -e 's,\\,\\\\,g' \ - -e 's,",\\",g' \ - -e 's,^,",' \ - -e 's,$$,\\n",' \ - $(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h - $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ - -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_DEBUG=1 -DSQLITE_PRIVATE="" \ - $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \ - -o sqlite3_analyzer$(EXE) \ - $(LIBTCL) $(THREADLIB) - -TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO) -$(TEST_EXTENSION): $(TOP)/src/test_loadext.c - $(MKSHLIB) $(TOP)/src/test_loadext.c -o $(TEST_EXTENSION) - -extensiontest: testfixture$(EXE) $(TEST_EXTENSION) - ./testfixture$(EXE) $(TOP)/test/loadext.test - -clean: - rm -f *.o sqlite3$(EXE) libsqlite3.a sqlite3.h opcodes.* - rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h - rm -f $(PUBLISH) - rm -f *.da *.bb *.bbg gmon.out - rm -rf quota2a quota2b quota2c - rm -rf tsrc target_source - rm -f testloadext.dll libtestloadext.so - rm -f sqlite3.c fts?amal.c tclsqlite3.c - rm -f sqlite3rc.h - rm -f shell.c sqlite3ext.h - rm -f $(SHPREFIX)sqlite3.$(SO) diff --git a/VERSION b/VERSION index 89a1ad7ad3..a5c4c76339 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.8.12 +3.9.0 diff --git a/addopcodes.awk b/addopcodes.awk deleted file mode 100644 index dcd31eff84..0000000000 --- a/addopcodes.awk +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/awk -# -# This script appends additional token codes to the end of the -# parse.h file that lemon generates. These extra token codes are -# not used by the parser. But they are used by the tokenizer and/or -# the code generator. -# -# -BEGIN { - max = 0 -} -/^#define TK_/ { - print $0 - if( max<$3 ) max = $3 -} -END { - printf "#define TK_%-29s %4d\n", "TO_TEXT", ++max - printf "#define TK_%-29s %4d\n", "TO_BLOB", ++max - printf "#define TK_%-29s %4d\n", "TO_NUMERIC", ++max - printf "#define TK_%-29s %4d\n", "TO_INT", ++max - printf "#define TK_%-29s %4d\n", "TO_REAL", ++max - printf "#define TK_%-29s %4d\n", "ISNOT", ++max - printf "#define TK_%-29s %4d\n", "END_OF_FILE", ++max - printf "#define TK_%-29s %4d\n", "ILLEGAL", ++max - printf "#define TK_%-29s %4d\n", "SPACE", ++max - printf "#define TK_%-29s %4d\n", "UNCLOSED_STRING", ++max - printf "#define TK_%-29s %4d\n", "FUNCTION", ++max - printf "#define TK_%-29s %4d\n", "COLUMN", ++max - printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", ++max - printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max - printf "#define TK_%-29s %4d\n", "UMINUS", ++max - printf "#define TK_%-29s %4d\n", "UPLUS", ++max - printf "#define TK_%-29s %4d\n", "REGISTER", ++max -} diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am index 60d2ba2673..5b7c4a090a 100644 --- a/autoconf/Makefile.am +++ b/autoconf/Makefile.am @@ -1,5 +1,5 @@ -AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE +AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE lib_LTLIBRARIES = libsqlite3.la libsqlite3_la_SOURCES = sqlite3.c diff --git a/autoconf/configure.ac b/autoconf/configure.ac index 46b6563edc..888b924e40 100644 --- a/autoconf/configure.ac +++ b/autoconf/configure.ac @@ -78,6 +78,31 @@ AC_MSG_RESULT($enable_dynamic_extensions) AC_SUBST(DYNAMIC_EXTENSION_FLAGS) #----------------------------------------------------------------------- +#----------------------------------------------------------------------- +# --enable-fts5 +# +AC_ARG_ENABLE(fts5, [AS_HELP_STRING( + [--enable-fts5], [include fts5 support [default=no]])], + [], [enable_fts5=no]) +if test x"$enable_fts5" == "xyes"; then + AC_SEARCH_LIBS(log, m) + FTS5_FLAGS=-DSQLITE_ENABLE_FTS5 +fi +AC_SUBST(FTS5_FLAGS) +#----------------------------------------------------------------------- + +#----------------------------------------------------------------------- +# --enable-json1 +# +AC_ARG_ENABLE(json1, [AS_HELP_STRING( + [--enable-json1], [include json1 support [default=no]])], + [], [enable_json1=no]) +if test x"$enable_json1" == "xyes"; then + JSON1_FLAGS=-DSQLITE_ENABLE_JSON1 +fi +AC_SUBST(JSON1_FLAGS) +#----------------------------------------------------------------------- + AC_CHECK_FUNCS(posix_fallocate) #----------------------------------------------------------------------- diff --git a/configure b/configure index 2969759f3c..b5ec65f2df 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.8.12. +# Generated by GNU Autoconf 2.69 for sqlite 3.9.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.8.12' -PACKAGE_STRING='sqlite 3.8.12' +PACKAGE_VERSION='3.9.0' +PACKAGE_STRING='sqlite 3.9.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -803,7 +803,6 @@ VERSION program_prefix TCLLIBDIR TCLSH_CMD -AWK INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM @@ -902,6 +901,11 @@ with_readline_inc enable_debug enable_amalgamation enable_load_extension +enable_fts3 +enable_fts4 +enable_fts5 +enable_json1 +enable_rtree enable_gcov ' ac_precious_vars='build_alias @@ -1454,7 +1458,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.8.12 to adapt to many kinds of systems. +\`configure' configures sqlite 3.9.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1519,7 +1523,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.8.12:";; + short | recursive ) echo "Configuration of sqlite 3.9.0:";; esac cat <<\_ACEOF @@ -1544,6 +1548,11 @@ Optional Features: separately --disable-load-extension Disable loading of external extensions + --enable-fts3 Enable the FTS3 extension + --enable-fts4 Enable the FTS4 extension + --enable-fts5 Enable the FTS5 extension + --enable-json1 Enable the JSON1 extension + --enable-rtree Enable the RTREE extension --enable-gcov Enable coverage testing using gcov Optional Packages: @@ -1634,7 +1643,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.8.12 +sqlite configure 3.9.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2053,7 +2062,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.8.12, which was +It was created by sqlite $as_me 3.9.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3911,13 +3920,13 @@ if ${lt_cv_nm_interface+:} false; then : else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3914: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3923: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3917: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3926: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3920: output\"" >&5) + (eval echo "\"\$as_me:3929: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -5123,7 +5132,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5126 "configure"' > conftest.$ac_ext + echo '#line 5135 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6648,11 +6657,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6651: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6660: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6655: \$? = $ac_status" >&5 + echo "$as_me:6664: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -6987,11 +6996,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6990: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6999: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6994: \$? = $ac_status" >&5 + echo "$as_me:7003: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7092,11 +7101,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7095: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7104: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7099: \$? = $ac_status" >&5 + echo "$as_me:7108: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7147,11 +7156,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7150: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7159: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7154: \$? = $ac_status" >&5 + echo "$as_me:7163: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -9527,7 +9536,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 9530 "configure" +#line 9539 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -9623,7 +9632,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 9626 "configure" +#line 9635 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -9943,48 +9952,6 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' -for ac_prog in gawk mawk nawk awk -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AWK"; then - ac_cv_prog_AWK="$AWK" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AWK="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AWK=$ac_cv_prog_AWK -if test -n "$AWK"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 -$as_echo "$AWK" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$AWK" && break -done - ######### # Enable large file support (if special flags are necessary) @@ -11223,6 +11190,177 @@ else OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1" fi +######### +# See whether we should enable Full Text Search extensions +# Check whether --enable-fts3 was given. +if test "${enable_fts3+set}" = set; then : + enableval=$enable_fts3; enable_fts3=yes +else + enable_fts3=no +fi + +if test "${enable_fts3}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS3" +fi +# Check whether --enable-fts4 was given. +if test "${enable_fts4+set}" = set; then : + enableval=$enable_fts4; enable_fts4=yes +else + enable_fts4=no +fi + +if test "${enable_fts4}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS4" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5 +$as_echo_n "checking for library containing log... " >&6; } +if ${ac_cv_search_log+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char log (); +int +main () +{ +return log (); + ; + return 0; +} +_ACEOF +for ac_lib in '' m; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_log=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_log+:} false; then : + break +fi +done +if ${ac_cv_search_log+:} false; then : + +else + ac_cv_search_log=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_log" >&5 +$as_echo "$ac_cv_search_log" >&6; } +ac_res=$ac_cv_search_log +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +fi +# Check whether --enable-fts5 was given. +if test "${enable_fts5+set}" = set; then : + enableval=$enable_fts5; enable_fts5=yes +else + enable_fts5=no +fi + +if test "${enable_fts5}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS5" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5 +$as_echo_n "checking for library containing log... " >&6; } +if ${ac_cv_search_log+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char log (); +int +main () +{ +return log (); + ; + return 0; +} +_ACEOF +for ac_lib in '' m; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_log=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_log+:} false; then : + break +fi +done +if ${ac_cv_search_log+:} false; then : + +else + ac_cv_search_log=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_log" >&5 +$as_echo "$ac_cv_search_log" >&6; } +ac_res=$ac_cv_search_log +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +fi + +######### +# See whether we should enable JSON1 +# Check whether --enable-json1 was given. +if test "${enable_json1+set}" = set; then : + enableval=$enable_json1; enable_json1=yes +else + enable_json1=no +fi + +if test "${enable_json1}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_JSON1" +fi + +######### +# See whether we should enable RTREE +# Check whether --enable-rtree was given. +if test "${enable_rtree+set}" = set; then : + enableval=$enable_rtree; enable_rtree=yes +else + enable_rtree=no +fi + +if test "${enable_rtree}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_RTREE" +fi + ######### # attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter for option in $CFLAGS $CPPFLAGS @@ -11808,7 +11946,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.8.12, which was +This file was extended by sqlite $as_me 3.9.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -11874,7 +12012,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.8.12 +sqlite config.status 3.9.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -11885,7 +12023,6 @@ gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' -AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF diff --git a/configure.ac b/configure.ac index 92d9b47b39..11ab81d342 100644 --- a/configure.ac +++ b/configure.ac @@ -90,7 +90,6 @@ fi # AC_PROG_LIBTOOL AC_PROG_INSTALL -AC_PROG_AWK ######### # Enable large file support (if special flags are necessary) @@ -560,6 +559,47 @@ else OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1" fi +######### +# See whether we should enable Full Text Search extensions +AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3], + [Enable the FTS3 extension]), + [enable_fts3=yes],[enable_fts3=no]) +if test "${enable_fts3}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS3" +fi +AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4], + [Enable the FTS4 extension]), + [enable_fts4=yes],[enable_fts4=no]) +if test "${enable_fts4}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS4" + AC_SEARCH_LIBS([log],[m]) +fi +AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5], + [Enable the FTS5 extension]), + [enable_fts5=yes],[enable_fts5=no]) +if test "${enable_fts5}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_FTS5" + AC_SEARCH_LIBS([log],[m]) +fi + +######### +# See whether we should enable JSON1 +AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1], + [Enable the JSON1 extension]), + [enable_json1=yes],[enable_json1=no]) +if test "${enable_json1}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_JSON1" +fi + +######### +# See whether we should enable RTREE +AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree], + [Enable the RTREE extension]), + [enable_rtree=yes],[enable_rtree=no]) +if test "${enable_rtree}" = "yes" ; then + OPT_FEATURE_FLAGS+=" -DSQLITE_ENABLE_RTREE" +fi + ######### # attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter for option in $CFLAGS $CPPFLAGS diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 6a9b507fc0..748faefec5 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -1517,6 +1517,19 @@ static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){ #endif } +/* +** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this +** extension is currently being used by a version of SQLite too old to +** support index-info flags. In that case this function is a no-op. +*/ +static void fts3SetUniqueFlag(sqlite3_index_info *pIdxInfo){ +#if SQLITE_VERSION_NUMBER>=3008012 + if( sqlite3_libversion_number()>=3008012 ){ + pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE; + } +#endif +} + /* ** Implementation of the xBestIndex method for FTS3 tables. There ** are three possible strategies, in order of preference: @@ -1607,6 +1620,9 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ } } + /* If using a docid=? or rowid=? strategy, set the UNIQUE flag. */ + if( pInfo->idxNum==FTS3_DOCID_SEARCH ) fts3SetUniqueFlag(pInfo); + iIdx = 1; if( iCons>=0 ){ pInfo->aConstraintUsage[iCons].argvIndex = iIdx++; diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 981c37deee..06bcc7202e 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -264,6 +264,7 @@ struct Fts3Table { int nPendingData; /* Current bytes of pending data */ sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ int iPrevLangid; /* Langid of recently inserted document */ + int bPrevDelete; /* True if last operation was a delete */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) /* State variables used for validating that the transaction control diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c index d7cabd9919..788e5021ec 100644 --- a/ext/fts3/fts3_expr.c +++ b/ext/fts3/fts3_expr.c @@ -793,125 +793,151 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){ rc = SQLITE_ERROR; } - if( rc==SQLITE_OK && (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){ - Fts3Expr **apLeaf; - apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth); - if( 0==apLeaf ){ - rc = SQLITE_NOMEM; - }else{ - memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth); - } - - if( rc==SQLITE_OK ){ - int i; - Fts3Expr *p; - - /* Set $p to point to the left-most leaf in the tree of eType nodes. */ - for(p=pRoot; p->eType==eType; p=p->pLeft){ - assert( p->pParent==0 || p->pParent->pLeft==p ); - assert( p->pLeft && p->pRight ); - } - - /* This loop runs once for each leaf in the tree of eType nodes. */ - while( 1 ){ - int iLvl; - Fts3Expr *pParent = p->pParent; /* Current parent of p */ - - assert( pParent==0 || pParent->pLeft==p ); - p->pParent = 0; - if( pParent ){ - pParent->pLeft = 0; - }else{ - pRoot = 0; - } - rc = fts3ExprBalance(&p, nMaxDepth-1); - if( rc!=SQLITE_OK ) break; - - for(iLvl=0; p && iLvlpLeft = apLeaf[iLvl]; - pFree->pRight = p; - pFree->pLeft->pParent = pFree; - pFree->pRight->pParent = pFree; - - p = pFree; - pFree = pFree->pParent; - p->pParent = 0; - apLeaf[iLvl] = 0; - } - } - if( p ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_TOOBIG; - break; - } - - /* If that was the last leaf node, break out of the loop */ - if( pParent==0 ) break; - - /* Set $p to point to the next leaf in the tree of eType nodes */ - for(p=pParent->pRight; p->eType==eType; p=p->pLeft); - - /* Remove pParent from the original tree. */ - assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent ); - pParent->pRight->pParent = pParent->pParent; - if( pParent->pParent ){ - pParent->pParent->pLeft = pParent->pRight; - }else{ - assert( pParent==pRoot ); - pRoot = pParent->pRight; - } - - /* Link pParent into the free node list. It will be used as an - ** internal node of the new tree. */ - pParent->pParent = pFree; - pFree = pParent; + if( rc==SQLITE_OK ){ + if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){ + Fts3Expr **apLeaf; + apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth); + if( 0==apLeaf ){ + rc = SQLITE_NOMEM; + }else{ + memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth); } if( rc==SQLITE_OK ){ - p = 0; - for(i=0; ipParent = 0; + int i; + Fts3Expr *p; + + /* Set $p to point to the left-most leaf in the tree of eType nodes. */ + for(p=pRoot; p->eType==eType; p=p->pLeft){ + assert( p->pParent==0 || p->pParent->pLeft==p ); + assert( p->pLeft && p->pRight ); + } + + /* This loop runs once for each leaf in the tree of eType nodes. */ + while( 1 ){ + int iLvl; + Fts3Expr *pParent = p->pParent; /* Current parent of p */ + + assert( pParent==0 || pParent->pLeft==p ); + p->pParent = 0; + if( pParent ){ + pParent->pLeft = 0; + }else{ + pRoot = 0; + } + rc = fts3ExprBalance(&p, nMaxDepth-1); + if( rc!=SQLITE_OK ) break; + + for(iLvl=0; p && iLvlpLeft = apLeaf[iLvl]; pFree->pRight = p; - pFree->pLeft = apLeaf[i]; pFree->pLeft->pParent = pFree; pFree->pRight->pParent = pFree; p = pFree; pFree = pFree->pParent; p->pParent = 0; + apLeaf[iLvl] = 0; } } + if( p ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_TOOBIG; + break; + } + + /* If that was the last leaf node, break out of the loop */ + if( pParent==0 ) break; + + /* Set $p to point to the next leaf in the tree of eType nodes */ + for(p=pParent->pRight; p->eType==eType; p=p->pLeft); + + /* Remove pParent from the original tree. */ + assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent ); + pParent->pRight->pParent = pParent->pParent; + if( pParent->pParent ){ + pParent->pParent->pLeft = pParent->pRight; + }else{ + assert( pParent==pRoot ); + pRoot = pParent->pRight; + } + + /* Link pParent into the free node list. It will be used as an + ** internal node of the new tree. */ + pParent->pParent = pFree; + pFree = pParent; } - pRoot = p; - }else{ - /* An error occurred. Delete the contents of the apLeaf[] array - ** and pFree list. Everything else is cleaned up by the call to - ** sqlite3Fts3ExprFree(pRoot) below. */ - Fts3Expr *pDel; - for(i=0; ipParent; - sqlite3_free(pDel); + + if( rc==SQLITE_OK ){ + p = 0; + for(i=0; ipParent = 0; + }else{ + assert( pFree!=0 ); + pFree->pRight = p; + pFree->pLeft = apLeaf[i]; + pFree->pLeft->pParent = pFree; + pFree->pRight->pParent = pFree; + + p = pFree; + pFree = pFree->pParent; + p->pParent = 0; + } + } + } + pRoot = p; + }else{ + /* An error occurred. Delete the contents of the apLeaf[] array + ** and pFree list. Everything else is cleaned up by the call to + ** sqlite3Fts3ExprFree(pRoot) below. */ + Fts3Expr *pDel; + for(i=0; ipParent; + sqlite3_free(pDel); + } } + + assert( pFree==0 ); + sqlite3_free( apLeaf ); + } + }else if( eType==FTSQUERY_NOT ){ + Fts3Expr *pLeft = pRoot->pLeft; + Fts3Expr *pRight = pRoot->pRight; + + pRoot->pLeft = 0; + pRoot->pRight = 0; + pLeft->pParent = 0; + pRight->pParent = 0; + + rc = fts3ExprBalance(&pLeft, nMaxDepth-1); + if( rc==SQLITE_OK ){ + rc = fts3ExprBalance(&pRight, nMaxDepth-1); } - assert( pFree==0 ); - sqlite3_free( apLeaf ); + if( rc!=SQLITE_OK ){ + sqlite3Fts3ExprFree(pRight); + sqlite3Fts3ExprFree(pLeft); + }else{ + assert( pLeft && pRight ); + pRoot->pLeft = pLeft; + pLeft->pParent = pRoot; + pRoot->pRight = pRight; + pRight->pParent = pRoot; + } } } - + if( rc!=SQLITE_OK ){ sqlite3Fts3ExprFree(pRoot); pRoot = 0; diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index 4cd2aebf6a..d5a408222e 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -860,10 +860,12 @@ static int fts3PendingTermsAdd( */ static int fts3PendingTermsDocid( Fts3Table *p, /* Full-text table handle */ + int bDelete, /* True if this op is a delete */ int iLangid, /* Language id of row being written */ sqlite_int64 iDocid /* Docid of row being written */ ){ assert( iLangid>=0 ); + assert( bDelete==1 || bDelete==0 ); /* TODO(shess) Explore whether partially flushing the buffer on ** forced-flush would provide better performance. I suspect that if @@ -871,7 +873,8 @@ static int fts3PendingTermsDocid( ** buffer was half empty, that would let the less frequent terms ** generate longer doclists. */ - if( iDocid<=p->iPrevDocid + if( iDocidiPrevDocid + || (iDocid==p->iPrevDocid && p->bPrevDelete==0) || p->iPrevLangid!=iLangid || p->nPendingData>p->nMaxPendingData ){ @@ -880,6 +883,7 @@ static int fts3PendingTermsDocid( } p->iPrevDocid = iDocid; p->iPrevLangid = iLangid; + p->bPrevDelete = bDelete; return SQLITE_OK; } @@ -1069,7 +1073,8 @@ static void fts3DeleteTerms( if( SQLITE_ROW==sqlite3_step(pSelect) ){ int i; int iLangid = langidFromSelect(p, pSelect); - rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0)); + i64 iDocid = sqlite3_column_int64(pSelect, 0); + rc = fts3PendingTermsDocid(p, 1, iLangid, iDocid); for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){ int iCol = i-1; if( p->abNotindexed[iCol]==0 ){ @@ -1317,14 +1322,19 @@ static int fts3SegReaderNext( if( fts3SegReaderIsPending(pReader) ){ Fts3HashElem *pElem = *(pReader->ppNextElem); - if( pElem==0 ){ - pReader->aNode = 0; - }else{ + sqlite3_free(pReader->aNode); + pReader->aNode = 0; + if( pElem ){ + char *aCopy; PendingList *pList = (PendingList *)fts3HashData(pElem); + int nCopy = pList->nData+1; pReader->zTerm = (char *)fts3HashKey(pElem); pReader->nTerm = fts3HashKeysize(pElem); - pReader->nNode = pReader->nDoclist = pList->nData + 1; - pReader->aNode = pReader->aDoclist = pList->aData; + aCopy = (char*)sqlite3_malloc(nCopy); + if( !aCopy ) return SQLITE_NOMEM; + memcpy(aCopy, pList->aData, nCopy); + pReader->nNode = pReader->nDoclist = nCopy; + pReader->aNode = pReader->aDoclist = aCopy; pReader->ppNextElem++; assert( pReader->aNode ); } @@ -1564,12 +1574,14 @@ int sqlite3Fts3MsrOvfl( ** second argument. */ void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ - if( pReader && !fts3SegReaderIsPending(pReader) ){ - sqlite3_free(pReader->zTerm); + if( pReader ){ + if( !fts3SegReaderIsPending(pReader) ){ + sqlite3_free(pReader->zTerm); + } if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); - sqlite3_blob_close(pReader->pBlob); } + sqlite3_blob_close(pReader->pBlob); } sqlite3_free(pReader); } @@ -3512,7 +3524,7 @@ static int fts3DoRebuild(Fts3Table *p){ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ int iCol; int iLangid = langidFromSelect(p, pStmt); - rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0)); + rc = fts3PendingTermsDocid(p, 0, iLangid, sqlite3_column_int64(pStmt, 0)); memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1)); for(iCol=0; rc==SQLITE_OK && iColnColumn; iCol++){ if( p->abNotindexed[iCol]==0 ){ @@ -5617,7 +5629,7 @@ int sqlite3Fts3UpdateMethod( } } if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){ - rc = fts3PendingTermsDocid(p, iLangid, *pRowid); + rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid); } if( rc==SQLITE_OK ){ assert( p->iPrevDocid==*pRowid ); diff --git a/ext/fts5/fts5.h b/ext/fts5/fts5.h index 7d974921d1..5f528af793 100644 --- a/ext/fts5/fts5.h +++ b/ext/fts5/fts5.h @@ -23,6 +23,10 @@ #include "sqlite3.h" +#ifdef __cplusplus +extern "C" { +#endif + /************************************************************************* ** CUSTOM AUXILIARY FUNCTIONS ** @@ -508,5 +512,9 @@ struct fts5_api { ** END OF REGISTRATION API *************************************************************************/ +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + #endif /* _FTS5_H */ diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h index 6f6f4ed784..3671da1f12 100644 --- a/ext/fts5/fts5Int.h +++ b/ext/fts5/fts5Int.h @@ -81,6 +81,20 @@ extern int sqlite3_fts5_may_be_corrupt; #endif typedef struct Fts5Global Fts5Global; +typedef struct Fts5Colset Fts5Colset; + +/* If a NEAR() clump or phrase may only match a specific set of columns, +** then an object of the following type is used to record the set of columns. +** Each entry in the aiCol[] array is a column that may be matched. +** +** This object is used by fts5_expr.c and fts5_index.c. +*/ +struct Fts5Colset { + int nCol; + int aiCol[1]; +}; + + /************************************************************************** ** Interface to code in fts5_config.c. fts5_config.c contains contains code @@ -240,7 +254,6 @@ int sqlite3Fts5Get32(const u8*); typedef struct Fts5PoslistReader Fts5PoslistReader; struct Fts5PoslistReader { /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */ - int iCol; /* If (iCol>=0), this column only */ const u8 *a; /* Position list to iterate through */ int n; /* Size of buffer at a[] in bytes */ int i; /* Current offset in a[] */ @@ -252,7 +265,6 @@ struct Fts5PoslistReader { i64 iPos; /* (iCol<<32) + iPos */ }; int sqlite3Fts5PoslistReaderInit( - int iCol, /* If (iCol>=0), this column only */ const u8 *a, int n, /* Poslist buffer to iterate through */ Fts5PoslistReader *pIter /* Iterator object to initialize */ ); @@ -305,7 +317,7 @@ int sqlite3Fts5IndexClose(Fts5Index *p); /* ** for( -** pIter = sqlite3Fts5IndexQuery(p, "token", 5, 0); +** sqlite3Fts5IndexQuery(p, "token", 5, 0, 0, &pIter); ** 0==sqlite3Fts5IterEof(pIter); ** sqlite3Fts5IterNext(pIter) ** ){ @@ -321,7 +333,8 @@ int sqlite3Fts5IndexQuery( Fts5Index *p, /* FTS index to query */ const char *pToken, int nToken, /* Token (or prefix) to query for */ int flags, /* Mask of FTS5INDEX_QUERY_X flags */ - Fts5IndexIter **ppIter + Fts5Colset *pColset, /* Match these columns only */ + Fts5IndexIter **ppIter /* OUT: New iterator object */ ); /* @@ -332,7 +345,7 @@ int sqlite3Fts5IterEof(Fts5IndexIter*); int sqlite3Fts5IterNext(Fts5IndexIter*); int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch); i64 sqlite3Fts5IterRowid(Fts5IndexIter*); -int sqlite3Fts5IterPoslist(Fts5IndexIter*, const u8 **pp, int *pn, i64 *pi); +int sqlite3Fts5IterPoslist(Fts5IndexIter*,Fts5Colset*, const u8**, int*, i64*); int sqlite3Fts5IterPoslistBuffer(Fts5IndexIter *pIter, Fts5Buffer *pBuf); /* @@ -370,6 +383,7 @@ int sqlite3Fts5IndexWrite( */ int sqlite3Fts5IndexBeginWrite( Fts5Index *p, /* Index to write to */ + int bDelete, /* True if current operation is a delete */ i64 iDocid /* Docid to add or remove data from */ ); @@ -434,6 +448,15 @@ int sqlite3Fts5PutVarint(unsigned char *p, u64 v); #define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b) #define fts5GetVarint sqlite3Fts5GetVarint +#define fts5FastGetVarint32(a, iOff, nVal) { \ + nVal = (a)[iOff++]; \ + if( nVal & 0x80 ){ \ + iOff--; \ + iOff += fts5GetVarint32(&(a)[iOff], nVal); \ + } \ +} + + /* ** End of interface to code in fts5_varint.c. **************************************************************************/ @@ -526,7 +549,8 @@ int sqlite3Fts5DropAll(Fts5Config*); int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); int sqlite3Fts5StorageDelete(Fts5Storage *p, i64); -int sqlite3Fts5StorageInsert(Fts5Storage *p, sqlite3_value **apVal, int, i64*); +int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); +int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); int sqlite3Fts5StorageIntegrity(Fts5Storage *p); @@ -565,7 +589,6 @@ typedef struct Fts5Parse Fts5Parse; typedef struct Fts5Token Fts5Token; typedef struct Fts5ExprPhrase Fts5ExprPhrase; typedef struct Fts5ExprNearset Fts5ExprNearset; -typedef struct Fts5ExprColset Fts5ExprColset; struct Fts5Token { const char *p; /* Token text (not NULL terminated) */ @@ -633,9 +656,9 @@ Fts5ExprNearset *sqlite3Fts5ParseNearset( Fts5ExprPhrase* ); -Fts5ExprColset *sqlite3Fts5ParseColset( +Fts5Colset *sqlite3Fts5ParseColset( Fts5Parse*, - Fts5ExprColset*, + Fts5Colset*, Fts5Token * ); @@ -644,7 +667,7 @@ void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); -void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5ExprColset*); +void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*); void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c index 2e33d5132f..ad581bba77 100644 --- a/ext/fts5/fts5_aux.c +++ b/ext/fts5/fts5_aux.c @@ -13,7 +13,7 @@ #include "fts5Int.h" -#include +#include /* amalgamator: keep */ /* ** Object used to iterate through all "coalesced phrase instances" in diff --git a/ext/fts5/fts5_buffer.c b/ext/fts5/fts5_buffer.c index 1a7c0d0f8a..68f24bf18b 100644 --- a/ext/fts5/fts5_buffer.c +++ b/ext/fts5/fts5_buffer.c @@ -185,11 +185,11 @@ int sqlite3Fts5PoslistNext64( }else{ i64 iOff = *piOff; int iVal; - i += fts5GetVarint32(&a[i], iVal); + fts5FastGetVarint32(a, i, iVal); if( iVal==1 ){ - i += fts5GetVarint32(&a[i], iVal); + fts5FastGetVarint32(a, i, iVal); iOff = ((i64)iVal) << 32; - i += fts5GetVarint32(&a[i], iVal); + fts5FastGetVarint32(a, i, iVal); } *piOff = iOff + (iVal-2); *pi = i; @@ -203,26 +203,20 @@ int sqlite3Fts5PoslistNext64( ** if the iterator reaches EOF, or false otherwise. */ int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){ - if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) - || (pIter->iCol>=0 && (pIter->iPos >> 32) > pIter->iCol) - ){ + if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) ){ pIter->bEof = 1; } return pIter->bEof; } int sqlite3Fts5PoslistReaderInit( - int iCol, /* If (iCol>=0), this column only */ const u8 *a, int n, /* Poslist buffer to iterate through */ Fts5PoslistReader *pIter /* Iterator object to initialize */ ){ memset(pIter, 0, sizeof(*pIter)); pIter->a = a; pIter->n = n; - pIter->iCol = iCol; - do { - sqlite3Fts5PoslistReaderNext(pIter); - }while( pIter->bEof==0 && (pIter->iPos >> 32)bEof; } @@ -233,13 +227,15 @@ int sqlite3Fts5PoslistWriterAppend( ){ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; int rc = SQLITE_OK; - if( (iPos & colmask) != (pWriter->iPrev & colmask) ){ - fts5BufferAppendVarint(&rc, pBuf, 1); - fts5BufferAppendVarint(&rc, pBuf, (iPos >> 32)); - pWriter->iPrev = (iPos & colmask); + if( 0==sqlite3Fts5BufferGrow(&rc, pBuf, 5+5+5) ){ + if( (iPos & colmask) != (pWriter->iPrev & colmask) ){ + pBuf->p[pBuf->n++] = 1; + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32)); + pWriter->iPrev = (iPos & colmask); + } + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-pWriter->iPrev)+2); + pWriter->iPrev = iPos; } - fts5BufferAppendVarint(&rc, pBuf, (iPos - pWriter->iPrev) + 2); - pWriter->iPrev = iPos; return rc; } @@ -290,11 +286,12 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){ ** * The 52 upper and lower case ASCII characters, and ** * The 10 integer ASCII characters. ** * The underscore character "_" (0x5F). +** * The unicode "subsitute" character (0x1A). */ int sqlite3Fts5IsBareword(char t){ u8 aBareword[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 .. 0x2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30 .. 0x3F */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 .. 0x4F */ diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c index 559f0db82b..8800808a32 100644 --- a/ext/fts5/fts5_expr.c +++ b/ext/fts5/fts5_expr.c @@ -32,6 +32,11 @@ typedef struct Fts5ExprTerm Fts5ExprTerm; void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64)); void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*)); void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*); +#ifndef NDEBUG +#include +void sqlite3Fts5ParserTrace(FILE*, char*); +#endif + struct Fts5Expr { Fts5Index *pIndex; @@ -89,23 +94,13 @@ struct Fts5ExprPhrase { Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ }; -/* -** If a NEAR() clump may only match a specific set of columns, then -** Fts5ExprNearset.pColset points to an object of the following type. -** Each entry in the aiCol[] array -*/ -struct Fts5ExprColset { - int nCol; - int aiCol[1]; -}; - /* ** One or more phrases that must appear within a certain token distance of ** each other within each matching document. */ struct Fts5ExprNearset { int nNear; /* NEAR parameter */ - Fts5ExprColset *pColset; /* Columns to search (NULL -> all columns) */ + Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ int nPhrase; /* Number of entries in aPhrase[] array */ Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ }; @@ -276,14 +271,6 @@ void sqlite3Fts5ExprFree(Fts5Expr *p){ } } -static int fts5ExprColsetTest(Fts5ExprColset *pColset, int iCol){ - int i; - for(i=0; inCol; i++){ - if( pColset->aiCol[i]==iCol ) return 1; - } - return 0; -} - /* ** Argument pTerm must be a synonym iterator. Return the current rowid ** that it points to. @@ -314,6 +301,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){ */ static int fts5ExprSynonymPoslist( Fts5ExprTerm *pTerm, + Fts5Colset *pColset, i64 iRowid, int *pbDel, /* OUT: Caller should sqlite3_free(*pa) */ u8 **pa, int *pn @@ -332,7 +320,7 @@ static int fts5ExprSynonymPoslist( const u8 *a; int n; i64 dummy; - rc = sqlite3Fts5IterPoslist(pIter, &a, &n, &dummy); + rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy); if( rc!=SQLITE_OK ) goto synonym_poslist_out; if( nIter==nAlloc ){ int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; @@ -346,7 +334,7 @@ static int fts5ExprSynonymPoslist( if( aIter!=aStatic ) sqlite3_free(aIter); aIter = aNew; } - sqlite3Fts5PoslistReaderInit(-1, a, n, &aIter[nIter]); + sqlite3Fts5PoslistReaderInit(a, n, &aIter[nIter]); assert( aIter[nIter].bEof==0 ); nIter++; } @@ -405,7 +393,7 @@ static int fts5ExprSynonymPoslist( */ static int fts5ExprPhraseIsMatch( Fts5ExprNode *pNode, /* Node pPhrase belongs to */ - Fts5ExprColset *pColset, /* Restrict matches to these columns */ + Fts5Colset *pColset, /* Restrict matches to these columns */ Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */ int *pbMatch /* OUT: Set to true if really a match */ ){ @@ -414,13 +402,7 @@ static int fts5ExprPhraseIsMatch( Fts5PoslistReader *aIter = aStatic; int i; int rc = SQLITE_OK; - int iCol = -1; - if( pColset && pColset->nCol==1 ){ - iCol = pColset->aiCol[0]; - pColset = 0; - } - fts5BufferZero(&pPhrase->poslist); /* If the aStatic[] array is not large enough, allocate a large array @@ -440,12 +422,14 @@ static int fts5ExprPhraseIsMatch( int bFlag = 0; const u8 *a = 0; if( pTerm->pSynonym ){ - rc = fts5ExprSynonymPoslist(pTerm, pNode->iRowid, &bFlag, (u8**)&a, &n); + rc = fts5ExprSynonymPoslist( + pTerm, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n + ); }else{ - rc = sqlite3Fts5IterPoslist(pTerm->pIter, &a, &n, &dummy); + rc = sqlite3Fts5IterPoslist(pTerm->pIter, pColset, &a, &n, &dummy); } if( rc!=SQLITE_OK ) goto ismatch_out; - sqlite3Fts5PoslistReaderInit(iCol, a, n, &aIter[i]); + sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); aIter[i].bFlag = bFlag; if( aIter[i].bEof ) goto ismatch_out; } @@ -468,11 +452,9 @@ static int fts5ExprPhraseIsMatch( } }while( bMatch==0 ); - if( pColset==0 || fts5ExprColsetTest(pColset, FTS5_POS2COLUMN(iPos)) ){ - /* Append position iPos to the output */ - rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos); - if( rc!=SQLITE_OK ) goto ismatch_out; - } + /* Append position iPos to the output */ + rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos); + if( rc!=SQLITE_OK ) goto ismatch_out; for(i=0; inTerm; i++){ if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out; @@ -767,61 +749,6 @@ static int fts5ExprSynonymAdvanceto( return bEof; } -/* -** IN/OUT parameter (*pa) points to a position list n bytes in size. If -** the position list contains entries for column iCol, then (*pa) is set -** to point to the sub-position-list for that column and the number of -** bytes in it returned. Or, if the argument position list does not -** contain any entries for column iCol, return 0. -*/ -static int fts5ExprExtractCol( - const u8 **pa, /* IN/OUT: Pointer to poslist */ - int n, /* IN: Size of poslist in bytes */ - int iCol /* Column to extract from poslist */ -){ - int iCurrent = 0; - const u8 *p = *pa; - const u8 *pEnd = &p[n]; /* One byte past end of position list */ - u8 prev = 0; - - while( iCol!=iCurrent ){ - /* Advance pointer p until it points to pEnd or an 0x01 byte that is - ** not part of a varint */ - while( (prev & 0x80) || *p!=0x01 ){ - prev = *p++; - if( p==pEnd ) return 0; - } - *pa = p++; - p += fts5GetVarint32(p, iCurrent); - } - - /* Advance pointer p until it points to pEnd or an 0x01 byte that is - ** not part of a varint */ - assert( (prev & 0x80)==0 ); - while( pnCol; i++){ - const u8 *pSub = pPos; - int nSub = fts5ExprExtractCol(&pSub, nPos, pColset->aiCol[i]); - if( nSub ){ - fts5BufferAppendBlob(&rc, pBuf, nSub, pSub); - } - } - return rc; -} static int fts5ExprNearTest( int *pRc, @@ -868,35 +795,16 @@ static int fts5ExprTokenTest( Fts5ExprNearset *pNear = pNode->pNear; Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter; - Fts5ExprColset *pColset = pNear->pColset; - const u8 *pPos; - int nPos; + Fts5Colset *pColset = pNear->pColset; int rc; assert( pNode->eType==FTS5_TERM ); assert( pNear->nPhrase==1 && pPhrase->nTerm==1 ); assert( pPhrase->aTerm[0].pSynonym==0 ); - rc = sqlite3Fts5IterPoslist(pIter, &pPos, &nPos, &pNode->iRowid); - - /* If the term may match any column, then this must be a match. - ** Return immediately in this case. Otherwise, try to find the - ** part of the poslist that corresponds to the required column. - ** If it can be found, return. If it cannot, the next iteration - ** of the loop will test the next rowid in the database for this - ** term. */ - if( pColset==0 ){ - assert( pPhrase->poslist.nSpace==0 ); - pPhrase->poslist.p = (u8*)pPos; - pPhrase->poslist.n = nPos; - }else if( pColset->nCol==1 ){ - assert( pPhrase->poslist.nSpace==0 ); - pPhrase->poslist.n = fts5ExprExtractCol(&pPos, nPos, pColset->aiCol[0]); - pPhrase->poslist.p = (u8*)pPos; - }else if( rc==SQLITE_OK ){ - rc = fts5ExprExtractColset(pColset, pPos, nPos, &pPhrase->poslist); - } - + rc = sqlite3Fts5IterPoslist(pIter, pColset, + (const u8**)&pPhrase->poslist.p, &pPhrase->poslist.n, &pNode->iRowid + ); pNode->bNomatch = (pPhrase->poslist.n==0); return rc; } @@ -1002,6 +910,7 @@ static int fts5ExprNearInitAll( pExpr->pIndex, p->zTerm, strlen(p->zTerm), (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), + pNear->pColset, &p->pIter ); assert( rc==SQLITE_OK || p->pIter==0 ); @@ -1754,25 +1663,25 @@ void sqlite3Fts5ParseSetDistance( /* ** The second argument passed to this function may be NULL, or it may be -** an existing Fts5ExprColset object. This function returns a pointer to +** an existing Fts5Colset object. This function returns a pointer to ** a new colset object containing the contents of (p) with new value column ** number iCol appended. ** ** If an OOM error occurs, store an error code in pParse and return NULL. ** The old colset object (if any) is not freed in this case. */ -static Fts5ExprColset *fts5ParseColset( +static Fts5Colset *fts5ParseColset( Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ - Fts5ExprColset *p, /* Existing colset object */ + Fts5Colset *p, /* Existing colset object */ int iCol /* New column to add to colset object */ ){ int nCol = p ? p->nCol : 0; /* Num. columns already in colset object */ - Fts5ExprColset *pNew; /* New colset object to return */ + Fts5Colset *pNew; /* New colset object to return */ assert( pParse->rc==SQLITE_OK ); assert( iCol>=0 && iColpConfig->nCol ); - pNew = sqlite3_realloc(p, sizeof(Fts5ExprColset) + sizeof(int)*nCol); + pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol); if( pNew==0 ){ pParse->rc = SQLITE_NOMEM; }else{ @@ -1797,12 +1706,12 @@ static Fts5ExprColset *fts5ParseColset( return pNew; } -Fts5ExprColset *sqlite3Fts5ParseColset( +Fts5Colset *sqlite3Fts5ParseColset( Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ - Fts5ExprColset *pColset, /* Existing colset object */ + Fts5Colset *pColset, /* Existing colset object */ Fts5Token *p ){ - Fts5ExprColset *pRet = 0; + Fts5Colset *pRet = 0; int iCol; char *z; /* Dequoted copy of token p */ @@ -1832,7 +1741,7 @@ Fts5ExprColset *sqlite3Fts5ParseColset( void sqlite3Fts5ParseSetColset( Fts5Parse *pParse, Fts5ExprNearset *pNear, - Fts5ExprColset *pColset + Fts5Colset *pColset ){ if( pNear ){ pNear->pColset = pColset; @@ -2285,6 +2194,11 @@ int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0); } + /* Avoid a warning indicating that sqlite3Fts5ParserTrace() is unused */ +#ifndef NDEBUG + (void)sqlite3Fts5ParserTrace; +#endif + return rc; } @@ -2320,4 +2234,3 @@ int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){ } return nRet; } - diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index b8aa52c2f8..6c731432fe 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -291,7 +291,7 @@ struct Fts5Index { int nMaxPendingData; /* Max pending data before flush to disk */ int nPendingData; /* Current bytes of pending data */ i64 iWriteRowid; /* Rowid for current doc being written */ - Fts5Buffer scratch; + int bDelete; /* Current write is a delete */ /* Error state. */ int rc; /* Current error code */ @@ -307,14 +307,13 @@ struct Fts5Index { }; struct Fts5DoclistIter { - u8 *a; - int n; - int i; + u8 *aEof; /* Pointer to 1 byte past end of doclist */ /* Output variables. aPoslist==0 at EOF */ i64 iRowid; u8 *aPoslist; int nPoslist; + int nSize; }; /* @@ -512,8 +511,9 @@ struct Fts5IndexIter { int nSeg; /* Size of aSeg[] array */ int bRev; /* True to iterate in reverse order */ - int bSkipEmpty; /* True to skip deleted entries */ - int bEof; /* True at EOF */ + u8 bSkipEmpty; /* True to skip deleted entries */ + u8 bEof; /* True at EOF */ + u8 bFiltered; /* True if column-filter already applied */ i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ Fts5CResult *aFirst; /* Current merge state (see above) */ @@ -1458,7 +1458,8 @@ static void fts5SegIterNextPage( */ static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){ int nSz; - int n = fts5GetVarint32(p, nSz); + int n = 0; + fts5FastGetVarint32(p, n, nSz); assert_nc( nSz>=0 ); *pnSz = nSz/2; *pbDel = nSz & 0x0001; @@ -1479,13 +1480,12 @@ static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){ static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ if( p->rc==SQLITE_OK ){ int iOff = pIter->iLeafOffset; /* Offset to read at */ + int nSz; ASSERT_SZLEAF_OK(pIter->pLeaf); - if( iOff>=pIter->pLeaf->szLeaf ){ - p->rc = FTS5_CORRUPT; - }else{ - const u8 *a = &pIter->pLeaf->p[iOff]; - pIter->iLeafOffset += fts5GetPoslistSize(a, &pIter->nPos, &pIter->bDel); - } + fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz); + pIter->bDel = (nSz & 0x0001); + pIter->nPos = nSz>>1; + pIter->iLeafOffset = iOff; } } @@ -1780,6 +1780,7 @@ static void fts5SegIterNext( pIter->iEndofDoclist = nList+1; sqlite3Fts5BufferSet(&p->rc, &pIter->term, strlen(zTerm), (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); + if( pbNewTerm ) *pbNewTerm = 1; } }else{ iOff = 0; @@ -1940,14 +1941,6 @@ static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){ pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno); } -#define fts5IndexGetVarint32(a, iOff, nVal) { \ - nVal = (a)[iOff++]; \ - if( nVal & 0x80 ){ \ - iOff--; \ - iOff += fts5GetVarint32(&(a)[iOff], nVal); \ - } \ -} - #define fts5IndexSkipVarint(a, iOff) { \ int iEnd = iOff+9; \ while( (a[iOff++] & 0x80) && iOffaSeg[1]; + pNew->bFiltered = 1; pIter->flags = FTS5_SEGITER_ONETERM; if( pData->szLeaf>0 ){ pIter->pLeaf = pData; @@ -3705,10 +3699,15 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){ return ret; } -#define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob) { \ - assert( pBuf->nSpace>=(pBuf->n+nBlob) ); \ - memcpy(&pBuf->p[pBuf->n], pBlob, nBlob); \ - pBuf->n += nBlob; \ +#define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob) { \ + assert( (pBuf)->nSpace>=((pBuf)->n+nBlob) ); \ + memcpy(&(pBuf)->p[(pBuf)->n], pBlob, nBlob); \ + (pBuf)->n += nBlob; \ +} + +#define fts5BufferSafeAppendVarint(pBuf, iVal) { \ + (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf)->n], (iVal)); \ + assert( (pBuf)->nSpace>=(pBuf)->n ); \ } /* @@ -3938,12 +3937,81 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){ static void fts5PoslistCallback( Fts5Index *p, - void *pCtx, + void *pContext, const u8 *pChunk, int nChunk ){ assert_nc( nChunk>=0 ); if( nChunk>0 ){ - fts5BufferAppendBlob(&p->rc, (Fts5Buffer*)pCtx, nChunk, pChunk); + fts5BufferSafeAppendBlob((Fts5Buffer*)pContext, pChunk, nChunk); + } +} + +typedef struct PoslistCallbackCtx PoslistCallbackCtx; +struct PoslistCallbackCtx { + Fts5Buffer *pBuf; /* Append to this buffer */ + Fts5Colset *pColset; /* Restrict matches to this column */ + int eState; /* See above */ +}; + +/* +** TODO: Make this more efficient! +*/ +static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){ + int i; + for(i=0; inCol; i++){ + if( pColset->aiCol[i]==iCol ) return 1; + } + return 0; +} + +static void fts5PoslistFilterCallback( + Fts5Index *p, + void *pContext, + const u8 *pChunk, int nChunk +){ + PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext; + assert_nc( nChunk>=0 ); + if( nChunk>0 ){ + /* Search through to find the first varint with value 1. This is the + ** start of the next columns hits. */ + int i = 0; + int iStart = 0; + + if( pCtx->eState==2 ){ + int iCol; + fts5FastGetVarint32(pChunk, i, iCol); + if( fts5IndexColsetTest(pCtx->pColset, iCol) ){ + pCtx->eState = 1; + fts5BufferSafeAppendVarint(pCtx->pBuf, 1); + }else{ + pCtx->eState = 0; + } + } + + do { + while( ieState ){ + fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart); + } + if( i=nChunk ){ + pCtx->eState = 2; + }else{ + fts5FastGetVarint32(pChunk, i, iCol); + pCtx->eState = fts5IndexColsetTest(pCtx->pColset, iCol); + if( pCtx->eState ){ + fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart); + iStart = i; + } + } + } + }while( irc, pBuf, pSeg->nPos) ){ + if( pColset==0 ){ + fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); + }else{ + PoslistCallbackCtx sCtx; + sCtx.pBuf = pBuf; + sCtx.pColset = pColset; + sCtx.eState = pColset ? fts5IndexColsetTest(pColset, 0) : 1; + assert( sCtx.eState==0 || sCtx.eState==1 ); + fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback); + } + } } /* -** Iterator pMulti currently points to a valid entry (not EOF). This -** function appends a copy of the position-list of the entry pMulti -** currently points to to buffer pBuf. -** -** If an error occurs, an error code is left in p->rc. It is assumed -** no error has already occurred when this function is called. +** IN/OUT parameter (*pa) points to a position list n bytes in size. If +** the position list contains entries for column iCol, then (*pa) is set +** to point to the sub-position-list for that column and the number of +** bytes in it returned. Or, if the argument position list does not +** contain any entries for column iCol, return 0. */ -static void fts5MultiIterPoslist( +static int fts5IndexExtractCol( + const u8 **pa, /* IN/OUT: Pointer to poslist */ + int n, /* IN: Size of poslist in bytes */ + int iCol /* Column to extract from poslist */ +){ + int iCurrent = 0; /* Anything before the first 0x01 is col 0 */ + const u8 *p = *pa; + const u8 *pEnd = &p[n]; /* One byte past end of position list */ + u8 prev = 0; + + while( iCol!=iCurrent ){ + /* Advance pointer p until it points to pEnd or an 0x01 byte that is + ** not part of a varint */ + while( (prev & 0x80) || *p!=0x01 ){ + prev = *p++; + if( p==pEnd ) return 0; + } + *pa = p++; + p += fts5GetVarint32(p, iCurrent); + } + + /* Advance pointer p until it points to pEnd or an 0x01 byte that is + ** not part of a varint */ + assert( (prev & 0x80)==0 ); + while( prc. +*/ +static int fts5AppendPoslist( Fts5Index *p, + i64 iDelta, Fts5IndexIter *pMulti, - int bSz, /* Append a size field before the data */ + Fts5Colset *pColset, Fts5Buffer *pBuf ){ if( p->rc==SQLITE_OK ){ Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ]; assert( fts5MultiIterEof(p, pMulti)==0 ); + assert( pSeg->nPos>0 ); + if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+9+9) ){ + int iSv1; + int iSv2; + int iData; + + /* Append iDelta */ + iSv1 = pBuf->n; + fts5BufferSafeAppendVarint(pBuf, iDelta); - if( bSz ){ /* WRITEPOSLISTSIZE */ - fts5BufferAppendVarint(&p->rc, pBuf, pSeg->nPos*2); + iSv2 = pBuf->n; + fts5BufferSafeAppendVarint(pBuf, pSeg->nPos*2); + iData = pBuf->n; + + if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf + && (pColset==0 || pColset->nCol==1) + ){ + const u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; + int nPos; + if( pColset ){ + nPos = fts5IndexExtractCol(&pPos, pSeg->nPos, pColset->aiCol[0]); + }else{ + nPos = pSeg->nPos; + } + fts5BufferSafeAppendBlob(pBuf, pPos, nPos); + }else{ + fts5SegiterPoslist(p, pSeg, pColset, pBuf); + } + + if( pColset ){ + int nActual = pBuf->n - iData; + if( nActual!=pSeg->nPos ){ + if( nActual==0 ){ + pBuf->n = iSv1; + return 1; + }else{ + int nReq = sqlite3Fts5GetVarintLen((u32)(nActual*2)); + while( iSv2<(iData-nReq) ){ pBuf->p[iSv2++] = 0x80; } + sqlite3Fts5PutVarint(&pBuf->p[iSv2], nActual*2); + } + } + } } - fts5SegiterPoslist(p, pSeg, pBuf); } + + return 0; } static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ - if( pIter->in ){ - int bDummy; - if( pIter->i ){ - i64 iDelta; - pIter->i += fts5GetVarint(&pIter->a[pIter->i], (u64*)&iDelta); - pIter->iRowid += iDelta; - }else{ - pIter->i += fts5GetVarint(&pIter->a[pIter->i], (u64*)&pIter->iRowid); - } - pIter->i += fts5GetPoslistSize( - &pIter->a[pIter->i], &pIter->nPoslist, &bDummy - ); - pIter->aPoslist = &pIter->a[pIter->i]; - pIter->i += pIter->nPoslist; - }else{ + u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; + + assert( pIter->aPoslist ); + if( p>=pIter->aEof ){ pIter->aPoslist = 0; + }else{ + i64 iDelta; + + p += fts5GetVarint(p, (u64*)&iDelta); + pIter->iRowid += iDelta; + + /* Read position list size */ + if( p[0] & 0x80 ){ + int nPos; + pIter->nSize = fts5GetVarint32(p, nPos); + pIter->nPoslist = (nPos>>1); + }else{ + pIter->nPoslist = ((int)(p[0])) >> 1; + pIter->nSize = 1; + } + + pIter->aPoslist = p; } } @@ -4012,27 +4183,34 @@ static void fts5DoclistIterInit( Fts5DoclistIter *pIter ){ memset(pIter, 0, sizeof(*pIter)); - pIter->a = pBuf->p; - pIter->n = pBuf->n; + pIter->aPoslist = pBuf->p; + pIter->aEof = &pBuf->p[pBuf->n]; fts5DoclistIterNext(pIter); } +#if 0 /* ** Append a doclist to buffer pBuf. +** +** This function assumes that space within the buffer has already been +** allocated. */ static void fts5MergeAppendDocid( - int *pRc, /* IN/OUT: Error code */ Fts5Buffer *pBuf, /* Buffer to write to */ i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */ i64 iRowid /* Rowid to append */ ){ - if( pBuf->n==0 ){ - fts5BufferAppendVarint(pRc, pBuf, iRowid); - }else{ - fts5BufferAppendVarint(pRc, pBuf, iRowid - *piLastRowid); - } + assert( pBuf->n!=0 || (*piLastRowid)==0 ); + fts5BufferSafeAppendVarint(pBuf, iRowid - *piLastRowid); *piLastRowid = iRowid; } +#endif + +#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ + assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ + fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ + (iLastRowid) = (iRowid); \ +} /* ** Buffers p1 and p2 contain doclists. This function merges the content @@ -4056,53 +4234,58 @@ static void fts5MergePrefixLists( memset(&out, 0, sizeof(out)); memset(&tmp, 0, sizeof(tmp)); + sqlite3Fts5BufferGrow(&p->rc, &out, p1->n + p2->n); fts5DoclistIterInit(p1, &i1); fts5DoclistIterInit(p2, &i2); while( p->rc==SQLITE_OK && (i1.aPoslist!=0 || i2.aPoslist!=0) ){ if( i2.aPoslist==0 || (i1.aPoslist && i1.iRowidrc, &out, &iLastRowid, i1.iRowid); - /* WRITEPOSLISTSIZE */ - fts5BufferAppendVarint(&p->rc, &out, i1.nPoslist * 2); - fts5BufferAppendBlob(&p->rc, &out, i1.nPoslist, i1.aPoslist); + fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid); + fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize); fts5DoclistIterNext(&i1); } else if( i1.aPoslist==0 || i2.iRowid!=i1.iRowid ){ /* Copy entry from i2 */ - fts5MergeAppendDocid(&p->rc, &out, &iLastRowid, i2.iRowid); - /* WRITEPOSLISTSIZE */ - fts5BufferAppendVarint(&p->rc, &out, i2.nPoslist * 2); - fts5BufferAppendBlob(&p->rc, &out, i2.nPoslist, i2.aPoslist); + fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); + fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize); fts5DoclistIterNext(&i2); } else{ - Fts5PoslistReader r1; - Fts5PoslistReader r2; - Fts5PoslistWriter writer; + i64 iPos1 = 0; + i64 iPos2 = 0; + int iOff1 = 0; + int iOff2 = 0; + u8 *a1 = &i1.aPoslist[i1.nSize]; + u8 *a2 = &i2.aPoslist[i2.nSize]; + Fts5PoslistWriter writer; memset(&writer, 0, sizeof(writer)); /* Merge the two position lists. */ - fts5MergeAppendDocid(&p->rc, &out, &iLastRowid, i2.iRowid); + fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid); fts5BufferZero(&tmp); - sqlite3Fts5PoslistReaderInit(-1, i1.aPoslist, i1.nPoslist, &r1); - sqlite3Fts5PoslistReaderInit(-1, i2.aPoslist, i2.nPoslist, &r2); - while( p->rc==SQLITE_OK && (r1.bEof==0 || r2.bEof==0) ){ + + sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); + sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); + + while( p->rc==SQLITE_OK && (iPos1>=0 || iPos2>=0) ){ i64 iNew; - if( r2.bEof || (r1.bEof==0 && r1.iPos=0 && iPos1rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew); } /* WRITEPOSLISTSIZE */ - fts5BufferAppendVarint(&p->rc, &out, tmp.n * 2); - fts5BufferAppendBlob(&p->rc, &out, tmp.n, tmp.p); + fts5BufferSafeAppendVarint(&out, tmp.n * 2); + fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); fts5DoclistIterNext(&i1); fts5DoclistIterNext(&i2); } @@ -4125,7 +4308,8 @@ static void fts5SetupPrefixIter( int bDesc, /* True for "ORDER BY rowid DESC" */ const u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ - Fts5IndexIter **ppIter /* OUT: New iterator */ + Fts5Colset *pColset, /* Restrict matches to these columns */ + Fts5IndexIter **ppIter /* OUT: New iterator */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; @@ -4164,14 +4348,18 @@ static void fts5SetupPrefixIter( fts5BufferZero(&aBuf[i]); } } + iLastRowid = 0; } - fts5MergeAppendDocid(&p->rc, &doclist, &iLastRowid, iRowid); - fts5MultiIterPoslist(p, p1, 1, &doclist); + if( !fts5AppendPoslist(p, iRowid-iLastRowid, p1, pColset, &doclist) ){ + iLastRowid = iRowid; + } } for(i=0; irc==SQLITE_OK ){ + fts5MergePrefixLists(p, &doclist, &aBuf[i]); + } fts5BufferFree(&aBuf[i]); } fts5MultiIterFree(p, p1); @@ -4195,7 +4383,7 @@ static void fts5SetupPrefixIter( ** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain ** to the document with rowid iRowid. */ -int sqlite3Fts5IndexBeginWrite(Fts5Index *p, i64 iRowid){ +int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ assert( p->rc==SQLITE_OK ); /* Allocate the hash table if it has not already been allocated */ @@ -4204,10 +4392,15 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, i64 iRowid){ } /* Flush the hash table to disk if required */ - if( iRowid<=p->iWriteRowid || (p->nPendingData > p->nMaxPendingData) ){ + if( iRowidiWriteRowid + || (iRowid==p->iWriteRowid && p->bDelete==0) + || (p->nPendingData > p->nMaxPendingData) + ){ fts5IndexFlush(p); } + p->iWriteRowid = iRowid; + p->bDelete = bDelete; return fts5IndexReturn(p); } @@ -4306,7 +4499,6 @@ int sqlite3Fts5IndexClose(Fts5Index *p){ sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); sqlite3Fts5HashFree(p->pHash); - sqlite3Fts5BufferFree(&p->scratch); sqlite3_free(p->zDataTbl); sqlite3_free(p); } @@ -4367,6 +4559,7 @@ int sqlite3Fts5IndexWrite( Fts5Config *pConfig = p->pConfig; assert( p->rc==SQLITE_OK ); + assert( (iCol<0)==p->bDelete ); /* Add the entry to the main terms index. */ rc = sqlite3Fts5HashWrite( @@ -4393,6 +4586,7 @@ int sqlite3Fts5IndexQuery( Fts5Index *p, /* FTS index to query */ const char *pToken, int nToken, /* Token (or prefix) to query for */ int flags, /* Mask of FTS5INDEX_QUERY_X flags */ + Fts5Colset *pColset, /* Match these columns only */ Fts5IndexIter **ppIter /* OUT: New iterator object */ ){ Fts5Config *pConfig = p->pConfig; @@ -4436,7 +4630,7 @@ int sqlite3Fts5IndexQuery( }else{ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0; buf.p[0] = FTS5_MAIN_PREFIX; - fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, &pRet); + fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet); } if( p->rc ){ @@ -4516,6 +4710,26 @@ const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIter, int *pn){ } +static int fts5IndexExtractColset ( + Fts5Colset *pColset, /* Colset to filter on */ + const u8 *pPos, int nPos, /* Position list */ + Fts5Buffer *pBuf /* Output buffer */ +){ + int rc = SQLITE_OK; + int i; + + fts5BufferZero(pBuf); + for(i=0; inCol; i++){ + const u8 *pSub = pPos; + int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]); + if( nSub ){ + fts5BufferAppendBlob(&rc, pBuf, nSub, pSub); + } + } + return rc; +} + + /* ** Return a pointer to a buffer containing a copy of the position list for ** the current entry. Output variable *pn is set to the size of the buffer @@ -4526,6 +4740,7 @@ const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIter, int *pn){ */ int sqlite3Fts5IterPoslist( Fts5IndexIter *pIter, + Fts5Colset *pColset, /* Column filter (or NULL) */ const u8 **pp, /* OUT: Pointer to position-list data */ int *pn, /* OUT: Size of position-list in bytes */ i64 *piRowid /* OUT: Current rowid */ @@ -4533,13 +4748,25 @@ int sqlite3Fts5IterPoslist( Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; assert( pIter->pIndex->rc==SQLITE_OK ); *piRowid = pSeg->iRowid; - *pn = pSeg->nPos; - if( pSeg->iLeafOffset+pSeg->nPos <= pSeg->pLeaf->szLeaf ){ - *pp = &pSeg->pLeaf->p[pSeg->iLeafOffset]; + if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){ + u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; + if( pColset==0 || pIter->bFiltered ){ + *pn = pSeg->nPos; + *pp = pPos; + }else if( pColset->nCol==1 ){ + *pp = pPos; + *pn = fts5IndexExtractCol(pp, pSeg->nPos, pColset->aiCol[0]); + }else{ + fts5BufferZero(&pIter->poslist); + fts5IndexExtractColset(pColset, pPos, pSeg->nPos, &pIter->poslist); + *pp = pIter->poslist.p; + *pn = pIter->poslist.n; + } }else{ fts5BufferZero(&pIter->poslist); - fts5SegiterPoslist(pIter->pIndex, pSeg, &pIter->poslist); + fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist); *pp = pIter->poslist.p; + *pn = pIter->poslist.n; } return fts5IndexReturn(pIter->pIndex); } @@ -4551,10 +4778,10 @@ int sqlite3Fts5IterPoslist( */ int sqlite3Fts5IterPoslistBuffer(Fts5IndexIter *pIter, Fts5Buffer *pBuf){ Fts5Index *p = pIter->pIndex; - + Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; assert( p->rc==SQLITE_OK ); fts5BufferZero(pBuf); - fts5MultiIterPoslist(p, pIter, 0, pBuf); + fts5SegiterPoslist(p, pSeg, 0, pBuf); return fts5IndexReturn(p); } @@ -4729,17 +4956,17 @@ static int fts5QueryCksum( ){ u64 cksum = *pCksum; Fts5IndexIter *pIdxIter = 0; - int rc = sqlite3Fts5IndexQuery(p, z, n, flags, &pIdxIter); + int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIdxIter); while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIdxIter) ){ i64 dummy; const u8 *pPos; int nPos; i64 rowid = sqlite3Fts5IterRowid(pIdxIter); - rc = sqlite3Fts5IterPoslist(pIdxIter, &pPos, &nPos, &dummy); + rc = sqlite3Fts5IterPoslist(pIdxIter, 0, &pPos, &nPos, &dummy); if( rc==SQLITE_OK ){ Fts5PoslistReader sReader; - for(sqlite3Fts5PoslistReaderInit(-1, pPos, nPos, &sReader); + for(sqlite3Fts5PoslistReaderInit(pPos, nPos, &sReader); sReader.bEof==0; sqlite3Fts5PoslistReaderNext(&sReader) ){ @@ -5103,7 +5330,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ fts5TestTerm(p, &term, z, n, cksum2, &cksum3); poslist.n = 0; - fts5MultiIterPoslist(p, pIter, 0, &poslist); + fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst] , 0, &poslist); while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ int iCol = FTS5_POS2COLUMN(iPos); int iTokOff = FTS5_POS2OFFSET(iPos); @@ -5243,6 +5470,29 @@ static void fts5DecodeStructure( fts5StructureRelease(p); } +/* +** This is part of the fts5_decode() debugging aid. +** +** Arguments pBlob/nBlob contain an "averages" record. This function +** appends a human-readable representation of record to the buffer passed +** as the second argument. +*/ +static void fts5DecodeAverages( + int *pRc, /* IN/OUT: error code */ + Fts5Buffer *pBuf, + const u8 *pBlob, int nBlob +){ + int i = 0; + const char *zSpace = ""; + + while( iflags. Unless this +** extension is currently being used by a version of SQLite too old to +** support index-info flags. In that case this function is a no-op. +*/ +static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ +#if SQLITE_VERSION_NUMBER>=3008012 + if( sqlite3_libversion_number()>=3008012 ){ + pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE; + } +#endif +} + /* ** Implementation of the xBestIndex method for FTS5 tables. Within the ** WHERE constraint, it searches for the following: @@ -492,8 +505,10 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ int omit; /* True to omit this if found */ int iConsIndex; /* Index in pInfo->aConstraint[] */ } aConstraint[] = { - {SQLITE_INDEX_CONSTRAINT_MATCH, FTS5_BI_MATCH, 1, 1, -1}, - {SQLITE_INDEX_CONSTRAINT_MATCH, FTS5_BI_RANK, 2, 1, -1}, + {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ, + FTS5_BI_MATCH, 1, 1, -1}, + {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ, + FTS5_BI_RANK, 2, 1, -1}, {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1}, {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE, FTS5_BI_ROWID_LE, 0, 0, -1}, @@ -546,6 +561,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH); if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){ pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0; + if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo); }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){ pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0; }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){ @@ -1284,15 +1300,14 @@ static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ */ static int fts5SpecialInsert( Fts5Table *pTab, /* Fts5 table object */ - sqlite3_value *pCmd, /* Value inserted into special column */ + const char *zCmd, /* Text inserted into table-name column */ sqlite3_value *pVal /* Value inserted into rank column */ ){ Fts5Config *pConfig = pTab->pConfig; - const char *z = (const char*)sqlite3_value_text(pCmd); int rc = SQLITE_OK; int bError = 0; - if( 0==sqlite3_stricmp("delete-all", z) ){ + if( 0==sqlite3_stricmp("delete-all", zCmd) ){ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ fts5SetVtabError(pTab, "'delete-all' may only be used with a " @@ -1302,7 +1317,7 @@ static int fts5SpecialInsert( }else{ rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); } - }else if( 0==sqlite3_stricmp("rebuild", z) ){ + }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ if( pConfig->eContent==FTS5_CONTENT_NONE ){ fts5SetVtabError(pTab, "'rebuild' may not be used with a contentless fts5 table" @@ -1311,27 +1326,27 @@ static int fts5SpecialInsert( }else{ rc = sqlite3Fts5StorageRebuild(pTab->pStorage); } - }else if( 0==sqlite3_stricmp("optimize", z) ){ + }else if( 0==sqlite3_stricmp("optimize", zCmd) ){ rc = sqlite3Fts5StorageOptimize(pTab->pStorage); - }else if( 0==sqlite3_stricmp("merge", z) ){ + }else if( 0==sqlite3_stricmp("merge", zCmd) ){ int nMerge = sqlite3_value_int(pVal); rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge); - }else if( 0==sqlite3_stricmp("integrity-check", z) ){ + }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage); #ifdef SQLITE_DEBUG - }else if( 0==sqlite3_stricmp("prefix-index", z) ){ + }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ pConfig->bPrefixIndex = sqlite3_value_int(pVal); #endif }else{ rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, z, pVal, &bError); + rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError); } if( rc==SQLITE_OK ){ if( bError ){ rc = SQLITE_ERROR; }else{ - rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal, 0); + rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0); } } } @@ -1352,10 +1367,35 @@ static int fts5SpecialDelete( return rc; } +static void fts5StorageInsert( + int *pRc, + Fts5Table *pTab, + sqlite3_value **apVal, + i64 *piRowid +){ + int rc = *pRc; + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); + } + *pRc = rc; +} + /* ** This function is the implementation of the xUpdate callback used by ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be ** inserted, updated or deleted. +** +** A delete specifies a single argument - the rowid of the row to remove. +** +** Update and insert operations pass: +** +** 1. The "old" rowid, or NULL. +** 2. The "new" rowid. +** 3. Values for each of the nCol matchable columns. +** 4. Values for the two hidden columns ( and "rank"). */ static int fts5UpdateMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ @@ -1366,65 +1406,108 @@ static int fts5UpdateMethod( Fts5Table *pTab = (Fts5Table*)pVtab; Fts5Config *pConfig = pTab->pConfig; int eType0; /* value_type() of apVal[0] */ - int eConflict; /* ON CONFLICT for this DML */ int rc = SQLITE_OK; /* Return code */ /* A transaction must be open when this is called. */ assert( pTab->ts.eState==1 ); + assert( pVtab->zErrMsg==0 ); + assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); + assert( nArg==1 + || sqlite3_value_type(apVal[1])==SQLITE_INTEGER + || sqlite3_value_type(apVal[1])==SQLITE_NULL + ); assert( pTab->pConfig->pzErrmsg==0 ); pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; - /* A delete specifies a single argument - the rowid of the row to remove. - ** Update and insert operations pass: - ** - ** 1. The "old" rowid, or NULL. - ** 2. The "new" rowid. - ** 3. Values for each of the nCol matchable columns. - ** 4. Values for the two hidden columns ( and "rank"). - */ + /* Put any active cursors into REQUIRE_SEEK state. */ + fts5TripCursors(pTab); eType0 = sqlite3_value_type(apVal[0]); - eConflict = sqlite3_vtab_on_conflict(pConfig->db); + if( eType0==SQLITE_NULL + && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL + ){ + /* A "special" INSERT op. These are handled separately. */ + const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]); + if( pConfig->eContent!=FTS5_CONTENT_NORMAL + && 0==sqlite3_stricmp("delete", z) + ){ + rc = fts5SpecialDelete(pTab, apVal, pRowid); + }else{ + rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); + } + }else{ + /* A regular INSERT, UPDATE or DELETE statement. The trick here is that + ** any conflict on the rowid value must be detected before any + ** modifications are made to the database file. There are 4 cases: + ** + ** 1) DELETE + ** 2) UPDATE (rowid not modified) + ** 3) UPDATE (rowid modified) + ** 4) INSERT + ** + ** Cases 3 and 4 may violate the rowid constraint. + */ + int eConflict = sqlite3_vtab_on_conflict(pConfig->db); - assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); - assert( pVtab->zErrMsg==0 ); - assert( (nArg==1 && eType0==SQLITE_INTEGER) || nArg==(2+pConfig->nCol+2) ); + assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); + assert( nArg!=1 || eType0==SQLITE_INTEGER ); - fts5TripCursors(pTab); - if( eType0==SQLITE_INTEGER ){ - if( fts5IsContentless(pTab) ){ + /* Filter out attempts to run UPDATE or DELETE on contentless tables. + ** This is not suported. */ + if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ pTab->base.zErrMsg = sqlite3_mprintf( "cannot %s contentless fts5 table: %s", (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName ); rc = SQLITE_ERROR; - }else{ + } + + /* Case 1: DELETE */ + else if( nArg==1 ){ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); } - }else{ - sqlite3_value *pCmd = apVal[2 + pConfig->nCol]; - assert( nArg>1 ); - if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){ - const char *z = (const char*)sqlite3_value_text(pCmd); - if( pConfig->eContent!=FTS5_CONTENT_NORMAL - && 0==sqlite3_stricmp("delete", z) + + /* Case 2: INSERT */ + else if( eType0!=SQLITE_INTEGER ){ + /* If this is a REPLACE, first remove the current entry (if any) */ + if( eConflict==SQLITE_REPLACE + && sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ - rc = fts5SpecialDelete(pTab, apVal, pRowid); - }else{ - rc = fts5SpecialInsert(pTab, pCmd, apVal[2 + pConfig->nCol + 1]); + i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + } + + /* Case 2: UPDATE */ + else{ + i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ + i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ + if( iOld!=iNew ){ + if( eConflict==SQLITE_REPLACE ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + }else{ + rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid); + } + } + }else{ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); + fts5StorageInsert(&rc, pTab, apVal, pRowid); } - goto update_method_out; } } - - if( rc==SQLITE_OK && nArg>1 ){ - rc = sqlite3Fts5StorageInsert(pTab->pStorage, apVal, eConflict, pRowid); - } - - update_method_out: pTab->pConfig->pzErrmsg = 0; return rc; } @@ -1560,7 +1643,7 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ for(i=0; idb, zName, -1); if( rc==SQLITE_OK ){ Fts5Auxiliary *pAux; + int nName; /* Size of zName in bytes, including \0 */ int nByte; /* Bytes of space to allocate */ - nByte = sizeof(Fts5Auxiliary) + strlen(zName) + 1; + nName = (int)strlen(zName) + 1; + nByte = sizeof(Fts5Auxiliary) + nName; pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte); if( pAux ){ memset(pAux, 0, nByte); pAux->zFunc = (char*)&pAux[1]; - strcpy(pAux->zFunc, zName); + memcpy(pAux->zFunc, zName, nName); pAux->pGlobal = pGlobal; pAux->pUserData = pUserData; pAux->xFunc = xFunc; @@ -2167,15 +2252,17 @@ static int fts5CreateTokenizer( ){ Fts5Global *pGlobal = (Fts5Global*)pApi; Fts5TokenizerModule *pNew; + int nName; /* Size of zName and its \0 terminator */ int nByte; /* Bytes of space to allocate */ int rc = SQLITE_OK; - nByte = sizeof(Fts5TokenizerModule) + strlen(zName) + 1; + nName = (int)strlen(zName) + 1; + nByte = sizeof(Fts5TokenizerModule) + nName; pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte); if( pNew ){ memset(pNew, 0, nByte); pNew->zName = (char*)&pNew[1]; - strcpy(pNew->zName, zName); + memcpy(pNew->zName, zName, nName); pNew->pUserData = pUserData; pNew->x = *pTokenizer; pNew->xDestroy = xDestroy; @@ -2310,14 +2397,7 @@ static void fts5SourceIdFunc( sqlite3_result_text(pCtx, "--FTS5-SOURCE-ID--", -1, SQLITE_TRANSIENT); } -#ifdef _WIN32 -__declspec(dllexport) -#endif -int sqlite3_fts5_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ +static int fts5Init(sqlite3 *db){ static const sqlite3_module fts5Mod = { /* iVersion */ 2, /* xCreate */ fts5CreateMethod, @@ -2347,8 +2427,6 @@ int sqlite3_fts5_init( int rc; Fts5Global *pGlobal = 0; - SQLITE_EXTENSION_INIT2(pApi); - pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); if( pGlobal==0 ){ rc = SQLITE_NOMEM; @@ -2380,6 +2458,16 @@ int sqlite3_fts5_init( return rc; } +/* +** The following functions are used to register the module with SQLite. If +** this module is being built as part of the SQLite core (SQLITE_CORE is +** defined), then sqlite3_open() will call sqlite3Fts5Init() directly. +** +** Or, if this module is being built as a loadable extension, +** sqlite3Fts5Init() is omitted and the two standard entry points +** sqlite3_fts_init() and sqlite3_fts5_init() defined instead. +*/ +#ifndef SQLITE_CORE #ifdef _WIN32 __declspec(dllexport) #endif @@ -2388,7 +2476,25 @@ int sqlite3_fts_init( char **pzErrMsg, const sqlite3_api_routines *pApi ){ - return sqlite3_fts5_init(db, pzErrMsg, pApi); + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + return fts5Init(db); } - +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_fts5_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + return fts5Init(db); +} +#else +int sqlite3Fts5Init(sqlite3 *db){ + return fts5Init(db); +} +#endif diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c index 65c6d48a36..9f19e561b4 100644 --- a/ext/fts5/fts5_storage.c +++ b/ext/fts5/fts5_storage.c @@ -392,7 +392,7 @@ static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){ Fts5InsertCtx ctx; ctx.pStorage = p; ctx.iCol = -1; - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel); + rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ if( pConfig->abUnindexed[iCol-1] ) continue; ctx.szCol = 0; @@ -549,7 +549,7 @@ int sqlite3Fts5StorageSpecialDelete( ctx.pStorage = p; ctx.iCol = -1; - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel); + rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); for(iCol=0; rc==SQLITE_OK && iColnCol; iCol++){ if( pConfig->abUnindexed[iCol] ) continue; ctx.szCol = 0; @@ -639,7 +639,7 @@ int sqlite3Fts5StorageRebuild(Fts5Storage *p){ i64 iRowid = sqlite3_column_int64(pScan, 0); sqlite3Fts5BufferZero(&buf); - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iRowid); + rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ @@ -705,58 +705,69 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ } /* -** Insert a new row into the FTS table. +** Insert a new row into the FTS content table. */ -int sqlite3Fts5StorageInsert( - Fts5Storage *p, /* Storage module to write to */ - sqlite3_value **apVal, /* Array of values passed to xUpdate() */ - int eConflict, /* on conflict clause */ - i64 *piRowid /* OUT: rowid of new record */ +int sqlite3Fts5StorageContentInsert( + Fts5Storage *p, + sqlite3_value **apVal, + i64 *piRowid +){ + Fts5Config *pConfig = p->pConfig; + int rc = SQLITE_OK; + + /* Insert the new row into the %_content table. */ + if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ + if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ + *piRowid = sqlite3_value_int64(apVal[1]); + }else{ + rc = fts5StorageNewRowid(p, piRowid); + } + }else{ + sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ + int i; /* Counter variable */ +#if 0 + if( eConflict==SQLITE_REPLACE ){ + eStmt = FTS5_STMT_REPLACE_CONTENT; + rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1])); + }else{ + eStmt = FTS5_STMT_INSERT_CONTENT; + } +#endif + if( rc==SQLITE_OK ){ + rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); + } + for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ + rc = sqlite3_bind_value(pInsert, i, apVal[i]); + } + if( rc==SQLITE_OK ){ + sqlite3_step(pInsert); + rc = sqlite3_reset(pInsert); + } + *piRowid = sqlite3_last_insert_rowid(pConfig->db); + } + + return rc; +} + +/* +** Insert new entries into the FTS index and %_docsize table. +*/ +int sqlite3Fts5StorageIndexInsert( + Fts5Storage *p, + sqlite3_value **apVal, + i64 iRowid ){ Fts5Config *pConfig = p->pConfig; int rc = SQLITE_OK; /* Return code */ - sqlite3_stmt *pInsert = 0; /* Statement used to write %_content table */ - int eStmt = 0; /* Type of statement used on %_content */ - int i; /* Counter variable */ Fts5InsertCtx ctx; /* Tokenization callback context object */ Fts5Buffer buf; /* Buffer used to build up %_docsize blob */ memset(&buf, 0, sizeof(Fts5Buffer)); + ctx.pStorage = p; rc = fts5StorageLoadTotals(p, 1); - /* Insert the new row into the %_content table. */ if( rc==SQLITE_OK ){ - if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ - if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ - *piRowid = sqlite3_value_int64(apVal[1]); - }else{ - rc = fts5StorageNewRowid(p, piRowid); - } - }else{ - if( eConflict==SQLITE_REPLACE ){ - eStmt = FTS5_STMT_REPLACE_CONTENT; - rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1])); - }else{ - eStmt = FTS5_STMT_INSERT_CONTENT; - } - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, eStmt, &pInsert, 0); - } - for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ - rc = sqlite3_bind_value(pInsert, i, apVal[i]); - } - if( rc==SQLITE_OK ){ - sqlite3_step(pInsert); - rc = sqlite3_reset(pInsert); - } - *piRowid = sqlite3_last_insert_rowid(pConfig->db); - } - } - - /* Add new entries to the FTS index */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, *piRowid); - ctx.pStorage = p; + rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); } for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ ctx.szCol = 0; @@ -776,7 +787,7 @@ int sqlite3Fts5StorageInsert( /* Write the %_docsize record */ if( rc==SQLITE_OK ){ - rc = fts5StorageInsertDocsize(p, *piRowid, &buf); + rc = fts5StorageInsertDocsize(p, iRowid, &buf); } sqlite3_free(buf.p); diff --git a/ext/fts5/fts5_tcl.c b/ext/fts5/fts5_tcl.c index 21ce5f7bde..b470c557d3 100644 --- a/ext/fts5/fts5_tcl.c +++ b/ext/fts5/fts5_tcl.c @@ -976,7 +976,6 @@ static int f5tTokenHash( int objc, Tcl_Obj *CONST objv[] ){ - int bOld = sqlite3_fts5_may_be_corrupt; char *z; int n; unsigned int iVal; diff --git a/ext/fts5/fts5_vocab.c b/ext/fts5/fts5_vocab.c index bdf2e36b63..21b09448b1 100644 --- a/ext/fts5/fts5_vocab.c +++ b/ext/fts5/fts5_vocab.c @@ -349,7 +349,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ i64 iPos = 0; /* 64-bit position read from poslist */ int iOff = 0; /* Current offset within position list */ - rc = sqlite3Fts5IterPoslist(pCsr->pIter, &pPos, &nPos, &dummy); + rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy); if( rc==SQLITE_OK ){ if( pTab->eType==FTS5_VOCAB_ROW ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ @@ -402,7 +402,7 @@ static int fts5VocabFilterMethod( const int flags = FTS5INDEX_QUERY_SCAN; fts5VocabResetCursor(pCsr); - rc = sqlite3Fts5IndexQuery(pCsr->pIndex, 0, 0, flags, &pCsr->pIter); + rc = sqlite3Fts5IndexQuery(pCsr->pIndex, 0, 0, flags, 0, &pCsr->pIter); if( rc==SQLITE_OK ){ rc = fts5VocabNextMethod(pCursor); } diff --git a/ext/fts5/fts5parse.y b/ext/fts5/fts5parse.y index c880dc92cb..065af77053 100644 --- a/ext/fts5/fts5parse.y +++ b/ext/fts5/fts5parse.y @@ -67,6 +67,7 @@ %left COLON. input ::= expr(X). { sqlite3Fts5ParseFinished(pParse, X); } +%destructor input { (void)pParse; } %type cnearset {Fts5ExprNode*} %type expr {Fts5ExprNode*} @@ -101,9 +102,9 @@ cnearset(A) ::= colset(X) COLON nearset(Y). { A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y); } -%type colset {Fts5ExprColset*} +%type colset {Fts5Colset*} %destructor colset { sqlite3_free($$); } -%type colsetlist {Fts5ExprColset*} +%type colsetlist {Fts5Colset*} %destructor colsetlist { sqlite3_free($$); } colset(A) ::= LCP colsetlist(X) RCP. { A = X; } diff --git a/ext/fts5/test/fts5_common.tcl b/ext/fts5/test/fts5_common.tcl index 5eba1296ad..2c64b3b9a4 100644 --- a/ext/fts5/test/fts5_common.tcl +++ b/ext/fts5/test/fts5_common.tcl @@ -17,7 +17,6 @@ source $testdir/tester.tcl catch { sqlite3_fts5_may_be_corrupt 0 - append G(perm:dbconfig) "; load_static_extension \$::dbhandle fts5" reset_db } diff --git a/ext/fts5/test/fts5al.test b/ext/fts5/test/fts5al.test index efad1b2069..b8f8c6ebcd 100644 --- a/ext/fts5/test/fts5al.test +++ b/ext/fts5/test/fts5al.test @@ -251,7 +251,6 @@ do_execsql_test 4.3.1 { INSERT INTO t3 VALUES('a five'); INSERT INTO t3(t3, rank) VALUES('rank', 'bm25()'); } -breakpoint do_execsql_test 4.3.2 { SELECT * FROM t3 @@ -260,6 +259,7 @@ do_execsql_test 4.3.2 { } { {a four} {a one} {a five} {a two} {a three} } + do_execsql_test 4.3.3 { SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH 'rowidmod(3)' @@ -268,6 +268,18 @@ do_execsql_test 4.3.3 { {a three} 0 {a one} 1 {a four} 1 {a two} 2 {a five} 2 } +do_execsql_test 4.3.4 { + SELECT * FROM t3('a', 'rowidmod(4)') ORDER BY rank ASC; +} { + {a four} {a one} {a five} {a two} {a three} +} + +do_execsql_test 4.3.5 { + SELECT *, rank FROM t3('a', 'rowidmod(3)') ORDER BY rank ASC +} { + {a three} 0 {a one} 1 {a four} 1 {a two} 2 {a five} 2 +} + do_catchsql_test 4.4.3 { SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH 'xyz(3)' } {1 {no such function: xyz}} diff --git a/ext/fts5/test/fts5fault6.test b/ext/fts5/test/fts5fault6.test index c79cf7ab70..118761fab5 100644 --- a/ext/fts5/test/fts5fault6.test +++ b/ext/fts5/test/fts5fault6.test @@ -284,8 +284,6 @@ breakpoint do_faultsim_test 6 -faults oom* -prep { sqlite_orig db test.db sqlite3_db_config_lookaside db 0 0 0 -} -body { - load_static_extension db fts5 } -test { faultsim_test_result {0 {}} {1 {initialization of fts5 failed: }} if {$testrc==0} { diff --git a/ext/fts5/test/fts5onepass.test b/ext/fts5/test/fts5onepass.test new file mode 100644 index 0000000000..a614b780a2 --- /dev/null +++ b/ext/fts5/test/fts5onepass.test @@ -0,0 +1,181 @@ +# 2015 Sep 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5onepass + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts5(content); + INSERT INTO ft(rowid, content) VALUES(1, '1 2 3'); + INSERT INTO ft(rowid, content) VALUES(2, '4 5 6'); + INSERT INTO ft(rowid, content) VALUES(3, '7 8 9'); +} + +#------------------------------------------------------------------------- +# Check that UPDATE and DELETE statements that feature "WHERE rowid=?" or +# or "WHERE rowid=?" clauses do not use statement journals. But that other +# DELETE and UPDATE statements do. +# +# Note: "MATCH ? AND rowid=?" does use a statement journal. +# +foreach {tn sql uses} { + 1.1 { DELETE FROM ft } 1 + 1.2 { DELETE FROM ft WHERE rowid=? } 0 + 1.3 { DELETE FROM ft WHERE rowid=? } 0 + 1.4 { DELETE FROM ft WHERE ft MATCH '1' } 1 + 1.5 { DELETE FROM ft WHERE ft MATCH '1' AND rowid=? } 1 + 1.6 { DELETE FROM ft WHERE ft MATCH '1' AND rowid=? } 1 + + 2.1 { UPDATE ft SET content='a b c' } 1 + 2.2 { UPDATE ft SET content='a b c' WHERE rowid=? } 0 + 2.3 { UPDATE ft SET content='a b c' WHERE rowid=? } 0 + 2.4 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' } 1 + 2.5 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND rowid=? } 1 + 2.6 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND rowid=? } 1 +} { + do_test 1.$tn { sql_uses_stmt db $sql } $uses +} + +#------------------------------------------------------------------------- +# Check that putting a "DELETE/UPDATE ... WHERE rowid=?" statement in a +# trigger program does not prevent the VM from using a statement +# transaction. Even if the calling statement cannot hit a constraint. +# +do_execsql_test 2.0 { + CREATE TABLE t1(x); + + CREATE TRIGGER t1_ai AFTER INSERT ON t1 BEGIN + DELETE FROM ft WHERE rowid=new.x; + END; + + CREATE TRIGGER t1_ad AFTER DELETE ON t1 BEGIN + UPDATE ft SET content = 'a b c' WHERE rowid=old.x; + END; + + CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 BEGIN + DELETE FROM ft WHERE rowid=old.x; + END; +} + +foreach {tn sql uses} { + 1 { INSERT INTO t1 VALUES(1) } 1 + 2 { DELETE FROM t1 WHERE x=4 } 1 + 3 { UPDATE t1 SET x=10 WHERE x=11 } 1 +} { + do_test 2.$tn { sql_uses_stmt db $sql } $uses +} + +#------------------------------------------------------------------------- +# Test that an "UPDATE ... WHERE rowid=?" works and does not corrupt the +# index when it strikes a constraint. Both inside and outside a +# transaction. +# +foreach {tn tcl1 tcl2} { + 1 {} {} + + 2 { + execsql BEGIN + } { + if {[sqlite3_get_autocommit db]==1} { error "transaction rolled back!" } + execsql COMMIT + } +} { + + do_execsql_test 3.$tn.0 { + DROP TABLE IF EXISTS ft2; + CREATE VIRTUAL TABLE ft2 USING fts5(content); + INSERT INTO ft2(rowid, content) VALUES(1, 'a b c'); + INSERT INTO ft2(rowid, content) VALUES(2, 'a b d'); + INSERT INTO ft2(rowid, content) VALUES(3, 'a b e'); + } + + eval $tcl1 + foreach {tn2 sql content} { + 1 { UPDATE ft2 SET rowid=2 WHERE rowid=1 } + { 1 {a b c} 2 {a b d} 3 {a b e} } + + 2 { + INSERT INTO ft2(rowid, content) VALUES(4, 'a b f'); + UPDATE ft2 SET rowid=5 WHERE rowid=4; + UPDATE ft2 SET rowid=3 WHERE rowid=5; + } { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + + 3 { + UPDATE ft2 SET rowid=3 WHERE rowid=4; -- matches 0 rows + UPDATE ft2 SET rowid=2 WHERE rowid=3; + } { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + + 4 { + INSERT INTO ft2(rowid, content) VALUES(4, 'a b g'); + UPDATE ft2 SET rowid=-1 WHERE rowid=4; + UPDATE ft2 SET rowid=3 WHERE rowid=-1; + } {-1 {a b g} 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + + 5 { + DELETE FROM ft2 WHERE rowid=451; + DELETE FROM ft2 WHERE rowid=-1; + UPDATE ft2 SET rowid = 2 WHERE rowid = 1; + } {1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + } { + do_catchsql_test 3.$tn.$tn2.a $sql {1 {constraint failed}} + do_execsql_test 3.$tn.$tn2.b { SELECT rowid, content FROM ft2 } $content + + do_execsql_test 3.$tn.$tn2.c { + INSERT INTO ft2(ft2) VALUES('integrity-check'); + } + } + eval $tcl2 +} + +#------------------------------------------------------------------------- +# Check that DELETE and UPDATE operations can be done without flushing +# the in-memory hash table to disk. +# +reset_db +do_execsql_test 4.1.1 { + CREATE VIRTUAL TABLE ttt USING fts5(x); + BEGIN; + INSERT INTO ttt(rowid, x) VALUES(1, 'a b c'); + INSERT INTO ttt(rowid, x) VALUES(2, 'a b c'); + INSERT INTO ttt(rowid, x) VALUES(3, 'a b c'); + COMMIT +} +do_test 4.1.2 { fts5_level_segs ttt } {1} + +do_execsql_test 4.2.1 { + BEGIN; + DELETE FROM ttt WHERE rowid=1; + DELETE FROM ttt WHERE rowid=3; + INSERT INTO ttt(rowid, x) VALUES(4, 'd e f'); + INSERT INTO ttt(rowid, x) VALUES(5, 'd e f'); + COMMIT; +} {} +do_test 4.2.2 { fts5_level_segs ttt } {2} + + +do_execsql_test 4.3.1 { + BEGIN; + UPDATE ttt SET x = 'd e f' WHERE rowid = 2; + UPDATE ttt SET x = 'A B C' WHERE rowid = 4; + INSERT INTO ttt(rowid, x) VALUES(6, 'd e f'); + COMMIT; +} {} +do_test 4.2.2 { fts5_level_segs ttt } {3} + +finish_test + diff --git a/ext/fts5/test/fts5phrase.test b/ext/fts5/test/fts5phrase.test new file mode 100644 index 0000000000..6dac684e83 --- /dev/null +++ b/ext/fts5/test/fts5phrase.test @@ -0,0 +1,119 @@ +# 2014 Jan 08 +# +# 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 focused on phrase queries. +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5phrase + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t3 USING fts5(a, b, c); + INSERT INTO t3 VALUES('d e a', 'd i j j f', 'i j i e b f h'); -- 1 + INSERT INTO t3 VALUES('g a e', 'f g i g a', 'h d g i g h c'); -- 2 + INSERT INTO t3 VALUES('e a d', 'e i h a f', 'c e h i f b i'); -- 3 + INSERT INTO t3 VALUES('a g c', 'd j d j c', 'c d f j i g j'); -- 4 + INSERT INTO t3 VALUES('b c b', 'j g c d f', 'j c j d g f b'); -- 5 + INSERT INTO t3 VALUES('j a d', 'e b i h h', 'c c f g d i d'); -- 6 + INSERT INTO t3 VALUES('a d f', 'h g i i i', 'e a g c i f b'); -- 7 + INSERT INTO t3 VALUES('g f d', 'f c g b j', 'b b h h h j j'); -- 8 + INSERT INTO t3 VALUES('f h g', 'c j f g j', 'd h d f e b h'); -- 9 + INSERT INTO t3 VALUES('f h d', 'c i a d b', 'g b j b a d e'); -- 10 + INSERT INTO t3 VALUES('j h h', 'j i h a g', 'd e i e a g j'); -- 11 + INSERT INTO t3 VALUES('a b e', 'h g a g c', 'h c a a d e g'); -- 12 + INSERT INTO t3 VALUES('a j g', 'i h i f i', 'a g h j g i b'); -- 13 + INSERT INTO t3 VALUES('j h e', 'f e d i e', 'i d c f e d c'); -- 14 + INSERT INTO t3 VALUES('d j d', 'd b i a c', 'g d h i d b e'); -- 15 + INSERT INTO t3 VALUES('h j e', 'e b b c f', 'j a f g h d j'); -- 16 + INSERT INTO t3 VALUES('c b j', 'c a b a i', 'h f i d a d c'); -- 17 + INSERT INTO t3 VALUES('e e d', 'i d f c c', 'g i d a f e a'); -- 18 + INSERT INTO t3 VALUES('e i g', 'e a b i h', 'i f d d a d f'); -- 19 + INSERT INTO t3 VALUES('h g f', 'b h h j d', 'i f d e g j a'); -- 20 + INSERT INTO t3 VALUES('e h f', 'j c b c f', 'j a j g h a c'); -- 21 + INSERT INTO t3 VALUES('d c h', 'b g i c e', 'i i c d e h i'); -- 22 + INSERT INTO t3 VALUES('a h i', 'a g d f f', 'e f i i b b h'); -- 23 + INSERT INTO t3 VALUES('d d g', 'c c b c g', 'g c h e b c e'); -- 24 + INSERT INTO t3 VALUES('a b b', 'b f a d i', 'd a h a b c i'); -- 25 + INSERT INTO t3 VALUES('a f d', 'a j e a h', 'j i h j a i f'); -- 26 + INSERT INTO t3 VALUES('d j d', 'h a d i a', 'h h f j h g a'); -- 27 + INSERT INTO t3 VALUES('g a e', 'd g f a g', 'i d b c g g j'); -- 28 + INSERT INTO t3 VALUES('j e h', 'g h j h g', 'd a e j a a h'); -- 29 + INSERT INTO t3 VALUES('e j e', 'g e j g c', 'f c e b e e a'); -- 30 + INSERT INTO t3 VALUES('h f f', 'i j g e c', 'j j f c a i j'); -- 31 + INSERT INTO t3 VALUES('a g c', 'c g d b i', 'g h c b a a f'); -- 32 + INSERT INTO t3 VALUES('c h i', 'j d h e e', 'a h i d c c j'); -- 33 + INSERT INTO t3 VALUES('d a c', 'e d d b j', 'c e b b h i h'); -- 34 + INSERT INTO t3 VALUES('d f h', 'c a f c c', 'j b b c c j f'); -- 35 + INSERT INTO t3 VALUES('b g h', 'g c c c f', 'c g c f h e e'); -- 36 + INSERT INTO t3 VALUES('f e a', 'b h f j h', 'j g h f d g f'); -- 37 + INSERT INTO t3 VALUES('h f a', 'a e i j g', 'f d a f d f c'); -- 38 + INSERT INTO t3 VALUES('f i c', 'f i i i i', 'e c f d h j f'); -- 39 + INSERT INTO t3 VALUES('h h d', 'd i e d i', 'd f e i a h a'); -- 40 + INSERT INTO t3 VALUES('f g c', 'd a f c h', 'b b g j c e g'); -- 41 + INSERT INTO t3 VALUES('h i h', 'h d j d e', 'e d b b i e g'); -- 42 + INSERT INTO t3 VALUES('b h i', 'j e i d a', 'j j h e e c a'); -- 43 + INSERT INTO t3 VALUES('g i g', 'f c c f d', 'a c i c a d a'); -- 44 + INSERT INTO t3 VALUES('c c f', 'a b j d b', 'c a e g f e c'); -- 45 + INSERT INTO t3 VALUES('d h j', 'g c b j d', 'e a h f h j g'); -- 46 + INSERT INTO t3 VALUES('a a d', 'j e j a i', 'i d c f f f b'); -- 47 + INSERT INTO t3 VALUES('b g j', 'e c i h f', 'd d h b g a d'); -- 48 + INSERT INTO t3 VALUES('c i a', 'a c c c c', 'e h i e h i e'); -- 49 + INSERT INTO t3 VALUES('f f c', 'f f b i i', 'f f a j e c i'); -- 50 +} + +proc pmatch {col expr} { + return [expr {[string first $expr $col]>=0}] +} +db func pmatch pmatch + +foreach {tn cols tokens} { + 1 a "c c" + 2 b "c c" + 3 c "c c" + 4 {a b c} "c c" + 5 {a b c} "b h" + 6 {a b} "b h" + 7 {a c} "b h" + 8 {c a} "b h" + 9 {c} "i e" + 10 {b} "i e" + 11 {a} "i e" +} { + set fts "{$cols}:[join $tokens +]" + set where [list] + foreach c $cols { lappend where "pmatch($c, '$tokens')" } + set where [join $where " OR "] + + set res [db eval "SELECT rowid FROM t3 WHERE $where"] + do_execsql_test "1.$tn.$fts->([llength $res] rows)" { + SELECT rowid FROM t3($fts) + } $res +} + +do_execsql_test 2.0 { + SELECT rowid, + highlight(t3, 0, '*', '*'), + highlight(t3, 1, '*', '*'), + highlight(t3, 2, '*', '*') + FROM t3('a:f+f') +} { + 31 {h *f f*} {i j g e c} {j j f c a i j} + 50 {*f f* c} {f f b i i} {f f a j e c i} +} + +finish_test + diff --git a/ext/fts5/test/fts5prefix.test b/ext/fts5/test/fts5prefix.test index 076ecaa09b..9da7821a54 100644 --- a/ext/fts5/test/fts5prefix.test +++ b/ext/fts5/test/fts5prefix.test @@ -62,6 +62,140 @@ foreach {tn q res} { do_execsql_test 2.3.$tn $q $res } +#------------------------------------------------------------------------- +# Check that prefix queries with: +# +# * a column filter, and +# * no prefix index. +# +# work Ok. +# +do_execsql_test 3.0 { + CREATE VIRTUAL TABLE t3 USING fts5(a, b, c); + INSERT INTO t3(t3, rank) VALUES('pgsz', 32); + BEGIN; + INSERT INTO t3 VALUES('acb ccc bba', 'cca bba bca', 'bbc ccc bca'); -- 1 + INSERT INTO t3 VALUES('cbb cac cab', 'abb aac bba', 'aab ccc cac'); -- 2 + INSERT INTO t3 VALUES('aac bcb aac', 'acb bcb caa', 'aca bab bca'); -- 3 + INSERT INTO t3 VALUES('aab ccb ccc', 'aca cba cca', 'aca aac cbb'); -- 4 + INSERT INTO t3 VALUES('bac aab bab', 'ccb bac cba', 'acb aba abb'); -- 5 + INSERT INTO t3 VALUES('bab abc ccb', 'acb cba abb', 'cbb aaa cab'); -- 6 + INSERT INTO t3 VALUES('cbb bbc baa', 'aab aca baa', 'bcc cca aca'); -- 7 + INSERT INTO t3 VALUES('abc bba abb', 'cac abc cba', 'acc aac cac'); -- 8 + INSERT INTO t3 VALUES('bbc bbc cab', 'bcb ccb cba', 'bcc cac acb'); -- 9 + COMMIT; +} + +foreach {tn match res} { + 1 "a : c*" {1 2 4 6 7 9} + 2 "b : c*" {1 3 4 5 6 8 9} + 3 "c : c*" {1 2 4 6 7 8 9} + 4 "a : b*" {1 3 5 6 7 8 9} + 5 "b : b*" {1 2 3 5 7 9} + 6 "c : b*" {1 3 7 9} + 7 "a : a*" {1 3 4 5 6 8} + 8 "b : a*" {2 3 4 6 7 8} + 9 "c : a*" {2 3 4 5 6 7 8 9} +} { + do_execsql_test 3.1.$tn { + SELECT rowid FROM t3($match) + } $res +} + +do_test 3.2 { + expr srand(0) + execsql { DELETE FROM t3 } + for {set i 0} {$i < 1000} {incr i} { + set a [fts5_rnddoc 3] + set b [fts5_rnddoc 8] + set c [fts5_rnddoc 20] + execsql { INSERT INTO t3 VALUES($a, $b, $c) } + } + execsql { INSERT INTO t3(t3) VALUES('integrity-check') } +} {} + +proc gmatch {col pattern} { + expr {[lsearch -glob $col $pattern]>=0} +} +db func gmatch gmatch + +proc ghl {col pattern} { + foreach t $col { + if {[string match $pattern $t]} { + lappend res "*$t*" + } else { + lappend res $t + } + } + set res +} +db func ghl ghl + +set COLS(a) 0 +set COLS(b) 1 +set COLS(c) 2 + +for {set x 0} {$x<2} {incr x} { + foreach {tn pattern} { + 1 {xa*} + 2 {xb*} + 3 {xc*} + 4 {xd*} + 5 {xe*} + 6 {xf*} + 7 {xg*} + 8 {xh*} + 9 {xi*} + 10 {xj*} + } { + foreach col {a b c} { + + # Check that the list of returned rowids is correct. + # + set res [db eval "SELECT rowid FROM t3 WHERE gmatch($col, '$pattern')"] + set query "$col : $pattern" + do_execsql_test 3.3.$x.$tn.$col.rowid { + SELECT rowid FROM t3($query); + } $res + + # Check that the highlight() function works. + # + set res [db eval \ + "SELECT ghl($col, '$pattern') FROM t3 WHERE gmatch($col, '$pattern')" + ] + set idx $COLS($col) + do_execsql_test 3.3.$x.$tn.$col.highlight { + SELECT highlight(t3, $idx, '*', '*') FROM t3($query); + } $res + } + + foreach colset {{a b} {b c} {c a} {a c} {b a}} { + # Check that the list of returned rowids is correct. + # + foreach {col1 col2} $colset {} + set expr "gmatch($col1, '$pattern') OR gmatch($col2, '$pattern')" + set res [db eval "SELECT rowid FROM t3 WHERE $expr"] + set query "{$colset} : $pattern" + do_execsql_test 3.3.$x.$tn.{$colset}.rowid { + SELECT rowid FROM t3($query); + } $res + + set resq "SELECT ghl($col1, '$pattern'), ghl($col2, '$pattern')" + append resq " FROM t3 WHERE $expr" + set res [db eval $resq] + set idx1 $COLS($col1) + set idx2 $COLS($col2) + do_execsql_test 3.3.$x.$tn.{$colset}.highlight { + SELECT highlight(t3, $idx1, '*', '*'), highlight(t3, $idx2, '*', '*') + FROM t3($query) + } $res + } + } + execsql { INSERT INTO t3(t3) VALUES('optimize') } + execsql { INSERT INTO t3(t3) VALUES('integrity-check') } +} + finish_test + diff --git a/ext/fts5/test/fts5simple.test b/ext/fts5/test/fts5simple.test index 6a980c1b19..9175e420a2 100644 --- a/ext/fts5/test/fts5simple.test +++ b/ext/fts5/test/fts5simple.test @@ -19,7 +19,6 @@ ifcapable !fts5 { return } -if 1 { #------------------------------------------------------------------------- # set doc "x x [string repeat {y } 50]z z" @@ -137,8 +136,6 @@ do_execsql_test 5.4 { SELECT rowid FROM tt WHERE tt MATCH 'a*'; } {1 2} -} - do_execsql_test 5.5 { DELETE FROM tt; BEGIN; @@ -184,6 +181,124 @@ do_catchsql_test 6.3 { SELECT * FROM xyz WHERE xyz MATCH NULL } {1 {fts5: syntax error near ""}} +#------------------------------------------------------------------------- + +do_execsql_test 7.1 { + CREATE VIRTUAL TABLE ft2 USING fts5(content); + INSERT INTO ft2(rowid, content) VALUES(1, 'a b c'); + INSERT INTO ft2(rowid, content) VALUES(2, 'a b d'); +} + +do_catchsql_test 7.2 { + BEGIN; + UPDATE ft2 SET rowid=2 WHERE rowid=1; +} {1 {constraint failed}} + +do_execsql_test 7.3 { + COMMIT; + INSERT INTO ft2(ft2) VALUES('integrity-check'); +} {} + +do_execsql_test 7.4 { + SELECT * FROM ft2; +} {{a b c} {a b d}} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 8.1 { + CREATE VIRTUAL TABLE ft2 USING fts5(content); + INSERT INTO ft2(rowid, content) VALUES(1, 'a b'); +} + +do_execsql_test 8.2 { + BEGIN; + INSERT INTO ft2(rowid, content) VALUES(4, 'a x'); +} + +do_execsql_test 8.3 { + INSERT INTO ft2(ft2) VALUES('integrity-check'); +} + +#------------------------------------------------------------------------- +# Check that the "table function" syntax works. +# +reset_db +do_execsql_test 9.1 { + CREATE VIRTUAL TABLE ft2 USING fts5(content); + INSERT INTO ft2(rowid, content) VALUES(1, 'a b'); + INSERT INTO ft2(rowid, content) VALUES(2, 'a b c d'); + INSERT INTO ft2(rowid, content) VALUES(3, 'c d e f'); +} + +do_execsql_test 9.2 { + SELECT rowid FROM ft2('a'); +} {1 2} + +do_execsql_test 9.3 { + SELECT rowid FROM ft2('b AND c'); +} {2} + +#------------------------------------------------------------------------- +# +do_execsql_test 10.0 { + CREATE VIRTUAL TABLE t3 USING fts5(a, b, c); + INSERT INTO t3 VALUES('bac aab bab', 'c bac c', 'acb aba abb'); -- 1 + INSERT INTO t3 VALUES('bab abc c', 'acb c abb', 'c aaa c'); -- 2 +} + +do_execsql_test 10.1 { + SELECT rowid FROM t3('c: c*'); +} {2} + +do_execsql_test 10.2 { + SELECT rowid FROM t3('b: acb'); +} {2} + +#------------------------------------------------------------------------- +# Test that character 0x1A is allowed in fts5 barewords. +# +do_test 11.0 { + execsql "CREATE VIRTUAL TABLE t4 USING fts5(x, tokenize=\"ascii tokenchars '\x1A'\")" + execsql " + INSERT INTO t4 VALUES('a b c \x1A'); + INSERT INTO t4 VALUES('a b c d\x1A'); + INSERT INTO t4 VALUES('a b c \x1Ad'); + INSERT INTO t4 VALUES('a b c d'); + " +} {} + +do_test 11.1 { + execsql "SELECT rowid FROM t4('\x1A')" +} {1} +do_test 11.2 { + execsql "SELECT rowid FROM t4('\x1A*')" +} {1 3} +do_test 11.3 { + execsql "SELECT rowid FROM t4('d\x1A')" +} {2} + +do_test 11.4 { + catchsql "SELECT rowid FROM t4('d\x1B')" +} {/fts5: syntax error/} +do_test 11.5 { + catchsql "SELECT rowid FROM t4('d\x19')" +} {/fts5: syntax error/} + +#------------------------------------------------------------------------- +# +do_test 12.1 { + execsql { + CREATE VIRTUAL TABLE xx USING fts5(x,y); + BEGIN; + INSERT INTO xx VALUES('1 2 3', 'a b c'); + } +} {} + +do_execsql_test 12.2 { + SELECT rowid FROM xx('x:a'); + COMMIT; +} {} finish_test diff --git a/ext/fts5/tool/fts5txt2db.tcl b/ext/fts5/tool/fts5txt2db.tcl new file mode 100644 index 0000000000..23f607a801 --- /dev/null +++ b/ext/fts5/tool/fts5txt2db.tcl @@ -0,0 +1,135 @@ + + +proc usage {} { + puts stderr "$::argv0 ?OPTIONS? DATABASE FILE1..." + puts stderr "" + puts stderr "Options are" + puts stderr " -fts5" + puts stderr " -fts4" + puts stderr " -colsize " + puts stderr { +This script is designed to create fts4/5 tables with more than one column. +The -colsize option should be set to a Tcl list of integer values, one for +each column in the table. Each value is the number of tokens that will be +inserted into the column value for each row. For example, setting the -colsize +option to "5 10" creates an FTS table with 2 columns, with roughly 5 and 10 +tokens per row in each, respectively. + +Each "FILE" argument should be a text file. The contents of these text files is +split on whitespace characters to form a list of tokens. The first N1 tokens +are used for the first column of the first row, where N1 is the first element +of the -colsize list. The next N2 are used for the second column of the first +row, and so on. Rows are added to the table until the entire list of tokens +is exhausted. +} + exit -1 +} + +set O(aColsize) [list 10 10 10] +set O(tblname) t1 +set O(fts) fts5 + + +set options_with_values {-colsize} + +for {set i 0} {$i < [llength $argv]} {incr i} { + set opt [lindex $argv $i] + if {[string range $opt 0 0]!="-"} break + + if {[lsearch $options_with_values $opt]>=0} { + incr i + if {$i==[llength $argv]} usage + set val [lindex $argv $i] + } + + switch -- $opt { + -colsize { + set O(aColSize) $val + } + + -fts4 { + set O(fts) fts4 + } + + -fts5 { + set O(fts) fts5 + } + } +} + +if {$i > [llength $argv]-2} usage +set O(db) [lindex $argv $i] +set O(files) [lrange $argv [expr $i+1] end] + +foreach {k v} [lrange $argv 0 end-2] { + switch -- $k { + -colsize { + set O(aColSize) $v + } + + -colsize { + set O(aColSize) $v + } + } + +} + +sqlite3 db $O(db) +load_static_extension db fts5 + + +# Create the FTS table in the db. Return a list of the table columns. +# +proc create_table {} { + global O + set cols [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] + + set nCol [llength $O(aColsize)] + set cols [lrange $cols 0 [expr $nCol-1]] + + set sql "CREATE VIRTUAL TABLE IF NOT EXISTS $O(tblname) USING $O(fts) (" + append sql [join $cols ,] + append sql ");" + + db eval $sql + return $cols +} + +# Return a list of tokens from the named file. +# +proc readfile {file} { + set fd [open $file] + set data [read $fd] + close $fd + split $data +} + + +# Load all the data into a big list of tokens. +# +set tokens [list] +foreach f $O(files) { + set tokens [concat $tokens [readfile $f]] +} + +set N [llength $tokens] +set i 0 +set cols [create_table] +set sql "INSERT INTO $O(tblname) VALUES(\$[lindex $cols 0]" +foreach c [lrange $cols 1 end] { + append sql ", \$$c" +} +append sql ")" + +db eval BEGIN + while {$i < $N} { + foreach c $cols s $O(aColsize) { + set $c [lrange $tokens $i [expr $i+$s-1]] + incr i $s + } + db eval $sql + } +db eval COMMIT + + + diff --git a/ext/fts5/tool/mkfts5c.tcl b/ext/fts5/tool/mkfts5c.tcl index f5cf5197e2..535506de1b 100644 --- a/ext/fts5/tool/mkfts5c.tcl +++ b/ext/fts5/tool/mkfts5c.tcl @@ -7,6 +7,7 @@ set G(src) [string map [list %dir% $srcdir] { %dir%/fts5.h %dir%/fts5Int.h fts5parse.h + fts5parse.c %dir%/fts5_aux.c %dir%/fts5_buffer.c %dir%/fts5_config.c @@ -19,12 +20,11 @@ set G(src) [string map [list %dir% $srcdir] { %dir%/fts5_unicode2.c %dir%/fts5_varint.c %dir%/fts5_vocab.c - fts5parse.c }] set G(hdr) { -#if !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_FTS5) +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 @@ -37,7 +37,7 @@ set G(hdr) { set G(footer) { -#endif /* !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_FTS5) */ +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ } #------------------------------------------------------------------------- @@ -87,7 +87,9 @@ proc fts5c_printfile {zIn} { foreach line [split $data "\n"] { if {[regexp {^#include.*fts5} $line]} continue - if {[regexp {^(const )?[a-zA-Z][a-zA-Z0-9]* [*]?sqlite3Fts5} $line]} { + if { ![regexp { sqlite3Fts5Init\(} $line] + && [regexp {^(const )?[a-zA-Z][a-zA-Z0-9]* [*]?sqlite3Fts5} $line] + } { set line "static $line" } set line [string map $sub_map $line] @@ -107,7 +109,3 @@ proc fts5c_close {} { fts5c_init fts5.c foreach f $G(src) { fts5c_printfile $f } fts5c_close - - - - diff --git a/ext/misc/json1.c b/ext/misc/json1.c index 89b70f0838..337e4d3b0d 100644 --- a/ext/misc/json1.c +++ b/ext/misc/json1.c @@ -21,18 +21,24 @@ ** This implementation parses JSON text at 250 MB/s, so it is hard to see ** how JSONB might improve on that.) */ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) #if !defined(_SQLITEINT_H_) #include "sqlite3ext.h" #endif SQLITE_EXTENSION_INIT1 #include #include -#include +#include /* amalgamator: keep */ #include #include #define UNUSED_PARAM(X) (void)(X) +#ifndef LARGEST_INT64 +# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) +# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) +#endif + /* ** Versions of isspace(), isalnum() and isdigit() to which it is safe ** to pass signed char values. @@ -65,10 +71,13 @@ static const char jsonIsSpace[] = { }; #define safe_isspace(x) (jsonIsSpace[(unsigned char)x]) -/* Unsigned integer types */ -typedef sqlite3_uint64 u64; -typedef unsigned int u32; -typedef unsigned char u8; +#ifndef SQLITE_AMALGAMATION + /* Unsigned integer types. These are already defined in the sqliteInt.h, + ** but the definitions need to be repeated for separate compilation. */ + typedef sqlite3_uint64 u64; + typedef unsigned int u32; + typedef unsigned char u8; +#endif /* Objects */ typedef struct JsonString JsonString; @@ -477,18 +486,36 @@ static void jsonReturn( sqlite3_result_int(pCtx, 0); break; } - case JSON_REAL: { - double r = strtod(pNode->u.zJContent, 0); - sqlite3_result_double(pCtx, r); - break; - } case JSON_INT: { sqlite3_int64 i = 0; const char *z = pNode->u.zJContent; if( z[0]=='-' ){ z++; } - while( z[0]>='0' && z[0]<='9' ){ i = i*10 + *(z++) - '0'; } + while( z[0]>='0' && z[0]<='9' ){ + unsigned v = *(z++) - '0'; + if( i>=LARGEST_INT64/10 ){ + if( i>LARGEST_INT64/10 ) goto int_as_real; + if( z[0]>='0' && z[0]<='9' ) goto int_as_real; + if( v==9 ) goto int_as_real; + if( v==8 ){ + if( pNode->u.zJContent[0]=='-' ){ + sqlite3_result_int64(pCtx, SMALLEST_INT64); + goto int_done; + }else{ + goto int_as_real; + } + } + } + i = i*10 + v; + } if( pNode->u.zJContent[0]=='-' ){ i = -i; } sqlite3_result_int64(pCtx, i); + int_done: + break; + int_as_real: /* fall through to real */; + } + case JSON_REAL: { + double r = strtod(pNode->u.zJContent, 0); + sqlite3_result_double(pCtx, r); break; } case JSON_STRING: { @@ -858,7 +885,7 @@ static int jsonParseFindParents(JsonParse *pParse){ ** Compare the OBJECT label at pNode against zKey,nKey. Return true on ** a match. */ -static int jsonLabelCompare(JsonNode *pNode, const char *zKey, int nKey){ +static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ if( pNode->jnFlags & JNODE_RAW ){ if( pNode->n!=nKey ) return 0; return strncmp(pNode->u.zJContent, zKey, nKey)==0; @@ -1937,19 +1964,12 @@ static sqlite3_module jsonTreeModule = { #endif /* SQLITE_OMIT_VIRTUALTABLE */ /**************************************************************************** -** The following routine is the only publically visible identifier in this -** file. Call the following routine in order to register the various SQL +** The following routines are the only publically visible identifiers in this +** file. Call the following routines in order to register the various SQL ** functions and the virtual table implemented by this file. ****************************************************************************/ -#ifdef _WIN32 -__declspec(dllexport) -#endif -int sqlite3_json_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ +int sqlite3Json1Init(sqlite3 *db){ int rc = SQLITE_OK; unsigned int i; static const struct { @@ -1987,8 +2007,6 @@ int sqlite3_json_init( { "json_tree", &jsonTreeModule }, }; #endif - SQLITE_EXTENSION_INIT2(pApi); - (void)pzErrMsg; /* Unused parameter */ for(i=0; iobjiter); sqlite3_close(p->dbMain); sqlite3_close(p->dbRbu); + p->dbMain = 0; + p->dbRbu = 0; + +#if defined(_WIN32_WCE) + { + LPWSTR zWideOal; + LPWSTR zWideWal; + + zWideOal = rbuWinUtf8ToUnicode(zOal); + if( zWideOal ){ + zWideWal = rbuWinUtf8ToUnicode(zWal); + if( zWideWal ){ + if( MoveFileW(zWideOal, zWideWal) ){ + p->rc = SQLITE_OK; + }else{ + p->rc = SQLITE_IOERR; + } + sqlite3_free(zWideWal); + }else{ + p->rc = SQLITE_IOERR_NOMEM; + } + sqlite3_free(zWideOal); + }else{ + p->rc = SQLITE_IOERR_NOMEM; + } + } +#else p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK; +#endif + if( p->rc==SQLITE_OK ){ - p->dbMain = 0; - p->dbRbu = 0; rbuOpenDatabase(p); rbuSetupCheckpoint(p, 0); } diff --git a/ext/rbu/sqlite3rbu.h b/ext/rbu/sqlite3rbu.h index b6cbf4d8b9..65d1c5f6e5 100644 --- a/ext/rbu/sqlite3rbu.h +++ b/ext/rbu/sqlite3rbu.h @@ -269,6 +269,10 @@ #include "sqlite3.h" /* Required for error code definitions */ +#ifdef __cplusplus +extern "C" { +#endif + typedef struct sqlite3rbu sqlite3rbu; /* @@ -447,4 +451,8 @@ int sqlite3rbu_create_vfs(const char *zName, const char *zParent); */ void sqlite3rbu_destroy_vfs(const char *zName); +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + #endif /* _SQLITE3RBU_H */ diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test index 0beb16cc94..c9192de192 100644 --- a/ext/rtree/rtree1.test +++ b/ext/rtree/rtree1.test @@ -34,6 +34,11 @@ set testprefix rtree1 # # rtree-12.*: Test that on-conflict clauses are supported. # rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed. +# rtree-14.*: Test if a non-integer is inserted into the PK column of an +# r-tree table, it is converted to an integer before being +# inserted. Also that if a non-numeric is inserted into one +# of the min/max dimension columns, it is converted to the +# required type before being inserted. # ifcapable !rtree { @@ -535,4 +540,54 @@ do_execsql_test 13.2 { SELECT * FROM r CROSS JOIN t9 WHERE id=x; } {1 1 0.0 0.0 2 2 0.0 0.0} +#------------------------------------------------------------------------- +# Test if a non-integer is inserted into the PK column of an r-tree +# table, it is converted to an integer before being inserted. Also +# that if a non-numeric is inserted into one of the min/max dimension +# columns, it is converted to the required type before being inserted. +# +do_execsql_test 14.1 { + CREATE VIRTUAL TABLE t10 USING rtree(ii, x1, x2); +} + +do_execsql_test 14.2 { + INSERT INTO t10 VALUES(NULL, 1, 2); + INSERT INTO t10 VALUES(NULL, 2, 3); + INSERT INTO t10 VALUES('4xxx', 3, 4); + INSERT INTO t10 VALUES(5.0, 4, 5); + INSERT INTO t10 VALUES(6.4, 5, 6); +} +do_execsql_test 14.3 { + SELECT * FROM t10; +} { + 1 1.0 2.0 2 2.0 3.0 4 3.0 4.0 5 4.0 5.0 6 5.0 6.0 +} + +do_execsql_test 14.4 { + DELETE FROM t10; + INSERT INTO t10 VALUES(1, 'one', 'two'); + INSERT INTO t10 VALUES(2, '52xyz', '81...'); +} +do_execsql_test 14.5 { + SELECT * FROM t10; +} { + 1 0.0 0.0 + 2 52.0 81.0 +} + +do_execsql_test 14.4 { + DROP TABLE t10; + CREATE VIRTUAL TABLE t10 USING rtree_i32(ii, x1, x2); + INSERT INTO t10 VALUES(1, 'one', 'two'); + INSERT INTO t10 VALUES(2, '52xyz', '81...'); + INSERT INTO t10 VALUES(3, 42.3, 49.9); +} +do_execsql_test 14.5 { + SELECT * FROM t10; +} { + 1 0 0 + 2 52 81 + 3 42 49 +} + finish_test diff --git a/main.mk b/main.mk index 128f07dd1c..a3e78d05e8 100644 --- a/main.mk +++ b/main.mk @@ -12,6 +12,8 @@ # THREADLIB Specify any extra linker options needed to make the library # thread safe # +# LIBS Extra libraries options +# # OPTS Extra compiler command-line options. # # EXE The suffix to add to executable files. ".exe" for windows @@ -35,9 +37,6 @@ # LIBREADLINE Linker options needed by programs using readline() must # link against. # -# NAWK Nawk compatible awk program. Older (obsolete?) solaris -# systems need this to avoid using the original AT&T AWK. -# # Once the macros above are defined, the rest of this make script will # build the SQLite library and testing tools. ################################################################################ @@ -48,19 +47,21 @@ TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3 TCCX += -I$(TOP)/ext/async -I$(TOP)/ext/userauth TCCX += -I$(TOP)/ext/fts5 +THREADLIB += $(LIBS) # Object files for the SQLite library. # LIBOBJ+= vdbe.o parse.o \ alter.o analyze.o attach.o auth.o \ backup.o bitvec.o btmutex.o btree.o build.o \ - callback.o complete.o ctime.o date.o dbstat.o delete.o expr.o fault.o fkey.o \ + callback.o complete.o ctime.o date.o dbstat.o delete.o expr.o \ + fault.o fkey.o \ fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \ fts3_tokenize_vtab.o \ fts3_unicode.o fts3_unicode2.o \ - fts3_write.o func.o global.o hash.o \ - icu.o insert.o journal.o legacy.o loadext.o \ + fts3_write.o fts5.o func.o global.o hash.o \ + icu.o insert.o journal.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 \ @@ -225,10 +226,11 @@ SRC += \ SRC += \ $(TOP)/ext/userauth/userauth.c \ $(TOP)/ext/userauth/sqlite3userauth.h - SRC += \ $(TOP)/ext/rbu/sqlite3rbu.c \ $(TOP)/ext/rbu/sqlite3rbu.h +SRC += \ + $(TOP)/ext/misc/json1.c # FTS5 things @@ -321,7 +323,6 @@ TESTSRC += \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/misc/ieee754.c \ - $(TOP)/ext/misc/json1.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ @@ -331,8 +332,7 @@ TESTSRC += \ $(TOP)/ext/misc/wholenumber.c \ $(TOP)/ext/misc/vfslog.c \ $(TOP)/ext/fts5/fts5_tcl.c \ - $(TOP)/ext/fts5/fts5_test_mi.c \ - fts5.c + $(TOP)/ext/fts5/fts5_test_mi.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c @@ -379,7 +379,7 @@ TESTSRC2 = \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ - $(TOP)/ext/async/sqlite3async.c + $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # @@ -451,16 +451,16 @@ FUZZDATA = \ $(TOP)/test/fuzzdata3.db \ $(TOP)/test/fuzzdata4.db -# Extra arguments for including json1 in the build of tools -# -JSON1_DEP = $(TOP)/ext/misc/json1.c sqlite3ext.h -JSON1_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_CORE -JSON1_SRC = $(TOP)/ext/misc/json1.c - # Standard options to testfixture # 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 +FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 +FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 + # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # @@ -470,24 +470,23 @@ libsqlite3.a: $(LIBOBJ) $(AR) libsqlite3.a $(LIBOBJ) $(RANLIB) libsqlite3.a -sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(JSON1_DEP) - $(TCCX) $(READLINE_FLAGS) $(JSON1_OPT) -o sqlite3$(EXE) \ - $(TOP)/src/shell.c $(JSON1_SRC) \ - libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) +sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h + $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \ + $(TOP)/src/shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB) -fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(JSON1_DEP) +fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ - $(JSON1_OPT) $(TOP)/tool/fuzzershell.c $(JSON1_SRC) sqlite3.c \ + $(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \ $(TLIBS) $(THREADLIB) -fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(JSON1_DEP) +fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ - -DSQLITE_ENABLE_MEMSYS5 $(JSON1_OPT) \ - $(TOP)/test/fuzzcheck.c $(JSON1_SRC) sqlite3.c $(TLIBS) $(THREADLIB) + -DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) \ + $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) $(THREADLIB) mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c $(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ @@ -515,13 +514,14 @@ sqlite3.o: sqlite3.c # files are automatically generated. This target takes care of # all that automatic generation. # -target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl +target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c rm -rf tsrc mkdir tsrc cp -f $(SRC) tsrc rm tsrc/sqlite.h.in tsrc/parse.y tclsh $(TOP)/tool/vdbe-compress.tcl $(OPTS) vdbe.new mv vdbe.new tsrc/vdbe.c + cp fts5.c fts5.h tsrc touch target_source sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl @@ -532,6 +532,9 @@ sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c +sqlite3ext.h: target_source + cp tsrc/sqlite3ext.h . + sqlite3.c-debug: target_source $(TOP)/tool/mksqlite3c.tcl tclsh $(TOP)/tool/mksqlite3c.tcl --linemacros echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c @@ -576,23 +579,23 @@ tclsqlite.o: $(TOP)/src/tclsqlite.c $(HDR) # Rules to build opcodes.c and opcodes.h # -opcodes.c: opcodes.h $(TOP)/mkopcodec.awk - $(NAWK) -f $(TOP)/mkopcodec.awk opcodes.h >opcodes.c +opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl + tclsh $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c -opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/mkopcodeh.awk +opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl cat parse.h $(TOP)/src/vdbe.c | \ - $(NAWK) -f $(TOP)/mkopcodeh.awk >opcodes.h + tclsh $(TOP)/tool/mkopcodeh.tcl >opcodes.h # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c -parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk +parse.c: $(TOP)/src/parse.y lemon $(TOP)/tool/addopcodes.tcl cp $(TOP)/src/parse.y . rm -f parse.h ./lemon -s $(OPTS) parse.y mv parse.h parse.h.temp - $(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h + tclsh $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h @@ -665,9 +668,17 @@ fts3_unicode2.o: $(TOP)/ext/fts3/fts3_unicode2.c $(HDR) $(EXTHDR) fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c +fts5.o: fts5.c + $(TCCX) -DSQLITE_CORE -c fts5.c + +json1.o: $(TOP)/ext/misc/json1.c + $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c + rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c + + fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon cp $(TOP)/ext/fts5/fts5parse.y . rm -f fts5parse.h @@ -698,7 +709,7 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl cat sqlite3.c $(TOP)/src/tclsqlite.c >> $@ echo "static const char *tclsh_main_loop(void){" >> $@ echo "static const char *zMainloop = " >> $@ - $(NAWK) -f $(TOP)/tool/tostr.awk $(TOP)/tool/spaceanal.tcl >> $@ + tclsh $(TOP)/tool/tostr.tcl $(TOP)/tool/spaceanal.tcl >> $@ echo "; return zMainloop; }" >> $@ sqlite3_analyzer$(EXE): sqlite3_analyzer.c @@ -714,9 +725,9 @@ testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c \ -o testfixture$(EXE) $(LIBTCL) libsqlite3.a $(THREADLIB) -amalgamation-testfixture$(EXE): sqlite3.c fts5.c $(TESTSRC) $(TOP)/src/tclsqlite.c +amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS) \ - $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts5.c \ + $(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \ -o testfixture$(EXE) $(LIBTCL) $(THREADLIB) fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c diff --git a/manifest b/manifest index 1db1a35aeb..c234b17ae3 100644 --- a/manifest +++ b/manifest @@ -1,24 +1,21 @@ -C Merge\strunk\schanges\sinto\sthe\scursor-hints\sbranch. -D 2015-09-24T15:06:30.389 -F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in e1afa6fb2de2bddd50e0ddae8166c2ee9d69b301 -F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 -F Makefile.msc f090cdf036f3c07fb13aa2f4494e388c0b1ed1e4 -F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858 +C Merge\sin\sall\sthe\s3.9.0\supdates\sfrom\strunk. +D 2015-10-14T20:09:54.505 +F Makefile.in 2ea961bc09e441874eb3d1bf7398e04feb24f3ee +F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 +F Makefile.msc 9660c072f65b2742595687b30f6d9ae55001ae06 F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7 -F VERSION ccfc4d1576dbfdeece0a4372a2e6a2e37d3e7975 +F VERSION cacf16a72f9a03cd06b939a764e32f6f53254c7f F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 -F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903 -F autoconf/Makefile.am 27de1af382c82e81f1fe36a7f38528fba004eb1a +F autoconf/Makefile.am bd4a90972aa87f079af6624ddea3df3d58f26d2f F autoconf/README 14458f1046c118efa721aadec5f227e876d3cd38 F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7 F autoconf/config.guess 94cc57e2a3fdb9c235b362ace86d77e89d188cad x F autoconf/config.sub 1efb390a8fb4bfafd74783a15a8fb5311c84300e x -F autoconf/configure.ac ba3e99ba1a8171d0682b68443517088fc5d6f13a +F autoconf/configure.ac a1fe4eee429fd9d3170be41941514a2b7120ba4e F autoconf/depcomp 0b26f101e3bc9fd1ff0be1da9fb4a82371142f92 x F autoconf/install-sh 06ee6336e63bb845c8439d777c32eb2eccc4fbf1 x F autoconf/ltmain.sh 7a658a24028f02331c1d2446562758083c5eadd1 @@ -38,8 +35,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 2f61915a1bdfbc589244334401cf97d3401e6a39 x -F configure.ac 713de38000413e469188db2cb85bed759b56f322 +F configure 99485c9c0446d1236a78a53de6f2737f45f0aca5 x +F configure.ac f36bd4fb8c53eed8374c5a5ef319807c08c23fa9 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 @@ -78,11 +75,11 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c b04b0c57761fdba2ae562d9d9ba50c7c4a95d9ea +F ext/fts3/fts3.c e028eb13432f108d2e22cded019fc980700e4e00 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h 601743955ac43a0e82e6828a931c07bb3b0c95ff +F ext/fts3/fts3Int.h c84125c666ee54cef6efce6ff64abb0d0e2f4535 F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1 -F ext/fts3/fts3_expr.c 71c063da9c2a4167fb54aec089dd5ef33a58c9cb +F ext/fts3/fts3_expr.c dfd571a24412779ac01f25c01d888c6ef7b2d0ef F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60 F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf F ext/fts3/fts3_icu.c deb46f7020d87ea7a14a433fb7a7f4bef42a9652 @@ -96,7 +93,7 @@ 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 4f005f78592a1447ca96c8475ef5342ab7dbe02a +F ext/fts3/fts3_write.c f442223e4a1914dc1fc12b65af7e4f2c255fa47c F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/tool/fts3view.c 8e53d0190a7b3443764bbd32ad47be2bd852026d @@ -105,25 +102,25 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl 95cf7ec186e48d4985e433ff8a1c89090a774252 F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95 F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0 -F ext/fts5/fts5.h 98f802fe41481f9d797fce496f0fefcad72c7782 -F ext/fts5/fts5Int.h 666aba8432940a8449a3bd4636e898fe906ed95d -F ext/fts5/fts5_aux.c 7a307760a9c57c750d043188ec0bad59f5b5ec7e -F ext/fts5/fts5_buffer.c 64dcaf36a3ebda9e84b7c3b8788887ec325e12a4 +F ext/fts5/fts5.h 8b9a13b309b180e9fb88ea5666c0d8d73c6102d9 +F ext/fts5/fts5Int.h 38667e39859ff3f3bc91f47efe672023a145a118 +F ext/fts5/fts5_aux.c b09aa27dcdaa3d50a30be433fddaa48a50aa827b +F ext/fts5/fts5_buffer.c e99224a316cc5b2c574ccccdc7f2344bca54784d F ext/fts5/fts5_config.c 57ee5fe71578cb494574fc0e6e51acb9a22a8695 -F ext/fts5/fts5_expr.c 667faaf14a69a5683ac383acdc8d942cf32c3f93 +F ext/fts5/fts5_expr.c bc31478fd04de55150031f6e6a652939d3e335ac F ext/fts5/fts5_hash.c 4bf4b99708848357b8a2b5819e509eb6d3df9246 -F ext/fts5/fts5_index.c 4fdbc0a321e3a1d73741a623d7aea4db78d6a86d -F ext/fts5/fts5_main.c 53116cffeb26898832ff7700cc5ebac5fe085d32 -F ext/fts5/fts5_storage.c 120f7b143688b5b7710dacbd48cff211609b8059 -F ext/fts5/fts5_tcl.c 6da58d6e8f42a93c4486b5ba9b187a7f995dee37 +F ext/fts5/fts5_index.c f73968357818455039ecb79dcd4b082c3baaeaeb +F ext/fts5/fts5_main.c bf43550b8e9a68514abd179500f1917a2256cd7a +F ext/fts5/fts5_storage.c df061a5caf9e50fbbd43113009b5b248362f4995 +F ext/fts5/fts5_tcl.c 3bf445e66de32137d4693694ff7b1fd6074e32bd F ext/fts5/fts5_test_mi.c e96be827aa8f571031e65e481251dc1981d608bf F ext/fts5/fts5_tokenize.c f380f46f341af9c9a9908e1aade685ba1eaa157a F ext/fts5/fts5_unicode2.c 78273fbd588d1d9bd0a7e4e0ccc9207348bae33c F ext/fts5/fts5_varint.c 3f86ce09cab152e3d45490d7586b7ed2e40c13f1 -F ext/fts5/fts5_vocab.c 4622e0b7d84a488a1585aaa56eb214ee67a988bc -F ext/fts5/fts5parse.y 833db1101b78c0c47686ab1b84918e38c36e9452 +F ext/fts5/fts5_vocab.c a05027ab6abb692ad27654c85137a4f1061a159e +F ext/fts5/fts5parse.y e83dca6028e3309178d05b5bd920e372dc295d35 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba -F ext/fts5/test/fts5_common.tcl b6e6a40ef5d069c8e86ca4fbad491e1195485dbc +F ext/fts5/test/fts5_common.tcl 51f7ef3af444b89c6f6ce3896a0ac349ff4e996d F ext/fts5/test/fts5aa.test 4804f237005bb4ba8ea4a76120d8011ebcb5d611 F ext/fts5/test/fts5ab.test 6fe3a56731d15978afbb74ae51b355fc9310f2ad F ext/fts5/test/fts5ac.test 9737992d08c56bfd4803e933744d2d764e23795c @@ -135,7 +132,7 @@ F ext/fts5/test/fts5ah.test e592c4978622dbc4de552cd0f9395df60ac5d54c F ext/fts5/test/fts5ai.test f20e53bbf0c55bc596f1fd47f2740dae028b8f37 F ext/fts5/test/fts5aj.test 05b569f5c16ea3098fb1984eec5cf50dbdaae5d8 F ext/fts5/test/fts5ak.test 7b8c5df96df599293f920b7e5521ebc79f647592 -F ext/fts5/test/fts5al.test 5c79525671862861906fa0a848da462a8473eafb +F ext/fts5/test/fts5al.test a1b7b6393376bc2adc216527a28f5ae5594069df F ext/fts5/test/fts5alter.test 6022c61467a82aa11c70822ccad22b328dcf0d04 F ext/fts5/test/fts5auto.test caa5bcf917db11944655a2a9bd38c67c520376ca F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f @@ -156,7 +153,7 @@ F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341 F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3 F ext/fts5/test/fts5fault4.test 762991d526ee67c2b374351a17248097ea38bee7 F ext/fts5/test/fts5fault5.test 54da9fd4c3434a1d4f6abdcb6469299d91cf5875 -F ext/fts5/test/fts5fault6.test 97bce1a36b7a64e3203fea504ae8e5cfd5ada423 +F ext/fts5/test/fts5fault6.test 9682664d679643ac6736e90c225526cc84073cda F ext/fts5/test/fts5fault7.test f62ed4d98f137eb03f1db94d1fa41b17a771d971 F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741 F ext/fts5/test/fts5hash.test 42eb066f667e9a389a63437cb7038c51974d4fc6 @@ -164,16 +161,18 @@ F ext/fts5/test/fts5integrity.test 29f41d2c7126c6122fbb5d54e556506456876145 F ext/fts5/test/fts5matchinfo.test 2163b0013e824bba65499da9e34ea4da41349cc2 F ext/fts5/test/fts5merge.test 8f3cdba2ec9c5e7e568246e81b700ad37f764367 F ext/fts5/test/fts5near.test b214cddb1c1f1bddf45c75af768f20145f7e71cc +F ext/fts5/test/fts5onepass.test 7ed9608e258132cb8d55e7c479b08676ad68810c F ext/fts5/test/fts5optimize.test 42741e7c085ee0a1276140a752d4407d97c2c9f5 +F ext/fts5/test/fts5phrase.test f6d1d464da5beb25dc56277aa4f1d6102f0d9a2f F ext/fts5/test/fts5plan.test 6a55ecbac9890765b0e16f8c421c7e0888cfe436 F ext/fts5/test/fts5porter.test 7cdc07bef301d70eebbfa75dcaf45c3680e1d0e1 F ext/fts5/test/fts5porter2.test 2e65633d58a1c525d5af0f6c01e5a59155bb3487 -F ext/fts5/test/fts5prefix.test 552a462f0e8595676611f41643de217fb4ac2808 +F ext/fts5/test/fts5prefix.test ad3069f099ff593a2196422244fa218f8942dfb9 F ext/fts5/test/fts5rank.test 11dcebba31d822f7e99685b4ea2c2ae3ec0b16f1 F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17 F ext/fts5/test/fts5rowid.test 400384798349d658eaf06aefa1e364957d5d4821 -F ext/fts5/test/fts5simple.test 967b7144644ad4b40b2526160a5adfa896864c55 +F ext/fts5/test/fts5simple.test f8463172dc2d4bf9f74c78e9df9c83e942c63a94 F ext/fts5/test/fts5synonym.test cf88c0a56d5ea9591e3939ef1f6e294f7f2d0671 F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89 F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841 @@ -182,8 +181,9 @@ F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680 F ext/fts5/test/fts5version.test 978f59541d8cef7e8591f8be2115ec5ccb863e2e F ext/fts5/test/fts5vocab.test cdf97b9678484e9bad5062edf9c9106e5c3b0c5c +F ext/fts5/tool/fts5txt2db.tcl 3d19fb8ffb234031d33d7d2151acfbc55e9cfcc4 F ext/fts5/tool/loadfts5.tcl 58e90407cc5c2b1770460119488fd7c0090d4dd3 -F ext/fts5/tool/mkfts5c.tcl 5745072c7de346e18c7f491e4c3281fe8a1cfe51 +F ext/fts5/tool/mkfts5c.tcl 09ce6a7997440508360f5ba1651ab7e923a8bf31 F ext/fts5/tool/showfts5.tcl 9eaf6c3df352f98a2ab5ce1921dd94128ab1381d F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c b2732aef0b076e4276d9b39b5a33cec7a05e1413 @@ -195,7 +195,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767 F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e -F ext/misc/json1.c 557d6b2d0c3d26625e686a4b4ef8d4a50b8cec94 +F ext/misc/json1.c fed5b948168f26f39e67d898b03d93dd00e75196 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc @@ -229,13 +229,13 @@ 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 4ba82bd850aa012f73c31dd242d570f18c9cc35a -F ext/rbu/sqlite3rbu.h 5357f070cd8c0bcad459b620651ec1656859e4d0 +F ext/rbu/sqlite3rbu.c ea47de615e911b3a69a8e7fb3be3866298403a25 +F ext/rbu/sqlite3rbu.h 1d568cb33738d7900975cc5c72e6f68049f15914 F ext/rbu/test_rbu.c 2a3652241fa45d5eaa141775e4ae68c1d3582c03 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/rtree.c 0f9b595bd0debcbedf1d7a63d0e0678d619e6c9c F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e -F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9 +F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 @@ -261,9 +261,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk d12601118f1d1dadebe1329a53a6d5c512b36d44 -F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea -F mkopcodeh.awk 0e7f04a8eb90f92259e47d80110e4e98d7ce337a +F main.mk 47b879ad92fe896895dcfc1bbec5c119ee2aa690 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -275,37 +273,37 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 4911e1f18fc11b60edbc6410643e938762969a6a +F src/alter.c 9d649e46c780166e416fb11dbd23f8d49aab8267 F src/analyze.c 4c308880cf53c558070cb8513bdff4ffb1a38a77 F src/attach.c e944d0052b577703b9b83aac1638452ff42a8395 F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c c3a9c4209439b806c44cf30daf466955727bf46c F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79 -F src/btree.c 9c791540434eef573ea7d3d5b7d77d66678be3d8 +F src/btree.c 7fc9513b151e3ef30369609151a18177b0b3beda F src/btree.h 9cc758f0f1e0002049d9eb0232f9783f21f25ac5 F src/btreeInt.h 8177c9ab90d772d6d2c6c517e05bed774b7c92c0 -F src/build.c 8a86f4203ac8a9ac0734f242a96f043edffb6018 +F src/build.c d6162335d690396dfc5c4bd59e8b2b0c14ba6285 F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f -F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b +F src/ctime.c 509ef9c64d1321f42448f111da86400b1799218a F src/date.c fb1c99172017dcc8e237339132c91a21a0788584 F src/dbstat.c e637e7a7ff40ef32132a418c6fdf1cfb63aa27c7 -F src/delete.c 371df4fc86e96efeaed3d37565aef77f956be109 -F src/expr.c 572ebfeb5c848e8d32096c823f87baa1eb988391 +F src/delete.c 35c939eb8bacc9dd8a6715964e5f69feb8c20e44 +F src/expr.c 7afc6450c3da35127f2ffe8b0412962c46cc0fa6 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 83e1baba999bed3144ea5a2143fc922edf51135f +F src/fkey.c 31900763094a3736a5fc887469202eb579fef2d0 F src/func.c ecdd69ec6a1e406f04cc73324be2ebbf6354197f F src/global.c 508e4087f7b41d688e4762dcf4d4fe28cfbc87f9 F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5 F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c 9748a37e058256eb2ead69f028ab85ebf203ad15 +F src/insert.c 3c522beb5bf50a2efee1fca1e80fd40942e5817c F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/lempar.c d344a95d60c24e2f490ee59db9784b1b17439012 -F src/loadext.c f0b66d28e377fd6c6d36cc9d92df1ff251ebee44 -F src/main.c e17fcffae4306a9b8334faf3bac80d7396850b54 +F src/loadext.c c5916a158f1fb74d955847c84f7ebdb6f5de9d8c +F src/main.c fec97668771438033a7559883401067b139729e1 F src/malloc.c 3a37ce6979a40f499d8cea9e9ab4e8517854d35d F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987 @@ -317,8 +315,8 @@ F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495 F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85 F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4 -F src/mutex_unix.c 8cfa6e83c618d2fcae0fe63f4d2b5bb16b11a97a -F src/mutex_w32.c 2e025e6642eaf27597403690980f560d1a91f62c +F src/mutex_unix.c a94b46f3f7beba307dde1b298b0f99f9c3677a93 +F src/mutex_w32.c 5e6fe1c298fb5a8a15aaed4161d5759311431c17 F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7 F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8 F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf @@ -333,24 +331,24 @@ F src/parse.y f599aa5e871a493330d567ced93de696f61f48f7 F src/pcache.c 24be750c79272e0ca7b6e007bc94999700f3e5ef F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9 F src/pcache1.c e822007159d53a7ea7aa040d6e28964ddb6de083 -F src/pragma.c d71b813e67bf03f3116b9dd5164fbfd81ec673a2 +F src/pragma.c dcfe3a35d2de935feeaba1455528b4a5c4f1208c F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1 F src/printf.c 0c4bcdd1c2e2521024f0a69cb5eb334f86b3652a F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 1954a0f01bf65d78d7d559aea3d5c67f33376d91 F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e -F src/select.c 514646c18ed52f2607585e2d7361f26bc3ea460a -F src/shell.c 6332ef06db1390ef812cfdff1fc97b4fd76cdd42 -F src/sqlite.h.in 4e06cb5fed36736242c66237991db6f3e2395eb9 +F src/select.c 3e4a4691e11d810b024407d90aa5dd845b1f5aac +F src/shell.c d25df04168d6ba5a4fa05bdbf859df667f9eb621 +F src/sqlite.h.in dc35357824c6bb84a04acd4e99dee3e07299a8c6 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad -F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308 -F src/sqliteInt.h b108aac991a28e4208f526df54f82995316c56ee +F src/sqlite3ext.h 4b66e3e3435da4b4c8c83696d0349f0c503b3924 +F src/sqliteInt.h d2d529e342d5becf735ca2edde2ba1477c935d45 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179 F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e F src/tclsqlite.c d9439b6a910985b7fff43ba6756bcef00de22649 -F src/test1.c c96508c835979bf15dc0e3146e2c7a51a2333d3c +F src/test1.c 8fff9c5aa63d6490f516d018b70c12a9cb9a4d8a F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622 F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e @@ -364,7 +362,7 @@ F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f -F src/test_config.c fb2e5d354d9a077f5fbb261652eff4787deb104f +F src/test_config.c ada6f38b0acb6722fb7f0ed8c54fd66df41085b9 F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f @@ -395,32 +393,32 @@ F src/test_thread.c af391ec03d23486dffbcc250b7e58e073f172af9 F src/test_vfs.c 3b65d42e18b262805716bd96178c81da8f2d9283 F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 -F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481 +F src/threads.c bbfb74450643cb5372a43ad4f6cffd7e9dfcecb0 F src/tokenize.c 83c6ed569423a3af83a83973b444cf7123be33a6 F src/treeview.c 154f0acc622fa3514de8777dcedf4c8a8802b4ce F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f -F src/update.c eb7ab3ff2928628692a4f14be397c95f4a681d97 +F src/update.c dc37664095ca8604293ff1de2d9a547c6efb5e6e F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 -F src/vdbe.c 1704a79d60f9c316adac0cbce09cae1ffc4b6af8 +F src/vdbe.c 4d4d88a68c354a62e8d4d3fffa365397e532374d F src/vdbe.h efb7a8c1459e31f3ea4377824c6a7e4cb5068637 F src/vdbeInt.h c1508eb4ee04d4db7bdfca5620cfa8ad632fdd62 F src/vdbeapi.c 020681b943e77766b32ae1cddf86d7831b7374ca F src/vdbeaux.c 7dccd114c9ee5f801709daecee36464fe6ce71af -F src/vdbeblob.c 1d7b97115e7bbac4c318db416d2ca83fc779544a +F src/vdbeblob.c 565fabd302f5fca3bdf3d56cac330483616a39b6 F src/vdbemem.c 19b3036aa4d676e7103b0fb5efd6327da455f915 F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0 -F src/vtab.c 2ecfe020c10e0a0c7b078203fdba2fae844744bc +F src/vtab.c 2a8b44aa372c33f6154208e7a7f6c44254549806 F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba -F src/where.c edcaa18b274526a2a74519d5b38ca6845b24dfc2 +F src/where.c 05a178b78c835928d7037c0b2e409b7b656a68a4 F src/whereInt.h 7892bb54cf9ca0ae5c7e6094491b94c9286dc647 -F src/wherecode.c 16f0d8b39ecf70c10b3c9af40b1fd679cdb4e811 -F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d +F src/wherecode.c 4d60b2e8a88adb35851201fa4eff6fbf9aa57ad6 +F src/whereexpr.c e63244ca06c503e5f3c5b7f3c9aea0db826089ed F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -573,7 +571,7 @@ F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d F test/delete.test e1bcdf8926234e27aac24b346ad83d3329ec8b6f F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab -F test/delete4.test d9e7d553a939597b27d205b022d769469f361c1f +F test/delete4.test 3ac0b8d23689ba764c2e8b78c1b56b8f1b942fa2 F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240 F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 @@ -622,7 +620,7 @@ F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79 F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7 F test/filectrl.test 7c13f96457435238da99aff7343ad6a3a4885787 F test/filefmt.test cb34663f126cbc2d358af552dcaf5c72769b0146 -F test/fkey1.test de5b287f6a480b36bd51e8debcf48168e26e4ed2 +F test/fkey1.test 13e3d48236a2b9f5c5ebd232eef9b3ab682a8a2c F test/fkey2.test f3d27ecba480a348c328965d154214719bb158a9 F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49 F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d @@ -691,7 +689,7 @@ F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c -F test/fts3conf.test ee8500c86dd58ec075e8831a1e216a79989436de +F test/fts3conf.test ff90127b2462c788348c0dd7f6f8c573ef9cb5d9 F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 @@ -703,7 +701,7 @@ F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297 F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851 F test/fts3expr.test 3401d47b229c4504424caf362cc4ff704cad4162 F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a -F test/fts3expr3.test 9e91b8edbcb197bf2e92161aa7696446d96dce5f +F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8 F test/fts3expr4.test e1be1248566f43c252d4404d52914f1fc4bfa065 F test/fts3expr5.test f9abfffbf5e53d48a33e12a1e8f8ba2c551c9b49 F test/fts3fault.test da49627b280b210ebc6657f76344c7851f10ce66 @@ -737,6 +735,7 @@ F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891 F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7 F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849 +F test/fts4onepass.test bfca61f69c6ca74cd71e6dca12a0cdd47192fc24 F test/fts4unicode.test 27378af76394542cf490cf001d8d1505fe55f6a9 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef @@ -750,10 +749,10 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test 53fabcd5f0f430f8b221282f6c12c4d0903c21eb F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzcheck.c b8eb7ee40f6d28548a0b028e0676293522f3427f +F test/fuzzcheck.c c84086021a514360268190a1bc6ae8ed78d7c94f F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664 F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973 -F test/fuzzdata3.db 1d6044c33a114007f02b6e6846f1fa232f607bfd +F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21 F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36 F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536 @@ -786,7 +785,7 @@ F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7 F test/index6.test 7102ec371414c42dfb1d5ca37eb4519aa9edc23a F test/index7.test 9c6765a74fc3fcde7aebc5b3bd40d98df14a527c F test/indexedby.test 9c4cd331224e57f79fbf411ae245e6272d415985 -F test/indexexpr1.test 203c83a05accf6f2b748834192f3564321b8c0d8 +F test/indexexpr1.test bbb52b5d5717d9f23853826963b0af5110009366 F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371 @@ -817,8 +816,8 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307 F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa -F test/json101.test e8b50fbcdbf283cfafbc42632bf2c7dfa4541c46 -F test/json102.test 796b1c59894c6e0f38fc1a3acb0e690573b952a3 +F test/json101.test 83e6ebfb3cef6329853ab854403ec82b1787b537 +F test/json102.test bf3fe7a706d30936a76a0f7a0375e1e8e73aff5a F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 @@ -898,6 +897,7 @@ F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934 F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62 F test/null.test 0dcce4f04284ec66108c503327ad6d224c0752b3 F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1 +F test/offset1.test f06b83657bcf26f9ce805e67450e189e282143b2 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 F test/orderby1.test 870e150450437d3980badbde3d0166b81d9e33f6 F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04 @@ -943,7 +943,7 @@ F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736 F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8 F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 -F test/releasetest.tcl afdac5c3429dceb034295617c0a51df9954d467a +F test/releasetest.tcl 67a82199e6ddee609211488bc04ed3f9ea3aa28a F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14 @@ -1029,7 +1029,7 @@ F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b F test/speedtest1.c 857439869d1cb4db35e1c720ee9c2756eb9ea2a0 F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135 -F test/spellfix2.test 1ff48bb65b6198d21674ae24d19bb136e547585a +F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5 F test/sqllimits1.test 89b3d5aad05b99f707ee3786bdd4416dccf83304 F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf @@ -1044,7 +1044,7 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 -F test/tabfunc01.test 83e63be7b6e3f67b6a03519c9c61bc68efb25f31 +F test/tabfunc01.test 03c4ad422c6ab596cff6dcaf86dd061a9f039525 F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 @@ -1240,7 +1240,7 @@ F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825 F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8 F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32 -F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b +F test/uri.test 6630ecbdea2aac10df3c89dbae2243f4c2c353e4 F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9 F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d @@ -1249,7 +1249,7 @@ F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 -F test/view.test 3930ae94042d702ab15a6a0ef692cfa5c9f9b68b +F test/view.test f6c3a39e0c819891265e1d0754e99960d81ef6c9 F test/vtab1.test 6210e076997f176bedc300a87ad6404651b601dd F test/vtab2.test f8cd1bb9aba7143eba97812d9617880a36d247ad F test/vtab3.test b45f47d20f225ccc9c28dc915d92740c2dee311e @@ -1333,10 +1333,12 @@ F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac -F tool/build-all-msvc.bat 761d8c82a1a529261291812732a853a1b4256d85 x +F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5 +F tool/GetTclKit.bat f8159730269bdbf5c334383134011dda4df45511 +F tool/addopcodes.tcl 7cc82ecca456a6b3148abf492b0419b83140881a +F tool/build-all-msvc.bat 2b1703b322da121e56b955cb58de091107f777c3 x F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 -F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2 F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1 F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439 @@ -1350,18 +1352,20 @@ F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862 F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6 F tool/mkautoconfamal.sh d1a2da0e15b2ed33d60af35c7e9d483f13a8eb9f F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670 +F tool/mkopcodec.tcl edde8adc42621b5e598127f8cdc6d52cfe21f52b +F tool/mkopcodeh.tcl e04177031532b7aa9379ded50e820231ac4abd6e F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkpragmatab.tcl 84af2b180484323a2ea22a2279e8bd9e3e1e492e F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 87240b09c20042999b41d5fabe091b7111287835 -F tool/mksqlite3c.tcl 421ecbd437d8cd7123e67430ee8146218396680d -F tool/mksqlite3h.tcl 44730d586c9031638cdd2eb443b801c0d2dbd9f8 +F tool/mksqlite3c.tcl b66b4170f693602cd6985aed15d9509fe2f18c84 +F tool/mksqlite3h.tcl e3ac3f23897d86cb8f5f5df92e91643229fcc8d1 F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mkvsix.tcl bbe57cd9ae11c6cc70319241101ef8d2b8c3765b F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091 F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97 -F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b +F tool/replace.tcl 7727c60a04299b65a92f5e1590896fea0f25b9e0 F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 F tool/showdb.c b1e16174385d5bd0815823a7fda1ecc82ed6088b @@ -1370,7 +1374,6 @@ F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68 F tool/showstat4.c 9515faa8ec176599d4a8288293ba8ec61f7b728a F tool/showwal.c 85cb36d4fe3e93e2fbd63e786e0d1ce42d0c4fad F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe -F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b F tool/spaceanal.tcl 93c1fdc9733c525b17a2024c7df193daa002e037 F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355 F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 @@ -1382,14 +1385,14 @@ F tool/sqldiff.c b318efc2eaf7a7fac4d281a0ce736193cb2506df F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f -F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576 +F tool/tostr.tcl 96022f35ada2194f6f8ccf6fd95809e90ed277c4 F tool/varint.c 5d94cb5003db9dbbcbcc5df08d66f16071aee003 F tool/vdbe-compress.tcl 5926c71f9c12d2ab73ef35c29376e756eb68361c F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 66fe06832614010d3156d7b21a760af9957018cc c6ab807b72ddfc1462f61aa91442b6fac04ace8a -R 645294bb497caea763068fb2d987fbfc +P fbe637620fb7f2c9395c9ddac77d26746d6d4178 4bd0d43db7c1877f2d8a8d2f2a48f24a10f0c3b8 +R 95393720801ae9935a00efdbdde27bba U drh -Z e6351460f21c4d00d95c8fbc91fe5568 +Z 98cfd2338cc7382cd129bb1eb7f3da88 diff --git a/manifest.uuid b/manifest.uuid index 1d4e637caa..1d90e1ad2e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fbe637620fb7f2c9395c9ddac77d26746d6d4178 \ No newline at end of file +29444149342fc6b1ea8cd34c2c8e1fcb06eaa7ed \ No newline at end of file diff --git a/mkopcodec.awk b/mkopcodec.awk deleted file mode 100644 index de19068c20..0000000000 --- a/mkopcodec.awk +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/awk -f -# -# This AWK script scans the opcodes.h file (which is itself generated by -# another awk script) and uses the information gleaned to create the -# opcodes.c source file. -# -# Opcodes.c contains strings which are the symbolic names for the various -# opcodes used by the VDBE. These strings are used when disassembling a -# VDBE program during tracing or as a result of the EXPLAIN keyword. -# -BEGIN { - print "/* Automatically generated. Do not edit */" - print "/* See the mkopcodec.awk script for details. */" - printf "#if !defined(SQLITE_OMIT_EXPLAIN)" - printf " || defined(VDBE_PROFILE)" - print " || defined(SQLITE_DEBUG)" - print "#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)" - print "# define OpHelp(X) \"\\0\" X" - print "#else" - print "# define OpHelp(X)" - print "#endif" - print "const char *sqlite3OpcodeName(int i){" - print " static const char *const azName[] = { \"?\"," - mx = 0 -} -/^.define OP_/ { - sub("OP_","",$2) - i = $3+0 - label[i] = $2 - if( mx=0 ) continue; - if( name=="OP_Transaction" \ - || name=="OP_AutoCommit" \ - || name=="OP_Savepoint" \ - || name=="OP_Checkpoint" \ - || name=="OP_Vacuum" \ - || name=="OP_JournalMode" \ - || name=="OP_VUpdate" \ - || name=="OP_VFilter" \ - || name=="OP_Next" \ - || name=="OP_NextIfOpen" \ - || name=="OP_SorterNext" \ - || name=="OP_Prev" \ - || name=="OP_PrevIfOpen" \ - ){ - cnt++ - while( used[cnt] ) cnt++ - op[name] = cnt - used[cnt] = 1 - def[cnt] = name - } - } - - # Generate the numeric values for opcodes - for(i=0; ieState = CURSOR_REQUIRESEEK; } - invalidateOverflowCache(pCur); + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl|BTCF_AtLast); return rc; } @@ -6520,7 +6520,13 @@ static int pageInsertArray( if( pDataapCell[i], sz); + /* pSlot and pCArray->apCell[i] will never overlap on a well-formed + ** database. But they might for a corrupt database. Hence use memmove() + ** since memcpy() sends SIGABORT with overlapping buffers on OpenBSD */ + assert( (pSlot+sz)<=pCArray->apCell[i] + || pSlot>=(pCArray->apCell[i]+sz) + || CORRUPT_DB ); + memmove(pSlot, pCArray->apCell[i], sz); put2byte(pCellptr, (pSlot - aData)); pCellptr += 2; } @@ -7645,7 +7651,7 @@ static int balance_nonroot( ** by smaller than the child due to the database header, and so all the ** free space needs to be up front. */ - assert( nNew==1 ); + assert( nNew==1 || CORRUPT_DB ); rc = defragmentPage(apNew[0]); testcase( rc!=SQLITE_OK ); assert( apNew[0]->nFree == diff --git a/src/build.c b/src/build.c index eee62ee151..7c79fe5400 100644 --- a/src/build.c +++ b/src/build.c @@ -192,6 +192,8 @@ void sqlite3FinishCoding(Parse *pParse){ db->aDb[iDb].pSchema->iGeneration /* P4 */ ); if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1); + VdbeComment((v, + "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite)); } #ifndef SQLITE_OMIT_VIRTUALTABLE for(i=0; inVtabLock; i++){ @@ -983,7 +985,7 @@ void sqlite3StartTable( ** now. */ if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){ - int j1; + int addr1; int fileFormat; int reg1, reg2, reg3; /* nullRow[] is an OP_Record encoding of a row containing 5 NULLs */ @@ -1004,14 +1006,14 @@ void sqlite3StartTable( reg3 = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); sqlite3VdbeUsesBtree(v, iDb); - j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v); + addr1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v); fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? 1 : SQLITE_MAX_FILE_FORMAT; sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3); sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); /* This just creates a place-holder record in the sqlite_master table. ** The record created does not contain anything yet. It will be replaced @@ -2082,8 +2084,7 @@ void sqlite3CreateView( if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); - sqlite3SelectDelete(db, pSelect); - return; + goto create_view_fail; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; @@ -3135,7 +3136,7 @@ Index *sqlite3CreateIndex( /* Analyze the list of expressions that form the terms of the index and ** report any errors. In the common case where the expression is exactly ** a table column, store that column in aiColumn[]. For general expressions, - ** populate pIndex->aColExpr and store -2 in aiColumn[]. + ** populate pIndex->aColExpr and store XN_EXPR (-2) in aiColumn[]. ** ** TODO: Issue a warning if two or more columns of the index are identical. ** TODO: Issue a warning if the table primary key is used as part of the @@ -3164,8 +3165,8 @@ Index *sqlite3CreateIndex( pListItem = &pCopy->a[i]; } } - j = -2; - pIndex->aiColumn[i] = -2; + j = XN_EXPR; + pIndex->aiColumn[i] = XN_EXPR; pIndex->uniqNotNull = 0; }else{ j = pCExpr->iColumn; @@ -3218,7 +3219,7 @@ Index *sqlite3CreateIndex( } assert( i==pIndex->nColumn ); }else{ - pIndex->aiColumn[i] = -1; + pIndex->aiColumn[i] = XN_ROWID; pIndex->azColl[i] = "BINARY"; } sqlite3DefaultRowEst(pIndex); diff --git a/src/ctime.c b/src/ctime.c index 9503214f50..17dd710bc3 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -96,12 +96,18 @@ static const char * const azCompileOpt[] = { #if SQLITE_ENABLE_FTS4 "ENABLE_FTS4", #endif +#if SQLITE_ENABLE_FTS5 + "ENABLE_FTS5", +#endif #if SQLITE_ENABLE_ICU "ENABLE_ICU", #endif #if SQLITE_ENABLE_IOTRACE "ENABLE_IOTRACE", #endif +#if SQLITE_ENABLE_JSON1 + "ENABLE_JSON1", +#endif #if SQLITE_ENABLE_LOAD_EXTENSION "ENABLE_LOAD_EXTENSION", #endif diff --git a/src/delete.c b/src/delete.c index c387c20bef..faef3a814e 100644 --- a/src/delete.c +++ b/src/delete.c @@ -411,7 +411,7 @@ void sqlite3DeleteFrom( pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); - assert( IsVirtual(pTab)==0 || eOnePass==ONEPASS_OFF ); + assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF ); /* Keep track of the number of rows to be deleted */ @@ -422,7 +422,7 @@ void sqlite3DeleteFrom( /* Extract the rowid or primary key for the current row */ if( pPk ){ for(i=0; iaiColumn[i]>=(-1) ); + assert( pPk->aiColumn[i]>=0 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, pPk->aiColumn[i], iPk+i); } @@ -494,7 +494,7 @@ void sqlite3DeleteFrom( */ if( eOnePass!=ONEPASS_OFF ){ assert( nKey==nPk ); /* OP_Found will use an unpacked key */ - if( aToOpen[iDataCur-iTabCur] ){ + if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ assert( pPk!=0 || pTab->pSelect!=0 ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); VdbeCoverage(v); @@ -516,7 +516,11 @@ void sqlite3DeleteFrom( sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, OE_Abort); + assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE ); sqlite3MayAbort(pParse); + if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){ + pParse->isMultiWrite = 0; + } }else #endif { @@ -854,7 +858,7 @@ int sqlite3GenerateIndexKey( for(j=0; jaiColumn[j]==pIdx->aiColumn[j] - && pPrior->aiColumn[j]>=(-1) + && pPrior->aiColumn[j]!=XN_EXPR ){ /* This column was already computed by the previous index */ continue; diff --git a/src/expr.c b/src/expr.c index aa9cc7b528..7d80c361e6 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1611,13 +1611,13 @@ int sqlite3CodeOnce(Parse *pParse){ ** to be set to NULL if iCur contains one or more NULL values. */ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){ - int j1; + int addr1; sqlite3VdbeAddOp2(v, OP_Integer, 0, regHasNull); - j1 = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); + addr1 = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, regHasNull); sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG); VdbeComment((v, "first_entry_in(%d)", iCur)); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); } @@ -2217,7 +2217,7 @@ static void sqlite3ExprCodeIN( ** the presence of a NULL on the RHS makes a difference in the ** outcome. */ - int j1; + int addr1; /* First check to see if the LHS is contained in the RHS. If so, ** then the answer is TRUE the presence of NULLs in the RHS does @@ -2225,12 +2225,12 @@ static void sqlite3ExprCodeIN( ** answer is NULL if the RHS contains NULLs and the answer is ** FALSE if the RHS is NULL-free. */ - j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); + addr1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_IsNull, rRhsHasNull, destIfNull); VdbeCoverage(v); sqlite3VdbeGoto(v, destIfFalse); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); } } } @@ -2459,15 +2459,15 @@ void sqlite3ExprCodeLoadIndexColumn( int regOut /* Store the index column value in this register */ ){ i16 iTabCol = pIdx->aiColumn[iIdxCol]; - if( iTabCol>=(-1) ){ + if( iTabCol==XN_EXPR ){ + assert( pIdx->aColExpr ); + assert( pIdx->aColExpr->nExpr>iIdxCol ); + pParse->iSelfTab = iTabCur; + sqlite3ExprCode(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut); + }else{ sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur, iTabCol, regOut); - return; } - assert( pIdx->aColExpr ); - assert( pIdx->aColExpr->nExpr>iIdxCol ); - pParse->iSelfTab = iTabCur; - sqlite3ExprCode(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut); } /* diff --git a/src/fkey.c b/src/fkey.c index a087889f4c..b55e2a9813 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -252,6 +252,8 @@ int sqlite3FkLocateIndex( char *zDfltColl; /* Def. collation for column */ char *zIdxCol; /* Name of indexed column */ + if( iCol<0 ) break; /* No foreign keys against expression indexes */ + /* If the index uses a collation sequence that is different from ** the default collation sequence for the column, this index is ** unusable. Bail out early in this case. */ @@ -404,6 +406,7 @@ static void fkLookupParent( for(i=0; iaiColumn[i]+1+regData; + assert( pIdx->aiColumn[i]>=0 ); assert( aiCol[i]!=pTab->iPKey ); if( pIdx->aiColumn[i]==pTab->iPKey ){ /* The parent key is a composite key that includes the IPK column */ @@ -612,6 +615,7 @@ static void fkScanChildren( assert( pIdx!=0 ); for(i=0; inKeyCol; i++){ i16 iCol = pIdx->aiColumn[i]; + assert( iCol>=0 ); pLeft = exprTableRegister(pParse, pTab, regData, iCol); pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); @@ -931,6 +935,7 @@ void sqlite3FkCheck( if( aiCol[i]==pTab->iPKey ){ aiCol[i] = -1; } + assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION /* Request permission to read the parent key columns. If the ** authorization callback returns SQLITE_IGNORE, behave as if any @@ -1062,7 +1067,10 @@ u32 sqlite3FkOldmask( Index *pIdx = 0; sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); if( pIdx ){ - for(i=0; inKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); + for(i=0; inKeyCol; i++){ + assert( pIdx->aiColumn[i]>=0 ); + mask |= COLUMN_MASK(pIdx->aiColumn[i]); + } } } } @@ -1185,6 +1193,7 @@ static Trigger *fkActionTrigger( iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iFromCol>=0 ); assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKeynCol) ); + assert( pIdx==0 || pIdx->aiColumn[i]>=0 ); tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName; tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; diff --git a/src/insert.c b/src/insert.c index 53b429c1f4..0cf670e6cf 100644 --- a/src/insert.c +++ b/src/insert.c @@ -90,11 +90,11 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ i16 x = pIdx->aiColumn[n]; if( x>=0 ){ pIdx->zColAff[n] = pTab->aCol[x].affinity; - }else if( x==(-1) ){ + }else if( x==XN_ROWID ){ pIdx->zColAff[n] = SQLITE_AFF_INTEGER; }else{ char aff; - assert( x==(-2) ); + assert( x==XN_EXPR ); assert( pIdx->aColExpr!=0 ); aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); if( aff==0 ) aff = SQLITE_AFF_BLOB; @@ -260,7 +260,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){ /* This routine is never called during trigger-generation. It is ** only called from the top-level */ assert( pParse->pTriggerTab==0 ); - assert( pParse==sqlite3ParseToplevel(pParse) ); + assert( sqlite3IsToplevel(pParse) ); assert( v ); /* We failed long ago if this is not so */ for(p = pParse->pAinc; p; p = p->pNext){ @@ -313,16 +313,16 @@ void sqlite3AutoincrementEnd(Parse *pParse){ assert( v ); for(p = pParse->pAinc; p; p = p->pNext){ Db *pDb = &db->aDb[p->iDb]; - int j1; + int addr1; int iRec; int memId = p->regCtr; iRec = sqlite3GetTempReg(pParse); assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); VdbeCoverage(v); + addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec); sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); @@ -814,7 +814,7 @@ void sqlite3Insert( if( ipkColumn<0 ){ sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); }else{ - int j1; + int addr1; assert( !withoutRowid ); if( useTempTable ){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regCols); @@ -822,9 +822,9 @@ void sqlite3Insert( assert( pSelect==0 ); /* Otherwise useTempTable is true */ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regCols); } - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v); + addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v); } @@ -898,14 +898,14 @@ void sqlite3Insert( ** to generate a unique primary key value. */ if( !appendFlag ){ - int j1; + int addr1; if( !IsVirtual(pTab) ){ - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v); + addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); }else{ - j1 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); VdbeCoverage(v); + addr1 = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, addr1+2); VdbeCoverage(v); } sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); VdbeCoverage(v); } @@ -1159,7 +1159,7 @@ void sqlite3GenerateConstraintChecks( int ix; /* Index loop counter */ int nCol; /* Number of columns */ int onError; /* Conflict resolution strategy */ - int j1; /* Address of jump instruction */ + int addr1; /* Address of jump instruction */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ int ipkTop = 0; /* Top of the rowid change constraint check */ @@ -1230,9 +1230,10 @@ void sqlite3GenerateConstraintChecks( } default: { assert( onError==OE_Replace ); - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); VdbeCoverage(v); + addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); + VdbeCoverage(v); sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); break; } } @@ -1408,13 +1409,13 @@ void sqlite3GenerateConstraintChecks( for(i=0; inColumn; i++){ int iField = pIdx->aiColumn[i]; int x; - if( iField==(-2) ){ + if( iField==XN_EXPR ){ pParse->ckBase = regNewData+1; sqlite3ExprCode(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i); pParse->ckBase = 0; VdbeComment((v, "%s column %d", pIdx->zName, i)); }else{ - if( iField==(-1) || iField==pTab->iPKey ){ + if( iField==XN_ROWID || iField==pTab->iPKey ){ if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */ x = regNewData; regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i; @@ -1473,6 +1474,7 @@ void sqlite3GenerateConstraintChecks( ** store it in registers regR..regR+nPk-1 */ if( pIdx!=pPk ){ for(i=0; inKeyCol; i++){ + assert( pPk->aiColumn[i]>=0 ); x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); VdbeComment((v, "%s.%s", pTab->zName, @@ -1494,6 +1496,7 @@ void sqlite3GenerateConstraintChecks( for(i=0; inKeyCol; i++){ char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]); x = pPk->aiColumn[i]; + assert( x>=0 ); if( i==(pPk->nKeyCol-1) ){ addrJump = addrUniqueOk; op = OP_Eq; @@ -1745,7 +1748,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){ if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){ return 0; /* Different columns indexed */ } - if( pSrc->aiColumn[i]==(-2) ){ + if( pSrc->aiColumn[i]==XN_EXPR ){ assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 ); if( sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr, pDest->aColExpr->a[i].pExpr, -1)!=0 ){ diff --git a/src/loadext.c b/src/loadext.c index b4b981e548..1ae87d6b7e 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -408,7 +408,7 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_value_free, sqlite3_result_zeroblob64, sqlite3_bind_zeroblob64, - /* Version 3.8.12 and later */ + /* Version 3.9.0 and later */ sqlite3_value_subtype, sqlite3_result_subtype }; @@ -615,7 +615,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ ** dummy pointer. */ #ifdef SQLITE_OMIT_LOAD_EXTENSION -static const sqlite3_api_routines sqlite3Apis = { 0 }; +static const sqlite3_api_routines sqlite3Apis; #endif diff --git a/src/main.c b/src/main.c index 575cad92c5..ef2fa66ec3 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,12 @@ #ifdef SQLITE_ENABLE_ICU # include "sqliteicu.h" #endif +#ifdef SQLITE_ENABLE_JSON1 +int sqlite3Json1Init(sqlite3*); +#endif +#ifdef SQLITE_ENABLE_FTS5 +int sqlite3Fts5Init(sqlite3*); +#endif #ifndef SQLITE_AMALGAMATION /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant @@ -2872,12 +2878,18 @@ static int openDatabase( } #endif -#ifdef SQLITE_ENABLE_FTS3 +#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */ if( !db->mallocFailed && rc==SQLITE_OK ){ rc = sqlite3Fts3Init(db); } #endif +#ifdef SQLITE_ENABLE_FTS5 + if( !db->mallocFailed && rc==SQLITE_OK ){ + rc = sqlite3Fts5Init(db); + } +#endif + #ifdef SQLITE_ENABLE_ICU if( !db->mallocFailed && rc==SQLITE_OK ){ rc = sqlite3IcuInit(db); @@ -2896,6 +2908,12 @@ static int openDatabase( } #endif +#ifdef SQLITE_ENABLE_JSON1 + if( !db->mallocFailed && rc==SQLITE_OK){ + rc = sqlite3Json1Init(db); + } +#endif + /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. diff --git a/src/mutex_unix.c b/src/mutex_unix.c index cebb96c90e..dbdaa225b3 100644 --- a/src/mutex_unix.c +++ b/src/mutex_unix.c @@ -81,7 +81,9 @@ static int pthreadMutexNotheld(sqlite3_mutex *p){ #endif /* -** Try to provide a memory barrier operation, needed for initialization only. +** Try to provide a memory barrier operation, needed for initialization +** and also for the implementation of xShmBarrier in the VFS in cases +** where SQLite is compiled without mutexes. */ void sqlite3MemoryBarrier(void){ #if defined(SQLITE_MEMORY_BARRIER) diff --git a/src/mutex_w32.c b/src/mutex_w32.c index 90be07db2d..9570bdc0bf 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -78,14 +78,19 @@ static int winMutexNotheld(sqlite3_mutex *p){ #endif /* -** Try to provide a memory barrier operation, needed for initialization only. +** Try to provide a memory barrier operation, needed for initialization +** and also for the xShmBarrier method of the VFS in cases when SQLite is +** compiled without mutexes (SQLITE_THREADSAFE=0). */ void sqlite3MemoryBarrier(void){ #if defined(SQLITE_MEMORY_BARRIER) SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) __sync_synchronize(); -#else +#elif !defined(SQLITE_DISABLE_INTRINSIC) && \ + defined(_MSC_VER) && _MSC_VER>=1300 + _ReadWriteBarrier(); +#elif defined(MemoryBarrier) MemoryBarrier(); #endif } diff --git a/src/pragma.c b/src/pragma.c index 2dcad614f1..64614a7ebc 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1361,8 +1361,9 @@ void sqlite3Pragma( */ static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList endCode[] = { - { OP_IfNeg, 1, 0, 0}, /* 0 */ - { OP_String8, 0, 3, 0}, /* 1 */ + { OP_AddImm, 1, 0, 0}, /* 0 */ + { OP_If, 1, 0, 0}, /* 1 */ + { OP_String8, 0, 3, 0}, /* 2 */ { OP_ResultRow, 3, 1, 0}, }; @@ -1524,8 +1525,8 @@ void sqlite3Pragma( int kk; for(kk=0; kknKeyCol; kk++){ int iCol = pIdx->aiColumn[kk]; - assert( iCol>=0 && iColnCol ); - if( pTab->aCol[iCol].notNull ) continue; + assert( iCol!=XN_ROWID && iColnCol ); + if( iCol>=0 && pTab->aCol[iCol].notNull ) continue; sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk); VdbeCoverage(v); } @@ -1563,9 +1564,9 @@ void sqlite3Pragma( } } addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn); - sqlite3VdbeChangeP3(v, addr, -mxErr); - sqlite3VdbeJumpHere(v, addr); - sqlite3VdbeChangeP4(v, addr+1, "ok", P4_STATIC); + sqlite3VdbeChangeP2(v, addr, -mxErr); + sqlite3VdbeJumpHere(v, addr+1); + sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC); } break; #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ diff --git a/src/select.c b/src/select.c index 682fe91203..14417f6021 100644 --- a/src/select.c +++ b/src/select.c @@ -579,7 +579,7 @@ static void pushOntoSorter( }else{ iLimit = pSelect->iLimit; } - addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, -1); VdbeCoverage(v); + addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v); sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); sqlite3VdbeJumpHere(v, addr); @@ -595,11 +595,8 @@ static void codeOffset( int iContinue /* Jump here to skip the current record */ ){ if( iOffset>0 ){ - int addr; - addr = sqlite3VdbeAddOp3(v, OP_IfNeg, iOffset, 0, -1); VdbeCoverage(v); - sqlite3VdbeGoto(v, iContinue); - VdbeComment((v, "skip OFFSET records")); - sqlite3VdbeJumpHere(v, addr); + sqlite3VdbeAddOp3(v, OP_IfPos, iOffset, iContinue, 1); VdbeCoverage(v); + VdbeComment((v, "OFFSET")); } } @@ -1815,7 +1812,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ Vdbe *v = 0; int iLimit = 0; int iOffset; - int addr1, n; + int n; if( p->iLimit ) return; /* @@ -1850,14 +1847,10 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ sqlite3ExprCode(pParse, p->pOffset, iOffset); sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v); VdbeComment((v, "OFFSET counter")); - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset); - sqlite3VdbeJumpHere(v, addr1); + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iOffset, iOffset, 0); sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1); VdbeComment((v, "LIMIT+OFFSET")); - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1); - sqlite3VdbeJumpHere(v, addr1); + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iLimit, iOffset+1, -1); } } } @@ -2273,6 +2266,11 @@ static int multiSelect( if( p->iLimit ){ addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); VdbeComment((v, "Jump ahead if LIMIT reached")); + if( p->iOffset ){ + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, p->iOffset, p->iOffset, 0); + sqlite3VdbeAddOp3(v, OP_Add, p->iLimit, p->iOffset, p->iOffset+1); + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, p->iLimit, p->iOffset+1, -1); + } } explainSetInteger(iSub2, pParse->iNextSelectId); rc = sqlite3Select(pParse, p, &dest); @@ -2580,12 +2578,12 @@ static int generateOutputSubroutine( /* Suppress duplicates for UNION, EXCEPT, and INTERSECT */ if( regPrev ){ - int j1, j2; - j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v); - j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst, + int addr1, addr2; + addr1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v); + addr2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst, (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); - sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); VdbeCoverage(v); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeAddOp3(v, OP_Jump, addr2+2, iContinue, addr2+2); VdbeCoverage(v); + sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1); sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev); } @@ -2802,7 +2800,7 @@ static int multiSelectOrderBy( int savedOffset; /* Saved value of p->iOffset */ int labelCmpr; /* Label for the start of the merge algorithm */ int labelEnd; /* Label for the end of the overall SELECT stmt */ - int j1; /* Jump instructions that get retargetted */ + int addr1; /* Jump instructions that get retargetted */ int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ @@ -2938,19 +2936,19 @@ static int multiSelectOrderBy( ** left of the compound operator - the "A" select. */ addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; - j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); + addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); VdbeComment((v, "left SELECT")); pPrior->iLimit = regLimitA; explainSetInteger(iSub1, pParse->iNextSelectId); sqlite3Select(pParse, pPrior, &destA); sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrA); - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); /* Generate a coroutine to evaluate the SELECT statement on ** the right - the "B" select */ addrSelectB = sqlite3VdbeCurrentAddr(v) + 1; - j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); + addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); VdbeComment((v, "right SELECT")); savedLimit = p->iLimit; savedOffset = p->iOffset; @@ -3041,7 +3039,7 @@ static int multiSelectOrderBy( /* This code runs once to initialize everything. */ - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); @@ -3084,7 +3082,7 @@ static int multiSelectOrderBy( #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Forward Declarations */ static void substExprList(sqlite3*, ExprList*, int, ExprList*); -static void substSelect(sqlite3*, Select *, int, ExprList *); +static void substSelect(sqlite3*, Select *, int, ExprList*, int); /* ** Scan through the expression pExpr. Replace every reference to @@ -3121,7 +3119,7 @@ static Expr *substExpr( pExpr->pLeft = substExpr(db, pExpr->pLeft, iTable, pEList); pExpr->pRight = substExpr(db, pExpr->pRight, iTable, pEList); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - substSelect(db, pExpr->x.pSelect, iTable, pEList); + substSelect(db, pExpr->x.pSelect, iTable, pEList, 1); }else{ substExprList(db, pExpr->x.pList, iTable, pEList); } @@ -3144,25 +3142,28 @@ static void substSelect( sqlite3 *db, /* Report malloc errors here */ Select *p, /* SELECT statement in which to make substitutions */ int iTable, /* Table to be replaced */ - ExprList *pEList /* Substitute values */ + ExprList *pEList, /* Substitute values */ + int doPrior /* Do substitutes on p->pPrior too */ ){ SrcList *pSrc; struct SrcList_item *pItem; int i; if( !p ) return; - substExprList(db, p->pEList, iTable, pEList); - substExprList(db, p->pGroupBy, iTable, pEList); - substExprList(db, p->pOrderBy, iTable, pEList); - p->pHaving = substExpr(db, p->pHaving, iTable, pEList); - p->pWhere = substExpr(db, p->pWhere, iTable, pEList); - substSelect(db, p->pPrior, iTable, pEList); - pSrc = p->pSrc; - assert( pSrc ); /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */ - if( ALWAYS(pSrc) ){ + do{ + substExprList(db, p->pEList, iTable, pEList); + substExprList(db, p->pGroupBy, iTable, pEList); + substExprList(db, p->pOrderBy, iTable, pEList); + p->pHaving = substExpr(db, p->pHaving, iTable, pEList); + p->pWhere = substExpr(db, p->pWhere, iTable, pEList); + pSrc = p->pSrc; + assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ - substSelect(db, pItem->pSelect, iTable, pEList); + substSelect(db, pItem->pSelect, iTable, pEList, 1); + if( pItem->fg.isTabFunc ){ + substExprList(db, pItem->u1.pFuncArg, iTable, pEList); + } } - } + }while( doPrior && (p = p->pPrior)!=0 ); } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ @@ -3314,7 +3315,7 @@ static int flattenSubquery( int subqueryIsAgg /* True if the subquery uses aggregate functions */ ){ const char *zSavedAuthContext = pParse->zAuthContext; - Select *pParent; + Select *pParent; /* Current UNION ALL term of the other query */ Select *pSub; /* The inner query or "subquery" */ Select *pSub1; /* Pointer to the rightmost select in sub-query */ SrcList *pSrc; /* The FROM clause of the outer query */ @@ -3609,9 +3610,9 @@ static int flattenSubquery( ** ** The outer query has 3 slots in its FROM clause. One slot of the ** outer query (the middle slot) is used by the subquery. The next - ** block of code will expand the out query to 4 slots. The middle - ** slot is expanded to two slots in order to make space for the - ** two elements in the FROM clause of the subquery. + ** block of code will expand the outer query FROM clause to 4 slots. + ** The middle slot is expanded to two slots in order to make space + ** for the two elements in the FROM clause of the subquery. */ if( nSubSrc>1 ){ pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1); @@ -3650,11 +3651,6 @@ static int flattenSubquery( pList->a[i].zName = zName; } } - substExprList(db, pParent->pEList, iParent, pSub->pEList); - if( isAgg ){ - substExprList(db, pParent->pGroupBy, iParent, pSub->pEList); - pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList); - } if( pSub->pOrderBy ){ /* At this point, any non-zero iOrderByCol values indicate that the ** ORDER BY column expression is identical to the iOrderByCol'th @@ -3674,27 +3670,20 @@ static int flattenSubquery( assert( pSub->pPrior==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; - }else if( pParent->pOrderBy ){ - substExprList(db, pParent->pOrderBy, iParent, pSub->pEList); - } - if( pSub->pWhere ){ - pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); - }else{ - pWhere = 0; } + pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); if( subqueryIsAgg ){ assert( pParent->pHaving==0 ); pParent->pHaving = pParent->pWhere; pParent->pWhere = pWhere; - pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList); pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving, sqlite3ExprDup(db, pSub->pHaving, 0)); assert( pParent->pGroupBy==0 ); pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0); }else{ - pParent->pWhere = substExpr(db, pParent->pWhere, iParent, pSub->pEList); pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere); } + substSelect(db, pParent, iParent, pSub->pEList, 0); /* The flattened query is distinct if either the inner or the ** outer query is distinct. @@ -4221,17 +4210,9 @@ static int selectExpander(Walker *pWalker, Select *p){ */ for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab; - assert( pFrom->fg.isRecursive==0 || pFrom->pTab ); + assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 ); if( pFrom->fg.isRecursive ) continue; - if( pFrom->pTab!=0 ){ - /* This statement has already been prepared. There is no need - ** to go further. */ - assert( i==0 ); -#ifndef SQLITE_OMIT_CTE - selectPopWith(pWalker, p); -#endif - return WRC_Prune; - } + assert( pFrom->pTab==0 ); #ifndef SQLITE_OMIT_CTE if( withExpand(pWalker, pFrom) ) return WRC_Abort; if( pFrom->pTab ) {} else @@ -4267,6 +4248,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pTab->nRef++; #if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE) if( pTab->pSelect || IsVirtual(pTab) ){ + i16 nCol; if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; assert( pFrom->pSelect==0 ); if( pFrom->fg.isTabFunc && !IsVirtual(pTab) ){ @@ -4275,7 +4257,10 @@ static int selectExpander(Walker *pWalker, Select *p){ } pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0); sqlite3SelectSetName(pFrom->pSelect, pTab->zName); + nCol = pTab->nCol; + pTab->nCol = -1; sqlite3WalkSelect(pWalker, pFrom->pSelect); + pTab->nCol = nCol; } #endif } @@ -4523,19 +4508,19 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ struct SrcList_item *pFrom; assert( p->selFlags & SF_Resolved ); - if( (p->selFlags & SF_HasTypeInfo)==0 ){ - p->selFlags |= SF_HasTypeInfo; - pParse = pWalker->pParse; - pTabList = p->pSrc; - for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ - Table *pTab = pFrom->pTab; - if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){ - /* A sub-query in the FROM clause of a SELECT */ - Select *pSel = pFrom->pSelect; - if( pSel ){ - while( pSel->pPrior ) pSel = pSel->pPrior; - selectAddColumnTypeAndCollation(pParse, pTab, pSel); - } + assert( (p->selFlags & SF_HasTypeInfo)==0 ); + p->selFlags |= SF_HasTypeInfo; + pParse = pWalker->pParse; + pTabList = p->pSrc; + for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ + Table *pTab = pFrom->pTab; + assert( pTab!=0 ); + if( (pTab->tabFlags & TF_Ephemeral)!=0 ){ + /* A sub-query in the FROM clause of a SELECT */ + Select *pSel = pFrom->pSelect; + if( pSel ){ + while( pSel->pPrior ) pSel = pSel->pPrior; + selectAddColumnTypeAndCollation(pParse, pTab, pSel); } } } @@ -4861,7 +4846,17 @@ int sqlite3Select( struct SrcList_item *pItem = &pTabList->a[i]; Select *pSub = pItem->pSelect; int isAggSub; + Table *pTab = pItem->pTab; if( pSub==0 ) continue; + + /* Catch mismatch in the declared columns of a view and the number of + ** columns in the SELECT on the RHS */ + if( pTab->nCol!=pSub->pEList->nExpr ){ + sqlite3ErrorMsg(pParse, "expected %d columns for '%s' but got %d", + pTab->nCol, pTab->zName, pSub->pEList->nExpr); + goto select_end; + } + isAggSub = (pSub->selFlags & SF_Aggregate)!=0; if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){ /* This subquery can be absorbed into its parent. */ @@ -5213,7 +5208,7 @@ int sqlite3Select( */ if( pGroupBy ){ KeyInfo *pKeyInfo; /* Keying information for the group by clause */ - int j1; /* A-vs-B comparision jump */ + int addr1; /* A-vs-B comparision jump */ int addrOutputRow; /* Start of subroutine that outputs a result row */ int regOutputRow; /* Return address register for output subroutine */ int addrSetAbort; /* Set the abort flag and return */ @@ -5361,8 +5356,8 @@ int sqlite3Select( } sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); - j1 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); VdbeCoverage(v); + addr1 = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp3(v, OP_Jump, addr1+1, 0, addr1+1); VdbeCoverage(v); /* Generate code that runs whenever the GROUP BY changes. ** Changes in the GROUP BY are detected by the previous code @@ -5384,7 +5379,7 @@ int sqlite3Select( /* Update the aggregate accumulators based on the content of ** the current row */ - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); updateAccumulator(pParse, &sAggInfo); sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); VdbeComment((v, "indicate data in accumulator")); diff --git a/src/shell.c b/src/shell.c index e5eb394ead..f79087122c 100644 --- a/src/shell.c +++ b/src/shell.c @@ -2612,6 +2612,22 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ return 0; } +/* +** Print the current sqlite3_errmsg() value to stderr and return 1. +*/ +static int shellDatabaseError(sqlite3 *db){ + const char *zErr = sqlite3_errmsg(db); + fprintf(stderr, "Error: %s\n", zErr); + return 1; +} + +/* +** Print an out-of-memory message to stderr and return 1. +*/ +static int shellNomemError(void){ + fprintf(stderr, "Error: out of memory\n"); + return 1; +} /* ** If an input line begins with "." then invoke this routine to @@ -3713,13 +3729,17 @@ static int do_meta_command(char *zLine, ShellState *p){ int ii; open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); - if( rc ) return rc; + if( rc ) return shellDatabaseError(p->db); + + /* Create an SQL statement to query for the list of tables in the + ** main and all attached databases where the table name matches the + ** LIKE pattern bound to variable "?1". */ zSql = sqlite3_mprintf( "SELECT name FROM sqlite_master" " WHERE type IN ('table','view')" " AND name NOT LIKE 'sqlite_%%'" " AND name LIKE ?1"); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ + while( zSql && sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue; if( strcmp(zDbName,"temp")==0 ){ @@ -3738,11 +3758,17 @@ static int do_meta_command(char *zLine, ShellState *p){ " AND name LIKE ?1", zSql, zDbName, zDbName); } } - sqlite3_finalize(pStmt); - zSql = sqlite3_mprintf("%z ORDER BY 1", zSql); - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3_finalize(pStmt); + if( zSql && rc==SQLITE_OK ){ + zSql = sqlite3_mprintf("%z ORDER BY 1", zSql); + if( zSql ) rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + } sqlite3_free(zSql); - if( rc ) return rc; + if( !zSql ) return shellNomemError(); + if( rc ) return shellDatabaseError(p->db); + + /* Run the SQL statement prepared by the above block. Store the results + ** as an array of nul-terminated strings in azResult[]. */ nRow = nAlloc = 0; azResult = 0; if( nArg>1 ){ @@ -3756,17 +3782,25 @@ static int do_meta_command(char *zLine, ShellState *p){ int n2 = nAlloc*2 + 10; azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2); if( azNew==0 ){ - fprintf(stderr, "Error: out of memory\n"); + rc = shellNomemError(); break; } nAlloc = n2; azResult = azNew; } azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); - if( azResult[nRow] ) nRow++; + if( 0==azResult[nRow] ){ + rc = shellNomemError(); + break; + } + nRow++; } - sqlite3_finalize(pStmt); - if( nRow>0 ){ + if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ + rc = shellDatabaseError(p->db); + } + + /* Pretty-print the contents of array azResult[] to the output */ + if( rc==0 && nRow>0 ){ int len, maxlen = 0; int i, j; int nPrintCol, nPrintRow; @@ -3785,6 +3819,7 @@ static int do_meta_command(char *zLine, ShellState *p){ fprintf(p->out, "\n"); } } + for(ii=0; iivalue_free #define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 #define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 -/* Version 3.8.12 and later */ +/* Version 3.9.0 and later */ #define sqlite3_value_subtype sqlite3_api->value_subtype #define sqlite3_result_subtype sqlite3_api->result_subtype #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4a9b135312..061c3cbd0f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -196,6 +196,7 @@ # include # pragma intrinsic(_byteswap_ushort) # pragma intrinsic(_byteswap_ulong) +# pragma intrinsic(_ReadWriteBarrier) # else # include # endif @@ -1918,6 +1919,12 @@ struct Index { /* Return true if index X is a UNIQUE index */ #define IsUniqueIndex(X) ((X)->onError!=OE_None) +/* The Index.aiColumn[] values are normally positive integer. But +** there are some negative values that have special meaning: +*/ +#define XN_ROWID (-1) /* Indexed column is the rowid */ +#define XN_EXPR (-2) /* Indexed column is an expression */ + /* ** 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 @@ -3510,6 +3517,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int); # define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) +# define sqlite3IsToplevel(p) ((p)->pToplevel==0) #else # define sqlite3TriggersExist(B,C,D,E,F) 0 # define sqlite3DeleteTrigger(A,B) @@ -3519,6 +3527,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); # define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F) # define sqlite3TriggerList(X, Y) 0 # define sqlite3ParseToplevel(p) p +# define sqlite3IsToplevel(p) 1 # define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0 #endif diff --git a/src/test1.c b/src/test1.c index 1f76ea5ba5..6c47754bf9 100644 --- a/src/test1.c +++ b/src/test1.c @@ -6377,7 +6377,6 @@ static int tclLoadStaticExtensionCmd( extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); - extern int sqlite3_json_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); @@ -6385,7 +6384,6 @@ static int tclLoadStaticExtensionCmd( extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); - extern int sqlite3_fts5_init(sqlite3*,char**,const sqlite3_api_routines*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); @@ -6393,13 +6391,9 @@ static int tclLoadStaticExtensionCmd( { "amatch", sqlite3_amatch_init }, { "closure", sqlite3_closure_init }, { "eval", sqlite3_eval_init }, -#ifdef SQLITE_ENABLE_FTS5 - { "fts5", sqlite3_fts5_init }, -#endif { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, - { "json", sqlite3_json_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_init }, { "regexp", sqlite3_regexp_init }, @@ -6426,7 +6420,11 @@ static int tclLoadStaticExtensionCmd( Tcl_AppendResult(interp, "no such extension: ", zName, (char*)0); return TCL_ERROR; } - rc = aExtension[i].pInit(db, &zErrMsg, 0); + if( aExtension[i].pInit ){ + rc = aExtension[i].pInit(db, &zErrMsg, 0); + }else{ + rc = SQLITE_OK; + } if( rc!=SQLITE_OK || zErrMsg ){ Tcl_AppendResult(interp, "initialization of ", zName, " failed: ", zErrMsg, (char*)0); diff --git a/src/test_config.c b/src/test_config.c index 0aa29c70d7..5da2df16b7 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -167,6 +167,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "atomicwrite", "0", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_ENABLE_JSON1 + Tcl_SetVar2(interp, "sqlite_options", "json1", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "json1", "0", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_ATTACH Tcl_SetVar2(interp, "sqlite_options", "attach", "0", TCL_GLOBAL_ONLY); #else diff --git a/src/threads.c b/src/threads.c index 4ce6122274..251b9b7631 100644 --- a/src/threads.c +++ b/src/threads.c @@ -67,6 +67,10 @@ int sqlite3ThreadCreate( memset(p, 0, sizeof(*p)); p->xTask = xTask; p->pIn = pIn; + /* 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 + ** for testing purposes. */ if( sqlite3FaultSim(200) ){ rc = 1; }else{ @@ -151,7 +155,12 @@ int sqlite3ThreadCreate( *ppThread = 0; p = sqlite3Malloc(sizeof(*p)); if( p==0 ) return SQLITE_NOMEM; - if( sqlite3GlobalConfig.bCoreMutex==0 ){ + /* 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 + ** (via the sqlite3FaultSim() term of the conditional) for testing + ** purposes. */ + if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){ memset(p, 0, sizeof(*p)); }else{ p->xTask = xTask; @@ -179,7 +188,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ assert( ppOut!=0 ); if( NEVER(p==0) ) return SQLITE_NOMEM; if( p->xTask==0 ){ - assert( p->id==GetCurrentThreadId() ); + /* assert( p->id==GetCurrentThreadId() ); */ rc = WAIT_OBJECT_0; assert( p->tid==0 ); }else{ diff --git a/src/update.c b/src/update.c index 94f7a4dd99..c814a5b68a 100644 --- a/src/update.c +++ b/src/update.c @@ -134,9 +134,9 @@ void sqlite3Update( /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ - int regOldRowid; /* The old rowid */ - int regNewRowid; /* The new rowid */ - int regNew; /* Content of the NEW.* table in triggers */ + int regOldRowid = 0; /* The old rowid */ + int regNewRowid = 0; /* The new rowid */ + int regNew = 0; /* Content of the NEW.* table in triggers */ int regOld = 0; /* Content of OLD.* table in triggers */ int regRowSet = 0; /* Rowset of rows to be updated */ int regKey = 0; /* composite PRIMARY KEY value */ @@ -300,29 +300,20 @@ void sqlite3Update( if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, 1, iDb); -#ifndef SQLITE_OMIT_VIRTUALTABLE - /* Virtual tables must be handled separately */ - if( IsVirtual(pTab) ){ - updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, - pWhere, onError); - pWhere = 0; - pTabList = 0; - goto update_cleanup; - } -#endif - /* Allocate required registers. */ - regRowSet = ++pParse->nMem; - regOldRowid = regNewRowid = ++pParse->nMem; - if( chngPk || pTrigger || hasFK ){ - regOld = pParse->nMem + 1; + if( !IsVirtual(pTab) ){ + regRowSet = ++pParse->nMem; + regOldRowid = regNewRowid = ++pParse->nMem; + if( chngPk || pTrigger || hasFK ){ + regOld = pParse->nMem + 1; + pParse->nMem += pTab->nCol; + } + if( chngKey || pTrigger || hasFK ){ + regNewRowid = ++pParse->nMem; + } + regNew = pParse->nMem + 1; pParse->nMem += pTab->nCol; } - if( chngKey || pTrigger || hasFK ){ - regNewRowid = ++pParse->nMem; - } - regNew = pParse->nMem + 1; - pParse->nMem += pTab->nCol; /* Start the view context. */ if( isView ){ @@ -345,6 +336,15 @@ void sqlite3Update( goto update_cleanup; } +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* Virtual tables must be handled separately */ + if( IsVirtual(pTab) ){ + updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, + pWhere, onError); + goto update_cleanup; + } +#endif + /* Begin the database scan */ if( HasRowid(pTab) ){ @@ -384,7 +384,7 @@ void sqlite3Update( if( pWInfo==0 ) goto update_cleanup; okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); for(i=0; iaiColumn[i]>=(-1) ); + assert( pPk->aiColumn[i]>=0 ); sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i], iPk+i); } @@ -507,7 +507,6 @@ void sqlite3Update( newmask = sqlite3TriggerColmask( pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError ); - /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/ for(i=0; inCol; i++){ if( i==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); @@ -565,7 +564,7 @@ void sqlite3Update( } if( !isView ){ - int j1 = 0; /* Address of jump instruction */ + int addr1 = 0; /* Address of jump instruction */ int bReplace = 0; /* True if REPLACE conflict resolution might happen */ /* Do constraint checks. */ @@ -581,9 +580,9 @@ void sqlite3Update( /* Delete the index entries associated with the current record. */ if( bReplace || chngKey ){ if( pPk ){ - j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey); + addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey); }else{ - j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid); + addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid); } VdbeCoverageNeverTaken(v); } @@ -594,7 +593,7 @@ void sqlite3Update( sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0); } if( bReplace || chngKey ){ - sqlite3VdbeJumpHere(v, j1); + sqlite3VdbeJumpHere(v, addr1); } if( hasFK ){ @@ -685,21 +684,23 @@ update_cleanup: /* ** Generate code for an UPDATE of a virtual table. ** -** The strategy is that we create an ephemeral table that contains +** There are two possible strategies - the default and the special +** "onepass" strategy. Onepass is only used if the virtual table +** implementation indicates that pWhere may match at most one row. +** +** The default strategy is to create an ephemeral table that contains ** for each row to be changed: ** ** (A) The original rowid of that row. -** (B) The revised rowid for the row. (note1) +** (B) The revised rowid for the row. ** (C) The content of every column in the row. ** -** Then we loop over this ephemeral table and for each row in -** the ephemeral table call VUpdate. +** Then loop through the contents of this ephemeral table executing a +** VUpdate for each row. When finished, drop the ephemeral table. ** -** When finished, drop the ephemeral table. -** -** (note1) Actually, if we know in advance that (A) is always the same -** as (B) we only store (A), then duplicate (A) when pulling -** it out of the ephemeral table before calling VUpdate. +** The "onepass" strategy does not use an ephemeral table. Instead, it +** stores the same values (A, B and C above) in a register array and +** makes a single invocation of VUpdate. */ static void updateVirtualTable( Parse *pParse, /* The parsing context */ @@ -712,65 +713,95 @@ static void updateVirtualTable( int onError /* ON CONFLICT strategy */ ){ Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ - ExprList *pEList = 0; /* The result set of the SELECT statement */ - Select *pSelect = 0; /* The SELECT statement */ - Expr *pExpr; /* Temporary expression */ int ephemTab; /* Table holding the result of the SELECT */ int i; /* Loop counter */ - int addr; /* Address of top of loop */ - int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 *db = pParse->db; /* Database connection */ const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); - SelectDest dest; + WhereInfo *pWInfo; + int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */ + int regArg; /* First register in VUpdate arg array */ + int regRec; /* Register in which to assemble record */ + int regRowid; /* Register for ephem table rowid */ + int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */ + int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */ + int bOnePass; /* True to use onepass strategy */ + int addr; /* Address of OP_OpenEphemeral */ - /* Construct the SELECT statement that will find the new values for - ** all updated rows. - */ - pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_")); - if( pRowid ){ - pEList = sqlite3ExprListAppend(pParse, pEList, - sqlite3ExprDup(db, pRowid, 0)); - } - assert( pTab->iPKey<0 ); - for(i=0; inCol; i++){ - if( aXRef[i]>=0 ){ - pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0); - }else{ - pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName); - } - pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); - } - pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); - - /* Create the ephemeral table into which the update results will - ** be stored. - */ + /* Allocate nArg registers to martial the arguments to VUpdate. Then + ** create and open the ephemeral table in which the records created from + ** these arguments will be temporarily stored. */ assert( v ); ephemTab = pParse->nTab++; + addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg); + regArg = pParse->nMem + 1; + pParse->nMem += nArg; + regRec = ++pParse->nMem; + regRowid = ++pParse->nMem; - /* fill the ephemeral table - */ - sqlite3SelectDestInit(&dest, SRT_EphemTab, ephemTab); - sqlite3Select(pParse, pSelect, &dest); + /* Start scanning the virtual table */ + pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0); + if( pWInfo==0 ) return; - /* Generate code to scan the ephemeral table and call VUpdate. */ - iReg = ++pParse->nMem; - pParse->nMem += pTab->nCol+1; - addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); - sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1); + /* Populate the argument registers. */ + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); + if( pRowid ){ + sqlite3ExprCode(pParse, pRowid, regArg+1); + }else{ + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1); + } for(i=0; inCol; i++){ - sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); + if( aXRef[i]>=0 ){ + sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); + }else{ + sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); + } + } + + bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy); + + if( bOnePass ){ + /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded + ** above. Also, if this is a top-level parse (not a trigger), clear the + ** multi-write flag so that the VM does not open a statement journal */ + sqlite3VdbeChangeToNoop(v, addr); + if( sqlite3IsToplevel(pParse) ){ + pParse->isMultiWrite = 0; + } + }else{ + /* Create a record from the argument register contents and insert it into + ** the ephemeral table. */ + sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec); + sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid); + } + + + if( bOnePass==0 ){ + /* End the virtual table scan */ + sqlite3WhereEnd(pWInfo); + + /* Begin scannning through the ephemeral table. */ + addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v); + + /* Extract arguments from the current row of the ephemeral table and + ** invoke the VUpdate method. */ + for(i=0; inCol+2, iReg, pVTab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, nArg, regArg, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite3MayAbort(pParse); - sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v); - sqlite3VdbeJumpHere(v, addr); - sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); - /* Cleanup */ - sqlite3SelectDelete(db, pSelect); + /* End of the ephemeral table scan. Or, if using the onepass strategy, + ** jump to here if the scan visited zero rows. */ + if( bOnePass==0 ){ + sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v); + sqlite3VdbeJumpHere(v, addr); + sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); + }else{ + sqlite3WhereEnd(pWInfo); + } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ diff --git a/src/vdbe.c b/src/vdbe.c index 41ad915b16..0388c3235e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -5675,12 +5675,12 @@ case OP_MemMax: { /* in2 */ } #endif /* SQLITE_OMIT_AUTOINCREMENT */ -/* Opcode: IfPos P1 P2 * * * -** Synopsis: if r[P1]>0 goto P2 +/* Opcode: IfPos P1 P2 P3 * * +** Synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 ** ** Register P1 must contain an integer. -** If the value of register P1 is 1 or greater, jump to P2 and -** add the literal value P3 to register P1. +** If the value of register P1 is 1 or greater, subtract P3 from the +** value in P1 and jump to P2. ** ** If the initial value of register P1 is less than 1, then the ** value is unchanged and control passes through to the next instruction. @@ -5689,38 +5689,44 @@ case OP_IfPos: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); VdbeBranchTaken( pIn1->u.i>0, 2); - if( pIn1->u.i>0 ) goto jump_to_p2; + if( pIn1->u.i>0 ){ + pIn1->u.i -= pOp->p3; + goto jump_to_p2; + } break; } -/* Opcode: IfNeg P1 P2 P3 * * -** Synopsis: r[P1]+=P3, if r[P1]<0 goto P2 +/* Opcode: SetIfNotPos P1 P2 P3 * * +** Synopsis: if r[P1]<=0 then r[P2]=P3 ** -** Register P1 must contain an integer. Add literal P3 to the value in -** register P1 then if the value of register P1 is less than zero, jump to P2. +** Register P1 must contain an integer. +** If the value of register P1 is not positive (if it is less than 1) then +** set the value of register P2 to be the integer P3. */ -case OP_IfNeg: { /* jump, in1 */ +case OP_SetIfNotPos: { /* in1, in2 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); - pIn1->u.i += pOp->p3; - VdbeBranchTaken(pIn1->u.i<0, 2); - if( pIn1->u.i<0 ) goto jump_to_p2; + if( pIn1->u.i<=0 ){ + pOut = out2Prerelease(p, pOp); + pOut->u.i = pOp->p3; + } break; } /* Opcode: IfNotZero P1 P2 P3 * * -** Synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2 +** Synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2 ** ** Register P1 must contain an integer. If the content of register P1 is -** initially nonzero, then add P3 to P1 and jump to P2. If register P1 is -** initially zero, leave it unchanged and fall through. +** initially nonzero, then subtract P3 from the value in register P1 and +** jump to P2. If register P1 is initially zero, leave it unchanged +** and fall through. */ case OP_IfNotZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); VdbeBranchTaken(pIn1->u.i<0, 2); if( pIn1->u.i ){ - pIn1->u.i += pOp->p3; + pIn1->u.i -= pOp->p3; goto jump_to_p2; } break; diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 2cdc3edb00..30a329189e 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -248,7 +248,7 @@ int sqlite3_blob_open( int j; for(j=0; jnKeyCol; j++){ /* FIXME: Be smarter about indexes that use expressions */ - if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==(-2) ){ + if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){ zFault = "indexed"; } } diff --git a/src/vtab.c b/src/vtab.c index 041805ca49..6054df3d71 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -937,7 +937,9 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ if( rc==SQLITE_OK ){ rc = pModule->xBegin(pVTab->pVtab); if( rc==SQLITE_OK ){ + int iSvpt = db->nStatement + db->nSavepoint; addToVTrans(db, pVTab); + if( iSvpt ) rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, iSvpt-1); } } } @@ -1143,7 +1145,7 @@ int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ */ void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){ Table *pTab = pMod->pEpoTab; - if( (pTab = pMod->pEpoTab)!=0 ){ + if( pTab!=0 ){ sqlite3DeleteColumnNames(db, pTab); sqlite3VtabClear(db, pTab); sqlite3DbFree(db, pTab); diff --git a/src/where.c b/src/where.c index 8f000188eb..633c2f6d0f 100644 --- a/src/where.c +++ b/src/where.c @@ -87,6 +87,13 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ */ int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){ memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2); +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace && pWInfo->eOnePass!=ONEPASS_OFF ){ + sqlite3DebugPrintf("%s cursors: %d %d\n", + pWInfo->eOnePass==ONEPASS_SINGLE ? "ONEPASS_SINGLE" : "ONEPASS_MULTI", + aiCur[0], aiCur[1]); + } +#endif return pWInfo->eOnePass; } @@ -182,13 +189,13 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ while( pScan->iEquiv<=pScan->nEquiv ){ iCur = pScan->aiCur[pScan->iEquiv-1]; iColumn = pScan->aiColumn[pScan->iEquiv-1]; - if( iColumn==(-2) && pScan->pIdxExpr==0 ) return 0; + if( iColumn==XN_EXPR && pScan->pIdxExpr==0 ) return 0; while( (pWC = pScan->pWC)!=0 ){ for(pTerm=pWC->a+k; knTerm; k++, pTerm++){ if( pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn - && (iColumn!=(-2) - || sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0) + && (iColumn!=XN_EXPR + || sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0) && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) ){ if( (pTerm->eOperator & WO_EQUIV)!=0 @@ -281,7 +288,7 @@ static WhereTerm *whereScanInit( if( pIdx ){ j = iColumn; iColumn = pIdx->aiColumn[j]; - if( iColumn==(-2) ) pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; + if( iColumn==XN_EXPR ) pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; } if( pIdx && iColumn>=0 ){ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; @@ -720,7 +727,7 @@ static void constructAutomaticIndex( } } assert( n==nKeyCol ); - pIdx->aiColumn[n] = -1; + pIdx->aiColumn[n] = XN_ROWID; pIdx->azColl[n] = "BINARY"; /* Create the automatic index */ @@ -1159,6 +1166,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){ ** Return the affinity for a single column of an index. */ static char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){ + assert( iCol>=0 && iColnColumn ); if( !pIdx->zColAff ){ if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB; } @@ -1216,8 +1224,7 @@ static int whereRangeSkipScanEst( int nLower = -1; int nUpper = p->nSample+1; int rc = SQLITE_OK; - int iCol = p->aiColumn[nEq]; - u8 aff = sqlite3IndexColumnAffinity(db, p, iCol); + u8 aff = sqlite3IndexColumnAffinity(db, p, nEq); CollSeq *pColl; sqlite3_value *p1 = 0; /* Value extracted from pLower */ @@ -2235,7 +2242,9 @@ static int whereLoopAddBtreeIndex( int iCol = pProbe->aiColumn[saved_nEq]; pNew->wsFlags |= WHERE_COLUMN_EQ; assert( saved_nEq==pNew->u.btree.nEq ); - if( iCol==(-1) || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) ){ + if( iCol==XN_ROWID + || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) + ){ if( iCol>=0 && pProbe->uniqNotNull==0 ){ pNew->wsFlags |= WHERE_UNQ_WANTED; }else{ @@ -2421,18 +2430,25 @@ static int indexMightHelpWithOrderBy( int iCursor ){ ExprList *pOB; + ExprList *aColExpr; int ii, jj; if( pIndex->bUnordered ) return 0; if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; for(ii=0; iinExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr); - if( pExpr->op!=TK_COLUMN ) return 0; - if( pExpr->iTable==iCursor ){ + if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ if( pExpr->iColumn<0 ) return 1; for(jj=0; jjnKeyCol; jj++){ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; } + }else if( (aColExpr = pIndex->aColExpr)!=0 ){ + for(jj=0; jjnKeyCol; jj++){ + if( pIndex->aiColumn[jj]!=XN_EXPR ) continue; + if( sqlite3ExprCompare(pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){ + return 1; + } + } } } return 0; @@ -2580,7 +2596,7 @@ static int whereLoopAddBtree( && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ - && HasRowid(pTab) /* Is not a WITHOUT ROWID table. (FIXME: Why not?) */ + && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ ){ @@ -2825,6 +2841,7 @@ static int whereLoopAddVirtual( pIdxInfo->orderByConsumed = 0; pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; pIdxInfo->estimatedRows = 25; + pIdxInfo->idxFlags = 0; rc = vtabBestIndex(pParse, pTab, pIdxInfo); if( rc ) goto whereLoopAddVtab_exit; pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; @@ -2870,6 +2887,7 @@ static int whereLoopAddVirtual( ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; + pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; } } } @@ -2885,6 +2903,14 @@ static int whereLoopAddVirtual( 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); @@ -3206,7 +3232,8 @@ static i8 wherePathSatisfiesOrderBy( nKeyCol = pIndex->nKeyCol; nColumn = pIndex->nColumn; assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) ); - assert( pIndex->aiColumn[nColumn-1]==(-1) || !HasRowid(pIndex->pTable)); + assert( pIndex->aiColumn[nColumn-1]==XN_ROWID + || !HasRowid(pIndex->pTable)); isOrderDistinct = IsUniqueIndex(pIndex); } @@ -3238,7 +3265,7 @@ static i8 wherePathSatisfiesOrderBy( revIdx = pIndex->aSortOrder[j]; if( iColumn==pIndex->pTable->iPKey ) iColumn = -1; }else{ - iColumn = -1; + iColumn = XN_ROWID; revIdx = 0; } @@ -3264,9 +3291,15 @@ static i8 wherePathSatisfiesOrderBy( testcase( wctrlFlags & WHERE_GROUPBY ); testcase( wctrlFlags & WHERE_DISTINCTBY ); if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; - if( pOBExpr->op!=TK_COLUMN ) continue; - if( pOBExpr->iTable!=iCur ) continue; - if( pOBExpr->iColumn!=iColumn ) continue; + if( iColumn>=(-1) ){ + if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->iTable!=iCur ) continue; + if( pOBExpr->iColumn!=iColumn ) continue; + }else{ + if( sqlite3ExprCompare(pOBExpr,pIndex->aColExpr->a[j].pExpr,iCur) ){ + continue; + } + } if( iColumn>=0 ){ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); if( !pColl ) pColl = db->pDfltColl; @@ -4097,7 +4130,8 @@ WhereInfo *sqlite3WhereBegin( } /* Construct the WhereLoop objects */ - WHERETRACE(0xffff,("*** Optimizer Start ***\n")); + WHERETRACE(0xffff,("*** Optimizer Start *** (wctrlFlags: 0x%x)\n", + wctrlFlags)); #if defined(WHERETRACE_ENABLED) if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */ int i; @@ -4519,7 +4553,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ }else if( pLoop->wsFlags & WHERE_MULTI_OR ){ pIdx = pLevel->u.pCovidx; } - if( pIdx && !db->mallocFailed ){ + if( pIdx + && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable)) + && !db->mallocFailed + ){ last = sqlite3VdbeCurrentAddr(v); k = pLevel->addrBody; pOp = sqlite3VdbeGetOp(v, k); @@ -4531,6 +4568,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ if( !HasRowid(pTab) ){ Index *pPk = sqlite3PrimaryKeyIndex(pTab); x = pPk->aiColumn[x]; + assert( x>=0 ); } x = sqlite3ColumnOfIndex(pIdx, x); if( x>=0 ){ diff --git a/src/wherecode.c b/src/wherecode.c index 25a7766e39..5ee5ec2840 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -46,8 +46,8 @@ static void explainAppendTerm( */ static const char *explainIndexColumnName(Index *pIdx, int i){ i = pIdx->aiColumn[i]; - if( i==(-2) ) return ""; - if( i==(-1) ) return "rowid"; + if( i==XN_EXPR ) return ""; + if( i==XN_ROWID ) return "rowid"; return pIdx->pTable->aCol[i].zName; } @@ -514,7 +514,7 @@ static int codeAllEqualityTerms( sqlite3VdbeJumpHere(v, j); for(j=0; jaiColumn[j]==(-2) ); + testcase( pIdx->aiColumn[j]==XN_EXPR ); VdbeComment((v, "%s", explainIndexColumnName(pIdx, j))); } } @@ -848,8 +848,8 @@ Bitmask sqlite3WhereCodeOneLoopStart( disableTerm(pLevel, pLoop->aLTerm[j]); } } - pLevel->op = OP_VNext; pLevel->p1 = iCur; + pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext; pLevel->p2 = sqlite3VdbeCurrentAddr(v); sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); sqlite3ExprCachePop(pParse); @@ -1416,7 +1416,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ - int j1 = 0; /* Address of jump operation */ + int jmp1 = 0; /* Address of jump operation */ if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){ pAndExpr->pLeft = pOrExpr; pOrExpr = pAndExpr; @@ -1443,7 +1443,8 @@ Bitmask sqlite3WhereCodeOneLoopStart( int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); if( HasRowid(pTab) ){ r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0); - j1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0, r,iSet); + jmp1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0, + r,iSet); VdbeCoverage(v); }else{ Index *pPk = sqlite3PrimaryKeyIndex(pTab); @@ -1473,7 +1474,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( ** need to insert the key into the temp table, as it will never ** be tested for. */ if( iSet ){ - j1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk); + jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk); VdbeCoverage(v); } if( iSet>=0 ){ @@ -1492,7 +1493,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( /* Jump here (skipping the main loop body subroutine) if the ** current sub-WHERE row is a duplicate from prior sub-WHEREs. */ - if( j1 ) sqlite3VdbeJumpHere(v, j1); + if( jmp1 ) sqlite3VdbeJumpHere(v, jmp1); /* The pSubWInfo->untestedTerms flag means that this OR term ** contained one or more AND term from a notReady table. The diff --git a/src/whereexpr.c b/src/whereexpr.c index dff425d0ea..fadde79010 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -950,7 +950,6 @@ static void exprAnalyze( pNew = pTerm; } exprCommute(pParse, pDup); - pLeft = sqlite3ExprSkipCollate(pDup->pLeft); pNew->leftCursor = iCur; pNew->u.leftColumn = iColumn; testcase( (prereqLeft | extraRight) != prereqLeft ); diff --git a/test/delete4.test b/test/delete4.test index 7334bf02ed..f3598a9496 100644 --- a/test/delete4.test +++ b/test/delete4.test @@ -98,5 +98,48 @@ do_execsql_test 3.4 { PRAGMA integrity_check; } {ok} +# Between 2015-09-14 and 2015-09-28, the following test cases would result +# in corruption (wrong # of entries in index) due to a bug in the ONEPASS +# optimization. +# +do_execsql_test 4.1 { + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(col0, col1); + INSERT INTO "t4" VALUES(14, 'abcde'); + CREATE INDEX idx_t4_0 ON t4 (col1, col0); + CREATE INDEX idx_t4_3 ON t4 (col0); + DELETE FROM t4 WHERE col0=69 OR col0>7; + PRAGMA integrity_check; +} {ok} +do_execsql_test 4.2 { + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(col0, col1); + INSERT INTO "t4" VALUES(14, 'abcde'); + CREATE INDEX idx_t4_3 ON t4 (col0); + CREATE INDEX idx_t4_0 ON t4 (col1, col0); + DELETE FROM t4 WHERE col0=69 OR col0>7; + PRAGMA integrity_check; +} {ok} +do_execsql_test 4.11 { + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(col0, col1, pk PRIMARY KEY) WITHOUT ROWID; + INSERT INTO t4 VALUES(14, 'abcde','xyzzy'); + CREATE INDEX idx_t4_0 ON t4 (col1, col0); + CREATE INDEX idx_t4_3 ON t4 (col0); + DELETE FROM t4 WHERE col0=69 OR col0>7; + PRAGMA integrity_check; +} {ok} +do_execsql_test 4.12 { + DROP TABLE IF EXISTS t4; + CREATE TABLE t4(col0, col1, pk PRIMARY KEY) WITHOUT ROWID; + INSERT INTO t4 VALUES(14, 'abcde','xyzzy'); + CREATE INDEX idx_t4_3 ON t4 (col0); + CREATE INDEX idx_t4_0 ON t4 (col1, col0); + DELETE FROM t4 WHERE col0=69 OR col0>7; + PRAGMA integrity_check; +} {ok} + + + finish_test diff --git a/test/fkey1.test b/test/fkey1.test index 0bd4939eb5..e10781ac52 100644 --- a/test/fkey1.test +++ b/test/fkey1.test @@ -151,4 +151,38 @@ do_execsql_test fkey1-4.2 { PRAGMA table_info="""1"; } {0 {"2} TEXT 0 {} 1 1 {"3} TEXT 0 {} 0} +#------------------------------------------------------------------------- +# +do_execsql_test fkey1-5.1 { + CREATE TABLE t11( + x INTEGER PRIMARY KEY, + parent REFERENCES t11 ON DELETE CASCADE + ); + INSERT INTO t11 VALUES (1, NULL), (2, 1), (3, 2); +} {} + +# The REPLACE part of this statement deletes the row (2, 1). Then the +# DELETE CASCADE caused by deleting that row removes the (3, 2) row. Which +# would have been the parent of the new row being inserted. Causing an +# FK violation. +# +do_catchsql_test fkey1-5.2 { + INSERT OR REPLACE INTO t11 VALUES (2, 3); +} {1 {FOREIGN KEY constraint failed}} + +# A similar test to the above. +do_execsql_test fkey1-5.3 { + CREATE TABLE Foo ( + Id INTEGER PRIMARY KEY, + ParentId INTEGER REFERENCES Foo(Id) ON DELETE CASCADE, C1 + ); + INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (1, null, 'A'); + INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 1, 'A-2-1'); + INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (3, 2, 'A-3-2'); + INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (4, 3, 'A-4-3'); +} +do_catchsql_test fkey1-5.4 { + INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3'); +} {1 {FOREIGN KEY constraint failed}} + finish_test diff --git a/test/fts3conf.test b/test/fts3conf.test index e91efbefbe..e157c239ef 100644 --- a/test/fts3conf.test +++ b/test/fts3conf.test @@ -86,11 +86,11 @@ foreach {tn sql uses constraint data} [subst { 9 "INSERT OR IGNORE $T2" 1 0 {{a b c d} {e f g h} {i j k l} z} 10 "INSERT OR REPLACE $T2" 1 0 {{a b c d} y {i j k l} z} - 11 "UPDATE OR ROLLBACK $T3" 1 1 {{a b c d} {e f g h}} - 12 "UPDATE OR ABORT $T3" 1 1 {{a b c d} {e f g h} {i j k l}} - 13 "UPDATE OR FAIL $T3" 1 1 {{a b c d} {e f g h} {i j k l}} - 14 "UPDATE OR IGNORE $T3" 1 0 {{a b c d} {e f g h} {i j k l}} - 15 "UPDATE OR REPLACE $T3" 1 0 {{a b c d} {i j k l}} + 11 "UPDATE OR ROLLBACK $T3" 0 1 {{a b c d} {e f g h}} + 12 "UPDATE OR ABORT $T3" 0 1 {{a b c d} {e f g h} {i j k l}} + 13 "UPDATE OR FAIL $T3" 0 1 {{a b c d} {e f g h} {i j k l}} + 14 "UPDATE OR IGNORE $T3" 0 0 {{a b c d} {e f g h} {i j k l}} + 15 "UPDATE OR REPLACE $T3" 0 0 {{a b c d} {i j k l}} 16 "UPDATE OR ROLLBACK $T4" 1 1 {{a b c d} {e f g h}} 17 "UPDATE OR ABORT $T4" 1 1 {{a b c d} {e f g h} {i j k l}} @@ -178,4 +178,38 @@ do_execsql_test 3.8 { SELECT quote(matchinfo(t3, 'na')) FROM t3 WHERE t3 MATCH 'one' } {X'0200000002000000'} +#------------------------------------------------------------------------- +# Test that the xSavepoint is invoked correctly if the first write +# operation within a transaction is to a virtual table. +# +do_catchsql_test 4.1.1 { + CREATE VIRTUAL TABLE t0 USING fts4; + BEGIN; + INSERT INTO t0(rowid, content) SELECT + 1, 'abc' UNION ALL SELECT + 2, 'def' UNION ALL SELECT + 1, 'ghi'; +} {1 {constraint failed}} +do_execsql_test 4.1.2 { + COMMIT; +} +do_execsql_test 4.1.3 { + SELECT * FROM t0 WHERE t0 MATCH 'abc'; + INSERT INTO t0(t0) VALUES('integrity-check'); +} {} + +do_execsql_test 4.2.1 { + CREATE VIRTUAL TABLE t01 USING fts4; + BEGIN; + SAVEPOINT abc; + INSERT INTO t01 VALUES('a b c'); + ROLLBACK TO abc; + COMMIT; +} +do_execsql_test 4.2.2 { + SELECT * FROM t01 WHERE t01 MATCH 'b'; + INSERT INTO t01(t01) VALUES('integrity-check'); +} {} + finish_test + diff --git a/test/fts3expr3.test b/test/fts3expr3.test index a8d7319266..83c153218c 100644 --- a/test/fts3expr3.test +++ b/test/fts3expr3.test @@ -122,6 +122,8 @@ proc balanced_andor_tree {nEntry} { return $tree } +if 1 { + # Test that queries like "1 AND 2 AND 3 AND 4..." are transformed to # balanced trees by FTS. # @@ -202,5 +204,35 @@ do_faultsim_test fts3expr3-fault-1 -faults oom-* -body { faultsim_test_result [list 0 $::result] } +} + +#------------------------------------------------------------------- + +foreach {tn expr res} { + 1 {1 OR 2 OR 3 OR 4} {OR {OR {P 1} {P 2}} {OR {P 3} {P 4}}} + 2 {1 OR (2 AND 3 AND 4 AND 5)} + {OR {P 1} {AND {AND {P 2} {P 3}} {AND {P 4} {P 5}}}} + 3 {(2 AND 3 AND 4 AND 5) OR 1} + {OR {AND {AND {P 2} {P 3}} {AND {P 4} {P 5}}} {P 1}} + + 4 {1 AND (2 OR 3 OR 4 OR 5)} + {AND {P 1} {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}}} + 5 {(2 OR 3 OR 4 OR 5) AND 1} + {AND {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}} {P 1}} + + 6 {(2 OR 3 OR 4 OR 5) NOT 1} + {NOT {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}} {P 1}} + + 7 {1 NOT (2 OR 3 OR 4 OR 5)} + {NOT {P 1} {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}}} + + 8 {(1 OR 2 OR 3 OR 4) NOT (5 AND 6 AND 7 AND 8)} + {NOT {OR {OR {P 1} {P 2}} {OR {P 3} {P 4}}} {AND {AND {P 5} {P 6}} {AND {P 7} {P 8}}}} +} { + do_test 5.1.$tn { + test_fts3expr2 $expr + } $res +} + set sqlite_fts3_enable_parentheses 0 finish_test diff --git a/test/fts4onepass.test b/test/fts4onepass.test new file mode 100644 index 0000000000..d290980dc9 --- /dev/null +++ b/test/fts4onepass.test @@ -0,0 +1,147 @@ +# 2015 Sep 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/fts3_common.tcl +set ::testprefix fts4onepass + +# If SQLITE_ENABLE_FTS3 is defined, omit this file. +ifcapable !fts3 { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE ft USING fts3; + INSERT INTO ft(rowid, content) VALUES(1, '1 2 3'); + INSERT INTO ft(rowid, content) VALUES(2, '4 5 6'); + INSERT INTO ft(rowid, content) VALUES(3, '7 8 9'); +} + +#------------------------------------------------------------------------- +# Check that UPDATE and DELETE statements that feature "WHERE rowid=?" or +# or "WHERE docid=?" clauses do not use statement journals. But that other +# DELETE and UPDATE statements do. +# +# Note: "MATCH ? AND docid=?" does use a statement journal. +# +foreach {tn sql uses} { + 1.1 { DELETE FROM ft } 1 + 1.2 { DELETE FROM ft WHERE docid=? } 0 + 1.3 { DELETE FROM ft WHERE rowid=? } 0 + 1.4 { DELETE FROM ft WHERE ft MATCH '1' } 1 + 1.5 { DELETE FROM ft WHERE ft MATCH '1' AND docid=? } 1 + 1.6 { DELETE FROM ft WHERE ft MATCH '1' AND rowid=? } 1 + + 2.1 { UPDATE ft SET content='a b c' } 1 + 2.2 { UPDATE ft SET content='a b c' WHERE docid=? } 0 + 2.3 { UPDATE ft SET content='a b c' WHERE rowid=? } 0 + 2.4 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' } 1 + 2.5 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND docid=? } 1 + 2.6 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND rowid=? } 1 +} { + do_test 1.$tn { sql_uses_stmt db $sql } $uses +} + +#------------------------------------------------------------------------- +# Check that putting a "DELETE/UPDATE ... WHERE rowid=?" statement in a +# trigger program does not prevent the VM from using a statement +# transaction. Even if the calling statement cannot hit a constraint. +# +do_execsql_test 2.0 { + CREATE TABLE t1(x); + + CREATE TRIGGER t1_ai AFTER INSERT ON t1 BEGIN + DELETE FROM ft WHERE rowid=new.x; + END; + + CREATE TRIGGER t1_ad AFTER DELETE ON t1 BEGIN + UPDATE ft SET content = 'a b c' WHERE rowid=old.x; + END; + + CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 BEGIN + DELETE FROM ft WHERE rowid=old.x; + END; +} + +foreach {tn sql uses} { + 1 { INSERT INTO t1 VALUES(1) } 1 + 2 { DELETE FROM t1 WHERE x=4 } 1 + 3 { UPDATE t1 SET x=10 WHERE x=11 } 1 +} { + do_test 2.$tn { sql_uses_stmt db $sql } $uses +} + +#------------------------------------------------------------------------- +# Test that an "UPDATE ... WHERE rowid=?" works and does not corrupt the +# index when it strikes a constraint. Both inside and outside a +# transaction. +# +foreach {tn tcl1 tcl2} { + 1 {} {} + + 2 { + execsql BEGIN + } { + if {[sqlite3_get_autocommit db]==1} { error "transaction rolled back!" } + execsql COMMIT + } +} { + + do_execsql_test 3.$tn.0 { + DROP TABLE IF EXISTS ft2; + CREATE VIRTUAL TABLE ft2 USING fts4; + INSERT INTO ft2(rowid, content) VALUES(1, 'a b c'); + INSERT INTO ft2(rowid, content) VALUES(2, 'a b d'); + INSERT INTO ft2(rowid, content) VALUES(3, 'a b e'); + } + + eval $tcl1 + foreach {tn2 sql content} { + 1 { UPDATE ft2 SET docid=2 WHERE docid=1 } + { 1 {a b c} 2 {a b d} 3 {a b e} } + + 2 { + INSERT INTO ft2(rowid, content) VALUES(4, 'a b f'); + UPDATE ft2 SET docid=5 WHERE docid=4; + UPDATE ft2 SET docid=3 WHERE docid=5; + } { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + + 3 { + UPDATE ft2 SET docid=3 WHERE docid=4; -- matches 0 rows + UPDATE ft2 SET docid=2 WHERE docid=3; + } { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + + 4 { + INSERT INTO ft2(rowid, content) VALUES(4, 'a b g'); + UPDATE ft2 SET docid=-1 WHERE docid=4; + UPDATE ft2 SET docid=3 WHERE docid=-1; + } {-1 {a b g} 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + + 5 { + DELETE FROM ft2 WHERE rowid=451; + DELETE FROM ft2 WHERE rowid=-1; + UPDATE ft2 SET docid = 2 WHERE docid = 1; + } {1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} } + } { + do_catchsql_test 3.$tn.$tn2.a $sql {1 {constraint failed}} + do_execsql_test 3.$tn.$tn2.b { SELECT rowid, content FROM ft2 } $content + do_execsql_test 3.$tn.$tn2.c { + INSERT INTO ft2(ft2) VALUES('integrity-check'); + } + } + eval $tcl2 +} + +finish_test + diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index 4597891c3a..15ddd09eed 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -1051,7 +1051,6 @@ int main(int argc, char **argv){ /* Print the description, if there is one */ if( !quietFlag ){ - int i; zDbName = azSrcDb[iSrcDb]; i = strlen(zDbName) - 1; while( i>0 && zDbName[i-1]!='/' && zDbName[i-1]!='\\' ){ i--; } @@ -1127,12 +1126,6 @@ int main(int argc, char **argv){ } rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs); if( rc ) fatalError("cannot open inmem database"); -#ifdef SQLITE_ENABLE_JSON1 - { - extern int sqlite3_json_init(sqlite3*); - sqlite3_json_init(db); - } -#endif if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags); setAlarm(iTimeout); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK diff --git a/test/fuzzdata3.db b/test/fuzzdata3.db index 4ca7a23578..0a41fd6e37 100644 Binary files a/test/fuzzdata3.db and b/test/fuzzdata3.db differ diff --git a/test/indexexpr1.test b/test/indexexpr1.test index 1945059f2b..89bea1877f 100644 --- a/test/indexexpr1.test +++ b/test/indexexpr1.test @@ -88,6 +88,23 @@ do_execsql_test indexexpr1-160eqp { WHERE substr(a,27,3)=='ord' AND d>=29; } {/USING INDEX t1a2/} +# ORDER BY using an indexed expression +# +do_execsql_test indexexpr1-170 { + CREATE INDEX t1alen ON t1(length(a)); + SELECT length(a) FROM t1 ORDER BY length(a); +} {20 25 27 29 38 52} +do_execsql_test indexexpr1-170eqp { + EXPLAIN QUERY PLAN + SELECT length(a) FROM t1 ORDER BY length(a); +} {/SCAN TABLE t1 USING INDEX t1alen/} +do_execsql_test indexexpr1-171 { + SELECT length(a) FROM t1 ORDER BY length(a) DESC; +} {52 38 29 27 25 20} +do_execsql_test indexexpr1-171eqp { + EXPLAIN QUERY PLAN + SELECT length(a) FROM t1 ORDER BY length(a) DESC; +} {/SCAN TABLE t1 USING INDEX t1alen/} do_execsql_test indexexpr1-200 { DROP TABLE t1; @@ -193,7 +210,8 @@ do_execsql_test indexexpr1-400 { SELECT x, printf('ab%04xyz',x), random() FROM c; CREATE UNIQUE INDEX t3abc ON t3(CAST(a AS text), b, substr(c,1,3)); SELECT a FROM t3 WHERE CAST(a AS text)<='10' ORDER BY +a; -} {1 10} + PRAGMA integrity_check; +} {1 10 ok} do_catchsql_test indexexpr1-410 { INSERT INTO t3 SELECT * FROM t3 WHERE rowid=10; } {1 {UNIQUE constraint failed: index 't3abc'}} @@ -255,4 +273,39 @@ do_execsql_test indexexpr1-710 { ORDER BY +a, +x; } {1 1 | 2 2 |} +# Collating sequences on indexes of expressions +# +do_execsql_test indexexpr1-800 { + DROP TABLE IF EXISTS t8; + CREATE TABLE t8(a INTEGER PRIMARY KEY, b TEXT); + CREATE UNIQUE INDEX t8bx ON t8(substr(b,2,4) COLLATE nocase); + INSERT INTO t8(a,b) VALUES(1,'Alice'),(2,'Bartholemew'),(3,'Cynthia'); + SELECT * FROM t8 WHERE substr(b,2,4)='ARTH' COLLATE nocase; +} {2 Bartholemew} +do_catchsql_test indexexpr1-810 { + INSERT INTO t8(a,b) VALUES(4,'BARTHMERE'); +} {1 {UNIQUE constraint failed: index 't8bx'}} +do_catchsql_test indexexpr1-820 { + DROP INDEX t8bx; + CREATE UNIQUE INDEX t8bx ON t8(substr(b,2,4) COLLATE rtrim); + INSERT INTO t8(a,b) VALUES(4,'BARTHMERE'); +} {0 {}} + +# Check that PRAGMA integrity_check works correctly on a +# UNIQUE index that includes rowid and expression terms. +# +do_execsql_test indexexpr1-900 { + CREATE TABLE t9(a,b,c,d); + CREATE UNIQUE INDEX t9x1 ON t9(c,abs(d),b); + INSERT INTO t9(rowid,a,b,c,d) VALUES(1,2,3,4,5); + INSERT INTO t9(rowid,a,b,c,d) VALUES(2,NULL,NULL,NULL,NULL); + INSERT INTO t9(rowid,a,b,c,d) VALUES(3,NULL,NULL,NULL,NULL); + INSERT INTO t9(rowid,a,b,c,d) VALUES(4,5,6,7,8); + PRAGMA integrity_check; +} {ok} +do_catchsql_test indexexpr1-910 { + INSERT INTO t9(a,b,c,d) VALUES(5,6,7,-8); +} {1 {UNIQUE constraint failed: index 't9x1'}} + + finish_test diff --git a/test/json101.test b/test/json101.test index 943292640d..e11cdd0138 100644 --- a/test/json101.test +++ b/test/json101.test @@ -15,7 +15,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -load_static_extension db json +ifcapable !json1 { + finish_test + return +} + do_execsql_test json101-1.1.00 { SELECT json_array(1,2.5,null,'hello'); } {[1,2.5,null,"hello"]} diff --git a/test/json102.test b/test/json102.test index f40580da4e..a4d88dbeaa 100644 --- a/test/json102.test +++ b/test/json102.test @@ -18,7 +18,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -load_static_extension db json +ifcapable !json1 { + finish_test + return +} + do_execsql_test json102-100 { SELECT json_object('ex','[52,3.14159]'); } {{{"ex":"[52,3.14159]"}}} diff --git a/test/offset1.test b/test/offset1.test new file mode 100644 index 0000000000..91dc0b00a1 --- /dev/null +++ b/test/offset1.test @@ -0,0 +1,161 @@ +# 2015-10-06 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements test cases for the [b65cb2c8d91f6685841d7d1e13b6] +# bug: Correct handling of LIMIT and OFFSET on a UNION ALL query where +# the right-hand SELECT contains an ORDER BY in a subquery. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !compound { + finish_test + return +} + +do_execsql_test offset1-1.1 { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e'); + CREATE TABLE t2(x,y); + INSERT INTO t2 VALUES(8,'y'),(9,'z'),(6,'w'),(7,'x'); + SELECT count(*) FROM t1, t2; +} {20} + +do_execsql_test offset1-1.2.0 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 0; +} {1 a 2 b 3 c} +do_execsql_test offset1-1.2.1 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 1; +} {2 b 3 c 4 d} +do_execsql_test offset1-1.2.2 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 2; +} {3 c 4 d 5 e} +do_execsql_test offset1-1.2.3 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 3; +} {4 d 5 e 6 w} +do_execsql_test offset1-1.2.4 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 4; +} {5 e 6 w 7 x} +do_execsql_test offset1-1.2.5 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 5; +} {6 w 7 x 8 y} +do_execsql_test offset1-1.2.6 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 6; +} {7 x 8 y 9 z} +do_execsql_test offset1-1.2.7 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 7; +} {8 y 9 z} +do_execsql_test offset1-1.2.8 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 8; +} {9 z} +do_execsql_test offset1-1.2.9 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 9; +} {} + +do_execsql_test offset1-1.3.0 { + SELECT * FROM t1 LIMIT 0; +} {} + +do_execsql_test offset1-1.4.0 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 0 OFFSET 1; +} {} +do_execsql_test offset1-1.4.1 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 1 OFFSET 1; +} {2 b} +do_execsql_test offset1-1.4.2 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 2 OFFSET 1; +} {2 b 3 c} +do_execsql_test offset1-1.4.3 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 3 OFFSET 1; +} {2 b 3 c 4 d} +do_execsql_test offset1-1.4.4 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 4 OFFSET 1; +} {2 b 3 c 4 d 5 e} +do_execsql_test offset1-1.4.5 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 5 OFFSET 1; +} {2 b 3 c 4 d 5 e 6 w} +do_execsql_test offset1-1.4.6 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 6 OFFSET 1; +} {2 b 3 c 4 d 5 e 6 w 7 x} +do_execsql_test offset1-1.4.7 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 7 OFFSET 1; +} {2 b 3 c 4 d 5 e 6 w 7 x 8 y} +do_execsql_test offset1-1.4.8 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 8 OFFSET 1; +} {2 b 3 c 4 d 5 e 6 w 7 x 8 y 9 z} +do_execsql_test offset1-1.4.9 { + SELECT a, b FROM t1 + UNION ALL + SELECT * FROM (SELECT x, y FROM t2 ORDER BY y) + LIMIT 9 OFFSET 1; +} {2 b 3 c 4 d 5 e 6 w 7 x 8 y 9 z} + + + +finish_test diff --git a/test/releasetest.tcl b/test/releasetest.tcl index dde3f039d6..04c2419ace 100644 --- a/test/releasetest.tcl +++ b/test/releasetest.tcl @@ -78,6 +78,7 @@ array set ::Configs [strip_comments { -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -DSQLITE_ENABLE_STMT_SCANSTATUS + --enable-json1 } "Check-Symbols" { -DSQLITE_MEMDEBUG=1 @@ -95,6 +96,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_STMT_SCANSTATUS + --enable-json1 --enable-fts5 } "Debug-One" { --disable-shared @@ -135,6 +137,7 @@ array set ::Configs [strip_comments { -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_OMIT_VIRTUALTABLE=1 -DSQLITE_TEMP_STORE=3 + --enable-json1 } "Device-Two" { -DSQLITE_4_BYTE_ALIGNED_MALLOC=1 @@ -152,6 +155,7 @@ array set ::Configs [strip_comments { -DSQLITE_OMIT_TRACE=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_THREADSAFE=2 + --enable-json1 --enable-fts5 } "Locking-Style" { -O2 @@ -163,6 +167,7 @@ array set ::Configs [strip_comments { -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_THREADSAFE=2 -DSQLITE_OS_UNIX=1 + -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_LOCKING_STYLE=1 -DUSE_PREAD=1 -DSQLITE_ENABLE_RTREE=1 @@ -174,6 +179,7 @@ array set ::Configs [strip_comments { -DSQLITE_DEBUG=1 -DSQLITE_PREFER_PROXY_LOCKING=1 -DSQLITE_ENABLE_API_ARMOR=1 + --enable-json1 --enable-fts5 } "Extra-Robustness" { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 @@ -187,6 +193,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_FTS4_PARENTHESIS -DSQLITE_DISABLE_FTS4_DEFERRED -DSQLITE_ENABLE_RTREE + --enable-json1 --enable-fts5 } "No-lookaside" { -DSQLITE_TEST_REALLOC_STRESS=1 @@ -197,6 +204,7 @@ array set ::Configs [strip_comments { -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE + --enable-json1 } # The next group of configurations are used only by the diff --git a/test/spellfix2.test b/test/spellfix2.test index e9d6c693b2..d1a13284ea 100644 --- a/test/spellfix2.test +++ b/test/spellfix2.test @@ -76,40 +76,42 @@ do_execsql_test 1.5 { do_execsql_test 1.6 { SELECT word, distance, matchlen FROM demo - WHERE word MATCH 'amstedam*' AND distance <= 100; + WHERE word MATCH 'amstedam*' AND distance <= 100 + ORDER BY distance, word; } { - amsterdam 100 9 amsterdamh 100 9 - amsterdamm 100 9 amsterdamn 100 9 - amsterdama 100 9 amsterdame 100 9 - amsterdami 100 9 amsterdamo 100 9 - amsterdamu 100 9 amsterdamy 100 9 - amsterdammetje 100 9 amsterdamania 100 9 - amsterdamb 100 9 amsterdamf 100 9 - amsterdamp 100 9 amsterdamv 100 9 - amsterdamw 100 9 amsterdamweg 100 9 - amsterdamc 100 9 amsterdamg 100 9 - amsterdamj 100 9 amsterdamk 100 9 - amsterdamq 100 9 amsterdams 100 9 - amsterdamx 100 9 amsterdamz 100 9 - amsterdamsestraat 100 9 amsterdamd 100 9 - amsterdamt 100 9 amsterdaml 100 9 - amsterdamlaan 100 9 amsterdamr 100 9 + amsterdam 100 9 amsterdama 100 9 + amsterdamania 100 9 amsterdamb 100 9 + amsterdamc 100 9 amsterdamd 100 9 + amsterdame 100 9 amsterdamf 100 9 + amsterdamg 100 9 amsterdamh 100 9 + amsterdami 100 9 amsterdamj 100 9 + amsterdamk 100 9 amsterdaml 100 9 + amsterdamlaan 100 9 amsterdamm 100 9 + amsterdammetje 100 9 amsterdamn 100 9 + amsterdamo 100 9 amsterdamp 100 9 + amsterdamq 100 9 amsterdamr 100 9 + amsterdams 100 9 amsterdamsestraat 100 9 + amsterdamt 100 9 amsterdamu 100 9 + amsterdamv 100 9 amsterdamw 100 9 + amsterdamweg 100 9 amsterdamx 100 9 + amsterdamy 100 9 amsterdamz 100 9 } do_execsql_test 1.7 { SELECT word, distance, matchlen FROM demo - WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20; + WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20 + ORDER BY distance, word; } { - amsterdam 100 9 amsterdamh 100 9 - amsterdamm 100 9 amsterdamn 100 9 - amsterdama 100 9 amsterdame 100 9 - amsterdami 100 9 amsterdamo 100 9 - amsterdamu 100 9 amsterdamy 100 9 - amsterdammetje 100 9 amsterdamania 100 9 - amsterdamb 100 9 amsterdamf 100 9 - amsterdamp 100 9 amsterdamv 100 9 - amsterdamw 100 9 amsterdamweg 100 9 - amsterdamc 100 9 amsterdamg 100 9 + amsterdam 100 9 amsterdama 100 9 + amsterdamania 100 9 amsterdamb 100 9 + amsterdamc 100 9 amsterdame 100 9 + amsterdamf 100 9 amsterdamg 100 9 + amsterdamh 100 9 amsterdami 100 9 + amsterdamm 100 9 amsterdammetje 100 9 + amsterdamn 100 9 amsterdamo 100 9 + amsterdamp 100 9 amsterdamu 100 9 + amsterdamv 100 9 amsterdamw 100 9 + amsterdamweg 100 9 amsterdamy 100 9 } diff --git a/test/tabfunc01.test b/test/tabfunc01.test index 5bd729b87d..47af39606e 100644 --- a/test/tabfunc01.test +++ b/test/tabfunc01.test @@ -75,8 +75,12 @@ do_execsql_test tabfunc01-2.1 { INSERT INTO t1(x) VALUES(2),(3); SELECT *, '|' FROM t1, generate_series(1,x) ORDER BY 1, 2 } {2 1 | 2 2 | 3 1 | 3 2 | 3 3 |} - do_execsql_test tabfunc01-2.2 { + SELECT *, '|' FROM (SELECT x FROM t1) AS y, generate_series(1,y.x) + ORDER BY 1, 2; +} {2 1 | 2 2 | 3 1 | 3 2 | 3 3 |} + +do_execsql_test tabfunc01-2.50 { SELECT * FROM generate_series() LIMIT 5; } {0 1 2 3 4} diff --git a/test/uri.test b/test/uri.test index dd78783ddd..a0a88d2263 100644 --- a/test/uri.test +++ b/test/uri.test @@ -63,6 +63,14 @@ foreach {tn uri file} { # if {$tn>14} break + # + # NOTE: When running on Tcl 8.6 (or higher?) on Windows, a colon within + # the file name no longer tries to access an alternate data stream + # (ADS) named "test.db" for the "http" file, causing some spurious + # failures of this test. + # + if {$tn==12 && $::tcl_version>=8.6} continue + # # NOTE: On Windows, we need to account for the fact that the current # directory does not start with a forward slash. diff --git a/test/view.test b/test/view.test index b39487689c..235f8605ac 100644 --- a/test/view.test +++ b/test/view.test @@ -161,6 +161,16 @@ do_test view-3.3.3 { do_catchsql_test view-3.3.4 { CREATE VIEW v1err(x,y DESC,z) AS SELECT a, b+c, c-b FROM t1; } {1 {syntax error after column name "y"}} +do_catchsql_test view-3.3.5 { + DROP VIEW IF EXISTS v1err; + CREATE VIEW v1err(x,y) AS SELECT a, b+c, c-b FROM t1; + SELECT * FROM v1err; +} {1 {expected 2 columns for 'v1err' but got 3}} +do_catchsql_test view-3.3.6 { + DROP VIEW IF EXISTS v1err; + CREATE VIEW v1err(w,x,y,z) AS SELECT a, b+c, c-b FROM t1; + SELECT * FROM v1err; +} {1 {expected 4 columns for 'v1err' but got 3}} ifcapable compound { do_test view-3.4 { @@ -464,6 +474,11 @@ do_test view-12.1 { CREATE VIEW v12 AS SELECT a FROM t1 WHERE b=? } } {1 {parameters are not allowed in views}} +do_test view-12.2 { + catchsql { + CREATE VIEW v12(x) AS SELECT a FROM t1 WHERE b=? + } +} {1 {parameters are not allowed in views}} ifcapable attach { do_test view-13.1 { @@ -484,6 +499,13 @@ do_test view-14.1 { SELECT * FROM temp.t1; } } {1 {view t1 is circularly defined}} +do_test view-14.2 { + catchsql { + DROP VIEW IF EXISTS temp.t1; + CREATE TEMP VIEW t1(a,b) AS SELECT a,b FROM t1; + SELECT * FROM temp.t1; + } +} {1 {view t1 is circularly defined}} # Tickets #1688, #1709 # diff --git a/tool/GetFile.cs b/tool/GetFile.cs new file mode 100644 index 0000000000..56601f3e85 --- /dev/null +++ b/tool/GetFile.cs @@ -0,0 +1,450 @@ +/* +** 2015 October 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. +** +************************************************************************* +** This file contains C# code to download a single file based on a URI. +*/ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; + +/////////////////////////////////////////////////////////////////////////////// + +#region Assembly Metadata +[assembly: AssemblyTitle("GetFile Tool")] +[assembly: AssemblyDescription("Download a single file based on a URI.")] +[assembly: AssemblyCompany("SQLite Development Team")] +[assembly: AssemblyProduct("SQLite")] +[assembly: AssemblyCopyright("Public Domain")] +[assembly: ComVisible(false)] +[assembly: Guid("5c4b3728-1693-4a33-a218-8e6973ca15a6")] +[assembly: AssemblyVersion("1.0.*")] + +#if DEBUG +[assembly: AssemblyConfiguration("Debug")] +#else +[assembly: AssemblyConfiguration("Release")] +#endif +#endregion + +/////////////////////////////////////////////////////////////////////////////// + +namespace GetFile +{ + /// + /// 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 URI specified on the command line could not be parsed as a + /// supported absolute URI. + /// + BadUri = 3, + + /// + /// The file name portion of the URI specified on the command line + /// could not be extracted from it. + /// + BadFileName = 4, + + /// + /// The temporary directory is either invalid (i.e. null) or does not + /// represent an available directory. + /// + BadTempPath = 5, + + /// + /// An exception was caught in . Generally, this + /// should not happen. + /// + Exception = 6, + + /// + /// The file download was canceled. This tool does not make use of + /// the method; therefore, this + /// should not happen. + /// + DownloadCanceled = 7, + + /// + /// The file download encountered an error. Further information about + /// this error should be displayed on the console. + /// + DownloadError = 8 + } + + /////////////////////////////////////////////////////////////////////////// + + internal static class Program + { + #region Private Data + /// + /// This is used to synchronize multithreaded access to the + /// and + /// fields. + /// + private static readonly object syncRoot = new object(); + + /////////////////////////////////////////////////////////////////////// + + /// + /// This event will be signed when the file download has completed, + /// even if the file download itself was canceled or unsuccessful. + /// + private static EventWaitHandle doneEvent; + + /////////////////////////////////////////////////////////////////////// + + /// + /// The previous file download completion percentage seen by the + /// event handler. This value + /// is never decreased, nor is it ever reset to zero. + /// + private static int previousPercent = 0; + + /////////////////////////////////////////////////////////////////////// + + /// + /// This will be the exit code returned by this tool after the file + /// download completes, successfully or otherwise. This value is only + /// changed by the event handler. + /// + private static ExitCode exitCode = ExitCode.Success; + #endregion + + /////////////////////////////////////////////////////////////////////// + + #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)); + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// This method attempts to determine the file name portion of the + /// specified URI. + /// + /// + /// The URI to process. + /// + /// + /// The file name portion of the specified URI -OR- null if it cannot + /// be determined. + /// + private static string GetFileName( + Uri uri + ) + { + if (uri == null) + return null; + + string pathAndQuery = uri.PathAndQuery; + + if (String.IsNullOrEmpty(pathAndQuery)) + return null; + + int index = pathAndQuery.LastIndexOf('/'); + + if ((index < 0) || (index == pathAndQuery.Length)) + return null; + + return pathAndQuery.Substring(index + 1); + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Private Event Handlers + /// + /// This method is an event handler that is called when the file + /// download completion percentage changes. It will display progress + /// on the console. Special care is taken to make sure that progress + /// events are not displayed out-of-order, even if duplicate and/or + /// out-of-order events are received. + /// + /// + /// The source of the event. + /// + /// + /// Information for the event being processed. + /// + private static void DownloadProgressChanged( + object sender, + DownloadProgressChangedEventArgs e + ) + { + if (e != null) + { + int percent = e.ProgressPercentage; + + lock (syncRoot) + { + if (percent > previousPercent) + { + Console.Write('.'); + + if ((percent % 10) == 0) + Console.Write(" {0}% ", percent); + + previousPercent = percent; + } + } + } + } + + /////////////////////////////////////////////////////////////////////// + + /// + /// This method is an event handler that is called when the file + /// download has completed, successfully or otherwise. It will + /// display the overall result of the file download on the console, + /// including any information, if applicable. + /// The field is changed by this method to + /// indicate the overall result of the file download and the event + /// within the field will be signaled. + /// + /// + /// The source of the event. + /// + /// + /// Information for the event being processed. + /// + private static void DownloadFileCompleted( + object sender, + AsyncCompletedEventArgs e + ) + { + if (e != null) + { + lock (syncRoot) + { + if (previousPercent < 100) + Console.Write(' '); + } + + if (e.Cancelled) + { + Console.WriteLine("Canceled"); + + lock (syncRoot) + { + exitCode = ExitCode.DownloadCanceled; + } + } + else + { + Exception error = e.Error; + + if (error != null) + { + Console.WriteLine("Error: {0}", error); + + lock (syncRoot) + { + exitCode = ExitCode.DownloadError; + } + } + else + { + Console.WriteLine("Done"); + } + } + } + + if (doneEvent != null) + doneEvent.Set(); + } + #endregion + + /////////////////////////////////////////////////////////////////////// + + #region Program Entry Point + /// + /// This is the entry-point for this tool. It handles processing the + /// command line arguments, setting up the web client, downloading the + /// file, and saving it to the file system. + /// + /// + /// 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 != 1) + { + Error(null, true); + return (int)ExitCode.WrongNumArgs; + } + + // + // NOTE: Attempt to convert the first (and only) command line + // argument to an absolute URI. + // + Uri uri; + + if (!Uri.TryCreate(args[0], UriKind.Absolute, out uri)) + { + Error("Could not create absolute URI from argument.", false); + return (int)ExitCode.BadUri; + } + + // + // NOTE: Attempt to extract the file name portion of the URI we + // just created. + // + string fileName = GetFileName(uri); + + if (fileName == null) + { + Error("Could not extract the file name from the URI.", false); + return (int)ExitCode.BadFileName; + } + + // + // NOTE: Grab the temporary path setup for this process. If it is + // unavailable, we will not continue. + // + string directory = Path.GetTempPath(); + + if (String.IsNullOrEmpty(directory) || + !Directory.Exists(directory)) + { + Error("Temporary directory is invalid or unavailable.", false); + return (int)ExitCode.BadTempPath; + } + + try + { + using (WebClient webClient = new WebClient()) + { + // + // NOTE: Create the event used to signal completion of the + // file download. + // + doneEvent = new ManualResetEvent(false); + + // + // NOTE: Hookup the event handlers we care about on the web + // client. These are necessary because the file is + // downloaded asynchronously. + // + webClient.DownloadProgressChanged += + new DownloadProgressChangedEventHandler( + DownloadProgressChanged); + + webClient.DownloadFileCompleted += + new AsyncCompletedEventHandler( + DownloadFileCompleted); + + // + // NOTE: Build the fully qualified path and file name, + // within the temporary directory, where the file to + // be downloaded will be saved. + // + fileName = Path.Combine(directory, fileName); + + // + // NOTE: If the file name already exists (in the temporary) + // directory, delete it. + // + // TODO: Perhaps an error should be raised here instead? + // + if (File.Exists(fileName)) + File.Delete(fileName); + + // + // NOTE: After kicking off the asynchronous file download + // process, wait [forever] until the "done" event is + // signaled. + // + Console.WriteLine( + "Downloading \"{0}\" to \"{1}\"...", uri, fileName); + + webClient.DownloadFileAsync(uri, fileName); + doneEvent.WaitOne(); + } + + lock (syncRoot) + { + return (int)exitCode; + } + } + 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/GetTclKit.bat b/tool/GetTclKit.bat new file mode 100644 index 0000000000..31988aadac --- /dev/null +++ b/tool/GetTclKit.bat @@ -0,0 +1,272 @@ +@ECHO OFF + +:: +:: GetTclKit.bat -- +:: +:: TclKit Download Tool +:: + +SETLOCAL + +REM SET __ECHO=ECHO +REM SET __ECHO2=ECHO +REM SET __ECHO3=ECHO +IF NOT DEFINED _AECHO (SET _AECHO=REM) +IF NOT DEFINED _CECHO (SET _CECHO=REM) +IF NOT DEFINED _VECHO (SET _VECHO=REM) + +SET OVERWRITE=^> +IF DEFINED __ECHO SET OVERWRITE=^^^> + +SET APPEND=^>^> +IF DEFINED __ECHO SET APPEND=^^^>^^^> + +SET PROCESSOR=%1 + +IF DEFINED PROCESSOR ( + CALL :fn_UnquoteVariable PROCESSOR +) ELSE ( + GOTO usage +) + +%_VECHO% Processor = '%PROCESSOR%' + +SET DUMMY2=%2 + +IF DEFINED DUMMY2 ( + GOTO usage +) + +SET ROOT=%~dp0\.. +SET ROOT=%ROOT:\\=\% + +%_VECHO% Root = '%ROOT%' + +SET TOOLS=%~dp0 +SET TOOLS=%TOOLS:~0,-1% + +%_VECHO% Tools = '%TOOLS%' + +IF NOT DEFINED windir ( + ECHO The windir environment variable must be set first. + GOTO errors +) + +%_VECHO% WinDir = '%windir%' + +IF NOT DEFINED TEMP ( + ECHO The TEMP environment variable must be set first. + GOTO errors +) + +%_VECHO% Temp = '%TEMP%' + +IF NOT DEFINED TCLKIT_URI ( + SET TCLKIT_URI=http://tclsh.com/ +) + +%_VECHO% TclKitUri = '%TCLKIT_URI%' + +IF /I "%PROCESSOR%" == "x86" ( + CALL :fn_TclKitX86Variables +) ELSE IF /I "%PROCESSOR%" == "x64" ( + CALL :fn_TclKitX64Variables +) ELSE ( + GOTO usage +) + +%_VECHO% TclKitVersion = '%TCLKIT_VERSION%' +%_VECHO% TclKitPatchLevel = '%TCLKIT_PATCHLEVEL%' +%_VECHO% TclKitNoSdk = '%TCLKIT_NOSDK%' +%_VECHO% TclKitExe = '%TCLKIT_EXE%' +%_VECHO% TclKitLib = '%TCLKIT_LIB%' +%_VECHO% TclKitLibStub = '%TCLKIT_LIB_STUB%' +%_VECHO% TclKitSdk = '%TCLKIT_SDK%' +%_VECHO% TclKitSdkZip = '%TCLKIT_SDK_ZIP%' +%_VECHO% TclKitFiles = '%TCLKIT_FILES%' + +CALL :fn_ResetErrorLevel + +FOR %%T IN (csc.exe) DO ( + SET %%T_PATH=%%~dp$PATH:T +) + +%_VECHO% Csc.exe_PATH = '%csc.exe_PATH%' + +IF DEFINED csc.exe_PATH ( + GOTO skip_addToPath +) + +IF DEFINED FRAMEWORKDIR ( + REM Use the existing .NET Framework directory... +) ELSE IF EXIST "%windir%\Microsoft.NET\Framework64\v2.0.50727" ( + SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework64\v2.0.50727 +) ELSE IF EXIST "%windir%\Microsoft.NET\Framework64\v3.5" ( + SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework64\v3.5 +) ELSE IF EXIST "%windir%\Microsoft.NET\Framework64\v4.0.30319" ( + SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework64\v4.0.30319 +) ELSE IF EXIST "%windir%\Microsoft.NET\Framework\v2.0.50727" ( + SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework\v2.0.50727 +) ELSE IF EXIST "%windir%\Microsoft.NET\Framework\v3.5" ( + SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework\v3.5 +) ELSE IF EXIST "%windir%\Microsoft.NET\Framework\v4.0.30319" ( + SET FRAMEWORKDIR=%windir%\Microsoft.NET\Framework\v4.0.30319 +) ELSE ( + ECHO No suitable version of the .NET Framework appears to be installed. + GOTO errors +) + +%_VECHO% FrameworkDir = '%FRAMEWORKDIR%' + +IF NOT EXIST "%FRAMEWORKDIR%\csc.exe" ( + ECHO The file "%FRAMEWORKDIR%\csc.exe" is missing. + GOTO errors +) + +SET PATH=%FRAMEWORKDIR%;%PATH% + +:skip_addToPath + +IF NOT EXIST "%TEMP%\GetFile.exe" ( + %__ECHO% csc.exe "/out:%TEMP%\GetFile.exe" /target:exe "%TOOLS%\GetFile.cs" + + IF ERRORLEVEL 1 ( + ECHO Compilation of "%TOOLS%\GetFile.cs" failed. + GOTO errors + ) +) + +FOR %%F IN (%TCLKIT_FILES%) DO ( + IF NOT EXIST "%TEMP%\%%F" ( + %__ECHO% "%TEMP%\GetFile.exe" "%TCLKIT_URI%%%F" + + IF ERRORLEVEL 1 ( + ECHO Download of "%%F" from "%TCLKIT_URI%" failed. + GOTO errors + ) + ) +) + +IF DEFINED TCLKIT_NOSDK GOTO skip_sdkUnZip + +IF NOT EXIST "%TEMP%\%TCLKIT_SDK%" ( + %__ECHO% MKDIR "%TEMP%\%TCLKIT_SDK%" + + IF ERRORLEVEL 1 ( + ECHO Could not create directory "%TEMP%\%TCLKIT_SDK%". + GOTO errors + ) +) + +%__ECHO% "%TEMP%\unzip.exe" -n "%TEMP%\%TCLKIT_SDK_ZIP%" -d "%TEMP%\%TCLKIT_SDK%" + +IF ERRORLEVEL 1 ( + ECHO Could not unzip "%TEMP%\%TCLKIT_SDK_ZIP%" to "%TEMP%\%TCLKIT_SDK%". + GOTO errors +) + +:skip_sdkUnZip + +%__ECHO% ECHO SET TCLSH_CMD=%TEMP%\%TCLKIT_EXE%%OVERWRITE%"%ROOT%\SetTclKitEnv.bat" + +IF DEFINED TCLKIT_NOSDK GOTO skip_sdkVariables + +%__ECHO% ECHO SET TCLINCDIR=%TEMP%\%TCLKIT_SDK%\include%APPEND%"%ROOT%\SetTclKitEnv.bat" +%__ECHO% ECHO SET TCLLIBDIR=%TEMP%\%TCLKIT_SDK%\lib%APPEND%"%ROOT%\SetTclKitEnv.bat" +%__ECHO% ECHO SET LIBTCL=%TCLKIT_LIB%%APPEND%"%ROOT%\SetTclKitEnv.bat" +%__ECHO% ECHO SET LIBTCLSTUB=%TCLKIT_LIB_STUB%%APPEND%"%ROOT%\SetTclKitEnv.bat" + +:skip_sdkVariables + +ECHO. +ECHO Wrote "%ROOT%\SetTclKitEnv.bat". +ECHO Please run it to set the necessary Tcl environment variables. +ECHO. + +GOTO no_errors + +:fn_TclKitX86Variables + IF NOT DEFINED TCLKIT_PATCHLEVEL ( + SET TCLKIT_PATCHLEVEL=8.6.4 + ) + SET TCLKIT_VERSION=%TCLKIT_PATCHLEVEL:.=% + SET TCLKIT_VERSION=%TCLKIT_VERSION:~0,2% + SET TCLKIT_EXE=tclkit-%TCLKIT_PATCHLEVEL%.exe + SET TCLKIT_LIB=libtclkit%TCLKIT_PATCHLEVEL:.=%.lib + SET TCLKIT_LIB_STUB=libtclstub%TCLKIT_VERSION:.=%.a + SET TCLKIT_SDK=libtclkit-sdk-x86-%TCLKIT_PATCHLEVEL% + SET TCLKIT_SDK_ZIP=%TCLKIT_SDK%.zip + SET TCLKIT_FILES=%TCLKIT_EXE% + IF NOT DEFINED TCLKIT_NOSDK ( + SET TCLKIT_FILES=%TCLKIT_FILES% unzip.exe %TCLKIT_SDK_ZIP% + ) + GOTO :EOF + +:fn_TclKitX64Variables + IF NOT DEFINED TCLKIT_PATCHLEVEL ( + REM + REM NOTE: By default, use latest available version of the TclKit SDK + REM for x64. However, the "default" TclKit executable for x86 + REM is still used here because it is the only one "well-known" + REM to be available for download. + REM + SET TCLKIT_PATCHLEVEL=8.6.3 + SET TCLKIT_EXE=tclkit-8.6.4.exe + ) ELSE ( + SET TCLKIT_EXE=tclkit-%TCLKIT_PATCHLEVEL%.exe + ) + SET TCLKIT_VERSION=%TCLKIT_PATCHLEVEL:.=% + SET TCLKIT_VERSION=%TCLKIT_VERSION:~0,2% + SET TCLKIT_LIB=libtclkit%TCLKIT_PATCHLEVEL:.=%.lib + SET TCLKIT_LIB_STUB=libtclstub%TCLKIT_VERSION:.=%.a + SET TCLKIT_SDK=libtclkit-sdk-x64-%TCLKIT_PATCHLEVEL% + SET TCLKIT_SDK_ZIP=%TCLKIT_SDK%.zip + SET TCLKIT_FILES=%TCLKIT_EXE% + IF NOT DEFINED TCLKIT_NOSDK ( + SET TCLKIT_FILES=%TCLKIT_FILES% unzip.exe %TCLKIT_SDK_ZIP% + ) + GOTO :EOF + +:fn_UnquoteVariable + IF NOT DEFINED %1 GOTO :EOF + SETLOCAL + SET __ECHO_CMD=ECHO %%%1%% + FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( + SET VALUE=%%V + ) + SET VALUE=%VALUE:"=% + REM " + ENDLOCAL && SET %1=%VALUE% + GOTO :EOF + +:fn_ResetErrorLevel + VERIFY > NUL + GOTO :EOF + +:fn_SetErrorLevel + VERIFY MAYBE 2> NUL + GOTO :EOF + +:usage + ECHO. + ECHO Usage: %~nx0 ^ + ECHO. + ECHO The only supported values for processor are "x86" and "x64". + GOTO errors + +:errors + CALL :fn_SetErrorLevel + ENDLOCAL + ECHO. + ECHO Failure, errors were encountered. + GOTO end_of_file + +:no_errors + CALL :fn_ResetErrorLevel + ENDLOCAL + ECHO. + ECHO Success, no errors were encountered. + GOTO end_of_file + +:end_of_file +%__ECHO% EXIT /B %ERRORLEVEL% diff --git a/tool/addopcodes.tcl b/tool/addopcodes.tcl new file mode 100644 index 0000000000..46675cb258 --- /dev/null +++ b/tool/addopcodes.tcl @@ -0,0 +1,45 @@ +#!/usr/bin/tclsh +# +# This script appends additional token codes to the end of the +# parse.h file that lemon generates. These extra token codes are +# not used by the parser. But they are used by the tokenizer and/or +# the code generator. +# +# +set in [open [lindex $argv 0] rb] +set max 0 +while {![eof $in]} { + set line [gets $in] + if {[regexp {^#define TK_} $line]} { + puts $line + set x [lindex $line 2] + if {$x>$max} {set max $x} + } +} +close $in + +# The following are the extra token codes to be added +# +set extras { + TO_TEXT + TO_BLOB + TO_NUMERIC + TO_INT + TO_REAL + ISNOT + END_OF_FILE + ILLEGAL + SPACE + UNCLOSED_STRING + FUNCTION + COLUMN + AGG_FUNCTION + AGG_COLUMN + UMINUS + UPLUS + REGISTER +} +foreach x $extras { + incr max + puts [format "#define TK_%-29s %4d" $x $max] +} diff --git a/tool/build-all-msvc.bat b/tool/build-all-msvc.bat index 4842dc4074..a8ba1bc293 100755 --- a/tool/build-all-msvc.bat +++ b/tool/build-all-msvc.bat @@ -29,9 +29,9 @@ REM source tree for SQLite and "C:\Temp" represents the final destination REM directory for the generated output files. REM REM Please note that the SQLite build process performed by the Makefile -REM associated with this batch script requires both Gawk ^(gawk.exe^) and Tcl -REM 8.5 ^(tclsh85.exe^) to be present in a directory contained in the PATH -REM environment variable unless a pre-existing amalgamation file is used. +REM associated with this batch script requires a Tcl shell to be present +REM in a directory contained in the PATH environment variable unless a +REM pre-existing amalgamation file is used. REM REM There are several environment variables that may be set to modify the REM behavior of this batch script and its associated Makefile. The list of @@ -232,25 +232,22 @@ REM NOTE: Check for the external tools needed during the build process ^(i.e. REM those that do not get compiled as part of the build process itself^) REM along the PATH. REM -FOR %%T IN (gawk.exe tclsh85.exe) DO ( +IF DEFINED TCLSH_CMD ( + SET TCLSH_FILE=%TCLSH_CMD% +) ELSE ( + SET TCLSH_FILE=tclsh85.exe +) + +FOR %%T IN (%TCLSH_FILE%) DO ( SET %%T_PATH=%%~dp$PATH:T ) REM -REM NOTE: The Gawk executable "gawk.exe" is required during the SQLite build -REM process unless a pre-existing amalgamation file is used. +REM NOTE: A Tcl shell executable is required during the SQLite build process +REM unless a pre-existing amalgamation file is used. REM -IF NOT DEFINED gawk.exe_PATH ( - ECHO The Gawk executable "gawk.exe" is required to be in the PATH. - GOTO errors -) - -REM -REM NOTE: The Tcl 8.5 executable "tclsh85.exe" is required during the SQLite -REM build process unless a pre-existing amalgamation file is used. -REM -IF NOT DEFINED tclsh85.exe_PATH ( - ECHO The Tcl 8.5 executable "tclsh85.exe" is required to be in the PATH. +IF NOT DEFINED %TCLSH_FILE%_PATH ( + ECHO The Tcl shell executable "%TCLSH_FILE%" is required to be in the PATH. GOTO errors ) @@ -258,7 +255,7 @@ REM REM NOTE: Set the TOOLPATH variable to contain all the directories where the REM external tools were found in the search above. REM -SET TOOLPATH=%gawk.exe_PATH%;%tclsh85.exe_PATH% +CALL :fn_CopyVariable %TCLSH_FILE%_PATH TOOLPATH %_VECHO% ToolPath = '%TOOLPATH%' diff --git a/tool/diffdb.c b/tool/diffdb.c deleted file mode 100644 index 0537d38723..0000000000 --- a/tool/diffdb.c +++ /dev/null @@ -1,44 +0,0 @@ -/* -** A utility for printing the differences between two SQLite database files. -*/ -#include -#include -#include -#include -#include -#include -#include - - -#define PAGESIZE 1024 -static int db1 = -1; -static int db2 = -1; - -int main(int argc, char **argv){ - int iPg; - unsigned char a1[PAGESIZE], a2[PAGESIZE]; - if( argc!=3 ){ - fprintf(stderr,"Usage: %s FILENAME FILENAME\n", argv[0]); - exit(1); - } - db1 = open(argv[1], O_RDONLY); - if( db1<0 ){ - fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]); - exit(1); - } - db2 = open(argv[2], O_RDONLY); - if( db2<0 ){ - fprintf(stderr,"%s: can't open %s\n", argv[0], argv[2]); - exit(1); - } - iPg = 1; - while( read(db1, a1, PAGESIZE)==PAGESIZE && read(db2,a2,PAGESIZE)==PAGESIZE ){ - if( memcmp(a1,a2,PAGESIZE) ){ - printf("Page %d\n", iPg); - } - iPg++; - } - printf("%d pages checked\n", iPg-1); - close(db1); - close(db2); -} diff --git a/tool/mkopcodec.tcl b/tool/mkopcodec.tcl new file mode 100644 index 0000000000..55d828b14d --- /dev/null +++ b/tool/mkopcodec.tcl @@ -0,0 +1,50 @@ +#!/usr/bin/tclsh +# +# This TCL script scans the opcodes.h file (which is itself generated by +# another TCL script) and uses the information gleaned to create the +# opcodes.c source file. +# +# Opcodes.c contains strings which are the symbolic names for the various +# opcodes used by the VDBE. These strings are used when disassembling a +# VDBE program during tracing or as a result of the EXPLAIN keyword. +# +puts "/* Automatically generated. Do not edit */" +puts "/* See the tool/mkopcodec.tcl script for details. */" +puts "#if !defined(SQLITE_OMIT_EXPLAIN) \\" +puts " || defined(VDBE_PROFILE) \\" +puts " || defined(SQLITE_DEBUG)" +puts "#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)" +puts "# define OpHelp(X) \"\\0\" X" +puts "#else" +puts "# define OpHelp(X)" +puts "#endif" +puts "const char *sqlite3OpcodeName(int i)\173" +puts " static const char *const azName\[\] = \173 \"?\"," +set mx 0 + +set in [open [lindex $argv 0] rb] +while {![eof $in]} { + set line [gets $in] + if {[regexp {^#define OP_} $line]} { + set name [lindex $line 1] + regsub {^OP_} $name {} name + set i [lindex $line 2] + set label($i) $name + if {$mx<$i} {set mx $i} + if {[regexp {synopsis: (.*) \*/} $line all x]} { + set synopsis($i) [string trim $x] + } else { + set synopsis($i) {} + } + } +} +close $in + +for {set i 1} {$i<=$mx} {incr i} { + puts [format " /* %3d */ %-18s OpHelp(\"%s\")," \ + $i \"$label($i)\" $synopsis($i)] +} +puts " \175;" +puts " return azName\[i\];" +puts "\175" +puts "#endif" diff --git a/tool/mkopcodeh.tcl b/tool/mkopcodeh.tcl new file mode 100644 index 0000000000..4c36f24ba4 --- /dev/null +++ b/tool/mkopcodeh.tcl @@ -0,0 +1,230 @@ +#!/usr/bin/tclsh +# +# Generate the file opcodes.h. +# +# This TCL script scans a concatenation of the parse.h output file from the +# parser and the vdbe.c source file in order to generate the opcodes numbers +# for all opcodes. +# +# The lines of the vdbe.c that we are interested in are of the form: +# +# case OP_aaaa: /* same as TK_bbbbb */ +# +# The TK_ comment is optional. If it is present, then the value assigned to +# the OP_ is the same as the TK_ value. If missing, the OP_ value is assigned +# a small integer that is different from every other OP_ value. +# +# We go to the trouble of making some OP_ values the same as TK_ values +# as an optimization. During parsing, things like expression operators +# are coded with TK_ values such as TK_ADD, TK_DIVIDE, and so forth. Later +# during code generation, we need to generate corresponding opcodes like +# OP_Add and OP_Divide. By making TK_ADD==OP_Add and TK_DIVIDE==OP_Divide, +# code to translate from one to the other is avoided. This makes the +# code generator run (infinitesimally) faster and more importantly it makes +# the library footprint smaller. +# +# This script also scans for lines of the form: +# +# case OP_aaaa: /* jump, in1, in2, in3, out2-prerelease, out3 */ +# +# When such comments are found on an opcode, it means that certain +# properties apply to that opcode. Set corresponding flags using the +# OPFLG_INITIALIZER macro. +# + +set in stdin +set currentOp {} +set nOp 0 +while {![eof $in]} { + set line [gets $in] + + # Remember the TK_ values from the parse.h file. + # NB: The "TK_" prefix stands for "ToKen", not the graphical Tk toolkit + # commonly associated with TCL. + # + if {[regexp {^#define TK_} $line]} { + set tk([lindex $line 1]) [lindex $line 2] + continue + } + + # Find "/* Opcode: " lines in the vdbe.c file. Each one introduces + # a new opcode. Remember which parameters are used. + # + if {[regexp {^.. Opcode: } $line]} { + set currentOp OP_[lindex $line 2] + set m 0 + foreach term $line { + switch $term { + P1 {incr m 1} + P2 {incr m 2} + P3 {incr m 4} + P4 {incr m 8} + P5 {incr m 16} + } + } + set paramused($currentOp) $m + } + + # Find "** Synopsis: " lines that follow Opcode: + # + if {[regexp {^.. Synopsis: (.*)} $line all x] && $currentOp!=""} { + set synopsis($currentOp) [string trim $x] + } + + # Scan for "case OP_aaaa:" lines in the vdbe.c file + # + if {[regexp {^case OP_} $line]} { + set line [split $line] + set name [string trim [lindex $line 1] :] + set op($name) -1 + set jump($name) 0 + set in1($name) 0 + set in2($name) 0 + set in3($name) 0 + set out1($name) 0 + set out2($name) 0 + for {set i 3} {$i<[llength $line]-1} {incr i} { + switch [string trim [lindex $line $i] ,] { + same { + incr i + if {[lindex $line $i]=="as"} { + incr i + set sym [string trim [lindex $line $i] ,] + set val $tk($sym) + set op($name) $val + set used($val) 1 + set sameas($val) $sym + set def($val) $name + } + } + jump {set jump($name) 1} + in1 {set in1($name) 1} + in2 {set in2($name) 1} + in3 {set in3($name) 1} + out2 {set out2($name) 1} + out3 {set out3($name) 1} + } + } + set order($nOp) $name + incr nOp + } +} + +# Assign numbers to all opcodes and output the result. +# +set cnt 0 +set max 0 +puts "/* Automatically generated. Do not edit */" +puts "/* See the tool/mkopcodeh.tcl script for details */" +set op(OP_Noop) -1 +set order($nOp) OP_Noop +incr nOp +set op(OP_Explain) -1 +set order($nOp) OP_Explain +incr nOp + +# The following are the opcodes that are processed by resolveP2Values() +# +set rp2v_ops { + OP_Transaction + OP_AutoCommit + OP_Savepoint + OP_Checkpoint + OP_Vacuum + OP_JournalMode + OP_VUpdate + OP_VFilter + OP_Next + OP_NextIfOpen + OP_SorterNext + OP_Prev + OP_PrevIfOpen +} + +# Assign small values to opcodes that are processed by resolveP2Values() +# to make code generation for the switch() statement smaller and faster. +# +set cnt 0 +for {set i 0} {$i<$nOp} {incr i} { + set name $order($i) + if {[lsearch $rp2v_ops $name]>=0} { + incr cnt + while {[info exists used($cnt)]} {incr cnt} + set op($name) $cnt + set used($cnt) 1 + set def($cnt) $name + } +} + +# Generate the numeric values for remaining opcodes +# +for {set i 0} {$i<$nOp} {incr i} { + set name $order($i) + if {$op($name)<0} { + incr cnt + while {[info exists used($cnt)]} {incr cnt} + set op($name) $cnt + set used($cnt) 1 + set def($cnt) $name + } +} +set max $cnt +for {set i 1} {$i<=$nOp} {incr i} { + if {![info exists used($i)]} { + set def($i) "OP_NotUsed_$i" + } + set name $def($i) + puts -nonewline [format {#define %-16s %3d} $name $i] + set com {} + if {[info exists sameas($i)]} { + set com "same as $sameas($i)" + } + if {[info exists synopsis($name)]} { + set x $synopsis($name) + if {$com==""} { + set com "synopsis: $x" + } else { + append com ", synopsis: $x" + } + } + if {$com!=""} { + puts -nonewline [format " /* %-42s */" $com] + } + puts "" +} + +# Generate the bitvectors: +# +set bv(0) 0 +for {set i 1} {$i<=$max} {incr i} { + set name $def($i) + if {[info exists jump($name)] && $jump($name)} {set a0 1} {set a0 0} + if {[info exists in1($name)] && $in1($name)} {set a1 2} {set a1 0} + if {[info exists in2($name)] && $in2($name)} {set a2 4} {set a2 0} + if {[info exists in3($name)] && $in3($name)} {set a3 8} {set a3 0} + if {[info exists out2($name)] && $out2($name)} {set a4 16} {set a4 0} + if {[info exists out3($name)] && $out3($name)} {set a5 32} {set a5 0} + set bv($i) [expr {$a0+$a1+$a2+$a3+$a4+$a5}] +} +puts "" +puts "/* Properties such as \"out2\" or \"jump\" that are specified in" +puts "** comments following the \"case\" for each opcode in the vdbe.c" +puts "** are encoded into bitvectors as follows:" +puts "*/" +puts "#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */" +puts "#define OPFLG_IN1 0x0002 /* in1: P1 is an input */" +puts "#define OPFLG_IN2 0x0004 /* in2: P2 is an input */" +puts "#define OPFLG_IN3 0x0008 /* in3: P3 is an input */" +puts "#define OPFLG_OUT2 0x0010 /* out2: P2 is an output */" +puts "#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */" +puts "#define OPFLG_INITIALIZER \173\\" +for {set i 0} {$i<=$max} {incr i} { + if {$i%8==0} { + puts -nonewline [format "/* %3d */" $i] + } + puts -nonewline [format " 0x%02x," $bv($i)] + if {$i%8==7} { + puts "\\" + } +} +puts "\175" diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index e52b036612..23241e27a6 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -211,7 +211,8 @@ proc copy_file {filename} { puts $out "#if 0" } elseif {!$linemacros && [regexp {^#line} $line]} { # Skip #line directives. - } elseif {$addstatic && ![regexp {^(static|typedef)} $line]} { + } elseif {$addstatic + && ![regexp {^(static|typedef|SQLITE_PRIVATE)} $line]} { # Skip adding the SQLITE_PRIVATE or SQLITE_API keyword before # functions if this header file does not need it. if {![info exists varonly_hdr($tail)] @@ -378,6 +379,8 @@ foreach file { fts3_icu.c sqlite3rbu.c dbstat.c + json1.c + fts5.c } { copy_file tsrc/$file } diff --git a/tool/mksqlite3h.tcl b/tool/mksqlite3h.tcl index cabce1f50b..2fe2157387 100644 --- a/tool/mksqlite3h.tcl +++ b/tool/mksqlite3h.tcl @@ -71,6 +71,7 @@ fconfigure stdout -translation lf set filelist [subst { $TOP/src/sqlite.h.in $TOP/ext/rtree/sqlite3rtree.h + $TOP/ext/fts5/fts5.h }] # These are the functions that accept a variable number of arguments. They diff --git a/tool/opcodeDoc.awk b/tool/opcodeDoc.awk deleted file mode 100644 index 492010624f..0000000000 --- a/tool/opcodeDoc.awk +++ /dev/null @@ -1,23 +0,0 @@ -# -# Extract opcode documentation for sqliteVdbe.c and generate HTML -# -BEGIN { - print "" - print "

SQLite Virtual Database Engine Opcodes

" - print "" -} -/ Opcode: /,/\*\// { - if( $2=="Opcode:" ){ - printf "\n\n" - }else if( NF>1 ){ - sub(/^ *\*\* /,"") - gsub(/" -} diff --git a/tool/replace.tcl b/tool/replace.tcl new file mode 100644 index 0000000000..b01a83accb --- /dev/null +++ b/tool/replace.tcl @@ -0,0 +1,20 @@ +#!/usr/bin/tcl +# +# Replace string with another string -OR- include +# only lines successfully modified with a regular +# expression. +# +set mode [string tolower [lindex $argv 0]] +set from [lindex $argv 1] +set to [lindex $argv 2] +if {$mode ni [list exact include]} {exit 1} +if {[string length $from]==0} {exit 2} +while {![eof stdin]} { + set line [gets stdin] + if {[eof stdin]} break + switch -exact $mode { + exact {set line [string map [list $from $to] $line]} + include {if {[regsub -all -- $from $line $to line]==0} continue} + } + puts stdout $line +} diff --git a/tool/space_used.tcl b/tool/space_used.tcl deleted file mode 100644 index 2044aa38c5..0000000000 --- a/tool/space_used.tcl +++ /dev/null @@ -1,111 +0,0 @@ -# Run this TCL script using "testfixture" in order get a report that shows -# how much disk space is used by a particular data to actually store data -# versus how much space is unused. -# - -# Get the name of the database to analyze -# -if {[llength $argv]!=1} { - puts stderr "Usage: $argv0 database-name" - exit 1 -} -set file_to_analyze [lindex $argv 0] - -# Open the database -# -sqlite db [lindex $argv 0] -set DB [btree_open [lindex $argv 0]] - -# Output the schema for the generated report -# -puts \ -{BEGIN; -CREATE TABLE space_used( - name clob, -- Name of a table or index in the database file - is_index boolean, -- TRUE if it is an index, false for a table - payload int, -- Total amount of data stored in this table or index - pri_pages int, -- Number of primary pages used - ovfl_pages int, -- Number of overflow pages used - pri_unused int, -- Number of unused bytes on primary pages - ovfl_unused int -- Number of unused bytes on overflow pages -);} - -# This query will be used to find the root page number for every index and -# table in the database. -# -set sql { - SELECT name, type, rootpage FROM sqlite_master - UNION ALL - SELECT 'sqlite_master', 'table', 2 - ORDER BY 1 -} - -# Initialize variables used for summary statistics. -# -set total_size 0 -set total_primary 0 -set total_overflow 0 -set total_unused_primary 0 -set total_unused_ovfl 0 - -# Analyze every table in the database, one at a time. -# -foreach {name type rootpage} [db eval $sql] { - set cursor [btree_cursor $DB $rootpage 0] - set go [btree_first $cursor] - set size 0 - catch {unset pg_used} - set unused_ovfl 0 - set n_overflow 0 - while {$go==0} { - set payload [btree_payload_size $cursor] - incr size $payload - set stat [btree_cursor_dump $cursor] - set pgno [lindex $stat 0] - set freebytes [lindex $stat 4] - set pg_used($pgno) $freebytes - if {$payload>238} { - set n [expr {($payload-238+1019)/1020}] - incr n_overflow $n - incr unused_ovfl [expr {$n*1020+238-$payload}] - } - set go [btree_next $cursor] - } - btree_close_cursor $cursor - set n_primary [llength [array names pg_used]] - set unused_primary 0 - foreach x [array names pg_used] {incr unused_primary $pg_used($x)} - regsub -all ' $name '' name - puts -nonewline "INSERT INTO space_used VALUES('$name'" - puts -nonewline ",[expr {$type=="index"}]" - puts ",$size,$n_primary,$n_overflow,$unused_primary,$unused_ovfl);" - incr total_size $size - incr total_primary $n_primary - incr total_overflow $n_overflow - incr total_unused_primary $unused_primary - incr total_unused_ovfl $unused_ovfl -} - -# Output summary statistics: -# -puts "-- Total payload size: $total_size" -puts "-- Total pages used: $total_primary primary and $total_overflow overflow" -set file_pgcnt [expr {[file size [lindex $argv 0]]/1024}] -puts -nonewline "-- Total unused bytes on primary pages: $total_unused_primary" -if {$total_primary>0} { - set upp [expr {$total_unused_primary/$total_primary}] - puts " (avg $upp bytes/page)" -} else { - puts "" -} -puts -nonewline "-- Total unused bytes on overflow pages: $total_unused_ovfl" -if {$total_overflow>0} { - set upp [expr {$total_unused_ovfl/$total_overflow}] - puts " (avg $upp bytes/page)" -} else { - puts "" -} -set n_free [expr {$file_pgcnt-$total_primary-$total_overflow}] -if {$n_free>0} {incr n_free -1} -puts "-- Total pages on freelist: $n_free" -puts "COMMIT;" diff --git a/tool/tostr.awk b/tool/tostr.awk deleted file mode 100644 index 83c6cc1a50..0000000000 --- a/tool/tostr.awk +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/awk -# -# Convert input text into a C string -# -{ - gsub(/\\/,"\\\\"); - gsub(/\"/,"\\\""); - print "\"" $0 "\\n\""; -} diff --git a/tool/tostr.tcl b/tool/tostr.tcl new file mode 100644 index 0000000000..cb06ee947f --- /dev/null +++ b/tool/tostr.tcl @@ -0,0 +1,12 @@ +#!/usr/bin/tcl +# +# Convert input text into a C string +# +set in [open [lindex $argv 0] rb] +while {![eof $in]} { + set line [gets $in] + if {[eof $in]} break; + set x [string map "\\\\ \\\\\\\\ \\\" \\\\\"" $line] + puts "\"$x\\n\"" +} +close $in
%s %s %s %s\n", $3, $4, $5, $6 - }else if( $1=="*/" ){ - printf "