diff --git a/Makefile.in b/Makefile.in
index a07279c3c9..54f7643327 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -583,6 +583,12 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS)
+srcck1$(BEXE): $(TOP)/tool/srcck1.c
+ $(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c
+
+sourcetest: srcck1$(BEXE) sqlite3.c
+ ./srcck1 sqlite3.c
+
fuzzershell$(TEXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(FUZZERSHELL_OPT) \
$(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS)
@@ -1083,7 +1089,7 @@ quicktest: ./testfixture$(TEXE)
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
-test: $(TESTPROGS) fastfuzztest
+test: $(TESTPROGS) sourcetest fastfuzztest
./testfixture$(TEXE) $(TOP)/test/veryquick.test $(TESTOPTS)
# Run a test using valgrind. This can take a really long time
@@ -1219,6 +1225,7 @@ clean:
rm -f sqlite-*-output.vsix
rm -f mptester mptester.exe
rm -f rbu rbu.exe
+ rm -f srcck1 srcck1.exe
rm -f fuzzershell fuzzershell.exe
rm -f fuzzcheck fuzzcheck.exe
rm -f sqldiff sqldiff.exe
diff --git a/Makefile.msc b/Makefile.msc
index 3621d4f92b..9620ff6237 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -10,11 +10,13 @@
#
TOP = .
+# <>
# Set this non-0 to create and use the SQLite amalgamation file.
#
!IFNDEF USE_AMALGAMATION
USE_AMALGAMATION = 1
!ENDIF
+# <>
# Set this non-0 to enable full warnings (-W4, etc) when compiling.
#
@@ -68,11 +70,13 @@ USE_WP81_OPTS = 0
SPLIT_AMALGAMATION = 0
!ENDIF
+# <>
# Set this non-0 to use the International Components for Unicode (ICU).
#
!IFNDEF USE_ICU
USE_ICU = 0
!ENDIF
+# <>
# Set this non-0 to dynamically link to the MSVC runtime library.
#
@@ -136,12 +140,14 @@ FOR_UAP = 0
FOR_WIN10 = 0
!ENDIF
+# <>
# Set this non-0 to skip attempting to look for and/or link with the Tcl
# runtime library.
#
!IFNDEF NO_TCL
NO_TCL = 0
!ENDIF
+# <>
# Set this to non-0 to create and use PDBs.
#
@@ -261,6 +267,15 @@ EXT_FEATURE_FLAGS =
############################### END OF OPTIONS ################################
###############################################################################
+# When compiling for the Windows 10 platform, the PLATFORM macro must be set
+# to an appropriate value (e.g. x86, x64, arm, arm64, etc).
+#
+!IF $(FOR_WIN10)!=0
+!IFNDEF PLATFORM
+!ERROR Using the FOR_WIN10 option requires a value for PLATFORM.
+!ENDIF
+!ENDIF
+
# This assumes that MSVC is always installed in 32-bit Program Files directory
# and sets the variable for use in locating other 32-bit installs accordingly.
#
@@ -291,7 +306,7 @@ LD = link.exe
RC = rc.exe
!ENDIF
-# Check for the MSVC runtime library path macro. Othertise, this value will
+# Check for the MSVC runtime library path macro. Otherwise, this value will
# default to the 'lib' directory underneath the MSVC installation directory.
#
!IFNDEF CRTLIBPATH
@@ -328,7 +343,7 @@ NCC = $(NCC:\\=\)
NCC = $(CC)
!ENDIF
-# Check for the MSVC native runtime library path macro. Othertise,
+# Check for the MSVC native runtime library path macro. Otherwise,
# this value will default to the 'lib' directory underneath the MSVC
# installation directory.
#
@@ -338,7 +353,7 @@ NCRTLIBPATH = $(VCINSTALLDIR)\lib
NCRTLIBPATH = $(NCRTLIBPATH:\\=\)
-# Check for the Platform SDK library path macro. Othertise, this
+# Check for the Platform SDK library path macro. Otherwise, this
# value will default to the 'lib' directory underneath the Windows
# SDK installation directory (the environment variable used appears
# to be available when using Visual C++ 2008 or later via the
@@ -350,6 +365,16 @@ NSDKLIBPATH = $(WINDOWSSDKDIR)\lib
NSDKLIBPATH = $(NSDKLIBPATH:\\=\)
+# Check for the UCRT library path macro. Otherwise, this value will
+# default to the version-specific, platform-specific 'lib' directory
+# underneath the Windows SDK installation directory.
+#
+!IFNDEF UCRTLIBPATH
+UCRTLIBPATH = $(WINDOWSSDKDIR)\lib\$(WINDOWSSDKLIBVERSION)\ucrt\$(PLATFORM)
+!ENDIF
+
+UCRTLIBPATH = $(UCRTLIBPATH:\\=\)
+
# C compiler and options for use in building executables that
# will run on the platform that is doing the build.
#
@@ -535,7 +560,7 @@ BCC = $(BCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
# USE_CRT_DLL option is set to force dynamically linking to the
# MSVC runtime library.
#
-!IF $(FOR_WINRT)!=0 || $(FOR_WIN10)!=0 || $(USE_CRT_DLL)!=0
+!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
!IF $(DEBUG)>1
TCC = $(TCC) -MDd
BCC = $(BCC) -MDd
@@ -553,6 +578,7 @@ BCC = $(BCC) -MT
!ENDIF
!ENDIF
+# <>
# The mksqlite3c.tcl and mksqlite3h.tcl scripts will pull in
# any extension header files by default. For non-amalgamation
# builds, we need to make sure the compiler can find these.
@@ -576,6 +602,7 @@ MKSQLITE3C_ARGS = --linemacros
MKSQLITE3C_ARGS =
!ENDIF
!ENDIF
+# <>
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
# Omitting the define will cause extra debugging code to be inserted and
@@ -640,6 +667,7 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
!ENDIF
!ENDIF
+# <>
# The locations of the Tcl header and library files. Also, the library that
# non-stubs enabled programs using Tcl must link against. These variables
# (TCLINCDIR, TCLLIBDIR, and LIBTCL) may be overridden via the environment
@@ -691,6 +719,7 @@ LIBICU = icuuc.lib icuin.lib
!IFNDEF TCLSH_CMD
TCLSH_CMD = tclsh85
!ENDIF
+# <>
# Compiler options needed for programs that use the readline() library.
#
@@ -789,6 +818,7 @@ TCC = $(TCC) -Zi
BCC = $(BCC) -Zi
!ENDIF
+# <>
# If ICU support is enabled, add the compiler options for it.
#
!IF $(USE_ICU)!=0
@@ -799,6 +829,7 @@ RCC = $(RCC) -I$(TOP)\ext\icu
TCC = $(TCC) -I$(ICUINCDIR)
RCC = $(RCC) -I$(ICUINCDIR)
!ENDIF
+# <>
# Command line prefixes for compiling code, compiling resources,
# linking, etc.
@@ -876,9 +907,10 @@ LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelH
LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
!ENDIF
-# When compiling for UAP, some extra linker options are also required.
+# When compiling for UAP or the Windows 10 platform, some extra linker
+# options are also required.
#
-!IF $(FOR_UAP)!=0
+!IF $(FOR_UAP)!=0 || $(FOR_WIN10)!=0
LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE /NODEFAULTLIB:kernel32.lib
LTLINKOPTS = $(LTLINKOPTS) mincore.lib
!IFDEF PSDKLIBPATH
@@ -886,6 +918,15 @@ LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(PSDKLIBPATH)"
!ENDIF
!ENDIF
+!IF $(FOR_WIN10)!=0
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(UCRTLIBPATH)"
+!IF $(DEBUG)>1
+LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrtd.lib /DEFAULTLIB:ucrtd.lib
+!ELSE
+LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
+!ENDIF
+!ENDIF
+
# If either debugging or symbols are enabled, enable PDBs.
#
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
@@ -894,6 +935,7 @@ LDFLAGS = /DEBUG $(LDOPTS)
LDFLAGS = $(LDOPTS)
!ENDIF
+# <>
# Start with the Tcl related linker options.
#
!IF $(NO_TCL)==0
@@ -907,10 +949,12 @@ LTLIBS = $(LIBTCL)
LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ICULIBDIR)
LTLIBS = $(LTLIBS) $(LIBICU)
!ENDIF
+# <>
# You should not have to change anything below this line
###############################################################################
+# <>
# Object files for the SQLite library (non-amalgamation).
#
LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
@@ -934,6 +978,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
utf.lo vtab.lo
+# <>
# Object files for the amalgamation.
#
@@ -941,11 +986,15 @@ LIBOBJS1 = sqlite3.lo
# Determine the real value of LIBOBJ based on the 'configure' script
#
+# <>
!IF $(USE_AMALGAMATION)==0
LIBOBJ = $(LIBOBJS0)
!ELSE
+# <>
LIBOBJ = $(LIBOBJS1)
+# <>
!ENDIF
+# <>
# Determine if embedded resource compilation and usage are enabled.
#
@@ -955,6 +1004,7 @@ LIBRESOBJS = sqlite3res.lo
LIBRESOBJS =
!ENDIF
+# <>
# All of the source code files.
#
SRC1 = \
@@ -1303,6 +1353,7 @@ FUZZDATA = \
$(TOP)\test\fuzzdata2.db \
$(TOP)\test\fuzzdata3.db \
$(TOP)\test\fuzzdata4.db
+# <>
# Additional compiler options for the shell. These are only effective
# when the shell is not being dynamically linked.
@@ -1311,6 +1362,7 @@ FUZZDATA = \
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5
!ENDIF
+# <>
# Extra compiler options for various test tools.
#
MPTESTER_COMPILE_OPTS = -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS5
@@ -1321,24 +1373,61 @@ FUZZCHECK_COMPILE_OPTS = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
#
TESTOPTS = --verbose=file --output=test-out.txt
+# Extra targets for the "all" target that require Tcl.
+#
+!IF $(NO_TCL)==0
+ALL_TCL_TARGETS = libtclsqlite3.lib
+!ELSE
+ALL_TCL_TARGETS =
+!ENDIF
+# <>
+
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
-all: dll libsqlite3.lib shell libtclsqlite3.lib
+all: dll libsqlite3.lib shell $(ALL_TCL_TARGETS)
+
+# Dynamic link library section.
+#
+dll: $(SQLITE3DLL)
+
+# Shell executable.
+#
+shell: $(SQLITE3EXE)
libsqlite3.lib: $(LIBOBJ)
$(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS)
+# <>
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS)
+# <>
+
+$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
+ $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+
+# <>
+sqlite3.def: libsqlite3.lib
+ echo EXPORTS > sqlite3.def
+ dumpbin /all libsqlite3.lib \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3_.*)$$" \1 \
+ | sort >> sqlite3.def
+# <>
$(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
$(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\src\shell.c $(SHELL_CORE_SRC) \
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
+# <>
sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+srcck1.exe: $(TOP)\tool\srcck1.c
+ $(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c
+
+sourcetest: srcck1.exe sqlite3.c
+ srcck1.exe sqlite3.c
+
fuzzershell.exe: $(TOP)\tool\fuzzershell.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(FUZZERSHELL_COMPILE_OPTS) $(TOP)\tool\fuzzershell.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
@@ -1389,12 +1478,14 @@ sqlite3.c: .target_source sqlite3ext.h $(TOP)\tool\mksqlite3c.tcl
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
+# <>
# Rule to build the amalgamation
#
sqlite3.lo: $(SQLITE3C)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(SQLITE3C)
+# <>
# Rules to build the LEMON compiler generator
#
lempar.c: $(TOP)\tool\lempar.c
@@ -1415,11 +1506,13 @@ parse.lo: parse.c $(HDR)
opcodes.lo: opcodes.c
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c opcodes.c
+# <>
# Rule to build the Win32 resources object file.
#
!IF $(USE_RC)!=0
-$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(HDR)
+# <>
+$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(SQLITE3H)
echo #ifndef SQLITE_RESOURCE_VERSION > sqlite3rc.h
for /F %%V in ('type "$(TOP)\VERSION"') do ( \
echo #define SQLITE_RESOURCE_VERSION %%V \
@@ -1427,8 +1520,10 @@ $(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(HDR)
)
echo #endif >> sqlite3rc.h
$(LTRCOMPILE) -fo $(LIBRESOBJS) $(TOP)\src\sqlite3.rc
+# <>
!ENDIF
+# <>
# Rules to build individual *.lo files from files in the src directory.
#
alter.lo: $(TOP)\src\alter.c $(HDR)
@@ -1856,14 +1951,14 @@ fastfuzztest: fuzzcheck.exe
# Minimal testing that runs in less than 3 minutes (on a fast machine)
#
-quicktest: testfixture.exe
+quicktest: testfixture.exe sourcetest
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\extraquick.test $(TESTOPTS)
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
-test: $(TESTPROGS) fastfuzztest
+test: $(TESTPROGS) sourcetest fastfuzztest
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\veryquick.test $(TESTOPTS)
@@ -1928,10 +2023,12 @@ speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(SQLITE3H)
rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU -Fe$@ \
$(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
+# <>
clean:
del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL
- del /Q *.bsc *.cod *.da *.bb *.bbg gmon.out 2>NUL
+ del /Q *.bsc *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL
+# <>
del /Q $(SQLITE3C) $(SQLITE3H) opcodes.c opcodes.h 2>NUL
del /Q lemon.* lempar.c parse.* 2>NUL
del /Q mkkeywordhash.* keywordhash.h 2>NUL
@@ -1948,7 +2045,7 @@ clean:
del /Q testfixture.exe test.db 2>NUL
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe 2>NUL
del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL
- del /Q mptester.exe wordcount.exe rbu.exe 2>NUL
+ del /Q mptester.exe wordcount.exe rbu.exe srcck1.exe 2>NUL
del /Q $(SQLITE3EXE) $(SQLITE3DLL) sqlite3.def 2>NUL
del /Q sqlite3.c sqlite3-*.c 2>NUL
del /Q sqlite3rc.h 2>NUL
@@ -1957,20 +2054,4 @@ clean:
del /Q sqlite-*-output.vsix 2>NUL
del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe 2>NUL
del /Q fts5.* fts5parse.* 2>NUL
-
-# Shell executable.
-#
-shell: $(SQLITE3EXE)
-
-# Dynamic link library section.
-#
-dll: $(SQLITE3DLL)
-
-sqlite3.def: libsqlite3.lib
- echo EXPORTS > sqlite3.def
- dumpbin /all libsqlite3.lib \
- | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3_.*)$$" \1 \
- | sort >> sqlite3.def
-
-$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
- $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+# <>
diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am
index eed3ba41cb..03424a606b 100644
--- a/autoconf/Makefile.am
+++ b/autoconf/Makefile.am
@@ -14,7 +14,7 @@ sqlite3_CFLAGS = $(AM_CFLAGS)
include_HEADERS = sqlite3.h sqlite3ext.h
-EXTRA_DIST = sqlite3.1 tea
+EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt
pkgconfigdir = ${libdir}/pkgconfig
pkgconfig_DATA = sqlite3.pc
diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc
new file mode 100644
index 0000000000..65f2396180
--- /dev/null
+++ b/autoconf/Makefile.msc
@@ -0,0 +1,921 @@
+#### DO NOT EDIT ####
+# This makefile is automatically generated from the Makefile.msc at
+# the root of the canonical SQLite source tree (not the
+# amalgamation tarball) using the tool/mkmsvcmin.tcl
+# script.
+#
+
+#
+# nmake Makefile for SQLite
+#
+###############################################################################
+############################## START OF OPTIONS ###############################
+###############################################################################
+
+# The toplevel directory of the source tree. This is the directory
+# that contains this "Makefile.msc".
+#
+TOP = .
+
+
+# Set this non-0 to enable full warnings (-W4, etc) when compiling.
+#
+!IFNDEF USE_FULLWARN
+USE_FULLWARN = 0
+!ENDIF
+
+# Set this non-0 to use "stdcall" calling convention for the core library
+# and shell executable.
+#
+!IFNDEF USE_STDCALL
+USE_STDCALL = 0
+!ENDIF
+
+# Set this non-0 to have the shell executable link against the core dynamic
+# link library.
+#
+!IFNDEF DYNAMIC_SHELL
+DYNAMIC_SHELL = 0
+!ENDIF
+
+# Set this non-0 to enable extra code that attempts to detect misuse of the
+# SQLite API.
+#
+!IFNDEF API_ARMOR
+API_ARMOR = 0
+!ENDIF
+
+# If necessary, create a list of harmless compiler warnings to disable when
+# compiling the various tools. For the SQLite source code itself, warnings,
+# if any, will be disabled from within it.
+#
+!IFNDEF NO_WARN
+!IF $(USE_FULLWARN)!=0
+NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4130 -wd4152 -wd4189 -wd4206
+NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4305 -wd4306 -wd4702 -wd4706
+!ENDIF
+!ENDIF
+
+# Set this non-0 to use the library paths and other options necessary for
+# Windows Phone 8.1.
+#
+!IFNDEF USE_WP81_OPTS
+USE_WP81_OPTS = 0
+!ENDIF
+
+# Set this non-0 to split the SQLite amalgamation file into chunks to
+# be used for debugging with Visual Studio.
+#
+!IFNDEF SPLIT_AMALGAMATION
+SPLIT_AMALGAMATION = 0
+!ENDIF
+
+
+# Set this non-0 to dynamically link to the MSVC runtime library.
+#
+!IFNDEF USE_CRT_DLL
+USE_CRT_DLL = 0
+!ENDIF
+
+# Set this non-0 to link to the RPCRT4 library.
+#
+!IFNDEF USE_RPCRT4_LIB
+USE_RPCRT4_LIB = 0
+!ENDIF
+
+# Set this non-0 to generate assembly code listings for the source code
+# files.
+#
+!IFNDEF USE_LISTINGS
+USE_LISTINGS = 0
+!ENDIF
+
+# Set this non-0 to attempt setting the native compiler automatically
+# for cross-compiling the command line tools needed during the compilation
+# process.
+#
+!IFNDEF XCOMPILE
+XCOMPILE = 0
+!ENDIF
+
+# Set this non-0 to use the native libraries paths for cross-compiling
+# the command line tools needed during the compilation process.
+#
+!IFNDEF USE_NATIVE_LIBPATHS
+USE_NATIVE_LIBPATHS = 0
+!ENDIF
+
+# Set this 0 to skip the compiling and embedding of version resources.
+#
+!IFNDEF USE_RC
+USE_RC = 1
+!ENDIF
+
+# Set this non-0 to compile binaries suitable for the WinRT environment.
+# This setting does not apply to any binaries that require Tcl to operate
+# properly (i.e. the text fixture, etc).
+#
+!IFNDEF FOR_WINRT
+FOR_WINRT = 0
+!ENDIF
+
+# Set this non-0 to compile binaries suitable for the UAP environment.
+# This setting does not apply to any binaries that require Tcl to operate
+# properly (i.e. the text fixture, etc).
+#
+!IFNDEF FOR_UAP
+FOR_UAP = 0
+!ENDIF
+
+# Set this non-0 to compile binaries suitable for the Windows 10 platform.
+#
+!IFNDEF FOR_WIN10
+FOR_WIN10 = 0
+!ENDIF
+
+
+# Set this to non-0 to create and use PDBs.
+#
+!IFNDEF SYMBOLS
+SYMBOLS = 1
+!ENDIF
+
+# Set this to non-0 to use the SQLite debugging heap subsystem.
+#
+!IFNDEF MEMDEBUG
+MEMDEBUG = 0
+!ENDIF
+
+# Set this to non-0 to use the Win32 native heap subsystem.
+#
+!IFNDEF WIN32HEAP
+WIN32HEAP = 0
+!ENDIF
+
+# Set this to non-0 to enable OSTRACE() macros, which can be useful when
+# debugging.
+#
+!IFNDEF OSTRACE
+OSTRACE = 0
+!ENDIF
+
+# Set this to one of the following values to enable various debugging
+# features. Each level includes the debugging options from the previous
+# levels. Currently, the recognized values for DEBUG are:
+#
+# 0 == NDEBUG: Disables assert() and other runtime diagnostics.
+# 1 == SQLITE_ENABLE_API_ARMOR: extra attempts to detect misuse of the API.
+# 2 == Disables NDEBUG and all optimizations and then enables PDBs.
+# 3 == SQLITE_DEBUG: Enables various diagnostics messages and code.
+# 4 == SQLITE_WIN32_MALLOC_VALIDATE: Validate the Win32 native heap per call.
+# 5 == SQLITE_DEBUG_OS_TRACE: Enables output from the OSTRACE() macros.
+# 6 == SQLITE_ENABLE_IOTRACE: Enables output from the IOTRACE() macros.
+#
+!IFNDEF DEBUG
+DEBUG = 0
+!ENDIF
+
+# Enable use of available compiler optimizations? Normally, this should be
+# non-zero. Setting this to zero, thus disabling all compiler optimizations,
+# can be useful for testing.
+#
+!IFNDEF OPTIMIZATIONS
+OPTIMIZATIONS = 2
+!ENDIF
+
+# Set the source code file to be used by executables and libraries when
+# they need the amalgamation.
+#
+!IFNDEF SQLITE3C
+!IF $(SPLIT_AMALGAMATION)!=0
+SQLITE3C = sqlite3-all.c
+!ELSE
+SQLITE3C = sqlite3.c
+!ENDIF
+!ENDIF
+
+# Set the include code file to be used by executables and libraries when
+# they need SQLite.
+#
+!IFNDEF SQLITE3H
+SQLITE3H = sqlite3.h
+!ENDIF
+
+# This is the name to use for the SQLite dynamic link library (DLL).
+#
+!IFNDEF SQLITE3DLL
+SQLITE3DLL = sqlite3.dll
+!ENDIF
+
+# This is the name to use for the SQLite import library (LIB).
+#
+!IFNDEF SQLITE3LIB
+SQLITE3LIB = sqlite3.lib
+!ENDIF
+
+# This is the name to use for the SQLite shell executable (EXE).
+#
+!IFNDEF SQLITE3EXE
+SQLITE3EXE = sqlite3.exe
+!ENDIF
+
+# This is the argument used to set the program database (PDB) file for the
+# SQLite shell executable (EXE).
+#
+!IFNDEF SQLITE3EXEPDB
+SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
+!ENDIF
+
+# These are the "standard" SQLite compilation options used when compiling for
+# the Windows platform.
+#
+!IFNDEF OPT_FEATURE_FLAGS
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
+!ENDIF
+
+# These are the "extended" SQLite compilation options used when compiling for
+# the Windows 10 platform.
+#
+!IFNDEF EXT_FEATURE_FLAGS
+!IF $(FOR_WIN10)!=0
+EXT_FEATURE_FLAGS = $(EXT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS4=1
+EXT_FEATURE_FLAGS = $(EXT_FEATURE_FLAGS) -DSQLITE_SYSTEM_MALLOC=1
+EXT_FEATURE_FLAGS = $(EXT_FEATURE_FLAGS) -DSQLITE_OMIT_LOCALTIME=1
+!ELSE
+EXT_FEATURE_FLAGS =
+!ENDIF
+!ENDIF
+
+###############################################################################
+############################### END OF OPTIONS ################################
+###############################################################################
+
+# When compiling for the Windows 10 platform, the PLATFORM macro must be set
+# to an appropriate value (e.g. x86, x64, arm, arm64, etc).
+#
+!IF $(FOR_WIN10)!=0
+!IFNDEF PLATFORM
+!ERROR Using the FOR_WIN10 option requires a value for PLATFORM.
+!ENDIF
+!ENDIF
+
+# This assumes that MSVC is always installed in 32-bit Program Files directory
+# and sets the variable for use in locating other 32-bit installs accordingly.
+#
+PROGRAMFILES_X86 = $(VCINSTALLDIR)\..\..
+PROGRAMFILES_X86 = $(PROGRAMFILES_X86:\\=\)
+
+# Check for the predefined command macro CC. This should point to the compiler
+# binary for the target platform. If it is not defined, simply define it to
+# the legacy default value 'cl.exe'.
+#
+!IFNDEF CC
+CC = cl.exe
+!ENDIF
+
+# Check for the command macro LD. This should point to the linker binary for
+# the target platform. If it is not defined, simply define it to the legacy
+# default value 'link.exe'.
+#
+!IFNDEF LD
+LD = link.exe
+!ENDIF
+
+# Check for the predefined command macro RC. This should point to the resource
+# compiler binary for the target platform. If it is not defined, simply define
+# it to the legacy default value 'rc.exe'.
+#
+!IFNDEF RC
+RC = rc.exe
+!ENDIF
+
+# Check for the MSVC runtime library path macro. Otherwise, this value will
+# default to the 'lib' directory underneath the MSVC installation directory.
+#
+!IFNDEF CRTLIBPATH
+CRTLIBPATH = $(VCINSTALLDIR)\lib
+!ENDIF
+
+CRTLIBPATH = $(CRTLIBPATH:\\=\)
+
+# Check for the command macro NCC. This should point to the compiler binary
+# for the platform the compilation process is taking place on. If it is not
+# defined, simply define it to have the same value as the CC macro. When
+# cross-compiling, it is suggested that this macro be modified via the command
+# line (since nmake itself does not provide a built-in method to guess it).
+# For example, to use the x86 compiler when cross-compiling for x64, a command
+# line similar to the following could be used (all on one line):
+#
+# nmake /f Makefile.msc sqlite3.dll
+# XCOMPILE=1 USE_NATIVE_LIBPATHS=1
+#
+# Alternatively, the full path and file name to the compiler binary for the
+# platform the compilation process is taking place may be specified (all on
+# one line):
+#
+# nmake /f Makefile.msc sqlite3.dll
+# "NCC=""%VCINSTALLDIR%\bin\cl.exe"""
+# USE_NATIVE_LIBPATHS=1
+#
+!IFDEF NCC
+NCC = $(NCC:\\=\)
+!ELSEIF $(XCOMPILE)!=0
+NCC = "$(VCINSTALLDIR)\bin\$(CC)"
+NCC = $(NCC:\\=\)
+!ELSE
+NCC = $(CC)
+!ENDIF
+
+# Check for the MSVC native runtime library path macro. Otherwise,
+# this value will default to the 'lib' directory underneath the MSVC
+# installation directory.
+#
+!IFNDEF NCRTLIBPATH
+NCRTLIBPATH = $(VCINSTALLDIR)\lib
+!ENDIF
+
+NCRTLIBPATH = $(NCRTLIBPATH:\\=\)
+
+# Check for the Platform SDK library path macro. Otherwise, this
+# value will default to the 'lib' directory underneath the Windows
+# SDK installation directory (the environment variable used appears
+# to be available when using Visual C++ 2008 or later via the
+# command line).
+#
+!IFNDEF NSDKLIBPATH
+NSDKLIBPATH = $(WINDOWSSDKDIR)\lib
+!ENDIF
+
+NSDKLIBPATH = $(NSDKLIBPATH:\\=\)
+
+# Check for the UCRT library path macro. Otherwise, this value will
+# default to the version-specific, platform-specific 'lib' directory
+# underneath the Windows SDK installation directory.
+#
+!IFNDEF UCRTLIBPATH
+UCRTLIBPATH = $(WINDOWSSDKDIR)\lib\$(WINDOWSSDKLIBVERSION)\ucrt\$(PLATFORM)
+!ENDIF
+
+UCRTLIBPATH = $(UCRTLIBPATH:\\=\)
+
+# C compiler and options for use in building executables that
+# will run on the platform that is doing the build.
+#
+!IF $(USE_FULLWARN)!=0
+BCC = $(NCC) -nologo -W4 $(CCOPTS) $(BCCOPTS)
+!ELSE
+BCC = $(NCC) -nologo -W3 $(CCOPTS) $(BCCOPTS)
+!ENDIF
+
+# Check if assembly code listings should be generated for the source
+# code files to be compiled.
+#
+!IF $(USE_LISTINGS)!=0
+BCC = $(BCC) -FAcs
+!ENDIF
+
+# Check if the native library paths should be used when compiling
+# the command line tools used during the compilation process. If
+# so, set the necessary macro now.
+#
+!IF $(USE_NATIVE_LIBPATHS)!=0
+NLTLIBPATHS = "/LIBPATH:$(NCRTLIBPATH)" "/LIBPATH:$(NSDKLIBPATH)"
+
+!IFDEF NUCRTLIBPATH
+NUCRTLIBPATH = $(NUCRTLIBPATH:\\=\)
+NLTLIBPATHS = $(NLTLIBPATHS) "/LIBPATH:$(NUCRTLIBPATH)"
+!ENDIF
+!ENDIF
+
+# C compiler and options for use in building executables that
+# will run on the target platform. (BCC and TCC are usually the
+# same unless your are cross-compiling.)
+#
+!IF $(USE_FULLWARN)!=0
+TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS)
+!ELSE
+TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS)
+!ENDIF
+
+TCC = $(TCC) -DSQLITE_OS_WIN=1 -I$(TOP) -fp:precise
+RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) $(RCOPTS) $(RCCOPTS)
+
+# Adjust the names of the primary targets for use with Windows 10.
+#
+!IF $(FOR_WIN10)!=0
+SQLITE3DLL = winsqlite3.dll
+SQLITE3LIB = winsqlite3.lib
+SQLITE3EXE = winsqlite3shell.exe
+SQLITE3EXEPDB =
+!ENDIF
+
+# Check if we want to use the "stdcall" calling convention when compiling.
+# This is not supported by the compilers for non-x86 platforms. It should
+# also be noted here that building any target with these "stdcall" options
+# will most likely fail if the Tcl library is also required. This is due
+# to how the Tcl library functions are declared and exported (i.e. without
+# an explicit calling convention, which results in "cdecl").
+#
+!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
+!IF "$(PLATFORM)"=="x86"
+CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
+SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
+!ELSE
+!IFNDEF PLATFORM
+CORE_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
+SHELL_CCONV_OPTS = -Gz -DSQLITE_CDECL=__cdecl -DSQLITE_STDCALL=__stdcall
+!ELSE
+CORE_CCONV_OPTS =
+SHELL_CCONV_OPTS =
+!ENDIF
+!ENDIF
+!ELSE
+CORE_CCONV_OPTS =
+SHELL_CCONV_OPTS =
+!ENDIF
+
+# These are additional compiler options used for the core library.
+#
+!IFNDEF CORE_COMPILE_OPTS
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+CORE_COMPILE_OPTS = $(CORE_CCONV_OPTS) -DSQLITE_API=__declspec(dllexport)
+!ELSE
+CORE_COMPILE_OPTS = $(CORE_CCONV_OPTS)
+!ENDIF
+!ENDIF
+
+# These are the additional targets that the core library should depend on
+# when linking.
+#
+!IFNDEF CORE_LINK_DEP
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+CORE_LINK_DEP =
+!ELSE
+CORE_LINK_DEP =
+!ENDIF
+!ENDIF
+
+# These are additional linker options used for the core library.
+#
+!IFNDEF CORE_LINK_OPTS
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+CORE_LINK_OPTS =
+!ELSE
+CORE_LINK_OPTS =
+!ENDIF
+!ENDIF
+
+# These are additional compiler options used for the shell executable.
+#
+!IFNDEF SHELL_COMPILE_OPTS
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+SHELL_COMPILE_OPTS = $(SHELL_CCONV_OPTS) -DSQLITE_API=__declspec(dllimport)
+!ELSE
+SHELL_COMPILE_OPTS = $(SHELL_CCONV_OPTS)
+!ENDIF
+!ENDIF
+
+# This is the source code that the shell executable should be compiled
+# with.
+#
+!IFNDEF SHELL_CORE_SRC
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+SHELL_CORE_SRC =
+!ELSE
+SHELL_CORE_SRC = $(SQLITE3C)
+!ENDIF
+!ENDIF
+
+# This is the core library that the shell executable should depend on.
+#
+!IFNDEF SHELL_CORE_DEP
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+SHELL_CORE_DEP = $(SQLITE3DLL)
+!ELSE
+SHELL_CORE_DEP =
+!ENDIF
+!ENDIF
+
+# This is the core library that the shell executable should link with.
+#
+!IFNDEF SHELL_CORE_LIB
+!IF $(DYNAMIC_SHELL)!=0 || $(FOR_WIN10)!=0
+SHELL_CORE_LIB = $(SQLITE3LIB)
+!ELSE
+SHELL_CORE_LIB =
+!ENDIF
+!ENDIF
+
+# These are additional linker options used for the shell executable.
+#
+!IFNDEF SHELL_LINK_OPTS
+SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
+!ENDIF
+
+# Check if assembly code listings should be generated for the source
+# code files to be compiled.
+#
+!IF $(USE_LISTINGS)!=0
+TCC = $(TCC) -FAcs
+!ENDIF
+
+# When compiling the library for use in the WinRT environment,
+# the following compile-time options must be used as well to
+# disable use of Win32 APIs that are not available and to enable
+# use of Win32 APIs that are specific to Windows 8 and/or WinRT.
+#
+!IF $(FOR_WINRT)!=0
+TCC = $(TCC) -DSQLITE_OS_WINRT=1
+RCC = $(RCC) -DSQLITE_OS_WINRT=1
+TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
+RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
+!ENDIF
+
+# C compiler options for the Windows 10 platform (needs MSVC 2015).
+#
+!IF $(FOR_WIN10)!=0
+TCC = $(TCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
+BCC = $(BCC) /guard:cf -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
+!ENDIF
+
+# Also, we need to dynamically link to the correct MSVC runtime
+# when compiling for WinRT (e.g. debug or release) OR if the
+# USE_CRT_DLL option is set to force dynamically linking to the
+# MSVC runtime library.
+#
+!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
+!IF $(DEBUG)>1
+TCC = $(TCC) -MDd
+BCC = $(BCC) -MDd
+!ELSE
+TCC = $(TCC) -MD
+BCC = $(BCC) -MD
+!ENDIF
+!ELSE
+!IF $(DEBUG)>1
+TCC = $(TCC) -MTd
+BCC = $(BCC) -MTd
+!ELSE
+TCC = $(TCC) -MT
+BCC = $(BCC) -MT
+!ENDIF
+!ENDIF
+
+
+# Define -DNDEBUG to compile without debugging (i.e., for production usage)
+# Omitting the define will cause extra debugging code to be inserted and
+# includes extra comments when "EXPLAIN stmt" is used.
+#
+!IF $(DEBUG)==0
+TCC = $(TCC) -DNDEBUG
+BCC = $(BCC) -DNDEBUG
+RCC = $(RCC) -DNDEBUG
+!ENDIF
+
+!IF $(DEBUG)>0 || $(API_ARMOR)!=0 || $(FOR_WIN10)!=0
+TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1
+RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
+!ENDIF
+
+!IF $(DEBUG)>2
+TCC = $(TCC) -DSQLITE_DEBUG=1
+RCC = $(RCC) -DSQLITE_DEBUG=1
+!ENDIF
+
+!IF $(DEBUG)>4 || $(OSTRACE)!=0
+TCC = $(TCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1
+RCC = $(RCC) -DSQLITE_FORCE_OS_TRACE=1 -DSQLITE_DEBUG_OS_TRACE=1
+!ENDIF
+
+!IF $(DEBUG)>5
+TCC = $(TCC) -DSQLITE_ENABLE_IOTRACE=1
+RCC = $(RCC) -DSQLITE_ENABLE_IOTRACE=1
+!ENDIF
+
+# Prevent warnings about "insecure" MSVC runtime library functions
+# being used.
+#
+TCC = $(TCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
+BCC = $(BCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
+RCC = $(RCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
+
+# Prevent warnings about "deprecated" POSIX functions being used.
+#
+TCC = $(TCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
+BCC = $(BCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
+RCC = $(RCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
+
+# Use the SQLite debugging heap subsystem?
+#
+!IF $(MEMDEBUG)!=0
+TCC = $(TCC) -DSQLITE_MEMDEBUG=1
+RCC = $(RCC) -DSQLITE_MEMDEBUG=1
+
+# Use native Win32 heap subsystem instead of malloc/free?
+#
+!ELSEIF $(WIN32HEAP)!=0
+TCC = $(TCC) -DSQLITE_WIN32_MALLOC=1
+RCC = $(RCC) -DSQLITE_WIN32_MALLOC=1
+
+# Validate the heap on every call into the native Win32 heap subsystem?
+#
+!IF $(DEBUG)>3
+TCC = $(TCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
+RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
+!ENDIF
+!ENDIF
+
+
+# Compiler options needed for programs that use the readline() library.
+#
+!IFNDEF READLINE_FLAGS
+READLINE_FLAGS = -DHAVE_READLINE=0
+!ENDIF
+
+# The library that programs using readline() must link against.
+#
+!IFNDEF LIBREADLINE
+LIBREADLINE =
+!ENDIF
+
+# Should the database engine be compiled threadsafe
+#
+TCC = $(TCC) -DSQLITE_THREADSAFE=1
+RCC = $(RCC) -DSQLITE_THREADSAFE=1
+
+# Do threads override each others locks by default (1), or do we test (-1)
+#
+TCC = $(TCC) -DSQLITE_THREAD_OVERRIDE_LOCK=-1
+RCC = $(RCC) -DSQLITE_THREAD_OVERRIDE_LOCK=-1
+
+# Any target libraries which libsqlite must be linked against
+#
+!IFNDEF TLIBS
+TLIBS =
+!ENDIF
+
+# Flags controlling use of the in memory btree implementation
+#
+# SQLITE_TEMP_STORE is 0 to force temporary tables to be in a file, 1 to
+# default to file, 2 to default to memory, and 3 to force temporary
+# tables to always be in memory.
+#
+TCC = $(TCC) -DSQLITE_TEMP_STORE=1
+RCC = $(RCC) -DSQLITE_TEMP_STORE=1
+
+# Enable/disable loadable extensions, and other optional features
+# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
+# The same set of OMIT and ENABLE flags should be passed to the
+# LEMON parser generator and the mkkeywordhash tool as well.
+
+# These are the required SQLite compilation options used when compiling for
+# the Windows platform.
+#
+REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100
+
+# If we are linking to the RPCRT4 library, enable features that need it.
+#
+!IF $(USE_RPCRT4_LIB)!=0
+REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS) -DSQLITE_WIN32_USE_UUID=1
+!ENDIF
+
+# Add the required and optional SQLite compilation options into the command
+# lines used to invoke the MSVC code and resource compilers.
+#
+TCC = $(TCC) $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS)
+RCC = $(RCC) $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS)
+
+# Add in any optional parameters specified on the commane line, e.g.
+# nmake /f Makefile.msc all "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1"
+#
+TCC = $(TCC) $(OPTS)
+RCC = $(RCC) $(OPTS)
+
+# If compiling for debugging, add some defines.
+#
+!IF $(DEBUG)>1
+TCC = $(TCC) -D_DEBUG
+BCC = $(BCC) -D_DEBUG
+RCC = $(RCC) -D_DEBUG
+!ENDIF
+
+# If optimizations are enabled or disabled (either implicitly or
+# explicitly), add the necessary flags.
+#
+!IF $(DEBUG)>1 || $(OPTIMIZATIONS)==0
+TCC = $(TCC) -Od
+BCC = $(BCC) -Od
+!ELSEIF $(OPTIMIZATIONS)>=3
+TCC = $(TCC) -Ox
+BCC = $(BCC) -Ox
+!ELSEIF $(OPTIMIZATIONS)==2
+TCC = $(TCC) -O2
+BCC = $(BCC) -O2
+!ELSEIF $(OPTIMIZATIONS)==1
+TCC = $(TCC) -O1
+BCC = $(BCC) -O1
+!ENDIF
+
+# If symbols are enabled (or compiling for debugging), enable PDBs.
+#
+!IF $(DEBUG)>1 || $(SYMBOLS)!=0
+TCC = $(TCC) -Zi
+BCC = $(BCC) -Zi
+!ENDIF
+
+
+# Command line prefixes for compiling code, compiling resources,
+# linking, etc.
+#
+LTCOMPILE = $(TCC) -Fo$@
+LTRCOMPILE = $(RCC) -r
+LTLIB = lib.exe
+LTLINK = $(TCC) -Fe$@
+
+# If requested, link to the RPCRT4 library.
+#
+!IF $(USE_RPCRT4_LIB)!=0
+LTLINK = $(LTLINK) rpcrt4.lib
+!ENDIF
+
+# If a platform was set, force the linker to target that.
+# Note that the vcvars*.bat family of batch files typically
+# set this for you. Otherwise, the linker will attempt
+# to deduce the binary type based on the object files.
+!IFDEF PLATFORM
+LTLINKOPTS = /NOLOGO /MACHINE:$(PLATFORM)
+LTLIBOPTS = /NOLOGO /MACHINE:$(PLATFORM)
+!ELSE
+LTLINKOPTS = /NOLOGO
+LTLIBOPTS = /NOLOGO
+!ENDIF
+
+# When compiling for use in the WinRT environment, the following
+# linker option must be used to mark the executable as runnable
+# only in the context of an application container.
+#
+!IF $(FOR_WINRT)!=0
+LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
+!IF "$(VISUALSTUDIOVERSION)"=="12.0" || "$(VISUALSTUDIOVERSION)"=="14.0"
+!IFNDEF STORELIBPATH
+!IF "$(PLATFORM)"=="x86"
+STORELIBPATH = $(CRTLIBPATH)\store
+!ELSEIF "$(PLATFORM)"=="x64"
+STORELIBPATH = $(CRTLIBPATH)\store\amd64
+!ELSEIF "$(PLATFORM)"=="ARM"
+STORELIBPATH = $(CRTLIBPATH)\store\arm
+!ELSE
+STORELIBPATH = $(CRTLIBPATH)\store
+!ENDIF
+!ENDIF
+STORELIBPATH = $(STORELIBPATH:\\=\)
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
+!ENDIF
+!ENDIF
+
+# When compiling for Windows Phone 8.1, an extra library path is
+# required.
+#
+!IF $(USE_WP81_OPTS)!=0
+!IFNDEF WP81LIBPATH
+!IF "$(PLATFORM)"=="x86"
+WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
+!ELSEIF "$(PLATFORM)"=="ARM"
+WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
+!ELSE
+WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
+!ENDIF
+!ENDIF
+!ENDIF
+
+# When compiling for Windows Phone 8.1, some extra linker options
+# are also required.
+#
+!IF $(USE_WP81_OPTS)!=0
+!IFDEF WP81LIBPATH
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
+!ENDIF
+LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
+LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
+LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
+!ENDIF
+
+# When compiling for UAP or the Windows 10 platform, some extra linker
+# options are also required.
+#
+!IF $(FOR_UAP)!=0 || $(FOR_WIN10)!=0
+LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE /NODEFAULTLIB:kernel32.lib
+LTLINKOPTS = $(LTLINKOPTS) mincore.lib
+!IFDEF PSDKLIBPATH
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(PSDKLIBPATH)"
+!ENDIF
+!ENDIF
+
+!IF $(FOR_WIN10)!=0
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(UCRTLIBPATH)"
+!IF $(DEBUG)>1
+LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrtd.lib /DEFAULTLIB:ucrtd.lib
+!ELSE
+LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
+!ENDIF
+!ENDIF
+
+# If either debugging or symbols are enabled, enable PDBs.
+#
+!IF $(DEBUG)>1 || $(SYMBOLS)!=0
+LDFLAGS = /DEBUG $(LDOPTS)
+!ELSE
+LDFLAGS = $(LDOPTS)
+!ENDIF
+
+
+# You should not have to change anything below this line
+###############################################################################
+
+
+# Object files for the amalgamation.
+#
+LIBOBJS1 = sqlite3.lo
+
+# Determine the real value of LIBOBJ based on the 'configure' script
+#
+LIBOBJ = $(LIBOBJS1)
+
+# Determine if embedded resource compilation and usage are enabled.
+#
+!IF $(USE_RC)!=0
+LIBRESOBJS = sqlite3res.lo
+!ELSE
+LIBRESOBJS =
+!ENDIF
+
+
+# Additional compiler options for the shell. These are only effective
+# when the shell is not being dynamically linked.
+#
+!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
+SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5
+!ENDIF
+
+
+# This is the default Makefile target. The objects listed here
+# are what get build when you type just "make" with no arguments.
+#
+all: dll libsqlite3.lib shell
+
+# Dynamic link library section.
+#
+dll: $(SQLITE3DLL)
+
+# Shell executable.
+#
+shell: $(SQLITE3EXE)
+
+libsqlite3.lib: $(LIBOBJ)
+ $(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS)
+
+
+$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
+ $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+
+
+$(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
+ $(LTLINK) $(SHELL_COMPILE_OPTS) $(READLINE_FLAGS) $(TOP)\shell.c $(SHELL_CORE_SRC) \
+ /link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
+
+
+# Rule to build the amalgamation
+#
+sqlite3.lo: $(SQLITE3C)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(SQLITE3C)
+
+
+# Rule to build the Win32 resources object file.
+#
+!IF $(USE_RC)!=0
+_HASHCHAR=^#
+!IF ![echo !IFNDEF VERSION > rcver.vc] && \
+ ![for /F "delims=" %V in ('type "$(SQLITE3H)" ^| find "$(_HASHCHAR)define SQLITE_VERSION "') do (echo VERSION = ^^%V >> rcver.vc)] && \
+ ![echo !ENDIF >> rcver.vc]
+!INCLUDE rcver.vc
+!ENDIF
+
+RESOURCE_VERSION = $(VERSION:^#=)
+RESOURCE_VERSION = $(RESOURCE_VERSION:define=)
+RESOURCE_VERSION = $(RESOURCE_VERSION:SQLITE_VERSION=)
+RESOURCE_VERSION = $(RESOURCE_VERSION:"=)
+RESOURCE_VERSION = $(RESOURCE_VERSION:.=,)
+
+$(LIBRESOBJS): $(TOP)\sqlite3.rc rcver.vc $(SQLITE3H)
+ echo #ifndef SQLITE_RESOURCE_VERSION > sqlite3rc.h
+ echo #define SQLITE_RESOURCE_VERSION $(RESOURCE_VERSION) >> sqlite3rc.h
+ echo #endif >> sqlite3rc.h
+ $(LTRCOMPILE) -fo $(LIBRESOBJS) -DRC_VERONLY $(TOP)\sqlite3.rc
+!ENDIF
+
+
+clean:
+ del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL
+ del /Q *.bsc *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL
diff --git a/autoconf/README b/autoconf/README
deleted file mode 100644
index dd8cca24bd..0000000000
--- a/autoconf/README
+++ /dev/null
@@ -1,32 +0,0 @@
-
-This package contains:
-
- * the SQLite library amalgamation (single file) source code distribution,
- * the shell.c file used to build the sqlite3 shell too, and
- * the sqlite3.h and sqlite3ext.h header files required to link programs
- and sqlite extensions against the installed libary.
- * autoconf/automake installation infrastucture.
-
-The generic installation instructions for autoconf/automake are found
-in the INSTALL file.
-
-The following SQLite specific boolean options are supported:
-
- --enable-readline use readline in shell tool [default=yes]
- --enable-threadsafe build a thread-safe library [default=yes]
- --enable-dynamic-extensions support loadable extensions [default=yes]
-
-The default value for the CFLAGS variable (options passed to the C
-compiler) includes debugging symbols in the build, resulting in larger
-binaries than are necessary. Override it on the configure command
-line like this:
-
- $ CFLAGS="-Os" ./configure
-
-to produce a smaller installation footprint.
-
-Other SQLite compilation parameters can also be set using CFLAGS. For
-example:
-
- $ CFLAGS="-Os -DSQLITE_OMIT_TRIGGERS" ./configure
-
diff --git a/autoconf/README.txt b/autoconf/README.txt
new file mode 100644
index 0000000000..c27c08d0df
--- /dev/null
+++ b/autoconf/README.txt
@@ -0,0 +1,113 @@
+This package contains:
+
+ * the SQLite library amalgamation (single file) source code distribution,
+ * the shell.c file used to build the sqlite3 shell too, and
+ * the sqlite3.h and sqlite3ext.h header files required to link programs
+ and sqlite extensions against the installed libary.
+ * autoconf/automake installation infrastucture for building on POSIX
+ compliant systems.
+ * a Makefile.msc and sqlite3.rc for building with Microsoft Visual C++ on
+ Windows.
+
+SUMMARY OF HOW TO BUILD
+=======================
+
+ Unix: ./configure; make
+ Windows: nmake /f Makefile.msc
+
+BUILDING ON POSIX
+=================
+
+The generic installation instructions for autoconf/automake are found
+in the INSTALL file.
+
+The following SQLite specific boolean options are supported:
+
+ --enable-readline use readline in shell tool [default=yes]
+ --enable-threadsafe build a thread-safe library [default=yes]
+ --enable-dynamic-extensions support loadable extensions [default=yes]
+
+The default value for the CFLAGS variable (options passed to the C
+compiler) includes debugging symbols in the build, resulting in larger
+binaries than are necessary. Override it on the configure command
+line like this:
+
+ $ CFLAGS="-Os" ./configure
+
+to produce a smaller installation footprint.
+
+Other SQLite compilation parameters can also be set using CFLAGS. For
+example:
+
+ $ CFLAGS="-Os -DSQLITE_OMIT_TRIGGERS" ./configure
+
+
+BUILDING WITH MICROSOFT VISUAL C++
+==================================
+
+To compile for Windows using Microsoft Visual C++:
+
+ $ nmake /f Makefile.msc
+
+Using Microsoft Visual C++ 2005 (or later) is recommended. Several Windows
+platform variants may be built by adding additional macros to the NMAKE
+command line.
+
+Building for WinRT 8.0
+----------------------
+
+ FOR_WINRT=1
+
+Using Microsoft Visual C++ 2012 (or later) is required. When using the
+above, something like the following macro will need to be added to the
+NMAKE command line as well:
+
+ "NSDKLIBPATH=%WindowsSdkDir%\..\8.0\lib\win8\um\x86"
+
+Building for WinRT 8.1
+----------------------
+
+ FOR_WINRT=1
+
+Using Microsoft Visual C++ 2013 (or later) is required. When using the
+above, something like the following macro will need to be added to the
+NMAKE command line as well:
+
+ "NSDKLIBPATH=%WindowsSdkDir%\..\8.1\lib\winv6.3\um\x86"
+
+Building for UAP 10.0
+---------------------
+
+ FOR_WINRT=1 FOR_UAP=1
+
+Using Microsoft Visual C++ 2015 (or later) is required. When using the
+above, something like the following macros will need to be added to the
+NMAKE command line as well:
+
+ "NSDKLIBPATH=%WindowsSdkDir%\..\10\lib\10.0.10586.0\um\x86"
+ "PSDKLIBPATH=%WindowsSdkDir%\..\10\lib\10.0.10586.0\um\x86"
+ "NUCRTLIBPATH=%UniversalCRTSdkDir%\..\10\lib\10.0.10586.0\ucrt\x86"
+
+Building for the Windows 10 SDK
+-------------------------------
+
+ FOR_WIN10=1
+
+Using Microsoft Visual C++ 2015 (or later) is required. When using the
+above, no other macros should be needed on the NMAKE command line.
+
+Other preprocessor defines
+--------------------------
+
+Additionally, preprocessor defines may be specified by using the OPTS macro
+on the NMAKE command line. However, not all possible preprocessor defines
+may be specified in this manner as some require the amalgamation to be built
+with them enabled (see http://www.sqlite.org/compile.html). For example, the
+following will work:
+
+ "OPTS=-DSQLITE_ENABLE_STAT4=1 -DSQLITE_ENABLE_JSON1=1"
+
+However, the following will not compile unless the amalgamation was built
+with it enabled:
+
+ "OPTS=-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1"
diff --git a/autoconf/configure.ac b/autoconf/configure.ac
index fcca148aea..9492530680 100644
--- a/autoconf/configure.ac
+++ b/autoconf/configure.ac
@@ -75,6 +75,7 @@ AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
THREADSAFE_FLAGS=-DSQLITE_THREADSAFE=0
if test x"$enable_threadsafe" != "xno"; then
THREADSAFE_FLAGS="-D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
+ AC_SEARCH_LIBS(pthread_create, pthread)
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
fi
AC_SUBST(THREADSAFE_FLAGS)
diff --git a/configure b/configure
index 951b719ed5..b2cb4d1308 100755
--- a/configure
+++ b/configure
@@ -10464,6 +10464,62 @@ fi
if test "$SQLITE_THREADSAFE" = "1"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5
+$as_echo_n "checking for library containing pthread_create... " >&6; }
+if ${ac_cv_search_pthread_create+:} 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 pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' pthread; 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_pthread_create=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_pthread_create+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_pthread_create+:} false; then :
+
+else
+ ac_cv_search_pthread_create=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5
+$as_echo "$ac_cv_search_pthread_create" >&6; }
+ac_res=$ac_cv_search_pthread_create
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_mutexattr_init" >&5
$as_echo_n "checking for library containing pthread_mutexattr_init... " >&6; }
if ${ac_cv_search_pthread_mutexattr_init+:} false; then :
diff --git a/configure.ac b/configure.ac
index 26a60147dd..19f85dc875 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,7 @@ fi
AC_SUBST(SQLITE_THREADSAFE)
if test "$SQLITE_THREADSAFE" = "1"; then
+ AC_SEARCH_LIBS(pthread_create, pthread)
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
fi
diff --git a/ext/fts3/fts3_tokenizer.c b/ext/fts3/fts3_tokenizer.c
index 64cfe07aac..fcabe5cca2 100644
--- a/ext/fts3/fts3_tokenizer.c
+++ b/ext/fts3/fts3_tokenizer.c
@@ -67,6 +67,7 @@ static void scalarFunc(
nName = sqlite3_value_bytes(argv[0])+1;
if( argc==2 ){
+#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
void *pOld;
int n = sqlite3_value_bytes(argv[1]);
if( zName==0 || n!=sizeof(pPtr) ){
@@ -79,7 +80,14 @@ static void scalarFunc(
sqlite3_result_error(context, "out of memory", -1);
return;
}
- }else{
+#else
+ sqlite3_result_error(context, "fts3tokenize: "
+ "disabled - rebuild with -DSQLITE_ENABLE_FTS3_TOKENIZER", -1
+ );
+ return;
+#endif /* SQLITE_ENABLE_FTS3_TOKENIZER */
+ }else
+ {
if( zName ){
pPtr = sqlite3Fts3HashFind(pHash, zName, nName);
}
@@ -328,6 +336,7 @@ finish:
Tcl_DecrRefCount(pRet);
}
+#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
static
int registerTokenizer(
sqlite3 *db,
@@ -349,6 +358,8 @@ int registerTokenizer(
return sqlite3_finalize(pStmt);
}
+#endif /* SQLITE_ENABLE_FTS3_TOKENIZER */
+
static
int queryTokenizer(
@@ -420,11 +431,13 @@ static void intTestFunc(
assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") );
/* Test the storage function */
+#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
rc = registerTokenizer(db, "nosuchtokenizer", p1);
assert( rc==SQLITE_OK );
rc = queryTokenizer(db, "nosuchtokenizer", &p2);
assert( rc==SQLITE_OK );
assert( p2==p1 );
+#endif
sqlite3_result_text(context, "ok", -1, SQLITE_STATIC);
}
diff --git a/ext/fts5/fts5Int.h b/ext/fts5/fts5Int.h
index b2f0d6c34e..6318af900f 100644
--- a/ext/fts5/fts5Int.h
+++ b/ext/fts5/fts5Int.h
@@ -29,7 +29,7 @@ typedef unsigned short u16;
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
-#define ArraySize(x) (sizeof(x) / sizeof(x[0]))
+#define ArraySize(x) ((int)(sizeof(x) / sizeof(x[0])))
#define testcase(x)
#define ALWAYS(x) 1
@@ -225,8 +225,8 @@ int sqlite3Fts5ConfigParseRank(const char*, char**, char**);
typedef struct Fts5Buffer Fts5Buffer;
struct Fts5Buffer {
u8 *p;
- u32 n;
- u32 nSpace;
+ int n;
+ int nSpace;
};
int sqlite3Fts5BufferSize(int*, Fts5Buffer*, u32);
@@ -247,7 +247,7 @@ char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d)
#define fts5BufferGrow(pRc,pBuf,nn) ( \
- (pBuf)->n + (nn) <= (pBuf)->nSpace ? 0 : \
+ (u32)((pBuf)->n) + (u32)(nn) <= (u32)((pBuf)->nSpace) ? 0 : \
sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) \
)
@@ -282,6 +282,7 @@ struct Fts5PoslistWriter {
i64 iPrev;
};
int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
+void sqlite3Fts5PoslistSafeAppend(Fts5Buffer*, i64*, i64);
int sqlite3Fts5PoslistNext64(
const u8 *a, int n, /* Buffer containing poslist */
@@ -315,6 +316,15 @@ void sqlite3Fts5TermsetFree(Fts5Termset*);
typedef struct Fts5Index Fts5Index;
typedef struct Fts5IndexIter Fts5IndexIter;
+struct Fts5IndexIter {
+ i64 iRowid;
+ const u8 *pData;
+ int nData;
+ u8 bEof;
+};
+
+#define sqlite3Fts5IterEof(x) ((x)->bEof)
+
/*
** Values used as part of the flags argument passed to IndexQuery().
*/
@@ -323,6 +333,12 @@ typedef struct Fts5IndexIter Fts5IndexIter;
#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
+/* The following are used internally by the fts5_index.c module. They are
+** defined here only to make it easier to avoid clashes with the flags
+** above. */
+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+
/*
** Create/destroy an Fts5Index object.
*/
@@ -378,12 +394,9 @@ int sqlite3Fts5IndexQuery(
** The various operations on open token or token prefix iterators opened
** using sqlite3Fts5IndexQuery().
*/
-int sqlite3Fts5IterEof(Fts5IndexIter*);
int sqlite3Fts5IterNext(Fts5IndexIter*);
int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
i64 sqlite3Fts5IterRowid(Fts5IndexIter*);
-int sqlite3Fts5IterPoslist(Fts5IndexIter*,Fts5Colset*, const u8**, int*, i64*);
-int sqlite3Fts5IterPoslistBuffer(Fts5IndexIter *pIter, Fts5Buffer *pBuf);
/*
** Close an iterator opened by sqlite3Fts5IndexQuery().
@@ -469,8 +482,6 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
-int sqlite3Fts5IterCollist(Fts5IndexIter*, const u8 **, int*);
-
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
diff --git a/ext/fts5/fts5_aux.c b/ext/fts5/fts5_aux.c
index 011064d405..79bed1c8f7 100644
--- a/ext/fts5/fts5_aux.c
+++ b/ext/fts5/fts5_aux.c
@@ -544,7 +544,7 @@ int sqlite3Fts5AuxInit(fts5_api *pApi){
int rc = SQLITE_OK; /* Return code */
int i; /* To iterate through builtin functions */
- for(i=0; rc==SQLITE_OK && i<(int)ArraySize(aBuiltin); i++){
+ for(i=0; rc==SQLITE_OK && ixCreateFunction(pApi,
aBuiltin[i].zFunc,
aBuiltin[i].pUserData,
diff --git a/ext/fts5/fts5_buffer.c b/ext/fts5/fts5_buffer.c
index 94591188d3..c552e860eb 100644
--- a/ext/fts5/fts5_buffer.c
+++ b/ext/fts5/fts5_buffer.c
@@ -16,18 +16,20 @@
#include "fts5Int.h"
int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){
- u32 nNew = pBuf->nSpace ? pBuf->nSpace*2 : 64;
- u8 *pNew;
- while( nNewp, nNew);
- if( pNew==0 ){
- *pRc = SQLITE_NOMEM;
- return 1;
- }else{
- pBuf->nSpace = nNew;
- pBuf->p = pNew;
+ if( (u32)pBuf->nSpacenSpace ? pBuf->nSpace : 64;
+ u8 *pNew;
+ while( nNewp, nNew);
+ if( pNew==0 ){
+ *pRc = SQLITE_NOMEM;
+ return 1;
+ }else{
+ pBuf->nSpace = nNew;
+ pBuf->p = pNew;
+ }
}
return 0;
}
@@ -208,23 +210,36 @@ int sqlite3Fts5PoslistReaderInit(
return pIter->bEof;
}
+/*
+** Append position iPos to the position list being accumulated in buffer
+** pBuf, which must be already be large enough to hold the new data.
+** The previous position written to this list is *piPrev. *piPrev is set
+** to iPos before returning.
+*/
+void sqlite3Fts5PoslistSafeAppend(
+ Fts5Buffer *pBuf,
+ i64 *piPrev,
+ i64 iPos
+){
+ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
+ if( (iPos & colmask) != (*piPrev & colmask) ){
+ pBuf->p[pBuf->n++] = 1;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32));
+ *piPrev = (iPos & colmask);
+ }
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2);
+ *piPrev = iPos;
+}
+
int sqlite3Fts5PoslistWriterAppend(
Fts5Buffer *pBuf,
Fts5PoslistWriter *pWriter,
i64 iPos
){
- static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
- int rc = SQLITE_OK;
- if( 0==fts5BufferGrow(&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;
- }
- return rc;
+ int rc;
+ if( fts5BufferGrow(&rc, pBuf, 5+5+5) ) return rc;
+ sqlite3Fts5PoslistSafeAppend(pBuf, &pWriter->iPrev, iPos);
+ return SQLITE_OK;
}
void *sqlite3Fts5MallocZero(int *pRc, int nByte){
@@ -322,7 +337,7 @@ int sqlite3Fts5TermsetAdd(
*pbPresent = 0;
if( p ){
int i;
- int hash = 13;
+ u32 hash = 13;
Fts5TermsetEntry *pEntry;
/* Calculate a hash value for this term. This is the same hash checksum
@@ -339,7 +354,7 @@ int sqlite3Fts5TermsetAdd(
if( pEntry->iIdx==iIdx
&& pEntry->nTerm==nTerm
&& memcmp(pEntry->pTerm, pTerm, nTerm)==0
- ){
+ ){
*pbPresent = 1;
break;
}
@@ -363,7 +378,7 @@ int sqlite3Fts5TermsetAdd(
void sqlite3Fts5TermsetFree(Fts5Termset *p){
if( p ){
- int i;
+ u32 i;
for(i=0; iapHash); i++){
Fts5TermsetEntry *pEntry = p->apHash[i];
while( pEntry ){
diff --git a/ext/fts5/fts5_expr.c b/ext/fts5/fts5_expr.c
index c51ed7939c..5006d88d0f 100644
--- a/ext/fts5/fts5_expr.c
+++ b/ext/fts5/fts5_expr.c
@@ -62,6 +62,9 @@ struct Fts5ExprNode {
int bEof; /* True at EOF */
int bNomatch; /* True if entry is not a match */
+ /* Next method for this node. */
+ int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
+
i64 iRowid; /* Current rowid */
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
@@ -73,6 +76,12 @@ struct Fts5ExprNode {
#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
+/*
+** Invoke the xNext method of an Fts5ExprNode object. This macro should be
+** used as if it has the same signature as the xNext() methods themselves.
+*/
+#define fts5ExprNodeNext(a,b,c,d) (b)->xNext((a), (b), (c), (d))
+
/*
** An instance of the following structure represents a single search term
** or term prefix.
@@ -234,7 +243,15 @@ int sqlite3Fts5ExprNew(
sParse.rc = SQLITE_NOMEM;
sqlite3Fts5ParseNodeFree(sParse.pExpr);
}else{
- pNew->pRoot = sParse.pExpr;
+ if( !sParse.pExpr ){
+ const int nByte = sizeof(Fts5ExprNode);
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte);
+ if( pNew->pRoot ){
+ pNew->pRoot->bEof = 1;
+ }
+ }else{
+ pNew->pRoot = sParse.pExpr;
+ }
pNew->pIndex = 0;
pNew->pConfig = pConfig;
pNew->apExprPhrase = sParse.apPhrase;
@@ -286,7 +303,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
assert( bDesc==0 || bDesc==1 );
for(p=pTerm; p; p=p->pSynonym){
if( 0==sqlite3Fts5IterEof(p->pIter) ){
- i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
+ i64 iRowid = p->pIter->iRowid;
if( bRetValid==0 || (bDesc!=(iRowidpSynonym );
for(p=pTerm; p; p=p->pSynonym){
Fts5IndexIter *pIter = p->pIter;
- if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){
- const u8 *a;
- int n;
-
- if( bCollist ){
- rc = sqlite3Fts5IterCollist(pIter, &a, &n);
- }else{
- i64 dummy;
- rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy);
- }
-
- if( rc!=SQLITE_OK ) goto synonym_poslist_out;
- if( n==0 ) continue;
+ if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){
+ if( pIter->nData==0 ) continue;
if( nIter==nAlloc ){
int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
@@ -344,20 +350,19 @@ static int fts5ExprSynonymList(
if( aIter!=aStatic ) sqlite3_free(aIter);
aIter = aNew;
}
- sqlite3Fts5PoslistReaderInit(a, n, &aIter[nIter]);
+ sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &aIter[nIter]);
assert( aIter[nIter].bEof==0 );
nIter++;
}
}
- assert( *pbDel==0 );
if( nIter==1 ){
*pa = (u8*)aIter[0].a;
*pn = aIter[0].n;
}else{
Fts5PoslistWriter writer = {0};
- Fts5Buffer buf = {0,0,0};
i64 iPrev = -1;
+ fts5BufferZero(pBuf);
while( 1 ){
int i;
i64 iMin = FTS5_LARGEST_INT64;
@@ -372,15 +377,12 @@ static int fts5ExprSynonymList(
}
}
if( iMin==FTS5_LARGEST_INT64 || rc!=SQLITE_OK ) break;
- rc = sqlite3Fts5PoslistWriterAppend(&buf, &writer, iMin);
+ rc = sqlite3Fts5PoslistWriterAppend(pBuf, &writer, iMin);
iPrev = iMin;
}
- if( rc ){
- sqlite3_free(buf.p);
- }else{
- *pa = buf.p;
- *pn = buf.n;
- *pbDel = 1;
+ if( rc==SQLITE_OK ){
+ *pa = pBuf->p;
+ *pn = pBuf->n;
}
}
@@ -417,7 +419,7 @@ static int fts5ExprPhraseIsMatch(
/* If the aStatic[] array is not large enough, allocate a large array
** using sqlite3_malloc(). This approach could be improved upon. */
- if( pPhrase->nTerm>(int)ArraySize(aStatic) ){
+ if( pPhrase->nTerm>ArraySize(aStatic) ){
int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
if( !aIter ) return SQLITE_NOMEM;
@@ -427,18 +429,23 @@ static int fts5ExprPhraseIsMatch(
/* Initialize a term iterator for each term in the phrase */
for(i=0; inTerm; i++){
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
- i64 dummy;
int n = 0;
int bFlag = 0;
- const u8 *a = 0;
+ u8 *a = 0;
if( pTerm->pSynonym ){
+ Fts5Buffer buf = {0, 0, 0};
rc = fts5ExprSynonymList(
- pTerm, 0, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n
+ pTerm, 0, pColset, pNode->iRowid, &buf, &a, &n
);
+ if( rc ){
+ sqlite3_free(a);
+ goto ismatch_out;
+ }
+ if( a==buf.p ) bFlag = 1;
}else{
- rc = sqlite3Fts5IterPoslist(pTerm->pIter, pColset, &a, &n, &dummy);
+ a = (u8*)pTerm->pIter->pData;
+ n = pTerm->pIter->nData;
}
- if( rc!=SQLITE_OK ) goto ismatch_out;
sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
aIter[i].bFlag = (u8)bFlag;
if( aIter[i].bEof ) goto ismatch_out;
@@ -510,12 +517,6 @@ static int fts5LookaheadReaderInit(
return fts5LookaheadReaderNext(p);
}
-#if 0
-static int fts5LookaheadReaderEof(Fts5LookaheadReader *p){
- return (p->iPos==FTS5_LOOKAHEAD_EOF);
-}
-#endif
-
typedef struct Fts5NearTrimmer Fts5NearTrimmer;
struct Fts5NearTrimmer {
Fts5LookaheadReader reader; /* Input iterator */
@@ -553,7 +554,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
/* If the aStatic[] array is not large enough, allocate a large array
** using sqlite3_malloc(). This approach could be improved upon. */
- if( pNear->nPhrase>(int)ArraySize(aStatic) ){
+ if( pNear->nPhrase>ArraySize(aStatic) ){
int nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte);
}else{
@@ -630,71 +631,6 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
}
}
-/*
-** Advance the first term iterator in the first phrase of pNear. Set output
-** variable *pbEof to true if it reaches EOF or if an error occurs.
-**
-** Return SQLITE_OK if successful, or an SQLite error code if an error
-** occurs.
-*/
-static int fts5ExprNearAdvanceFirst(
- Fts5Expr *pExpr, /* Expression pPhrase belongs to */
- Fts5ExprNode *pNode, /* FTS5_STRING or FTS5_TERM node */
- int bFromValid,
- i64 iFrom
-){
- Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
- int rc = SQLITE_OK;
-
- if( pTerm->pSynonym ){
- int bEof = 1;
- Fts5ExprTerm *p;
-
- /* Find the firstest rowid any synonym points to. */
- i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
-
- /* Advance each iterator that currently points to iRowid. Or, if iFrom
- ** is valid - each iterator that points to a rowid before iFrom. */
- for(p=pTerm; p; p=p->pSynonym){
- if( sqlite3Fts5IterEof(p->pIter)==0 ){
- i64 ii = sqlite3Fts5IterRowid(p->pIter);
- if( ii==iRowid
- || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc)
- ){
- if( bFromValid ){
- rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
- }else{
- rc = sqlite3Fts5IterNext(p->pIter);
- }
- if( rc!=SQLITE_OK ) break;
- if( sqlite3Fts5IterEof(p->pIter)==0 ){
- bEof = 0;
- }
- }else{
- bEof = 0;
- }
- }
- }
-
- /* Set the EOF flag if either all synonym iterators are at EOF or an
- ** error has occurred. */
- pNode->bEof = (rc || bEof);
- }else{
- Fts5IndexIter *pIter = pTerm->pIter;
-
- assert( Fts5NodeIsString(pNode) );
- if( bFromValid ){
- rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
- }else{
- rc = sqlite3Fts5IterNext(pIter);
- }
-
- pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
- }
-
- return rc;
-}
-
/*
** Advance iterator pIter until it points to a value equal to or laster
** than the initial value of *piLast. If this means the iterator points
@@ -714,7 +650,7 @@ static int fts5ExprAdvanceto(
i64 iLast = *piLast;
i64 iRowid;
- iRowid = sqlite3Fts5IterRowid(pIter);
+ iRowid = pIter->iRowid;
if( (bDesc==0 && iLast>iRowid) || (bDesc && iLastiRowid;
assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) );
}
*piLast = iRowid;
@@ -743,7 +679,7 @@ static int fts5ExprSynonymAdvanceto(
for(p=pTerm; rc==SQLITE_OK && p; p=p->pSynonym){
if( sqlite3Fts5IterEof(p->pIter)==0 ){
- i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
+ i64 iRowid = p->pIter->iRowid;
if( (bDesc==0 && iLast>iRowid) || (bDesc && iLastpIter, iLast);
}
@@ -775,13 +711,7 @@ static int fts5ExprNearTest(
for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
Fts5IndexIter *pIter = pTerm->pIter;
if( sqlite3Fts5IterEof(pIter)==0 ){
- int n;
- i64 iRowid;
- rc = sqlite3Fts5IterPoslist(pIter, pNear->pColset, 0, &n, &iRowid);
- if( rc!=SQLITE_OK ){
- *pRc = rc;
- return 0;
- }else if( iRowid==pNode->iRowid && n>0 ){
+ if( pIter->iRowid==pNode->iRowid && pIter->nData>0 ){
pPhrase->poslist.n = 1;
}
}
@@ -800,9 +730,8 @@ static int fts5ExprNearTest(
rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch);
if( bMatch==0 ) break;
}else{
- rc = sqlite3Fts5IterPoslistBuffer(
- pPhrase->aTerm[0].pIter, &pPhrase->poslist
- );
+ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
+ fts5BufferSet(&rc, &pPhrase->poslist, pIter->nData, pIter->pData);
}
}
@@ -814,103 +743,6 @@ static int fts5ExprNearTest(
}
}
-static int fts5ExprTokenTest(
- Fts5Expr *pExpr, /* Expression that pNear is a part of */
- Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */
-){
- /* As this "NEAR" object is actually a single phrase that consists
- ** of a single term only, grab pointers into the poslist managed by the
- ** fts5_index.c iterator object. This is much faster than synthesizing
- ** a new poslist the way we have to for more complicated phrase or NEAR
- ** expressions. */
- Fts5ExprNearset *pNear = pNode->pNear;
- Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
- Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
- 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, pColset,
- (const u8**)&pPhrase->poslist.p, (int*)&pPhrase->poslist.n, &pNode->iRowid
- );
- pNode->bNomatch = (pPhrase->poslist.n==0);
- return rc;
-}
-
-/*
-** All individual term iterators in pNear are guaranteed to be valid when
-** this function is called. This function checks if all term iterators
-** point to the same rowid, and if not, advances them until they do.
-** If an EOF is reached before this happens, *pbEof is set to true before
-** returning.
-**
-** SQLITE_OK is returned if an error occurs, or an SQLite error code
-** otherwise. It is not considered an error code if an iterator reaches
-** EOF.
-*/
-static int fts5ExprNearNextMatch(
- Fts5Expr *pExpr, /* Expression pPhrase belongs to */
- Fts5ExprNode *pNode
-){
- Fts5ExprNearset *pNear = pNode->pNear;
- Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
- int rc = SQLITE_OK;
- i64 iLast; /* Lastest rowid any iterator points to */
- int i, j; /* Phrase and token index, respectively */
- int bMatch; /* True if all terms are at the same rowid */
- const int bDesc = pExpr->bDesc;
-
- /* Check that this node should not be FTS5_TERM */
- assert( pNear->nPhrase>1
- || pNear->apPhrase[0]->nTerm>1
- || pNear->apPhrase[0]->aTerm[0].pSynonym
- );
-
- /* Initialize iLast, the "lastest" rowid any iterator points to. If the
- ** iterator skips through rowids in the default ascending order, this means
- ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
- ** means the minimum rowid. */
- if( pLeft->aTerm[0].pSynonym ){
- iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
- }else{
- iLast = sqlite3Fts5IterRowid(pLeft->aTerm[0].pIter);
- }
-
- do {
- bMatch = 1;
- for(i=0; inPhrase; i++){
- Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
- for(j=0; jnTerm; j++){
- Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
- if( pTerm->pSynonym ){
- i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
- if( iRowid==iLast ) continue;
- bMatch = 0;
- if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
- pNode->bEof = 1;
- return rc;
- }
- }else{
- Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
- i64 iRowid = sqlite3Fts5IterRowid(pIter);
- if( iRowid==iLast ) continue;
- bMatch = 0;
- if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
- return rc;
- }
- }
- }
- }
- }while( bMatch==0 );
-
- pNode->iRowid = iLast;
- pNode->bNomatch = (0==fts5ExprNearTest(&rc, pExpr, pNode));
-
- return rc;
-}
/*
** Initialize all term iterators in the pNear object. If any term is found
@@ -925,6 +757,7 @@ static int fts5ExprNearInitAll(
int i, j;
int rc = SQLITE_OK;
+ assert( pNode->bNomatch==0 );
for(i=0; rc==SQLITE_OK && inPhrase; i++){
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
for(j=0; jnTerm; j++){
@@ -960,10 +793,6 @@ static int fts5ExprNearInitAll(
return rc;
}
-/* fts5ExprNodeNext() calls fts5ExprNodeNextMatch(). And vice-versa. */
-static int fts5ExprNodeNextMatch(Fts5Expr*, Fts5ExprNode*);
-
-
/*
** If pExpr is an ASC iterator, this function returns a value with the
** same sign as:
@@ -992,6 +821,7 @@ static int fts5RowidCmp(
static void fts5ExprSetEof(Fts5ExprNode *pNode){
int i;
pNode->bEof = 1;
+ pNode->bNomatch = 0;
for(i=0; inChild; i++){
fts5ExprSetEof(pNode->apChild[i]);
}
@@ -1014,12 +844,275 @@ static void fts5ExprNodeZeroPoslist(Fts5ExprNode *pNode){
}
-static int fts5ExprNodeNext(Fts5Expr*, Fts5ExprNode*, int, i64);
+
+/*
+** Compare the values currently indicated by the two nodes as follows:
+**
+** res = (*p1) - (*p2)
+**
+** Nodes that point to values that come later in the iteration order are
+** considered to be larger. Nodes at EOF are the largest of all.
+**
+** This means that if the iteration order is ASC, then numerically larger
+** rowids are considered larger. Or if it is the default DESC, numerically
+** smaller rowids are larger.
+*/
+static int fts5NodeCompare(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *p1,
+ Fts5ExprNode *p2
+){
+ if( p2->bEof ) return -1;
+ if( p1->bEof ) return +1;
+ return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid);
+}
+
+/*
+** All individual term iterators in pNear are guaranteed to be valid when
+** this function is called. This function checks if all term iterators
+** point to the same rowid, and if not, advances them until they do.
+** If an EOF is reached before this happens, *pbEof is set to true before
+** returning.
+**
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** otherwise. It is not considered an error code if an iterator reaches
+** EOF.
+*/
+static int fts5ExprNodeTest_STRING(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
+ int rc = SQLITE_OK;
+ i64 iLast; /* Lastest rowid any iterator points to */
+ int i, j; /* Phrase and token index, respectively */
+ int bMatch; /* True if all terms are at the same rowid */
+ const int bDesc = pExpr->bDesc;
+
+ /* Check that this node should not be FTS5_TERM */
+ assert( pNear->nPhrase>1
+ || pNear->apPhrase[0]->nTerm>1
+ || pNear->apPhrase[0]->aTerm[0].pSynonym
+ );
+
+ /* Initialize iLast, the "lastest" rowid any iterator points to. If the
+ ** iterator skips through rowids in the default ascending order, this means
+ ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
+ ** means the minimum rowid. */
+ if( pLeft->aTerm[0].pSynonym ){
+ iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
+ }else{
+ iLast = pLeft->aTerm[0].pIter->iRowid;
+ }
+
+ do {
+ bMatch = 1;
+ for(i=0; inPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ for(j=0; jnTerm; j++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
+ if( pTerm->pSynonym ){
+ i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
+ if( iRowid==iLast ) continue;
+ bMatch = 0;
+ if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
+ pNode->bNomatch = 0;
+ pNode->bEof = 1;
+ return rc;
+ }
+ }else{
+ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
+ if( pIter->iRowid==iLast ) continue;
+ bMatch = 0;
+ if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
+ return rc;
+ }
+ }
+ }
+ }
+ }while( bMatch==0 );
+
+ pNode->iRowid = iLast;
+ pNode->bNomatch = ((0==fts5ExprNearTest(&rc, pExpr, pNode)) && rc==SQLITE_OK);
+ assert( pNode->bEof==0 || pNode->bNomatch==0 );
+
+ return rc;
+}
+
+/*
+** Advance the first term iterator in the first phrase of pNear. Set output
+** variable *pbEof to true if it reaches EOF or if an error occurs.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5ExprNodeNext_STRING(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode, /* FTS5_STRING or FTS5_TERM node */
+ int bFromValid,
+ i64 iFrom
+){
+ Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
+ int rc = SQLITE_OK;
+
+ pNode->bNomatch = 0;
+ if( pTerm->pSynonym ){
+ int bEof = 1;
+ Fts5ExprTerm *p;
+
+ /* Find the firstest rowid any synonym points to. */
+ i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
+
+ /* Advance each iterator that currently points to iRowid. Or, if iFrom
+ ** is valid - each iterator that points to a rowid before iFrom. */
+ for(p=pTerm; p; p=p->pSynonym){
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ i64 ii = p->pIter->iRowid;
+ if( ii==iRowid
+ || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc)
+ ){
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(p->pIter);
+ }
+ if( rc!=SQLITE_OK ) break;
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ bEof = 0;
+ }
+ }else{
+ bEof = 0;
+ }
+ }
+ }
+
+ /* Set the EOF flag if either all synonym iterators are at EOF or an
+ ** error has occurred. */
+ pNode->bEof = (rc || bEof);
+ }else{
+ Fts5IndexIter *pIter = pTerm->pIter;
+
+ assert( Fts5NodeIsString(pNode) );
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+
+ pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
+ }
+
+ if( pNode->bEof==0 ){
+ assert( rc==SQLITE_OK );
+ rc = fts5ExprNodeTest_STRING(pExpr, pNode);
+ }
+
+ return rc;
+}
+
+
+static int fts5ExprNodeTest_TERM(
+ Fts5Expr *pExpr, /* Expression that pNear is a part of */
+ Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */
+){
+ /* As this "NEAR" object is actually a single phrase that consists
+ ** of a single term only, grab pointers into the poslist managed by the
+ ** fts5_index.c iterator object. This is much faster than synthesizing
+ ** a new poslist the way we have to for more complicated phrase or NEAR
+ ** expressions. */
+ Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0];
+ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
+
+ assert( pNode->eType==FTS5_TERM );
+ assert( pNode->pNear->nPhrase==1 && pPhrase->nTerm==1 );
+ assert( pPhrase->aTerm[0].pSynonym==0 );
+
+ pPhrase->poslist.n = pIter->nData;
+ if( pExpr->pConfig->eDetail==FTS5_DETAIL_FULL ){
+ pPhrase->poslist.p = (u8*)pIter->pData;
+ }
+ pNode->iRowid = pIter->iRowid;
+ pNode->bNomatch = (pPhrase->poslist.n==0);
+ return SQLITE_OK;
+}
+
+/*
+** xNext() method for a node of type FTS5_TERM.
+*/
+static int fts5ExprNodeNext_TERM(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc;
+ Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
+
+ assert( pNode->bEof==0 );
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+ if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
+ rc = fts5ExprNodeTest_TERM(pExpr, pNode);
+ }else{
+ pNode->bEof = 1;
+ pNode->bNomatch = 0;
+ }
+ return rc;
+}
+
+static void fts5ExprNodeTest_OR(
+ Fts5Expr *pExpr, /* Expression of which pNode is a part */
+ Fts5ExprNode *pNode /* Expression node to test */
+){
+ Fts5ExprNode *pNext = pNode->apChild[0];
+ int i;
+
+ for(i=1; inChild; i++){
+ Fts5ExprNode *pChild = pNode->apChild[i];
+ int cmp = fts5NodeCompare(pExpr, pNext, pChild);
+ if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){
+ pNext = pChild;
+ }
+ }
+ pNode->iRowid = pNext->iRowid;
+ pNode->bEof = pNext->bEof;
+ pNode->bNomatch = pNext->bNomatch;
+}
+
+static int fts5ExprNodeNext_OR(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int i;
+ i64 iLast = pNode->iRowid;
+
+ for(i=0; inChild; i++){
+ Fts5ExprNode *p1 = pNode->apChild[i];
+ assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
+ if( p1->bEof==0 ){
+ if( (p1->iRowid==iLast)
+ || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
+ ){
+ int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ }
+ }
+
+ fts5ExprNodeTest_OR(pExpr, pNode);
+ return SQLITE_OK;
+}
/*
** Argument pNode is an FTS5_AND node.
*/
-static int fts5ExprAndNextRowid(
+static int fts5ExprNodeTest_AND(
Fts5Expr *pExpr, /* Expression pPhrase belongs to */
Fts5ExprNode *pAnd /* FTS5_AND node to advance */
){
@@ -1034,15 +1127,11 @@ static int fts5ExprAndNextRowid(
bMatch = 1;
for(iChild=0; iChildnChild; iChild++){
Fts5ExprNode *pChild = pAnd->apChild[iChild];
- if( 0 && pChild->eType==FTS5_STRING ){
- /* TODO */
- }else{
- int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
- if( cmp>0 ){
- /* Advance pChild until it points to iLast or laster */
- rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
- if( rc!=SQLITE_OK ) return rc;
- }
+ int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
+ if( cmp>0 ){
+ /* Advance pChild until it points to iLast or laster */
+ rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
+ if( rc!=SQLITE_OK ) return rc;
}
/* If the child node is now at EOF, so is the parent AND node. Otherwise,
@@ -1072,126 +1161,66 @@ static int fts5ExprAndNextRowid(
return SQLITE_OK;
}
-
-/*
-** Compare the values currently indicated by the two nodes as follows:
-**
-** res = (*p1) - (*p2)
-**
-** Nodes that point to values that come later in the iteration order are
-** considered to be larger. Nodes at EOF are the largest of all.
-**
-** This means that if the iteration order is ASC, then numerically larger
-** rowids are considered larger. Or if it is the default DESC, numerically
-** smaller rowids are larger.
-*/
-static int fts5NodeCompare(
- Fts5Expr *pExpr,
- Fts5ExprNode *p1,
- Fts5ExprNode *p2
-){
- if( p2->bEof ) return -1;
- if( p1->bEof ) return +1;
- return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid);
-}
-
-/*
-** Advance node iterator pNode, part of expression pExpr. If argument
-** bFromValid is zero, then pNode is advanced exactly once. Or, if argument
-** bFromValid is non-zero, then pNode is advanced until it is at or past
-** rowid value iFrom. Whether "past" means "less than" or "greater than"
-** depends on whether this is an ASC or DESC iterator.
-*/
-static int fts5ExprNodeNext(
+static int fts5ExprNodeNext_AND(
Fts5Expr *pExpr,
Fts5ExprNode *pNode,
int bFromValid,
i64 iFrom
){
- int rc = SQLITE_OK;
-
- if( pNode->bEof==0 ){
- switch( pNode->eType ){
- case FTS5_STRING: {
- rc = fts5ExprNearAdvanceFirst(pExpr, pNode, bFromValid, iFrom);
- break;
- };
-
- case FTS5_TERM: {
- Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
- if( bFromValid ){
- rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
- }else{
- rc = sqlite3Fts5IterNext(pIter);
- }
- if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
- assert( rc==SQLITE_OK );
- rc = fts5ExprTokenTest(pExpr, pNode);
- }else{
- pNode->bEof = 1;
- }
- return rc;
- };
-
- case FTS5_AND: {
- Fts5ExprNode *pLeft = pNode->apChild[0];
- rc = fts5ExprNodeNext(pExpr, pLeft, bFromValid, iFrom);
- break;
- }
-
- case FTS5_OR: {
- int i;
- i64 iLast = pNode->iRowid;
-
- for(i=0; rc==SQLITE_OK && inChild; i++){
- Fts5ExprNode *p1 = pNode->apChild[i];
- assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
- if( p1->bEof==0 ){
- if( (p1->iRowid==iLast)
- || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
- ){
- rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
- }
- }
- }
-
- break;
- }
-
- default: assert( pNode->eType==FTS5_NOT ); {
- assert( pNode->nChild==2 );
- rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
- break;
- }
- }
-
- if( rc==SQLITE_OK ){
- rc = fts5ExprNodeNextMatch(pExpr, pNode);
- }
+ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest_AND(pExpr, pNode);
}
-
- /* Assert that if bFromValid was true, either:
- **
- ** a) an error occurred, or
- ** b) the node is now at EOF, or
- ** c) the node is now at or past rowid iFrom.
- */
- assert( bFromValid==0
- || rc!=SQLITE_OK /* a */
- || pNode->bEof /* b */
- || pNode->iRowid==iFrom || pExpr->bDesc==(pNode->iRowidapChild[0];
+ Fts5ExprNode *p2 = pNode->apChild[1];
+ assert( pNode->nChild==2 );
+
+ while( rc==SQLITE_OK && p1->bEof==0 ){
+ int cmp = fts5NodeCompare(pExpr, p1, p2);
+ if( cmp>0 ){
+ rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid);
+ cmp = fts5NodeCompare(pExpr, p1, p2);
+ }
+ assert( rc!=SQLITE_OK || cmp<=0 );
+ if( cmp || p2->bNomatch ) break;
+ rc = fts5ExprNodeNext(pExpr, p1, 0, 0);
+ }
+ pNode->bEof = p1->bEof;
+ pNode->bNomatch = p1->bNomatch;
+ pNode->iRowid = p1->iRowid;
+ if( p1->bEof ){
+ fts5ExprNodeZeroPoslist(p2);
+ }
+ return rc;
+}
+
+static int fts5ExprNodeNext_NOT(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest_NOT(pExpr, pNode);
+ }
+ return rc;
+}
/*
** If pNode currently points to a match, this function returns SQLITE_OK
** without modifying it. Otherwise, pNode is advanced until it does point
** to a match or EOF is reached.
*/
-static int fts5ExprNodeNextMatch(
+static int fts5ExprNodeTest(
Fts5Expr *pExpr, /* Expression of which pNode is a part */
Fts5ExprNode *pNode /* Expression node to test */
){
@@ -1200,58 +1229,27 @@ static int fts5ExprNodeNextMatch(
switch( pNode->eType ){
case FTS5_STRING: {
- /* Advance the iterators until they all point to the same rowid */
- rc = fts5ExprNearNextMatch(pExpr, pNode);
+ rc = fts5ExprNodeTest_STRING(pExpr, pNode);
break;
}
case FTS5_TERM: {
- rc = fts5ExprTokenTest(pExpr, pNode);
+ rc = fts5ExprNodeTest_TERM(pExpr, pNode);
break;
}
case FTS5_AND: {
- rc = fts5ExprAndNextRowid(pExpr, pNode);
+ rc = fts5ExprNodeTest_AND(pExpr, pNode);
break;
}
case FTS5_OR: {
- Fts5ExprNode *pNext = pNode->apChild[0];
- int i;
-
- for(i=1; inChild; i++){
- Fts5ExprNode *pChild = pNode->apChild[i];
- int cmp = fts5NodeCompare(pExpr, pNext, pChild);
- if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){
- pNext = pChild;
- }
- }
- pNode->iRowid = pNext->iRowid;
- pNode->bEof = pNext->bEof;
- pNode->bNomatch = pNext->bNomatch;
+ fts5ExprNodeTest_OR(pExpr, pNode);
break;
}
default: assert( pNode->eType==FTS5_NOT ); {
- Fts5ExprNode *p1 = pNode->apChild[0];
- Fts5ExprNode *p2 = pNode->apChild[1];
- assert( pNode->nChild==2 );
-
- while( rc==SQLITE_OK && p1->bEof==0 ){
- int cmp = fts5NodeCompare(pExpr, p1, p2);
- if( cmp>0 ){
- rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid);
- cmp = fts5NodeCompare(pExpr, p1, p2);
- }
- assert( rc!=SQLITE_OK || cmp<=0 );
- if( cmp || p2->bNomatch ) break;
- rc = fts5ExprNodeNext(pExpr, p1, 0, 0);
- }
- pNode->bEof = p1->bEof;
- pNode->iRowid = p1->iRowid;
- if( p1->bEof ){
- fts5ExprNodeZeroPoslist(p2);
- }
+ rc = fts5ExprNodeTest_NOT(pExpr, pNode);
break;
}
}
@@ -1270,20 +1268,40 @@ static int fts5ExprNodeNextMatch(
static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){
int rc = SQLITE_OK;
pNode->bEof = 0;
+ pNode->bNomatch = 0;
if( Fts5NodeIsString(pNode) ){
/* Initialize all term iterators in the NEAR object. */
rc = fts5ExprNearInitAll(pExpr, pNode);
}else{
int i;
+ int nEof = 0;
for(i=0; inChild && rc==SQLITE_OK; i++){
+ Fts5ExprNode *pChild = pNode->apChild[i];
rc = fts5ExprNodeFirst(pExpr, pNode->apChild[i]);
+ assert( pChild->bEof==0 || pChild->bEof==1 );
+ nEof += pChild->bEof;
}
pNode->iRowid = pNode->apChild[0]->iRowid;
+
+ switch( pNode->eType ){
+ case FTS5_AND:
+ if( nEof>0 ) fts5ExprSetEof(pNode);
+ break;
+
+ case FTS5_OR:
+ if( pNode->nChild==nEof ) fts5ExprSetEof(pNode);
+ break;
+
+ default:
+ assert( pNode->eType==FTS5_NOT );
+ pNode->bEof = pNode->apChild[0]->bEof;
+ break;
+ }
}
if( rc==SQLITE_OK ){
- rc = fts5ExprNodeNextMatch(pExpr, pNode);
+ rc = fts5ExprNodeTest(pExpr, pNode);
}
return rc;
}
@@ -1307,7 +1325,7 @@ static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){
int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
Fts5ExprNode *pRoot = p->pRoot;
int rc = SQLITE_OK;
- if( pRoot ){
+ if( pRoot->xNext ){
p->pIndex = pIdx;
p->bDesc = bDesc;
rc = fts5ExprNodeFirst(p, pRoot);
@@ -1319,7 +1337,8 @@ int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
}
/* If the iterator is not at a real match, skip forward until it is. */
- while( pRoot->bNomatch && rc==SQLITE_OK && pRoot->bEof==0 ){
+ while( pRoot->bNomatch ){
+ assert( pRoot->bEof==0 && rc==SQLITE_OK );
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
}
}
@@ -1335,9 +1354,11 @@ int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
int sqlite3Fts5ExprNext(Fts5Expr *p, i64 iLast){
int rc;
Fts5ExprNode *pRoot = p->pRoot;
+ assert( pRoot->bEof==0 && pRoot->bNomatch==0 );
do {
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
- }while( pRoot->bNomatch && pRoot->bEof==0 && rc==SQLITE_OK );
+ assert( pRoot->bNomatch==0 || (rc==SQLITE_OK && pRoot->bEof==0) );
+ }while( pRoot->bNomatch );
if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){
pRoot->bEof = 1;
}
@@ -1345,7 +1366,7 @@ int sqlite3Fts5ExprNext(Fts5Expr *p, i64 iLast){
}
int sqlite3Fts5ExprEof(Fts5Expr *p){
- return (p->pRoot==0 || p->pRoot->bEof);
+ return p->pRoot->bEof;
}
i64 sqlite3Fts5ExprRowid(Fts5Expr *p){
@@ -1370,10 +1391,10 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
sqlite3_free(pTerm->zTerm);
sqlite3Fts5IterClose(pTerm->pIter);
-
for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
pNext = pSyn->pSynonym;
sqlite3Fts5IterClose(pSyn->pIter);
+ fts5BufferFree((Fts5Buffer*)&pSyn[1]);
sqlite3_free(pSyn);
}
}
@@ -1461,13 +1482,13 @@ static int fts5ParseTokenize(
assert( pPhrase==0 || pPhrase->nTerm>0 );
if( pPhrase && (tflags & FTS5_TOKEN_COLOCATED) ){
Fts5ExprTerm *pSyn;
- int nByte = sizeof(Fts5ExprTerm) + nToken+1;
+ int nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte);
if( pSyn==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pSyn, 0, nByte);
- pSyn->zTerm = (char*)&pSyn[1];
+ pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
memcpy(pSyn->zTerm, pToken, nToken);
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
@@ -1646,8 +1667,10 @@ int sqlite3Fts5ExprClonePhrase(
if( pOrig->nTerm==1 && pOrig->aTerm[0].pSynonym==0 ){
pNew->pRoot->eType = FTS5_TERM;
+ pNew->pRoot->xNext = fts5ExprNodeNext_TERM;
}else{
pNew->pRoot->eType = FTS5_STRING;
+ pNew->pRoot->xNext = fts5ExprNodeNext_STRING;
}
}else{
sqlite3Fts5ExprFree(pNew);
@@ -1795,6 +1818,38 @@ void sqlite3Fts5ParseSetColset(
}
}
+static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
+ switch( pNode->eType ){
+ case FTS5_STRING: {
+ Fts5ExprNearset *pNear = pNode->pNear;
+ if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1
+ && pNear->apPhrase[0]->aTerm[0].pSynonym==0
+ ){
+ pNode->eType = FTS5_TERM;
+ pNode->xNext = fts5ExprNodeNext_TERM;
+ }else{
+ pNode->xNext = fts5ExprNodeNext_STRING;
+ }
+ break;
+ };
+
+ case FTS5_OR: {
+ pNode->xNext = fts5ExprNodeNext_OR;
+ break;
+ };
+
+ case FTS5_AND: {
+ pNode->xNext = fts5ExprNodeNext_AND;
+ break;
+ };
+
+ default: assert( pNode->eType==FTS5_NOT ); {
+ pNode->xNext = fts5ExprNodeNext_NOT;
+ break;
+ };
+ }
+}
+
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
@@ -1844,16 +1899,16 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
if( pRet ){
pRet->eType = eType;
pRet->pNear = pNear;
+ fts5ExprAssignXNext(pRet);
if( eType==FTS5_STRING ){
int iPhrase;
for(iPhrase=0; iPhrasenPhrase; iPhrase++){
pNear->apPhrase[iPhrase]->pNode = pRet;
}
- if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 ){
- if( pNear->apPhrase[0]->aTerm[0].pSynonym==0 ){
- pRet->eType = FTS5_TERM;
- }
- }else if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
+
+ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL
+ && (pNear->nPhrase!=1 || pNear->apPhrase[0]->nTerm!=1)
+ ){
assert( pParse->rc==SQLITE_OK );
pParse->rc = SQLITE_ERROR;
assert( pParse->zErr==0 );
@@ -1864,6 +1919,7 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
sqlite3_free(pRet);
pRet = 0;
}
+
}else{
fts5ExprAddChildren(pRet, pLeft);
fts5ExprAddChildren(pRet, pRight);
@@ -2146,7 +2202,7 @@ static void fts5ExprFunction(
}
if( rc==SQLITE_OK ){
char *zText;
- if( pExpr->pRoot==0 ){
+ if( pExpr->pRoot->xNext==0 ){
zText = sqlite3_mprintf("");
}else if( bTcl ){
zText = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRoot);
@@ -2246,7 +2302,7 @@ int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
int rc = SQLITE_OK;
void *pCtx = (void*)pGlobal;
- for(i=0; rc==SQLITE_OK && i<(int)ArraySize(aFunc); i++){
+ for(i=0; rc==SQLITE_OK && iz, -1, SQLITE_UTF8, pCtx, p->x, 0, 0);
}
@@ -2484,26 +2540,21 @@ int sqlite3Fts5ExprPhraseCollist(
int rc = SQLITE_OK;
assert( iPhrase>=0 && iPhrasenPhrase );
+ assert( pExpr->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+
if( pNode->bEof==0
&& pNode->iRowid==pExpr->pRoot->iRowid
&& pPhrase->poslist.n>0
){
Fts5ExprTerm *pTerm = &pPhrase->aTerm[0];
if( pTerm->pSynonym ){
- int bDel = 0;
- u8 *a;
+ Fts5Buffer *pBuf = (Fts5Buffer*)&pTerm->pSynonym[1];
rc = fts5ExprSynonymList(
- pTerm, 1, 0, pNode->iRowid, &bDel, &a, pnCollist
+ pTerm, 1, 0, pNode->iRowid, pBuf, (u8**)ppCollist, pnCollist
);
- if( bDel ){
- sqlite3Fts5BufferSet(&rc, &pPhrase->poslist, *pnCollist, a);
- *ppCollist = pPhrase->poslist.p;
- sqlite3_free(a);
- }else{
- *ppCollist = a;
- }
}else{
- sqlite3Fts5IterCollist(pPhrase->aTerm[0].pIter, ppCollist, pnCollist);
+ *ppCollist = pPhrase->aTerm[0].pIter->pData;
+ *pnCollist = pPhrase->aTerm[0].pIter->nData;
}
}else{
*ppCollist = 0;
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c
index fc11a23413..12e18c6445 100644
--- a/ext/fts5/fts5_index.c
+++ b/ext/fts5/fts5_index.c
@@ -261,6 +261,7 @@ typedef struct Fts5Data Fts5Data;
typedef struct Fts5DlidxIter Fts5DlidxIter;
typedef struct Fts5DlidxLvl Fts5DlidxLvl;
typedef struct Fts5DlidxWriter Fts5DlidxWriter;
+typedef struct Fts5Iter Fts5Iter;
typedef struct Fts5PageWriter Fts5PageWriter;
typedef struct Fts5SegIter Fts5SegIter;
typedef struct Fts5DoclistIter Fts5DoclistIter;
@@ -503,16 +504,20 @@ struct Fts5SegIter {
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
** There is no way to tell if this is populated or not.
*/
-struct Fts5IndexIter {
+struct Fts5Iter {
+ Fts5IndexIter base; /* Base class containing output vars */
+
Fts5Index *pIndex; /* Index that owns this iterator */
Fts5Structure *pStruct; /* Database structure for this iterator */
Fts5Buffer poslist; /* Buffer containing current poslist */
+ Fts5Colset *pColset; /* Restrict matches to these columns */
+
+ /* Invoked to set output variables. */
+ void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);
int nSeg; /* Size of aSeg[] array */
int bRev; /* True to iterate in reverse order */
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) */
@@ -1752,7 +1757,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
** points to a delete marker. A delete marker is an entry with a 0 byte
** position-list.
*/
-static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5IndexIter *pIter){
+static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5Iter *pIter){
Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
return (p->rc==SQLITE_OK && pSeg->pLeaf && pSeg->nPos==0);
}
@@ -2024,9 +2029,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
iPoslist = 4;
}
fts5IndexSkipVarint(pLeaf->p, iPoslist);
- assert( p->pConfig->eDetail==FTS5_DETAIL_NONE || iPoslist==(
- pIter->iLeafOffset - sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel)
- ));
pIter->iLeafOffset = iPoslist;
/* If this condition is true then the largest rowid for the current
@@ -2258,9 +2260,6 @@ static void fts5SegIterSeekInit(
int bGe = (flags & FTS5INDEX_QUERY_SCAN);
int bDlidx = 0; /* True if there is a doclist-index */
- static int nCall = 0;
- nCall++;
-
assert( bGe==0 || (flags & FTS5INDEX_QUERY_DESC)==0 );
assert( pTerm && nTerm );
memset(pIter, 0, sizeof(*pIter));
@@ -2406,7 +2405,7 @@ static void fts5SegIterClear(Fts5SegIter *pIter){
** two iterators.
*/
static void fts5AssertComparisonResult(
- Fts5IndexIter *pIter,
+ Fts5Iter *pIter,
Fts5SegIter *p1,
Fts5SegIter *p2,
Fts5CResult *pRes
@@ -2447,12 +2446,12 @@ static void fts5AssertComparisonResult(
** statement used to verify that the contents of the pIter->aFirst[] array
** are correct.
*/
-static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5IndexIter *pIter){
+static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){
if( p->rc==SQLITE_OK ){
Fts5SegIter *pFirst = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
int i;
- assert( (pFirst->pLeaf==0)==pIter->bEof );
+ assert( (pFirst->pLeaf==0)==pIter->base.bEof );
/* Check that pIter->iSwitchRowid is set correctly. */
for(i=0; inSeg; i++){
@@ -2492,7 +2491,7 @@ static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5IndexIter *pIter){
** to a key that is a duplicate of another, higher priority,
** segment-iterator in the pSeg->aSeg[] array.
*/
-static int fts5MultiIterDoCompare(Fts5IndexIter *pIter, int iOut){
+static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
int i1; /* Index of left-hand Fts5SegIter */
int i2; /* Index of right-hand Fts5SegIter */
int iRes;
@@ -2638,7 +2637,7 @@ static void fts5SegIterNextFrom(
/*
** Free the iterator object passed as the second argument.
*/
-static void fts5MultiIterFree(Fts5Index *p, Fts5IndexIter *pIter){
+static void fts5MultiIterFree(Fts5Index *p, Fts5Iter *pIter){
if( pIter ){
int i;
for(i=0; inSeg; i++){
@@ -2652,7 +2651,7 @@ static void fts5MultiIterFree(Fts5Index *p, Fts5IndexIter *pIter){
static void fts5MultiIterAdvanced(
Fts5Index *p, /* FTS5 backend to iterate within */
- Fts5IndexIter *pIter, /* Iterator to update aFirst[] array for */
+ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
int iChanged, /* Index of sub-iterator just advanced */
int iMinset /* Minimum entry in aFirst[] to set */
){
@@ -2680,8 +2679,9 @@ static void fts5MultiIterAdvanced(
*/
static int fts5MultiIterAdvanceRowid(
Fts5Index *p, /* FTS5 backend to iterate within */
- Fts5IndexIter *pIter, /* Iterator to update aFirst[] array for */
- int iChanged /* Index of sub-iterator just advanced */
+ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
+ int iChanged, /* Index of sub-iterator just advanced */
+ Fts5SegIter **ppFirst
){
Fts5SegIter *pNew = &pIter->aSeg[iChanged];
@@ -2714,15 +2714,16 @@ static int fts5MultiIterAdvanceRowid(
}
}
+ *ppFirst = pNew;
return 0;
}
/*
** Set the pIter->bEof variable based on the state of the sub-iterators.
*/
-static void fts5MultiIterSetEof(Fts5IndexIter *pIter){
+static void fts5MultiIterSetEof(Fts5Iter *pIter){
Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
- pIter->bEof = pSeg->pLeaf==0;
+ pIter->base.bEof = pSeg->pLeaf==0;
pIter->iSwitchRowid = pSeg->iRowid;
}
@@ -2735,39 +2736,44 @@ static void fts5MultiIterSetEof(Fts5IndexIter *pIter){
*/
static void fts5MultiIterNext(
Fts5Index *p,
- Fts5IndexIter *pIter,
+ Fts5Iter *pIter,
int bFrom, /* True if argument iFrom is valid */
i64 iFrom /* Advance at least as far as this */
){
- if( p->rc==SQLITE_OK ){
- int bUseFrom = bFrom;
- do {
- int iFirst = pIter->aFirst[1].iFirst;
- int bNewTerm = 0;
- Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
- assert( p->rc==SQLITE_OK );
- if( bUseFrom && pSeg->pDlidx ){
- fts5SegIterNextFrom(p, pSeg, iFrom);
- }else{
- pSeg->xNext(p, pSeg, &bNewTerm);
- }
+ int bUseFrom = bFrom;
+ while( p->rc==SQLITE_OK ){
+ int iFirst = pIter->aFirst[1].iFirst;
+ int bNewTerm = 0;
+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ assert( p->rc==SQLITE_OK );
+ if( bUseFrom && pSeg->pDlidx ){
+ fts5SegIterNextFrom(p, pSeg, iFrom);
+ }else{
+ pSeg->xNext(p, pSeg, &bNewTerm);
+ }
- if( pSeg->pLeaf==0 || bNewTerm
- || fts5MultiIterAdvanceRowid(p, pIter, iFirst)
- ){
- fts5MultiIterAdvanced(p, pIter, iFirst, 1);
- fts5MultiIterSetEof(pIter);
- }
- fts5AssertMultiIterSetup(p, pIter);
+ if( pSeg->pLeaf==0 || bNewTerm
+ || fts5MultiIterAdvanceRowid(p, pIter, iFirst, &pSeg)
+ ){
+ fts5MultiIterAdvanced(p, pIter, iFirst, 1);
+ fts5MultiIterSetEof(pIter);
+ pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ if( pSeg->pLeaf==0 ) return;
+ }
- bUseFrom = 0;
- }while( pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter) );
+ fts5AssertMultiIterSetup(p, pIter);
+ assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf );
+ if( pIter->bSkipEmpty==0 || pSeg->nPos ){
+ pIter->xSetOutputs(pIter, pSeg);
+ return;
+ }
+ bUseFrom = 0;
}
}
static void fts5MultiIterNext2(
Fts5Index *p,
- Fts5IndexIter *pIter,
+ Fts5Iter *pIter,
int *pbNewTerm /* OUT: True if *might* be new term */
){
assert( pIter->bSkipEmpty );
@@ -2780,7 +2786,7 @@ static void fts5MultiIterNext2(
assert( p->rc==SQLITE_OK );
pSeg->xNext(p, pSeg, &bNewTerm);
if( pSeg->pLeaf==0 || bNewTerm
- || fts5MultiIterAdvanceRowid(p, pIter, iFirst)
+ || fts5MultiIterAdvanceRowid(p, pIter, iFirst, &pSeg)
){
fts5MultiIterAdvanced(p, pIter, iFirst, 1);
fts5MultiIterSetEof(pIter);
@@ -2794,17 +2800,19 @@ static void fts5MultiIterNext2(
}
}
+static void fts5IterSetOutputs_Noop(Fts5Iter *pIter, Fts5SegIter *pSeg){
+}
-static Fts5IndexIter *fts5MultiIterAlloc(
+static Fts5Iter *fts5MultiIterAlloc(
Fts5Index *p, /* FTS5 backend to iterate within */
int nSeg
){
- Fts5IndexIter *pNew;
+ Fts5Iter *pNew;
int nSlot; /* Power of two >= nSeg */
for(nSlot=2; nSlotaSeg[] */
sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
);
@@ -2812,198 +2820,119 @@ static Fts5IndexIter *fts5MultiIterAlloc(
pNew->nSeg = nSlot;
pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
pNew->pIndex = p;
+ pNew->xSetOutputs = fts5IterSetOutputs_Noop;
}
return pNew;
}
-/*
-** Allocate a new Fts5IndexIter object.
-**
-** The new object will be used to iterate through data in structure pStruct.
-** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
-** is zero or greater, data from the first nSegment segments on level iLevel
-** is merged.
-**
-** The iterator initially points to the first term/rowid entry in the
-** iterated data.
-*/
-static void fts5MultiIterNew(
- Fts5Index *p, /* FTS5 backend to iterate within */
- Fts5Structure *pStruct, /* Structure of specific index */
- int bSkipEmpty, /* True to ignore delete-keys */
- int flags, /* FTS5INDEX_QUERY_XXX flags */
- const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */
- int iLevel, /* Level to iterate (-1 for all) */
- int nSegment, /* Number of segments to merge (iLevel>=0) */
- Fts5IndexIter **ppOut /* New object */
+static void fts5PoslistCallback(
+ Fts5Index *p,
+ void *pContext,
+ const u8 *pChunk, int nChunk
){
- int nSeg = 0; /* Number of segment-iters in use */
- int iIter = 0; /* */
- int iSeg; /* Used to iterate through segments */
- Fts5Buffer buf = {0,0,0}; /* Buffer used by fts5SegIterSeekInit() */
- Fts5StructureLevel *pLvl;
- Fts5IndexIter *pNew;
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ fts5BufferSafeAppendBlob((Fts5Buffer*)pContext, pChunk, nChunk);
+ }
+}
- assert( (pTerm==0 && nTerm==0) || iLevel<0 );
+typedef struct PoslistCallbackCtx PoslistCallbackCtx;
+struct PoslistCallbackCtx {
+ Fts5Buffer *pBuf; /* Append to this buffer */
+ Fts5Colset *pColset; /* Restrict matches to this column */
+ int eState; /* See above */
+};
- /* Allocate space for the new multi-seg-iterator. */
- if( p->rc==SQLITE_OK ){
- if( iLevel<0 ){
- assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
- nSeg = pStruct->nSegment;
- nSeg += (p->pHash ? 1 : 0);
- }else{
- nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
+typedef struct PoslistOffsetsCtx PoslistOffsetsCtx;
+struct PoslistOffsetsCtx {
+ Fts5Buffer *pBuf; /* Append to this buffer */
+ Fts5Colset *pColset; /* Restrict matches to this column */
+ int iRead;
+ int iWrite;
+};
+
+/*
+** 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 fts5PoslistOffsetsCallback(
+ Fts5Index *p,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext;
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ int i = 0;
+ while( iiRead - 2;
+ pCtx->iRead = iVal;
+ if( fts5IndexColsetTest(pCtx->pColset, iVal) ){
+ fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite);
+ pCtx->iWrite = iVal;
+ }
}
}
- *ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
- if( pNew==0 ) return;
- pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
- pNew->bSkipEmpty = (u8)bSkipEmpty;
- pNew->pStruct = pStruct;
- fts5StructureRef(pStruct);
+}
- /* Initialize each of the component segment iterators. */
- if( iLevel<0 ){
- Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
- if( p->pHash ){
- /* Add a segment iterator for the current contents of the hash table. */
- Fts5SegIter *pIter = &pNew->aSeg[iIter++];
- fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
+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;
+ }
}
- for(pLvl=&pStruct->aLevel[0]; pLvlnSeg-1; iSeg>=0; iSeg--){
- Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
- Fts5SegIter *pIter = &pNew->aSeg[iIter++];
- if( pTerm==0 ){
- fts5SegIterInit(p, pSeg, pIter);
+
+ do {
+ while( ieState ){
+ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
+ }
+ if( i=nChunk ){
+ pCtx->eState = 2;
}else{
- fts5SegIterSeekInit(p, &buf, pTerm, nTerm, flags, pSeg, pIter);
+ fts5FastGetVarint32(pChunk, i, iCol);
+ pCtx->eState = fts5IndexColsetTest(pCtx->pColset, iCol);
+ if( pCtx->eState ){
+ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
+ iStart = i;
+ }
}
}
- }
- }else{
- pLvl = &pStruct->aLevel[iLevel];
- for(iSeg=nSeg-1; iSeg>=0; iSeg--){
- fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
- }
+ }while( irc==SQLITE_OK ){
- for(iIter=pNew->nSeg-1; iIter>0; iIter--){
- int iEq;
- if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
- Fts5SegIter *pSeg = &pNew->aSeg[iEq];
- if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
- fts5MultiIterAdvanced(p, pNew, iEq, iIter);
- }
- }
- fts5MultiIterSetEof(pNew);
- fts5AssertMultiIterSetup(p, pNew);
-
- if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){
- fts5MultiIterNext(p, pNew, 0, 0);
- }
- }else{
- fts5MultiIterFree(p, pNew);
- *ppOut = 0;
- }
- fts5BufferFree(&buf);
-}
-
-/*
-** Create an Fts5IndexIter that iterates through the doclist provided
-** as the second argument.
-*/
-static void fts5MultiIterNew2(
- Fts5Index *p, /* FTS5 backend to iterate within */
- Fts5Data *pData, /* Doclist to iterate through */
- int bDesc, /* True for descending rowid order */
- Fts5IndexIter **ppOut /* New object */
-){
- Fts5IndexIter *pNew;
- pNew = fts5MultiIterAlloc(p, 2);
- if( pNew ){
- Fts5SegIter *pIter = &pNew->aSeg[1];
-
- pNew->bFiltered = 1;
- pIter->flags = FTS5_SEGITER_ONETERM;
- if( pData->szLeaf>0 ){
- pIter->pLeaf = pData;
- pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
- pIter->iEndofDoclist = pData->nn;
- pNew->aFirst[1].iFirst = 1;
- if( bDesc ){
- pNew->bRev = 1;
- pIter->flags |= FTS5_SEGITER_REVERSE;
- fts5SegIterReverseInitPage(p, pIter);
- }else{
- fts5SegIterLoadNPos(p, pIter);
- }
- pData = 0;
- }else{
- pNew->bEof = 1;
- }
- fts5SegIterSetNext(p, pIter);
-
- *ppOut = pNew;
- }
-
- fts5DataRelease(pData);
-}
-
-/*
-** Return true if the iterator is at EOF or if an error has occurred.
-** False otherwise.
-*/
-static int fts5MultiIterEof(Fts5Index *p, Fts5IndexIter *pIter){
- assert( p->rc
- || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->bEof
- );
- return (p->rc || pIter->bEof);
-}
-
-/*
-** Return the rowid of the entry that the iterator currently points
-** to. If the iterator points to EOF when this function is called the
-** results are undefined.
-*/
-static i64 fts5MultiIterRowid(Fts5IndexIter *pIter){
- assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf );
- return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid;
-}
-
-/*
-** Move the iterator to the next entry at or following iMatch.
-*/
-static void fts5MultiIterNextFrom(
- Fts5Index *p,
- Fts5IndexIter *pIter,
- i64 iMatch
-){
- while( 1 ){
- i64 iRowid;
- fts5MultiIterNext(p, pIter, 1, iMatch);
- if( fts5MultiIterEof(p, pIter) ) break;
- iRowid = fts5MultiIterRowid(pIter);
- if( pIter->bRev==0 && iRowid>=iMatch ) break;
- if( pIter->bRev!=0 && iRowid<=iMatch ) break;
- }
-}
-
-/*
-** Return a pointer to a buffer containing the term associated with the
-** entry that the iterator currently points to.
-*/
-static const u8 *fts5MultiIterTerm(Fts5IndexIter *pIter, int *pn){
- Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
- *pn = p->term.n;
- return p->term.p;
}
static void fts5ChunkIterate(
@@ -3047,7 +2976,458 @@ static void fts5ChunkIterate(
}
}
+/*
+** Iterator pIter currently points to a valid entry (not EOF). This
+** function appends the position list data for the current entry to
+** buffer pBuf. It does not make a copy of the position-list size
+** field.
+*/
+static void fts5SegiterPoslist(
+ Fts5Index *p,
+ Fts5SegIter *pSeg,
+ Fts5Colset *pColset,
+ Fts5Buffer *pBuf
+){
+ if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){
+ if( pColset==0 ){
+ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
+ }else{
+ if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){
+ PoslistCallbackCtx sCtx;
+ sCtx.pBuf = pBuf;
+ sCtx.pColset = pColset;
+ sCtx.eState = fts5IndexColsetTest(pColset, 0);
+ assert( sCtx.eState==0 || sCtx.eState==1 );
+ fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback);
+ }else{
+ PoslistOffsetsCtx sCtx;
+ memset(&sCtx, 0, sizeof(sCtx));
+ sCtx.pBuf = pBuf;
+ sCtx.pColset = pColset;
+ fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback);
+ }
+ }
+ }
+}
+/*
+** 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 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 */
+
+ while( iCol>iCurrent ){
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint. Note that it is not possible for a negative
+ ** or extremely large varint to occur within an uncorrupted position
+ ** list. So the last byte of each varint may be assumed to have a clear
+ ** 0x80 bit. */
+ while( *p!=0x01 ){
+ while( *p++ & 0x80 );
+ if( p>=pEnd ) return 0;
+ }
+ *pa = p++;
+ iCurrent = *p++;
+ if( iCurrent & 0x80 ){
+ p--;
+ p += fts5GetVarint32(p, iCurrent);
+ }
+ }
+ if( iCol!=iCurrent ) return 0;
+
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint */
+ while( pnCol; i++){
+ const u8 *pSub = pPos;
+ int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
+ if( nSub ){
+ fts5BufferAppendBlob(&rc, pBuf, nSub, pSub);
+ }
+ }
+ return rc;
+}
+
+/*
+** xSetOutputs callback used by detail=none tables.
+*/
+static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE );
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.nData = pSeg->nPos;
+}
+
+/*
+** xSetOutputs callback used by detail=full and detail=col tables when no
+** column filters are specified.
+*/
+static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.nData = pSeg->nPos;
+
+ assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_NONE );
+ assert( pIter->pColset==0 );
+
+ if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
+ /* All data is stored on the current page. Populate the output
+ ** variables to point into the body of the page object. */
+ pIter->base.pData = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ }else{
+ /* The data is distributed over two or more pages. Copy it into the
+ ** Fts5Iter.poslist buffer and then set the output pointer to point
+ ** to this buffer. */
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ }
+}
+
+/*
+** xSetOutputs callback used by detail=col when there is a column filter
+** and there are 100 or more columns. Also called as a fallback from
+** fts5IterSetOutputs_Col100 if the column-list spans more than one page.
+*/
+static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, pIter->pColset, &pIter->poslist);
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+}
+
+/*
+** xSetOutputs callback used when:
+**
+** * detail=col,
+** * there is a column filter, and
+** * the table contains 100 or fewer columns.
+**
+** The last point is to ensure all column numbers are stored as
+** single-byte varints.
+*/
+static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
+
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+ assert( pIter->pColset );
+
+ if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){
+ fts5IterSetOutputs_Col(pIter, pSeg);
+ }else{
+ u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset];
+ u8 *pEnd = (u8*)&a[pSeg->nPos];
+ int iPrev = 0;
+ int *aiCol = pIter->pColset->aiCol;
+ int *aiColEnd = &aiCol[pIter->pColset->nCol];
+
+ u8 *aOut = pIter->poslist.p;
+ int iPrevOut = 0;
+
+ pIter->base.iRowid = pSeg->iRowid;
+
+ while( abase.pData = pIter->poslist.p;
+ pIter->base.nData = aOut - pIter->poslist.p;
+ }
+}
+
+/*
+** xSetOutputs callback used by detail=full when there is a column filter.
+*/
+static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ Fts5Colset *pColset = pIter->pColset;
+ pIter->base.iRowid = pSeg->iRowid;
+
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_FULL );
+ assert( pColset );
+
+ if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
+ /* All data is stored on the current page. Populate the output
+ ** variables to point into the body of the page object. */
+ const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ if( pColset->nCol==1 ){
+ pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
+ pIter->base.pData = a;
+ }else{
+ fts5BufferZero(&pIter->poslist);
+ fts5IndexExtractColset(pColset, a, pSeg->nPos, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+ }else{
+ /* The data is distributed over two or more pages. Copy it into the
+ ** Fts5Iter.poslist buffer and then set the output pointer to point
+ ** to this buffer. */
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+}
+
+static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
+ if( *pRc==SQLITE_OK ){
+ Fts5Config *pConfig = pIter->pIndex->pConfig;
+ if( pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pIter->xSetOutputs = fts5IterSetOutputs_None;
+ }
+
+ else if( pIter->pColset==0 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Nocolset;
+ }
+
+ else if( pConfig->eDetail==FTS5_DETAIL_FULL ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Full;
+ }
+
+ else{
+ assert( pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+ if( pConfig->nCol<=100 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Col100;
+ sqlite3Fts5BufferSize(pRc, &pIter->poslist, pConfig->nCol);
+ }else{
+ pIter->xSetOutputs = fts5IterSetOutputs_Col;
+ }
+ }
+ }
+}
+
+
+/*
+** Allocate a new Fts5Iter object.
+**
+** The new object will be used to iterate through data in structure pStruct.
+** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
+** is zero or greater, data from the first nSegment segments on level iLevel
+** is merged.
+**
+** The iterator initially points to the first term/rowid entry in the
+** iterated data.
+*/
+static void fts5MultiIterNew(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Structure *pStruct, /* Structure of specific index */
+ int flags, /* FTS5INDEX_QUERY_XXX flags */
+ Fts5Colset *pColset, /* Colset to filter on (or NULL) */
+ const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */
+ int iLevel, /* Level to iterate (-1 for all) */
+ int nSegment, /* Number of segments to merge (iLevel>=0) */
+ Fts5Iter **ppOut /* New object */
+){
+ int nSeg = 0; /* Number of segment-iters in use */
+ int iIter = 0; /* */
+ int iSeg; /* Used to iterate through segments */
+ Fts5Buffer buf = {0,0,0}; /* Buffer used by fts5SegIterSeekInit() */
+ Fts5StructureLevel *pLvl;
+ Fts5Iter *pNew;
+
+ assert( (pTerm==0 && nTerm==0) || iLevel<0 );
+
+ /* Allocate space for the new multi-seg-iterator. */
+ if( p->rc==SQLITE_OK ){
+ if( iLevel<0 ){
+ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
+ nSeg = pStruct->nSegment;
+ nSeg += (p->pHash ? 1 : 0);
+ }else{
+ nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
+ }
+ }
+ *ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
+ if( pNew==0 ) return;
+ pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
+ pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY));
+ pNew->pStruct = pStruct;
+ pNew->pColset = pColset;
+ fts5StructureRef(pStruct);
+ if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){
+ fts5IterSetOutputCb(&p->rc, pNew);
+ }
+
+ /* Initialize each of the component segment iterators. */
+ if( p->rc==SQLITE_OK ){
+ if( iLevel<0 ){
+ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
+ if( p->pHash ){
+ /* Add a segment iterator for the current contents of the hash table. */
+ Fts5SegIter *pIter = &pNew->aSeg[iIter++];
+ fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
+ }
+ for(pLvl=&pStruct->aLevel[0]; pLvlnSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
+ Fts5SegIter *pIter = &pNew->aSeg[iIter++];
+ if( pTerm==0 ){
+ fts5SegIterInit(p, pSeg, pIter);
+ }else{
+ fts5SegIterSeekInit(p, &buf, pTerm, nTerm, flags, pSeg, pIter);
+ }
+ }
+ }
+ }else{
+ pLvl = &pStruct->aLevel[iLevel];
+ for(iSeg=nSeg-1; iSeg>=0; iSeg--){
+ fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
+ }
+ }
+ assert( iIter==nSeg );
+ }
+
+ /* If the above was successful, each component iterators now points
+ ** to the first entry in its segment. In this case initialize the
+ ** aFirst[] array. Or, if an error has occurred, free the iterator
+ ** object and set the output variable to NULL. */
+ if( p->rc==SQLITE_OK ){
+ for(iIter=pNew->nSeg-1; iIter>0; iIter--){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
+ Fts5SegIter *pSeg = &pNew->aSeg[iEq];
+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
+ fts5MultiIterAdvanced(p, pNew, iEq, iIter);
+ }
+ }
+ fts5MultiIterSetEof(pNew);
+ fts5AssertMultiIterSetup(p, pNew);
+
+ if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){
+ fts5MultiIterNext(p, pNew, 0, 0);
+ }else if( pNew->base.bEof==0 ){
+ Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
+ pNew->xSetOutputs(pNew, pSeg);
+ }
+
+ }else{
+ fts5MultiIterFree(p, pNew);
+ *ppOut = 0;
+ }
+ fts5BufferFree(&buf);
+
+}
+
+/*
+** Create an Fts5Iter that iterates through the doclist provided
+** as the second argument.
+*/
+static void fts5MultiIterNew2(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Data *pData, /* Doclist to iterate through */
+ int bDesc, /* True for descending rowid order */
+ Fts5Iter **ppOut /* New object */
+){
+ Fts5Iter *pNew;
+ pNew = fts5MultiIterAlloc(p, 2);
+ if( pNew ){
+ Fts5SegIter *pIter = &pNew->aSeg[1];
+
+ pIter->flags = FTS5_SEGITER_ONETERM;
+ if( pData->szLeaf>0 ){
+ pIter->pLeaf = pData;
+ pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
+ pIter->iEndofDoclist = pData->nn;
+ pNew->aFirst[1].iFirst = 1;
+ if( bDesc ){
+ pNew->bRev = 1;
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ fts5SegIterReverseInitPage(p, pIter);
+ }else{
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ pData = 0;
+ }else{
+ pNew->base.bEof = 1;
+ }
+ fts5SegIterSetNext(p, pIter);
+
+ *ppOut = pNew;
+ }
+
+ fts5DataRelease(pData);
+}
+
+/*
+** Return true if the iterator is at EOF or if an error has occurred.
+** False otherwise.
+*/
+static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
+ assert( p->rc
+ || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof
+ );
+ return (p->rc || pIter->base.bEof);
+}
+
+/*
+** Return the rowid of the entry that the iterator currently points
+** to. If the iterator points to EOF when this function is called the
+** results are undefined.
+*/
+static i64 fts5MultiIterRowid(Fts5Iter *pIter){
+ assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf );
+ return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid;
+}
+
+/*
+** Move the iterator to the next entry at or following iMatch.
+*/
+static void fts5MultiIterNextFrom(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ i64 iMatch
+){
+ while( 1 ){
+ i64 iRowid;
+ fts5MultiIterNext(p, pIter, 1, iMatch);
+ if( fts5MultiIterEof(p, pIter) ) break;
+ iRowid = fts5MultiIterRowid(pIter);
+ if( pIter->bRev==0 && iRowid>=iMatch ) break;
+ if( pIter->bRev!=0 && iRowid<=iMatch ) break;
+ }
+}
+
+/*
+** Return a pointer to a buffer containing the term associated with the
+** entry that the iterator currently points to.
+*/
+static const u8 *fts5MultiIterTerm(Fts5Iter *pIter, int *pn){
+ Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ *pn = p->term.n;
+ return p->term.p;
+}
/*
** Allocate a new segment-id for the structure pStruct. The new segment
@@ -3582,7 +3962,7 @@ static void fts5WriteInit(
** incremental merge operation. This function is called if the incremental
** merge step has finished but the input has not been completely exhausted.
*/
-static void fts5TrimSegments(Fts5Index *p, Fts5IndexIter *pIter){
+static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
int i;
Fts5Buffer buf;
memset(&buf, 0, sizeof(Fts5Buffer));
@@ -3660,7 +4040,7 @@ static void fts5IndexMergeLevel(
Fts5Structure *pStruct = *ppStruct;
Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
Fts5StructureLevel *pLvlOut;
- Fts5IndexIter *pIter = 0; /* Iterator to read input data */
+ Fts5Iter *pIter = 0; /* Iterator to read input data */
int nRem = pnRem ? *pnRem : 0; /* Output leaf pages left to write */
int nInput; /* Number of input segments */
Fts5SegWriter writer; /* Writer object */
@@ -3668,6 +4048,7 @@ static void fts5IndexMergeLevel(
Fts5Buffer term;
int bOldest; /* True if the output segment is the oldest */
int eDetail = p->pConfig->eDetail;
+ const int flags = FTS5INDEX_QUERY_NOOUTPUT;
assert( iLvlnLevel );
assert( pLvl->nMerge<=pLvl->nSeg );
@@ -3712,7 +4093,7 @@ static void fts5IndexMergeLevel(
bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2);
assert( iLvl>=0 );
- for(fts5MultiIterNew(p, pStruct, 0, 0, 0, 0, iLvl, nInput, &pIter);
+ for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, iLvl, nInput, &pIter);
fts5MultiIterEof(p, pIter)==0;
fts5MultiIterNext(p, pIter, 0, 0)
){
@@ -4157,275 +4538,28 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
return fts5IndexReturn(p);
}
-static void fts5PoslistCallback(
- Fts5Index *p,
- void *pContext,
- const u8 *pChunk, int nChunk
-){
- assert_nc( nChunk>=0 );
- if( nChunk>0 ){
- 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 */
-};
-
-typedef struct PoslistOffsetsCtx PoslistOffsetsCtx;
-struct PoslistOffsetsCtx {
- Fts5Buffer *pBuf; /* Append to this buffer */
- Fts5Colset *pColset; /* Restrict matches to this column */
- int iRead;
- int iWrite;
-};
-
-/*
-** 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 fts5PoslistOffsetsCallback(
- Fts5Index *p,
- void *pContext,
- const u8 *pChunk, int nChunk
-){
- PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext;
- assert_nc( nChunk>=0 );
- if( nChunk>0 ){
- int i = 0;
- while( iiRead - 2;
- pCtx->iRead = iVal;
- if( fts5IndexColsetTest(pCtx->pColset, iVal) ){
- fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite);
- pCtx->iWrite = iVal;
- }
- }
- }
-}
-
-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{
- if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){
- PoslistCallbackCtx sCtx;
- sCtx.pBuf = pBuf;
- sCtx.pColset = pColset;
- sCtx.eState = fts5IndexColsetTest(pColset, 0);
- assert( sCtx.eState==0 || sCtx.eState==1 );
- fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback);
- }else{
- PoslistOffsetsCtx sCtx;
- memset(&sCtx, 0, sizeof(sCtx));
- sCtx.pBuf = pBuf;
- sCtx.pColset = pColset;
- fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback);
- }
- }
- }
-}
-
-/*
-** 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 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);
- }
- if( iCol!=iCurrent ) return 0;
-
- /* 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, pBuf, iDelta);
- return 0;
}
-/*
-** Iterator pMulti currently points to a valid entry (not EOF). This
-** function appends the following to buffer pBuf:
-**
-** * The varint iDelta, and
-** * the position list that currently points to, including the size field.
-**
-** If argument pColset is NULL, then the position list is filtered according
-** to pColset before being appended to the buffer. If this means there are
-** no entries in the position list, nothing is appended to the buffer (not
-** even iDelta).
-**
-** If an error occurs, an error code is left in p->rc.
-*/
-static int fts5AppendPoslist(
+static void fts5AppendPoslist(
Fts5Index *p,
i64 iDelta,
- Fts5IndexIter *pMulti,
- Fts5Colset *pColset,
+ Fts5Iter *pMulti,
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) ){
- if( p->pConfig->eDetail==FTS5_DETAIL_FULL
- && 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]);
- if( nPos==0 ) return 1;
- }else{
- nPos = pSeg->nPos;
- }
- assert( nPos>0 );
- fts5BufferSafeAppendVarint(pBuf, iDelta);
- fts5BufferSafeAppendVarint(pBuf, nPos*2);
- fts5BufferSafeAppendBlob(pBuf, pPos, nPos);
- }else{
- int iSv1;
- int iSv2;
- int iData;
-
- /* Append iDelta */
- iSv1 = pBuf->n;
- fts5BufferSafeAppendVarint(pBuf, iDelta);
-
- /* WRITEPOSLISTSIZE */
- iSv2 = pBuf->n;
- fts5BufferSafeAppendVarint(pBuf, pSeg->nPos*2);
- iData = pBuf->n;
-
- 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);
- }
- }
- }
- }
- }
+ int nData = pMulti->base.nData;
+ assert( nData>0 );
+ if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nData+9+9) ){
+ fts5BufferSafeAppendVarint(pBuf, iDelta);
+ fts5BufferSafeAppendVarint(pBuf, nData*2);
+ fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData);
}
-
- return 0;
}
@@ -4569,28 +4703,30 @@ static void fts5MergePrefixLists(
i64 iLastRowid = 0;
Fts5DoclistIter i1;
Fts5DoclistIter i2;
- Fts5Buffer out;
- Fts5Buffer tmp;
- memset(&out, 0, sizeof(out));
- memset(&tmp, 0, sizeof(tmp));
+ Fts5Buffer out = {0, 0, 0};
+ Fts5Buffer tmp = {0, 0, 0};
- sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
+ if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n) ) return;
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, &tmp, i1.nPoslist + i2.nPoslist);
+ if( p->rc ) break;
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ assert( iPos1>=0 && iPos2>=0 );
- while( p->rc==SQLITE_OK && (iPos1>=0 || iPos2>=0) ){
- i64 iNew;
- if( iPos2<0 || (iPos1>=0 && iPos1=0 && iPos2>=0 ){
+ while( 1 ){
+ if( iPos1rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew);
+ }
+
+ if( iPos1>=0 ){
+ if( iPos1!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
}
+ fts5BufferSafeAppendBlob(&tmp, &a1[iOff1], i1.nPoslist-iOff1);
+ }else{
+ assert( iPos2>=0 && iPos2!=iPrev );
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ fts5BufferSafeAppendBlob(&tmp, &a2[iOff2], i2.nPoslist-iOff2);
}
/* WRITEPOSLISTSIZE */
@@ -4630,9 +4788,19 @@ static void fts5MergePrefixLists(
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
fts5DoclistIterNext(&i1);
fts5DoclistIterNext(&i2);
+ if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
}
}
+ if( i1.aPoslist ){
+ fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
+ fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);
+ }
+ else if( i2.aPoslist ){
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
+ }
+
fts5BufferSet(&p->rc, p1, out.n, out.p);
fts5BufferFree(&tmp);
fts5BufferFree(&out);
@@ -4645,14 +4813,14 @@ static void fts5SetupPrefixIter(
const u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
- Fts5IndexIter **ppIter /* OUT: New iterator */
+ Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
const int nBuf = 32;
void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
- int (*xAppend)(Fts5Index*, i64, Fts5IndexIter*, Fts5Colset*, Fts5Buffer*);
+ void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
xMerge = fts5MergeRowidLists;
xAppend = fts5AppendRowid;
@@ -4665,28 +4833,36 @@ static void fts5SetupPrefixIter(
pStruct = fts5StructureRead(p);
if( aBuf && pStruct ){
- const int flags = FTS5INDEX_QUERY_SCAN;
+ const int flags = FTS5INDEX_QUERY_SCAN
+ | FTS5INDEX_QUERY_SKIPEMPTY
+ | FTS5INDEX_QUERY_NOOUTPUT;
int i;
i64 iLastRowid = 0;
- Fts5IndexIter *p1 = 0; /* Iterator used to gather data from index */
+ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
Fts5Data *pData;
Fts5Buffer doclist;
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
- for(fts5MultiIterNew(p, pStruct, 1, flags, pToken, nToken, -1, 0, &p1);
+ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
+ fts5IterSetOutputCb(&p->rc, p1);
+ for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
){
- i64 iRowid = fts5MultiIterRowid(p1);
- int nTerm;
- const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm);
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
+ int nTerm = pSeg->term.n;
+ const u8 *pTerm = pSeg->term.p;
+ p1->xSetOutputs(p1, pSeg);
+
assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
if( bNewTerm ){
if( nTerm0 && iRowid<=iLastRowid ){
+ if( p1->base.nData==0 ) continue;
+
+ if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
assert( ibase.iRowid-iLastRowid, p1, &doclist);
+ iLastRowid = p1->base.iRowid;
}
for(i=0; irc==SQLITE_OK );
+ /* assert( p->rc==SQLITE_OK ); */
return SQLITE_OK;
}
@@ -4944,22 +5119,27 @@ int sqlite3Fts5IndexQuery(
Fts5IndexIter **ppIter /* OUT: New iterator object */
){
Fts5Config *pConfig = p->pConfig;
- Fts5IndexIter *pRet = 0;
- int iIdx = 0;
+ Fts5Iter *pRet = 0;
Fts5Buffer buf = {0, 0, 0};
/* If the QUERY_SCAN flag is set, all other flags must be clear. */
assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
+ int iIdx = 0; /* Index to search */
memcpy(&buf.p[1], pToken, nToken);
-#ifdef SQLITE_DEBUG
- /* If the QUERY_TEST_NOIDX flag was specified, then this must be a
+ /* Figure out which index to search and set iIdx accordingly. If this
+ ** is a prefix query for which there is no prefix index, set iIdx to
+ ** greater than pConfig->nPrefix to indicate that the query will be
+ ** satisfied by scanning multiple terms in the main index.
+ **
+ ** If the QUERY_TEST_NOIDX flag was specified, then this must be a
** prefix-query. Instead of using a prefix-index (if one exists),
** evaluate the prefix query using the main FTS index. This is used
** for internal sanity checking by the integrity-check in debug
** mode only. */
+#ifdef SQLITE_DEBUG
if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX) ){
assert( flags & FTS5INDEX_QUERY_PREFIX );
iIdx = 1+pConfig->nPrefix;
@@ -4973,24 +5153,35 @@ int sqlite3Fts5IndexQuery(
}
if( iIdx<=pConfig->nPrefix ){
+ /* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
if( pStruct ){
- fts5MultiIterNew(p, pStruct, 1, flags, buf.p, nToken+1, -1, 0, &pRet);
+ fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
+ pColset, buf.p, nToken+1, -1, 0, &pRet
+ );
fts5StructureRelease(pStruct);
}
}else{
+ /* Scan multiple terms in the main index */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
buf.p[0] = FTS5_MAIN_PREFIX;
fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
+ assert( pRet->pColset==0 );
+ fts5IterSetOutputCb(&p->rc, pRet);
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
+ if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
+ }
}
if( p->rc ){
- sqlite3Fts5IterClose(pRet);
+ sqlite3Fts5IterClose(&pRet->base);
pRet = 0;
fts5CloseReader(p);
}
- *ppIter = pRet;
+
+ *ppIter = &pRet->base;
sqlite3Fts5BufferFree(&buf);
}
return fts5IndexReturn(p);
@@ -4999,15 +5190,11 @@ int sqlite3Fts5IndexQuery(
/*
** Return true if the iterator passed as the only argument is at EOF.
*/
-int sqlite3Fts5IterEof(Fts5IndexIter *pIter){
- assert( pIter->pIndex->rc==SQLITE_OK );
- return pIter->bEof;
-}
-
/*
** Move to the next matching rowid.
*/
-int sqlite3Fts5IterNext(Fts5IndexIter *pIter){
+int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
return fts5IndexReturn(pIter->pIndex);
@@ -5016,7 +5203,8 @@ int sqlite3Fts5IterNext(Fts5IndexIter *pIter){
/*
** Move to the next matching term/rowid. Used by the fts5vocab module.
*/
-int sqlite3Fts5IterNextScan(Fts5IndexIter *pIter){
+int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *p = pIter->pIndex;
assert( pIter->pIndex->rc==SQLITE_OK );
@@ -5027,7 +5215,7 @@ int sqlite3Fts5IterNextScan(Fts5IndexIter *pIter){
if( pSeg->pLeaf && pSeg->term.p[0]!=FTS5_MAIN_PREFIX ){
fts5DataRelease(pSeg->pLeaf);
pSeg->pLeaf = 0;
- pIter->bEof = 1;
+ pIter->base.bEof = 1;
}
}
@@ -5039,129 +5227,28 @@ int sqlite3Fts5IterNextScan(Fts5IndexIter *pIter){
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
-int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIter, i64 iMatch){
+int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
return fts5IndexReturn(pIter->pIndex);
}
-/*
-** Return the current rowid.
-*/
-i64 sqlite3Fts5IterRowid(Fts5IndexIter *pIter){
- return fts5MultiIterRowid(pIter);
-}
-
/*
** Return the current term.
*/
-const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIter, int *pn){
+const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
int n;
- const char *z = (const char*)fts5MultiIterTerm(pIter, &n);
+ const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
*pn = n-1;
return &z[1];
}
-
-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
-** in bytes before returning.
-**
-** The returned position list does not include the "number of bytes" varint
-** field that starts the position list on disk.
-*/
-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 */
-){
- Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
- int eDetail = pIter->pIndex->pConfig->eDetail;
-
- assert( pIter->pIndex->rc==SQLITE_OK );
- *piRowid = pSeg->iRowid;
- if( eDetail==FTS5_DETAIL_NONE ){
- *pn = pSeg->nPos;
- }else
- if( eDetail==FTS5_DETAIL_FULL
- && 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, pColset, &pIter->poslist);
- if( eDetail==FTS5_DETAIL_FULL ){
- *pp = pIter->poslist.p;
- }
- *pn = pIter->poslist.n;
- }
- return fts5IndexReturn(pIter->pIndex);
-}
-
-int sqlite3Fts5IterCollist(
- Fts5IndexIter *pIter,
- const u8 **pp, /* OUT: Pointer to position-list data */
- int *pn /* OUT: Size of position-list in bytes */
-){
- assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
- *pp = pIter->poslist.p;
- *pn = pIter->poslist.n;
- return SQLITE_OK;
-}
-
-/*
-** This function is similar to sqlite3Fts5IterPoslist(), except that it
-** copies the position list into the buffer supplied as the second
-** argument.
-*/
-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);
- fts5SegiterPoslist(p, pSeg, 0, pBuf);
- return fts5IndexReturn(p);
-}
-
/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
-void sqlite3Fts5IterClose(Fts5IndexIter *pIter){
- if( pIter ){
+void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
+ if( pIndexIter ){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *pIndex = pIter->pIndex;
fts5MultiIterFree(pIter->pIndex, pIter);
fts5CloseReader(pIndex);
@@ -5328,35 +5415,30 @@ static int fts5QueryCksum(
){
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
- Fts5IndexIter *pIdxIter = 0;
- Fts5Buffer buf = {0, 0, 0};
- int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIdxIter);
+ Fts5IndexIter *pIter = 0;
+ int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
- while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIdxIter) ){
- i64 rowid = sqlite3Fts5IterRowid(pIdxIter);
+ while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){
+ i64 rowid = pIter->iRowid;
if( eDetail==FTS5_DETAIL_NONE ){
cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
}else{
- rc = sqlite3Fts5IterPoslistBuffer(pIdxIter, &buf);
- if( rc==SQLITE_OK ){
- Fts5PoslistReader sReader;
- for(sqlite3Fts5PoslistReaderInit(buf.p, buf.n, &sReader);
- sReader.bEof==0;
- sqlite3Fts5PoslistReaderNext(&sReader)
- ){
- int iCol = FTS5_POS2COLUMN(sReader.iPos);
- int iOff = FTS5_POS2OFFSET(sReader.iPos);
- cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n);
- }
+ Fts5PoslistReader sReader;
+ for(sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &sReader);
+ sReader.bEof==0;
+ sqlite3Fts5PoslistReaderNext(&sReader)
+ ){
+ int iCol = FTS5_POS2COLUMN(sReader.iPos);
+ int iOff = FTS5_POS2OFFSET(sReader.iPos);
+ cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n);
}
}
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IterNext(pIdxIter);
+ rc = sqlite3Fts5IterNext(pIter);
}
}
- sqlite3Fts5IterClose(pIdxIter);
- fts5BufferFree(&buf);
+ sqlite3Fts5IterClose(pIter);
*pCksum = cksum;
return rc;
@@ -5661,7 +5743,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
int eDetail = p->pConfig->eDetail;
u64 cksum2 = 0; /* Checksum based on contents of indexes */
Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
- Fts5IndexIter *pIter; /* Used to iterate through entire index */
+ Fts5Iter *pIter; /* Used to iterate through entire index */
Fts5Structure *pStruct; /* Index structure */
#ifdef SQLITE_DEBUG
@@ -5669,6 +5751,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
u64 cksum3 = 0; /* Checksum based on contents of indexes */
Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
#endif
+ const int flags = FTS5INDEX_QUERY_NOOUTPUT;
/* Load the FTS index structure */
pStruct = fts5StructureRead(p);
@@ -5697,7 +5780,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
** same term is performed. cksum3 is calculated based on the entries
** extracted by these queries.
*/
- for(fts5MultiIterNew(p, pStruct, 0, 0, 0, 0, -1, 0, &pIter);
+ for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, -1, 0, &pIter);
fts5MultiIterEof(p, pIter)==0;
fts5MultiIterNext(p, pIter, 0, 0)
){
diff --git a/ext/fts5/fts5_main.c b/ext/fts5/fts5_main.c
index 7447f9b5e1..4b798314c0 100644
--- a/ext/fts5/fts5_main.c
+++ b/ext/fts5/fts5_main.c
@@ -220,10 +220,10 @@ struct Fts5Cursor {
/*
** Values for Fts5Cursor.csrflags
*/
-#define FTS5CSR_REQUIRE_CONTENT 0x01
-#define FTS5CSR_REQUIRE_DOCSIZE 0x02
-#define FTS5CSR_REQUIRE_INST 0x04
-#define FTS5CSR_EOF 0x08
+#define FTS5CSR_EOF 0x01
+#define FTS5CSR_REQUIRE_CONTENT 0x02
+#define FTS5CSR_REQUIRE_DOCSIZE 0x04
+#define FTS5CSR_REQUIRE_INST 0x08
#define FTS5CSR_FREE_ZRANK 0x10
#define FTS5CSR_REQUIRE_RESEEK 0x20
#define FTS5CSR_REQUIRE_POSLIST 0x40
@@ -538,7 +538,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
for(i=0; inConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
int j;
- for(j=0; j<(int)ArraySize(aConstraint); j++){
+ for(j=0; jiColumn==aColMap[pC->iCol] && p->op & pC->op ){
if( p->usable ){
@@ -585,7 +585,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
/* Assign argvIndex values to each constraint in use. */
iNext = 1;
- for(i=0; i<(int)ArraySize(aConstraint); i++){
+ for(i=0; iiConsIndex>=0 ){
pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
@@ -778,7 +778,7 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc);
- if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
+ if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
*pbSkip = 1;
}
@@ -786,6 +786,7 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
fts5CsrNewrow(pCsr);
if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
CsrFlagSet(pCsr, FTS5CSR_EOF);
+ *pbSkip = 1;
}
}
return rc;
@@ -802,24 +803,24 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
*/
static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- int rc = SQLITE_OK;
+ int rc;
assert( (pCsr->ePlan<3)==
(pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
);
+ assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
if( pCsr->ePlan<3 ){
int bSkip = 0;
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
- if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
- CsrFlagSet(pCsr, FTS5CSR_EOF);
- }
+ CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
fts5CsrNewrow(pCsr);
}else{
switch( pCsr->ePlan ){
case FTS5_PLAN_SPECIAL: {
CsrFlagSet(pCsr, FTS5CSR_EOF);
+ rc = SQLITE_OK;
break;
}
diff --git a/ext/fts5/fts5_storage.c b/ext/fts5/fts5_storage.c
index 56383619d1..da9524dea3 100644
--- a/ext/fts5/fts5_storage.c
+++ b/ext/fts5/fts5_storage.c
@@ -338,7 +338,7 @@ int sqlite3Fts5StorageClose(Fts5Storage *p){
int i;
/* Finalize all SQL statements */
- for(i=0; i<(int)ArraySize(p->aStmt); i++){
+ for(i=0; iaStmt); i++){
sqlite3_finalize(p->aStmt[i]);
}
diff --git a/ext/fts5/fts5_tokenize.c b/ext/fts5/fts5_tokenize.c
index e60183c095..790adaae47 100644
--- a/ext/fts5/fts5_tokenize.c
+++ b/ext/fts5/fts5_tokenize.c
@@ -1220,7 +1220,7 @@ int sqlite3Fts5TokenizerInit(fts5_api *pApi){
int rc = SQLITE_OK; /* Return code */
int i; /* To iterate through builtin functions */
- for(i=0; rc==SQLITE_OK && i<(int)ArraySize(aBuiltin); i++){
+ for(i=0; rc==SQLITE_OK && ixCreateTokenizer(pApi,
aBuiltin[i].zName,
(void*)pApi,
diff --git a/ext/fts5/fts5_varint.c b/ext/fts5/fts5_varint.c
index 21858506ac..bb212ab5a8 100644
--- a/ext/fts5/fts5_varint.c
+++ b/ext/fts5/fts5_varint.c
@@ -333,7 +333,10 @@ int sqlite3Fts5PutVarint(unsigned char *p, u64 v){
int sqlite3Fts5GetVarintLen(u32 iVal){
+#if 0
if( iVal<(1 << 7 ) ) return 1;
+#endif
+ assert( iVal>=(1 << 7) );
if( iVal<(1 << 14) ) return 2;
if( iVal<(1 << 21) ) return 3;
if( iVal<(1 << 28) ) return 4;
diff --git a/ext/fts5/fts5_vocab.c b/ext/fts5/fts5_vocab.c
index ddc5576c5d..b17e8974fb 100644
--- a/ext/fts5/fts5_vocab.c
+++ b/ext/fts5/fts5_vocab.c
@@ -184,7 +184,7 @@ static int fts5VocabInitVtab(
rc = fts5VocabTableType(zType, pzErr, &eType);
if( rc==SQLITE_OK ){
- assert( eType>=0 && eType=0 && eTypeeType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
while( rc==SQLITE_OK ){
- i64 dummy;
const u8 *pPos; int nPos; /* Position list */
i64 iPos = 0; /* 64-bit position read from poslist */
int iOff = 0; /* Current offset within position list */
+ pPos = pCsr->pIter->pData;
+ nPos = pCsr->pIter->nData;
switch( pCsr->pConfig->eDetail ){
case FTS5_DETAIL_FULL:
- 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) ){
- pCsr->aCnt[0]++;
- }
- pCsr->aDoc[0]++;
- }else{
- int iCol = -1;
- while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
- int ii = FTS5_POS2COLUMN(iPos);
- pCsr->aCnt[ii]++;
- if( iCol!=ii ){
- if( ii>=nCol ){
- rc = FTS5_CORRUPT;
- break;
- }
- pCsr->aDoc[ii]++;
- iCol = ii;
+ pPos = pCsr->pIter->pData;
+ nPos = pCsr->pIter->nData;
+ if( pTab->eType==FTS5_VOCAB_ROW ){
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
+ pCsr->aCnt[0]++;
+ }
+ pCsr->aDoc[0]++;
+ }else{
+ int iCol = -1;
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
+ int ii = FTS5_POS2COLUMN(iPos);
+ pCsr->aCnt[ii]++;
+ if( iCol!=ii ){
+ if( ii>=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
}
+ pCsr->aDoc[ii]++;
+ iCol = ii;
}
}
}
@@ -443,19 +443,14 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
if( pTab->eType==FTS5_VOCAB_ROW ){
pCsr->aDoc[0]++;
}else{
- Fts5Buffer buf = {0, 0, 0};
- rc = sqlite3Fts5IterPoslistBuffer(pCsr->pIter, &buf);
- if( rc==SQLITE_OK ){
- while( 0==sqlite3Fts5PoslistNext64(buf.p, buf.n, &iOff,&iPos) ){
- assert_nc( iPos>=0 && iPos=nCol ){
- rc = FTS5_CORRUPT;
- break;
- }
- pCsr->aDoc[iPos]++;
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
+ assert_nc( iPos>=0 && iPos=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
}
+ pCsr->aDoc[iPos]++;
}
- sqlite3Fts5BufferFree(&buf);
}
break;
diff --git a/ext/fts5/test/fts5_common.tcl b/ext/fts5/test/fts5_common.tcl
index e7c61af00c..cf688dcc0d 100644
--- a/ext/fts5/test/fts5_common.tcl
+++ b/ext/fts5/test/fts5_common.tcl
@@ -48,7 +48,8 @@ proc fts5_test_poslist2 {cmd} {
}
}
- set res
+ #set res
+ sort_poslist $res
}
proc fts5_test_collist {cmd} {
diff --git a/ext/fts5/test/fts5ac.test b/ext/fts5/test/fts5ac.test
index 00b1328867..61b3230772 100644
--- a/ext/fts5/test/fts5ac.test
+++ b/ext/fts5/test/fts5ac.test
@@ -158,8 +158,8 @@ foreach {tn2 sql} {
#-------------------------------------------------------------------------
#
foreach {tn expr} {
- 1.2 "a OR b"
1.1 "a AND b"
+ 1.2 "a OR b"
1.3 "o"
1.4 "b q"
1.5 "e a e"
@@ -250,7 +250,6 @@ foreach {tn2 sql} {
FROM xx WHERE xx match $expr
} $res
-
set res [fts5_query_data $expr xx DESC]
do_execsql_test 1.$tn2.$tn.[llength $res].desc {
SELECT rowid, fts5_test_poslist(xx), fts5_test_collist(xx)
diff --git a/ext/fts5/test/fts5ad.test b/ext/fts5/test/fts5ad.test
index c6b09d8121..974aa781aa 100644
--- a/ext/fts5/test/fts5ad.test
+++ b/ext/fts5/test/fts5ad.test
@@ -11,6 +11,8 @@
# This file implements regression tests for SQLite library. The
# focus of this script is testing the FTS5 module.
#
+# More specifically, the focus is on testing prefix queries, both with and
+# without prefix indexes.
#
source [file join [file dirname [info script]] fts5_common.tcl]
diff --git a/ext/fts5/test/fts5config.test b/ext/fts5/test/fts5config.test
index 223e504a65..c30a597242 100644
--- a/ext/fts5/test/fts5config.test
+++ b/ext/fts5/test/fts5config.test
@@ -243,5 +243,9 @@ foreach {tn opt} {
do_catchsql_test 11.$tn "CREATE VIRTUAL TABLE f1 USING fts5(x, $opt)" $res
}
+do_catchsql_test 12.1 {
+ INSERT INTO t1(t1, rank) VALUES('rank', NULL);;
+} {1 {SQL logic error or missing database}}
+
finish_test
diff --git a/ext/fts5/test/fts5detail.test b/ext/fts5/test/fts5detail.test
index 5bdd14424e..58fda3e995 100644
--- a/ext/fts5/test/fts5detail.test
+++ b/ext/fts5/test/fts5detail.test
@@ -85,6 +85,10 @@ do_execsql_test 2.2 {
SELECT fts5_test_poslist(t2) FROM t2('aa');
} {0.0.0}
+do_execsql_test 2.3 {
+ SELECT fts5_test_collist(t2) FROM t2('aa');
+} {0.0}
+
set ::pc 0
#puts [nearset {{ax bx cx}} -pc ::pc -near 10 -- b*]
#exit
diff --git a/ext/fts5/test/fts5fault1.test b/ext/fts5/test/fts5fault1.test
index 429ea39af0..9d63a1175f 100644
--- a/ext/fts5/test/fts5fault1.test
+++ b/ext/fts5/test/fts5fault1.test
@@ -72,7 +72,7 @@ do_faultsim_test 3 -prep {
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t2 USING fts5(a, b);
- INSERT INTO t2 VALUES('m f a jj th q jr ar', 'hj n h h sg j i m');
+ INSERT INTO t2 VALUES('m f a jj th q gi ar', 'hj n h h sg j i m');
INSERT INTO t2 VALUES('nr s t g od j kf h', 'sb h aq rg op rb n nl');
INSERT INTO t2 VALUES('do h h pb p p q fr', 'c rj qs or cr a l i');
INSERT INTO t2 VALUES('lk gp t i lq mq qm p', 'h mr g f op ld aj h');
@@ -95,6 +95,7 @@ foreach {tn expr res} {
7 { NEAR(r a, 5) } {9}
8 { m* f* } {1 4 6 8 9 10}
9 { m* + f* } {1 8}
+ 10 { c NOT p } {5 6 7 10}
} {
do_faultsim_test 4.$tn -prep {
faultsim_restore_and_reopen
diff --git a/ext/fts5/test/fts5fault2.test b/ext/fts5/test/fts5fault2.test
index ef1df8826a..43c7c7a3dd 100644
--- a/ext/fts5/test/fts5fault2.test
+++ b/ext/fts5/test/fts5fault2.test
@@ -16,7 +16,7 @@ source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5fault2
-# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
diff --git a/ext/fts5/test/fts5fault7.test b/ext/fts5/test/fts5fault7.test
index bfe6420615..a35b19ade2 100644
--- a/ext/fts5/test/fts5fault7.test
+++ b/ext/fts5/test/fts5fault7.test
@@ -99,7 +99,7 @@ do_execsql_test 2.0 {
}
faultsim_save_and_close
-do_faultsim_test 2 -faults oom-* -prep {
+do_faultsim_test 2.1 -faults oom-* -prep {
faultsim_restore_and_reopen
} -body {
db eval { UPDATE OR REPLACE xy SET rowid=3 WHERE rowid = 2 }
@@ -107,6 +107,13 @@ do_faultsim_test 2 -faults oom-* -prep {
faultsim_test_result {0 {}}
}
+# Test fault-injection when an empty expression is parsed.
+#
+do_faultsim_test 2.2 -faults oom-* -body {
+ db eval { SELECT * FROM xy('""') }
+} -test {
+ faultsim_test_result {0 {}}
+}
finish_test
diff --git a/ext/fts5/test/fts5fault8.test b/ext/fts5/test/fts5fault8.test
index d93066c6c0..ae5849495b 100644
--- a/ext/fts5/test/fts5fault8.test
+++ b/ext/fts5/test/fts5fault8.test
@@ -24,32 +24,37 @@ ifcapable !fts5 {
foreach_detail_mode $testprefix {
-fts5_aux_test_functions db
-do_execsql_test 1.0 {
- CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
- INSERT INTO t1 VALUES('a b c d', '1 2 3 4');
- INSERT INTO t1 VALUES('a b a b', NULL);
- INSERT INTO t1 VALUES(NULL, '1 2 1 2');
-}
-
-do_faultsim_test 1 -faults oom-* -body {
- execsql {
- SELECT rowid, fts5_test_poslist(t1) FROM t1 WHERE t1 MATCH 'b OR 2'
+ fts5_aux_test_functions db
+ do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
+ INSERT INTO t1 VALUES('a b c d', '1 2 3 4');
+ INSERT INTO t1 VALUES('a b a b', NULL);
+ INSERT INTO t1 VALUES(NULL, '1 2 1 2');
}
-} -test {
- faultsim_test_result {0 {1 {0.0.1 1.1.1} 2 {0.0.1 0.0.3} 3 {1.1.1 1.1.3}}} \
- {1 SQLITE_NOMEM}
-}
-
-do_faultsim_test 2 -faults oom-* -body {
- execsql {
- INSERT INTO t1(t1) VALUES('integrity-check');
+
+ do_faultsim_test 1 -faults oom-* -body {
+ execsql {
+ SELECT rowid, fts5_test_poslist(t1) FROM t1 WHERE t1 MATCH 'b OR 2'
+ }
+ } -test {
+ faultsim_test_result {0 {1 {0.0.1 1.1.1} 2 {0.0.1 0.0.3} 3 {1.1.1 1.1.3}}} \
+ {1 SQLITE_NOMEM}
+ }
+
+ do_faultsim_test 2 -faults oom-* -body {
+ execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
+ } -test {
+ faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
-} -test {
- faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
-}
-}
+ if {[detail_is_none]==0} {
+ do_faultsim_test 3 -faults oom-* -body {
+ execsql { SELECT rowid FROM t1('b:2') }
+ } -test {
+ faultsim_test_result {0 {1 3}} {1 SQLITE_NOMEM}
+ }
+ }
+} ;# foreach_detail_mode...
finish_test
diff --git a/ext/fts5/test/fts5faultA.test b/ext/fts5/test/fts5faultA.test
new file mode 100644
index 0000000000..817ccb4ee8
--- /dev/null
+++ b/ext/fts5/test/fts5faultA.test
@@ -0,0 +1,64 @@
+# 2016 February 2
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+#
+# This file is focused on OOM errors.
+#
+
+source [file join [file dirname [info script]] fts5_common.tcl]
+source $testdir/malloc_common.tcl
+set testprefix fts5faultA
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+foreach_detail_mode $testprefix {
+ do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE o1 USING fts5(a, detail=%DETAIL%);
+ INSERT INTO o1(o1, rank) VALUES('pgsz', 32);
+
+ WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<300 )
+ INSERT INTO o1 SELECT 'A B C' FROM s;
+
+ INSERT INTO o1 VALUES('A X C');
+
+ WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<300 )
+ INSERT INTO o1 SELECT 'A B C' FROM s;
+ }
+
+ do_faultsim_test 1 -faults oom* -prep {
+ sqlite3 db test.db
+ } -body {
+ execsql { SELECT rowid FROM o1('a NOT b') }
+ } -test {
+ faultsim_test_result {0 301}
+ }
+}
+
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE o2 USING fts5(a);
+
+ INSERT INTO o2 VALUES('A B C');
+ WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<300 )
+ INSERT INTO o2 SELECT group_concat('A B C ') FROM s;
+}
+
+do_faultsim_test 2 -faults oom* -prep {
+ sqlite3 db test.db
+} -body {
+ execsql { SELECT rowid FROM o2('a+b+c NOT xyz') }
+} -test {
+ faultsim_test_result {0 {1 2}}
+}
+finish_test
+
diff --git a/ext/fts5/test/fts5simple.test b/ext/fts5/test/fts5simple.test
index e29c13117c..b6563fd9d0 100644
--- a/ext/fts5/test/fts5simple.test
+++ b/ext/fts5/test/fts5simple.test
@@ -19,7 +19,7 @@ ifcapable !fts5 {
return
}
- if 1 {
+if 1 {
#-------------------------------------------------------------------------
#
@@ -363,8 +363,6 @@ do_execsql_test 15.1 {
INSERT INTO x2(x2) VALUES('integrity-check');
}
-}
-
#-------------------------------------------------------------------------
foreach_detail_mode $testprefix {
reset_db
@@ -382,5 +380,24 @@ foreach_detail_mode $testprefix {
} {{0.0.0 1.0.2}}
}
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 17.0 {
+ CREATE VIRTUAL TABLE x3 USING fts5(x);
+ INSERT INTO x3 VALUES('a b c');
+}
+
+do_execsql_test 17.1 {
+ SELECT rowid FROM x3('b AND d');
+}
+
+#-------------------------------------------------------------------------
+do_execsql_test 18.1 {
+ CREATE VIRTUAL TABLE x4 USING fts5(x);
+ SELECT rowid FROM x4('""');
+}
+
finish_test
diff --git a/ext/fts5/test/fts5simple3.test b/ext/fts5/test/fts5simple3.test
new file mode 100644
index 0000000000..b6922f84dd
--- /dev/null
+++ b/ext/fts5/test/fts5simple3.test
@@ -0,0 +1,85 @@
+# 2015 September 05
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+#
+
+source [file join [file dirname [info script]] fts5_common.tcl]
+set testprefix fts5simple3
+
+# If SQLITE_ENABLE_FTS5 is defined, omit this file.
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+fts5_aux_test_functions db
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, detail=col);
+ INSERT INTO t1 VALUES('a', 'b', 'c');
+ INSERT INTO t1 VALUES('x', 'x', 'x');
+}
+
+do_execsql_test 1.1 {
+ SELECT rowid, fts5_test_collist(t1) FROM t1('a:a');
+} {1 0.0}
+
+do_execsql_test 1.2 {
+ SELECT rowid, fts5_test_collist(t1) FROM t1('b:x');
+} {2 0.1}
+
+do_execsql_test 1.3 {
+ SELECT rowid, fts5_test_collist(t1) FROM t1('b:a');
+} {}
+
+#-------------------------------------------------------------------------
+# Create detail=col and detail=full tables with 998 columns.
+#
+foreach_detail_mode $testprefix {
+ if {[detail_is_none]} continue
+
+ do_test 2.1 {
+ execsql { DROP TABLE IF EXISTS t2 }
+ set cols [list]
+ set vals [list]
+ for {set i 1} {$i <= 998} {incr i} {
+ lappend cols "c$i"
+ lappend vals "'val$i'"
+ }
+ execsql "CREATE VIRTUAL TABLE t2 USING fts5(detail=%DETAIL%,[join $cols ,])"
+ } {}
+
+ do_test 2.2 {
+ execsql "INSERT INTO t2 VALUES([join $vals ,])"
+ } {}
+
+ foreach {tn q res} {
+ 1 { c1:val1 } 1
+ 2 { c300:val300 } 1
+ 3 { c300:val1 } {}
+ 4 { c1:val300 } {}
+ } {
+ do_execsql_test 2.3.$tn {
+ SELECT rowid FROM t2($q)
+ } $res
+ }
+}
+
+do_execsql_test 3.0 {
+ CREATE VIRTUAL TABLE x3 USING fts5(one);
+ INSERT INTO x3 VALUES('a b c');
+ INSERT INTO x3 VALUES('c b a');
+ INSERT INTO x3 VALUES('o t t');
+ SELECT * FROM x3('x OR y OR z');
+}
+
+
+finish_test
+
diff --git a/ext/fts5/test/fts5synonym2.test b/ext/fts5/test/fts5synonym2.test
index dddaa17af8..7e92822c33 100644
--- a/ext/fts5/test/fts5synonym2.test
+++ b/ext/fts5/test/fts5synonym2.test
@@ -27,6 +27,21 @@ foreach_detail_mode $testprefix {
fts5_tclnum_register db
fts5_aux_test_functions db
+proc fts5_test_bothlist {cmd} {
+
+ for {set i 0} {$i < [$cmd xPhraseCount]} {incr i} {
+ set bFirst 1
+ $cmd xPhraseColumnForeach $i c {
+ lappend CL $i.$c
+ if {$bFirst} { $cmd xPhraseForeach $i c o { lappend PL $i.$c.$o } }
+ set bFirst 0
+ }
+ }
+
+ list [sort_poslist $PL] $CL
+}
+sqlite3_fts5_create_function db fts5_test_bothlist fts5_test_bothlist
+
proc fts5_rowid {cmd} { expr [$cmd xColumnText -1] }
sqlite3_fts5_create_function db fts5_rowid fts5_rowid
@@ -89,6 +104,8 @@ do_execsql_test 1.$tok.0.2 {
}
foreach {tn expr} {
+ 2.1 "one OR two OR three OR four"
+
1.1 "one" 1.2 "two" 1.3 "three" 1.4 "four"
1.5 "v" 1.6 "vi" 1.7 "vii" 1.8 "viii"
1.9 "9" 1.10 "0" 1.11 "1" 1.12 "2"
@@ -113,13 +130,31 @@ foreach {tn expr} {
set res [fts5_query_data $expr ss ASC ::tclnum_syn]
do_execsql_test 1.$tok.$tn.[llength $res].asc.1 {
- SELECT rowid, fts5_test_poslist(ss), fts5_test_collist(ss) FROM ss($expr)
+ SELECT rowid, fts5_test_poslist2(ss), fts5_test_collist(ss) FROM ss($expr)
} $res
do_execsql_test 1.$tok.$tn.[llength $res].asc.2 {
SELECT rowid, fts5_test_poslist(ss), fts5_test_collist(ss) FROM ss($expr)
+ } $res
+
+ do_execsql_test 1.$tok.$tn.[llength $res].asc.2 {
+ SELECT rowid, fts5_test_poslist2(ss), fts5_test_collist(ss) FROM ss($expr)
ORDER BY rank ASC
} $res
+
+ set res2 [list]
+ foreach {a b c} $res { lappend res2 $a $c $b }
+ do_execsql_test 1.$tok.$tn.[llength $res].asc.3 {
+ SELECT rowid, fts5_test_collist(ss), fts5_test_poslist2(ss) FROM ss($expr)
+ } $res2
+
+ set res3 [list]
+ foreach {a b c} $res { lappend res3 $a [list $b $c] }
+ do_execsql_test 1.$tok.$tn.[llength $res].asc.3 {
+ SELECT rowid, fts5_test_bothlist(ss) FROM ss($expr)
+ } $res3
+
+
}
}
diff --git a/ext/fts5/tool/fts5speed.tcl b/ext/fts5/tool/fts5speed.tcl
index 1b060ea759..0f38638c26 100644
--- a/ext/fts5/tool/fts5speed.tcl
+++ b/ext/fts5/tool/fts5speed.tcl
@@ -11,6 +11,11 @@ set Q {
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'c:t*'"}
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'a:t* OR b:t* OR c:t* OR d:t* OR e:t* OR f:t* OR g:t*'"}
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'a:t*'"}
+ {2 "SELECT count(*) FROM t1 WHERE t1 MATCH 'c:the'"}
+
+ {2 "SELECT count(*) FROM t1 WHERE t1 MATCH 'd:holmes OR e:holmes OR f:holmes OR g:holmes'" }
+ {2 "SELECT count(*) FROM t1 WHERE t1 MATCH 'd:holmes AND e:holmes AND f:holmes AND g:holmes'" }
+ {4 "SELECT count(*) FROM t1 WHERE t1 MATCH 'd:holmes NOT e:holmes'" }
}
proc usage {} {
diff --git a/ext/fts5/tool/fts5txt2db.tcl b/ext/fts5/tool/fts5txt2db.tcl
index c22c5dae72..d5df971d4d 100644
--- a/ext/fts5/tool/fts5txt2db.tcl
+++ b/ext/fts5/tool/fts5txt2db.tcl
@@ -1,80 +1,180 @@
-
-
-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
- }
+##########################################################################
+# 2016 Jan 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.
+#
+proc process_cmdline {} {
+ cmdline::process ::A $::argv {
+ {fts5 "use fts5 (this is the default)"}
+ {fts4 "use fts4"}
+ {colsize "10 10 10" "list of column sizes"}
+ {tblname "t1" "table name to create"}
+ {detail "full" "Fts5 detail mode to use"}
+ {repeat 1 "Load each file this many times"}
+ {prefix "" "Fts prefix= option"}
+ database
+ file...
+ } {
+ 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.
}
}
-if {$i > [llength $argv]-2} usage
-set O(db) [lindex $argv $i]
-set O(files) [lrange $argv [expr $i+1] end]
+###########################################################################
+###########################################################################
+# Command line options processor. This is generic code that can be copied
+# between scripts.
+#
+namespace eval cmdline {
+ proc cmdline_error {O E {msg ""}} {
+ if {$msg != ""} {
+ puts stderr "Error: $msg"
+ puts stderr ""
+ }
+
+ set L [list]
+ foreach o $O {
+ if {[llength $o]==1} {
+ lappend L [string toupper $o]
+ }
+ }
+
+ puts stderr "Usage: $::argv0 ?SWITCHES? $L"
+ puts stderr ""
+ puts stderr "Switches are:"
+ foreach o $O {
+ if {[llength $o]==3} {
+ foreach {a b c} $o {}
+ puts stderr [format " -%-15s %s (default \"%s\")" "$a VAL" $c $b]
+ } elseif {[llength $o]==2} {
+ foreach {a b} $o {}
+ puts stderr [format " -%-15s %s" $a $b]
+ }
+ }
+ puts stderr ""
+ puts stderr $E
+ exit -1
+ }
+
+ proc process {avar lArgs O E} {
+ upvar $avar A
+ set zTrailing "" ;# True if ... is present in $O
+ set lPosargs [list]
+
+ # Populate A() with default values. Also, for each switch in the command
+ # line spec, set an entry in the idx() array as follows:
+ #
+ # {tblname t1 "table name to use"}
+ # -> [set idx(-tblname) {tblname t1 "table name to use"}
+ #
+ # For each position parameter, append its name to $lPosargs. If the ...
+ # specifier is present, set $zTrailing to the name of the prefix.
+ #
+ foreach o $O {
+ set nm [lindex $o 0]
+ set nArg [llength $o]
+ switch -- $nArg {
+ 1 {
+ if {[string range $nm end-2 end]=="..."} {
+ set zTrailing [string range $nm 0 end-3]
+ } else {
+ lappend lPosargs $nm
+ }
+ }
+ 2 {
+ set A($nm) 0
+ set idx(-$nm) $o
+ }
+ 3 {
+ set A($nm) [lindex $o 1]
+ set idx(-$nm) $o
+ }
+ default {
+ error "Error in command line specification"
+ }
+ }
+ }
+
+ # Set explicitly specified option values
+ #
+ set nArg [llength $lArgs]
+ for {set i 0} {$i < $nArg} {incr i} {
+ set opt [lindex $lArgs $i]
+ if {[string range $opt 0 0]!="-" || $opt=="--"} break
+ set c [array names idx "${opt}*"]
+ if {[llength $c]==0} { cmdline_error $O $E "Unrecognized option: $opt"}
+ if {[llength $c]>1} { cmdline_error $O $E "Ambiguous option: $opt"}
+
+ if {[llength $idx($c)]==3} {
+ if {$i==[llength $lArgs]-1} {
+ cmdline_error $O $E "Option requires argument: $c"
+ }
+ incr i
+ set A([lindex $idx($c) 0]) [lindex $lArgs $i]
+ } else {
+ set A([lindex $idx($c) 0]) 1
+ }
+ }
+
+ # Deal with position arguments.
+ #
+ set nPosarg [llength $lPosargs]
+ set nRem [expr $nArg - $i]
+ if {$nRem < $nPosarg || ($zTrailing=="" && $nRem > $nPosarg)} {
+ cmdline_error $O $E
+ }
+ for {set j 0} {$j < $nPosarg} {incr j} {
+ set A([lindex $lPosargs $j]) [lindex $lArgs [expr $j+$i]]
+ }
+ if {$zTrailing!=""} {
+ set A($zTrailing) [lrange $lArgs [expr $j+$i] end]
+ }
+ }
+} ;# namespace eval cmdline
+# End of command line options processor.
+###########################################################################
+###########################################################################
-sqlite3 db $O(db)
+process_cmdline
+
+# If -fts4 was specified, use fts4. Otherwise, fts5.
+if {$A(fts4)} {
+ set A(fts) fts4
+} else {
+ set A(fts) fts5
+}
+
+sqlite3 db $A(database)
# Create the FTS table in the db. Return a list of the table columns.
#
proc create_table {} {
- global O
+ global A
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 nCol [llength $A(colsize)]
set cols [lrange $cols 0 [expr $nCol-1]]
- set sql "CREATE VIRTUAL TABLE IF NOT EXISTS $O(tblname) USING $O(fts) ("
+ set sql "CREATE VIRTUAL TABLE IF NOT EXISTS $A(tblname) USING $A(fts) ("
append sql [join $cols ,]
- append sql ");"
+ if {$A(fts)=="fts5"} { append sql ",detail=$A(detail)" }
+ append sql ", prefix='$A(prefix)');"
db eval $sql
return $cols
@@ -89,27 +189,35 @@ proc readfile {file} {
split $data
}
+proc repeat {L n} {
+ set res [list]
+ for {set i 0} {$i < $n} {incr i} {
+ set res [concat $res $L]
+ }
+ set res
+}
+
# Load all the data into a big list of tokens.
#
set tokens [list]
-foreach f $O(files) {
- set tokens [concat $tokens [readfile $f]]
+foreach f $A(file) {
+ set tokens [concat $tokens [repeat [readfile $f] $A(repeat)]]
}
set N [llength $tokens]
set i 0
set cols [create_table]
-set sql "INSERT INTO $O(tblname) VALUES(\$[lindex $cols 0]"
+set sql "INSERT INTO $A(tblname) VALUES(\$R([lindex $cols 0])"
foreach c [lrange $cols 1 end] {
- append sql ", \$A($c)"
+ append sql ", \$R($c)"
}
append sql ")"
db eval BEGIN
while {$i < $N} {
- foreach c $cols s $O(aColSize) {
- set A($c) [lrange $tokens $i [expr $i+$s-1]]
+ foreach c $cols s $A(colsize) {
+ set R($c) [lrange $tokens $i [expr $i+$s-1]]
incr i $s
}
db eval $sql
diff --git a/ext/misc/json1.c b/ext/misc/json1.c
index 3707516660..3c63b6e946 100644
--- a/ext/misc/json1.c
+++ b/ext/misc/json1.c
@@ -276,10 +276,33 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
p->zBuf[p->nUsed++] = '"';
for(i=0; inUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
+ }else if( c<=0x1f ){
+ static const char aSpecial[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assert( sizeof(aSpecial)==32 );
+ assert( aSpecial['\b']=='b' );
+ assert( aSpecial['\f']=='f' );
+ assert( aSpecial['\n']=='n' );
+ assert( aSpecial['\r']=='r' );
+ assert( aSpecial['\t']=='t' );
+ if( aSpecial[c] ){
+ c = aSpecial[c];
+ goto json_simple_escape;
+ }
+ if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ p->zBuf[p->nUsed++] = 'u';
+ p->zBuf[p->nUsed++] = '0';
+ p->zBuf[p->nUsed++] = '0';
+ p->zBuf[p->nUsed++] = '0' + (c>>4);
+ c = "0123456789abcdef"[c&0xf];
}
p->zBuf[p->nUsed++] = c;
}
@@ -320,7 +343,7 @@ static void jsonAppendValue(
default: {
if( p->bErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
- p->bErr = 1;
+ p->bErr = 2;
jsonReset(p);
}
break;
@@ -1548,7 +1571,7 @@ static void jsonArrayFinal(sqlite3_context *ctx){
pStr->pCtx = ctx;
jsonAppendChar(pStr, ']');
if( pStr->bErr ){
- sqlite3_result_error_nomem(ctx);
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
assert( pStr->bStatic );
}else{
sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
@@ -1596,7 +1619,7 @@ static void jsonObjectFinal(sqlite3_context *ctx){
if( pStr ){
jsonAppendChar(pStr, '}');
if( pStr->bErr ){
- sqlite3_result_error_nomem(ctx);
+ if( pStr->bErr==0 ) sqlite3_result_error_nomem(ctx);
assert( pStr->bStatic );
}else{
sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c
index c0da6b339b..cac1edcff8 100644
--- a/ext/misc/spellfix.c
+++ b/ext/misc/spellfix.c
@@ -186,7 +186,7 @@ static const unsigned char className[] = ".ABCDHLRMY9 ?";
** Return NULL if memory allocation fails.
*/
static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){
- unsigned char *zOut = sqlite3_malloc( nIn + 1 );
+ unsigned char *zOut = sqlite3_malloc64( nIn + 1 );
int i;
int nOut = 0;
char cPrev = 0x77;
@@ -365,8 +365,8 @@ static int editdist1(const char *zA, const char *zB, int *pnMatch){
int *m; /* The cost matrix */
char *cx; /* Corresponding character values */
int *toFree = 0; /* Malloced space */
- int mStack[60+15]; /* Stack space to use if not too much is needed */
int nMatch = 0;
+ int mStack[60+15]; /* Stack space to use if not too much is needed */
/* Early out if either input is NULL */
if( zA==0 || zB==0 ) return -1;
@@ -413,7 +413,7 @@ static int editdist1(const char *zA, const char *zB, int *pnMatch){
if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ){
m = mStack;
}else{
- m = toFree = sqlite3_malloc( (nB+1)*5*sizeof(m[0])/4 );
+ m = toFree = sqlite3_malloc64( (nB+1)*5*sizeof(m[0])/4 );
if( m==0 ) return -3;
}
cx = (char*)&m[nB+1];
@@ -687,7 +687,7 @@ static int editDist3ConfigLoad(
if( iCost<0 ) continue;
if( pLang==0 || iLang!=iLangPrev ){
EditDist3Lang *pNew;
- pNew = sqlite3_realloc(p->a, (p->nLang+1)*sizeof(p->a[0]));
+ pNew = sqlite3_realloc64(p->a, (p->nLang+1)*sizeof(p->a[0]));
if( pNew==0 ){ rc = SQLITE_NOMEM; break; }
p->a = pNew;
pLang = &p->a[p->nLang];
@@ -709,7 +709,7 @@ static int editDist3ConfigLoad(
EditDist3Cost *pCost;
int nExtra = nFrom + nTo - 4;
if( nExtra<0 ) nExtra = 0;
- pCost = sqlite3_malloc( sizeof(*pCost) + nExtra );
+ pCost = sqlite3_malloc64( sizeof(*pCost) + nExtra );
if( pCost==0 ){ rc = SQLITE_NOMEM; break; }
pCost->nFrom = nFrom;
pCost->nTo = nTo;
@@ -808,7 +808,7 @@ static EditDist3FromString *editDist3FromStringNew(
if( z==0 ) return 0;
if( n<0 ) n = (int)strlen(z);
- pStr = sqlite3_malloc( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 );
+ pStr = sqlite3_malloc64( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 );
if( pStr==0 ) return 0;
pStr->a = (EditDist3From*)&pStr[1];
memset(pStr->a, 0, sizeof(pStr->a[0])*n);
@@ -833,13 +833,13 @@ static EditDist3FromString *editDist3FromStringNew(
if( i+p->nFrom>n ) continue;
if( matchFrom(p, z+i, n-i)==0 ) continue;
if( p->nTo==0 ){
- apNew = sqlite3_realloc(pFrom->apDel,
+ apNew = sqlite3_realloc64(pFrom->apDel,
sizeof(*apNew)*(pFrom->nDel+1));
if( apNew==0 ) break;
pFrom->apDel = apNew;
apNew[pFrom->nDel++] = p;
}else{
- apNew = sqlite3_realloc(pFrom->apSubst,
+ apNew = sqlite3_realloc64(pFrom->apSubst,
sizeof(*apNew)*(pFrom->nSubst+1));
if( apNew==0 ) break;
pFrom->apSubst = apNew;
@@ -875,6 +875,17 @@ static void updateCost(
}
}
+/*
+** How much stack space (int bytes) to use for Wagner matrix in
+** editDist3Core(). If more space than this is required, the entire
+** matrix is taken from the heap. To reduce the load on the memory
+** allocator, make this value as large as practical for the
+** architecture in use.
+*/
+#ifndef SQLITE_SPELLFIX_STACKALLOC_SZ
+# define SQLITE_SPELLFIX_STACKALLOC_SZ (1024)
+#endif
+
/* Compute the edit distance between two strings.
**
** If an error occurs, return a negative number which is the error code.
@@ -899,15 +910,24 @@ static int editDist3Core(
EditDist3FromString f = *pFrom;
EditDist3To *a2;
unsigned int *m;
+ unsigned int *pToFree;
int szRow;
EditDist3Cost *p;
int res;
+ sqlite3_uint64 nByte;
+ unsigned int stackSpace[SQLITE_SPELLFIX_STACKALLOC_SZ/sizeof(unsigned int)];
/* allocate the Wagner matrix and the aTo[] array for the TO string */
n = (f.n+1)*(n2+1);
n = (n+1)&~1;
- m = sqlite3_malloc( n*sizeof(m[0]) + sizeof(a2[0])*n2 );
- if( m==0 ) return -1; /* Out of memory */
+ nByte = n*sizeof(m[0]) + sizeof(a2[0])*n2;
+ if( nByte<=sizeof(stackSpace) ){
+ m = stackSpace;
+ pToFree = 0;
+ }else{
+ m = pToFree = sqlite3_malloc64( nByte );
+ if( m==0 ) return -1; /* Out of memory */
+ }
a2 = (EditDist3To*)&m[n];
memset(a2, 0, sizeof(a2[0])*n2);
@@ -920,7 +940,7 @@ static int editDist3Core(
if( i2+p->nTo>n2 ) continue;
if( matchTo(p, z2+i2, n2-i2)==0 ) continue;
a2[i2].nIns++;
- apNew = sqlite3_realloc(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns);
+ apNew = sqlite3_realloc64(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns);
if( apNew==0 ){
res = -1; /* Out of memory */
goto editDist3Abort;
@@ -1029,7 +1049,7 @@ static int editDist3Core(
editDist3Abort:
for(i2=0; i2=pCur->nRow );
- aNew = sqlite3_realloc(pCur->a, sizeof(pCur->a[0])*N);
+ aNew = sqlite3_realloc64(pCur->a, sizeof(pCur->a[0])*N);
if( aNew==0 && N>0 ){
spellfix1ResetCursor(pCur);
sqlite3_free(pCur->a);
@@ -2183,7 +2203,7 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
static int spellfix1Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
spellfix1_cursor *pCur;
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3_malloc64( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVTab = p;
@@ -2397,7 +2417,7 @@ static int spellfix1FilterForMatch(
/* Load the cost table if we have not already done so */
if( p->zCostTable!=0 && p->pConfig3==0 ){
- p->pConfig3 = sqlite3_malloc( sizeof(p->pConfig3[0]) );
+ p->pConfig3 = sqlite3_malloc64( sizeof(p->pConfig3[0]) );
if( p->pConfig3==0 ) return SQLITE_NOMEM;
memset(p->pConfig3, 0, sizeof(p->pConfig3[0]));
rc = editDist3ConfigLoad(p->pConfig3, p->db, p->zCostTable);
diff --git a/main.mk b/main.mk
index 2ae8c7bac4..22d40fa258 100644
--- a/main.mk
+++ b/main.mk
@@ -480,6 +480,12 @@ 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)
+srcck1$(EXE): $(TOP)/tool/srcck1.c
+ $(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c
+
+sourcetest: srcck1$(EXE) sqlite3.c
+ ./srcck1 sqlite3.c
+
fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
$(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \
@@ -782,7 +788,7 @@ quicktest: ./testfixture$(EXE)
# The default test case. Runs most of the faster standard TCL tests,
# and fuzz tests, and sqlite3_analyzer and sqldiff tests.
#
-test: $(TESTPROGS) fastfuzztest
+test: $(TESTPROGS) sourcetest fastfuzztest
./testfixture$(EXE) $(TOP)/test/veryquick.test $(TESTOPTS)
# Run a test using valgrind. This can take a really long time
@@ -907,6 +913,7 @@ clean:
rm -f speedtest1 speedtest1.exe
rm -f wordcount wordcount.exe
rm -f rbu rbu.exe
+ rm -f srcck1 srcck1.exe
rm -f sqlite3.c sqlite3-*.c fts?amal.c tclsqlite3.c
rm -f sqlite3rc.h
rm -f shell.c sqlite3ext.h
diff --git a/manifest b/manifest
index 6014c72a7b..78c07a5223 100644
--- a/manifest
+++ b/manifest
@@ -1,8 +1,8 @@
-C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
-D 2016-01-22T14:46:21.566
-F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4
+C Merge\slatest\strunk\schanges\swith\sthis\sbranch.
+D 2016-02-09T15:10:56.225
+F Makefile.in dac2776c84e0d533b158a9af6e57e05c4a6b19f3
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
-F Makefile.msc d2b93511a969c0c8fcf52aeb5e426571e8c610d2
+F Makefile.msc b0493f10caddb8adf992a4e6f1943141fc7c6816
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION 866588d1edf0ccb5b0d33896974338f97564f719
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -10,10 +10,11 @@ F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
-F autoconf/Makefile.am 089e5ecdb5761e64ea1013ded02feb4d8b29927d
-F autoconf/README 14458f1046c118efa721aadec5f227e876d3cd38
+F autoconf/Makefile.am 1c1657650775960804945dc392e14d9e43c5ed84
+F autoconf/Makefile.msc a35b2aab24d1603f3f0ae65cf01686c2578d319c
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
-F autoconf/configure.ac 7b1ea0dcaf49fafba262ce4b0ee8cb3281b555d1
+F autoconf/README.txt e9757a381e5ce2553dbaa6247bb8ad00eb8d87aa w autoconf/README
+F autoconf/configure.ac 72a5e42beb090b32bca580285dc0ab3c4670adb8
F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd
F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
@@ -29,8 +30,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
-F configure d57d3b9d5c66549e54a4a2960de9813142d30a5a x
-F configure.ac c59513d560b3107995c73b9cc1e55bfd086c4764
+F configure 12d96e3798e612e0ffa53a7a8c4d7fb1090df80e x
+F configure.ac a2224b1162f79848982d3618ac1deffcd94e88ec
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
@@ -82,7 +83,7 @@ F ext/fts3/fts3_snippet.c 68ae118b0f834ea53d2b89e4087fc0f0b8c4ee4e
F ext/fts3/fts3_term.c 88c55a6fa1a51ab494e33dced0401a6c28791fd7
F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038
F ext/fts3/fts3_tokenize_vtab.c a27593ab19657166f6fa5ec073b678cc29a75860
-F ext/fts3/fts3_tokenizer.c 50e7a69a549ac5882cc1971ee43f66aaabc11395
+F ext/fts3/fts3_tokenizer.c 4bd72f767f61c9ce5a7575c844e8d1ed2c3c561a
F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
F ext/fts3/fts3_unicode.c a93f5edc0aff44ef8b06d7cb55b52026541ca145
@@ -97,29 +98,29 @@ 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 ff9c2782e8ed890b0de2f697a8d63971939e70c7
-F ext/fts5/fts5Int.h 5599703af9c13512900a9f22fec39d48078d619d
-F ext/fts5/fts5_aux.c 2dafc3aee0c70d643140c77d8d70daffa51a9e9e
-F ext/fts5/fts5_buffer.c 7d3f6f01f8fdc45204e6a33925ef8478a67d28dd
+F ext/fts5/fts5Int.h efb02807dbe5a2bfb0ea592a472d1171cb553d53
+F ext/fts5/fts5_aux.c b9bcce753ef5b451267b2232f0ca153ddeb3951d
+F ext/fts5/fts5_buffer.c 5142f73defd7d2ad7419f3a8e487681b38b72097
F ext/fts5/fts5_config.c 0c384ebdd23fd055e2e50a93277b8d59da538238
-F ext/fts5/fts5_expr.c 4ab4504c54bbe24689c83411d8588f4ec99136e9
+F ext/fts5/fts5_expr.c ff5c451a6d025909639ac0f0d0af0cc595b50feb
F ext/fts5/fts5_hash.c 1b113977296cf4212c6ec667d5e3f2bd18036955
-F ext/fts5/fts5_index.c 716c301835a122ba36910b4f821c87d26ae9a5d9
-F ext/fts5/fts5_main.c 833db0a3df10ab26e0221a9baa40cf871c450df3
-F ext/fts5/fts5_storage.c fb2eaec3aa954b680d43096dc539f8270bd6390e
+F ext/fts5/fts5_index.c 28f72130400cb54d179a9a120b7232915e3e7a4e
+F ext/fts5/fts5_main.c 6e23df904049edb498538bd3e22e53ec1ab6f4f7
+F ext/fts5/fts5_storage.c 2a1f44deae090cd711f02cec0c2af8e660360d24
F ext/fts5/fts5_tcl.c f8731e0508299bd43f1a2eff7dbeaac870768966
F ext/fts5/fts5_test_mi.c 1ec66ffdf7632077fbd773b7a6df5153272ec070
F ext/fts5/fts5_test_tok.c db08af63673c3a7d39f053b36fd6e065017706be
-F ext/fts5/fts5_tokenize.c 504984ac6993323247221eebe3cd55bead01b5f8
+F ext/fts5/fts5_tokenize.c 4d5c4f183c7d07d144bc219b92da1ea0e962fae3
F ext/fts5/fts5_unicode2.c 78273fbd588d1d9bd0a7e4e0ccc9207348bae33c
-F ext/fts5/fts5_varint.c 3f86ce09cab152e3d45490d7586b7ed2e40c13f1
-F ext/fts5/fts5_vocab.c ee6df1a3be103414d7b7af833ae1885c7b83a9d0
+F ext/fts5/fts5_varint.c a5aceacda04dafcbae725413d7a16818ecd65738
+F ext/fts5/fts5_vocab.c 3ef401a8d6932db56368de32f446eb9fe73aa623
F ext/fts5/fts5parse.y 1647eba089b9b3fc058b4dc989d9da87d15b9580
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
-F ext/fts5/test/fts5_common.tcl 6d0d74b695c4be055a8ba1dd807f22a2abc95b5e
+F ext/fts5/test/fts5_common.tcl 61ff0d1a29d98a91c4553b20b3f410d858834ee9
F ext/fts5/test/fts5aa.test 7e814df4a0e6c22a6fe2d84f210fdc0b5068a084
F ext/fts5/test/fts5ab.test 30325a89453280160106be411bba3acf138e6d1b
-F ext/fts5/test/fts5ac.test d5073ca7bd2d9fe8aab0c82c6c75a7e4b0d70ced
-F ext/fts5/test/fts5ad.test 0ddaa5b692ff220100ee396228838f4331399eaa
+F ext/fts5/test/fts5ac.test 55cad4275a1f5acabfe14d8442a8046b47e49e5f
+F ext/fts5/test/fts5ad.test 36995f0586f30f5602074e012b9224c71ec5171c
F ext/fts5/test/fts5ae.test 612dcb51f4069226791ff14c17dbfb3138c56f20
F ext/fts5/test/fts5af.test be858a96b1f5de66ba6d64f0021bd8b2408e126c
F ext/fts5/test/fts5ag.test 27180de76c03036be75ee80b93d8c5f540014071
@@ -135,26 +136,27 @@ F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e
F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb
F ext/fts5/test/fts5bigtok.test 981b2790f6fa02773c889bd35d42c6b97f80f0f4
F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07
-F ext/fts5/test/fts5config.test 83941309b94d002ed6f55d9cd814e0353c9ae013
+F ext/fts5/test/fts5config.test 8b2bc6dcc0eb06fa2b7dd65b2ce2db09e829e873
F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5
F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1
F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62
F ext/fts5/test/fts5corrupt2.test 26c0a39dd9ff73207e6229f83b50b21d37c7658c
F ext/fts5/test/fts5corrupt3.test a2b537c120bdd43c79c42fe2438d7b8c81fe5599
-F ext/fts5/test/fts5detail.test 4e971d28e7336c61ab916fc287900355dab7054d
+F ext/fts5/test/fts5detail.test ef5c690535a797413acaf5ad9b8ab5d49972df69
F ext/fts5/test/fts5dlidx.test 13871a14641017ae42f6f1055a8067bafd44cb3d
F ext/fts5/test/fts5doclist.test 8edb5b57e5f144030ed74ec00ef6fa4294fed79b
F ext/fts5/test/fts5ea.test b01e3a18cdfabbff8104a96a5242a06a68a998a0
F ext/fts5/test/fts5eb.test 021aa80b7ac09b964249aa32ced9ee908703e4aa
-F ext/fts5/test/fts5fault1.test 4b39c47ca3544615daa8a2f733b911fa08022c77
-F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341
+F ext/fts5/test/fts5fault1.test e09040d3e17b8c0837101e8c79c8a874c4376fb7
+F ext/fts5/test/fts5fault2.test d8c6c7f916ccbdfc10b2c69530e9dd3bc8313232
F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
F ext/fts5/test/fts5fault4.test 532b6dacb963016cbf7003196bd87fb366540277
F ext/fts5/test/fts5fault5.test 10c13a783de3f42a21e3e53e123b62ed0c3a1618
F ext/fts5/test/fts5fault6.test 9682664d679643ac6736e90c225526cc84073cda
-F ext/fts5/test/fts5fault7.test 01be274bfc8d9bf22451a3bf5892e9399d044f1b
-F ext/fts5/test/fts5fault8.test f2d8a2b673a5f72ca1fa0e85bdbfb2041ffd347d
+F ext/fts5/test/fts5fault7.test cb14ea3c1f42394f06f2284abc58eecee6ff8080
+F ext/fts5/test/fts5fault8.test 430837fe6dd0511fd3aea52bd602ac02441bcb58
F ext/fts5/test/fts5fault9.test e10e395428a9ea0596ebe752ff7123d16ab78e08
+F ext/fts5/test/fts5faultA.test fa5d59c0ff62b7125cd14eee38ded1c46e15a7ea
F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741
F ext/fts5/test/fts5hash.test 00668f6fa9b9bffbd7c1be29f408aa2bdade0451
F ext/fts5/test/fts5integrity.test f5e4f8d284385875068ad0f3e894ce43e9de835d
@@ -174,10 +176,11 @@ F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab
F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
F ext/fts5/test/fts5rowid.test 16908a99d6efc9ba21081b4f2b86b3fc699839a6
-F ext/fts5/test/fts5simple.test 2bc6451cbe887a9215f5b14ae307c70d850344c9
+F ext/fts5/test/fts5simple.test 7fcacfa473a37355af2e60096650c87b5ba8f3ba
F ext/fts5/test/fts5simple2.test 98377ae1ff7749a42c21fe1a139c1ed312522c46
+F ext/fts5/test/fts5simple3.test 8e71733b3d1b0e695011d02c68ebc5ca40b6124e
F ext/fts5/test/fts5synonym.test 6475d189c2e20d60795808f83e36bf9318708d48
-F ext/fts5/test/fts5synonym2.test eadb00c73ef0653258873e756b7e9102e0687539
+F ext/fts5/test/fts5synonym2.test aa4c43bd3b691ff80f658cb064f5ab40690e834e
F ext/fts5/test/fts5tok1.test beb894c6f3468f10a574302f69ebe4436b0287c7
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
@@ -188,8 +191,8 @@ F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
F ext/fts5/test/fts5update.test 57c7012a7919889048947addae10e0613df45529
F ext/fts5/test/fts5version.test 978f59541d8cef7e8591f8be2115ec5ccb863e2e
F ext/fts5/test/fts5vocab.test 480d780aa6b699816c5066225fbd86f3a0239477
-F ext/fts5/tool/fts5speed.tcl aaee41894b552df8fbf8616aad003b2ea9ba3221
-F ext/fts5/tool/fts5txt2db.tcl c374c4c4797e8cdfadabdfaeeb5412dcd6686e84
+F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85
+F ext/fts5/tool/fts5txt2db.tcl 1343745b89ca2a1e975c23f836d0cee410052975
F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093
F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45
F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
@@ -203,14 +206,14 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
F ext/misc/fuzzer.c 4c84635c71c26cfa7c2e5848cf49fe2d2cfcd767
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
-F ext/misc/json1.c 7b1155f520d5e8ec1c005d978ac675e8a7f2688a
+F ext/misc/json1.c a27cf1eca6583f9b6e18abab5c2a9a82c4540ca9
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
F ext/misc/series.c b8fb7befd85b3a9b4a10e701b30b2b79ca92b6d4
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
-F ext/misc/spellfix.c df6efb90eb668d1860c9b59e4320e985e46dffa7
+F ext/misc/spellfix.c 525190484b7a9dbc6be646c4842274fff4f27d53
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
@@ -269,7 +272,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk a94c4aea441c36e88fd14e009b99029d2e4f8e84
+F main.mk cd48a5d8a6dc59229f4f3fe40771104f2918f789
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -281,42 +284,42 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
-F src/alter.c 9d649e46c780166e416fb11dbd23f8d49aab8267
-F src/analyze.c 0043d3e501f04297fed2bb50b488bc08d5c39f36
-F src/attach.c 07b3a34a1702dce92a7f1d3888c0c06222b63760
+F src/alter.c d50b7dbb49a4affee951301afb76a008463e3625
+F src/analyze.c fbf0e80d83cc893734e872f932f249a056b86e11
+F src/attach.c c16c2648a577fa3def2adfa48c28901376389bc5
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc
F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf
F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
-F src/btree.c f224ae877fde69d1a9d430f502edaf8502752dbe
-F src/btree.h 526137361963e746949ab966a910c7f455ac6b04
+F src/btree.c 4c8caaeed7878aafdb607c3d2bcbc365bb0d19a1
+F src/btree.h 368ceeb4bd9312dc8df2ffd64b4b7dbcf4db5f8e
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
-F src/build.c 31af80bba31ac159967951ef58f3144cc7db9d70
-F src/callback.c 29ae4faba226c7ebb9aee93016b5ce8a8f071261
+F src/build.c 54866fbafa09d494269bdefc79995eb7207003a6
+F src/callback.c ed6c2a4a712eb7287ff64e20e3c23265dfb8a7ce
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
F src/ctime.c 60e135af364d777a9ab41c97e5e89cd224da6198
-F src/date.c 997651e3ee6c2818fbf7fcdb7156cef9eb3ece20
-F src/dbstat.c ffd63fc8ba7541476ced189b95e95d7f2bc63f78
-F src/delete.c 00af9f08a15ddc5cba5962d3d3e5bf2d67b2e7da
-F src/expr.c df0d7c3230d59abd679da22ff5ce4cfd0e3a0e63
+F src/date.c ca17321bc17cca8f40e0843edea4fafff974998e
+F src/dbstat.c b2ec6793eef97aebb4d171d490a4ffdfa9f2475c
+F src/delete.c 48802aa3ee6339f576d074336d3ae1b5f40e240f
+F src/expr.c fbf0706199aea23c54efe36b6932d8307c4eb872
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c e18b3dff7d47c7bcac5ac4fc178a89b9fd322b44
-F src/func.c ba6c03f9e440f5693086c08ee88e6e60212b3504
+F src/fkey.c 08edad1fce30f761f14b3997e89bad58f9f7f4e0
+F src/func.c 86e55fee35b9577e485f47d9dd5c1d34cd513288
F src/global.c bd5a0af3f30b0c01be6db756c626cd3c33a3d260
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c a00e6d8a843dc22e2c136df04e6300c4528d9b9f
+F src/insert.c 046199e085e69e05af7bef197d53c5b4b402b6fa
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
-F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
+F src/legacy.c b1b0880fc474abfab89e737b0ecfde0bd7a60902
F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b
-F src/main.c b686dabe9a7ece9121da87120d5c7bf402d77eb3
-F src/malloc.c b67c26c359c13836d370350b3f43d228dff5b360
+F src/main.c b67a45397b93b7ba8fbd6bfcb03423d245baed05
+F src/malloc.c 337e9808b5231855fe28857950f4f60ae42c417f
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a
-F src/mem5.c 71f81a11fc5e29a57428761ab38a7bf2ef4ee19d
+F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memjournal.c 3eb2c0b51adbd869cb6a44780323f05fa904dc85
F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495
F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c
@@ -329,32 +332,32 @@ F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
-F src/os_unix.c b509b49b40a269e7b75ab511b6e92b2dc9444359
-F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811
+F src/os_unix.c 821ed110197175165cf2f50b0930c7ff9a24504c
+F src/os_win.c ccf29ddded3e41e506b6bd98c1171aa0963b23f2
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
-F src/pager.c f4e9ac39fbb1e0fde97af85c0f4e00eb90764b67
-F src/pager.h 1c2a49143dfba9e69cc8159ef019f472ed8d260b
-F src/parse.y caad1e98edeca6960493d0c60d31b76820dd7776
+F src/pager.c 67cd2fbab58d0e35fed5f81432856f4f0af9fc6d
+F src/pager.h f3eb324a3ff2408b28bab7e81c1c55c13720f865
+F src/parse.y d7bff41d460f2df96fb890f36700e85cb0fc5634
F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23
F src/pcache.h 4d0ccaad264d360981ec5e6a2b596d6e85242545
F src/pcache1.c 72f644dc9e1468c72922eff5904048427b817051
-F src/pragma.c ea290193369faa0a26ae2f924e7b86289b4a7987
+F src/pragma.c 80ee77226d0008d9188356a6cbbe6010866e1bee
F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
-F src/prepare.c 74855ddbdfad6a1c4a4d5c4b0913ebb01174ba19
-F src/printf.c af589a27b7d40f6f4f704e9eea99f02f18ad6d32
+F src/prepare.c c12b786713df3e8270c0f85f988c5359d8b4d87c
+F src/printf.c 63e6fb12bbe702dd664dc3703776c090383a5a26
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c
-F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
-F src/select.c 718954db86277d696c520fe671148db1e9c4ed3c
+F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e
+F src/select.c ff80004a9a6ece891a8d9327a88e7b6e2588ee6d
F src/shell.c dcd7a83645ef2a58ee9c6d0ea4714d877d7835c4
-F src/sqlite.h.in 214476a62012e578f42133a9a3b4f97a9aa421a3
-F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
+F src/sqlite.h.in cf22ad1d52dca2c9862d63833e581028119aab7e
+F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
-F src/sqliteInt.h 0403328581127bc8ad2f9cc7af2c3bb23d5316da
+F src/sqliteInt.h 3aeaff9611acd790c8e76719b33db09ab885d537
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
-F src/tclsqlite.c 82979239a896992f9b78efec81cfda05d316a7d0
+F src/tclsqlite.c 94ef6e2794220c5b6064d4c78ec7169a8c5cc45d
F src/test1.c 4f1b42699068b7806af3111786f5ad760c2c1ff7
F src/test2.c 5586f43fcd9a1be0830793cf9d354082c261b25b
F src/test3.c a8887dabbbee3059af338f20d290084a63ed1b0f
@@ -369,11 +372,11 @@ 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 0dee90328e3dedf8ba002ee94b6a7e7ea7726fe4
+F src/test_config.c 7985332c806d1cece793475c75a6abcccde9d331
F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_fs.c a61f54247fdb843761d709879c3bcd1989b2050c
-F src/test_func.c 0d9c25956152adefee8881c6fadc8354793764d0
+F src/test_func.c 37453d346cfcf118774efd5bf6187f7e6a7e3254
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32
F src/test_intarray.c 870124b95ec4c645d4eb84f15efb7133528fb1a5
@@ -403,31 +406,31 @@ F src/test_windirent.c 8f5fada630348558d5745b334702f301da1ffc61
F src/test_windirent.h b12055cab6227f7be10f5c19296f67c60cc5e2a5
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c bbfb74450643cb5372a43ad4f6cffd7e9dfcecb0
-F src/tokenize.c 5606871a377f390af7040ec3c12e0d183512d785
-F src/treeview.c 78842e90c1f71269e7a73a1d4221b6fe360bab66
-F src/trigger.c 056e51182a3677434423e3be0c74e61b90b4a663
-F src/update.c 17332f9fe818cbc0444c36a811800af8498af4c3
-F src/utf.c 32d7f82aa921322f3e1c956f4b58f019ebd2c6b3
-F src/util.c e802e8e311a0d6c48cd1b3e89db164f6f0248d70
+F src/tokenize.c 813934be70597edfbb685ae08fc4c8b549cf5a1e
+F src/treeview.c dc39ccf04e9331237388b9cb73289c9d87ea050b
+F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280
+F src/update.c 310ca7adb86a7d1f2afae46905b21c83580f3e17
+F src/utf.c 10cc2519e82e3369344d0969ad4b1a333dc86d18
+F src/util.c 49ce0a65306c1c51d61cb5bc214c71cb62452de6
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
-F src/vdbe.c b90d9d38e5e0260c2eafa3cb4c2274d8ea94da27
-F src/vdbe.h 7a733ea8aac1b77305a67698e784fa3484ee3337
-F src/vdbeInt.h 42eefa4f9e7432b9968d321b44e48821ec13b189
-F src/vdbeapi.c ffae8f5af4570fbd548504e815e9fb7227f0822e
-F src/vdbeaux.c 07f8f485a6cbc0a62da660f14e303061d45d5cb6
-F src/vdbeblob.c 37c3d11a753e403698c69e17383d282e1ae73e75
-F src/vdbemem.c b9181e77eca2a095929d46250daf85c8d2621fc0
-F src/vdbesort.c 0971557e5d3c289e46f56a52aed2197c13251de7
-F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
-F src/vtab.c 320682cca733115b4cbe71320b5c5eeb1074ebde
-F src/vxworks.h 974e7d9a98f602d6310d563e1dc4e08f9fc48e47
+F src/vdbe.c c193299e595a13eba247738e22fce25c49346a6c
+F src/vdbe.h c743791f723049db94f009e3e30958952bc2d512
+F src/vdbeInt.h 4b69d5451bcadd473e745af53ef1e8abfdce0a79
+F src/vdbeapi.c 9324f6baee1a1b2284c6543e98f916888a81e459
+F src/vdbeaux.c deae5d3bd45da0e57c7d9e1d7436333d142dc3bb
+F src/vdbeblob.c 3b570b730109e8f653d9d2081649f6e7015113db
+F src/vdbemem.c 68fcfac37dc6601d98c32cc5adee4d39f2c1b7b4
+F src/vdbesort.c ef3c6d1f1a7d44cf67bb2bee59ea3d1fe5bad174
+F src/vdbetrace.c f75c5455d8cf389ef86a8bfdfd3177e0e3692484
+F src/vtab.c bef51b4f693d82b4b0184457faa8625654534091
+F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c d21b99fd1458159d0b1ecdccc8ee6ada4fdc4c54
F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
-F src/where.c 4610f0b9b937af62df7196f39a3f469d06f01636
+F src/where.c 438b89caa0cbe17cd32703a8f93baca9789d0474
F src/whereInt.h 78b6b4de94db84aecbdc07fe3e38f648eb391e9a
-F src/wherecode.c 8dee26eb181ea9daa8b1a4d96f34c0860aaf99bd
-F src/whereexpr.c 197a448b52073aee43eca3a2233fc113369eb2d4
+F src/wherecode.c 791a784bbf8749d560fdb0b990b607bc4f44a38d
+F src/whereexpr.c de117970b29471177a6901d60ad83a194671dc03
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -447,11 +450,11 @@ F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f
F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
F test/analyze8.test c05a461d0a6b05991106467d0c47480f2e709c82
-F test/analyze9.test 3dd9e203fad353ec8027b18a6d9a92af59f4e727
+F test/analyze9.test 88c1f2aa20b614236f03e1cc38c3619e7e8a38b4
F test/analyzeA.test 3335697f6700c7052295cfd0067fc5b2aacddf9a
-F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d
+F test/analyzeB.test a4c1c3048f6d9e090eb76e83eecb18bcf6d31a70
F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93
-F test/analyzeD.test 08f9d0bee4e118a66fff3a32d02dbe0ee0a2b594
+F test/analyzeD.test f3d77cd0fefe2849d784897d52df13beee19271d
F test/analyzeE.test 8684e8ac5722fb97c251887ad97e5d496a98af1d
F test/analyzeF.test 5d1fe1024ba2dfea3c18bede8c1ccef8aba1ab34
F test/analyzer1.test 498e2ff4b62740c2751c3a2f8b744fe26689fae9
@@ -510,7 +513,7 @@ F test/btree02.test fe69453d474d8154d19b904157ff1db4812fed99
F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de
-F test/cacheflush.test a755c93482ce2e20c04825304bef27e7b7ea0111
+F test/cacheflush.test af25bb1509df04c1da10e38d8f322d66eceedf61
F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738
F test/capi3.test bf6f0308bbbba1e770dac13aa08e5c2ac61c7324
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
@@ -518,7 +521,7 @@ F test/capi3c.test 06f6261f9e9b4ef6f76afcd9900f3665408af1c8
F test/capi3d.test 485048dc5cd07bc68011e4917ad035ad6047ab82
F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
-F test/cffault.test 1647eef45512817265c360ed4539538eed26ac69
+F test/cffault.test aadc1f61f8811cb600e3e069acbf8796f472a096
F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763
F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815
F test/closure01.test b1703ba40639cfc9b295cf478d70739415eec6a4
@@ -555,10 +558,10 @@ F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040
F test/corruptE.test be8e5088c369fc7979c662cd644efdaafc0f7f6d
F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
-F test/corruptH.test 5dd4fa98c6c1ed33b178f9e8a48c4fdd3cfc9067
-F test/corruptI.test f2b10e4fec2a4315bca2b936ffa52ccbffac3422
+F test/corruptH.test 99ad81a4bda7cc078c589ef7542ecbc64e453c80
+F test/corruptI.test 347babbf970e7947e3f91dccf7a1bec28a1bab04
F test/corruptJ.test 9e29e7a81ee3b6ac50f77ea7a9e2f3fa03f32d91
-F test/cost.test 19d314526616ce4473eb4e4e450fcb94499ce318
+F test/cost.test 1eedbfd868f806f3fa08ff072b04cf270dcf61c8
F test/count.test cb2e0f934c6eb33670044520748d2ecccd46259c
F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62
F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
@@ -589,10 +592,10 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
F test/distinct.test a1783b960ad8c15a77cd9f207be072898db1026c
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
-F test/e_blobbytes.test 9bea1d3e2b20f3010b04abba58f6ba172301f49f
-F test/e_blobclose.test df756753f571bc30e42e3a6cba2807576e49e716
-F test/e_blobopen.test 234f960d90235a9b51ec3ca1e062e8541dd558d8
-F test/e_blobwrite.test 615b405f29feb2cfb5a1f03dab7933258294fa26
+F test/e_blobbytes.test 439a945953b35cb6948a552edaec4dc31fd70a05
+F test/e_blobclose.test 4b3c8c60c2171164d472059c73e9f3c1844bb66d
+F test/e_blobopen.test e95e1d40f995056f6f322cd5e1a1b83a27e1a145
+F test/e_blobwrite.test 650ded42ee5e0c2dbf263583ce01adf280129599
F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
F test/e_createtable.test d4c6059d44dcd4b636de9aae322766062b471844
F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
@@ -612,7 +615,7 @@ F test/e_uri.test eed3eb41b22d051a1164110dacdc778899126e14
F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
F test/e_wal.test ae9a593207a77d711443ee69ffe081fda9243625
F test/e_walauto.test 280714ddf14e1a47dcbc59d515cd0b026dfd5567
-F test/e_walckpt.test 65e29b6631e51f210f83e4ff11571e647ba93608
+F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0
F test/e_walhook.test da3ea8b3483d1af72190337bda50155a91a4b664
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
@@ -638,9 +641,9 @@ F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
F test/fkey5.test 5a373303f201ac03c22ba1ef17a733d3f56e611a
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
F test/fkey7.test 72e915890ee4a005daaf3002cb208e8fe973ac13
-F test/fkey8.test 8f08203458321e6c19a263829de4cfc936274ab0
+F test/fkey8.test 7bd1dd0174a0e29a90c62c517b9e2a410a0b345d
F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
-F test/fordelete.test ba12ec1d27cc34a4c23db4446029126d773f3849
+F test/fordelete.test eb93a2f34137bb87bdab88fcab06c0bd92719aff
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7
@@ -694,14 +697,14 @@ F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f
F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test 3e4e3d5e75c076520341d0bdf4eb17c00e8cbde2
-F test/fts3atoken.test e3a126365131a6db52efc20a9a6053cd44e5f289
+F test/fts3atoken.test 76262be798f23a390717d14266f0df551e52a7ee
F test/fts3auto.test b981fea19b132b4e6878f50d7c1f369b28f68eb9
F test/fts3aux1.test f8f287a4a73f381f8fa15b6a70f36245f903d221
F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
-F test/fts3conf.test ff90127b2462c788348c0dd7f6f8c573ef9cb5d9
+F test/fts3conf.test 1c8b8adb0ab28a41b68d1514df44380bd7353402
F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
@@ -714,40 +717,40 @@ F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test 3401d47b229c4504424caf362cc4ff704cad4162
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
-F test/fts3expr4.test e1be1248566f43c252d4404d52914f1fc4bfa065
+F test/fts3expr4.test c39a15d676b14fc439d9bf845aa7bddcf4a74dc3
F test/fts3expr5.test f9abfffbf5e53d48a33e12a1e8f8ba2c551c9b49
F test/fts3fault.test da49627b280b210ebc6657f76344c7851f10ce66
F test/fts3fault2.test f953bb3cf903988172270a9a0aafd5a890b0f98f
F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641
-F test/fts3join.test 53e66a0c21eb568580674a43b21c059acb26f499
+F test/fts3join.test 34750f3ce1e29b2749eaf0f1be2fa6301c5d50da
F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6
-F test/fts3matchinfo.test 07009313ad6c082f94d8c9c3228eb8940c93ac71
+F test/fts3matchinfo.test ce864e0bd92429df8008f31cf557269ba172482a
F test/fts3near.test 7e3354d46f155a822b59c0e957fd2a70c1d7e905
-F test/fts3offsets.test 5b8ec5be27dd2070af3538b23c67f1ca8c822853
+F test/fts3offsets.test b85fd382abdc78ebce721d8117bd552dfb75094c
F test/fts3prefix.test fa794eaab0bdae466494947b0b153d7844478ab2
F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce
F test/fts3query.test f33eb71a1fe1084ea585eeb7ee76b390729f5170
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e
-F test/fts3snippet.test 63dbd687d5bf5191f1b8e6a0977aa9c1e28a7004
+F test/fts3snippet.test 01a4231816e03a0660ae53ba2404fe69012fe0db
F test/fts3sort.test ed34c716a11cc2009a35210e84ad5f9c102362ca
F test/fts3tok1.test 178c050199af8c05299b1ad572514ce1c54b7827
F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d
F test/fts3varint.test 752c08ed5d32c5d7dc211b056f4ed68a76b7e36e
F test/fts4aa.test 10aac8e9d62c7357590acfabe3fad01e9a9ce1cb
-F test/fts4check.test 9d9e818fd6cb29c0e007cd6d00447739d4fde430
-F test/fts4content.test 8707425b926663f8ca81de866c007900442b5ec0
+F test/fts4check.test c3056eab9524232e4c9bdcd119912947e07bcc1c
+F test/fts4content.test 05716af19a899cd70d5cd916c580043c03f30db4
F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01
-F test/fts4growth.test df10fde9f47cf5c71861e63fd8efcd573c4f7e53
-F test/fts4growth2.test 2f063be1902a73cd087355837c52fed42ac11a5d
+F test/fts4growth.test 60d6bb3f78e25b34f533797dd9f2f9402310a13a
+F test/fts4growth2.test 13ad4e76451af6e6906c95cdc725d01b00044269
F test/fts4incr.test 4e353a0bd886ea984e56fce9e77724fc923b8d0d
-F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7
+F test/fts4langid.test 8bd8759e0d4b04d71771544b861193a6841fee84
F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee
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/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309
+F test/fts4onepass.test 7319d61a2ed1325fc54afd0c060a0513b462303a
F test/fts4unicode.test 27378af76394542cf490cf001d8d1505fe55f6a9
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
@@ -758,7 +761,7 @@ F test/func5.test cdd224400bc3e48d891827cc913a57051a426fa4
F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74
F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
-F test/fuzz3.test 53fabcd5f0f430f8b221282f6c12c4d0903c21eb
+F test/fuzz3.test b47377143f0c80f91ed29d722861077ff34415d5
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
F test/fuzzcheck.c 3309d793165ca61a9996271cb799694839348f9a
@@ -766,7 +769,8 @@ F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21
-F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
+F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
+F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
F test/hexlit.test d7b0a5f41123df1e43985b91b8b2e70f95282d21
@@ -786,7 +790,7 @@ F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
F test/incrblob4.test f26502a5697893e5acea268c910f16478c2f0fab
F test/incrblob_err.test af1f12ba60d220c9752073ff2bda2ad59e88960d
F test/incrblobfault.test 280474078f6da9e732cd2a215d3d854969014b6e
-F test/incrcorrupt.test 9786cba68c5832f01887fde1c06b43c3904d86f6
+F test/incrcorrupt.test 6c567fbf870aa9e91866fe52ce6f200cd548939a
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
F test/incrvacuum2.test 676c41428765d58f1da7dbe659ef27726d3d30ac
F test/incrvacuum3.test 75256fb1377e7c39ef2de62bfc42bbff67be295a
@@ -830,9 +834,9 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
F test/jrnlmode.test 7864d59cf7f6e552b9b99ba0f38acd167edc10fa
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
-F test/json101.test f0178422b3a2418f423fd0d3caf3571c8d1b9863
+F test/json101.test ef42283f0b60d8bacbc2243448e7c84988578e52
F test/json102.test bf3fe7a706d30936a76a0f7a0375e1e8e73aff5a
-F test/json103.test 923b288a0610ec86c0951778f7db19cbcca36ad1
+F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
@@ -872,8 +876,8 @@ F test/mallocG.test 0ff91b65c50bdaba680fb75d87fe4ad35bb7934f
F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
-F test/mallocK.test da01dcdd316767b8356741f8d33a23a06a23def5
-F test/mallocL.test 252ddc7eb4fbf75364eab17b938816085ff1fc17
+F test/mallocK.test 27cb5566a6e5f2d76f9d4aa2eca45524401fd61e
+F test/mallocL.test fb311ff80afddf3b1a75e52289081f4754d901dc
F test/malloc_common.tcl aac62499b76be719fac31e7a3e54a7fd53272e7f
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
@@ -894,9 +898,10 @@ F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
F test/misc8.test fc2754d38892f7dac30c22db3616c2764f117d66
F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912
-F test/mmap1.test 1bfd611b9841eafb44f7d83c0788e146d84a33c9
+F test/mmap1.test 44a5ff1c1bcc7dcf2de50227d1f997e75a8ef1ae
F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022
F test/mmap3.test c92273e16eb8d23c1d55c9815b446bb72ef0512e
+F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93
F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
F test/multiplex.test efd015ca0b5b4a57dc9535b8feb1273eebeadb60
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
@@ -924,8 +929,8 @@ F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3
-F test/oserror.test 361346396ae18462c7393c1ac5c3f17237bd89b2
-F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799
+F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f
+F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
@@ -939,9 +944,9 @@ F test/parser1.test 222b5cbf3e2e659fec1bf7d723488c8b9c94f1d0
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 4ea119731c62d2f7d0aa86dd5b184cbb61ca411b
-F test/pragma.test a44253f911e7d50127d4a08f927f47c861a4c772
-F test/pragma2.test a9400a7289605280576098b97f5cde3f204075c0
+F test/permutations.test 382a43c49f49bafe6fddffe904ea33d6bb3ff33e
+F test/pragma.test 507ac7ef2ea5682241ea0ef041799ca70bb5e0bf
+F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
F test/printf.test b3ff34e73d59124140eaf89f7672e21bc2ca5fcc
F test/printf2.test 0b61566dd1c0f0b802f59dffa228c5dc5aa6b054
@@ -950,7 +955,7 @@ F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
-F test/quota.test 2379902c25e291eac5c12b4cf96946a3447e3744
+F test/quota.test 36cd78b178c4eb0401d4f25754ef410fbd9df2a7
F test/quota2.test 7dc12e08b11cbc4c16c9ba2aa2e040ea8d8ab4b8
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
@@ -962,8 +967,8 @@ F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl 975449bf742b8bb9025208292208af816a1fcb58
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
-F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
-F test/rollbackfault.test 6a004f71087cc399296cffbb5429ea6da655ae65
+F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5
+F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
@@ -971,11 +976,11 @@ F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
F test/savepoint.test c671fdbd34cd3bfe1518a777526ada595180cf8d
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
-F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
F test/savepoint7.test db3db281486c925095f305aad09fe806e5188ff3
+F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2 w test/savepoint3.test
F test/scanstatus.test 5253c219e331318a437f436268e0e82345700285
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
@@ -988,10 +993,10 @@ F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
F test/select1.test be62204d2bd9a5a8a149e9974cfddce893d8f686
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
-F test/select4.test 6d5bc6d178a367e8b48fa1c1d3ea12cae9c2d650
+F test/select4.test 453631158540e5f685b81cac5b7e8bd8c6b4c5fc
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
-F test/select7.test 71f06cd37cb6f65bb08ba1ccf8e2f5818c09329f
+F test/select7.test 95e370c42d47c3c52377d05e9ffc01ccff7c1f61
F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
F test/selectA.test e452bdb975f488ea46d091382a9185b5853ed2c7
@@ -1004,7 +1009,7 @@ F test/selectG.test e8600e379589e85e9fefd2fe4d44a4cdd63f6982
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
-F test/shared3.test fcd65cb11d189eff5f5c85cc4fad246fb0933108
+F test/shared3.test ab693f9b6e156b8bfb2a0ad94f29fe69602a5d38
F test/shared4.test c75f476804e76e26bf6fa0e7b421fb0ca7d07558
F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9
F test/shared7.test a81e99f83e6c51b02ac99c96fb3a2a7b5978c956
@@ -1028,15 +1033,15 @@ F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
-F test/snapshot.test efc6b4edc5d571161835f9dd8552e181ad1f0ac2
+F test/snapshot.test 5ec4651d16c3d1eb6c010d102febe32b3614bf56
F test/snapshot_fault.test 25973aeb1b86a280800e0bcf1eb5ce70e9ef57ab
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087
-F test/sort.test 3f492e5b7be1d3f756728d2ff6edf4f6091e84cb
-F test/sort2.test 37afbc03f5559f2eb0f18940b55d38dfbb5172ac
-F test/sort3.test 6178ade30810ac9166fcdf14b7065e49c0f534e2
+F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b
+F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0
+F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5
F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c
-F test/sort5.test a448240a42b49239edc00f85d6d7ac7a1b261e1f
+F test/sort5.test d3041ce3c475aa04142a959ae56ef6593f98a99f
F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
@@ -1047,13 +1052,13 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/speedtest1.c f8bf04214e7b5f745feea99f7bde68b1c4870666
-F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135
+F test/spellfix.test f9c1f431e2c096c8775fec032952320c0e4700db
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
F test/spellfix3.test f7bf7b3482971473d32b6b00f6944c5c066cff97
F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5
F test/sqllimits1.test a74ee2a3740b9f9c2437c246d8fb77354862a142
-F test/sqllog.test a8faa2df39610a037dd372ed872d124260d32953
-F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf
+F test/sqllog.test 6af6cb0b09f4e44e1917e06ce85be7670302517a
+F test/stat.test acc91e80517fff447ae8adcfd953cfdaa5efc0af
F test/statfault.test f525a7bf633e50afd027700e9a486090684b1ac1
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
F test/subquery.test d7268d193dd33d5505df965399d3a594e76ae13f
@@ -1062,9 +1067,9 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
-F test/symlink.test cbf6cb8c6c4b63a39e9f0f6b0d5c99e249dbc102
-F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
-F test/syscall.test 2aa9e111b79fb385681ff8940124def6f8faab87
+F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
+F test/sync.test 2f607e1821aa3af3c5c53b58835c05e511c95899
+F test/syscall.test f59ba4e25f7ba4a4c031026cc2ef8b6e4b4c639c
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
F test/tabfunc01.test cc33684f9480fcf1fd5ce287ac28d22971cad1cc
F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
@@ -1074,7 +1079,7 @@ F test/tclsqlite.test 7fb866443c7deceed22b63948ccd6f76b52ad054
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
-F test/tester.tcl af4749cf4abf04291710c5e73f40bc8f411bae86
+F test/tester.tcl 462376b478c1429030911b4cb7c8c517ef1fbd9b
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1121,14 +1126,14 @@ F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
F test/tkt-94c04eaadb.test f738c57c7f68ab8be1c054415af7774617cb6223
F test/tkt-9a8b09f8e6.test b2ef151d0984b2ebf237760dbeaa50724e5a0667
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
-F test/tkt-9f2eb3abac.test 85bc63e749f050e6a61c8f9207f1eee65c9d3395
+F test/tkt-9f2eb3abac.test cb6123ac695a08b4454c3792fbe85108f67fabf8
F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4
F test/tkt-a8a0d2996a.test eb597379dbcefa24765763d7f682c00cb5924fa9
F test/tkt-b1d3a2e531.test 8f7576e41ca179289ee1a8fee28386fd8e4b0550
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
F test/tkt-b75a9ca6b0.test 97cc2d5eeaf82799eb42138c0a1ff64370238ce4
-F test/tkt-ba7cbfaedc.test e76d88e572e489ee0d64fe4caf4af18b3d1dc688
+F test/tkt-ba7cbfaedc.test b4c0deccc12aeb55cfdb57935b16b5d67c5a9877
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447
@@ -1248,7 +1253,7 @@ F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 302d8995f5ffe63bbc15053abb3ef7a39cf5a092
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
-F test/triggerE.test 355e9c5cbaed5cd039a60baad1fb2197caeb8e52
+F test/triggerE.test 15fa63f1097db1f83dd62d121616006978063d1f
F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1
F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a
F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9
@@ -1291,13 +1296,13 @@ F test/vtabH.test 5f5157a1501d9889ec35c1a1832f69612dd31444
F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
-F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772
-F test/wal.test 65bfc68f3f09dcbc62cee9f794e560428d96cec7
+F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
+F test/wal.test 0148c8b3421a25fdb4d9c160e84a681d0646371b
F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
-F test/wal3.test b1d425f68a1f61d12563f0fa1ee6fca7d5afabf4
+F test/wal3.test 5dd734147f1f8f958c5261a1f2775d346d7013ce
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 88b5d9a6a3d1532497ee9f4296f010d66f07e33c
-F test/wal6.test 4421cd5a2fa99d29cc91ef12fb23bed171ed3a4c
+F test/wal6.test a9d6aa635b9d63607dabdc11406f5f96ca986635
F test/wal64k.test 163655ecd2cb8afef4737cac2a40fdd2eeaf20b8
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal8.test 75c42e1bc4545c277fed212f8fc9b7723cd02216
@@ -1306,19 +1311,20 @@ F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
F test/walblock.test be48f3a75eff0b4456209f26b3ce186c2015497d
-F test/walcksum.test 9afeb96240296c08c72fc524d199c912cfe34daa
-F test/walcrash.test 451d79e528add5c42764cea74aa2750754171b25
+F test/walcksum.test bb234a1bb42248b3515d992b719708015c384278
+F test/walcrash.test 21038858cc552077b0522f50b0fa87e38139306a
F test/walcrash2.test a0edab4e5390f03b99a790de89aad15d6ec70b36
F test/walcrash3.test e426aa58122d20f2b9fbe9a507f9eb8cab85b8af
F test/walfault.test 1f8389f7709877e9b4cc679033d71d6fe529056b
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
-F test/waloverwrite.test a0d2ae0783187374c1e6a9571e0916152977cb81
+F test/waloverwrite.test dad2f26567f1b45174e54fbf9a8dc1cb876a7f03
F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
+F test/walprotocol.test 059cb75484a1ecf6357a2c1b3324b8156749221e
F test/walro.test 34422d1d95aaff0388f0791ec20edb34e2a3ed57
F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
-F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
+F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
F test/where.test 9902a3d84e9bc80357a2c54ed0e76c0d6d04a867
F test/where2.test af78c55589cbc82d793449493adba0dc3d659f23
@@ -1328,19 +1334,19 @@ F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test f520bcec2c3d12dc4615623b06b2aec7c2d67e94
F test/where8.test 98eedca0d375fb400b8377269c4b4686582dfb45
-F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
-F test/whereD.test 9eba1f9b18e5b19a0b0bcaae5e8c037260195f2b
+F test/whereD.test 51366b07cb6f546cd30cc803f7e754f063b940de
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
F test/whereG.test dde4c52a97385a55be6a7cd46be8373f0cf35501
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
-F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
+F test/whereI.test eab5b226bbc344ac70d7dc09b963a064860ae6d7
F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
+F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864 w test/where8m.test
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
@@ -1349,7 +1355,7 @@ F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
F test/with1.test cef099a491eac9874f2c28bd2dc86394fb3e47b3
F test/with2.test 2b40da883658eb74ad8ad06afabe11a408e7fb87
F test/with3.test 511bacdbe41c49cf34f9fd1bd3245fe1575bca98
-F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
+F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
F test/without_rowid1.test 1a7b9bd51b899928d327052df9741d2fe8dbe701
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
F test/without_rowid3.test aad4f9d383e199349b6c7e508a778f7dff5dff79
@@ -1362,7 +1368,7 @@ F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
F tool/GetTclKit.bat 629d87562e0487c386db630033931d12d62e6372
F tool/addopcodes.tcl 4ca9c3ef196f08da30add5d07ce0c9458dc8c633
-F tool/build-all-msvc.bat 204a039f985d5a4f4f9df3a3aa594fd17848c37e x
+F tool/build-all-msvc.bat 31866578036cd1d962628059b0760d407c3ce4d8 x
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
@@ -1377,10 +1383,11 @@ F tool/lemon.c 799e73e19a33b8dd7767a7fa34618ed2a9c2397d
F tool/lempar.c 3ec1463a034b37d87d782be5f6b8b10a3b1ecbe7
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
-F tool/mkautoconfamal.sh 5a5441280b509d2bb3bdc71bfb63781b0d570373
-F tool/mkkeywordhash.c 06ec0b78bd4fa68c12d90ef2bdfe76b039133ff8
-F tool/mkopcodec.tcl edde8adc42621b5e598127f8cdc6d52cfe21f52b
-F tool/mkopcodeh.tcl e04177031532b7aa9379ded50e820231ac4abd6e
+F tool/mkautoconfamal.sh a29b14d54302b33fd892958f6895582ea90e4a45
+F tool/mkkeywordhash.c f7f3b342211ac6a14258b9726d5b97cf4f548f22
+F tool/mkmsvcmin.tcl d57e6efc9428605f5418d0b235721ddf7b5d9c0b
+F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
+F tool/mkopcodeh.tcl 385c62d78c38b2d92146dcb5abd319dbbc33506d
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkpragmatab.tcl f0d5bb266d1d388cf86fce5ba01a891e95d72d41
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
@@ -1411,6 +1418,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/sqldiff.c 5a26205111e6fa856d9b1535b1637744dcdb930b
+F tool/srcck1.c 4c39bdfa9a92edd20233ee720df84dbeb2417602
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
@@ -1421,7 +1429,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 44edc1aa3b412ddbe2a242075e2bf36a99437688 e4c07df557cd50786b05eecf011bf94708e6e31b
-R 52349324086f20f4365ca416f93ec5a6
+P 9341491c3a11d5a66e4f88d2af9b0d3799b4f27a ca72be8618e5d466d6f35819ca8bbd2b84269959
+R c3affb84a750d99cc1d1198e66d07e76
U dan
-Z f458e202045d703a80b0791cfb8fc733
+Z dbcdec085ed65802fe686b0a9369fd51
diff --git a/manifest.uuid b/manifest.uuid
index eb173bec24..b2c40d21e2 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-9341491c3a11d5a66e4f88d2af9b0d3799b4f27a
\ No newline at end of file
+1a4182eedd0143c3f71b3d97f1d1bb25adeba617
\ No newline at end of file
diff --git a/src/alter.c b/src/alter.c
index 2b043ef158..34ca8abd2c 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -608,7 +608,7 @@ void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){
sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2);
addr1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1);
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, minFormat);
sqlite3VdbeJumpHere(v, addr1);
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -695,7 +695,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
if( rc!=SQLITE_OK ){
- db->mallocFailed = 1;
+ assert( db->mallocFailed == 1 );
return;
}
if( !pVal ){
@@ -803,7 +803,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
if( !pNew->aCol || !pNew->zName ){
- db->mallocFailed = 1;
+ assert( db->mallocFailed );
goto exit_begin_add_column;
}
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
diff --git a/src/analyze.c b/src/analyze.c
index 4d777fa9f5..1e026a7530 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -313,7 +313,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
- p->u.aRowid = sqlite3DbMallocRaw(db, n);
+ p->u.aRowid = sqlite3DbMallocRawNN(db, n);
if( p->u.aRowid ){
p->nRowid = n;
memcpy(p->u.aRowid, pData, n);
@@ -1115,7 +1115,7 @@ static void analyzeOneTable(
if( nColTest>0 ){
int endDistinctTest = sqlite3VdbeMakeLabel(v);
int *aGotoChng; /* Array of jump instruction addresses */
- aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*nColTest);
+ aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest);
if( aGotoChng==0 ) continue;
/*
@@ -1523,7 +1523,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** the old data with the new instead of allocating a new array. */
if( pIndex->aiRowEst==0 ){
pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol);
- if( pIndex->aiRowEst==0 ) pInfo->db->mallocFailed = 1;
+ if( pIndex->aiRowEst==0 ) sqlite3OomFault(pInfo->db);
}
aiRowEst = pIndex->aiRowEst;
#endif
@@ -1670,7 +1670,7 @@ static int loadStatTbl(
Index *pPrevIdx = 0; /* Previous index in the loop */
IndexSample *pSample; /* A slot in pIdx->aSample[] */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
zSql = sqlite3MPrintf(db, zSql1, zDb);
if( !zSql ){
return SQLITE_NOMEM;
@@ -1784,7 +1784,7 @@ static int loadStatTbl(
static int loadStat4(sqlite3 *db, const char *zDb){
int rc = SQLITE_OK; /* Result codes from subroutines */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
rc = loadStatTbl(db, 0,
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
@@ -1866,10 +1866,9 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load the statistics from the sqlite_stat4 table. */
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( rc==SQLITE_OK && OptimizationEnabled(db, SQLITE_Stat34) ){
- int lookasideEnabled = db->lookaside.bEnabled;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
rc = loadStat4(db, sInfo.zDatabase);
- db->lookaside.bEnabled = lookasideEnabled;
+ db->lookaside.bDisable--;
}
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
@@ -1879,7 +1878,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
#endif
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return rc;
}
diff --git a/src/attach.c b/src/attach.c
index fd5cc7c76c..2288ac9b62 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -109,7 +109,7 @@ static void attachFunc(
** hash tables.
*/
if( db->aDb==db->aDbStatic ){
- aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 );
+ aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
if( aNew==0 ) return;
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
}else{
@@ -127,7 +127,7 @@ static void attachFunc(
flags = db->openFlags;
rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
return;
@@ -156,7 +156,8 @@ static void attachFunc(
sqlite3BtreeSecureDelete(aNew->pBt,
sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
- sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK));
+ sqlite3BtreeSetPagerFlags(aNew->pBt,
+ PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK));
#endif
sqlite3BtreeLeave(aNew->pBt);
}
@@ -229,7 +230,7 @@ static void attachFunc(
sqlite3ResetAllSchemasOfConnection(db);
db->nDb = iDb;
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, zErrDyn);
zErrDyn = sqlite3MPrintf(db, "out of memory");
}else if( zErrDyn==0 ){
diff --git a/src/btree.c b/src/btree.c
index 4e6f6478af..c6f9c34f7b 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -2338,7 +2338,6 @@ int sqlite3BtreeOpen(
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
if( pBt->mutex==0 ){
rc = SQLITE_NOMEM;
- db->mallocFailed = 0;
goto btree_open_out;
}
}
@@ -4049,13 +4048,13 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
** on the database already. If a write-cursor is requested, then
** the caller is assumed to have an open write transaction.
**
-** If wrFlag==0, then the cursor can only be used for reading.
-** If wrFlag==1, then the cursor can be used for reading or for
-** writing if other conditions for writing are also met. These
-** are the conditions that must be met in order for writing to
-** be allowed:
+** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only
+** be used for reading. If the BTREE_WRCSR bit is set, then the cursor
+** can be used for reading or for writing if other conditions for writing
+** are also met. These are the conditions that must be met in order
+** for writing to be allowed:
**
-** 1: The cursor must have been opened with wrFlag==1
+** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR
**
** 2: Other database connections that share the same pager cache
** but which are not in the READ_UNCOMMITTED state may not have
@@ -4067,6 +4066,16 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
**
** 4: There must be an active transaction.
**
+** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR
+** is set. If FORDELETE is set, that is a hint to the implementation that
+** this cursor will only be used to seek to and delete entries of an index
+** as part of a larger DELETE statement. The FORDELETE hint is not used by
+** this implementation. But in a hypothetical alternative storage engine
+** in which index entries are automatically deleted when corresponding table
+** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE
+** operations on this cursor can be no-ops and all READ operations can
+** return a null row (2-bytes: 0x01 0x00).
+**
** No checking is done to make sure that page iTable really is the
** root page of a b-tree. If it is not, then the cursor acquired
** will not work correctly.
@@ -6139,7 +6148,7 @@ static int fillInCell(
{
CellInfo info;
pPage->xParseCell(pPage, pCell, &info);
- assert( nHeader=(int)(info.pPayload - pCell) );
+ assert( nHeader==(int)(info.pPayload - pCell) );
assert( info.nKey==nKey );
assert( *pnSize == info.nSize );
assert( spaceLeft == info.nLocal );
@@ -7798,8 +7807,8 @@ static int balance(BtCursor *pCur){
u8 aBalanceQuickSpace[13];
u8 *pFree = 0;
- TESTONLY( int balance_quick_called = 0 );
- TESTONLY( int balance_deeper_called = 0 );
+ VVA_ONLY( int balance_quick_called = 0 );
+ VVA_ONLY( int balance_deeper_called = 0 );
do {
int iPage = pCur->iPage;
@@ -7812,7 +7821,8 @@ static int balance(BtCursor *pCur){
** and copy the current contents of the root-page to it. The
** next iteration of the do-loop will balance the child page.
*/
- assert( (balance_deeper_called++)==0 );
+ assert( balance_deeper_called==0 );
+ VVA_ONLY( balance_deeper_called++ );
rc = balance_deeper(pPage, &pCur->apPage[1]);
if( rc==SQLITE_OK ){
pCur->iPage = 1;
@@ -7851,7 +7861,8 @@ static int balance(BtCursor *pCur){
** function. If this were not verified, a subtle bug involving reuse
** of the aBalanceQuickSpace[] might sneak in.
*/
- assert( (balance_quick_called++)==0 );
+ assert( balance_quick_called==0 );
+ VVA_ONLY( balance_quick_called++ );
rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
}else
#endif
@@ -8082,13 +8093,21 @@ end_insert:
/*
** Delete the entry that the cursor is pointing to.
**
-** If the second parameter is zero, then the cursor is left pointing at an
-** arbitrary location after the delete. If it is non-zero, then the cursor
-** is left in a state such that the next call to BtreeNext() or BtreePrev()
-** moves it to the same row as it would if the call to BtreeDelete() had
-** been omitted.
+** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then
+** the cursor is left pointing at an arbitrary location after the delete.
+** But if that bit is set, then the cursor is left in a state such that
+** the next call to BtreeNext() or BtreePrev() moves it to the same row
+** as it would have been on if the call to BtreeDelete() had been omitted.
+**
+** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes
+** associated with a single table entry and its indexes. Only one of those
+** deletes is considered the "primary" delete. The primary delete occurs
+** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete
+** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag.
+** The BTREE_AUXDELETE bit is a hint that is not used by this implementation,
+** but which might be used by alternative storage engines.
*/
-int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
+int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
Btree *p = pCur->pBtree;
BtShared *pBt = p->pBt;
int rc; /* Return code */
@@ -8098,6 +8117,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
int iCellDepth; /* Depth of node containing pCell */
u16 szCell; /* Size of the cell being deleted */
int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
+ u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */
assert( cursorOwnsBtShared(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
@@ -8107,6 +8127,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell );
assert( pCur->eState==CURSOR_VALID );
+ assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 );
iCellDepth = pCur->iPage;
iCellIdx = pCur->aiIdx[iCellDepth];
@@ -8219,7 +8240,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
if( rc==SQLITE_OK ){
if( bSkipnext ){
assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) );
- assert( pPage==pCur->apPage[pCur->iPage] );
+ assert( pPage==pCur->apPage[pCur->iPage] || CORRUPT_DB );
assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
pCur->eState = CURSOR_SKIPNEXT;
if( iCellIdx>=pPage->nCell ){
@@ -8805,9 +8826,9 @@ static void checkAppendMsg(
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
}
if( pCheck->zPfx ){
- sqlite3XPrintf(&pCheck->errMsg, 0, pCheck->zPfx, pCheck->v1, pCheck->v2);
+ sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
}
- sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
+ sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
pCheck->mallocFailed = 1;
@@ -9308,7 +9329,8 @@ char *sqlite3BtreeIntegrityCheck(
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
- assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 );
+ VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
+ assert( nRef>=0 );
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = btreePagecount(sCheck.pBt);
@@ -9321,6 +9343,7 @@ char *sqlite3BtreeIntegrityCheck(
sCheck.aPgRef = 0;
sCheck.heap = 0;
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
+ sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
if( sCheck.nPage==0 ){
goto integrity_ck_cleanup;
}
diff --git a/src/btree.h b/src/btree.h
index 37a9915eda..30522e99e1 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -245,7 +245,12 @@ int sqlite3BtreeMovetoUnpacked(
);
int sqlite3BtreeCursorHasMoved(BtCursor*);
int sqlite3BtreeCursorRestore(BtCursor*, int*);
-int sqlite3BtreeDelete(BtCursor*, int);
+int sqlite3BtreeDelete(BtCursor*, u8 flags);
+
+/* Allowed flags for the 2nd argument to sqlite3BtreeDelete() */
+#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
+#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
+
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
const void *pData, int nData,
int nZero, int bias, int seekResult);
diff --git a/src/build.c b/src/build.c
index f7074ab417..250dc20d20 100644
--- a/src/build.c
+++ b/src/build.c
@@ -78,7 +78,7 @@ void sqlite3TableLock(
p->zName = zName;
}else{
pToplevel->nTableLock = 0;
- pToplevel->db->mallocFailed = 1;
+ sqlite3OomFault(pToplevel->db);
}
}
@@ -926,7 +926,7 @@ void sqlite3StartTable(
pTable = sqlite3DbMallocZero(db, sizeof(Table));
if( pTable==0 ){
- db->mallocFailed = 1;
+ assert( db->mallocFailed );
pParse->rc = SQLITE_NOMEM;
pParse->nErr++;
goto begin_table_error;
@@ -983,10 +983,8 @@ void sqlite3StartTable(
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);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, fileFormat);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, ENC(db));
sqlite3VdbeJumpHere(v, addr1);
/* This just creates a place-holder record in the sqlite_master table.
@@ -1471,13 +1469,11 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
** 1 chance in 2^32. So we're safe enough.
*/
void sqlite3ChangeCookie(Parse *pParse, int iDb){
- int r1 = sqlite3GetTempReg(pParse);
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
+ db->aDb[iDb].pSchema->schema_cookie+1);
}
/*
@@ -1559,7 +1555,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
n += 35 + 6*p->nCol;
zStmt = sqlite3DbMallocRaw(0, n);
if( zStmt==0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 0;
}
sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
@@ -1708,8 +1704,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
if( pTab->iPKey>=0 ){
ExprList *pList;
Token ipkToken;
- ipkToken.z = pTab->aCol[pTab->iPKey].zName;
- ipkToken.n = sqlite3Strlen30(ipkToken.z);
+ sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName);
pList = sqlite3ExprListAppend(pParse, 0,
sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0));
if( pList==0 ) return;
@@ -1959,7 +1954,7 @@ void sqlite3EndTable(
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
sqlite3Select(pParse, pSelect, &dest);
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+ sqlite3VdbeEndCoroutine(v, regYield);
sqlite3VdbeJumpHere(v, addrTop - 1);
if( pParse->nErr ) return;
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
@@ -2043,7 +2038,7 @@ void sqlite3EndTable(
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return;
}
pParse->pNewTable = 0;
@@ -2147,7 +2142,6 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
int n; /* Temporarily holds the number of cursors assigned */
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
sqlite3_xauth xAuth; /* Saved xAuth pointer */
- u8 bEnabledLA; /* Saved db->lookaside.bEnabled state */
assert( pTable );
@@ -2193,18 +2187,18 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
** statement that defines the view.
*/
assert( pTable->pSelect );
- bEnabledLA = db->lookaside.bEnabled;
if( pTable->pCheck ){
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
&pTable->nCol, &pTable->aCol);
+ db->lookaside.bDisable--;
}else{
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
if( pSel ){
n = pParse->nTab;
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
pTable->nCol = -1;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
@@ -2213,6 +2207,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#else
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif
+ db->lookaside.bDisable--;
pParse->nTab = n;
if( pSelTab ){
assert( pTable->aCol==0 );
@@ -2231,7 +2226,6 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
nErr++;
}
}
- db->lookaside.bEnabled = bEnabledLA;
pTable->pSchema->schemaFlags |= DB_UnresetViews;
#endif /* SQLITE_OMIT_VIEW */
return nErr;
@@ -2697,7 +2691,7 @@ void sqlite3CreateForeignKey(
pFKey->zTo, (void *)pFKey
);
if( pNextTo==pFKey ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto fk_end;
}
if( pNextTo ){
@@ -3057,8 +3051,7 @@ Index *sqlite3CreateIndex(
*/
if( pList==0 ){
Token prevCol;
- prevCol.z = pTab->aCol[pTab->nCol-1].zName;
- prevCol.n = sqlite3Strlen30(prevCol.z);
+ sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol-1].zName);
pList = sqlite3ExprListAppend(pParse, 0,
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
@@ -3280,7 +3273,7 @@ Index *sqlite3CreateIndex(
pIndex->zName, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto exit_create_index;
}
db->flags |= SQLITE_InternChanges;
@@ -3709,8 +3702,9 @@ SrcList *sqlite3SrcListAppend(
){
struct SrcList_item *pItem;
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
+ assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocRaw(db, sizeof(SrcList) );
+ pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) );
if( pList==0 ) return 0;
pList->nAlloc = 1;
pList->nSrc = 0;
@@ -3894,7 +3888,7 @@ void sqlite3SrcListShiftJoinType(SrcList *p){
}
/*
-** Begin a transaction
+** Generate VDBE code for a BEGIN statement.
*/
void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3 *db;
@@ -3904,7 +3898,6 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
assert( pParse!=0 );
db = pParse->db;
assert( db!=0 );
-/* if( db->aDb[0].pBt==0 ) return; */
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){
return;
}
@@ -3916,11 +3909,11 @@ void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
- sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_AutoCommit);
}
/*
-** Commit a transaction
+** Generate VDBE code for a COMMIT statement.
*/
void sqlite3CommitTransaction(Parse *pParse){
Vdbe *v;
@@ -3932,12 +3925,12 @@ void sqlite3CommitTransaction(Parse *pParse){
}
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 0);
+ sqlite3VdbeAddOp1(v, OP_AutoCommit, 1);
}
}
/*
-** Rollback a transaction
+** Generate VDBE code for a ROLLBACK statement.
*/
void sqlite3RollbackTransaction(Parse *pParse){
Vdbe *v;
@@ -3999,7 +3992,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){
db->aDb[1].pBt = pBt;
assert( db->aDb[1].pSchema );
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 1;
}
}
@@ -4134,14 +4127,14 @@ void sqlite3UniqueConstraint(
sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
if( pIdx->aColExpr ){
- sqlite3XPrintf(&errMsg, 0, "index '%q'", pIdx->zName);
+ sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
}else{
for(j=0; jnKeyCol; j++){
char *zCol;
assert( pIdx->aiColumn[j]>=0 );
zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
- sqlite3XPrintf(&errMsg, 0, "%s.%s", pTab->zName, zCol);
+ sqlite3XPrintf(&errMsg, "%s.%s", pTab->zName, zCol);
}
}
zErr = sqlite3StrAccumFinish(&errMsg);
@@ -4374,10 +4367,9 @@ With *sqlite3WithAdd(
}else{
pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
}
- assert( zName!=0 || pNew==0 );
- assert( db->mallocFailed==0 || pNew==0 );
+ assert( (pNew!=0 && zName!=0) || db->mallocFailed );
- if( pNew==0 ){
+ if( db->mallocFailed ){
sqlite3ExprListDelete(db, pArglist);
sqlite3SelectDelete(db, pQuery);
sqlite3DbFree(db, zName);
diff --git a/src/callback.c b/src/callback.c
index 2a9f5b802a..2b955fdcdb 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -177,7 +177,7 @@ static CollSeq *findCollSeqEntry(
*/
assert( pDel==0 || pDel==pColl );
if( pDel!=0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pColl = 0;
}
@@ -465,7 +465,7 @@ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
}
if( !p ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if ( 0==p->file_format ){
sqlite3HashInit(&p->tblHash);
sqlite3HashInit(&p->idxHash);
diff --git a/src/date.c b/src/date.c
index d78e83cf57..0a1d0e0798 100644
--- a/src/date.c
+++ b/src/date.c
@@ -967,7 +967,7 @@ static void strftimeFunc(
sqlite3_result_error_toobig(context);
return;
}else{
- z = sqlite3DbMallocRaw(db, (int)n);
+ z = sqlite3DbMallocRawNN(db, (int)n);
if( z==0 ){
sqlite3_result_error_nomem(context);
return;
diff --git a/src/dbstat.c b/src/dbstat.c
index ae55d6b803..5e42cdfe38 100644
--- a/src/dbstat.c
+++ b/src/dbstat.c
@@ -149,7 +149,9 @@ static int statConnect(
int iDb;
if( argc>=4 ){
- iDb = sqlite3FindDbName(db, argv[3]);
+ Token nm;
+ sqlite3TokenInit(&nm, (char*)argv[3]);
+ iDb = sqlite3FindDb(db, &nm);
if( iDb<0 ){
*pzErr = sqlite3_mprintf("no such database: %s", argv[3]);
return SQLITE_ERROR;
diff --git a/src/delete.c b/src/delete.c
index ed273bde81..0fe064bc45 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -439,7 +439,7 @@ void sqlite3DeleteFrom(
** one, so just keep it in its register(s) and fall through to the
** delete code. */
nKey = nPk; /* OP_Found will use an unpacked key */
- aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
+ aToOpen = sqlite3DbMallocRawNN(db, nIdx+2);
if( aToOpen==0 ){
sqlite3WhereEnd(pWInfo);
goto delete_from_cleanup;
@@ -479,13 +479,12 @@ void sqlite3DeleteFrom(
*/
if( !isView ){
int iAddrOnce = 0;
- u8 p5 = (eOnePass==ONEPASS_OFF ? 0 : OPFLAG_FORDELETE);
if( eOnePass==ONEPASS_MULTI ){
iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
}
testcase( IsVirtual(pTab) );
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, p5, iTabCur,
- aToOpen, &iDataCur, &iIdxCur);
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,
+ iTabCur, aToOpen, &iDataCur, &iIdxCur);
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
@@ -718,15 +717,20 @@ void sqlite3GenerateRowDelete(
** a view (in which case the only effect of the DELETE statement is to
** fire the INSTEAD OF triggers). */
if( pTab->pSelect==0 ){
+ u8 p5 = 0;
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
if( count ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}
+ if( eMode!=ONEPASS_OFF ){
+ sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE);
+ }
if( iIdxNoSeek>=0 ){
sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);
}
- sqlite3VdbeChangeP5(v, eMode==ONEPASS_MULTI);
+ if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION;
+ sqlite3VdbeChangeP5(v, p5);
}
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
diff --git a/src/expr.c b/src/expr.c
index 403e81cf34..8d96ba10cd 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -85,8 +85,7 @@ Expr *sqlite3ExprAddCollateToken(
Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
Token s;
assert( zC!=0 );
- s.z = zC;
- s.n = sqlite3Strlen30(s.z);
+ sqlite3TokenInit(&s, (char*)zC);
return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
}
@@ -454,6 +453,7 @@ Expr *sqlite3ExprAlloc(
int nExtra = 0;
int iValue = 0;
+ assert( db!=0 );
if( pToken ){
if( op!=TK_INTEGER || pToken->z==0
|| sqlite3GetInt32(pToken->z, &iValue)==0 ){
@@ -461,7 +461,7 @@ Expr *sqlite3ExprAlloc(
assert( iValue>=0 );
}
}
- pNew = sqlite3DbMallocRaw(db, sizeof(Expr)+nExtra);
+ pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra);
if( pNew ){
memset(pNew, 0, sizeof(Expr));
pNew->op = (u8)op;
@@ -700,7 +700,10 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
if( x>pParse->nzVar ){
char **a;
a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
- if( a==0 ) return; /* Error reported through db->mallocFailed */
+ if( a==0 ){
+ assert( db->mallocFailed ); /* Error reported through mallocFailed */
+ return;
+ }
pParse->azVar = a;
memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
pParse->nzVar = x;
@@ -855,6 +858,7 @@ static int dupedExprSize(Expr *p, int flags){
static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
Expr *pNew = 0; /* Value to return */
assert( flags==0 || flags==EXPRDUP_REDUCE );
+ assert( db!=0 );
if( p ){
const int isReduced = (flags&EXPRDUP_REDUCE);
u8 *zAlloc;
@@ -867,7 +871,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
zAlloc = *pzBuffer;
staticFlag = EP_Static;
}else{
- zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags));
+ zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, flags));
}
pNew = (Expr *)zAlloc;
@@ -990,12 +994,13 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
ExprList *pNew;
struct ExprList_item *pItem, *pOldItem;
int i;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
pNew->nExpr = i = p->nExpr;
if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; inExpr; i+=i){}
- pNew->a = pItem = sqlite3DbMallocRaw(db, i*sizeof(p->a[0]) );
+ pNew->a = pItem = sqlite3DbMallocRawNN(db, i*sizeof(p->a[0]) );
if( pItem==0 ){
sqlite3DbFree(db, pNew);
return 0;
@@ -1026,9 +1031,10 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
SrcList *pNew;
int i;
int nByte;
+ assert( db!=0 );
if( p==0 ) return 0;
nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
- pNew = sqlite3DbMallocRaw(db, nByte );
+ pNew = sqlite3DbMallocRawNN(db, nByte );
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; inSrc; i++){
@@ -1065,11 +1071,12 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
IdList *pNew;
int i;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
pNew->nId = p->nId;
- pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) );
+ pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) );
if( pNew->a==0 ){
sqlite3DbFree(db, pNew);
return 0;
@@ -1087,8 +1094,9 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
}
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
Select *pNew, *pPrior;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*p) );
if( pNew==0 ) return 0;
pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags);
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
@@ -1134,13 +1142,14 @@ ExprList *sqlite3ExprListAppend(
Expr *pExpr /* Expression to be appended. Might be NULL */
){
sqlite3 *db = pParse->db;
+ assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocRaw(db, sizeof(ExprList) );
+ pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) );
if( pList==0 ){
goto no_mem;
}
pList->nExpr = 0;
- pList->a = sqlite3DbMallocRaw(db, sizeof(pList->a[0]));
+ pList->a = sqlite3DbMallocRawNN(db, sizeof(pList->a[0]));
if( pList->a==0 ) goto no_mem;
}else if( (pList->nExpr & (pList->nExpr-1))==0 ){
struct ExprList_item *a;
diff --git a/src/fkey.c b/src/fkey.c
index 2abd06c693..38fd4f756b 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -219,7 +219,7 @@ int sqlite3FkLocateIndex(
}
}else if( paiCol ){
assert( nCol>1 );
- aiCol = (int *)sqlite3DbMallocRaw(pParse->db, nCol*sizeof(int));
+ aiCol = (int *)sqlite3DbMallocRawNN(pParse->db, nCol*sizeof(int));
if( !aiCol ) return 1;
*paiCol = aiCol;
}
@@ -1165,7 +1165,6 @@ static Trigger *fkActionTrigger(
pTrigger = pFKey->apTrigger[iAction];
if( action!=OE_None && !pTrigger ){
- u8 enableLookaside; /* Copy of db->lookaside.bEnabled */
char const *zFrom; /* Name of child table */
int nFrom; /* Length in bytes of zFrom */
Index *pIdx = 0; /* Parent key index for this FK */
@@ -1192,11 +1191,9 @@ static Trigger *fkActionTrigger(
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;
-
- tToCol.n = sqlite3Strlen30(tToCol.z);
- tFromCol.n = sqlite3Strlen30(tFromCol.z);
+ sqlite3TokenInit(&tToCol,
+ pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName);
+ sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName);
/* Create the expression "OLD.zToCol = zFromCol". It is important
** that the "OLD.zToCol" term is on the LHS of the = operator, so
@@ -1276,8 +1273,7 @@ static Trigger *fkActionTrigger(
}
/* Disable lookaside memory allocation */
- enableLookaside = db->lookaside.bEnabled;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
pTrigger = (Trigger *)sqlite3DbMallocZero(db,
sizeof(Trigger) + /* struct Trigger */
@@ -1299,7 +1295,7 @@ static Trigger *fkActionTrigger(
}
/* Re-enable the lookaside buffer, if it was disabled earlier. */
- db->lookaside.bEnabled = enableLookaside;
+ db->lookaside.bDisable--;
sqlite3ExprDelete(db, pWhere);
sqlite3ExprDelete(db, pWhen);
diff --git a/src/func.c b/src/func.c
index b927296782..d615cf90b1 100644
--- a/src/func.c
+++ b/src/func.c
@@ -239,7 +239,8 @@ static void printfFunc(
x.nUsed = 0;
x.apArg = argv+1;
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
- sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x);
+ str.printfFlags = SQLITE_PRINTF_SQLFUNC;
+ sqlite3XPrintf(&str, zFormat, &x);
n = str.nChar;
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
SQLITE_DYNAMIC);
@@ -1614,7 +1615,7 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
int rc = sqlite3_overload_function(db, "MATCH", 2);
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
}
diff --git a/src/insert.c b/src/insert.c
index 8082bcb459..650f397de0 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -83,7 +83,7 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
Table *pTab = pIdx->pTable;
pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
if( !pIdx->zColAff ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 0;
}
for(n=0; nnColumn; n++){
@@ -134,7 +134,7 @@ void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
sqlite3 *db = sqlite3VdbeDb(v);
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
if( !zColAff ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return;
}
@@ -230,7 +230,7 @@ static int autoIncBegin(
pInfo = pToplevel->pAinc;
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
- pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo));
+ pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
if( pInfo==0 ) return 0;
pInfo->pNext = pToplevel->pAinc;
pToplevel->pAinc = pInfo;
@@ -254,7 +254,6 @@ void sqlite3AutoincrementBegin(Parse *pParse){
sqlite3 *db = pParse->db; /* The database connection */
Db *pDb; /* Database only autoinc table */
int memId; /* Register holding max rowid */
- int addr; /* A VDBE address */
Vdbe *v = pParse->pVdbe; /* VDBE under construction */
/* This routine is never called during trigger-generation. It is
@@ -264,33 +263,46 @@ void sqlite3AutoincrementBegin(Parse *pParse){
assert( v ); /* We failed long ago if this is not so */
for(p = pParse->pAinc; p; p = p->pNext){
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList autoInc[] = {
+ /* 0 */ {OP_Null, 0, 0, 0},
+ /* 1 */ {OP_Rewind, 0, 9, 0},
+ /* 2 */ {OP_Column, 0, 0, 0},
+ /* 3 */ {OP_Ne, 0, 7, 0},
+ /* 4 */ {OP_Rowid, 0, 0, 0},
+ /* 5 */ {OP_Column, 0, 1, 0},
+ /* 6 */ {OP_Goto, 0, 9, 0},
+ /* 7 */ {OP_Next, 0, 2, 0},
+ /* 8 */ {OP_Integer, 0, 0, 0},
+ /* 9 */ {OP_Close, 0, 0, 0}
+ };
+ VdbeOp *aOp;
pDb = &db->aDb[p->iDb];
memId = p->regCtr;
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
- addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeLoadString(v, memId-1, p->pTab->zName);
- sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId);
- sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); VdbeCoverage(v);
- sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1);
- sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId);
- sqlite3VdbeGoto(v, addr+9);
- sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, memId);
- sqlite3VdbeAddOp0(v, OP_Close);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn);
+ if( aOp==0 ) break;
+ aOp[0].p2 = memId;
+ aOp[0].p3 = memId+1;
+ aOp[2].p3 = memId;
+ aOp[3].p1 = memId-1;
+ aOp[3].p3 = memId;
+ aOp[3].p5 = SQLITE_JUMPIFNULL;
+ aOp[4].p2 = memId+1;
+ aOp[5].p3 = memId;
+ aOp[8].p2 = memId;
}
}
/*
** Update the maximum rowid for an autoincrement calculation.
**
-** This routine should be called when the top of the stack holds a
+** This routine should be called when the regRowid register holds a
** new rowid that is about to be inserted. If that new rowid is
** larger than the maximum rowid in the memId memory cell, then the
-** memory cell is updated. The stack is unchanged.
+** memory cell is updated.
*/
static void autoIncStep(Parse *pParse, int memId, int regRowid){
if( memId>0 ){
@@ -305,31 +317,44 @@ static void autoIncStep(Parse *pParse, int memId, int regRowid){
** table (either directly or through triggers) needs to call this
** routine just before the "exit" code.
*/
-void sqlite3AutoincrementEnd(Parse *pParse){
+static SQLITE_NOINLINE void autoIncrementEnd(Parse *pParse){
AutoincInfo *p;
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
assert( v );
for(p = pParse->pAinc; p; p = p->pNext){
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList autoIncEnd[] = {
+ /* 0 */ {OP_NotNull, 0, 2, 0},
+ /* 1 */ {OP_NewRowid, 0, 0, 0},
+ /* 2 */ {OP_MakeRecord, 0, 2, 0},
+ /* 3 */ {OP_Insert, 0, 0, 0},
+ /* 4 */ {OP_Close, 0, 0, 0}
+ };
+ VdbeOp *aOp;
Db *pDb = &db->aDb[p->iDb];
- 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);
- addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1);
- sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec);
- sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1);
- sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- sqlite3VdbeAddOp0(v, OP_Close);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn);
+ if( aOp==0 ) break;
+ aOp[0].p1 = memId+1;
+ aOp[1].p2 = memId+1;
+ aOp[2].p1 = memId-1;
+ aOp[2].p3 = iRec;
+ aOp[3].p2 = iRec;
+ aOp[3].p3 = memId+1;
+ aOp[3].p5 = OPFLAG_APPEND;
sqlite3ReleaseTempReg(pParse, iRec);
}
}
+void sqlite3AutoincrementEnd(Parse *pParse){
+ if( pParse->pAinc ) autoIncrementEnd(pParse);
+}
#else
/*
** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines
@@ -660,7 +685,7 @@ void sqlite3Insert(
rc = sqlite3Select(pParse, pSelect, &dest);
regFromSelect = dest.iSdst;
if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup;
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+ sqlite3VdbeEndCoroutine(v, regYield);
sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
@@ -762,7 +787,7 @@ void sqlite3Insert(
int nIdx;
nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
&iDataCur, &iIdxCur);
- aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1));
+ aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1));
if( aRegIdx==0 ){
goto insert_cleanup;
}
@@ -1647,7 +1672,7 @@ int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table to be opened */
int op, /* OP_OpenRead or OP_OpenWrite */
- u8 p5, /* P5 value for OP_Open* instructions */
+ u8 p5, /* P5 value for OP_Open* opcodes (except on WITHOUT ROWID) */
int iBase, /* Use this for the table cursor, if there is one */
u8 *aToOpen, /* If not NULL: boolean for each table and index */
int *piDataCur, /* Write the database source cursor number here */
@@ -1682,15 +1707,16 @@ int sqlite3OpenTableAndIndices(
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
int iIdxCur = iBase++;
assert( pIdx->pSchema==pTab->pSchema );
- if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) && piDataCur ){
- *piDataCur = iIdxCur;
- }
if( aToOpen==0 || aToOpen[i+1] ){
sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
- sqlite3VdbeChangeP5(v, p5);
VdbeComment((v, "%s", pIdx->zName));
}
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
+ if( piDataCur ) *piDataCur = iIdxCur;
+ }else{
+ sqlite3VdbeChangeP5(v, p5);
+ }
}
if( iBase>pParse->nTab ) pParse->nTab = iBase;
return i;
diff --git a/src/legacy.c b/src/legacy.c
index a10006e558..1b5e518d43 100644
--- a/src/legacy.c
+++ b/src/legacy.c
@@ -90,7 +90,7 @@ int sqlite3_exec(
for(i=0; imallocFailed = 1;
+ sqlite3OomFault(db);
goto exec_out;
}
}
diff --git a/src/main.c b/src/main.c
index 63a105c3f0..922af1315a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -698,12 +698,12 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
p = (LookasideSlot*)&((u8*)p)[sz];
}
db->lookaside.pEnd = p;
- db->lookaside.bEnabled = 1;
+ db->lookaside.bDisable = 0;
db->lookaside.bMalloced = pBuf==0 ?1:0;
}else{
db->lookaside.pStart = db;
db->lookaside.pEnd = db;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable = 1;
db->lookaside.bMalloced = 0;
}
#endif /* SQLITE_OMIT_LOOKASIDE */
@@ -2208,7 +2208,7 @@ const void *sqlite3_errmsg16(sqlite3 *db){
** be cleared before returning. Do this directly, instead of via
** sqlite3ApiExit(), to avoid setting the database handle error message.
*/
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
}
sqlite3_mutex_leave(db->mutex);
return z;
@@ -2846,7 +2846,7 @@ static int openDatabase(
db->openFlags = flags;
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
sqlite3_free(zErrMsg);
goto opendb_out;
@@ -3566,7 +3566,7 @@ int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_ASSERT: {
volatile int x = 0;
- assert( (x = va_arg(ap,int))!=0 );
+ assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
break;
}
diff --git a/src/malloc.c b/src/malloc.c
index c8a04128cf..ebe0440350 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -575,10 +575,24 @@ void *sqlite3MallocZero(u64 n){
** the mallocFailed flag in the connection pointer.
*/
void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
- void *p = sqlite3DbMallocRaw(db, n);
- if( p ){
- memset(p, 0, (size_t)n);
- }
+ void *p;
+ testcase( db==0 );
+ p = sqlite3DbMallocRaw(db, n);
+ if( p ) memset(p, 0, (size_t)n);
+ return p;
+}
+
+
+/* Finish the work of sqlite3DbMallocRawNN for the unusual and
+** slower case when the allocation cannot be fulfilled using lookaside.
+*/
+static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
+ void *p;
+ assert( db!=0 );
+ p = sqlite3Malloc(n);
+ if( !p ) sqlite3OomFault(db);
+ sqlite3MemdebugSetType(p,
+ (db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
return p;
}
@@ -600,67 +614,73 @@ void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
**
** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
** that all prior mallocs (ex: "a") worked too.
+**
+** The sqlite3MallocRawNN() variant guarantees that the "db" parameter is
+** not a NULL pointer.
*/
-static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n);
void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
- assert( db==0 || db->pnBytesFreed==0 );
+ void *p;
+ if( db ) return sqlite3DbMallocRawNN(db, n);
+ p = sqlite3Malloc(n);
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
+ return p;
+}
+void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
#ifndef SQLITE_OMIT_LOOKASIDE
- if( db ){
- LookasideSlot *pBuf;
- if( db->mallocFailed ){
- return 0;
- }
- if( db->lookaside.bEnabled ){
- if( n>db->lookaside.sz ){
- db->lookaside.anStat[1]++;
- }else if( (pBuf = db->lookaside.pFree)==0 ){
- db->lookaside.anStat[2]++;
- }else{
- db->lookaside.pFree = pBuf->pNext;
- db->lookaside.nOut++;
- db->lookaside.anStat[0]++;
- if( db->lookaside.nOut>db->lookaside.mxOut ){
- db->lookaside.mxOut = db->lookaside.nOut;
- }
- return (void*)pBuf;
+ LookasideSlot *pBuf;
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( db->pnBytesFreed==0 );
+ if( db->lookaside.bDisable==0 ){
+ assert( db->mallocFailed==0 );
+ if( n>db->lookaside.sz ){
+ db->lookaside.anStat[1]++;
+ }else if( (pBuf = db->lookaside.pFree)==0 ){
+ db->lookaside.anStat[2]++;
+ }else{
+ db->lookaside.pFree = pBuf->pNext;
+ db->lookaside.nOut++;
+ db->lookaside.anStat[0]++;
+ if( db->lookaside.nOut>db->lookaside.mxOut ){
+ db->lookaside.mxOut = db->lookaside.nOut;
}
+ return (void*)pBuf;
}
+ }else if( db->mallocFailed ){
+ return 0;
}
#else
- if( db && db->mallocFailed ){
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( db->pnBytesFreed==0 );
+ if( db->mallocFailed ){
return 0;
}
#endif
return dbMallocRawFinish(db, n);
}
-static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
- void *p = sqlite3Malloc(n);
- if( !p && db ){
- db->mallocFailed = 1;
- }
- sqlite3MemdebugSetType(p,
- (db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
- return p;
-}
+
+/* Forward declaration */
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n);
/*
** Resize the block of memory pointed to by p to n bytes. If the
** resize fails, set the mallocFailed flag in the connection object.
*/
void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
+ assert( db!=0 );
+ if( p==0 ) return sqlite3DbMallocRawNN(db, n);
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
+ return dbReallocFinish(db, p, n);
+}
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
void *pNew = 0;
assert( db!=0 );
- assert( sqlite3_mutex_held(db->mutex) );
+ assert( p!=0 );
if( db->mallocFailed==0 ){
- if( p==0 ){
- return sqlite3DbMallocRaw(db, n);
- }
if( isLookaside(db, p) ){
- if( n<=db->lookaside.sz ){
- return p;
- }
- pNew = sqlite3DbMallocRaw(db, n);
+ pNew = sqlite3DbMallocRawNN(db, n);
if( pNew ){
memcpy(pNew, p, db->lookaside.sz);
sqlite3DbFree(db, p);
@@ -671,10 +691,10 @@ void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
pNew = sqlite3_realloc64(p, n);
if( !pNew ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
sqlite3MemdebugSetType(pNew,
- (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
+ (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
}
}
return pNew;
@@ -716,11 +736,12 @@ char *sqlite3DbStrDup(sqlite3 *db, const char *z){
}
char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
char *zNew;
+ assert( db!=0 );
if( z==0 ){
return 0;
}
assert( (n&0x7fffffff)==n );
- zNew = sqlite3DbMallocRaw(db, n+1);
+ zNew = sqlite3DbMallocRawNN(db, n+1);
if( zNew ){
memcpy(zNew, z, (size_t)n);
zNew[n] = 0;
@@ -736,11 +757,43 @@ void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
*pz = sqlite3DbStrDup(db, zNew);
}
+/*
+** Call this routine to record the fact that an OOM (out-of-memory) error
+** has happened. This routine will set db->mallocFailed, and also
+** temporarily disable the lookaside memory allocator and interrupt
+** any running VDBEs.
+*/
+void sqlite3OomFault(sqlite3 *db){
+ if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
+ db->mallocFailed = 1;
+ if( db->nVdbeExec>0 ){
+ db->u1.isInterrupted = 1;
+ }
+ db->lookaside.bDisable++;
+ }
+}
+
+/*
+** This routine reactivates the memory allocator and clears the
+** db->mallocFailed flag as necessary.
+**
+** The memory allocator is not restarted if there are running
+** VDBEs.
+*/
+void sqlite3OomClear(sqlite3 *db){
+ if( db->mallocFailed && db->nVdbeExec==0 ){
+ db->mallocFailed = 0;
+ db->u1.isInterrupted = 0;
+ assert( db->lookaside.bDisable>0 );
+ db->lookaside.bDisable--;
+ }
+}
+
/*
** Take actions at the end of an API call to indicate an OOM error
*/
static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
sqlite3Error(db, SQLITE_NOMEM);
return SQLITE_NOMEM;
}
diff --git a/src/mem5.c b/src/mem5.c
index 7316a630a6..c194a6b778 100644
--- a/src/mem5.c
+++ b/src/mem5.c
@@ -322,11 +322,11 @@ static void memsys5FreeUnsafe(void *pOld){
int iBuddy;
if( (iBlock>>iLogsize) & 1 ){
iBuddy = iBlock - size;
+ assert( iBuddy>=0 );
}else{
iBuddy = iBlock + size;
+ if( iBuddy>=mem5.nBlock ) break;
}
- assert( iBuddy>=0 );
- if( (iBuddy+(1<mem5.nBlock ) break;
if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
memsys5Unlink(iBuddy, iLogsize);
iLogsize++;
diff --git a/src/os_unix.c b/src/os_unix.c
index 5bd6116945..fe1fc6af19 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -149,6 +149,11 @@
*/
#define MAX_PATHNAME 512
+/*
+** Maximum supported symbolic links
+*/
+#define SQLITE_MAX_SYMLINKS 100
+
/* Always cast the getpid() return type for compatibility with
** kernel modules in VxWorks. */
#define osGetpid(X) (pid_t)getpid()
@@ -475,6 +480,12 @@ static struct unix_syscall {
#endif
#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent)
+#if defined(HAVE_LSTAT)
+ { "lstat", (sqlite3_syscall_ptr)lstat, 0 },
+#else
+ { "lstat", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
}; /* End of the overrideable system calls */
@@ -5876,12 +5887,7 @@ static int unixDelete(
int fd;
rc = osOpenDirectory(zPath, &fd);
if( rc==SQLITE_OK ){
-#if OS_VXWORKS
- if( fsync(fd)==-1 )
-#else
- if( fsync(fd) )
-#endif
- {
+ if( full_fsync(fd,0,0) ){
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
}
robust_close(0, fd, __LINE__);
@@ -5927,6 +5933,32 @@ static int unixAccess(
return SQLITE_OK;
}
+/*
+**
+*/
+static int mkFullPathname(
+ const char *zPath, /* Input path */
+ char *zOut, /* Output buffer */
+ int nOut /* Allocated size of buffer zOut */
+){
+ int nPath = sqlite3Strlen30(zPath);
+ int iOff = 0;
+ if( zPath[0]!='/' ){
+ if( osGetcwd(zOut, nOut-2)==0 ){
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
+ }
+ iOff = sqlite3Strlen30(zOut);
+ zOut[iOff++] = '/';
+ }
+ if( (iOff+nPath+1)>nOut ){
+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer
+ ** even if it returns an error. */
+ zOut[iOff] = '\0';
+ return SQLITE_CANTOPEN_BKPT;
+ }
+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
+ return SQLITE_OK;
+}
/*
** Turn a relative pathname into a full pathname. The relative path
@@ -5943,7 +5975,17 @@ static int unixFullPathname(
int nOut, /* Size of output buffer in bytes */
char *zOut /* Output buffer */
){
+#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT)
+ return mkFullPathname(zPath, zOut, nOut);
+#else
+ int rc = SQLITE_OK;
int nByte;
+ int nLink = 1; /* Number of symbolic links followed so far */
+ const char *zIn = zPath; /* Input path for each iteration of loop */
+ char *zDel = 0;
+
+ assert( pVfs->mxPathname==MAX_PATHNAME );
+ UNUSED_PARAMETER(pVfs);
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
@@ -5952,60 +5994,62 @@ static int unixFullPathname(
*/
SimulateIOError( return SQLITE_ERROR );
- assert( pVfs->mxPathname==MAX_PATHNAME );
- UNUSED_PARAMETER(pVfs);
+ do {
-#if defined(HAVE_READLINK)
- /* Attempt to resolve the path as if it were a symbolic link. If it is
- ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if
- ** the identified file is not a symbolic link or does not exist, then
- ** zPath is copied directly into zOut. Either way, nByte is left set to
- ** the size of the string copied into zOut[] in bytes. */
- nByte = osReadlink(zPath, zOut, nOut-1);
- if( nByte<0 ){
- if( errno!=EINVAL && errno!=ENOENT ){
- return unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath);
+ /* Call stat() on path zIn. Set bLink to true if the path is a symbolic
+ ** link, or false otherwise. */
+ int bLink = 0;
+ struct stat buf;
+ if( osLstat(zIn, &buf)!=0 ){
+ if( errno!=ENOENT ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn);
+ }
+ }else{
+ bLink = S_ISLNK(buf.st_mode);
}
- sqlite3_snprintf(nOut, zOut, "%s", zPath);
- nByte = sqlite3Strlen30(zOut);
- }else{
- zOut[nByte] = '\0';
- }
-#endif
- /* If buffer zOut[] now contains an absolute path there is nothing more
- ** to do. If it contains a relative path, do the following:
- **
- ** * move the relative path string so that it is at the end of th
- ** zOut[] buffer.
- ** * Call getcwd() to read the path of the current working directory
- ** into the start of the zOut[] buffer.
- ** * Append a '/' character to the cwd string and move the
- ** relative path back within the buffer so that it immediately
- ** follows the '/'.
- **
- ** This code is written so that if the combination of the CWD and relative
- ** path are larger than the allocated size of zOut[] the CWD is silently
- ** truncated to make it fit. This is Ok, as SQLite refuses to open any
- ** file for which this function returns a full path larger than (nOut-8)
- ** bytes in size. */
- testcase( nByte==nOut-5 );
- testcase( nByte==nOut-4 );
- if( zOut[0]!='/' && nByteSQLITE_MAX_SYMLINKS ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }
+
+ if( rc==SQLITE_OK ){
+ nByte = osReadlink(zIn, zDel, nOut-1);
+ if( nByte<0 ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
+ }else{
+ if( zDel[0]!='/' ){
+ int n;
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
+ if( nByte+n+1>nOut ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }else{
+ memmove(&zDel[n], zDel, nByte+1);
+ memcpy(zDel, zIn, n);
+ nByte += n;
+ }
+ }
+ zDel[nByte] = '\0';
+ }
+ }
+
+ zIn = zDel;
}
- nCwd = sqlite3Strlen30(zOut);
- assert( nCwd<=nRem-1 );
- zOut[nCwd] = '/';
- memmove(&zOut[nCwd+1], &zOut[nRem], nByte+1);
- }
- return SQLITE_OK;
+ assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' );
+ if( rc==SQLITE_OK && zIn!=zOut ){
+ rc = mkFullPathname(zIn, zOut, nOut);
+ }
+ if( bLink==0 ) break;
+ zIn = zOut;
+ }while( rc==SQLITE_OK );
+
+ sqlite3_free(zDel);
+ return rc;
+#endif /* HAVE_READLINK && HAVE_LSTAT */
}
@@ -6187,7 +6231,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
return rc;
}
-#if 0 /* Not used */
+#ifndef SQLITE_OMIT_DEPRECATED
/*
** Find the current time (in Universal Coordinated Time). Write the
** current time and date as a Julian Day number into *prNow and
@@ -6205,7 +6249,7 @@ static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
# define unixCurrentTime 0
#endif
-#if 0 /* Not used */
+#ifndef SQLITE_OMIT_DEPRECATED
/*
** We added the xGetLastError() method with the intention of providing
** better low-level error messages when operating-system problems come up
@@ -6887,7 +6931,7 @@ static int proxyTakeConch(unixFile *pFile){
writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
robust_ftruncate(conchFile->h, writeSize);
rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
- fsync(conchFile->h);
+ full_fsync(conchFile->h,0,0);
/* If we created a new conch file (not just updated the contents of a
** valid conch file), try to match the permissions of the database
*/
@@ -7504,7 +7548,7 @@ int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==27 );
+ assert( ArraySize(aSyscall)==28 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
diff --git a/src/os_win.c b/src/os_win.c
index c54bfd6a90..eda6cf59fb 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -76,6 +76,10 @@
# define NTDDI_WINBLUE 0x06030000
#endif
+#ifndef NTDDI_WINTHRESHOLD
+# define NTDDI_WINTHRESHOLD 0x06040000
+#endif
+
/*
** Check to see if the GetVersionEx[AW] functions are deprecated on the
** target system. GetVersionEx was first deprecated in Win8.1.
@@ -88,6 +92,19 @@
# endif
#endif
+/*
+** Check to see if the CreateFileMappingA function is supported on the
+** target system. It is unavailable when using "mincore.lib" on Win10.
+** When compiling for Windows 10, always assume "mincore.lib" is in use.
+*/
+#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD
+# define SQLITE_WIN32_CREATEFILEMAPPINGA 0
+# else
+# define SQLITE_WIN32_CREATEFILEMAPPINGA 1
+# endif
+#endif
+
/*
** This constant should already be defined (in the "WinDef.h" SDK file).
*/
@@ -494,8 +511,9 @@ static struct win_syscall {
#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
-#if (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
- (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \
+ SQLITE_WIN32_CREATEFILEMAPPINGA
{ "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 },
#else
{ "CreateFileMappingA", (SYSCALL)0, 0 },
@@ -725,8 +743,7 @@ static struct win_syscall {
#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
-#if defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_GETVERSIONEX) && \
- SQLITE_WIN32_GETVERSIONEX
+#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX
{ "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
#else
{ "GetVersionExA", (SYSCALL)0, 0 },
@@ -736,7 +753,7 @@ static struct win_syscall {
LPOSVERSIONINFOA))aSyscall[34].pCurrent)
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
- defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+ SQLITE_WIN32_GETVERSIONEX
{ "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
#else
{ "GetVersionExW", (SYSCALL)0, 0 },
@@ -1347,7 +1364,7 @@ DWORD sqlite3Win32Wait(HANDLE hObject){
** the LockFileEx() API.
*/
-#if !defined(SQLITE_WIN32_GETVERSIONEX) || !SQLITE_WIN32_GETVERSIONEX
+#if !SQLITE_WIN32_GETVERSIONEX
# define osIsNT() (1)
#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
# define osIsNT() (1)
@@ -1368,7 +1385,7 @@ int sqlite3_win32_is_nt(void){
** kernel.
*/
return 1;
-#elif defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+#elif SQLITE_WIN32_GETVERSIONEX
if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
#if defined(SQLITE_WIN32_HAS_ANSI)
OSVERSIONINFOA sInfo;
@@ -3952,7 +3969,7 @@ static int winShmMap(
hMap = osCreateFileMappingW(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
-#elif defined(SQLITE_WIN32_HAS_ANSI)
+#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
hMap = osCreateFileMappingA(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
@@ -4108,7 +4125,7 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect,
(DWORD)((nMap>>32) & 0xffffffff),
(DWORD)(nMap & 0xffffffff), NULL);
-#elif defined(SQLITE_WIN32_HAS_ANSI)
+#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect,
(DWORD)((nMap>>32) & 0xffffffff),
(DWORD)(nMap & 0xffffffff), NULL);
diff --git a/src/pager.c b/src/pager.c
index 2c904d2df1..5c61968e36 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -428,6 +428,20 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
*/
#define MAX_SECTOR_SIZE 0x10000
+/*
+** If the option SQLITE_EXTRA_DURABLE option is set at compile-time, then
+** SQLite will do extra fsync() operations when synchronous==FULL to help
+** ensure that transactions are durable across a power failure. Most
+** applications are happy as long as transactions are consistent across
+** a power failure, and are perfectly willing to lose the last transaction
+** in exchange for the extra performance of avoiding directory syncs.
+** And so the default SQLITE_EXTRA_DURABLE setting is off.
+*/
+#ifndef SQLITE_EXTRA_DURABLE
+# define SQLITE_EXTRA_DURABLE 0
+#endif
+
+
/*
** An instance of the following structure is allocated for each active
** savepoint and statement transaction in the system. All such structures
@@ -623,6 +637,7 @@ struct Pager {
u8 useJournal; /* Use a rollback journal on this file */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
+ u8 extraSync; /* sync directory after journal delete */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
@@ -1983,7 +1998,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
);
sqlite3OsClose(pPager->jfd);
if( bDelete ){
- rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
+ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync);
}
}
}
@@ -3489,9 +3504,15 @@ void sqlite3PagerSetFlags(
unsigned pgFlags /* Various flags */
){
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
- assert( level>=1 && level<=3 );
- pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
- pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
+ if( pPager->tempFile ){
+ pPager->noSync = 1;
+ pPager->fullSync = 0;
+ pPager->extraSync = 0;
+ }else{
+ pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0;
+ pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
+ pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0;
+ }
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
@@ -4796,11 +4817,17 @@ act_like_temp_file:
pPager->noSync = pPager->tempFile;
if( pPager->noSync ){
assert( pPager->fullSync==0 );
+ assert( pPager->extraSync==0 );
assert( pPager->syncFlags==0 );
assert( pPager->walSyncFlags==0 );
assert( pPager->ckptSyncFlags==0 );
}else{
pPager->fullSync = 1;
+#if SQLITE_EXTRA_DURABLE
+ pPager->extraSync = 1;
+#else
+ pPager->extraSync = 0;
+#endif
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
diff --git a/src/pager.h b/src/pager.h
index 3552a876e7..8d9f08108d 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -90,11 +90,12 @@ typedef struct PgHdr DbPage;
#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
-#define PAGER_SYNCHRONOUS_MASK 0x03 /* Mask for three values above */
-#define PAGER_FULLFSYNC 0x04 /* PRAGMA fullfsync=ON */
-#define PAGER_CKPT_FULLFSYNC 0x08 /* PRAGMA checkpoint_fullfsync=ON */
-#define PAGER_CACHESPILL 0x10 /* PRAGMA cache_spill=ON */
-#define PAGER_FLAGS_MASK 0x1c /* All above except SYNCHRONOUS */
+#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */
+#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */
+#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */
+#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */
+#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */
+#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */
/*
** The remainder of this file contains the declarations of the functions
diff --git a/src/parse.y b/src/parse.y
index d6a587a2c8..0bfe4e473a 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -106,6 +106,15 @@ struct TrigEvent { int a; IdList * b; };
*/
struct AttachKey { int type; Token key; };
+/*
+** Disable lookaside memory allocation for objects that might be
+** shared across database connections.
+*/
+static void disableLookaside(Parse *pParse){
+ pParse->disableLookaside++;
+ pParse->db->lookaside.bDisable++;
+}
+
} // end %include
// Input is a single SQL command
@@ -156,7 +165,7 @@ create_table ::= createkw temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
sqlite3StartTable(pParse,&Y,&Z,T,0,0,E);
}
createkw(A) ::= CREATE(X). {
- pParse->db->lookaside.bEnabled = 0;
+ disableLookaside(pParse);
A = X;
}
%type ifnotexists {int}
@@ -1010,7 +1019,7 @@ expr(A) ::= expr(X) NOT NULL(E). {spanUnaryPostfix(&A,pParse,TK_NOTNULL,&X,&E);}
** unary TK_ISNULL or TK_NOTNULL expression. */
static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){
sqlite3 *db = pParse->db;
- if( pY && pA && pY->op==TK_NULL ){
+ if( pA && pY && pY->op==TK_NULL ){
pA->op = (u8)op;
sqlite3ExprDelete(db, pA->pRight);
pA->pRight = 0;
@@ -1507,7 +1516,7 @@ cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). {
sqlite3AlterFinishAddColumn(pParse, &Y);
}
add_column_fullname ::= fullname(X). {
- pParse->db->lookaside.bEnabled = 0;
+ disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, X);
}
kwcolumn_opt ::= .
diff --git a/src/pragma.c b/src/pragma.c
index 15f0eecce3..c34d5421c2 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -32,8 +32,8 @@
/*
** Interpret the given string as a safety level. Return 0 for OFF,
-** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
-** unrecognized string argument. The FULL option is disallowed
+** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
+** unrecognized string argument. The FULL and EXTRA option is disallowed
** if the omitFull parameter it 1.
**
** Note that the values returned are one less that the values that
@@ -42,18 +42,21 @@
** and older scripts may have used numbers 0 for OFF and 1 for ON.
*/
static u8 getSafetyLevel(const char *z, int omitFull, u8 dflt){
- /* 123456789 123456789 */
- static const char zText[] = "onoffalseyestruefull";
- static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16};
- static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 4};
- static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2};
+ /* 123456789 123456789 123 */
+ static const char zText[] = "onoffalseyestruextrafull";
+ static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 15, 20};
+ static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 5, 4};
+ static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 3, 2};
+ /* on no off false yes true extra full */
int i, n;
if( sqlite3Isdigit(*z) ){
return (u8)sqlite3Atoi(z);
}
n = sqlite3Strlen30(z);
- for(i=0; ipSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
@@ -476,7 +478,7 @@ void sqlite3Pragma(
*/
db->nextPagesize = sqlite3Atoi(zRight);
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
}
break;
@@ -683,8 +685,7 @@ void sqlite3Pragma(
{ OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
{ OP_If, 1, 0, 0}, /* 2 */
{ OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
- { OP_Integer, 0, 1, 0}, /* 4 */
- { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
+ { OP_SetCookie, 0, BTREE_INCR_VACUUM, 0}, /* 4 */
};
VdbeOp *aOp;
int iAddr = sqlite3VdbeCurrentAddr(v);
@@ -694,8 +695,8 @@ void sqlite3Pragma(
aOp[0].p1 = iDb;
aOp[1].p1 = iDb;
aOp[2].p2 = iAddr+4;
- aOp[4].p1 = eAuto - 1;
- aOp[5].p1 = iDb;
+ aOp[4].p1 = iDb;
+ aOp[4].p3 = eAuto - 1;
sqlite3VdbeUsesBtree(v, iDb);
}
}
@@ -974,7 +975,7 @@ void sqlite3Pragma(
/*
** PRAGMA [schema.]synchronous
- ** PRAGMA [schema.]synchronous=OFF|ON|NORMAL|FULL
+ ** PRAGMA [schema.]synchronous=OFF|ON|NORMAL|FULL|EXTRA
**
** Return or set the local value of the synchronous flag. Changing
** the local value does not make changes to the disk file and the
@@ -1601,16 +1602,15 @@ void sqlite3Pragma(
static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList endCode[] = {
{ OP_AddImm, 1, 0, 0}, /* 0 */
- { OP_If, 1, 0, 0}, /* 1 */
+ { OP_If, 1, 4, 0}, /* 1 */
{ OP_String8, 0, 3, 0}, /* 2 */
- { OP_ResultRow, 3, 1, 0},
+ { OP_ResultRow, 3, 1, 0}, /* 3 */
};
VdbeOp *aOp;
aOp = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
if( aOp ){
aOp[0].p2 = -mxErr;
- aOp[1].p2 = sqlite3VdbeCurrentAddr(v);
aOp[2].p4type = P4_STATIC;
aOp[2].p4.z = "ok";
}
@@ -1728,17 +1728,16 @@ void sqlite3Pragma(
/* Write the specified cookie value */
static const VdbeOpList setCookie[] = {
{ OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_Integer, 0, 1, 0}, /* 1 */
- { OP_SetCookie, 0, 0, 1}, /* 2 */
+ { OP_SetCookie, 0, 0, 0}, /* 1 */
};
VdbeOp *aOp;
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setCookie));
aOp = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
aOp[0].p1 = iDb;
- aOp[1].p1 = sqlite3Atoi(zRight);
- aOp[2].p1 = iDb;
- aOp[2].p2 = iCookie;
+ aOp[1].p1 = iDb;
+ aOp[1].p2 = iCookie;
+ aOp[1].p3 = sqlite3Atoi(zRight);
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
diff --git a/src/prepare.c b/src/prepare.c
index acc70dd2d5..f74aa52e0b 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -28,11 +28,10 @@ static void corruptSchema(
if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){
char *z;
if( zObj==0 ) zObj = "?";
- z = sqlite3_mprintf("malformed database schema (%s)", zObj);
- if( z && zExtra ) z = sqlite3_mprintf("%z - %s", z, zExtra);
+ z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
+ if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
sqlite3DbFree(db, *pData->pzErrMsg);
*pData->pzErrMsg = z;
- if( z==0 ) db->mallocFailed = 1;
}
pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT;
}
@@ -91,7 +90,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
}else{
pData->rc = rc;
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
corruptSchema(pData, argv[0], sqlite3_errmsg(db));
}
@@ -336,7 +335,7 @@ initone_error_out:
error_out:
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return rc;
}
@@ -434,7 +433,7 @@ static void schemaIsValid(Parse *pParse){
if( !sqlite3BtreeIsInReadTrans(pBt) ){
rc = sqlite3BtreeBeginTrans(pBt, 0);
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
if( rc!=SQLITE_OK ) return;
openedTransaction = 1;
@@ -497,6 +496,11 @@ void sqlite3ParserReset(Parse *pParse){
sqlite3 *db = pParse->db;
sqlite3DbFree(db, pParse->aLabel);
sqlite3ExprListDelete(db, pParse->pConstExpr);
+ if( db ){
+ assert( db->lookaside.bDisable >= pParse->disableLookaside );
+ db->lookaside.bDisable -= pParse->disableLookaside;
+ }
+ pParse->disableLookaside = 0;
}
}
@@ -525,7 +529,7 @@ static int sqlite3Prepare(
}
pParse->pReprepare = pReprepare;
assert( ppStmt && *ppStmt==0 );
- assert( !db->mallocFailed );
+ /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
assert( sqlite3_mutex_held(db->mutex) );
/* Check to verify that it is possible to get a read lock on all
@@ -582,8 +586,8 @@ static int sqlite3Prepare(
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
sqlite3RunParser(pParse, zSqlCopy, &zErrMsg);
- sqlite3DbFree(db, zSqlCopy);
pParse->zTail = &zSql[pParse->zTail-zSqlCopy];
+ sqlite3DbFree(db, zSqlCopy);
}else{
pParse->zTail = &zSql[nBytes];
}
@@ -592,9 +596,6 @@ static int sqlite3Prepare(
}
assert( 0==pParse->nQueryLoop );
- if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
- }
if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK;
if( pParse->checkSchema ){
schemaIsValid(pParse);
@@ -716,7 +717,7 @@ int sqlite3Reprepare(Vdbe *p){
rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0);
if( rc ){
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
assert( pNew==0 );
return rc;
diff --git a/src/printf.c b/src/printf.c
index 969950c154..238997f37d 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -171,7 +171,6 @@ static char *getTextArg(PrintfArguments *p){
*/
void sqlite3VXPrintf(
StrAccum *pAccum, /* Accumulate results here */
- u32 bFlags, /* SQLITE_PRINTF_* flags */
const char *fmt, /* Format string */
va_list ap /* arguments */
){
@@ -211,11 +210,11 @@ void sqlite3VXPrintf(
char buf[etBUFSIZE]; /* Conversion buffer */
bufpt = 0;
- if( bFlags ){
- if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
+ if( pAccum->printfFlags ){
+ if( (bArgList = (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
pArgList = va_arg(ap, PrintfArguments*);
}
- useIntern = bFlags & SQLITE_PRINTF_INTERNAL;
+ useIntern = pAccum->printfFlags & SQLITE_PRINTF_INTERNAL;
}else{
bArgList = useIntern = 0;
}
@@ -766,9 +765,9 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
setStrAccumError(p, STRACCUM_TOOBIG);
return N;
}else{
- char *zOld = p->bMalloced ? p->zText : 0;
+ char *zOld = isMalloced(p) ? p->zText : 0;
i64 szNew = p->nChar;
- assert( (p->zText==0 || p->zText==p->zBase)==(p->bMalloced==0) );
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
szNew += N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
@@ -789,10 +788,10 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
}
if( zNew ){
assert( p->zText!=0 || p->nChar==0 );
- if( !p->bMalloced && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
+ if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
p->zText = zNew;
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
- p->bMalloced = 1;
+ p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
sqlite3StrAccumReset(p);
setStrAccumError(p, STRACCUM_NOMEM);
@@ -810,7 +809,7 @@ void sqlite3AppendChar(StrAccum *p, int N, char c){
if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
return;
}
- assert( (p->zText==p->zBase)==(p->bMalloced==0) );
+ assert( (p->zText==p->zBase)==!isMalloced(p) );
while( (N--)>0 ) p->zText[p->nChar++] = c;
}
@@ -828,7 +827,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
memcpy(&p->zText[p->nChar], z, N);
p->nChar += N;
}
- assert( (p->zText==0 || p->zText==p->zBase)==(p->bMalloced==0) );
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
}
/*
@@ -864,13 +863,13 @@ void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
*/
char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
- assert( (p->zText==p->zBase)==(p->bMalloced==0) );
+ assert( (p->zText==p->zBase)==!isMalloced(p) );
p->zText[p->nChar] = 0;
- if( p->mxAlloc>0 && p->bMalloced==0 ){
+ if( p->mxAlloc>0 && !isMalloced(p) ){
p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
if( p->zText ){
memcpy(p->zText, p->zBase, p->nChar+1);
- p->bMalloced = 1;
+ p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
setStrAccumError(p, STRACCUM_NOMEM);
}
@@ -883,10 +882,10 @@ char *sqlite3StrAccumFinish(StrAccum *p){
** Reset an StrAccum string. Reclaim all malloced memory.
*/
void sqlite3StrAccumReset(StrAccum *p){
- assert( (p->zText==0 || p->zText==p->zBase)==(p->bMalloced==0) );
- if( p->bMalloced ){
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
+ if( isMalloced(p) ){
sqlite3DbFree(p->db, p->zText);
- p->bMalloced = 0;
+ p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
}
p->zText = 0;
}
@@ -912,7 +911,7 @@ void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
p->nAlloc = n;
p->mxAlloc = mx;
p->accError = 0;
- p->bMalloced = 0;
+ p->printfFlags = 0;
}
/*
@@ -926,10 +925,11 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
assert( db!=0 );
sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
- sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap);
+ acc.printfFlags = SQLITE_PRINTF_INTERNAL;
+ sqlite3VXPrintf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
if( acc.accError==STRACCUM_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return z;
}
@@ -966,7 +966,7 @@ char *sqlite3_vmprintf(const char *zFormat, va_list ap){
if( sqlite3_initialize() ) return 0;
#endif
sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
return z;
}
@@ -1011,7 +1011,7 @@ char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
}
#endif
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
return sqlite3StrAccumFinish(&acc);
}
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
@@ -1042,7 +1042,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
sqlite3StrAccumFinish(&acc));
}
@@ -1071,7 +1071,7 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
char zBuf[500];
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
va_start(ap,zFormat);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
va_end(ap);
sqlite3StrAccumFinish(&acc);
fprintf(stdout,"%s", zBuf);
@@ -1084,9 +1084,9 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
*/
-void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){
+void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
va_list ap;
va_start(ap,zFormat);
- sqlite3VXPrintf(p, bFlags, zFormat, ap);
+ sqlite3VXPrintf(p, zFormat, ap);
va_end(ap);
}
diff --git a/src/rowset.c b/src/rowset.c
index ff5593892a..c2e73ed72e 100644
--- a/src/rowset.c
+++ b/src/rowset.c
@@ -181,7 +181,7 @@ static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
assert( p!=0 );
if( p->nFresh==0 ){
struct RowSetChunk *pNew;
- pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
+ pNew = sqlite3DbMallocRawNN(p->db, sizeof(*pNew));
if( pNew==0 ){
return 0;
}
diff --git a/src/select.c b/src/select.c
index 9eb6279c10..c3132c2325 100644
--- a/src/select.c
+++ b/src/select.c
@@ -112,7 +112,7 @@ Select *sqlite3SelectNew(
Select *pNew;
Select standin;
sqlite3 *db = pParse->db;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ){
assert( db->mallocFailed );
pNew = &standin;
@@ -1016,7 +1016,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
p->nRef = 1;
memset(&p[1], 0, nExtra);
}else{
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return p;
}
@@ -1677,7 +1677,7 @@ int sqlite3ColumnsFromExprList(
pCol->zName = zName;
sqlite3ColumnPropertiesFromName(0, pCol);
if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
}
sqlite3HashClear(&ht);
@@ -1764,7 +1764,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
}
/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
** is disabled */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
pTab->nRef = 1;
pTab->zName = 0;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
@@ -1860,10 +1860,8 @@ 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"));
- sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iOffset, iOffset, 0);
- sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit, iLimit, iOffset+1, iOffset);
VdbeComment((v, "LIMIT+OFFSET"));
- sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iLimit, iOffset+1, -1);
}
}
}
@@ -2280,9 +2278,8 @@ static int multiSelect(
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);
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit,
+ p->iLimit, p->iOffset+1, p->iOffset);
}
}
explainSetInteger(iSub2, pParse->iNextSelectId);
@@ -2873,10 +2870,11 @@ static int multiSelectOrderBy(
** to the right and the left are evaluated, they use the correct
** collation.
*/
- aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
+ aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
- for(i=0, pItem=pOrderBy->a; ia; i<=nOrderBy; i++, pItem++){
assert( pItem->u.x.iOrderByCol>0 );
assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
@@ -2954,7 +2952,7 @@ static int multiSelectOrderBy(
pPrior->iLimit = regLimitA;
explainSetInteger(iSub1, pParse->iNextSelectId);
sqlite3Select(pParse, pPrior, &destA);
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrA);
+ sqlite3VdbeEndCoroutine(v, regAddrA);
sqlite3VdbeJumpHere(v, addr1);
/* Generate a coroutine to evaluate the SELECT statement on
@@ -2971,7 +2969,7 @@ static int multiSelectOrderBy(
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrB);
+ sqlite3VdbeEndCoroutine(v, regAddrB);
/* Generate a subroutine that outputs the current row of the A
** select as the next output row of the compound select.
@@ -4438,8 +4436,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pExpr = pRight;
}
pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
- sColname.z = zColname;
- sColname.n = sqlite3Strlen30(zColname);
+ sqlite3TokenInit(&sColname, zColname);
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
@@ -4993,7 +4990,7 @@ int sqlite3Select(
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
pItem->fg.viaCoroutine = 1;
pItem->regResult = dest.iSdst;
- sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
+ sqlite3VdbeEndCoroutine(v, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else{
@@ -5565,7 +5562,8 @@ int sqlite3Select(
if( flag ){
pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
pDel = pMinMax;
- if( pMinMax && !db->mallocFailed ){
+ assert( db->mallocFailed || pMinMax!=0 );
+ if( !db->mallocFailed ){
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
pMinMax->a[0].pExpr->op = TK_COLUMN;
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 59b30cdd3a..fce396c0f6 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -5697,7 +5697,7 @@ struct sqlite3_index_info {
/* Inputs */
int nConstraint; /* Number of entries in aConstraint */
struct sqlite3_index_constraint {
- int iColumn; /* Column on left-hand side of constraint */
+ int iColumn; /* Column constrained. -1 for ROWID */
unsigned char op; /* Constraint operator */
unsigned char usable; /* True if this constraint is usable */
int iTermOffset; /* Used internally - xBestIndex should ignore */
diff --git a/src/sqlite3.rc b/src/sqlite3.rc
index 04dd086488..5a856490d6 100644
--- a/src/sqlite3.rc
+++ b/src/sqlite3.rc
@@ -39,9 +39,11 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
* Icon
*/
+#if !defined(RC_VERONLY)
#define IDI_SQLITE 101
IDI_SQLITE ICON "..\\art\\sqlite370.ico"
+#endif /* !defined(RC_VERONLY) */
/*
* Version
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index b536b8ab28..760c1f4d21 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1094,8 +1094,8 @@ struct Schema {
** lookaside allocations are not used to construct the schema objects.
*/
struct Lookaside {
+ u32 bDisable; /* Only operate the lookaside when zero */
u16 sz; /* Size of each buffer in bytes */
- u8 bEnabled; /* False to disable new lookaside allocations */
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
int nOut; /* Number of buffers currently checked out */
int mxOut; /* Highwater mark for nOut */
@@ -1178,6 +1178,7 @@ struct sqlite3 {
u8 autoCommit; /* The auto-commit flag. */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
+ u8 bBenignMalloc; /* Do not require OOMs if true */
u8 dfltLockMode; /* Default locking-mode for attached dbs */
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */
@@ -1286,10 +1287,10 @@ struct sqlite3 {
*/
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */
-#define SQLITE_FullFSync 0x00000004 /* Use full fsync on the backend */
-#define SQLITE_CkptFullFSync 0x00000008 /* Use full fsync for checkpoint */
-#define SQLITE_CacheSpill 0x00000010 /* OK to spill pager cache */
-#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */
+#define SQLITE_FullFSync 0x00000008 /* Use full fsync on the backend */
+#define SQLITE_CkptFullFSync 0x00000010 /* Use full fsync for checkpoint */
+#define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */
#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
@@ -2641,7 +2642,7 @@ struct SelectDest {
** tables, the following information is attached to the Table.u.autoInc.p
** pointer of each autoincrement table to record some side information that
** the code generator needs. We have to keep per-table autoincrement
-** information in case inserts are down within triggers. Triggers do not
+** information in case inserts are done within triggers. Triggers do not
** normally coordinate their activities, but we do need to coordinate the
** loading and saving of autoincrement information.
*/
@@ -2733,6 +2734,7 @@ struct Parse {
u8 mayAbort; /* True if statement may throw an ABORT exception */
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
u8 okConstFactor; /* OK to factor out constants */
+ u8 disableLookaside; /* Number of times lookaside has been disabled */
int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
@@ -2847,7 +2849,8 @@ struct AuthContext {
/*
** Bitfield flags for P5 value in various opcodes.
*/
-#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
+#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
+ /* Also used in P2 (not P5) of OP_Delete */
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
@@ -2860,6 +2863,8 @@ struct AuthContext {
#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
#define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
+#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */
+#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
/*
* Each trigger present in the database schema is stored as an instance of
@@ -2978,10 +2983,16 @@ struct StrAccum {
u32 nAlloc; /* Amount of space allocated in zText */
u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
- u8 bMalloced; /* zText points to allocated space */
+ u8 printfFlags; /* SQLITE_PRINTF flags below */
};
#define STRACCUM_NOMEM 1
#define STRACCUM_TOOBIG 2
+#define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */
+#define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
+#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */
+
+#define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0)
+
/*
** A pointer to this structure is used to communicate information
@@ -3216,6 +3227,7 @@ void *sqlite3Malloc(u64);
void *sqlite3MallocZero(u64);
void *sqlite3DbMallocZero(sqlite3*, u64);
void *sqlite3DbMallocRaw(sqlite3*, u64);
+void *sqlite3DbMallocRawNN(sqlite3*, u64);
char *sqlite3DbStrDup(sqlite3*,const char*);
char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
void *sqlite3Realloc(void*, u64);
@@ -3298,10 +3310,8 @@ struct PrintfArguments {
sqlite3_value **apArg; /* The argument values */
};
-#define SQLITE_PRINTF_INTERNAL 0x01
-#define SQLITE_PRINTF_SQLFUNC 0x02
-void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list);
-void sqlite3XPrintf(StrAccum*, u32, const char*, ...);
+void sqlite3VXPrintf(StrAccum*, const char*, va_list);
+void sqlite3XPrintf(StrAccum*, const char*, ...);
char *sqlite3MPrintf(sqlite3*,const char*, ...);
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
@@ -3322,6 +3332,7 @@ char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
void sqlite3SetString(char **, sqlite3*, const char*);
void sqlite3ErrorMsg(Parse*, const char*, ...);
int sqlite3Dequote(char*);
+void sqlite3TokenInit(Token*,char*);
int sqlite3KeywordCode(const unsigned char*, int);
int sqlite3RunParser(Parse*, const char*, char **);
void sqlite3FinishCoding(Parse*);
@@ -3769,6 +3780,8 @@ int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
FuncDestructor *pDestructor
);
+void sqlite3OomFault(sqlite3*);
+void sqlite3OomClear(sqlite3*);
int sqlite3ApiExit(sqlite3 *db, int);
int sqlite3OpenTempDatabase(Parse *);
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index 604e898265..aa913ca7c7 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -153,6 +153,7 @@ struct SqliteDb {
IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */
int nStep, nSort, nIndex; /* Statistics for most recent operation */
int nTransaction; /* Number of nested [transaction] methods */
+ int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */
#ifdef SQLITE_TEST
int bLegacyPrepare; /* True to use sqlite3_prepare() */
#endif
@@ -1750,7 +1751,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME");
return TCL_ERROR;
}
- rc = sqlite3_open(zDestFile, &pDest);
+ rc = sqlite3_open_v2(zDestFile, &pDest,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE| pDb->openFlags, 0);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "cannot open target database: ",
sqlite3_errmsg(pDest), (char*)0);
@@ -2613,7 +2615,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
Tcl_WrongNumArgs(interp, 2, objv, "?DATABASE? FILENAME");
return TCL_ERROR;
}
- rc = sqlite3_open_v2(zSrcFile, &pSrc, SQLITE_OPEN_READONLY, 0);
+ rc = sqlite3_open_v2(zSrcFile, &pSrc,
+ SQLITE_OPEN_READONLY | pDb->openFlags, 0);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "cannot open source database: ",
sqlite3_errmsg(pSrc), (char*)0);
@@ -3088,6 +3091,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}
p->maxStmt = NUM_PREPARED_STMTS;
+ p->openFlags = flags & SQLITE_OPEN_URI;
p->interp = interp;
zArg = Tcl_GetStringFromObj(objv[1], 0);
if( DbUseNre() ){
diff --git a/src/test_config.c b/src/test_config.c
index 30b421e00b..5db7117555 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -370,6 +370,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
+ Tcl_SetVar2(interp, "sqlite_options", "fts3_tokenizer", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "fts3_tokenizer", "0", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_ENABLE_FTS5
Tcl_SetVar2(interp, "sqlite_options", "fts5", "1", TCL_GLOBAL_ONLY);
#else
diff --git a/src/test_func.c b/src/test_func.c
index 63cf18e3f7..579177b70c 100644
--- a/src/test_func.c
+++ b/src/test_func.c
@@ -166,9 +166,7 @@ static void test_agg_errmsg16_final(sqlite3_context *ctx){
const void *z;
sqlite3 * db = sqlite3_context_db_handle(ctx);
sqlite3_aggregate_context(ctx, 2048);
- sqlite3BeginBenignMalloc();
z = sqlite3_errmsg16(db);
- sqlite3EndBenignMalloc();
sqlite3_result_text16(ctx, z, -1, SQLITE_TRANSIENT);
#endif
}
diff --git a/src/tokenize.c b/src/tokenize.c
index b85e35dc10..9b3444ac82 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -18,12 +18,92 @@
#include "sqliteInt.h"
#include
+/* Character classes for tokenizing
+**
+** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented
+** using a lookup table, whereas a switch() directly on c uses a binary search.
+** The lookup table is much faster. To maximize speed, and to ensure that
+** a lookup table is used, all of the classes need to be small integers and
+** all of them need to be used within the switch.
+*/
+#define CC_X 0 /* The letter 'x', or start of BLOB literal */
+#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */
+#define CC_ID 2 /* unicode characters usable in IDs */
+#define CC_DIGIT 3 /* Digits */
+#define CC_DOLLAR 4 /* '$' */
+#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
+#define CC_VARNUM 6 /* '?'. Numeric SQL variables */
+#define CC_SPACE 7 /* Space characters */
+#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */
+#define CC_QUOTE2 9 /* '['. [...] style quoted ids */
+#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */
+#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */
+#define CC_LT 12 /* '<'. Part of < or <= or <> */
+#define CC_GT 13 /* '>'. Part of > or >= */
+#define CC_EQ 14 /* '='. Part of = or == */
+#define CC_BANG 15 /* '!'. Part of != */
+#define CC_SLASH 16 /* '/'. / or c-style comment */
+#define CC_LP 17 /* '(' */
+#define CC_RP 18 /* ')' */
+#define CC_SEMI 19 /* ';' */
+#define CC_PLUS 20 /* '+' */
+#define CC_STAR 21 /* '*' */
+#define CC_PERCENT 22 /* '%' */
+#define CC_COMMA 23 /* ',' */
+#define CC_AND 24 /* '&' */
+#define CC_TILDA 25 /* '~' */
+#define CC_DOT 26 /* '.' */
+#define CC_ILLEGAL 27 /* Illegal character */
+
+static const unsigned char aiClass[] = {
+#ifdef SQLITE_ASCII
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
+/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
+/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1,
+/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27,
+/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+#endif
+#ifdef SQLITE_EBCDIC
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27,
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 12, 17, 20, 10,
+/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27,
+/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 7,
+/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8,
+/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* 9x */ 25, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27,
+/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27,
+#endif
+};
+
/*
-** The charMap() macro maps alphabetic characters into their
+** The charMap() macro maps alphabetic characters (only) into their
** lower-case ASCII equivalent. On ASCII machines, this is just
** an upper-to-lower case map. On EBCDIC machines we also need
-** to adjust the encoding. Only alphabetic characters and underscores
-** need to be translated.
+** to adjust the encoding. The mapping is only valid for alphabetics
+** which are the only characters for which this feature is used.
+**
+** Used by keywordhash.h
*/
#ifdef SQLITE_ASCII
# define charMap(X) sqlite3UpperToLower[(unsigned char)X]
@@ -57,7 +137,7 @@ const unsigned char ebcdicToAscii[] = {
** returned. If the input is not a keyword, TK_ID is returned.
**
** The implementation of this routine was generated by a program,
-** mkkeywordhash.h, located in the tool subdirectory of the distribution.
+** mkkeywordhash.c, located in the tool subdirectory of the distribution.
** The output of the mkkeywordhash.c program is written into a file
** named keywordhash.h and then included into this source file by
** the #include below.
@@ -110,13 +190,15 @@ int sqlite3IsIdChar(u8 c){ return IdChar(c); }
/*
-** Return the length of the token that begins at z[0].
+** Return the length (in bytes) of the token that begins at z[0].
** Store the token type in *tokenType before returning.
*/
int sqlite3GetToken(const unsigned char *z, int *tokenType){
int i, c;
- switch( *z ){
- case ' ': case '\t': case '\n': case '\f': case '\r': {
+ switch( aiClass[*z] ){ /* Switch on the character-class of the first byte
+ ** of the token. See the comment on the CC_ defines
+ ** above. */
+ case CC_SPACE: {
testcase( z[0]==' ' );
testcase( z[0]=='\t' );
testcase( z[0]=='\n' );
@@ -126,7 +208,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SPACE;
return i;
}
- case '-': {
+ case CC_MINUS: {
if( z[1]=='-' ){
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
@@ -135,27 +217,27 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_MINUS;
return 1;
}
- case '(': {
+ case CC_LP: {
*tokenType = TK_LP;
return 1;
}
- case ')': {
+ case CC_RP: {
*tokenType = TK_RP;
return 1;
}
- case ';': {
+ case CC_SEMI: {
*tokenType = TK_SEMI;
return 1;
}
- case '+': {
+ case CC_PLUS: {
*tokenType = TK_PLUS;
return 1;
}
- case '*': {
+ case CC_STAR: {
*tokenType = TK_STAR;
return 1;
}
- case '/': {
+ case CC_SLASH: {
if( z[1]!='*' || z[2]==0 ){
*tokenType = TK_SLASH;
return 1;
@@ -165,15 +247,15 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
}
- case '%': {
+ case CC_PERCENT: {
*tokenType = TK_REM;
return 1;
}
- case '=': {
+ case CC_EQ: {
*tokenType = TK_EQ;
return 1 + (z[1]=='=');
}
- case '<': {
+ case CC_LT: {
if( (c=z[1])=='=' ){
*tokenType = TK_LE;
return 2;
@@ -188,7 +270,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 1;
}
}
- case '>': {
+ case CC_GT: {
if( (c=z[1])=='=' ){
*tokenType = TK_GE;
return 2;
@@ -200,7 +282,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 1;
}
}
- case '!': {
+ case CC_BANG: {
if( z[1]!='=' ){
*tokenType = TK_ILLEGAL;
return 2;
@@ -209,7 +291,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 2;
}
}
- case '|': {
+ case CC_PIPE: {
if( z[1]!='|' ){
*tokenType = TK_BITOR;
return 1;
@@ -218,21 +300,19 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 2;
}
}
- case ',': {
+ case CC_COMMA: {
*tokenType = TK_COMMA;
return 1;
}
- case '&': {
+ case CC_AND: {
*tokenType = TK_BITAND;
return 1;
}
- case '~': {
+ case CC_TILDA: {
*tokenType = TK_BITNOT;
return 1;
}
- case '`':
- case '\'':
- case '"': {
+ case CC_QUOTE: {
int delim = z[0];
testcase( delim=='`' );
testcase( delim=='\'' );
@@ -257,7 +337,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
return i;
}
}
- case '.': {
+ case CC_DOT: {
#ifndef SQLITE_OMIT_FLOATING_POINT
if( !sqlite3Isdigit(z[1]) )
#endif
@@ -268,8 +348,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
/* If the next character is a digit, this is a floating point
** number that begins with ".". Fall thru into the next case */
}
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9': {
+ case CC_DIGIT: {
testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' );
testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' );
testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' );
@@ -304,22 +383,18 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
return i;
}
- case '[': {
+ case CC_QUOTE2: {
for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
*tokenType = c==']' ? TK_ID : TK_ILLEGAL;
return i;
}
- case '?': {
+ case CC_VARNUM: {
*tokenType = TK_VARIABLE;
for(i=1; sqlite3Isdigit(z[i]); i++){}
return i;
}
-#ifndef SQLITE_OMIT_TCL_VARIABLE
- case '$':
-#endif
- case '@': /* For compatibility with MS SQL Server */
- case '#':
- case ':': {
+ case CC_DOLLAR:
+ case CC_VARALPHA: {
int n = 0;
testcase( z[0]=='$' ); testcase( z[0]=='@' );
testcase( z[0]==':' ); testcase( z[0]=='#' );
@@ -348,8 +423,20 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( n==0 ) *tokenType = TK_ILLEGAL;
return i;
}
+ case CC_KYWD: {
+ for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
+ if( IdChar(z[i]) ){
+ /* This token started out using characters that can appear in keywords,
+ ** but z[i] is a character not allowed within keywords, so this must
+ ** be an identifier instead */
+ i++;
+ break;
+ }
+ *tokenType = TK_ID;
+ return keywordCode((char*)z, i, tokenType);
+ }
#ifndef SQLITE_OMIT_BLOB_LITERAL
- case 'x': case 'X': {
+ case CC_X: {
testcase( z[0]=='x' ); testcase( z[0]=='X' );
if( z[1]=='\'' ){
*tokenType = TK_BLOB;
@@ -361,20 +448,22 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( z[i] ) i++;
return i;
}
- /* Otherwise fall through to the next case */
+ /* If it is not a BLOB literal, then it must be an ID, since no
+ ** SQL keywords start with the letter 'x'. Fall through */
}
#endif
+ case CC_ID: {
+ i = 1;
+ break;
+ }
default: {
- if( !IdChar(*z) ){
- break;
- }
- for(i=1; IdChar(z[i]); i++){}
- *tokenType = TK_ID;
- return keywordCode((char*)z, i, tokenType);
+ *tokenType = TK_ILLEGAL;
+ return 1;
}
}
- *tokenType = TK_ILLEGAL;
- return 1;
+ while( IdChar(z[i]) ){ i++; }
+ *tokenType = TK_ID;
+ return i;
}
/*
@@ -390,7 +479,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
void *pEngine; /* The LEMON-generated LALR(1) parser */
int tokenType; /* type of the next token */
int lastTokenParsed = -1; /* type of the previous token */
- u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */
sqlite3 *db = pParse->db; /* The database connection */
int mxSqlLen; /* Max length of an SQL string */
@@ -406,7 +494,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
/* sqlite3ParserTrace(stdout, "parser: "); */
pEngine = sqlite3ParserAlloc(sqlite3Malloc);
if( pEngine==0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return SQLITE_NOMEM;
}
assert( pParse->pNewTable==0 );
@@ -414,8 +502,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
assert( pParse->nVar==0 );
assert( pParse->nzVar==0 );
assert( pParse->azVar==0 );
- enableLookaside = db->lookaside.bEnabled;
- if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
while( zSql[i]!=0 ){
assert( i>=0 );
pParse->sLastToken.z = &zSql[i];
@@ -428,7 +514,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
if( tokenType>=TK_SPACE ){
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
if( db->u1.isInterrupted ){
- sqlite3ErrorMsg(pParse, "interrupt");
pParse->rc = SQLITE_INTERRUPT;
break;
}
@@ -463,7 +548,6 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
sqlite3_mutex_leave(sqlite3MallocMutex());
#endif /* YYDEBUG */
sqlite3ParserFree(pEngine, sqlite3_free);
- db->lookaside.bEnabled = enableLookaside;
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM;
}
diff --git a/src/treeview.c b/src/treeview.c
index a26e9e2b9f..2985804314 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -63,7 +63,7 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
}
va_start(ap, zFormat);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
va_end(ap);
if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
sqlite3StrAccumFinish(&acc);
@@ -98,17 +98,17 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
char zLine[1000];
const struct Cte *pCte = &pWith->a[i];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
- sqlite3XPrintf(&x, 0, "%s", pCte->zName);
+ sqlite3XPrintf(&x, "%s", pCte->zName);
if( pCte->pCols && pCte->pCols->nExpr>0 ){
char cSep = '(';
int j;
for(j=0; jpCols->nExpr; j++){
- sqlite3XPrintf(&x, 0, "%c%s", cSep, pCte->pCols->a[j].zName);
+ sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
cSep = ',';
}
- sqlite3XPrintf(&x, 0, ")");
+ sqlite3XPrintf(&x, ")");
}
- sqlite3XPrintf(&x, 0, " AS");
+ sqlite3XPrintf(&x, " AS");
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, inCte-1);
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
@@ -159,20 +159,20 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
StrAccum x;
char zLine[100];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
- sqlite3XPrintf(&x, 0, "{%d,*}", pItem->iCursor);
+ sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
if( pItem->zDatabase ){
- sqlite3XPrintf(&x, 0, " %s.%s", pItem->zDatabase, pItem->zName);
+ sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
}else if( pItem->zName ){
- sqlite3XPrintf(&x, 0, " %s", pItem->zName);
+ sqlite3XPrintf(&x, " %s", pItem->zName);
}
if( pItem->pTab ){
- sqlite3XPrintf(&x, 0, " tabname=%Q", pItem->pTab->zName);
+ sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
}
if( pItem->zAlias ){
- sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias);
+ sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
}
if( pItem->fg.jointype & JT_LEFT ){
- sqlite3XPrintf(&x, 0, " LEFT-JOIN");
+ sqlite3XPrintf(&x, " LEFT-JOIN");
}
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, ipSrc->nSrc-1);
diff --git a/src/trigger.c b/src/trigger.c
index 48d6772992..72c31eb2a3 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -287,8 +287,7 @@ void sqlite3FinishTrigger(
pStepList->pTrig = pTrig;
pStepList = pStepList->pNext;
}
- nameToken.z = pTrig->zName;
- nameToken.n = sqlite3Strlen30(nameToken.z);
+ sqlite3TokenInit(&nameToken, pTrig->zName);
sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
|| sqlite3FixExpr(&sFix, pTrig->pWhen)
@@ -324,7 +323,7 @@ void sqlite3FinishTrigger(
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pTrig = sqlite3HashInsert(pHash, zName, pTrig);
if( pTrig ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if( pLink->pSchema==pLink->pTabSchema ){
Table *pTab;
pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table);
diff --git a/src/update.c b/src/update.c
index a9735cadca..20951aade9 100644
--- a/src/update.c
+++ b/src/update.c
@@ -197,7 +197,7 @@ void sqlite3Update(
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
** Initialize aXRef[] and aToOpen[] to their default values.
*/
- aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
+ aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
if( aXRef==0 ) goto update_cleanup;
aRegIdx = aXRef+pTab->nCol;
aToOpen = (u8*)(aRegIdx+nIdx);
diff --git a/src/utf.c b/src/utf.c
index ee367c1399..e42ab418ad 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -316,7 +316,7 @@ SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
c = pMem->flags;
sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Str|MEM_Term|(c&MEM_AffMask);
+ pMem->flags = MEM_Str|MEM_Term|(c&(MEM_AffMask|MEM_Subtype));
pMem->enc = desiredEnc;
pMem->z = (char*)zOut;
pMem->zMalloc = pMem->z;
diff --git a/src/util.c b/src/util.c
index b4c5e62bb3..37b585b2ea 100644
--- a/src/util.c
+++ b/src/util.c
@@ -234,6 +234,14 @@ int sqlite3Dequote(char *z){
return j;
}
+/*
+** Generate a Token object from a string
+*/
+void sqlite3TokenInit(Token *p, char *z){
+ p->z = z;
+ p->n = sqlite3Strlen30(z);
+}
+
/* Convenient short-hand */
#define UpperToLower sqlite3UpperToLower
@@ -1142,7 +1150,7 @@ void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
char *zBlob;
int i;
- zBlob = (char *)sqlite3DbMallocRaw(db, n/2 + 1);
+ zBlob = (char *)sqlite3DbMallocRawNN(db, n/2 + 1);
n--;
if( zBlob ){
for(i=0; iflags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype);
}
static void registerTrace(int iReg, Mem *p){
printf("REG[%d] = ", iReg);
@@ -551,6 +552,9 @@ int sqlite3VdbeExec(
Op *pOp = aOp; /* Current operation */
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
Op *pOrigOp; /* Value of pOp at the top of the loop */
+#endif
+#ifdef SQLITE_DEBUG
+ int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
#endif
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
@@ -625,7 +629,6 @@ int sqlite3VdbeExec(
#endif
for(pOp=&aOp[p->pc]; rc==SQLITE_OK; pOp++){
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
- if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
start = sqlite3Hwtime();
#endif
@@ -1623,7 +1626,7 @@ case OP_Function0: {
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) );
assert( pOp->p3p2 || pOp->p3>=pOp->p2+n );
- pCtx = sqlite3DbMallocRaw(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
+ pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
if( pCtx==0 ) goto no_mem;
pCtx->pOut = 0;
pCtx->pFunc = pOp->p4.pFunc;
@@ -2067,11 +2070,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** The permutation is only valid until the next OP_Compare that has
** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
** occur immediately prior to the OP_Compare.
+**
+** The first integer in the P4 integer array is the length of the array
+** and does not become part of the permutation.
*/
case OP_Permutation: {
assert( pOp->p4type==P4_INTARRAY );
assert( pOp->p4.ai );
- aPermute = pOp->p4.ai;
+ aPermute = pOp->p4.ai + 1;
break;
}
@@ -2376,12 +2382,16 @@ case OP_Column: {
u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
+ pC = p->apCsr[pOp->p1];
p2 = pOp->p2;
+
+ /* If the cursor cache is stale, bring it up-to-date */
+ rc = sqlite3VdbeCursorMoveto(&pC, &p2);
+
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
assert( pOp->p1>=0 && pOp->p1nCursor );
- pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( p2nField );
aOffset = pC->aOffset;
@@ -2390,8 +2400,6 @@ case OP_Column: {
assert( pC->eCurType!=CURTYPE_SORTER );
pCrsr = pC->uc.pCursor;
- /* If the cursor cache is stale, bring it up-to-date */
- rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
if( pC->cacheStatus!=p->cacheCtr ){
if( pC->nullRow ){
@@ -2862,7 +2870,7 @@ case OP_Savepoint: {
#endif
/* Create a new savepoint structure. */
- pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
+ pNew = sqlite3DbMallocRawNN(db, sizeof(Savepoint)+nName+1);
if( pNew ){
pNew->zName = (char *)&pNew[1];
memcpy(pNew->zName, zName, nName+1);
@@ -2999,28 +3007,27 @@ case OP_Savepoint: {
case OP_AutoCommit: {
int desiredAutoCommit;
int iRollback;
- int turnOnAC;
desiredAutoCommit = pOp->p1;
iRollback = pOp->p2;
- turnOnAC = desiredAutoCommit && !db->autoCommit;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader );
- if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
- /* If this instruction implements a COMMIT and other VMs are writing
- ** return an error indicating that the other VMs must complete first.
- */
- sqlite3VdbeError(p, "cannot commit transaction - "
- "SQL statements in progress");
- rc = SQLITE_BUSY;
- }else if( desiredAutoCommit!=db->autoCommit ){
+ if( desiredAutoCommit!=db->autoCommit ){
if( iRollback ){
assert( desiredAutoCommit==1 );
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
+ }else if( desiredAutoCommit && db->nVdbeWrite>0 ){
+ /* If this instruction implements a COMMIT and other VMs are writing
+ ** return an error indicating that the other VMs must complete first.
+ */
+ sqlite3VdbeError(p, "cannot commit transaction - "
+ "SQL statements in progress");
+ rc = SQLITE_BUSY;
+ break;
}else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
}else{
@@ -3205,15 +3212,15 @@ case OP_ReadCookie: { /* out2 */
/* Opcode: SetCookie P1 P2 P3 * *
**
-** Write the content of register P3 (interpreted as an integer)
-** into cookie number P2 of database P1. P2==1 is the schema version.
-** P2==2 is the database format. P2==3 is the recommended pager cache
+** Write the integer value P3 into cookie number P2 of database P1.
+** P2==1 is the schema version. P2==2 is the database format.
+** P2==3 is the recommended pager cache
** size, and so forth. P1==0 is the main database file and P1==1 is the
** database file used to store temporary tables.
**
** A transaction must be started before executing this opcode.
*/
-case OP_SetCookie: { /* in3 */
+case OP_SetCookie: {
Db *pDb;
assert( pOp->p2p1>=0 && pOp->p1nDb );
@@ -3222,17 +3229,15 @@ case OP_SetCookie: { /* in3 */
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
- pIn3 = &aMem[pOp->p3];
- sqlite3VdbeMemIntegerify(pIn3);
/* See note about index shifting on OP_ReadCookie */
- rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i);
+ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
- pDb->pSchema->schema_cookie = (int)pIn3->u.i;
+ pDb->pSchema->schema_cookie = pOp->p3;
db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
- pDb->pSchema->file_format = (u8)pIn3->u.i;
+ pDb->pSchema->file_format = pOp->p3;
}
if( pOp->p1==1 ){
/* Invalidate all prepared statements whenever the TEMP database
@@ -3392,6 +3397,9 @@ case OP_OpenWrite:
pCur->nullRow = 1;
pCur->isOrdered = 1;
pCur->pgnoRoot = p2;
+#ifdef SQLITE_DEBUG
+ pCur->wrFlag = wrFlag;
+#endif
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor);
pCur->pKeyInfo = pKeyInfo;
/* Set the VdbeCursor.isTable variable. Previous versions of
@@ -3845,32 +3853,6 @@ seek_not_found:
}
break;
}
-
-/* Opcode: Seek P1 P2 * * *
-** Synopsis: intkey=r[P2]
-**
-** P1 is an open table cursor and P2 is a rowid integer. Arrange
-** for P1 to move so that it points to the rowid given by P2.
-**
-** This is actually a deferred seek. Nothing actually happens until
-** the cursor is used to read a record. That way, if no reads
-** occur, no unnecessary I/O happens.
-*/
-case OP_Seek: { /* in2 */
- VdbeCursor *pC;
-
- assert( pOp->p1>=0 && pOp->p1nCursor );
- pC = p->apCsr[pOp->p1];
- assert( pC!=0 );
- assert( pC->eCurType==CURTYPE_BTREE );
- assert( pC->uc.pCursor!=0 );
- assert( pC->isTable );
- pC->nullRow = 0;
- pIn2 = &aMem[pOp->p2];
- pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
- pC->deferredMoveto = 1;
- break;
-}
/* Opcode: Found P1 P2 P3 P4 *
@@ -4341,14 +4323,22 @@ case OP_InsertInt: {
**
** Delete the record at which the P1 cursor is currently pointing.
**
-** If the P5 parameter is non-zero, the cursor will be left pointing at
-** either the next or the previous record in the table. If it is left
-** pointing at the next record, then the next Next instruction will be a
-** no-op. As a result, in this case it is OK to delete a record from within a
-** Next loop. If P5 is zero, then the cursor is left in an undefined state.
+** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then
+** the cursor will be left pointing at either the next or the previous
+** record in the table. If it is left pointing at the next record, then
+** the next Next instruction will be a no-op. As a result, in this case
+** it is ok to delete a record from within a Next loop. If
+** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be
+** left in an undefined state.
**
-** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
-** incremented (otherwise not).
+** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this
+** delete one of several associated with deleting a table row and all its
+** associated index entries. Exactly one of those deletes is the "primary"
+** delete. The others are all on OPFLAG_FORDELETE cursors or else are
+** marked with the AUXDELETE flag.
+**
+** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
+** change count is incremented (otherwise not).
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
@@ -4384,7 +4374,26 @@ case OP_Delete: {
assert( pC->movetoTarget==iKey );
}
#endif
-
+
+ /* Only flags that can be set are SAVEPOISTION and AUXDELETE */
+ assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 );
+ assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION );
+ assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE );
+
+#ifdef SQLITE_DEBUG
+ if( p->pFrame==0 ){
+ if( pC->isEphemeral==0
+ && (pOp->p5 & OPFLAG_AUXDELETE)==0
+ && (pC->wrFlag & OPFLAG_FORDELETE)==0
+ ){
+ nExtraDelete++;
+ }
+ if( pOp->p2 & OPFLAG_NCHANGE ){
+ nExtraDelete--;
+ }
+ }
+#endif
+
rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5);
pC->cacheStatus = CACHE_STALE;
@@ -4929,18 +4938,34 @@ case OP_IdxDelete: {
r.nField = (u16)pOp->p3;
r.default_rc = 0;
r.aMem = &aMem[pOp->p2];
-#ifdef SQLITE_DEBUG
- { int i; for(i=0; ideferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
break;
}
+/* Opcode: Seek P1 * P3 P4 *
+** Synopsis: Move P3 to P1.rowid
+**
+** P1 is an open index cursor and P3 is a cursor on the corresponding
+** table. This opcode does a deferred seek of the P3 table cursor
+** to the row that corresponds to the current row of P1.
+**
+** This is a deferred seek. Nothing actually happens until
+** the cursor is used to read a record. That way, if no reads
+** occur, no unnecessary I/O happens.
+**
+** P4 may be an array of integers (type P4_INTARRAY) containing
+** one entry for each column in the P3 table. If array entry a(i)
+** is non-zero, then reading column a(i)-1 from cursor P3 is
+** equivalent to performing the deferred seek and then reading column i
+** from P1. This information is stored in P3 and used to redirect
+** reads against P3 over to P1, thus possibly avoiding the need to
+** seek and read cursor P3.
+*/
/* Opcode: IdxRowid P1 P2 * * *
** Synopsis: r[P2]=rowid
**
@@ -4950,37 +4975,57 @@ case OP_IdxDelete: {
**
** See also: Rowid, MakeRecord.
*/
+case OP_Seek:
case OP_IdxRowid: { /* out2 */
- BtCursor *pCrsr;
- VdbeCursor *pC;
- i64 rowid;
+ VdbeCursor *pC; /* The P1 index cursor */
+ VdbeCursor *pTabCur; /* The P2 table cursor (OP_Seek only) */
+ i64 rowid; /* Rowid that P1 current points to */
- pOut = out2Prerelease(p, pOp);
assert( pOp->p1>=0 && pOp->p1nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
- pCrsr = pC->uc.pCursor;
- assert( pCrsr!=0 );
- pOut->flags = MEM_Null;
+ assert( pC->uc.pCursor!=0 );
assert( pC->isTable==0 );
assert( pC->deferredMoveto==0 );
+ assert( !pC->nullRow || pOp->opcode==OP_IdxRowid );
+
+ /* The IdxRowid and Seek opcodes are combined because of the commonality
+ ** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */
+ rc = sqlite3VdbeCursorRestore(pC);
/* sqlite3VbeCursorRestore() can only fail if the record has been deleted
- ** out from under the cursor. That will never happend for an IdxRowid
- ** opcode, hence the NEVER() arround the check of the return value.
- */
- rc = sqlite3VdbeCursorRestore(pC);
+ ** out from under the cursor. That will never happens for an IdxRowid
+ ** or Seek opcode */
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
if( !pC->nullRow ){
rowid = 0; /* Not needed. Only used to silence a warning. */
- rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid);
+ rc = sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pOut->u.i = rowid;
- pOut->flags = MEM_Int;
+ if( pOp->opcode==OP_Seek ){
+ assert( pOp->p3>=0 && pOp->p3nCursor );
+ pTabCur = p->apCsr[pOp->p3];
+ assert( pTabCur!=0 );
+ assert( pTabCur->eCurType==CURTYPE_BTREE );
+ assert( pTabCur->uc.pCursor!=0 );
+ assert( pTabCur->isTable );
+ pTabCur->nullRow = 0;
+ pTabCur->movetoTarget = rowid;
+ pTabCur->deferredMoveto = 1;
+ assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
+ pTabCur->aAltMap = pOp->p4.ai;
+ pTabCur->pAltCursor = pC;
+ }else{
+ pOut = out2Prerelease(p, pOp);
+ pOut->u.i = rowid;
+ pOut->flags = MEM_Int;
+ }
+ }else{
+ assert( pOp->opcode==OP_IdxRowid );
+ sqlite3VdbeMemSetNull(&aMem[pOp->p2]);
}
break;
}
@@ -5375,7 +5420,7 @@ case OP_IntegrityCk: {
assert( p->bIsReader );
nRoot = pOp->p2;
assert( nRoot>0 );
- aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) );
+ aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(nRoot+1) );
if( aRoot==0 ) goto no_mem;
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pnErr = &aMem[pOp->p3];
@@ -5757,20 +5802,31 @@ case OP_IfPos: { /* jump, in1 */
break;
}
-/* Opcode: SetIfNotPos P1 P2 P3 * *
-** Synopsis: if r[P1]<=0 then r[P2]=P3
+/* Opcode: OffsetLimit P1 P2 P3 * *
+** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)
**
-** 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.
+** This opcode performs a commonly used computation associated with
+** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3]
+** holds the offset counter. The opcode computes the combined value
+** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2]
+** value computed is the total number of rows that will need to be
+** visited in order to complete the query.
+**
+** If r[P3] is zero or negative, that means there is no OFFSET
+** and r[P2] is set to be the value of the LIMIT, r[P1].
+**
+** if r[P1] is zero or negative, that means there is no LIMIT
+** and r[P2] is set to -1.
+**
+** Otherwise, r[P2] is set to the sum of r[P1] and r[P3].
*/
-case OP_SetIfNotPos: { /* in1, in2 */
+case OP_OffsetLimit: { /* in1, out2, in3 */
pIn1 = &aMem[pOp->p1];
- assert( pIn1->flags&MEM_Int );
- if( pIn1->u.i<=0 ){
- pOut = out2Prerelease(p, pOp);
- pOut->u.i = pOp->p3;
- }
+ pIn3 = &aMem[pOp->p3];
+ pOut = out2Prerelease(p, pOp);
+ assert( pIn1->flags & MEM_Int );
+ assert( pIn3->flags & MEM_Int );
+ pOut->u.i = pIn1->u.i<=0 ? -1 : pIn1->u.i+(pIn3->u.i>0?pIn3->u.i:0);
break;
}
@@ -5861,7 +5917,7 @@ case OP_AggStep0: {
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) );
assert( pOp->p3p2 || pOp->p3>=pOp->p2+n );
- pCtx = sqlite3DbMallocRaw(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
+ pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
if( pCtx==0 ) goto no_mem;
pCtx->pMem = 0;
pCtx->pFunc = pOp->p4.pFunc;
@@ -6728,7 +6784,7 @@ vdbe_error_halt:
sqlite3_log(rc, "statement aborts at %d: [%s] %s",
(int)(pOp - aOp), p->zSql, p->zErrMsg);
sqlite3VdbeHalt(p);
- if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
rc = SQLITE_ERROR;
if( resetSchemaOnFault>0 ){
sqlite3ResetOneSchema(db, resetSchemaOnFault-1);
@@ -6742,6 +6798,9 @@ vdbe_return:
testcase( nVmStep>0 );
p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
sqlite3VdbeLeave(p);
+ assert( rc!=SQLITE_OK || nExtraDelete==0
+ || sqlite3_strlike("DELETE%",p->zSql,0)!=0
+ );
return rc;
/* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
@@ -6755,7 +6814,7 @@ too_big:
/* Jump to here if a malloc() fails.
*/
no_mem:
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3VdbeError(p, "out of memory");
rc = SQLITE_NOMEM;
goto vdbe_error_halt;
@@ -6776,7 +6835,7 @@ abort_due_to_error:
*/
abort_due_to_interrupt:
assert( db->u1.isInterrupted );
- rc = SQLITE_INTERRUPT;
+ rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_INTERRUPT;
p->rc = rc;
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
goto vdbe_error_halt;
diff --git a/src/vdbe.h b/src/vdbe.h
index f09997bf94..4c02f5844d 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -180,6 +180,7 @@ int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int);
int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
+void sqlite3VdbeEndCoroutine(Vdbe*,int);
#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
#else
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index b231cf908e..839c2039be 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -74,6 +74,7 @@ typedef struct AuxData AuxData;
** * A virtual table
** * A one-row "pseudotable" stored in a single register
*/
+typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
u8 eCurType; /* One of the CURTYPE_* values above */
i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
@@ -82,6 +83,7 @@ struct VdbeCursor {
u8 isTable; /* True for rowid tables. False for indexes */
#ifdef SQLITE_DEBUG
u8 seekOp; /* Most recent seek operation on this cursor */
+ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */
#endif
Bool isEphemeral:1; /* True for an ephemeral table */
Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
@@ -100,6 +102,8 @@ struct VdbeCursor {
int seekResult; /* Result of previous sqlite3BtreeMoveto() */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
+ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
+ int *aAltMap; /* Mapping from table to index column numbers */
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
u64 maskUsed; /* Mask of columns used by this cursor */
#endif
@@ -124,7 +128,6 @@ struct VdbeCursor {
** static element declared in the structure. nField total array slots for
** aType[] and nField+1 array slots for aOffset[] */
};
-typedef struct VdbeCursor VdbeCursor;
/*
** When a sub-program is executed (OP_Program), a structure of this type
@@ -235,7 +238,7 @@ struct Mem {
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
-#define MEM_TypeMask 0x01ff /* Mask of type bits */
+#define MEM_TypeMask 0x81ff /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of
@@ -249,11 +252,18 @@ struct Mem {
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
+#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */
#ifdef SQLITE_OMIT_INCRBLOB
#undef MEM_Zero
#define MEM_Zero 0x0000
#endif
+/* Return TRUE if Mem X contains dynamically allocated content - anything
+** that needs to be deallocated to avoid a leak.
+*/
+#define VdbeMemDynamic(X) \
+ (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
+
/*
** Clear any existing type flags from a Mem and replace them with f
*/
@@ -423,7 +433,7 @@ struct Vdbe {
void sqlite3VdbeError(Vdbe*, const char *, ...);
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
-int sqlite3VdbeCursorMoveto(VdbeCursor*);
+int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
void sqlite3VdbePrintOp(FILE*, int, Op*);
@@ -469,8 +479,6 @@ int sqlite3VdbeMemNumerify(Mem*);
void sqlite3VdbeMemCast(Mem*,u8,u8);
int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
-#define VdbeMemDynamic(X) \
- (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 4bc912b940..fce43630e7 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -188,7 +188,8 @@ sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){
return sqlite3VdbeIntValue((Mem*)pVal);
}
unsigned int sqlite3_value_subtype(sqlite3_value *pVal){
- return ((Mem*)pVal)->eSubtype;
+ Mem *pMem = (Mem*)pVal;
+ return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0);
}
const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
@@ -369,8 +370,10 @@ void sqlite3_result_null(sqlite3_context *pCtx){
sqlite3VdbeMemSetNull(pCtx->pOut);
}
void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- pCtx->pOut->eSubtype = eSubtype & 0xff;
+ Mem *pOut = pCtx->pOut;
+ assert( sqlite3_mutex_held(pOut->db->mutex) );
+ pOut->eSubtype = eSubtype & 0xff;
+ pOut->flags |= MEM_Subtype;
}
void sqlite3_result_text(
sqlite3_context *pCtx,
@@ -470,7 +473,7 @@ void sqlite3_result_error_nomem(sqlite3_context *pCtx){
sqlite3VdbeMemSetNull(pCtx->pOut);
pCtx->isError = SQLITE_NOMEM;
pCtx->fErrorOrAux = 1;
- pCtx->pOut->db->mallocFailed = 1;
+ sqlite3OomFault(pCtx->pOut->db);
}
/*
@@ -1098,7 +1101,7 @@ static const void *columnName(
** is the case, clear the mallocFailed flag and return NULL.
*/
if( db->mallocFailed ){
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
ret = 0;
}
sqlite3_mutex_leave(db->mutex);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 17f1cb7dc4..ce98edd0a5 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -171,7 +171,7 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
i = p->nOp;
assert( p->magic==VDBE_MAGIC_INIT );
- assert( op>0 && op<0xff );
+ assert( op>=0 && op<0xff );
if( p->pParse->nOpAlloc<=i ){
return growOp3(p, op, p1, p2, p3);
}
@@ -289,7 +289,7 @@ int sqlite3VdbeAddOp4Dup8(
const u8 *zP4, /* The P4 operand */
int p4type /* P4 operand type */
){
- char *p4copy = sqlite3DbMallocRaw(sqlite3VdbeDb(p), 8);
+ char *p4copy = sqlite3DbMallocRawNN(sqlite3VdbeDb(p), 8);
if( p4copy ) memcpy(p4copy, zP4, 8);
return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
}
@@ -324,6 +324,21 @@ int sqlite3VdbeAddOp4Int(
return addr;
}
+/* Insert the end of a co-routine
+*/
+void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+
+ /* Clear the temporary register cache, thereby ensuring that each
+ ** co-routine has its own independent set of registers, because co-routines
+ ** might expect their registers to be preserved across an OP_Yield, and
+ ** that could cause problems if two or more co-routines are using the same
+ ** temporary register.
+ */
+ v->pParse->nTempReg = 0;
+ v->pParse->nRangeReg = 0;
+}
+
/*
** Create a new symbolic label for an instruction that has yet to be
** coded. The symbolic label is really just a negative number. The
@@ -534,7 +549,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
- /* NOTE: Be sure to update mkopcodeh.awk when adding or removing
+ /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing
** cases from this switch! */
switch( opcode ){
case OP_Transaction: {
@@ -646,6 +661,9 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
/*
** Add a whole list of operations to the operation stack. Return a
** pointer to the first operation inserted.
+**
+** Non-zero P2 arguments to jump instructions are automatically adjusted
+** so that the jump target is relative to the first operation inserted.
*/
VdbeOp *sqlite3VdbeAddOpList(
Vdbe *p, /* Add opcodes to the prepared statement */
@@ -666,6 +684,9 @@ VdbeOp *sqlite3VdbeAddOpList(
pOut->p1 = aOp->p1;
pOut->p2 = aOp->p2;
assert( aOp->p2>=0 );
+ if( (sqlite3OpcodeProperty[aOp->opcode] & OPFLG_JUMP)!=0 && aOp->p2>0 ){
+ pOut->p2 += p->nOp;
+ }
pOut->p3 = aOp->p3;
pOut->p4type = P4_NOTUSED;
pOut->p4.p = 0;
@@ -1117,28 +1138,27 @@ static int displayComment(
** Translate the P4.pExpr value for an OP_CursorHint opcode into text
** that can be displayed in the P4 column of EXPLAIN output.
*/
-static int displayP4Expr(int nTemp, char *zTemp, Expr *pExpr){
+static void displayP4Expr(StrAccum *p, Expr *pExpr){
const char *zOp = 0;
- int n;
switch( pExpr->op ){
case TK_STRING:
- sqlite3_snprintf(nTemp, zTemp, "%Q", pExpr->u.zToken);
+ sqlite3XPrintf(p, "%Q", pExpr->u.zToken);
break;
case TK_INTEGER:
- sqlite3_snprintf(nTemp, zTemp, "%d", pExpr->u.iValue);
+ sqlite3XPrintf(p, "%d", pExpr->u.iValue);
break;
case TK_NULL:
- sqlite3_snprintf(nTemp, zTemp, "NULL");
+ sqlite3XPrintf(p, "NULL");
break;
case TK_REGISTER: {
- sqlite3_snprintf(nTemp, zTemp, "r[%d]", pExpr->iTable);
+ sqlite3XPrintf(p, "r[%d]", pExpr->iTable);
break;
}
case TK_COLUMN: {
if( pExpr->iColumn<0 ){
- sqlite3_snprintf(nTemp, zTemp, "rowid");
+ sqlite3XPrintf(p, "rowid");
}else{
- sqlite3_snprintf(nTemp, zTemp, "c%d", (int)pExpr->iColumn);
+ sqlite3XPrintf(p, "c%d", (int)pExpr->iColumn);
}
break;
}
@@ -1170,21 +1190,19 @@ static int displayP4Expr(int nTemp, char *zTemp, Expr *pExpr){
case TK_NOTNULL: zOp = "NOTNULL"; break;
default:
- sqlite3_snprintf(nTemp, zTemp, "%s", "expr");
+ sqlite3XPrintf(p, "%s", "expr");
break;
}
if( zOp ){
- sqlite3_snprintf(nTemp, zTemp, "%s(", zOp);
- n = sqlite3Strlen30(zTemp);
- n += displayP4Expr(nTemp-n, zTemp+n, pExpr->pLeft);
- if( npRight ){
- zTemp[n++] = ',';
- n += displayP4Expr(nTemp-n, zTemp+n, pExpr->pRight);
+ sqlite3XPrintf(p, "%s(", zOp);
+ displayP4Expr(p, pExpr->pLeft);
+ if( pExpr->pRight ){
+ sqlite3StrAccumAppend(p, ",", 1);
+ displayP4Expr(p, pExpr->pRight);
}
- sqlite3_snprintf(nTemp-n, zTemp+n, ")");
+ sqlite3StrAccumAppend(p, ")", 1);
}
- return sqlite3Strlen30(zTemp);
}
#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
@@ -1196,72 +1214,57 @@ static int displayP4Expr(int nTemp, char *zTemp, Expr *pExpr){
*/
static char *displayP4(Op *pOp, char *zTemp, int nTemp){
char *zP4 = zTemp;
+ StrAccum x;
assert( nTemp>=20 );
+ sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
switch( pOp->p4type ){
case P4_KEYINFO: {
- int i, j;
+ int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->aSortOrder!=0 );
- sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField);
- i = sqlite3Strlen30(zTemp);
+ sqlite3XPrintf(&x, "k(%d", pKeyInfo->nField);
for(j=0; jnField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
- const char *zColl = pColl ? pColl->zName : "nil";
- int n = sqlite3Strlen30(zColl);
- if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
- zColl = "B";
- n = 1;
- }
- if( i+n>nTemp-7 ){
- memcpy(&zTemp[i],",...",4);
- i += 4;
- break;
- }
- zTemp[i++] = ',';
- if( pKeyInfo->aSortOrder[j] ){
- zTemp[i++] = '-';
- }
- memcpy(&zTemp[i], zColl, n+1);
- i += n;
+ const char *zColl = pColl ? pColl->zName : "";
+ if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
+ sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
}
- zTemp[i++] = ')';
- zTemp[i] = 0;
- assert( ip4.pExpr);
+ displayP4Expr(&x, pOp->p4.pExpr);
break;
}
#endif
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
- sqlite3_snprintf(nTemp, zTemp, "(%.20s)", pColl->zName);
+ sqlite3XPrintf(&x, "(%.20s)", pColl->zName);
break;
}
case P4_FUNCDEF: {
FuncDef *pDef = pOp->p4.pFunc;
- sqlite3_snprintf(nTemp, zTemp, "%s(%d)", pDef->zName, pDef->nArg);
+ sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
#ifdef SQLITE_DEBUG
case P4_FUNCCTX: {
FuncDef *pDef = pOp->p4.pCtx->pFunc;
- sqlite3_snprintf(nTemp, zTemp, "%s(%d)", pDef->zName, pDef->nArg);
+ sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
#endif
case P4_INT64: {
- sqlite3_snprintf(nTemp, zTemp, "%lld", *pOp->p4.pI64);
+ sqlite3XPrintf(&x, "%lld", *pOp->p4.pI64);
break;
}
case P4_INT32: {
- sqlite3_snprintf(nTemp, zTemp, "%d", pOp->p4.i);
+ sqlite3XPrintf(&x, "%d", pOp->p4.i);
break;
}
case P4_REAL: {
- sqlite3_snprintf(nTemp, zTemp, "%.16g", *pOp->p4.pReal);
+ sqlite3XPrintf(&x, "%.16g", *pOp->p4.pReal);
break;
}
case P4_MEM: {
@@ -1269,11 +1272,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
- sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
+ sqlite3XPrintf(&x, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
- sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->u.r);
+ sqlite3XPrintf(&x, "%.16g", pMem->u.r);
}else if( pMem->flags & MEM_Null ){
- sqlite3_snprintf(nTemp, zTemp, "NULL");
+ zP4 = "NULL";
}else{
assert( pMem->flags & MEM_Blob );
zP4 = "(blob)";
@@ -1283,16 +1286,24 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
#ifndef SQLITE_OMIT_VIRTUALTABLE
case P4_VTAB: {
sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab;
- sqlite3_snprintf(nTemp, zTemp, "vtab:%p", pVtab);
+ sqlite3XPrintf(&x, "vtab:%p", pVtab);
break;
}
#endif
case P4_INTARRAY: {
- sqlite3_snprintf(nTemp, zTemp, "intarray");
+ int i;
+ int *ai = pOp->p4.ai;
+ int n = ai[0]; /* The first element of an INTARRAY is always the
+ ** count of the number of elements to follow */
+ for(i=1; idb;
- u8 malloc_failed = db->mallocFailed;
if( db->pnBytesFreed ){
do{
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
@@ -1462,7 +1473,6 @@ static void releaseMemArray(Mem *p, int N){
p->flags = MEM_Undefined;
}while( (++p)mallocFailed = malloc_failed;
}
}
@@ -1523,7 +1533,7 @@ int sqlite3VdbeList(
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return SQLITE_ERROR;
}
@@ -1721,41 +1731,43 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
}
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
-/*
-** Allocate space from a fixed size buffer and return a pointer to
-** that space. If insufficient space is available, return NULL.
+/* An instance of this object describes bulk memory available for use
+** by subcomponents of a prepared statement. Space is allocated out
+** of a ReusableSpace object by the allocSpace() routine below.
+*/
+struct ReusableSpace {
+ u8 *pSpace; /* Available memory */
+ int nFree; /* Bytes of available memory */
+ int nNeeded; /* Total bytes that could not be allocated */
+};
+
+/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf
+** from the ReusableSpace object. Return a pointer to the allocated
+** memory on success. If insufficient memory is available in the
+** ReusableSpace object, increase the ReusableSpace.nNeeded
+** value by the amount needed and return NULL.
**
-** The pBuf parameter is the initial value of a pointer which will
-** receive the new memory. pBuf is normally NULL. If pBuf is not
-** NULL, it means that memory space has already been allocated and that
-** this routine should not allocate any new memory. When pBuf is not
-** NULL simply return pBuf. Only allocate new memory space when pBuf
-** is NULL.
+** If pBuf is not initially NULL, that means that the memory has already
+** been allocated by a prior call to this routine, so just return a copy
+** of pBuf and leave ReusableSpace unchanged.
**
-** nByte is the number of bytes of space needed.
-**
-** pFrom points to *pnFrom bytes of available space. New space is allocated
-** from the end of the pFrom buffer and *pnFrom is decremented.
-**
-** *pnNeeded is a counter of the number of bytes of space that have failed
-** to allocate. If there is insufficient space in pFrom to satisfy the
-** request, then increment *pnNeeded by the amount of the request.
+** This allocator is employed to repurpose unused slots at the end of the
+** opcode array of prepared state for other memory needs of the prepared
+** statement.
*/
static void *allocSpace(
- void *pBuf, /* Where return pointer will be stored */
- int nByte, /* Number of bytes to allocate */
- u8 *pFrom, /* Memory available for allocation */
- int *pnFrom, /* IN/OUT: Space available at pFrom */
- int *pnNeeded /* If allocation cannot be made, increment *pnByte */
+ struct ReusableSpace *p, /* Bulk memory available for allocation */
+ void *pBuf, /* Pointer to a prior allocation */
+ int nByte /* Bytes of memory needed */
){
- assert( EIGHT_BYTE_ALIGNMENT(pFrom) );
+ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) );
if( pBuf==0 ){
nByte = ROUND8(nByte);
- if( nByte <= *pnFrom ){
- *pnFrom -= nByte;
- pBuf = &pFrom[*pnFrom];
+ if( nByte <= p->nFree ){
+ p->nFree -= nByte;
+ pBuf = &p->pSpace[p->nFree];
}else{
- *pnNeeded += nByte;
+ p->nNeeded += nByte;
}
}
assert( EIGHT_BYTE_ALIGNMENT(pBuf) );
@@ -1788,7 +1800,6 @@ void sqlite3VdbeRewind(Vdbe *p){
p->pc = -1;
p->rc = SQLITE_OK;
p->errorAction = OE_Abort;
- p->magic = VDBE_MAGIC_RUN;
p->nChange = 0;
p->cacheCtr = 1;
p->minWriteFileFormat = 255;
@@ -1831,9 +1842,7 @@ void sqlite3VdbeMakeReady(
int nArg; /* Number of arguments in subprograms */
int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
- int nFree; /* Available free space */
- u8 *zCsr; /* Memory available for allocation */
- int nByte; /* How much extra memory is needed */
+ struct ReusableSpace x; /* Reusable bulk memory */
assert( p!=0 );
assert( p->nOp>0 );
@@ -1851,7 +1860,7 @@ void sqlite3VdbeMakeReady(
/* For each cursor required, also allocate a memory cell. Memory
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
- ** the vdbe program. Instead they are used to allocate space for
+ ** the vdbe program. Instead they are used to allocate memory for
** VdbeCursor/BtCursor structures. The blob of memory associated with
** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1)
** stores the blob of memory associated with cursor 1, etc.
@@ -1860,20 +1869,18 @@ void sqlite3VdbeMakeReady(
*/
nMem += nCursor;
- /* zCsr will initially point to nFree bytes of unused space at the
- ** end of the opcode array, p->aOp. The computation of nFree is
- ** conservative - it might be smaller than the true number of free
- ** bytes, but never larger. nFree must be a multiple of 8 - it is
- ** rounded down if is not.
+ /* Figure out how much reusable memory is available at the end of the
+ ** opcode array. This extra memory will be reallocated for other elements
+ ** of the prepared statement.
*/
- n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode space used */
- zCsr = &((u8*)p->aOp)[n]; /* Unused opcode space */
- assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
- nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused space */
- assert( nFree>=0 );
- if( nFree>0 ){
- memset(zCsr, 0, nFree);
- assert( EIGHT_BYTE_ALIGNMENT(&zCsr[nFree]) );
+ n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */
+ x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */
+ assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) );
+ x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */
+ assert( x.nFree>=0 );
+ if( x.nFree>0 ){
+ memset(x.pSpace, 0, x.nFree);
+ assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) );
}
resolveP2Values(p, &nArg);
@@ -1883,33 +1890,30 @@ void sqlite3VdbeMakeReady(
}
p->expired = 0;
- /* Memory for registers, parameters, cursor, etc, is allocated in two
- ** passes. On the first pass, we try to reuse unused space at the
+ /* Memory for registers, parameters, cursor, etc, is allocated in one or two
+ ** passes. On the first pass, we try to reuse unused memory at the
** end of the opcode array. If we are unable to satisfy all memory
** requirements by reusing the opcode array tail, then the second
- ** pass will fill in the rest using a fresh allocation.
+ ** pass will fill in the remainder using a fresh memory allocation.
**
** This two-pass approach that reuses as much memory as possible from
- ** the leftover space at the end of the opcode array can significantly
+ ** the leftover memory at the end of the opcode array. This can significantly
** reduce the amount of memory held by a prepared statement.
*/
do {
- nByte = 0;
- p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), zCsr, &nFree, &nByte);
- p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), zCsr, &nFree, &nByte);
- p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), zCsr, &nFree, &nByte);
- p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
- zCsr, &nFree, &nByte);
- p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, zCsr, &nFree, &nByte);
+ x.nNeeded = 0;
+ p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem));
+ p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
+ p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
+ p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
+ p->aOnceFlag = allocSpace(&x, p->aOnceFlag, nOnce);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = allocSpace(p->anExec, p->nOp*sizeof(i64), zCsr, &nFree, &nByte);
+ p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
#endif
- if( nByte ){
- p->pFree = sqlite3DbMallocZero(db, nByte);
- }
- zCsr = p->pFree;
- nFree = nByte;
- }while( nByte && !db->mallocFailed );
+ if( x.nNeeded==0 ) break;
+ x.pSpace = p->pFree = sqlite3DbMallocZero(db, x.nNeeded);
+ x.nFree = x.nNeeded;
+ }while( !db->mallocFailed );
p->nCursor = nCursor;
p->nOnceFlag = nOnce;
@@ -2522,7 +2526,7 @@ int sqlite3VdbeHalt(Vdbe *p){
** one, or the complete transaction if there is no statement transaction.
*/
- if( p->db->mallocFailed ){
+ if( db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
@@ -2683,7 +2687,7 @@ int sqlite3VdbeHalt(Vdbe *p){
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
- if( p->db->mallocFailed ){
+ if( db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
@@ -2720,12 +2724,12 @@ int sqlite3VdbeTransferError(Vdbe *p){
sqlite3 *db = p->db;
int rc = p->rc;
if( p->zErrMsg ){
- u8 mallocFailed = db->mallocFailed;
+ db->bBenignMalloc++;
sqlite3BeginBenignMalloc();
if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db);
sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
sqlite3EndBenignMalloc();
- db->mallocFailed = mallocFailed;
+ db->bBenignMalloc--;
db->errCode = rc;
}else{
sqlite3Error(db, rc);
@@ -3014,9 +3018,16 @@ int sqlite3VdbeCursorRestore(VdbeCursor *p){
** If the cursor is already pointing to the correct row and that row has
** not been deleted out from under the cursor, then this routine is a no-op.
*/
-int sqlite3VdbeCursorMoveto(VdbeCursor *p){
+int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
+ VdbeCursor *p = *pp;
if( p->eCurType==CURTYPE_BTREE ){
if( p->deferredMoveto ){
+ int iMap;
+ if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
+ *pp = p->pAltCursor;
+ *piCol = iMap - 1;
+ return SQLITE_OK;
+ }
return handleDeferredMoveto(p);
}
if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index f9015ad6f4..2ba5301e7a 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -249,19 +249,17 @@ int sqlite3_blob_open(
** which closes the b-tree cursor and (possibly) commits the
** transaction.
*/
- static const int iLn = VDBE_OFFSET_LINENO(4);
+ static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList openBlob[] = {
- /* addr/ofst */
- /* {OP_Transaction, 0, 0, 0}, // 0/ inserted separately */
- {OP_TableLock, 0, 0, 0}, /* 1/0: Acquire a read or write lock */
- {OP_OpenRead, 0, 0, 0}, /* 2/1: Open a cursor */
- {OP_Variable, 1, 1, 0}, /* 3/2: Move ?1 into reg[1] */
- {OP_NotExists, 0, 8, 1}, /* 4/3: Seek the cursor */
- {OP_Column, 0, 0, 1}, /* 5/4 */
- {OP_ResultRow, 1, 0, 0}, /* 6/5 */
- {OP_Goto, 0, 3, 0}, /* 7/6 */
- {OP_Close, 0, 0, 0}, /* 8/7 */
- {OP_Halt, 0, 0, 0}, /* 9/8 */
+ {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */
+ {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */
+ {OP_Variable, 1, 1, 0}, /* 2: Move ?1 into reg[1] */
+ {OP_NotExists, 0, 7, 1}, /* 3: Seek the cursor */
+ {OP_Column, 0, 0, 1}, /* 4 */
+ {OP_ResultRow, 1, 0, 0}, /* 5 */
+ {OP_Goto, 0, 2, 0}, /* 6 */
+ {OP_Close, 0, 0, 0}, /* 7 */
+ {OP_Halt, 0, 0, 0}, /* 8 */
};
Vdbe *v = (Vdbe *)pBlob->pStmt;
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 0eeb59ef85..6fb7cebbcc 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -116,6 +116,7 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
+ testcase( pMem->db==0 );
/* If the bPreserve flag is set to true, then the memory cell must already
** contain a valid string or blob value. */
@@ -719,7 +720,7 @@ void sqlite3VdbeMemSetRowSet(Mem *pMem){
assert( db!=0 );
assert( (pMem->flags & MEM_RowSet)==0 );
sqlite3VdbeMemRelease(pMem);
- pMem->zMalloc = sqlite3DbMallocRaw(db, 64);
+ pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
if( db->mallocFailed ){
pMem->flags = MEM_Null;
pMem->szMalloc = 0;
@@ -1381,7 +1382,7 @@ static int valueFromExpr(
return rc;
no_mem:
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, zVal);
assert( *ppVal==0 );
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
@@ -1440,7 +1441,7 @@ static void recordFunc(
db = sqlite3_context_db_handle(context);
nRet = 1 + nSerial + nVal;
- aRet = sqlite3DbMallocRaw(db, nRet);
+ aRet = sqlite3DbMallocRawNN(db, nRet);
if( aRet==0 ){
sqlite3_result_error_nomem(context);
}else{
diff --git a/src/vdbesort.c b/src/vdbesort.c
index e095f80912..380772ee16 100644
--- a/src/vdbesort.c
+++ b/src/vdbesort.c
@@ -1821,6 +1821,7 @@ int sqlite3VdbeSorterWrite(
if( nMin>pSorter->nMemory ){
u8 *aNew;
+ int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
int nNew = pSorter->nMemory * 2;
while( nNew < nMin ) nNew = nNew*2;
if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize;
@@ -1828,16 +1829,16 @@ int sqlite3VdbeSorterWrite(
aNew = sqlite3Realloc(pSorter->list.aMemory, nNew);
if( !aNew ) return SQLITE_NOMEM;
- pSorter->list.pList = (SorterRecord*)(
- aNew + ((u8*)pSorter->list.pList - pSorter->list.aMemory)
- );
+ pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
pSorter->list.aMemory = aNew;
pSorter->nMemory = nNew;
}
pNew = (SorterRecord*)&pSorter->list.aMemory[pSorter->iMemory];
pSorter->iMemory += ROUND8(nReq);
- pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory);
+ if( pSorter->list.pList ){
+ pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory);
+ }
}else{
pNew = (SorterRecord *)sqlite3Malloc(nReq);
if( pNew==0 ){
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index c230b50554..07235c931b 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -128,9 +128,9 @@ char *sqlite3VdbeExpandSql(
if( pVar->flags & MEM_Null ){
sqlite3StrAccumAppend(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
- sqlite3XPrintf(&out, 0, "%lld", pVar->u.i);
+ sqlite3XPrintf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
- sqlite3XPrintf(&out, 0, "%!.15g", pVar->u.r);
+ sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
}else if( pVar->flags & MEM_Str ){
int nOut; /* Number of bytes of the string text to include in output */
#ifndef SQLITE_OMIT_UTF16
@@ -151,17 +151,17 @@ char *sqlite3VdbeExpandSql(
while( nOutn && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
}
#endif
- sqlite3XPrintf(&out, 0, "'%.*q'", nOut, pVar->z);
+ sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOutn ){
- sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
}
#endif
#ifndef SQLITE_OMIT_UTF16
if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
#endif
}else if( pVar->flags & MEM_Zero ){
- sqlite3XPrintf(&out, 0, "zeroblob(%d)", pVar->u.nZero);
+ sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
}else{
int nOut; /* Number of bytes of the blob to include in output */
assert( pVar->flags & MEM_Blob );
@@ -171,12 +171,12 @@ char *sqlite3VdbeExpandSql(
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
#endif
for(i=0; iz[i]&0xff);
+ sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOutn ){
- sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
}
#endif
}
diff --git a/src/vtab.c b/src/vtab.c
index ea79cd8cef..e8794e687d 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -49,7 +49,7 @@ static int createModule(
rc = SQLITE_MISUSE_BKPT;
}else{
Module *pMod;
- pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
+ pMod = (Module *)sqlite3DbMallocRawNN(db, sizeof(Module) + nName + 1);
if( pMod ){
Module *pDel;
char *zCopy = (char *)(&pMod[1]);
@@ -62,7 +62,7 @@ static int createModule(
pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
assert( pDel==0 || pDel==pMod );
if( pDel ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
}
}
@@ -439,7 +439,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
assert( sqlite3SchemaMutexHeld(db, 0, pSchema) );
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab);
if( pOld ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */
return;
}
@@ -530,7 +530,7 @@ static int vtabCallConstructor(
db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
db->pVtabCtx = sCtx.pPrior;
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
@@ -1088,7 +1088,7 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
pToplevel->apVtabLock = apVtabLock;
pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
}else{
- pToplevel->db->mallocFailed = 1;
+ sqlite3OomFault(pToplevel->db);
}
}
diff --git a/src/vxworks.h b/src/vxworks.h
index 60c41a19b8..e7013c3f66 100644
--- a/src/vxworks.h
+++ b/src/vxworks.h
@@ -28,4 +28,5 @@
#define OS_VXWORKS 0
#define HAVE_FCHOWN 1
#define HAVE_READLINK 1
+#define HAVE_LSTAT 1
#endif /* defined(_WRS_KERNEL) */
diff --git a/src/where.c b/src/where.c
index 892633e572..af6bb20384 100644
--- a/src/where.c
+++ b/src/where.c
@@ -943,7 +943,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM ){
- pParse->db->mallocFailed = 1;
+ sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc));
}else{
@@ -1735,7 +1735,7 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
WhereTerm **paNew;
if( p->nLSlot>=n ) return SQLITE_OK;
n = (n+7)&~7;
- paNew = sqlite3DbMallocRaw(db, sizeof(p->aLTerm[0])*n);
+ paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n);
if( paNew==0 ) return SQLITE_NOMEM;
memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot);
if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
@@ -2032,7 +2032,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
#endif
if( p==0 ){
/* Allocate a new WhereLoop to add to the end of the list */
- *ppPrev = p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
+ *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop));
if( p==0 ) return SQLITE_NOMEM;
whereLoopInit(p);
p->pNextLoop = 0;
@@ -3529,7 +3529,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
/* Allocate and initialize space for aTo, aFrom and aSortCost[] */
nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
nSpace += sizeof(LogEst) * nOrderBy;
- pSpace = sqlite3DbMallocRaw(db, nSpace);
+ pSpace = sqlite3DbMallocRawNN(db, nSpace);
if( pSpace==0 ) return SQLITE_NOMEM;
aTo = (WherePath*)pSpace;
aFrom = aTo+mxChoice;
diff --git a/src/wherecode.c b/src/wherecode.c
index 9d53a20a67..bb48e5dc70 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -76,7 +76,7 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
for(i=0; i=nSkip ? "%s=?" : "ANY(%s)", z);
+ sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
}
j = i;
@@ -135,13 +135,13 @@ int sqlite3WhereExplainOneScan(
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
if( pItem->pSelect ){
- sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
+ sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
}else{
- sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
+ sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
}
if( pItem->zAlias ){
- sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
+ sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
}
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
const char *zFmt = 0;
@@ -165,7 +165,7 @@ int sqlite3WhereExplainOneScan(
}
if( zFmt ){
sqlite3StrAccumAppend(&str, " USING ", 7);
- sqlite3XPrintf(&str, 0, zFmt, pIdx->zName);
+ sqlite3XPrintf(&str, zFmt, pIdx->zName);
explainIndexRange(&str, pLoop);
}
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
@@ -180,17 +180,17 @@ int sqlite3WhereExplainOneScan(
assert( flags&WHERE_TOP_LIMIT);
zRangeOp = "<";
}
- sqlite3XPrintf(&str, 0, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
+ sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
+ sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
if( pLoop->nOut>=10 ){
- sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
+ sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
}else{
sqlite3StrAccumAppend(&str, " (~1 row)", 9);
}
@@ -495,9 +495,7 @@ static int codeAllEqualityTerms(
pParse->nMem += nReg;
zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx));
- if( !zAff ){
- pParse->db->mallocFailed = 1;
- }
+ assert( zAff!=0 || pParse->db->mallocFailed );
if( nSkip ){
int iIdxCur = pLevel->iIdxCur;
@@ -746,6 +744,54 @@ static void codeCursorHint(
# define codeCursorHint(A,B,C) /* No-op */
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
+/*
+** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains
+** a rowid value just read from cursor iIdxCur, open on index pIdx. This
+** function generates code to do a deferred seek of cursor iCur to the
+** rowid stored in register iRowid.
+**
+** Normally, this is just:
+**
+** OP_Seek $iCur $iRowid
+**
+** However, if the scan currently being coded is a branch of an OR-loop and
+** the statement currently being coded is a SELECT, then P3 of the OP_Seek
+** is set to iIdxCur and P4 is set to point to an array of integers
+** containing one entry for each column of the table cursor iCur is open
+** on. For each table column, if the column is the i'th column of the
+** index, then the corresponding array entry is set to (i+1). If the column
+** does not appear in the index at all, the array entry is set to 0.
+*/
+static void codeDeferredSeek(
+ WhereInfo *pWInfo, /* Where clause context */
+ Index *pIdx, /* Index scan is using */
+ int iCur, /* Cursor for IPK b-tree */
+ int iIdxCur /* Index cursor */
+){
+ Parse *pParse = pWInfo->pParse; /* Parse context */
+ Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */
+
+ assert( iIdxCur>0 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
+
+ sqlite3VdbeAddOp3(v, OP_Seek, iIdxCur, 0, iCur);
+ if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)
+ && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
+ ){
+ int i;
+ Table *pTab = pIdx->pTable;
+ int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1));
+ if( ai ){
+ ai[0] = pTab->nCol;
+ for(i=0; inColumn-1; i++){
+ assert( pIdx->aiColumn[i]nCol );
+ if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1;
+ }
+ sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
+ }
+ }
+}
+
/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
@@ -1225,14 +1271,14 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( omitTable ){
/* pIdx is a covering index. No need to access the main table. */
}else if( HasRowid(pIdx->pTable) ){
- iRowidReg = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
if( pWInfo->eOnePass!=ONEPASS_OFF ){
+ iRowidReg = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
VdbeCoverage(v);
}else{
- sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
+ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur);
}
}else if( iCur!=iIdxCur ){
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
@@ -1401,7 +1447,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
Expr *pExpr = pWC->a[iTerm].pExpr;
if( &pWC->a[iTerm] == pTerm ) continue;
if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
- if( (pWC->a[iTerm].wtFlags & TERM_VIRTUAL)!=0 ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
+ testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
+ if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
pExpr = sqlite3ExprDup(db, pExpr, 0);
diff --git a/src/whereexpr.c b/src/whereexpr.c
index e0b8bac1b7..c84d2f230a 100644
--- a/src/whereexpr.c
+++ b/src/whereexpr.c
@@ -64,7 +64,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
if( pWC->nTerm>=pWC->nSlot ){
WhereTerm *pOld = pWC->a;
sqlite3 *db = pWC->pWInfo->pParse->db;
- pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
+ pWC->a = sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
if( pWC->a==0 ){
if( wtFlags & TERM_DYNAMIC ){
sqlite3ExprDelete(db, p);
@@ -549,7 +549,7 @@ static void exprAnalyzeOrTerm(
WhereAndInfo *pAndInfo;
assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
chngToIN = 0;
- pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
+ pAndInfo = sqlite3DbMallocRawNN(db, sizeof(*pAndInfo));
if( pAndInfo ){
WhereClause *pAndWC;
WhereTerm *pAndTerm;
@@ -563,7 +563,6 @@ static void exprAnalyzeOrTerm(
sqlite3WhereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
sqlite3WhereExprAnalyze(pSrc, pAndWC);
pAndWC->pOuter = pWC;
- testcase( db->mallocFailed );
if( !db->mallocFailed ){
for(j=0, pAndTerm=pAndWC->a; jnTerm; j++, pAndTerm++){
assert( pAndTerm->pExpr );
diff --git a/test/analyze9.test b/test/analyze9.test
index 1ebd69c8d1..e24f3dd49d 100644
--- a/test/analyze9.test
+++ b/test/analyze9.test
@@ -1244,6 +1244,3 @@ do_eqp_test 26.2.2 {
finish_test
-
-
-
diff --git a/test/analyzeB.test b/test/analyzeB.test
index 2a78c18b3d..e91e7d585e 100644
--- a/test/analyzeB.test
+++ b/test/analyzeB.test
@@ -680,4 +680,3 @@ for {set i 0} {$i<16} {incr i} {
}
finish_test
-
diff --git a/test/analyzeD.test b/test/analyzeD.test
index 4d46be6c64..7ef1094f82 100644
--- a/test/analyzeD.test
+++ b/test/analyzeD.test
@@ -114,4 +114,3 @@ do_eqp_test 1.8 {
}
finish_test
-
diff --git a/test/cacheflush.test b/test/cacheflush.test
index 007ee8a065..50461893ef 100644
--- a/test/cacheflush.test
+++ b/test/cacheflush.test
@@ -321,4 +321,3 @@ do_test 4.5 {
test_restore_config_pagecache
finish_test
-
diff --git a/test/cffault.test b/test/cffault.test
index a89477b617..79cefd24c4 100644
--- a/test/cffault.test
+++ b/test/cffault.test
@@ -156,4 +156,3 @@ do_faultsim_test 2.4 -prep {
}
finish_test
-
diff --git a/test/corruptH.test b/test/corruptH.test
index 5c83cb3b90..0e1a1d4429 100644
--- a/test/corruptH.test
+++ b/test/corruptH.test
@@ -174,4 +174,3 @@ do_test 3.3 {
} {1 {database disk image is malformed}}
finish_test
-
diff --git a/test/corruptI.test b/test/corruptI.test
index 64323d9608..9f46efb744 100644
--- a/test/corruptI.test
+++ b/test/corruptI.test
@@ -206,7 +206,6 @@ do_test 6.1 {
db close
hexio_write test.db 616 8FFFFFFF7F02
sqlite3 db test.db
- breakpoint
execsql { DELETE FROM t1 WHERE rowid=2 }
} {}
diff --git a/test/cost.test b/test/cost.test
index 92fff9c888..9c10d821d9 100644
--- a/test/cost.test
+++ b/test/cost.test
@@ -287,6 +287,3 @@ ifcapable stat4 {
}
finish_test
-
-
-
diff --git a/test/e_blobbytes.test b/test/e_blobbytes.test
index a6283ab852..d38f56bf34 100644
--- a/test/e_blobbytes.test
+++ b/test/e_blobbytes.test
@@ -72,5 +72,3 @@ do_test 2.1 {
sqlite3_blob_close $B
finish_test
-
-
diff --git a/test/e_blobclose.test b/test/e_blobclose.test
index a5d432d3b5..2683197854 100644
--- a/test/e_blobclose.test
+++ b/test/e_blobclose.test
@@ -168,4 +168,3 @@ do_execsql_test 3.5 {
do_test 4.0 { sqlite3_blob_close 0 } {}
finish_test
-
diff --git a/test/e_blobopen.test b/test/e_blobopen.test
index 01f62cdd78..a6168042e9 100644
--- a/test/e_blobopen.test
+++ b/test/e_blobopen.test
@@ -546,4 +546,3 @@ do_test 13.5 {
finish_test
-
diff --git a/test/e_blobwrite.test b/test/e_blobwrite.test
index a0d33336de..7b2249c243 100644
--- a/test/e_blobwrite.test
+++ b/test/e_blobwrite.test
@@ -201,4 +201,3 @@ do_execsql_test 3.2.4 {
finish_test
-
diff --git a/test/e_walckpt.test b/test/e_walckpt.test
index e022f840cf..3b1f3b015a 100644
--- a/test/e_walckpt.test
+++ b/test/e_walckpt.test
@@ -751,4 +751,3 @@ do_test 6.5 {
finish_test
-
diff --git a/test/fkey8.test b/test/fkey8.test
index 60bb8e4640..b4b6bb75e7 100644
--- a/test/fkey8.test
+++ b/test/fkey8.test
@@ -103,4 +103,3 @@ foreach {tn use_stmt sql schema} {
finish_test
-
diff --git a/test/fordelete.test b/test/fordelete.test
index 1e860e8867..9a382d97f5 100644
--- a/test/fordelete.test
+++ b/test/fordelete.test
@@ -30,17 +30,49 @@ proc analyze_delete_program {sql} {
} {
set T($rootpage) $name
}
-
- # Calculate the results.
- set res [list]
+
+ # For each OpenWrite instruction generated for the proposed DELETE
+ # statement, add the following array entries:
+ #
+ # $M() ->