Compare commits

..

2 Commits

Author SHA1 Message Date
drh 843418ae70 In the CLI on the ".mode" command, replace the recently added --no-limits option with
"--limits L,C".  And add --sw as an alias for --screenwidth.

FossilOrigin-Name: caefe4c746599b22aec446f3aeb296e53edaf8c54f8ae803381e8c21d5e29ab4
2025-11-24 12:27:18 +00:00
dan 0ec8c56567 Remove the the SQLITE_SYNC_DATAONLY flag from the core on the grounds that it is not currently used by either built-in VFS and is therefore untested.
FossilOrigin-Name: ff2065b533769cbf5278d13d09ffa93a7a0f841b1b59b6be0e5f27e3d1c341d0
2025-11-24 11:22:56 +00:00
306 changed files with 12825 additions and 39158 deletions
+3 -11
View File
@@ -131,15 +131,9 @@ libsqlite3.DLL.install-rules = @SQLITE_DLL_INSTALL_RULES@
CFLAGS.fuzzcheck-asan.fsanitize = @CFLAGS_ASAN_FSANITIZE@
#
# TARGET_DEBUG is intended to either be empty or be set to -g
# -DSQLITE_DEBUG=1, plus any other flags relevant for debug-only
# builds. These get added near the front of $(T.cc) so that they can
# be overridden by CFLAGS passed directly to make. Historically (prior
# to 2026-03-30) these were at/near the end of the flags but that made
# it impossible to pass custom -O* flags to specific binaries without
# reconfiguring.
# Intended to either be empty or be set to -g -DSQLITE_DEBUG=1.
#
T.cc += @TARGET_DEBUG@
T.cc.TARGET_DEBUG = @TARGET_DEBUG@
#
# $(JIMSH) and $(CFLAGS.jimsh) are documented in main.mk. $(JIMSH)
@@ -170,9 +164,7 @@ OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ $(OPTIONS)
PACKAGE_VERSION = @PACKAGE_VERSION@
#
# Filename extensions for binaries and libraries. B.* are for the
# being-built-on platform and T.* are for the build's target platform.
# i.e. B.* is for build-time tooling and T.* is for deliverables.
# Filename extensions for binaries and libraries
#
B.exe = @BUILD_EXEEXT@
T.exe = @TARGET_EXEEXT@
+151 -221
View File
@@ -11,8 +11,6 @@
TOP = .
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Set this non-0 to create and use the SQLite amalgamation file.
#
!IFNDEF USE_AMALGAMATION
@@ -103,6 +101,13 @@ NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
# Set this non-0 to use the library paths and other options necessary for
# Windows Phone 8.1.
#
!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.
#
@@ -111,8 +116,14 @@ SPLIT_AMALGAMATION = 0
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
# Set this non-0 to have this makefile assume the Tcl shell executable
# (tclsh*.exe) is available in the PATH. By default, this is disabled
# for compatibility with older build environments. This setting only
# applies if TCLSH_CMD is not set manually.
#
!IFNDEF USE_TCLSH_IN_PATH
USE_TCLSH_IN_PATH = 0
!ENDIF
# Set this non-0 to use zlib, possibly compiling it from source code.
#
@@ -175,6 +186,14 @@ USE_NATIVE_LIBPATHS = 0
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 UWP environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
@@ -190,8 +209,6 @@ FOR_WIN10 = 0
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Set this non-0 to skip attempting to look for and/or link with the Tcl
# runtime library.
#
@@ -248,8 +265,6 @@ DEBUG = 0
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# By default, use --linemacros=1 argument to the mksqlite3c.tcl tool, which
# is used to build the amalgamation. This can be turned off to ease debug
# of the amalgamation away from the source tree.
@@ -345,8 +360,6 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# These are the names of the customized Tcl header files used by various parts
# of this makefile when the stdcall calling convention is in use. It is not
# used for any other purpose.
@@ -628,24 +641,16 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
!IF "$(PLATFORM)"=="x86"
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
# <</mark>>
!ELSE
!IFNDEF PLATFORM
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
# <</mark>>
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
@@ -772,6 +777,18 @@ SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
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
@@ -784,31 +801,25 @@ BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
# USE_CRT_DLL option is set to force dynamically linking to the
# MSVC runtime library.
#
!IF $(USE_CRT_DLL)!=0
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
!IF $(DEBUG)>1
TCC = $(TCC) -MDd
BCC = $(BCC) -MDd
ZLIBCFLAGS = -nologo -MDd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MD
BCC = $(BCC) -MD
ZLIBCFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi
!ENDIF
!ELSE
!IF $(DEBUG)>1
TCC = $(TCC) -MTd
BCC = $(BCC) -MTd
ZLIBCFLAGS = -nologo -MTd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MT
BCC = $(BCC) -MT
ZLIBCFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi
!ENDIF
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# 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.
@@ -859,6 +870,16 @@ MKSQLITE3H_ARGS =
!ENDIF
# <</mark>>
# 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
@@ -924,8 +945,6 @@ TCC = $(TCC) /fsanitize=address
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# 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
@@ -1165,8 +1184,6 @@ BCC = $(BCC) -Zi
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# If zlib support is enabled, add the compiler options for it.
#
!IF $(USE_ZLIB)!=0
@@ -1219,6 +1236,56 @@ 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 UWP or the Windows 10 platform, some extra linker
# options are also required.
#
@@ -1242,14 +1309,12 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
# If either debugging or symbols are enabled, enable PDBs.
#
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
LDFLAGS = /NODEFAULTLIB:msvcrt /DEBUG $(LDOPTS)
LDFLAGS = /DEBUG $(LDOPTS)
!ELSE
LDFLAGS = /NODEFAULTLIB:msvcrt $(LDOPTS)
LDFLAGS = $(LDOPTS)
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Start with the Tcl related linker options.
#
!IF $(NO_TCL)==0
@@ -1276,8 +1341,6 @@ LTLIBS = $(LTLIBS) $(LIBICU)
###############################################################################
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Object files for the SQLite library (non-amalgamation).
#
LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
@@ -1719,7 +1782,6 @@ FUZZERSHELL_COMPILE_OPTS =
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -I$(TOP)\test -I$(TOP)\ext\recover
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_MEMSYS5
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_OSS_FUZZ
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_DECIMAL_MAX_DIGIT=1000
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_CARRAY
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBPAGE_VTAB
@@ -1766,17 +1828,7 @@ FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\base64.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\base85.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\completion.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\decimal.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\ieee754.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\regexp.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\series.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\shathree.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\sha1.c
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\stmtrand.c
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
@@ -1877,8 +1929,8 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
# <<mark>>
sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS) $(TOP)\tool\winmain.c
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(TOP)\tool\winmain.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
@@ -2377,7 +2429,7 @@ shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl $(JIM_TCLSH)
$(JIM_TCLSH) $(TOP)\tool\mkshellc.tcl shell.c
zlib:
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) CFLAGS="$(ZLIBCFLAGS)" && popd
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd
# Rules to build the extension objects.
#
@@ -2551,19 +2603,43 @@ coretestprogs: testfixture.exe sqlite3.exe
testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe
fulltest: alltest fuzztest
alltest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\all.test $(TESTOPTS)
soaktest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\all.test -soak=1 $(TESTOPTS)
fulltestonly: $(TESTPROGS) fuzztest
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\full.test
queryplantest: testfixture.exe shell
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\permutations.test queryplanner $(TESTOPTS)
fuzztest: fuzzcheck.exe
.\fuzzcheck.exe $(FUZZDATA)
# Legacy testing target for third-party integrators. The SQLite
# developers seldom use this target themselves. Instead
# they use "nmake /f Makefile.msc devtest" which runs tests on
# a standard set of options
#
# Legacy testing targets, no longer used by the developers and
# now aliased to one of the commonly used testing targets.
test: $(TESTPROGS) sourcetest fuzztest tcltest
# Minimal testing that runs in less than 3 minutes (on a fast machine)
#
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.
#
quicktest: devtest
test: devtest
fulltest: releasetest
alltest: releasetest
soaktest: releasetest
fulltestonly: releasetest
# The veryquick.test TCL tests.
#
@@ -2588,22 +2664,6 @@ devtest: srctree-check sourcetest
mdevtest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
# Rerun test cases that failed on the previous testrunner invocation
#
retest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl retest
# Show all errors from the most reason testrunner invocation
#
errors:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl errors
# Show the status of the current testrunner invocation,
# updated every couple of seconds
#
status:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl status -d 2
# Validate that various generated files in the source tree
# are up-to-date.
#
@@ -2627,14 +2687,8 @@ smoketest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
# Measure the performance of floating-point conversions.
#
fp-speed-test: fp-speed-1.exe fp-speed-2.exe
fp-speed-1 1000000
fp-speed-2 1000000
shelltest:
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release shell
shelltest: $(TESTPROGS)
.\testfixture.exe $(TOP)\test\permutations.test shell
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) tclsqlite-ex.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl -DINCLUDE_SQLITE3_C $(TOP)\tool\sqlite3_analyzer.c.in > $@
@@ -2653,6 +2707,23 @@ sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
CHECKER_DEPS =\
$(TOP)\tool\mkccode.tcl \
sqlite3.c \
tclsqlite-ex.c \
$(TOP)\ext\repair\sqlite3_checker.tcl \
$(TOP)\ext\repair\checkindex.c \
$(TOP)\ext\repair\checkfreelist.c \
$(TOP)\ext\misc\btreeinfo.c \
$(TOP)\ext\repair\sqlite3_checker.c.in
sqlite3_checker.c: $(CHECKER_DEPS)
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\ext\repair\sqlite3_checker.c.in > $@
sqlite3_checker.exe: sqlite3_checker.c $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_checker.c \
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
$(LTLINK) $(NO_WARN) -DDBDUMP_STANDALONE $(TOP)\ext\misc\dbdump.c $(SQLITE3C) \
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS)
@@ -2685,9 +2756,6 @@ showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(SQLITE3H)
showshm.exe: $(TOP)\tool\showshm.c
$(LTLINK) $(NO_WARN) $(TOP)\tool\showshm.c /link $(LDFLAGS) $(LTLINKOPTS)
showtmlog.exe: $(TOP)\tool\showtmlog.c
$(LTLINK) $(NO_WARN) $(TOP)\tool\showtmlog.c /link $(LDFLAGS) $(LTLINKOPTS)
index_usage.exe: $(TOP)\tool\index_usage.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\index_usage.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
@@ -2725,14 +2793,6 @@ speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
fp-speed-1.exe: $(TOP)\test\fp-speed-1.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\test\fp-speed-1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
fp-speed-2.exe: $(TOP)\test\fp-speed-2.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(ST_COMPILE_OPTS) -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\test\fp-speed-2.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
kvtest.exe: $(TOP)\test\kvtest.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(KV_COMPILE_OPTS) \
$(TOP)\test\kvtest.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
@@ -2764,135 +2824,6 @@ tcl-env:
@echo JIM_TCLSH = $(JIM_TCLSH)
@echo VISUALSTUDIOVERSION = $(VISUALSTUDIOVERSION)
env:
@echo ALL_TCL_TARGETS = $(ALL_TCL_TARGETS)
@echo API_ARMOR = $(API_ARMOR)
@echo ASAN = $(ASAN)
@echo BCC = $(BCC)
@echo BUILD_ZLIB = $(BUILD_ZLIB)
@echo CC = $(CC)
@echo CCOPTS = $(CCOPTS)
@echo CHECKER_DEPS = $(CHECKER_DEPS)
@echo CORE_CCONV_OPTS = $(CORE_CCONV_OPTS)
@echo CORE_COMPILE_OPTS = $(CORE_COMPILE_OPTS)
@echo CORE_LINK_DEP = $(CORE_LINK_DEP)
@echo CORE_LINK_OPTS = $(CORE_LINK_OPTS)
@echo CRTLIBPATH = $(CRTLIBPATH)
@echo CSC = $(CSC)
@echo DBFUZZ_COMPILE_OPTS = $(DBFUZZ_COMPILE_OPTS)
@echo DEBUG = $(DEBUG)
@echo DYNAMIC_SHELL = $(DYNAMIC_SHELL)
@echo EXT_FEATURE_FLAGS = $(EXT_FEATURE_FLAGS)
@echo EXTHDR = $(EXTHDR)
@echo EXTRA_SRC = $(EXTRA_SRC)
@echo FOR_UWP = $(FOR_UWP)
@echo FUZZCHECK_OPTS = $(FUZZCHECK_OPTS)
@echo FUZZCHECK_SRC = $(FUZZCHECK_SRC)
@echo FUZZDATA = $(FUZZDATA)
@echo FUZZERSHELL_COMPILE_OPTS = $(FUZZERSHELL_COMPILE_OPTS)
@echo HDR = $(HDR)
@echo ICUDIR = $(ICUDIR)
@echo ICUINCDIR = $(ICUINCDIR)
@echo ICULIBDIR = $(ICULIBDIR)
@echo JIM_TCLSH = $(JIM_TCLSH)
@echo KV_COMPILE_OPTS = $(KV_COMPILE_OPTS)
@echo LDFLAGS = $(LDFLAGS)
@echo LD = $(LD)
@echo LIBICU = $(LIBICU)
@echo LIBOBJ = $(LIBOBJ)
@echo LIBREADLINE = $(LIBREADLINE)
@echo LIBRESOBJS = $(LIBRESOBJS)
@echo LIBTCLPATH = $(LIBTCLPATH)
@echo LIBTCLSTUB = $(LIBTCLSTUB)
@echo LIBTCL = $(LIBTCL)
@echo LTCOMPILE = $(LTCOMPILE)
@echo LTLIB = $(LTLIB)
@echo LTLIBOPTS = $(LTLIBOPTS)
@echo LTLIBPATHS = $(LTLIBPATHS)
@echo LTLIBS = $(LTLIBS)
@echo LTLINK = $(LTLINK)
@echo LTLINKOPTS = $(LTLINKOPTS)
@echo LTRCOMPILE = $(LTRCOMPILE)
@echo MEMDEBUG = $(MEMDEBUG)
@echo MINIMAL_AMALGAMATION = $(MINIMAL_AMALGAMATION)
@echo MPTESTER_COMPILE_OPTS = $(MPTESTER_COMPILE_OPTS)
@echo NCC = $(NCC)
@echo NCRTLIBPATH = $(NCRTLIBPATH)
@echo NLTLIBPATHS = $(NLTLIBPATHS)
@echo NO_LINEMACROS = $(NO_LINEMACROS)
@echo NO_TCL = $(NO_TCL)
@echo NO_WARN = $(NO_WARN)
@echo NSDKLIBPATH = $(NSDKLIBPATH)
@echo NUCRTLIBPATH = $(NUCRTLIBPATH)
@echo OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS)
@echo OPTIMIZATIONS = $(OPTIMIZATIONS)
@echo OSSSHELL_SRC = $(OSSSHELL_SRC)
@echo OSTRACE = $(OSTRACE)
@echo RBU = $(RBU)
@echo RCC = $(RCC)
@echo RC = $(RC)
@echo READLINE_FLAGS = $(READLINE_FLAGS)
@echo REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS)
@echo RSYNC_OPT = $(RSYNC_OPT)
@echo RSYNC_SRC = $(RSYNC_SRC)
@echo SESSION = $(SESSION)
@echo SETLK_TIMEOUT = $(SETLK_TIMEOUT)
@echo SHELL_CCONV_OPTS = $(SHELL_CCONV_OPTS)
@echo SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS)
@echo SHELL_CORE_DEP = $(SHELL_CORE_DEP)
@echo SHELL_CORE_LIB = $(SHELL_CORE_LIB)
@echo SHELL_CORE_SRC = $(SHELL_CORE_SRC)
@echo SHELL_DEP = $(SHELL_DEP)
@echo SHELL_LINK_OPTS = $(SHELL_LINK_OPTS)
@echo SPLIT_AMALGAMATION = $(SPLIT_AMALGAMATION)
@echo SQLITETCLDECLSH = $(SQLITETCLDECLSH)
@echo SQLITE_TCL_DEP = $(SQLITE_TCL_DEP)
@echo SQLITETCLH = $(SQLITETCLH)
@echo SRC = $(SRC)
@echo STATICALLY_LINK_TCL = $(STATICALLY_LINK_TCL)
@echo ST_COMPILE_OPTS = $(ST_COMPILE_OPTS)
@echo STORELIBPATH = $(STORELIBPATH)
@echo SYMBOLS = $(SYMBOLS)
@echo TCC = $(TCC)
@echo TCLDIR = $(TCLDIR)
@echo TCLINCDIR = $(TCLINCDIR)
@echo TCLLIBDIR = $(TCLLIBDIR)
@echo TCLLIBPATHS = $(TCLLIBPATHS)
@echo TCLLIBS = $(TCLLIBS)
@echo TCLSH_CMD = $(TCLSH_CMD)
@echo TCLSQLITEEX = $(TCLSQLITEEX)
@echo TCLSUFFIX = $(TCLSUFFIX)
@echo TCLVERSION = $(TCLVERSION)
@echo TEST_CCONV_OPTS = $(TEST_CCONV_OPTS)
@echo TESTEXT = $(TESTEXT)
@echo TESTFIXTURE_DEP = $(TESTFIXTURE_DEP)
@echo TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS)
@echo TESTFIXTURE_SRC = $(TESTFIXTURE_SRC)
@echo TESTOPTS = $(TESTOPTS)
@echo TESTPROGS = $(TESTPROGS)
@echo TESTSRC = $(TESTSRC)
@echo TLIBS = $(TLIBS)
@echo TOP = $(TOP)
@echo UCRTLIBPATH = $(UCRTLIBPATH)
@echo USE_AMALGAMATION = $(USE_AMALGAMATION)
@echo USE_CRT_DLL = $(USE_CRT_DLL)
@echo USE_FATAL_WARN = $(USE_FATAL_WARN)
@echo USE_FULLWARN = $(USE_FULLWARN)
@echo USE_ICU = $(USE_ICU)
@echo USE_LISTINGS = $(USE_LISTINGS)
@echo USE_NATIVE_LIBPATHS = $(USE_NATIVE_LIBPATHS)
@echo USE_RC = $(USE_RC)
@echo USE_RUNTIME_CHECKS = $(USE_RUNTIME_CHECKS)
@echo USE_SEH = $(USE_SEH)
@echo USE_STDCALL = $(USE_STDCALL)
@echo USE_ZLIB = $(USE_ZLIB)
@echo XCOMPILE = $(XCOMPILE)
@echo ZLIBCFLAGS = $(ZLIBCFLAGS)
@echo ZLIBDIR = $(ZLIBDIR)
@echo ZLIBINCDIR = $(ZLIBINCDIR)
@echo ZLIBLIBDIR = $(ZLIBLIBDIR)
@echo ZLIBLIB = $(ZLIBLIB)
moreclean: clean
del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL
# <</mark>>
@@ -2922,7 +2853,6 @@ clean:
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL
del /Q changeset.exe 2>NUL
del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL
del /Q fp-speed-1.exe fp-speed-2.exe 2>NUL
del /Q mptester.exe wordcount.exe rbu.exe srcck1.exe 2>NUL
del /Q sqlite3.c sqlite3-*.c sqlite3.h 2>NUL
del /Q sqlite3rc.h 2>NUL
+48 -49
View File
@@ -1,9 +1,8 @@
<h1 align="center">SQLite Source Repository</h1>
This repository contains the complete source code for the
[SQLite database engine](https://sqlite.org/) going back
to 2000-05-29. The tree includes many tests and some
documentation, though additional tests and most documentation
[SQLite database engine](https://sqlite.org/), including
many tests. Additional tests and most documentation
are managed separately.
See the [on-line documentation](https://sqlite.org/) for more information
@@ -97,21 +96,19 @@ the build products. It is recommended, but not required, that the
build directory be separate from the source directory. Cd into the
build directory and then from the build directory run the configure
script found at the root of the source tree. Then run "make".
See the [compile-for-unix.md](doc/compile-for-unix.md) document for
more detail.
For example:
apt install gcc make tcl-dev ;# Install the necessary build tools
apt install gcc make tcl-dev ;# Make sure you have all the necessary build tools
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
mkdir bld ;# Build happens in a sibling directory
mkdir bld ;# Build will occur in a sibling directory
cd bld ;# Change to the build directory
../sqlite/configure ;# Run the configure script
make sqlite3 ;# The "sqlite3" command-line tool
make sqlite3.c ;# The "amalgamation" source file
make sqldiff ;# The "sqldiff" command-line tool
#### Targets below require tcl-dev ####
make tclextension-install ;# Install the SQLite TCL extension
make sqlite3 ;# Builds the "sqlite3" command-line tool
make sqlite3.c ;# Build the "amalgamation" source file
make sqldiff ;# Builds the "sqldiff" command-line tool
# Makefile targets below this point require tcl-dev
make tclextension-install ;# Build and install the SQLite TCL extension
make devtest ;# Run development tests
make releasetest ;# Run full release tests
make sqlite3_analyzer ;# Builds the "sqlite3_analyzer" tool
@@ -119,15 +116,14 @@ For example:
See the makefile for additional targets. For debugging builds, the
core developers typically run "configure" with options like this:
../sqlite/configure --all --debug CFLAGS='-O0 -g'
../sqlite/configure --enable-all --enable-debug CFLAGS='-O0 -g'
For release builds, the core developers usually do:
../sqlite/configure --all
../sqlite/configure --enable-all
Core deliverables (sqlite3.c, sqlite3) can be built without a TCL, but
many makefile targets require a "tclsh" TCL interpreter version 8.6
or later. The "tclextension-install" target and the test targets that follow
Almost all makefile targets require a "tclsh" TCL interpreter version 8.6 or
later. The "tclextension-install" target and the test targets that follow
all require TCL development libraries too. ("apt install tcl-dev"). It is
helpful, but is not required, to install the SQLite TCL extension (the
"tclextension-install" target) prior to running tests. The "releasetest"
@@ -137,20 +133,20 @@ On "make" command-lines, one can add "OPTIONS=..." to specify additional
compile-time options over and above those set by ./configure. For example,
to compile with the SQLITE_OMIT_DEPRECATED compile-time option, one could say:
./configure --all
./configure --enable-all
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
The configure script uses [autosetup](https://msteveb.github.io/autosetup/).
If the configure script does not work out for you, there is a generic
makefile named "Makefile.linux-gcc" in the top directory of the source tree
that you can copy and edit to suit your needs. Comments on the generic
makefile show what changes are needed.
The configure script uses autoconf 2.61 and libtool. If the configure
script does not work out for you, there is a generic makefile named
"Makefile.linux-gcc" in the top directory of the source tree that you
can copy and edit to suit your needs. Comments on the generic makefile
show what changes are needed.
## Compiling for Windows Using MSVC
On Windows, everything can be compiled with MSVC.
You will also need a working installation of TCL if you want to run tests,
though TCL is not required if you just want to build SQLite itself.
You will also need a working installation of TCL if you want to run tests.
TCL is not required if you just want to build SQLite itself.
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
additional information about how to install MSVC and TCL and configure your
build environment.
@@ -160,25 +156,24 @@ TCL library, using a command like this:
set TCLDIR=c:\Tcl
SQLite itself does not contain any TCL code, but it does use TCL to run
tests. You may need to install TCL development libraries in order to
successfully complete some makefile targets. It is helpful, but is not
required, to install the SQLite TCL extension (the "tclextension-install"
target) prior to running tests.
SQLite uses "tclsh.exe" as part of the build process, and so that
program will need to be somewhere on your %PATH%. SQLite itself
does not contain any TCL code, but it does use TCL to run tests.
You may need to install TCL development
libraries in order to successfully complete some makefile targets.
It is helpful, but is not required, to install the SQLite TCL extension
(the "tclextension-install" target) prior to running tests.
The source tree contains a "make.bat" file that allows the same "make"
commands of Unix to work on Windows. In the following, you can substitute
"nmake /f Makefile.msc" in place of "make", if you prefer to avoid this BAT
file:
Build using Makefile.msc. Example:
make sqlite3.exe
make sqlite3.c
make sqldiff.exe
#### Targets below require TCL development libraries ####
make tclextension-install
make devtest
make releasetest
make sqlite3_analyzer.exe
nmake /f Makefile.msc sqlite3.exe
nmake /f Makefile.msc sqlite3.c
nmake /f Makefile.msc sqldiff.exe
# Makefile targets below this point require TCL development libraries
nmake /f Makefile.msc tclextension-install
nmake /f Makefile.msc devtest
nmake /f Makefile.msc releasetest
nmake /f Makefile.msc sqlite3_analyzer.exe
There are many other makefile targets. See comments in Makefile.msc for
details.
@@ -186,7 +181,7 @@ details.
As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
command-line to enable new compile-time options. For example:
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
## Source Tree Map
@@ -199,7 +194,8 @@ command-line to enable new compile-time options. For example:
* **test/** - This directory and its subdirectories contains code used
for testing. Files that end in "`.test`" are TCL scripts that run
tests using an augmented TCL interpreter named "testfixture". Use
a command like "`make testfixture`" to build that
a command like "`make testfixture`" (unix) or
"`nmake /f Makefile.msc testfixture.exe`" (windows) to build that
augmented TCL interpreter, then run individual tests using commands like
"`testfixture test/main.test`". This test/ subdirectory also contains
additional C code modules and scripts for other kinds of testing.
@@ -381,11 +377,10 @@ implementation. It will not be the easiest library in the world to hack.
(and some other test programs too) is built and run when you type
"make test".
* **VERSION**, **manifest**, **manifest.tags**, and **manifest.uuid** -
These files define the current SQLite version number. The "VERSION" file
is human generated, but the "manifest", "manifest.tags", and
"manifest.uuid" files are automatically generated by the
[Fossil version control system](https://fossil-scm.org/).
* **VERSION**, **manifest**, and **manifest.uuid** - These files define
the current SQLite version number. The "VERSION" file is human generated,
but the "manifest" and "manifest.uuid" files are automatically generated
by the [Fossil version control system](https://fossil-scm.org/).
There are many other source files. Each has a succinct header comment that
describes its purpose and role within the larger system.
@@ -411,6 +406,10 @@ makefile:
> make verify-source
Or on windows:
> nmake /f Makefile.msc verify-source
Using the makefile to verify source integrity is good for detecting
accidental changes to the source tree, but malicious changes could be
hidden by also modifying the makefiles.
+1 -1
View File
@@ -1 +1 @@
3.53.0
3.52.0
+1 -1
View File
@@ -282,7 +282,7 @@ DIST_FILES := \
README.txt VERSION \
auto.def autosetup configure tea \
sqlite3.h sqlite3.c shell.c sqlite3ext.h \
Makefile.in Makefile.msc Makefile.fallback make.bat \
Makefile.in Makefile.msc Makefile.fallback \
sqlite3.rc sqlite3rc.h Replace.cs \
sqlite3.pc.in sqlite3.1
+90 -11
View File
@@ -101,6 +101,13 @@ NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
# Set this non-0 to use the library paths and other options necessary for
# Windows Phone 8.1.
#
!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.
#
@@ -149,6 +156,14 @@ USE_NATIVE_LIBPATHS = 0
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 UWP environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
@@ -548,14 +563,10 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
!IF "$(PLATFORM)"=="x86"
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
!ELSE
!IFNDEF PLATFORM
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
@@ -656,6 +667,18 @@ SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
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
@@ -668,29 +691,35 @@ BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
# USE_CRT_DLL option is set to force dynamically linking to the
# MSVC runtime library.
#
!IF $(USE_CRT_DLL)!=0
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
!IF $(DEBUG)>1
TCC = $(TCC) -MDd
BCC = $(BCC) -MDd
ZLIBCFLAGS = -nologo -MDd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MD
BCC = $(BCC) -MD
ZLIBCFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi
!ENDIF
!ELSE
!IF $(DEBUG)>1
TCC = $(TCC) -MTd
BCC = $(BCC) -MTd
ZLIBCFLAGS = -nologo -MTd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MT
BCC = $(BCC) -MT
ZLIBCFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi
!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
@@ -880,6 +909,56 @@ 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 UWP or the Windows 10 platform, some extra linker
# options are also required.
#
@@ -903,9 +982,9 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
# If either debugging or symbols are enabled, enable PDBs.
#
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
LDFLAGS = /NODEFAULTLIB:msvcrt /DEBUG $(LDOPTS)
LDFLAGS = /DEBUG $(LDOPTS)
!ELSE
LDFLAGS = /NODEFAULTLIB:msvcrt $(LDOPTS)
LDFLAGS = $(LDOPTS)
!ENDIF
+1 -11
View File
@@ -375,11 +375,6 @@ configure process, and check it in.
Patching Autosetup for Project-local Changes
------------------------------------------------------------------------
The autosetup files require the following patches after updating
from their upstream sources:
### `--debug` flag
Autosetup reserves the flag name **`--debug`** for its own purposes,
and its own special handling of `--enable-...` flags makes `--debug`
an alias for `--enable-debug`. As this project has a long history of
@@ -393,11 +388,6 @@ If autosetup is upgraded and this patch is _not_ applied the invoking
`./configure` will fail loudly because of the declaration of the
`debug` flag in `auto.def` - duplicated flags are not permitted.
### Fail on `malloc()` error
See [check-in 72c8a5b94cdf5d](/info/72c8a5b94cdf5d).
<a name="branch-customization"></a>
Branch-specific Customization
========================================================================
@@ -461,4 +451,4 @@ all other significant processing.
[sqlite-config.tcl]: /file/autosetup/sqlite-config.tcl
[Makefile.in]: /file/Makefile.in
[main.mk]: /file/main.mk
[JimTCL]: https://msteveb.github.io/jimtcl/
[JimTCL]: https://jim.tcl.tk
+4 -6
View File
@@ -7409,13 +7409,11 @@ void *JimDefaultAllocator(void *ptr, size_t size)
free(ptr);
return NULL;
}
else if (ptr) {
return realloc(ptr, size);
}
else {
void *p = realloc(ptr, size);
if( p==0 ){
fprintf(stderr,"Out of memory\n");
exit(1);
}
return p;
return malloc(size);
}
}
+2 -2
View File
@@ -1842,7 +1842,7 @@ proc proj-setup-autoreconfig {defName} {
}
#
# @prop-define-append defineName args...
# @prop-append-to defineName args...
#
# A proxy for Autosetup's [define-append]. Appends all non-empty $args
# to [define-append $defineName].
@@ -1873,7 +1873,7 @@ proc proj-define-append {defineName args} {
# but it is technically correct and still relevant on some
# environments.
#
# See: proj-define-append
# See: proj-append-to
#
proc proj-define-amend {args} {
set defName ""
+4 -27
View File
@@ -679,7 +679,6 @@ proc sqlite-check-common-system-deps {} {
define HAVE_ZLIB 1
define LDFLAGS_ZLIB -lz
sqlite-add-shell-opt -DSQLITE_HAVE_ZLIB=1
sqlite-add-feature-flag -DSQLITE_HAVE_ZLIB=1
} else {
define HAVE_ZLIB 0
define LDFLAGS_ZLIB ""
@@ -743,11 +742,12 @@ proc sqlite-setup-default-cflags {} {
# compiling binaries for the target system (CC a.k.a. $(T.cc)).
# Normally they're the same, but they will differ when
# cross-compiling.
#
# When cross-compiling we default to not using the -g flag, based on a
# /chat discussion prompted by
# https://sqlite.org/forum/forumpost/9a67df63eda9925c
set defaultCFlags {-O2}
if {!$::sqliteConfig(is-cross-compiling)} {
# When cross-compiling we default to not using the -g flag, based
# on a /chat discussion prompted by
# https://sqlite.org/forum/forumpost/9a67df63eda9925c
lappend defaultCFlags -g
}
define CFLAGS [proj-get-env CFLAGS $defaultCFlags]
@@ -1137,27 +1137,6 @@ proc sqlite-check-line-editing {} {
set editLibName "readline" ; # "readline" or "editline"
set editLibDef "HAVE_READLINE" ; # "HAVE_READLINE" or "HAVE_EDITLINE"
set dirLn [opt-val with-linenoise]
# If none of --with-linenoise, --enable-readline, or --enable-editline
# are provided, but there exists a directory "linenoise" at $HOME or
# a sibling of the build or source directory, then try to use that linenoise
# direcctory.
#
if {"" eq $dirLn
&& ![proj-opt-was-provided readline]
&& ![proj-opt-was-provided editline]
} {
set dirlist ../linenoise
catch {lappend dirlist [file-normalize $::autosetup(srcdir)/../linenoise]}
catch {lappend dirlist $::env(HOME)/linenoise}
foreach d $dirlist {
if {[file exists $d/linenoise.c] && [file exists $d/linenoise.h]} {
set dirLn $d
break
}
}
}
if {"" ne $dirLn} {
# Use linenoise from a copy of its sources (not a library)...
if {![file isdir $dirLn]} {
@@ -1193,8 +1172,6 @@ proc sqlite-check-line-editing {} {
if {$::sqliteConfig(use-jim-for-codegen) && 2 == $lnVal} {
define-append CFLAGS_JIMSH -DUSE_LINENOISE [get-define CFLAGS_READLINE]
user-notice "Adding linenoise support to jimsh."
} else {
msg-result "Using linenoise at [file-normalize $dirLn]"
}
return "linenoise ($flavor)"
} elseif {[opt-bool editline]} {
+7 -33
View File
@@ -22,10 +22,9 @@ guidance on building for Windows.
or <https://sqlite.org/tmp/tcl9.0.0.tar.gz>.
<li>Untar the source archive. CD into the "unix/" subfolder
of the source tree.
<li>Run: &ensp; `mkdir $HOME/local`
<li>Run: &ensp; `./configure --prefix=$HOME/local`<br>
SQLite deliverable builds add: &emsp; `--static CFLAGS=-Os`
<li>Run: &ensp; `make install`
<li>Run: `mkdir $HOME/local`
<li>Run: `./configure --prefix=$HOME/local`
<li>Run: `make install`
</ol>
<p>
As of 2024-10-25, TCL is not longer required for many
@@ -37,35 +36,12 @@ guidance on building for Windows.
4. Download the SQLite source tree and unpack it. CD into the
toplevel directory of the source tree.
5. *(Optional):* Download Antirez's "linenoise" command-line editing
library to provide command-line editing in the CLI. You can find
a suitable copy of the linenoise sources at
<https://sqlite.org/linenoise.tar.gz> or at various other locations
on the internet. If you put the linenoise source tree in
a directory named $HOME/linenoise or a directory "linenoise" which
is a sibling of the SQLite source tree, then the SQLite ./configure
script will automatically find and use that source code to provide
command-line editing in the CLI. If you would rather use the readline
or editline libraries or a precompiled linenoise library, there are
./configure options to accommodate that choice. The SQLite developers
typically use $HOME/linenoise since linenoise is small, has no
external dependencies, "just works", and the ./configure script
will pick it up and use it automatically. But you do what works
best for you.
<p>
You are not required to have any command-line editing support in
order to use SQLite. But command-line editing does make the
interactive experience more enjoyable.
6. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
5. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
You do not need to use --with-tclsh if the tclsh you want to use is the
first one on your PATH or if you are building without TCL.
Lots of other options to ./configure are available.
Run `./configure --help` for further guidance.
7. Run the "`Makefile`" makefile with an appropriate target.
6. Run the "`Makefile`" makefile with an appropriate target.
Examples:
<ul>
<li> `make sqlite3.c`
@@ -90,7 +66,5 @@ guidance on building for Windows.
of SQLite.
8. For a debugging build of the CLI, use `./configure --dev`. A debugging
build contains lots of extra debugging code, so it is slow and a lot
bigger. You probably do not want to deploy a debugging build. But if
you are working on the code, a debugging build works much better.
7. For a debugging build of the CLI, where the ".treetrace" and ".wheretrace"
commands work, add the the --with-debug argument to configure.
-36
View File
@@ -180,39 +180,3 @@ statically linked so that it does not depend on separate DLL:
6. After your executable is built, you can verify that it does not
depend on the TCL DLL by running:
<blockquote><pre>dumpbin /dependents sqlite3_analyzer.exe</pre></blockquote>
## Linking Against ZLIB
Some feature (such as zip-file support in the CLI) require the ZLIB
compression library. That library is more or less universally available
on unix platforms, but is seldom provided on Windows. You will probably
need to provide it yourself. Here the the steps needed:
1. Download the zlib-1.3.1.tar.gz tarball (or a similar version).
Unpack the tarball sources. You can put them wherever you like.
For the purposes of this document, let's assume you put the source
tree in c:\\zlib-64. Note: If you are building for both x64 and
x86, you will need separate builds of ZLIB for each, thus separate
build directories.
2. Before building SQLite (as described above) first make these
environment changes. The lead-programmer for SQLite (who writes
these words) has BAT files named "env-x64.bat" and "env-x32.bat"
and "env-arm64.bat" that make these changes, and he runs those
BAT file whenever he starts a new shell. These are the settings
needed:
<blockquote>
set USE_ZLIB=1<br>
set BUILD_ZLIB=0<br>
set ZLIBDIR=c:\\zlib-64
</blockquote>
3. Because the settings in step 2 specify "BUILD_ZLIB=0", you will need
to build the library at least once. I recommand:
<blockquote>
make clean sqlite3.exe BUILD_ZLIB=1
</blockquote>
4. After making the environment changes specified in steps 1 through 3
above, you then build and test SQLite as you normally would. The
environment variable changes will cause ZLIB to be linked automatically.
+57 -102
View File
@@ -4,12 +4,6 @@
<ul type=none>
<li> 1. <a href=#overview>Overview</a>
<ul type=none>
<li> 1.1. <a href=#runtr>Running testrunner.tcl</a>
<li> 1.2. <a href=#runviamake>Run using "make"</a>
<li> 1.3. <a href=#outputs>Outputs from testrunner.tcl</a>
<li> 1.4. <a href=#help>Built-in help</a>
</ul>
<li> 2. <a href=#binary_tests>Binary Tests</a>
<ul type=none>
<li> 2.1. <a href=#organization_tests>Organization of Tcl Tests</a>
@@ -32,40 +26,17 @@
The testrunner.tcl program is a Tcl script used to run multiple SQLite
tests in parallel, thus reducing testing time on multi-core machines.
The testrunner.tcl supports running tests that based on `testfixture`,
`sqlite3`, and `fuzzcheck`.
It supports the following types of tests:
<a name="runtr"></a>
## 1.1 Running testrunner.tcl
* Tcl test scripts.
The testrunner.tcl script is located in the "test" subdirectory of the
SQLite source tree. So if your shell is current positioned at the top
of the source tree, you would normally run the script using the command:
"<tt>test/testrunner.tcl</tt>". On Windows, you have to specify the
`tclsh` interpreter command first, like this:
"<tt>tclsh&nbsp;test/testrunner.tcl</tt>".
* Fuzzcheck tests, including using an external fuzzcheck database.
In this document, we will assume that you are on a unix-like OS
(not on Windows) and that your current directory is the root
of the SQLite source tree, and so all invocations of the testrunner.tcl
script will be of the form "<tt>test/testrunner.tcl</tt>". If you
are in a different directory, then make appropriate adjustments to
the path. On Windows, add the "<tt>tclsh</tt>" interpreter command
up front.
<a name="runviamake"></a>
## 1.2 Run using make
The standard Makefiles for SQLite include targets that invoke
testrunner.tcl. So the following commands also run testrunner.tcl:
* `make devtest`
* `make releasetest`
* `make sdevtest`
* `make testrunner`
<a name="outputs"></a>
## 1.3 Outputs from testrunner.tcl
* Tests run with `make` commands. Examples:
- `make devtest`
- `make releasetest`
- `make sdevtest`
- `make testrunner`
The testrunner.tcl program stores output of all tests and builds run in
log file **testrunner.log**, created in the current working directory.
@@ -83,19 +54,17 @@ A useful query might be:
```
You can get a summary of errors in a prior run by invoking commands like
those shown below. Note that the testrunner.tcl script can be run directly
on unix systems (including Macs) but you will need to add <tt>tclsh</tt>
to the front on Windows.
these:
```
test/testrunner.tcl errors
test/testrunner.tcl errors -v
tclsh $(TESTDIR)/testrunner.tcl errors
tclsh $(TESTDIR)/testrunner.tcl errors -v
```
Running the command:
```
test/testrunner.tcl status
tclsh $(TESTDIR)/testrunner.tcl status
```
in the directory containing the testrunner.db database runs various queries
@@ -104,43 +73,32 @@ A good way to keep and eye on test progress is to run either of the two
following commands:
```
watch test/testrunner.tcl status
test/testrunner.tcl status -d 2
watch tclsh $(TESTDIR)/testrunner.tcl status
tclsh $(TESTDIR)/testrunner.tcl status -d 2
```
Both of the commands above accomplish about the same thing, but the second
one has the advantage of not requiring "watch" to be installed on your
system.
Sometimes testrunner.tcl uses the `testfixture` and `sqlite3` binaries that
are in the directory from which testrunner.tcl is run.
(see "Binary Tests" below). Sometimes it builds testfixture and
Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
other binaries in specific configurations to test (see "Source Tests").
<a name=help></a>
## 1.4 Built-in help
Run this command:
```
test/testrunner.tcl help
```
To get a summary of all of the various command-line options available
with testrunner.tcl
<a name=binary_tests></a>
# 2. Binary Tests
The commands described in this section all run various combinations of the Tcl
test scripts using whatever `testfixture` binary (and maybe also the `sqlite3`
binary, depending on the test) that is found in the directory from which
testrunner.tcl is launched. So typically, one must first run something
like "`make testfixture sqlite3`" before launching binary tests. In other
words, testrunner.tcl does not automatically build the binaries under test
for binary tests. The testrunner.tcl expects the binaries to be available
already.
test scripts using the `testfixture` binary used to run the testrunner.tcl
script (i.e. they do not invoke the compiler to build new binaries, or the
`make` command to run tests that are not Tcl scripts). The procedure to run
these tests is therefore:
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
whatever method seems convenient.
2. Test the binary built in step 1 by running testrunner.tcl with it,
perhaps with various options.
The following sub-sections describe the various options that can be
passed to testrunner.tcl to test binary testfixture builds.
@@ -182,22 +140,22 @@ are defined in file *testrunner_data.tcl*.
To run the "veryquick" test set, use either of the following:
```
test/testrunner.tcl
test/testrunner.tcl veryquick
./testfixture $TESTDIR/testrunner.tcl
./testfixture $TESTDIR/testrunner.tcl veryquick
```
To run the "full" test suite:
```
test/testrunner.tcl full
./testfixture $TESTDIR/testrunner.tcl full
```
To run the subset of the "full" test suite for which the test file name matches
a specified pattern (e.g. all tests that start with "fts5"), either of:
```
test/testrunner.tcl fts5%
test/testrunner.tcl 'fts5*'
./testfixture $TESTDIR/testrunner.tcl fts5%
./testfixture $TESTDIR/testrunner.tcl 'fts5*'
```
Strictly speaking, for a test to be run the pattern must match the script
@@ -209,7 +167,7 @@ characters specified as part of the pattern are transformed to "\*".
To run "all" tests (full + permutations):
```
test/testrunner.tcl all
./testfixture $TESTDIR/testrunner.tcl all
```
<a name=binary_test_failures></a>
@@ -228,15 +186,10 @@ If there is no permutation, the individual test script may be run with:
Or, if the failure occured as part of a permutation:
```
test/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
```
One can also rerun all tests that failed or did not complete
in the previous invocation by typing:
```
test/testrunner.tcl retest
```
TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
<a name=source_code_tests></a>
# 3. Source Code Tests
@@ -251,9 +204,11 @@ other tests. The advantages of this are that:
* it ensures that tests are always run using binaries created with the
same set of compiler options.
The testrunner.tcl commands described in this section do not require that
the testfixture and/or sqlite3 binaries be built ahead of time. Those
binaries will be constructed automatically.
The testrunner.tcl commands described in this section may be run using
either a *testfixture* (or testfixture.exe) build, or with any other Tcl
shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
TODO: ./configure + Makefile.msc build systems.
<a name=commands_to_run_tests></a>
## 3.1. Commands to Run SQLite Tests
@@ -263,7 +218,7 @@ the `make fuzztest` target once for each of two --enable-all builds - one
with debugging enabled and one without:
```
test/testrunner.tcl mdevtest
tclsh $TESTDIR/testrunner.tcl mdevtest
```
In other words, it is equivalent to running:
@@ -272,13 +227,13 @@ In other words, it is equivalent to running:
$TOP/configure --enable-all --enable-debug
make fuzztest
make testfixture
$TOP/test/testrunner.tcl veryquick
./testfixture $TOP/test/testrunner.tcl veryquick
# Then, after removing files created by the tests above:
$TOP/configure --enable-all OPTS="-O0"
make fuzztest
make testfixture
$TOP/test/testrunner.tcl veryquick
./testfixture $TOP/test/testrunner.tcl veryquick
```
The **sdevtest** command is identical to the mdevtest command, except that the
@@ -286,7 +241,7 @@ second of the two builds is a sanitizer build. Specifically, this means that
OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
```
test/testrunner.tcl sdevtest
tclsh $TESTDIR/testrunner.tcl sdevtest
```
The **release** command runs lots of tests under lots of builds. It runs
@@ -295,7 +250,7 @@ on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
of the specific tests run.
```
test/testrunner.tcl release
tclsh $TESTDIR/testrunner.tcl release
```
As with <a href=#source code tests>source code tests</a>, one or more patterns
@@ -303,7 +258,7 @@ may be appended to any of the above commands (mdevtest, sdevtest or release).
Pattern matching is used for both Tcl tests and fuzz tests.
```
test/testrunner.tcl release rtree%
tclsh $TESTDIR/testrunner.tcl release rtree%
```
<a name=zipvfs_tests></a>
@@ -313,14 +268,14 @@ testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
tests from the Zipvfs project with the following command:
```
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
```
This can be combined with any of "mdevtest", "sdevtest" or "release" to
test both SQLite and Zipvfs with a single command:
```
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
```
<a name=source_code_test_failures></a>
@@ -340,10 +295,10 @@ a dos \*.bat file on windows. For example:
```
# Create a script that recreates build configuration "Device-One" on
# Linux or OSX:
test/testrunner.tcl script Device-One > make.sh
tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
# Create a script that recreates build configuration "Have-Not" on Windows:
test/testrunner.tcl script Have-Not > make.bat
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
```
The generated bash or \*.bat file script accepts a single argument - a makefile
@@ -366,7 +321,7 @@ Thus, for example, to run a full releasetest including an external
dbsqlfuzz database, run a command like one of these:
```
test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
tclsh test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
FUZZDB=../fuzz/20250415.db make releasetest
nmake /f Makefile.msc FUZZDB=../fuzz/20250415.db releasetest
```
@@ -376,7 +331,7 @@ databases. So if you want to run *only* tests involving the external
database, you can use a command something like this:
```
test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
tclsh test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
```
<a name=testrunner_options></a>
@@ -390,7 +345,7 @@ required by a test, not to run any actual tests. For example:
```
# Build binaries required by release test.
test/testrunner.tcl --buildonly release"
tclsh $TESTDIR/testrunner.tcl --buildonly release"
```
The **--dryrun** option prevents testrunner.tcl from building any binaries
@@ -399,7 +354,7 @@ would normally execute into the testrunner.log file. Example:
```
# Log the shell commmands that make up the mdevtest test.
test/testrunner.tcl --dryrun mdevtest"
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
```
The **--explain** option is similar to --dryrun in that it prevents
@@ -409,7 +364,7 @@ summary of all the builds and tests that would have been run.
```
# Show what builds and tests would have been run
test/testrunner.tcl --explain mdevtest
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
```
The **--status** option uses VT100 escape sequences to display the test
@@ -425,7 +380,7 @@ When running either binary or source code tests, testrunner.tcl reports the
number of jobs it intends to use to stdout. e.g.
```
$ test/testrunner.tcl
$ ./testfixture $TESTDIR/testrunner.tcl
splitting work across 16 jobs
... more output ...
```
@@ -435,7 +390,7 @@ of real cores on the machine. This can be overridden using the "--jobs" (or -j)
switch:
```
$ test/testrunner.tcl --jobs 8
$ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
splitting work across 8 jobs
... more output ...
```
@@ -445,5 +400,5 @@ running by exucuting the following command from the directory containing the
testrunner.log and testrunner.db files:
```
$ test/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
```
+6 -3
View File
@@ -1816,7 +1816,9 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
if( !zSql ) return SQLITE_NOMEM;
p->bLock++;
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
rc = sqlite3_prepare_v3(
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
p->bLock--;
sqlite3_free(zSql);
}
@@ -3391,7 +3393,9 @@ static int fts3FilterMethod(
}
if( zSql ){
p->bLock++;
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
rc = sqlite3_prepare_v3(
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
p->bLock--;
sqlite3_free(zSql);
}else{
@@ -4014,7 +4018,6 @@ static int fts3IntegrityMethod(
UNUSED_PARAMETER(isQuick);
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK );
assert( rc!=SQLITE_CORRUPT_VTAB );
if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+1 -19
View File
@@ -203,16 +203,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
#if !defined(deliberate_fall_through)
# if defined(__has_attribute)
# if __has_attribute(fallthrough)
# define deliberate_fall_through __attribute__((fallthrough));
# endif
# endif
#endif
#if !defined(deliberate_fall_through)
# define deliberate_fall_through
#endif
#define deliberate_fall_through
/*
** Macros needed to provide flexible arrays in a portable way
@@ -610,15 +601,6 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
(*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
)
int sqlite3Fts3PrepareStmt(
Fts3Table *p, /* Prepare for this connection */
const char *zSql, /* SQL to prepare */
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
sqlite3_stmt **pp /* OUT: Prepared statement */
);
/* fts3.c */
void sqlite3Fts3ErrMsg(char**,const char*,...);
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
+1 -1
View File
@@ -325,7 +325,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
pCsr->aStat[1].nDoc++;
}
eState = 2;
/* no break */ deliberate_fall_through
/* fall through */
case 2:
if( v==0 ){ /* 0x00. Next integer will be a docid. */
+10 -32
View File
@@ -98,9 +98,9 @@ typedef struct SegmentWriter SegmentWriter;
** incrementally. See function fts3PendingListAppend() for details.
*/
struct PendingList {
sqlite3_int64 nData;
int nData;
char *aData;
sqlite3_int64 nSpace;
int nSpace;
sqlite3_int64 iLastDocid;
sqlite3_int64 iLastCol;
sqlite3_int64 iLastPos;
@@ -273,24 +273,6 @@ struct SegmentNode {
#define SQL_UPDATE_LEVEL_IDX 38
#define SQL_UPDATE_LEVEL 39
/*
** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL
** is always set.
*/
int sqlite3Fts3PrepareStmt(
Fts3Table *p, /* Prepare for this connection */
const char *zSql, /* SQL to prepare */
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
sqlite3_stmt **pp /* OUT: Prepared statement */
){
int f = SQLITE_PREPARE_FROM_DDL
|((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0)
|(bPersist ? SQLITE_PREPARE_PERSISTENT : 0);
return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL);
}
/*
** This function is used to obtain an SQLite prepared statement handle
** for the statement identified by the second argument. If successful,
@@ -416,12 +398,12 @@ static int fts3SqlStmt(
pStmt = p->aStmt[eStmt];
if( !pStmt ){
int bAllowVtab = 0;
int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
char *zSql;
if( eStmt==SQL_CONTENT_INSERT ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
bAllowVtab = 1;
f &= ~SQLITE_PREPARE_NO_VTAB;
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
@@ -429,7 +411,7 @@ static int fts3SqlStmt(
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt);
rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
sqlite3_free(zSql);
assert( rc==SQLITE_OK || pStmt==0 );
p->aStmt[eStmt] = pStmt;
@@ -778,9 +760,7 @@ static int fts3PendingTermsAddOne(
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
if( pList ){
assert( (i64)pList->nData+(i64)nToken+(i64)sizeof(Fts3HashElem)
<= (i64)p->nPendingData );
p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
}
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
@@ -793,9 +773,7 @@ static int fts3PendingTermsAddOne(
}
}
if( rc==SQLITE_OK ){
assert( (i64)p->nPendingData + pList->nData + nToken
+ sizeof(Fts3HashElem) <= 0x3fffffff );
p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
}
return rc;
}
@@ -3596,7 +3574,7 @@ static int fts3DoRebuild(Fts3Table *p){
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
@@ -5349,7 +5327,7 @@ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
@@ -5479,7 +5457,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
v = atoi(&zVal[9]);
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
v = atoi(&zVal[11]);
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
rc = SQLITE_OK;
+1 -7
View File
@@ -81,13 +81,7 @@ typedef sqlite3_uint64 u64;
# define FLEXARRAY 1
#endif
#endif /* SQLITE_AMALGAMATION */
/*
** Constants for the largest and smallest possible 32-bit signed integers.
*/
# define LARGEST_INT32 ((int)(0x7fffffff))
# define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32))
#endif
/* Truncate very long tokens to this many bytes. Hard limit is
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
+1 -1
View File
@@ -455,7 +455,7 @@ static void fts5SnippetFunction(
iBestCol = (iCol>=0 ? iCol : 0);
nPhrase = pApi->xPhraseCount(pFts);
aSeen = sqlite3_malloc64(nPhrase);
aSeen = sqlite3_malloc(nPhrase);
if( aSeen==0 ){
rc = SQLITE_NOMEM;
}
+1 -1
View File
@@ -288,7 +288,7 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
if( nIn<0 ){
nIn = (int)strlen(pIn);
}
zRet = (char*)sqlite3_malloc64((i64)nIn+1);
zRet = (char*)sqlite3_malloc(nIn+1);
if( zRet ){
memcpy(zRet, pIn, nIn);
zRet[nIn] = '\0';
+3 -1
View File
@@ -576,7 +576,7 @@ int sqlite3Fts5ConfigParse(
sqlite3_int64 nByte;
int bUnindexed = 0; /* True if there are one or more UNINDEXED */
*ppOut = pRet = (Fts5Config*)sqlite3_malloc64(sizeof(Fts5Config));
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
if( pRet==0 ) return SQLITE_NOMEM;
memset(pRet, 0, sizeof(Fts5Config));
pRet->pGlobal = pGlobal;
@@ -1123,3 +1123,5 @@ void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){
va_end(ap);
}
+2 -2
View File
@@ -314,7 +314,7 @@ int sqlite3Fts5ExprNew(
assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
if( sParse.rc==SQLITE_OK ){
*ppNew = pNew = sqlite3_malloc64(sizeof(Fts5Expr));
*ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
if( pNew==0 ){
sParse.rc = SQLITE_NOMEM;
sqlite3Fts5ParseNodeFree(sParse.pExpr);
@@ -466,7 +466,7 @@ int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
p2->pRoot = 0;
if( sParse.rc==SQLITE_OK ){
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64(
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
);
if( ap==0 ){
+1 -1
View File
@@ -91,7 +91,7 @@ int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
int rc = SQLITE_OK;
Fts5Hash *pNew;
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc64(sizeof(Fts5Hash));
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
+35 -42
View File
@@ -2074,7 +2074,6 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
while( 1 ){
u64 iDelta = 0;
if( i>=n ) break;
if( eDetail==FTS5_DETAIL_NONE ){
/* todo */
if( i<n && a[i]==0 ){
@@ -2093,7 +2092,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
/* If necessary, grow the pIter->aRowidOffset[] array. */
if( iRowidOffset>=pIter->nRowidOffset ){
i64 nNew = pIter->nRowidOffset + 8;
int nNew = pIter->nRowidOffset + 8;
int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int));
if( aNew==0 ){
p->rc = SQLITE_NOMEM;
@@ -5240,7 +5239,7 @@ static void fts5DoSecureDelete(
int iSegid = pSeg->pSeg->iSegid;
u8 *aPg = pSeg->pLeaf->p;
int nPg = pSeg->pLeaf->nn;
int iPgIdx = pSeg->pLeaf->szLeaf; /* Offset of page footer */
int iPgIdx = pSeg->pLeaf->szLeaf;
u64 iDelta = 0;
int iNextOff = 0;
@@ -5319,7 +5318,7 @@ static void fts5DoSecureDelete(
iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
}
assert_nc( iSOP==pSeg->iLeafOffset );
iNextOff = iSOP + pSeg->nPos;
iNextOff = pSeg->iLeafOffset + pSeg->nPos;
}
}
@@ -5399,31 +5398,31 @@ static void fts5DoSecureDelete(
** is another term following it on this page. So the subsequent term
** needs to be moved to replace the term associated with the entry
** being removed. */
u64 nPrefix = 0;
u64 nSuffix = 0;
u64 nPrefix2 = 0;
u64 nSuffix2 = 0;
int nPrefix = 0;
int nSuffix = 0;
int nPrefix2 = 0;
int nSuffix2 = 0;
iDelKeyOff = iNextOff;
iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2);
iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2);
iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2);
iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2);
if( iKey!=1 ){
iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix);
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix);
}
iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix);
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix);
nPrefix = MIN(nPrefix, nPrefix2);
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
FTS5_CORRUPT_IDX(p);
}else{
if( iKey!=1 ){
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
}
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
if( nPrefix2>(u64)pSeg->term.n ){
if( nPrefix2>pSeg->term.n ){
FTS5_CORRUPT_IDX(p);
}else if( nPrefix2>nPrefix ){
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
@@ -5454,7 +5453,7 @@ static void fts5DoSecureDelete(
u8 *aTermIdx = &pTerm->p[pTerm->szLeaf];
int nTermIdx = pTerm->nn - pTerm->szLeaf;
int iTermIdx = 0;
i64 iTermOff = 0;
int iTermOff = 0;
while( 1 ){
u32 iVal = 0;
@@ -5465,15 +5464,12 @@ static void fts5DoSecureDelete(
}
nTermIdx = iTermIdx;
if( iTermOff>pTerm->szLeaf ){
FTS5_CORRUPT_IDX(p);
}else{
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
fts5PutU16(&pTerm->p[2], iTermOff);
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
if( nTermIdx==0 ){
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
}
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
fts5PutU16(&pTerm->p[2], iTermOff);
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
if( nTermIdx==0 ){
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
}
}
fts5DataRelease(pTerm);
@@ -5496,9 +5492,7 @@ static void fts5DoSecureDelete(
int iPrevKeyOut = 0;
int iKeyIn = 0;
if( nMove>0 ){
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
}
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
iPgIdx -= nShift;
nPg = iPgIdx;
fts5PutU16(&aPg[2], iPgIdx);
@@ -5936,7 +5930,7 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
fts5StructureRelease(pStruct);
pStruct = pNew;
nMin = 1;
nMerge = (nMerge==SMALLEST_INT32 ? LARGEST_INT32 : (nMerge*-1));
nMerge = nMerge*-1;
}
if( pStruct && pStruct->nLevel ){
if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){
@@ -6418,16 +6412,16 @@ struct Fts5TokenDataMap {
** aMap[] variables.
*/
struct Fts5TokenDataIter {
i64 nMapAlloc; /* Allocated size of aMap[] in entries */
i64 nMap; /* Number of valid entries in aMap[] */
int nMapAlloc; /* Allocated size of aMap[] in entries */
int nMap; /* Number of valid entries in aMap[] */
Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
/* The following are used for prefix-queries only. */
Fts5Buffer terms;
/* The following are used for other full-token tokendata queries only. */
i64 nIter;
i64 nIterAlloc;
int nIter;
int nIterAlloc;
Fts5PoslistReader *aPoslistReader;
int *aPoslistToIter;
Fts5Iter *apIter[FLEXARRAY];
@@ -6483,11 +6477,11 @@ static void fts5TokendataIterAppendMap(
){
if( p->rc==SQLITE_OK ){
if( pT->nMap==pT->nMapAlloc ){
i64 nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
i64 nAlloc = nNew * sizeof(Fts5TokenDataMap);
int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
int nAlloc = nNew * sizeof(Fts5TokenDataMap);
Fts5TokenDataMap *aNew;
aNew = (Fts5TokenDataMap*)sqlite3_realloc64(pT->aMap, nAlloc);
aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
if( aNew==0 ){
p->rc = SQLITE_NOMEM;
return;
@@ -6513,7 +6507,7 @@ static void fts5TokendataIterAppendMap(
*/
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
Fts5TokenDataMap *aTmp = 0;
i64 nByte = pT->nMap * sizeof(Fts5TokenDataMap);
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( aTmp ){
@@ -7047,10 +7041,9 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
if( p->rc==SQLITE_OK ){
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
i64 nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
i64 nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
Fts5TokenDataIter *pNew;
pNew = (Fts5TokenDataIter*)sqlite3_realloc64(pIn, nByte);
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
if( pNew==0 ){
p->rc = SQLITE_NOMEM;
@@ -7147,8 +7140,8 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
/* Ensure the token-mapping is large enough */
if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
i64 nNew = (pT->nMapAlloc + nByte) * 2;
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc64(
int nNew = (pT->nMapAlloc + nByte) * 2;
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
pT->aMap, nNew*sizeof(Fts5TokenDataMap)
);
if( aNew==0 ){
+22 -48
View File
@@ -517,7 +517,7 @@ static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
if( sqlite3_libversion_number()>=3008002 )
#endif
{
pIdxInfo->estimatedRows = MAX(1, nRow);
pIdxInfo->estimatedRows = nRow;
}
#endif
}
@@ -586,30 +586,19 @@ static int fts5UsePatternMatch(
** a) If a MATCH operator is present, the cost depends on the other
** constraints also present. As follows:
**
** * No other constraints: cost=50000.0
** * One rowid range constraint: cost=37500.0
** * Both rowid range constraints: cost=30000.0
** * An == rowid constraint: cost=25000.0
** * No other constraints: cost=1000.0
** * One rowid range constraint: cost=750.0
** * Both rowid range constraints: cost=500.0
** * An == rowid constraint: cost=100.0
**
** b) Otherwise, if there is no MATCH:
**
** * No other constraints: cost=3000000.0
** * One rowid range constraints: cost=2250000.0
** * Both rowid range constraint: cost=750000.0
** * An == rowid constraint: cost=25.0
** * No other constraints: cost=1000000.0
** * One rowid range constraint: cost=750000.0
** * Both rowid range constraints: cost=250000.0
** * An == rowid constraint: cost=10.0
**
** Costs are not modified by the ORDER BY clause.
**
** The ratios used in case (a) are based on informal results obtained from
** the tool/fts5cost.tcl script. The "MATCH and ==" combination has the
** cost set quite high because the query may be a prefix query. Unless
** there is a prefix index, prefix queries with rowid constraints are much
** more expensive than non-prefix queries with rowid constraints.
**
** The estimated rows returned is set to the cost/40. For simple queries,
** experimental results show that cost/4 might be about right. But for
** more complex queries that use multiple terms the number of rows might
** be far fewer than this. So we compromise and use cost/40.
*/
static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts5Table *pTab = (Fts5Table*)pVTab;
@@ -642,7 +631,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
return SQLITE_ERROR;
}
idxStr = (char*)sqlite3_malloc64((i64)pInfo->nConstraint * 8 + 1);
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
if( idxStr==0 ) return SQLITE_NOMEM;
pInfo->idxStr = idxStr;
pInfo->needToFreeIdxStr = 1;
@@ -735,35 +724,21 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
/* Calculate the estimated cost based on the flags set in idxFlags. */
if( bSeenEq ){
pInfo->estimatedCost = nSeenMatch ? 25000.0 : 25.0;
fts5SetEstimatedRows(pInfo, 1);
pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
fts5SetUniqueFlag(pInfo);
fts5SetEstimatedRows(pInfo, 1);
}else{
i64 nEstRows;
if( nSeenMatch ){
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = 50000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = 37500.0;
}else{
pInfo->estimatedCost = 50000.0;
}
nEstRows = (i64)(pInfo->estimatedCost / 40.0);
for(i=1; i<nSeenMatch; i++){
pInfo->estimatedCost *= 2.5;
nEstRows = nEstRows / 2;
}
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0;
}else{
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = 750000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = 2250000.0;
}else{
pInfo->estimatedCost = 3000000.0;
}
nEstRows = (i64)(pInfo->estimatedCost / 4.0);
pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
}
fts5SetEstimatedRows(pInfo, nEstRows);
for(i=1; i<nSeenMatch; i++){
pInfo->estimatedCost *= 0.4;
}
fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
}
pInfo->idxNum = idxFlags;
@@ -2106,7 +2081,6 @@ static int fts5UpdateMethod(
}
update_out:
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
@@ -3788,7 +3762,7 @@ static int fts5Init(sqlite3 *db){
int rc;
Fts5Global *pGlobal = 0;
pGlobal = (Fts5Global*)sqlite3_malloc64(sizeof(Fts5Global));
pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
if( pGlobal==0 ){
rc = SQLITE_NOMEM;
}else{
+4 -4
View File
@@ -391,7 +391,7 @@ static int SQLITE_TCLAPI xF5tApi(
break;
}
CASE(12, "xSetAuxdata") {
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc64(sizeof(F5tAuxData));
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
if( pData==0 ){
Tcl_AppendResult(interp, "out of memory", (char*)0);
return TCL_ERROR;
@@ -780,7 +780,7 @@ static int SQLITE_TCLAPI f5tTokenize(
}
if( nText>0 ){
pCopy = sqlite3_malloc64(nText);
pCopy = sqlite3_malloc(nText);
if( pCopy==0 ){
tokenizer.xDelete(pTok);
Tcl_AppendResult(interp, "error in sqlite3_malloc()", (char*)0);
@@ -1420,7 +1420,7 @@ static int f5tOrigintextCreate(
void *pTokCtx = 0;
int rc = SQLITE_OK;
pTok = (OriginTextTokenizer*)sqlite3_malloc64(sizeof(OriginTextTokenizer));
pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer));
if( pTok==0 ){
rc = SQLITE_NOMEM;
}else if( nArg<1 ){
@@ -1480,7 +1480,7 @@ static int xOriginToken(
int nReq = nToken + 1 + (iEnd-iStart);
if( nReq>p->nBuf ){
sqlite3_free(p->aBuf);
p->aBuf = sqlite3_malloc64(nReq*2);
p->aBuf = sqlite3_malloc(nReq*2);
if( p->aBuf==0 ) return SQLITE_NOMEM;
p->nBuf = nReq*2;
}
+5 -5
View File
@@ -194,7 +194,7 @@ static int fts5tokConnectMethod(
}
if( rc==SQLITE_OK ){
pTab = (Fts5tokTable*)sqlite3_malloc64(sizeof(Fts5tokTable));
pTab = (Fts5tokTable*)sqlite3_malloc(sizeof(Fts5tokTable));
if( pTab==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -275,7 +275,7 @@ static int fts5tokBestIndexMethod(
static int fts5tokOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
Fts5tokCursor *pCsr;
pCsr = (Fts5tokCursor *)sqlite3_malloc64(sizeof(Fts5tokCursor));
pCsr = (Fts5tokCursor *)sqlite3_malloc(sizeof(Fts5tokCursor));
if( pCsr==0 ){
return SQLITE_NOMEM;
}
@@ -347,7 +347,7 @@ static int fts5tokCb(
if( pCsr->nRow ){
pRow->iPos = pRow[-1].iPos + ((tflags & FTS5_TOKEN_COLOCATED) ? 0 : 1);
}
pRow->zToken = sqlite3_malloc64((sqlite3_int64)nToken+1);
pRow->zToken = sqlite3_malloc(nToken+1);
if( pRow->zToken==0 ) return SQLITE_NOMEM;
memcpy(pRow->zToken, pToken, nToken);
pRow->zToken[nToken] = 0;
@@ -373,8 +373,8 @@ static int fts5tokFilterMethod(
fts5tokResetCursor(pCsr);
if( idxNum==1 ){
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]);
pCsr->zInput = sqlite3_malloc64(nByte+1);
int nByte = sqlite3_value_bytes(apVal[0]);
pCsr->zInput = sqlite3_malloc(nByte+1);
if( pCsr->zInput==0 ){
rc = SQLITE_NOMEM;
}else{
+4 -4
View File
@@ -72,7 +72,7 @@ static int fts5AsciiCreate(
if( nArg%2 ){
rc = SQLITE_ERROR;
}else{
p = sqlite3_malloc64(sizeof(AsciiTokenizer));
p = sqlite3_malloc(sizeof(AsciiTokenizer));
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -367,7 +367,7 @@ static int fts5UnicodeCreate(
if( nArg%2 ){
rc = SQLITE_ERROR;
}else{
p = (Unicode61Tokenizer*)sqlite3_malloc64(sizeof(Unicode61Tokenizer));
p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
if( p ){
const char *zCat = "L* N* Co";
int i;
@@ -590,7 +590,7 @@ static int fts5PorterCreate(
zBase = azArg[0];
}
pRet = (PorterTokenizer*)sqlite3_malloc64(sizeof(PorterTokenizer));
pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
if( pRet ){
memset(pRet, 0, sizeof(PorterTokenizer));
rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2);
@@ -1297,7 +1297,7 @@ static int fts5TriCreate(
rc = SQLITE_ERROR;
}else{
int i;
pNew = (TrigramTokenizer*)sqlite3_malloc64(sizeof(*pNew));
pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
+1 -12
View File
@@ -396,12 +396,7 @@ static int fts5VocabOpenMethod(
return rc;
}
/*
** Restore cursor pCsr to the state it was in immediately after being
** created by the xOpen() method.
*/
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
int nCol = pCsr->pFts5->pConfig->nCol;
pCsr->rowid = 0;
sqlite3Fts5IterClose(pCsr->pIter);
sqlite3Fts5StructureRelease(pCsr->pStruct);
@@ -411,12 +406,6 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
pCsr->nLeTerm = -1;
pCsr->zLeTerm = 0;
pCsr->bEof = 0;
pCsr->iCol = 0;
pCsr->iInstPos = 0;
pCsr->iInstOff = 0;
pCsr->colUsed = 0;
memset(pCsr->aCnt, 0, sizeof(i64)*nCol);
memset(pCsr->aDoc, 0, sizeof(i64)*nCol);
}
/*
@@ -666,7 +655,7 @@ static int fts5VocabFilterMethod(
const char *zCopy = (const char *)sqlite3_value_text(pLe);
if( zCopy==0 ) zCopy = "";
pCsr->nLeTerm = sqlite3_value_bytes(pLe);
pCsr->zLeTerm = sqlite3_malloc64((i64)pCsr->nLeTerm+1);
pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
if( pCsr->zLeTerm==0 ){
rc = SQLITE_NOMEM;
}else{
-362
View File
@@ -16124,367 +16124,5 @@ do_catchsql_test 83.1 {
SELECT * FROM t1('R*R*R*R*R*R*R*R*') WHERE (a,b)<=(current_date,0 BETWEEN 'a'<>11 AND '') ORDER BY rowid DESC;
} {/.*fts5: corruption found/}
#-------------------------------------------------------------------------
reset_db
do_test 84.0 {
sqlite3 db {}
db deserialize [decode_hexdb {
.open --hexdb
| size 53248 pagesize 4096 filename c1a.txt.db
| page 1 offset 0
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 0d .....@ ........
| 32: 00 00 00 02 00 00 00 01 00 00 00 09 00 00 00 04 ................
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
| 96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6
| 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 0d 92 00 00 00 00 ...k............
| 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
| 3488: 32 74 32 0d 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
| 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
| 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
| 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
| 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
| 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
| 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[.
| 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
| 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
| 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
| 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
| 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
| 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...!
| 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont
| 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c
| 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG
| 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
| 3776: 63 30 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c0, c1, c2)i....
| 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt
| 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB
| 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi
| 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P
| 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid
| 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT
| 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t
| 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da
| 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE
| 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT
| 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
| 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8..
| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR
| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB
| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5
| 4032: 28 61 2c 62 2c 63 29 00 00 00 39 00 00 00 00 00 (a,b,c)...9.....
| page 3 offset 8192
| 0: 05 00 00 00 02 0f f1 00 00 00 00 0c 0f fb 0f f1 ................
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0a ........:.^.:.:.
| 4080: 0a 00 00 00 0b 84 80 80 80 80 01 00 00 00 0a 0a ................
| page 4 offset 12288
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
| page 7 offset 24576
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
| page 10 offset 36864
| 0: 0d 00 00 00 02 0f e2 00 0f e2 0f ef 00 00 00 00 ................
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0f ........:.^.:.:.
| 4080: 0a 03 00 24 00 00 00 00 01 01 02 00 01 01 01 09 ...$............
| page 11 offset 40960
| 0: 0d 00 00 00 01 00 22 00 00 22 00 00 00 00 00 00 ................
| 32: 00 00 9f 56 84 80 80 80 80 01 04 00 bf 30 00 00 ...V.........0..
| 48: 0f 58 02 30 30 19 02 05 01 02 05 01 02 05 16 02 .X.00...........
| 64: 05 01 02 05 01 02 05 61 02 05 01 02 05 01 02 05 .......a........
| 80: 13 02 05 01 02 05 01 02 05 0d 02 03 01 02 03 01 ................
| 96: 02 03 02 09 78 66 66 66 66 66 66 66 65 81 17 02 ....xfffffffe...
| 112: 05 01 02 05 01 02 05 01 01 31 04 02 04 01 02 04 .........1......
| 128: 01 02 04 01 02 05 01 02 05 01 02 05 0d 02 06 01 ................
| 144: 02 06 01 02 06 02 01 30 79 02 04 01 02 04 01 02 .......0y.......
| 160: 04 03 02 30 30 2b 02 05 01 02 05 01 02 05 58 02 ...00+........X.
| 176: 05 01 02 05 01 02 05 01 02 05 01 02 05 01 02 05 ................
| 192: 16 02 05 01 02 05 01 02 05 05 06 30 30 30 30 30 ...........00000
| 208: 30 81 0b 02 04 01 02 04 01 02 04 10 02 05 01 02 0...............
| 224: 05 01 02 05 03 02 32 34 76 02 05 01 02 05 01 02 ......24v.......
| 240: 05 02 01 38 07 02 04 01 02 04 01 02 04 01 01 32 ...8...........2
| 256: 28 02 04 01 02 04 01 02 04 04 02 05 01 02 05 01 (...............
| 272: 02 05 02 01 30 1f 02 05 01 02 05 01 02 05 03 02 ....0...........
| 288: 30 30 10 02 05 01 02 05 01 02 05 6a 02 04 01 02 00.........j....
| 304: 04 01 02 04 02 08 35 30 30 30 30 30 30 30 81 26 ......50000000.&
| 320: 02 05 01 02 05 01 02 05 01 01 33 07 02 06 01 02 ..........3.....
| 336: 06 01 02 06 81 2c 02 04 01 02 04 01 02 04 02 04 .....,..........
| 352: 32 37 36 36 81 23 02 05 01 02 05 01 02 05 01 01 2766.#..........
| 368: 34 13 02 05 01 02 05 01 02 05 02 03 30 39 36 1c 4...........096.
| 384: 02 05 01 02 05 01 02 05 07 02 05 01 02 05 01 02 ................
| 400: 05 01 03 35 30 30 7f 02 05 01 02 05 01 02 05 04 ...500..........
| 416: 02 30 30 81 0e 02 06 01 02 06 01 02 06 06 03 30 .00............0
| 432: 30 30 81 11 02 04 01 02 04 01 02 04 01 05 36 35 00............65
| 448: 35 33 36 81 1a 02 05 01 02 05 01 02 05 01 04 38 536............8
| 464: 31 39 32 81 02 02 06 01 02 06 01 02 06 01 05 61 192............a
| 480: 6c 6c 6f 77 01 02 02 01 02 02 01 02 02 02 02 72 llow...........r
| 496: 67 81 08 02 04 01 02 04 01 02 04 02 05 74 6f 6d g............tom
| 512: 69 63 04 02 02 01 02 02 01 02 02 03 06 74 61 63 ic...........tac
| 528: 68 65 64 79 02 03 01 02 03 01 02 03 02 0d 75 74 hedy..........ut
| 544: 6f 63 68 65 63 6b 70 6f 69 6e 74 2b 02 04 01 02 ocheckpoint+....
| 560: 04 01 02 04 05 06 76 61 63 75 75 6d 0d 02 03 01 ......vacuum....
| 576: 02 03 01 02 03 01 06 62 69 6e 61 72 79 03 06 01 .......binary...
| 592: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 608: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 624: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 640: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 656: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 672: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 688: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 704: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 720: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 736: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 752: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 768: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 784: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 800: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 816: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 832: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 848: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 864: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 880: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 896: 01 02 02 02 07 79 74 65 63 6f 64 65 37 02 03 01 .....ytecode7...
| 912: 02 03 01 02 03 01 05 63 61 63 68 65 10 02 03 01 .......cache....
| 928: 02 03 01 01 03 02 04 6c 61 6e 67 07 02 03 01 02 .......lang.....
| 944: 03 01 02 03 02 05 6f 6c 75 6d 6e 7c 02 03 01 02 ......olumn|....
| 960: 03 01 02 03 03 06 6d 6d 65 6e 74 73 43 02 04 01 ......mmentsC...
| 976: 02 04 01 02 04 04 05 70 69 6c 65 72 07 02 02 01 .......piler....
| 992: 02 02 01 02 02 05 04 6f 75 6e 64 7f 02 03 01 02 .......ound.....
| 1008: 03 01 02 03 03 03 75 6e 74 81 17 02 04 01 02 04 ......unt.......
| 1024: 01 02 04 02 05 75 72 73 6f 72 3a 02 03 01 02 03 .....ursor:.....
| 1040: 01 02 03 01 06 64 62 70 61 67 65 3d 02 03 01 02 .....dbpage=....
| 1056: 03 01 02 03 03 04 73 74 61 74 40 02 03 01 02 03 ......stat@.....
| 1072: 01 02 03 02 04 65 62 75 67 0a 02 02 01 02 02 01 .....ebug.......
| 1088: 02 02 03 05 66 61 75 6c 74 0d 02 02 01 02 02 01 ....fault.......
| 1104: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1120: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1136: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1152: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1168: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1184: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1200: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 4f 02 ..............O.
| 1216: 03 01 02 03 01 02 03 03 03 70 74 68 81 05 02 04 .........pth....
| 1232: 01 02 04 01 02 04 19 02 04 01 02 04 01 02 04 02 ................
| 1248: 05 69 72 65 63 74 34 02 02 01 02 02 01 02 02 01 .irect4.........
| 1264: 06 65 6e 61 62 6c 65 37 02 02 01 02 02 01 02 02 .enable7........
| 1280: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1296: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1312: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1328: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1344: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1360: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1376: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1392: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1408: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1424: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1440: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1456: 02 01 02 02 02 06 78 70 6c 61 69 6e 43 02 03 01 ......xplainC...
| 1472: 02 03 01 02 03 04 01 72 81 05 02 03 01 02 03 01 .......r........
| 1488: 02 03 03 07 74 65 6e 73 69 6f 6e 81 2f 02 04 01 ....tension./...
| 1504: 02 04 01 02 04 01 04 66 69 6c 65 13 02 03 01 02 .......file.....
| 1520: 03 01 02 03 02 05 6f 72 6d 61 74 13 02 04 01 02 ......ormat.....
| 1536: 04 01 02 04 02 03 74 73 33 46 02 03 01 02 03 01 ......ts3F......
| 1552: 02 03 01 02 03 01 02 03 01 02 03 04 01 34 4c 02 .............4L.
| 1568: 03 01 02 03 01 02 03 04 01 35 4f 02 03 01 02 03 .........5O.....
| 1584: 01 02 03 02 03 75 6e 63 5e 02 05 01 02 05 01 02 .....unc^.......
| 1600: 05 05 04 74 69 6f 6e 73 02 05 01 02 05 01 02 05 ...tions........
| 1616: 13 02 03 01 02 03 01 02 03 09 01 73 55 02 04 01 ...........sU...
| 1632: 02 04 01 02 04 01 07 67 65 6f 70 6f 6c 79 52 02 .......geopolyR.
| 1648: 03 01 02 03 01 02 03 01 05 68 69 6e 74 73 3a 02 .........hints:.
| 1664: 04 01 02 04 01 02 04 02 03 6f 6f 6b 61 02 04 01 .........ooka...
| 1680: 02 04 01 02 04 01 02 69 6e 01 02 04 01 02 04 01 .......in.......
| 1696: 02 04 03 04 69 74 73 7a 1f 02 04 01 02 04 01 02 ....itsz........
| 1712: 04 03 08 74 72 69 6e 73 69 63 73 04 02 03 01 02 ...trinsics.....
| 1728: 03 01 02 03 01 07 6a 6f 75 72 6e 61 6c 16 02 03 ......journal...
| 1744: 01 02 03 01 02 03 01 06 6c 65 6e 67 74 68 81 0b ........length..
| 1760: 02 03 01 02 03 01 02 03 01 02 05 01 02 05 01 02 ................
| 1776: 05 0d 02 04 01 02 04 01 02 04 02 03 69 6b 65 81 ............ike.
| 1792: 0e 02 03 01 02 03 01 02 03 03 03 6d 69 74 16 02 ...........mit..
| 1808: 05 01 02 05 01 02 05 5e 02 04 01 02 04 01 02 04 .......^........
| 1824: 02 03 6f 61 64 81 2f 02 03 01 02 03 01 02 03 01 ..oad./.........
| 1840: 06 6d 61 6c 6c 6f 63 76 02 02 01 02 02 01 02 02 .mallocv........
| 1856: 3a 02 03 01 02 03 01 02 03 03 02 74 68 55 02 03 :..........thU..
| 1872: 01 02 03 01 02 03 03 01 78 79 02 02 01 02 02 01 ........xy......
| 1888: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1904: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1920: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1936: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 1952: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 1968: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 1984: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
| 2000: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
| 2016: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
| 2032: 02 02 02 05 65 6d 6f 72 79 81 11 02 03 01 02 03 ....emory.......
| 2048: 01 02 03 04 04 73 79 73 35 58 02 03 01 02 03 01 .....sys5X......
| 2064: 02 03 02 03 6d 61 70 19 02 03 01 02 03 01 02 03 ....map.........
| 2080: 79 02 03 01 02 03 01 02 03 02 04 75 74 65 78 81 y..........utex.
| 2096: 2c 02 02 01 02 02 01 02 02 01 06 6e 6f 63 61 73 ,..........nocas
| 2112: 65 02 06 01 02 02 03 06 01 02 02 03 06 01 02 02 e...............
| 2128: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2144: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2160: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2176: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2192: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2208: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2224: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2240: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2256: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2272: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2288: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2304: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2320: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2336: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2352: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2368: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2384: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2400: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2416: 02 02 03 06 01 02 02 03 07 72 6d 61 6c 69 7a 65 .........rmalize
| 2432: 5b 02 03 01 02 03 01 02 03 02 05 75 6d 62 65 72 [..........umber
| 2448: 81 23 02 04 01 02 04 01 02 04 01 06 6f 66 66 73 .#..........offs
| 2464: 65 74 5e 02 03 01 02 03 01 02 03 02 03 6d 69 74 et^..........mit
| 2480: 81 2c 02 03 01 02 03 01 02 03 01 02 02 01 02 02 .,..............
| 2496: 01 02 02 02 01 70 81 26 02 04 01 02 04 01 02 04 .....p.&........
| 2512: 02 07 76 65 72 66 6c 6f 77 34 02 03 01 02 03 01 ..verflow4......
| 2528: 02 03 01 04 70 61 67 65 1c 02 03 01 02 03 01 02 ....page........
| 2544: 03 64 02 04 01 02 04 01 02 04 13 02 03 01 02 03 .d..............
| 2560: 01 02 03 01 02 03 01 02 03 01 02 03 03 09 72 65 ..............re
| 2576: 6e 74 68 65 73 69 73 49 02 04 01 02 04 01 02 04 nthesisI........
| 2592: 03 05 74 74 65 72 6e 81 0e 02 04 01 02 04 01 02 ..ttern.........
| 2608: 04 02 05 63 61 63 68 65 1f 02 03 01 02 03 01 02 ...cache........
| 2624: 03 02 08 72 65 75 70 64 61 74 65 61 02 03 01 02 ...reupdatea....
| 2640: 03 01 02 03 01 04 72 65 61 64 34 02 04 01 02 04 ......read4.....
| 2656: 01 02 04 03 07 63 75 72 73 69 76 65 22 02 03 01 .....cursive....
| 2672: 02 03 01 02 03 02 04 6f 77 69 64 01 02 03 01 02 .......owid.....
| 2688: 03 01 02 03 02 04 74 72 65 65 64 02 03 01 02 03 ......treed.....
| 2704: 01 02 03 04 02 69 6d 01 06 01 02 02 03 06 01 02 .....im.........
| 2720: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2736: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2752: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2768: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2784: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2800: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2816: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2832: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2848: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2864: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2880: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2896: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2912: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 2928: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
| 2944: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
| 2960: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
| 2976: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
| 2992: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
| 3008: 01 02 02 03 06 01 02 02 03 06 01 02 02 01 0a 73 ...............s
| 3024: 63 61 6e 73 74 61 74 75 73 70 02 04 01 02 04 01 canstatusp......
| 3040: 02 04 02 05 65 63 74 6f 72 25 02 03 01 02 03 01 ....ector%......
| 3056: 02 03 03 04 6c 65 63 74 7f 02 04 01 02 04 01 02 ....lect........
| 3072: 04 03 05 73 73 69 6f 6e 67 02 03 01 02 03 01 02 ...ssiong.......
| 3088: 03 02 03 69 7a 65 10 02 04 01 02 04 01 02 04 04 ...ize..........
| 3104: 02 04 01 02 04 01 02 04 01 02 04 01 02 04 01 02 ................
| 3120: 04 01 02 04 01 02 04 01 02 04 07 02 04 01 02 04 ................
| 3136: 01 02 04 5b 02 05 01 02 05 01 02 05 10 02 04 01 ...[............
| 3152: 02 04 01 02 04 04 02 04 01 02 04 01 02 04 02 03 ................
| 3168: 6f 66 74 76 02 03 01 02 03 01 02 03 02 02 71 6c oftv..........ql
| 3184: 5e 02 04 01 02 04 01 02 04 13 02 04 01 02 04 01 ^...............
| 3200: 02 04 28 02 03 01 02 03 01 02 03 02 04 74 61 74 ..(..........tat
| 3216: 34 6a 02 03 01 02 03 01 02 03 03 02 6d 74 70 02 4j..........mtp.
| 3232: 03 01 02 03 01 02 03 05 04 76 74 61 62 6d 02 03 .........vtabm..
| 3248: 01 02 03 01 02 03 03 03 6f 72 65 81 35 02 03 01 ........ore.5...
| 3264: 02 03 01 02 03 02 0a 79 6e 63 68 72 6f 6e 6f 75 .......ynchronou
| 3280: 73 28 02 03 01 02 03 01 02 03 04 02 04 01 02 04 s(..............
| 3296: 01 02 04 03 04 73 74 65 6d 81 32 02 02 01 02 02 .....stem.2.....
| 3312: 01 02 02 01 04 74 65 6d 70 81 35 02 02 01 02 02 .....temp.5.....
| 3328: 01 02 02 02 06 68 72 65 61 64 73 31 02 04 01 02 .....hreads1....
| 3344: 04 01 02 04 76 02 04 01 02 04 01 02 04 08 03 61 ....v..........a
| 3360: 66 65 81 38 02 02 01 02 02 01 02 02 02 06 72 69 fe.8..........ri
| 3376: 67 67 65 72 81 20 02 03 01 02 03 01 02 03 08 01 gger. ..........
| 3392: 73 22 02 04 01 02 04 01 02 04 01 07 75 6e 6b 6e s...........unkn
| 3408: 6f 77 6e 73 02 03 01 02 03 01 02 03 01 08 76 61 owns..........va
| 3424: 72 69 61 62 6c 65 81 23 02 03 01 02 03 01 02 03 riable.#........
| 3440: 02 03 64 62 65 81 26 02 03 01 02 03 01 02 03 02 ..dbe.&.........
| 3456: 03 69 65 77 01 02 05 01 02 05 01 02 05 02 03 74 .iew...........t
| 3472: 61 62 37 02 04 01 02 04 01 02 04 04 02 04 01 02 ab7.............
| 3488: 04 01 02 04 01 02 04 01 02 04 01 02 04 01 03 77 ...............w
| 3504: 61 6c 2b 02 03 01 02 03 01 02 03 01 02 03 01 02 al+.............
| 3520: 03 01 02 03 02 05 6f 72 6b 65 72 31 02 03 01 02 ......orker1....
| 3536: 03 01 02 03 76 02 03 01 02 03 01 02 03 01 01 78 ....v..........x
| 3552: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3568: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3584: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3600: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3616: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3632: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3648: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3664: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3680: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3696: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3712: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3728: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3744: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3760: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3776: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3792: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3808: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3824: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3840: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3856: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3872: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3888: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3904: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3920: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3936: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3952: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3968: 06 01 01 02 01 06 04 30 15 1e 0c 28 1b 0d 0c 15 .......0...(....
| 3984: 0c 16 14 16 10 0c 17 0e 0e 0f 11 10 10 0e 10 11 ................
| 4000: 18 11 82 3e 12 10 0f 10 11 10 0f 0f 10 11 0f 0f ...>............
| 4016: 81 05 18 10 81 45 11 0d 13 0f 10 17 0c 0c 0e 18 .....E..........
| 4032: 0c 12 10 0e 0d 0f 13 12 24 0f 17 0f 1a 0d 81 1c ........$.......
| 4048: 11 0f 17 10 82 3e 12 11 11 18 0d 12 2a 14 11 10 .....>......*...
| 4064: 13 0f 12 0f 0f 82 3a 15 10 0f 10 4d 0e 1f 0f 0d ......:....M....
| 4080: 0f 0f 1e 10 10 1a 0f 12 0c 12 14 0f 0e 20 17 19 ............. ..
| page 12 offset 45056
| 0: 0d 00 00 00 01 0d f4 00 0d f4 00 00 00 00 00 00 ................
| 3568: 00 00 00 00 84 04 84 80 80 80 80 02 04 00 88 0c ................
| 3584: 00 07 02 00 01 01 02 56 06 01 01 02 01 06 01 01 .......V........
| 3600: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3616: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3632: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3648: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3664: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3680: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3696: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3712: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3728: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3744: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3760: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3776: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3792: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3808: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3824: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3840: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3856: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3872: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3888: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3904: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 3920: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 3936: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 3952: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 3968: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
| 3984: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 4000: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| 4016: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
| 4032: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
| 4048: 52 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 R...............
| 4064: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
| 4080: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
| end c1a.txt.db
}]} {}
do_catchsql_test 84.1 {
SELECT * FROM t1('R*R*x') ORDER BY rowid DESC;
} {1 {fts5: corruption found reading blob 137438953475 from table "t1"}}
sqlite3_fts5_may_be_corrupt 0
finish_test
File diff suppressed because one or more lines are too long
+3
View File
@@ -379,6 +379,9 @@ do_execsql_test 12.2 {
db close
sqlite3 db test.db -readonly 1
explain_i {
PRAGMA integrity_check
}
do_execsql_test 12.3 {
PRAGMA integrity_check
} {ok}
-17
View File
@@ -64,21 +64,4 @@ foreach {tn sql} {
}
}
#-------------------------------------------------------------------------
# Verify that https://sqlite.org/forum/forumpost/95413eb410 has been
# fixed.
#
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE f1 USING fts5(x);
BEGIN TRANSACTION;
INSERT INTO f1(x) VALUES('abc def ghi');
}
do_test 2.1 {
sqlite3_interrupt db
} {}
do_execsql_test 2.2 {
ROLLBACK
}
finish_test
-9
View File
@@ -65,14 +65,5 @@ do_eqp_test 1.4 {
`--SCAN vt VIRTUAL TABLE INDEX 0:=
}
do_eqp_test 1.5 {
SELECT * FROM vt, t1
WHERE vt.rowid = t1.rowid AND vt MATCH ? AND b = ?
} {
QUERY PLAN
|--SCAN vt VIRTUAL TABLE INDEX 0:M1
`--SEARCH t1 USING INTEGER PRIMARY KEY (rowid=?)
}
finish_test
-16
View File
@@ -238,22 +238,6 @@ do_execsql_test 6.3 {
INSERT INTO g1(g1) VALUES('integrity-check');
}
#--------------------------------------------------------------------------
# Check that passing -2147483648 as the parameter to a merge command
# does not cause a signed integer overflow error.
#
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE f1 USING fts5(a);
}
do_execsql_test 7.1 {
INSERT INTO f1 VALUES('one two three');
INSERT INTO f1 VALUES('four five six');
INSERT INTO f1 VALUES('seven eight nine');
}
do_execsql_test 7.2 {
INSERT INTO f1(f1, rank) VALUES('merge', -2147483648);
}
finish_test
-22
View File
@@ -305,28 +305,6 @@ do_catchsql_test 6.2 {
sqlite3_fts5_may_be_corrupt 0
#-------------------------------------------------------------------------
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, col);
INSERT INTO t1 VALUES('xx', 'xx');
CREATE TABLE x1(t);
INSERT INTO x1 VALUES('xx');
INSERT INTO x1 VALUES('xx');
SELECT term, col FROM v1;
} {
xx a xx b
}
do_execsql_test 7.1 {
SELECT * FROM x1 WHERE 'a'=(SELECT col FROM v1 WHERE term=t)
} {xx xx}
finish_test
-153
View File
@@ -1,153 +0,0 @@
#
# 2026 March 20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#--------------------------------------------------------------------------
#
# This script extracts the documentation for the API used by fts5 auxiliary
# functions from header file fts5.h. It outputs html text on stdout that
# is included in the documentation on the web.
#
sqlite3 db fts5cost.db
# Create an IPK table with 1,000,000 entries. Short records.
#
set res [list [catch { db eval {SELECT count(*) FROM t1} } msg] $msg]
if {$res!="0 1000000"} {
db eval {
PRAGMA mmap_size = 1000000000; -- 1GB
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000_000
)
INSERT INTO t1 SELECT i, hex(randomblob(8)) FROM s;
}
}
# Create an FTS5 table with 1,000,000 entries. Each row contains a single
# column containing a document of 100 terms chosen pseudo-randomly from
# a vocabularly of 2000.
set res [list [catch { db eval {SELECT count(*) FROM f1} } msg] $msg]
if {$res!="0 1000000"} {
set nVocab 2000
set nTerm 100
db eval {
BEGIN;
DROP TABLE IF EXISTS vocab1;
CREATE TABLE vocab1(w);
}
for {set ii 0} {$ii<$nVocab} {incr ii} {
set word [format %06x [expr {int(abs(rand()) * 0xFFFFFF)}]]
db eval { INSERT INTO vocab1 VALUES($word) }
lappend lVocab $word
}
db func doc doc
proc doc {} {
for {set ii 0} {$ii<$::nTerm} {incr ii} {
lappend ret [lindex $::lVocab [expr int(abs(rand())*$::nVocab)]]
}
set ret
}
db eval {
DROP TABLE IF EXISTS f1;
CREATE VIRTUAL TABLE f1 USING fts5(x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000_000
)
INSERT INTO f1(rowid, x) SELECT i, doc() FROM s;
COMMIT;
}
} else {
set lVocab [db eval { SELECT * FROM vocab1 }]
set nVocab [llength $lVocab]
}
proc rowid_query {n} {
set rowid 654
for {set ii 0} {$ii<$n} {incr ii} {
db eval { SELECT b FROM t1 WHERE a = $rowid }
set rowid [expr {($rowid + 7717) % 1000000}]
}
}
proc rowid_query_fts {n} {
set rowid 654
for {set ii 0} {$ii<$n} {incr ii} {
db eval { SELECT * FROM f1 WHERE rowid = $rowid }
set rowid [expr {($rowid + 7717) % 1000000}]
}
}
proc match_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match [lrange $::lVocab $idx $idx+1]
db eval { SELECT * FROM f1($match) }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc prefix_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match "[lindex $::lVocab $idx]*"
db eval { SELECT * FROM f1($match) }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc match_rowid_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match "[lindex $::lVocab $idx]"
db eval { SELECT * FROM f1($match) WHERE rowid=500000 }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc prefix_rowid_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match "[lindex $::lVocab $idx]*"
db eval { SELECT * FROM f1($match) WHERE rowid=500000 }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc mytime {cmd div} {
set tm [time $cmd]
expr {[lindex $tm 0] / $div}
}
#set us [mytime { match_rowid_query_fts 1000 } 1000]
#puts "1000 match/rowid queries on fts5 table: ${us} per query"
set us [mytime { prefix_rowid_query_fts 1000 } 1000]
puts "1000 prefix/rowid queries on fts5 table: ${us} per query"
set us [mytime { match_query_fts 10 } 10]
puts "10 match queries on fts5 table: ${us} per query"
set us [mytime { prefix_query_fts 10 } 10]
puts "10 prefix queries on fts5 table: ${us} per query"
set us [mytime { prefix_rowid_query_fts 1000 } 1000]
puts "1000 prefix/rowid queries on fts5 table: ${us} per query"
set us [mytime { rowid_query 10000 } 10000]
puts "10000 by-rowid queries on normal table: ${us} per query"
set us [mytime { rowid_query_fts 10000 } 10000]
puts "10000 by-rowid queries on fts5 table: ${us} per query"
+1 -1
View File
@@ -319,7 +319,7 @@ static int intckGetToken(const char *z){
char c = z[0];
int iRet = 1;
if( c=='\'' || c=='"' || c=='`' ){
while( z[iRet] ){
while( 1 ){
if( z[iRet]==c ){
iRet++;
if( z[iRet]!=c ) break;
+2 -2
View File
@@ -847,7 +847,7 @@ static int amatchConnect(
(void)pAux;
*ppVtab = 0;
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
rc = SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -928,7 +928,7 @@ amatchConnectError:
static int amatchOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
amatch_vtab *p = (amatch_vtab*)pVTab;
amatch_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
+3 -3
View File
@@ -232,7 +232,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
cBuf = sqlite3_malloc64(nc);
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
@@ -254,7 +254,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_zeroblob(context, 0);
break;
}
bBuf = sqlite3_malloc64(nb);
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
@@ -275,7 +275,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_base64_init
int sqlite3_base_init
#else
static int sqlite3_base64_init
#endif
+9 -9
View File
@@ -262,7 +262,7 @@ static int allBase85( char *p, int len ){
#ifndef BASE85_STANDALONE
#ifndef OMIT_BASE85_CHECKER
# ifndef OMIT_BASE85_CHECKER
/* This function does the work for the SQLite is_base85(t) UDF. */
static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
assert(na==1);
@@ -282,7 +282,7 @@ static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
return;
}
}
#endif
# endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
@@ -309,7 +309,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
cBuf = sqlite3_malloc64(nc);
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
@@ -331,7 +331,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_zeroblob(context, 0);
break;
}
bBuf = sqlite3_malloc64(nb);
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
@@ -352,14 +352,14 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_base85_init
int sqlite3_base_init
#else
static int sqlite3_base85_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErr;
#ifndef OMIT_BASE85_CHECKER
# ifndef OMIT_BASE85_CHECKER
{
int rc = sqlite3_create_function
(db, "is_base85", 1,
@@ -367,7 +367,7 @@ static int sqlite3_base85_init
0, is_base85, 0, 0);
if( rc!=SQLITE_OK ) return rc;
}
#endif
# endif
return sqlite3_create_function
(db, "base85", 1,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
@@ -432,9 +432,9 @@ int main(int na, char *av[]){
int nc = strlen(cBuf);
size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1;
#ifndef OMIT_BASE85_CHECKER
# ifndef OMIT_BASE85_CHECKER
b85Clean &= allBase85( cBuf, nc );
#endif
# endif
}
break;
default:
+1 -5
View File
@@ -306,10 +306,6 @@ static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
nEntry *= (nCell+1);
if( aData[0]==10 || aData[0]==13 ) break;
nPage *= (nCell+1);
if( 14+2*(nCell/2)>=pgsz ){
rc = SQLITE_CORRUPT;
break;
}
if( nCell<=1 ){
pgno = get_uint32(aData+8);
}else{
@@ -343,7 +339,7 @@ static int binfoColumn(
sqlite3 *db = sqlite3_context_db_handle(ctx);
int rc = binfoCompute(db, pgno, pCsr);
if( rc ){
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errstr(rc));
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
return SQLITE_ERROR;
}
}
+3 -3
View File
@@ -516,7 +516,7 @@ static int closureConnect(
(void)pAux;
*ppVtab = 0;
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
rc = SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -579,7 +579,7 @@ closureConnectError:
static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
closure_vtab *p = (closure_vtab*)pVTab;
closure_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
@@ -638,7 +638,7 @@ static int closureInsertNode(
sqlite3_int64 id, /* The node ID */
int iGeneration /* The generation number for this node */
){
closure_avl *pNew = sqlite3_malloc64( sizeof(*pNew) );
closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->id = id;
+15 -27
View File
@@ -132,7 +132,7 @@ static int completionConnect(
" phase INT HIDDEN" /* Used for debugging only */
")");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -154,7 +154,7 @@ static int completionDisconnect(sqlite3_vtab *pVtab){
*/
static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
completion_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((completion_vtab*)p)->db;
@@ -199,7 +199,6 @@ static int completionNext(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
int eNextPhase = 0; /* Next phase to try if current phase reaches end */
int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
int rc;
pCur->iRowid++;
while( pCur->ePhase!=COMPLETION_EOF ){
switch( pCur->ePhase ){
@@ -225,27 +224,22 @@ static int completionNext(sqlite3_vtab_cursor *cur){
case COMPLETION_TABLES: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
sqlite3_str* pStr = sqlite3_str_new(pCur->db);
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
sqlite3_str_appendf(pStr,
"%s"
zSql = sqlite3_mprintf(
"%z%s"
"SELECT name FROM \"%w\".sqlite_schema",
zSep, zDb
zSql, zSep, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
rc = sqlite3_finalize(pS2);
zSql = sqlite3_str_finish(pStr);
if( zSql==0 ) return SQLITE_NOMEM;
if( rc==SQLITE_OK ){
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
}
sqlite3_finalize(pS2);
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
sqlite3_free(zSql);
if( rc ) return rc;
}
iCol = 0;
eNextPhase = COMPLETION_COLUMNS;
@@ -254,29 +248,24 @@ static int completionNext(sqlite3_vtab_cursor *cur){
case COMPLETION_COLUMNS: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
sqlite3_str *pStr = sqlite3_str_new(pCur->db);
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
sqlite3_str_appendf(pStr,
"%s"
zSql = sqlite3_mprintf(
"%z%s"
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
" WHERE sm.type='table'",
zSep, zDb, zDb
zSql, zSep, zDb, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
rc = sqlite3_finalize(pS2);
zSql = sqlite3_str_finish(pStr);
if( zSql==0 ) return SQLITE_NOMEM;
if( rc==SQLITE_OK ){
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
}
sqlite3_finalize(pS2);
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
sqlite3_free(zSql);
if( rc ) return rc;
}
iCol = 0;
eNextPhase = COMPLETION_EOF;
@@ -293,10 +282,9 @@ static int completionNext(sqlite3_vtab_cursor *cur){
pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
}else{
/* When all rows are finished, advance to the next phase */
rc = sqlite3_finalize(pCur->pStmt);
sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0;
pCur->ePhase = eNextPhase;
if( rc ) return rc;
continue;
}
}
+2 -2
View File
@@ -59,7 +59,7 @@ static void compressFunc(
pIn = sqlite3_value_blob(argv[0]);
nIn = sqlite3_value_bytes(argv[0]);
nOut = 13 + nIn + (nIn+999)/1000;
pOut = sqlite3_malloc64( nOut+5 );
pOut = sqlite3_malloc( nOut+5 );
for(i=4; i>=0; i--){
x[i] = (nIn >> (7*(4-i)))&0x7f;
}
@@ -98,7 +98,7 @@ static void uncompressFunc(
nOut = (nOut<<7) | (pIn[i]&0x7f);
if( (pIn[i]&0x80)!=0 ){ i++; break; }
}
pOut = sqlite3_malloc64( nOut+1 );
pOut = sqlite3_malloc( nOut+1 );
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
if( rc==Z_OK ){
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
+15 -18
View File
@@ -24,8 +24,8 @@
** schema= parameter, like this:
**
** CREATE VIRTUAL TABLE temp.csv2 USING csv(
** filename = '../http.log',
** schema = 'CREATE TABLE x(date,ipaddr,url,referrer,userAgent)'
** filename = "../http.log",
** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)"
** );
**
** Instead of specifying a file, the text of the CSV can be loaded using
@@ -62,10 +62,6 @@ SQLITE_EXTENSION_INIT1
# define CSV_NOINLINE
#endif
#ifndef SQLITEINT_H
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
#endif
/* Max size of the error message in a CsvReader */
#define CSV_MXERR 200
@@ -78,9 +74,9 @@ typedef struct CsvReader CsvReader;
struct CsvReader {
FILE *in; /* Read the CSV text from this input stream */
char *z; /* Accumulated text for a field */
i64 n; /* Number of bytes in z */
i64 nAlloc; /* Space allocated for z[] */
i64 nLine; /* Current line number */
int n; /* Number of bytes in z */
int nAlloc; /* Space allocated for z[] */
int nLine; /* Current line number */
int bNotFirst; /* True if prior text has been seen */
int cTerm; /* Character that terminated the most recent field */
size_t iIn; /* Next unread character in the input buffer */
@@ -129,7 +125,7 @@ static int csv_reader_open(
const char *zData /* ... or use this data */
){
if( zFilename ){
p->zIn = sqlite3_malloc64( CSV_INBUFSZ );
p->zIn = sqlite3_malloc( CSV_INBUFSZ );
if( p->zIn==0 ){
csv_errmsg(p, "out of memory");
return 1;
@@ -178,7 +174,7 @@ static int csv_getc(CsvReader *p){
** Return 0 on success and non-zero if there is an OOM error */
static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){
char *zNew;
i64 nNew = p->nAlloc*2 + 100;
int nNew = p->nAlloc*2 + 100;
zNew = sqlite3_realloc64(p->z, nNew);
if( zNew ){
p->z = zNew;
@@ -222,7 +218,7 @@ static char *csv_read_one_field(CsvReader *p){
}
if( c=='"' ){
int pc, ppc;
i64 startLine = p->nLine;
int startLine = p->nLine;
pc = ppc = 0;
while( 1 ){
c = csv_getc(p);
@@ -326,7 +322,7 @@ typedef struct CsvCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
CsvReader rdr; /* The CsvReader object */
char **azVal; /* Value of the current row */
i64 *aLen; /* Length of each entry */
int *aLen; /* Length of each entry */
sqlite3_int64 iRowid; /* The current rowid. Negative for EOF */
} CsvCursor;
@@ -498,7 +494,7 @@ static int csvtabConnect(
CsvTable *pNew = 0; /* The CsvTable object to construct */
int bHeader = -1; /* header= flags. -1 means not seen yet */
int rc = SQLITE_OK; /* Result code from this routine */
u64 i, j; /* Loop counters */
int i, j; /* Loop counters */
#ifdef SQLITE_TEST
int tstFlags = 0; /* Value for testflags=N parameter */
#endif
@@ -514,10 +510,11 @@ static int csvtabConnect(
# define CSV_DATA (azPValue[1])
# define CSV_SCHEMA (azPValue[2])
assert( sizeof(azPValue)==sizeof(azParam) );
memset(&sRdr, 0, sizeof(sRdr));
memset(azPValue, 0, sizeof(azPValue));
for(i=3; i<(u64)argc; i++){
for(i=3; i<argc; i++){
const char *z = argv[i];
const char *zValue;
for(j=0; j<sizeof(azParam)/sizeof(azParam[0]); j++){
@@ -564,7 +561,7 @@ static int csvtabConnect(
){
goto csvtab_connect_error;
}
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) goto csvtab_connect_oom;
memset(pNew, 0, sizeof(*pNew));
@@ -710,12 +707,12 @@ static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
CsvTable *pTab = (CsvTable*)p;
CsvCursor *pCur;
size_t nByte;
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(i64))*pTab->nCol;
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol;
pCur = sqlite3_malloc64( nByte );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, nByte);
pCur->azVal = (char**)&pCur[1];
pCur->aLen = (i64*)&pCur->azVal[pTab->nCol];
pCur->aLen = (int*)&pCur->azVal[pTab->nCol];
*ppCursor = &pCur->base;
if( csv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData) ){
csv_xfer_error(pTab, &pCur->rdr);
+15 -65
View File
@@ -31,10 +31,6 @@ SQLITE_EXTENSION_INIT1
#define IsSpace(X) isspace((unsigned char)X)
#endif
#ifndef SQLITE_DECIMAL_MAX_DIGIT
# define SQLITE_DECIMAL_MAX_DIGIT 10000000
#endif
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
@@ -73,8 +69,7 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
int i;
int iExp = 0;
if( zIn==0 ) goto new_from_text_failed;
p = sqlite3_malloc64( sizeof(*p) );
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ) goto new_from_text_failed;
p->sign = 0;
p->oom = 0;
@@ -133,10 +128,9 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( a==0 ) goto new_from_text_failed;
p->a = a;
if( p->a==0 ) goto new_from_text_failed;
memset(p->a+p->nDigit, 0, iExp);
p->nDigit += iExp;
}
@@ -154,10 +148,9 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( a==0 ) goto new_from_text_failed;
p->a = a;
if( p->a==0 ) goto new_from_text_failed;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0, iExp);
p->nDigit += iExp;
@@ -168,7 +161,6 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
for(i=0; i<p->nDigit && p->a[i]==0; i++){}
if( i>=p->nDigit ) p->sign = 0;
}
if( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed;
return p;
new_from_text_failed:
@@ -299,38 +291,12 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
sqlite3_result_text(pCtx, z, i, sqlite3_free);
}
/*
** Round a decimal value to N significant digits. N must be positive.
*/
static void decimal_round(Decimal *p, int N){
int i;
int nZero;
if( N<1 ) return;
if( p==0 ) return;
if( p->nDigit<=N ) return;
for(nZero=0; nZero<p->nDigit && p->a[nZero]==0; nZero++){}
N += nZero;
if( p->nDigit<=N ) return;
if( p->a[N]>4 ){
p->a[N-1]++;
for(i=N-1; i>0 && p->a[i]>9; i--){
p->a[i] = 0;
p->a[i-1]++;
}
if( p->a[0]>9 ){
p->a[0] = 1;
p->nFrac--;
}
}
memset(&p->a[N], 0, p->nDigit - N);
}
/*
** Make the given Decimal the result in an format similar to '%+#e'.
** In other words, show exponential notation with leading and trailing
** zeros omitted.
*/
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
char *z; /* The output buffer */
int i; /* Loop counter */
int nZero; /* Number of leading zeros */
@@ -348,8 +314,7 @@ static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
sqlite3_result_null(pCtx);
return;
}
if( N<1 ) N = 0;
for(nDigit=p->nDigit; nDigit>N && p->a[nDigit-1]==0; nDigit--){}
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
@@ -465,18 +430,15 @@ cmp_done:
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
int nAddSig;
int nAddFrac;
signed char *a;
if( p==0 ) return;
nAddFrac = nFrac - p->nFrac;
nAddSig = (nDigit - p->nDigit) - nAddFrac;
if( nAddFrac==0 && nAddSig==0 ) return;
if( nDigit+1>SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1; return; }
a = sqlite3_realloc64(p->a, nDigit+1);
if( a==0 ){
p->a = sqlite3_realloc64(p->a, nDigit+1);
if( p->a==0 ){
p->oom = 1;
return;
}
p->a = a;
if( nAddSig ){
memmove(p->a+nAddSig, p->a, p->nDigit);
memset(p->a, 0, nAddSig);
@@ -571,18 +533,14 @@ static void decimalMul(Decimal *pA, Decimal *pB){
signed char *acc = 0;
int i, j, k;
int minFrac;
sqlite3_int64 sumDigit;
if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
sumDigit = pA->nDigit;
sumDigit += pB->nDigit;
sumDigit += 2;
if( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1; return; }
acc = sqlite3_malloc64( sumDigit );
acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
(sqlite3_int64)pB->nDigit + 2 );
if( acc==0 ){
pA->oom = 1;
goto mul_end;
@@ -719,16 +677,10 @@ static void decimalFunc(
sqlite3_value **argv
){
Decimal *p = decimal_new(context, argv[0], 0);
int N;
if( argc==2 ){
N = sqlite3_value_int(argv[1]);
if( N>0 ) decimal_round(p, N);
}else{
N = 0;
}
UNUSED_PARAMETER(argc);
if( p ){
if( sqlite3_user_data(context)!=0 ){
decimal_result_sci(context, p, N);
decimal_result_sci(context, p);
}else{
decimal_result(context, p);
}
@@ -814,7 +766,7 @@ static void decimalSumStep(
if( p==0 ) return;
if( !p->isInit ){
p->isInit = 1;
p->a = sqlite3_malloc64(2);
p->a = sqlite3_malloc(2);
if( p->a==0 ){
p->oom = 1;
}else{
@@ -898,7 +850,7 @@ static void decimalPow2Func(
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0]));
decimal_result_sci(context, pA, 0);
decimal_result_sci(context, pA);
decimal_free(pA);
}
}
@@ -919,9 +871,7 @@ int sqlite3_decimal_init(
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "decimal", 1, 0, decimalFunc },
{ "decimal", 2, 0, decimalFunc },
{ "decimal_exp", 1, 1, decimalFunc },
{ "decimal_exp", 2, 1, decimalFunc },
{ "decimal_cmp", 2, 0, decimalCmpFunc },
{ "decimal_add", 2, 0, decimalAddFunc },
{ "decimal_sub", 2, 0, decimalSubFunc },
+2 -2
View File
@@ -92,7 +92,7 @@ static int explainConnect(
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(addr,opcode,p1,p2,p3,p4,p5,comment,sql HIDDEN)");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -114,7 +114,7 @@ static int explainDisconnect(sqlite3_vtab *pVtab){
*/
static int explainOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
explain_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((explain_vtab*)p)->db;
+66 -176
View File
@@ -73,6 +73,11 @@
** $path is a relative path, then $path is interpreted relative to $dir.
** And the paths returned in the "name" column of the table are also
** relative to directory $dir.
**
** Notes on building this extension for Windows:
** Unless linked statically with the SQLite library, a preprocessor
** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone
** DLL form of this extension for WIN32. See its use below for details.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -89,16 +94,12 @@ SQLITE_EXTENSION_INIT1
# include <utime.h>
# include <sys/time.h>
# define STRUCT_STAT struct stat
# include <limits.h>
# include <stdlib.h>
#else
# include "windirent.h"
# include <direct.h>
# define STRUCT_STAT struct _stat
# define chmod(path,mode) fileio_chmod(path,mode)
# define mkdir(path,mode) fileio_mkdir(path)
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
#endif
#include <time.h>
#include <errno.h>
@@ -130,9 +131,12 @@ SQLITE_EXTENSION_INIT1
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_chmod(const char *zPath, int pmode){
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return -1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wchmod(b1, pmode);
sqlite3_free(b1);
return rc;
@@ -144,9 +148,12 @@ static int fileio_chmod(const char *zPath, int pmode){
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_mkdir(const char *zPath){
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return -1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wmkdir(b1);
sqlite3_free(b1);
return rc;
@@ -259,7 +266,50 @@ static sqlite3_uint64 fileTimeToUnixTime(
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
#endif /* _WIN32 */
#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
# /* To allow a standalone DLL, use this next replacement function: */
# undef sqlite3_win32_utf8_to_unicode
# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
#
LPWSTR utf8_to_utf16(const char *z){
int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
return rv;
sqlite3_free(rv);
return 0;
}
#endif
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
** appears to return these values as local times.
*/
static void statTimesToUtc(
const char *zPath,
STRUCT_STAT *pStatBuf
){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
if( zUnicodeName ){
memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
hFindFile = FindFirstFileW(zUnicodeName, &fd);
if( hFindFile!=NULL ){
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
FindClose(hFindFile);
}
sqlite3_free(zUnicodeName);
}
}
#endif
/*
** This function is used in place of stat(). On Windows, special handling
@@ -271,23 +321,14 @@ static int fileStat(
STRUCT_STAT *pStatBuf
){
#if defined(_WIN32)
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return 1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wstat(b1, pStatBuf);
if( rc==0 ){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
hFindFile = FindFirstFileW(b1, &fd);
if( hFindFile!=NULL ){
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
FindClose(hFindFile);
}
}
sqlite3_free(b1);
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
return rc;
#else
return stat(zPath, pStatBuf);
@@ -418,6 +459,7 @@ static int writeFile(
if( mtime>=0 ){
#if defined(_WIN32)
#if !SQLITE_OS_WINRT
/* Windows */
FILETIME lastAccess;
FILETIME lastWrite;
@@ -448,6 +490,7 @@ static int writeFile(
}else{
return 1;
}
#endif
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
/* Recent unix */
struct timespec times[2];
@@ -613,7 +656,7 @@ static int fsdirConnect(
(void)pzErr;
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
if( rc==SQLITE_OK ){
pNew = (fsdir_tab*)sqlite3_malloc64( sizeof(*pNew) );
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
@@ -636,7 +679,7 @@ static int fsdirDisconnect(sqlite3_vtab *pVtab){
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
fsdir_cursor *pCur;
(void)p;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->iLvl = -1;
@@ -1051,154 +1094,6 @@ static int fsdirRegister(sqlite3 *db){
# define fsdirRegister(x) SQLITE_OK
#endif
/*
** This version of realpath() works on any system. The string
** returned is held in memory allocated using sqlite3_malloc64().
** The caller is responsible for calling sqlite3_free().
*/
static char *portable_realpath(const char *zPath){
#if !defined(_WIN32) /* BEGIN unix */
char *zOut = 0; /* Result */
char *z; /* Temporary buffer */
#if defined(PATH_MAX)
char zBuf[PATH_MAX+1]; /* Space for the temporary buffer */
#endif
if( zPath==0 ) return 0;
#if defined(PATH_MAX)
z = realpath(zPath, zBuf);
if( z ){
zOut = sqlite3_mprintf("%s", zBuf);
}
#endif /* defined(PATH_MAX) */
if( zOut==0 ){
/* Try POSIX.1-2008 malloc behavior */
z = realpath(zPath, NULL);
if( z ){
zOut = sqlite3_mprintf("%s", z);
free(z);
}
}
return zOut;
#else /* End UNIX, Begin WINDOWS */
wchar_t *zPath16; /* UTF16 translation of zPath */
char *zOut = 0; /* Result */
wchar_t *z = 0; /* Temporary buffer */
if( zPath==0 ) return 0;
zPath16 = sqlite3_win32_utf8_to_unicode(zPath);
if( zPath16==0 ) return 0;
z = _wfullpath(NULL, zPath16, 0);
sqlite3_free(zPath16);
if( z ){
zOut = sqlite3_win32_unicode_to_utf8(z);
free(z);
}
return zOut;
#endif /* End WINDOWS, Begin common code */
}
/*
** SQL function: realpath(X)
**
** Try to convert file or pathname X into its real, absolute pathname.
** Return NULL if unable.
**
** The file or directory X is not required to exist. The answer is formed
** by calling system realpath() on the prefix of X that does exist and
** appending the tail of X that does not (yet) exist.
*/
static void realpathFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zPath; /* Original input path */
char *zCopy; /* An editable copy of zPath */
char *zOut; /* The result */
char cSep = 0; /* Separator turned into \000 */
size_t len; /* Prefix length before cSep */
#ifdef _WIN32
const int isWin = 1;
#else
const int isWin = 0;
#endif
(void)argc;
zPath = (const char*)sqlite3_value_text(argv[0]);
if( zPath==0 ) return;
if( zPath[0]==0 ) zPath = ".";
zCopy = sqlite3_mprintf("%s",zPath);
len = strlen(zCopy);
while( len>1 && (zCopy[len-1]=='/' || (isWin && zCopy[len-1]=='\\')) ){
len--;
}
zCopy[len] = 0;
while( 1 /*exit-by-break*/ ){
zOut = portable_realpath(zCopy);
zCopy[len] = cSep;
if( zOut ){
if( cSep ){
zOut = sqlite3_mprintf("%z%s",zOut,&zCopy[len]);
}
break;
}else{
size_t i = len-1;
while( i>0 ){
if( zCopy[i]=='/' || (isWin && zCopy[i]=='\\') ) break;
i--;
}
if( i<=0 ){
if( zCopy[0]=='/' ){
zOut = zCopy;
zCopy = 0;
}else if( (zOut = portable_realpath("."))!=0 ){
zOut = sqlite3_mprintf("%z/%s", zOut, zCopy);
}
break;
}
cSep = zCopy[i];
zCopy[i] = 0;
len = i;
}
}
sqlite3_free(zCopy);
if( zOut ){
/* Simplify any "/./" or "/../" that might have snuck into the
** pathname due to appending of zCopy. We only have to consider
** unix "/" separators, because the _wfilepath() system call on
** Windows will have already done this simplification for us. */
size_t i, j, n;
n = strlen(zOut);
for(i=j=0; i<n; i++){
if( zOut[i]=='/' ){
if( zOut[i+1]=='/' ) continue;
if( zOut[i+1]=='.' && i+2<n && zOut[i+2]=='/' ){
i += 1;
continue;
}
if( zOut[i+1]=='.' && i+3<n && zOut[i+2]=='.' && zOut[i+3]=='/' ){
while( j>0 && zOut[j-1]!='/' ){ j--; }
if( j>0 ){ j--; }
i += 2;
continue;
}
}
zOut[j++] = zOut[i];
}
zOut[j] = 0;
/* Return the result */
sqlite3_result_text(context, zOut, -1, sqlite3_free);
}
}
#ifdef _WIN32
__declspec(dllexport)
#endif
@@ -1225,10 +1120,5 @@ int sqlite3_fileio_init(
if( rc==SQLITE_OK ){
rc = fsdirRegister(db);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "realpath", 1,
SQLITE_UTF8, 0,
realpathFunc, 0, 0);
}
return rc;
}
+5 -7
View File
@@ -38,11 +38,9 @@ SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
/*
** The "u32" type must be an unsigned 32-bit integer. "u64" is
** an unsigned 64-bit integer.
** The "u32" type must be an unsigned 32-bit integer. Adjust this
*/
typedef unsigned int u32;
typedef sqlite3_uint64 u64;
/*
** Must be a 16-bit value
@@ -543,8 +541,8 @@ static int delta_apply(
int lenDelta, /* Length of the delta */
char *zOut /* Write the output into this preallocated buffer */
){
sqlite3_uint64 limit;
sqlite3_uint64 total = 0;
unsigned int limit;
unsigned int total = 0;
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
char *zOrigOut = zOut;
#endif
@@ -572,7 +570,7 @@ static int delta_apply(
/* ERROR: copy exceeds output file size */
return -1;
}
if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
if( ofst+cnt > lenSrc ){
/* ERROR: copy extends past end of input */
return -1;
}
@@ -843,7 +841,7 @@ static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
*/
static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
deltaparsevtab_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+3 -3
View File
@@ -556,7 +556,7 @@ static int fuzzerConnect(
static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
fuzzer_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
@@ -617,12 +617,12 @@ static int fuzzerRender(
int *pnBuf /* Size of the buffer */
){
const fuzzer_rule *pRule = pStem->pRule;
sqlite3_int64 n; /* Size of output term without nul-term */
int n; /* Size of output term without nul-term */
char *z; /* Buffer to assemble output term in */
n = pStem->nBasis + pRule->nTo - pRule->nFrom;
if( (*pnBuf)<n+1 ){
(*pzBuf) = sqlite3_realloc64((*pzBuf), n+100);
(*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
if( (*pzBuf)==0 ) return SQLITE_NOMEM;
(*pnBuf) = n+100;
}
+1 -35
View File
@@ -179,9 +179,9 @@ static void ieee754func(
}
if( m<0 ){
if( m<(-9223372036854775807LL) ) return;
isNeg = 1;
m = -m;
if( m<0 ) return;
}else if( m==0 && e>-1000 && e<1000 ){
sqlite3_result_double(context, 0.0);
return;
@@ -259,38 +259,6 @@ static void ieee754func_to_blob(
}
}
/*
** Functions to convert between 64-bit integers and floats.
**
** The bit patterns are copied. The numeric values are different.
*/
static void ieee754func_from_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
double r;
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(context, r);
}
}
static void ieee754func_to_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[0]);
sqlite3_uint64 v;
memcpy(&v, &r, sizeof(v));
sqlite3_result_int64(context, v);
}
}
/*
** SQL Function: ieee754_inc(r,N)
**
@@ -343,8 +311,6 @@ int sqlite3_ieee_init(
{ "ieee754_exponent", 1, 2, ieee754func },
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
{ "ieee754_to_int", 1, 0, ieee754func_to_int },
{ "ieee754_from_int", 1, 0, ieee754func_from_int },
{ "ieee754_inc", 2, 0, ieee754inc },
};
unsigned int i;
+2 -2
View File
@@ -85,7 +85,7 @@ static int memstatConnect(
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(name,schema,value,hiwtr)");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -107,7 +107,7 @@ static int memstatDisconnect(sqlite3_vtab *pVtab){
*/
static int memstatOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
memstat_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((memstat_vtab*)p)->db;
+2 -2
View File
@@ -75,7 +75,7 @@ static int prefixesConnect(
"CREATE TABLE prefixes(prefix TEXT, original_string TEXT HIDDEN)"
);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -98,7 +98,7 @@ static int prefixesDisconnect(sqlite3_vtab *pVtab){
*/
static int prefixesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
prefixes_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+2 -2
View File
@@ -152,7 +152,7 @@ static int qpvtabConnect(
#define QPVTAB_FLAGS 11
#define QPVTAB_NONE 12
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -174,7 +174,7 @@ static int qpvtabDisconnect(sqlite3_vtab *pVtab){
*/
static int qpvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
qpvtab_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+9 -23
View File
@@ -32,7 +32,7 @@
** ^X X occurring at the beginning of the string
** X$ X occurring at the end of the string
** . Match any single character
** \c Character c where c is one of \{}()[]|*+?-.
** \c Character c where c is one of \{}()[]|*+?.
** \c C-language escapes for c in afnrtv. ex: \t or \n
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
** \xXX Where XX is exactly 2 hex digits, unicode value XX
@@ -417,7 +417,7 @@ static int re_hex(int c, int *pV){
** return its interpretation.
*/
static unsigned re_esc_char(ReCompiled *p){
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-";
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
static const char zTrans[] = "\a\f\n\r\t\v";
int i, v = 0;
char c;
@@ -676,7 +676,7 @@ static const char *re_compile(
int i, j;
*ppRe = 0;
pRe = sqlite3_malloc64( sizeof(*pRe) );
pRe = sqlite3_malloc( sizeof(*pRe) );
if( pRe==0 ){
return "out of memory";
}
@@ -740,18 +740,11 @@ static const char *re_compile(
}
/*
** The value of LIMIT_MAX_PATTERN_LENGTH.
** Compute a reasonable limit on the length of the REGEXP NFA.
*/
static int re_maxlen(sqlite3_context *context){
sqlite3 *db = sqlite3_context_db_handle(context);
return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1);
}
/*
** Maximum NFA size given a maximum pattern length.
*/
static int re_maxnfa(int mxlen){
return 75+mxlen/2;
return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2;
}
/*
@@ -777,17 +770,10 @@ static void re_sql_func(
(void)argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
int mxLen = re_maxlen(context);
int nPattern;
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
nPattern = sqlite3_value_bytes(argv[0]);
if( nPattern>mxLen ){
zErr = "REGEXP pattern too big";
}else{
zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen),
sqlite3_user_data(context)!=0);
}
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
@@ -828,6 +814,7 @@ static void re_bytecode_func(
int i;
int n;
char *z;
(void)argc;
static const char *ReOpName[] = {
"EOF",
"MATCH",
@@ -850,10 +837,9 @@ static void re_bytecode_func(
"ATSTART",
};
(void)argc;
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)),
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
+1 -1
View File
@@ -92,7 +92,7 @@ static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){
static u8 *scrubBackupAllocPage(ScrubState *p){
u8 *pPage;
if( p->rcErr ) return 0;
pPage = sqlite3_malloc64( p->szPage );
pPage = sqlite3_malloc( p->szPage );
if( pPage==0 ) p->rcErr = SQLITE_NOMEM;
return pPage;
}
+2 -2
View File
@@ -239,7 +239,7 @@ static int seriesConnect(
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
@@ -261,7 +261,7 @@ static int seriesDisconnect(sqlite3_vtab *pVtab){
static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
series_cursor *pCur;
(void)pUnused;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+7 -16
View File
@@ -230,16 +230,13 @@ static void hash_finish(
*****************************************************************************/
/*
** Two SQL functions: sha1(X) and sha1b(X).
** Implementation of the sha1(X) function.
**
** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
** of the argument X. If X is a BLOB, it is hashed as is. For all other
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
** argument X. If X is a BLOB, it is hashed as is. For all other
** types of input, X is converted into a UTF-8 string and the string
** is hashed without the trailing 0x00 terminator. The hash of a NULL
** is hash without the trailing 0x00 terminator. The hash of a NULL
** value is NULL.
**
** sha1b(X) is the same except that it returns a 20-byte BLOB containing
** the binary hash instead of a hexadecimal string.
*/
static void sha1Func(
sqlite3_context *context,
@@ -249,27 +246,22 @@ static void sha1Func(
SHA1Context cx;
int eType = sqlite3_value_type(argv[0]);
int nByte = sqlite3_value_bytes(argv[0]);
const unsigned char *pData;
char zOut[44];
assert( argc==1 );
if( eType==SQLITE_NULL ) return;
hash_init(&cx);
if( eType==SQLITE_BLOB ){
pData = (const unsigned char*)sqlite3_value_blob(argv[0]);
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
}else{
pData = (const unsigned char*)sqlite3_value_text(argv[0]);
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
}
if( pData==0 ) return;
hash_step(&cx, pData, nByte);
if( sqlite3_user_data(context)!=0 ){
/* sha1b() - binary result */
hash_finish(&cx, zOut, 1);
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
}else{
/* sha1() - hexadecimal text result */
hash_finish(&cx, zOut, 0);
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
}
}
@@ -323,7 +315,6 @@ static void sha1QueryFunc(
}
nCol = sqlite3_column_count(pStmt);
z = sqlite3_sql(pStmt);
if( z==0 ) z = "";
n = (int)strlen(z);
hash_step_vformat(&cx,"S%d:",n);
hash_step(&cx,(unsigned char*)z,n);
+3 -3
View File
@@ -46,7 +46,7 @@ static void sqlarCompressFunc(
uLongf nOut = compressBound(nData);
Bytef *pOut;
pOut = (Bytef*)sqlite3_malloc64(nOut);
pOut = (Bytef*)sqlite3_malloc(nOut);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -84,14 +84,14 @@ static void sqlarUncompressFunc(
sqlite3_int64 sz;
assert( argc==2 );
sz = sqlite3_value_int64(argv[1]);
sz = sqlite3_value_int(argv[1]);
if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
sqlite3_result_value(context, argv[0]);
}else{
uLongf szf = sz;
const Bytef *pData= sqlite3_value_blob(argv[0]);
Bytef *pOut = sqlite3_malloc64(sz);
Bytef *pOut = sqlite3_malloc(sz);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
}else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
+6 -6
View File
@@ -101,8 +101,8 @@ FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
sz1 = (int)strlen(zFilename);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
b1[sz1] = 0;
@@ -127,8 +127,8 @@ FILE *sqlite3_popen(const char *zCommand, const char *zMode){
sz1 = (int)strlen(zCommand);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
b1[sz1] = 0;
@@ -151,7 +151,7 @@ char *sqlite3_fgets(char *buf, int sz, FILE *in){
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = sqlite3_malloc64( sz*sizeof(wchar_t) );
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nRead = 0;
@@ -226,7 +226,7 @@ int sqlite3_fputs(const char *z, FILE *out){
** to the console on Windows.
*/
int sz = (int)strlen(z);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(wchar_t) );
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
+1 -1
View File
@@ -52,7 +52,7 @@ static void stmtrandFunc(
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
if( p==0 ){
unsigned int seed;
p = sqlite3_malloc64( sizeof(*p) );
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
+2 -2
View File
@@ -101,7 +101,7 @@ static int templatevtabConnect(
#define TEMPLATEVTAB_A 0
#define TEMPLATEVTAB_B 1
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -123,7 +123,7 @@ static int templatevtabDisconnect(sqlite3_vtab *pVtab){
*/
static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
templatevtab_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
-1042
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -613,7 +613,7 @@ static int vstattabConnect(
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
@@ -633,7 +633,7 @@ static int vstattabDisconnect(sqlite3_vtab *pVtab){
*/
static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
VfsStatCursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+1 -1
View File
@@ -892,7 +892,7 @@ static int vfstraceOpen(
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
sqlite3_io_methods *pNew = sqlite3_malloc64( sizeof(*pNew) );
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
+8 -50
View File
@@ -14,8 +14,6 @@
** on stdout when its key interfaces are called. This is intended for
** interactive analysis and debugging of virtual table interfaces.
**
** HOW TO COMPILE:
**
** To build this extension as a separately loaded shared library or
** DLL, use compiler command-lines similar to the following:
**
@@ -23,7 +21,7 @@
** (mac) clang -fPIC -dynamiclib vtablog.c -o vtablog.dylib
** (windows) cl vtablog.c -link -dll -out:vtablog.dll
**
** USAGE EXAMPLE:
** Usage example:
**
** .load ./vtablog
** CREATE VIRTUAL TABLE temp.log USING vtablog(
@@ -31,23 +29,6 @@
** rows=25
** );
** SELECT * FROM log;
**
** ARGUMENTS TO CREATE VIRTUAL TABLE:
**
** In "CREATE VIRTUAL TABLE temp.log AS vtablog(ARGS....)" statement, the
** ARGS argument is a list of key-value pairs that can be any of the
** following.
**
** schema=TEXT Text is a CREATE TABLE statement that defines
** the schema of the new virtual table.
**
** rows=N The table as N rows.
**
** consume_order_by=N If the left-most ORDER BY terms is ASC and
** against column N (where the leftmost column
** is #1) then set the orderByConsumed=1 flag in
** xBestIndex. Or if the left-most ORDER BY is
** DESC and against column -N, do likewise.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -68,8 +49,6 @@ struct vtablog_vtab {
char *zName; /* Table name. argv[2] of xConnect/xCreate */
int nRow; /* Number of rows in the table */
int nCursor; /* Number of cursors created */
int iConsumeOB; /* Consume the ORDER BY clause if on column N-th
** and consumeOB=N or consumeOB=(-N) and DESC */
};
/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
@@ -201,7 +180,6 @@ static int vtablogConnectCreate(
int rc;
char *zSchema = 0;
char *zNRow = 0;
char *zConsumeOB = 0;
printf("%s.%s.%s():\n", argv[1], argv[2],
isCreate ? "xCreate" : "xConnect");
@@ -225,10 +203,6 @@ static int vtablogConnectCreate(
rc = SQLITE_ERROR;
goto vtablog_end_connect;
}
if( vtablog_string_parameter(pzErr, "consume_order_by", z, &zConsumeOB) ){
rc = SQLITE_ERROR;
goto vtablog_end_connect;
}
}
if( zSchema==0 ){
zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
@@ -240,17 +214,13 @@ static int vtablogConnectCreate(
printf(" schema = '%s'\n", zSchema);
rc = sqlite3_declare_vtab(db, zSchema);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->nRow = 10;
if( zNRow ) pNew->nRow = atoi(zNRow);
printf(" nrow = %d\n", pNew->nRow);
if( zConsumeOB ) pNew->iConsumeOB = atoi(zConsumeOB);
if( pNew->iConsumeOB ){
printf(" consume_order_by = %d\n", pNew->iConsumeOB);
}
pNew->zDb = sqlite3_mprintf("%s", argv[1]);
pNew->zName = sqlite3_mprintf("%s", argv[2]);
}
@@ -258,7 +228,6 @@ static int vtablogConnectCreate(
vtablog_end_connect:
sqlite3_free(zSchema);
sqlite3_free(zNRow);
sqlite3_free(zConsumeOB);
return rc;
}
static int vtablogCreate(
@@ -313,7 +282,7 @@ static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
vtablog_cursor *pCur;
printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName,
++pTab->nCursor);
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->iCursor = pTab->nCursor;
@@ -545,27 +514,16 @@ static int vtablogBestIndex(
}
}
printf(" nOrderBy: %d\n", p->nOrderBy);
if( p->nOrderBy ){
for(i=0; i<p->nOrderBy; i++){
printf(" orderby[%d]: col=%d desc=%d\n",
i,
p->aOrderBy[i].iColumn,
p->aOrderBy[i].desc);
}
if( pTab->iConsumeOB ){
int N = p->aOrderBy[0].iColumn+1;
if( (p->aOrderBy[0].desc && N==-pTab->iConsumeOB)
|| (!p->aOrderBy[0].desc && N==pTab->iConsumeOB)
){
p->orderByConsumed = 1;
}
}
for(i=0; i<p->nOrderBy; i++){
printf(" orderby[%d]: col=%d desc=%d\n",
i,
p->aOrderBy[i].iColumn,
p->aOrderBy[i].desc);
}
p->estimatedCost = (double)500;
p->estimatedRows = 500;
printf(" idxNum=%d\n", p->idxNum);
printf(" idxStr=NULL\n");
printf(" sqlite3_vtab_distinct()=%d\n", sqlite3_vtab_distinct(p));
printf(" orderByConsumed=%d\n", p->orderByConsumed);
printf(" estimatedCost=%g\n", p->estimatedCost);
printf(" estimatedRows=%lld\n", p->estimatedRows);
+5 -5
View File
@@ -86,7 +86,7 @@ static int vtshimCreate(
}
return SQLITE_ERROR;
}
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -125,7 +125,7 @@ static int vtshimConnect(
}
return SQLITE_ERROR;
}
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -192,7 +192,7 @@ static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
int rc;
*ppCursor = 0;
if( pAux->bDisposed ) return SQLITE_ERROR;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
@@ -444,7 +444,7 @@ static int vtshimCopyModule(
){
sqlite3_module *p;
if( !pMod || !ppMod ) return SQLITE_ERROR;
p = sqlite3_malloc64( sizeof(*p) );
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ) return SQLITE_NOMEM;
memcpy(p, pMod, sizeof(*p));
*ppMod = p;
@@ -464,7 +464,7 @@ void *sqlite3_create_disposable_module(
vtshim_aux *pAux;
sqlite3_module *pMod;
int rc;
pAux = sqlite3_malloc64( sizeof(*pAux) );
pAux = sqlite3_malloc( sizeof(*pAux) );
if( pAux==0 ){
if( xDestroy ) xDestroy(pClientData);
return 0;
+2 -2
View File
@@ -47,7 +47,7 @@ static int wholenumberConnect(
char **pzErr
){
sqlite3_vtab *pNew;
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
@@ -69,7 +69,7 @@ static int wholenumberDisconnect(sqlite3_vtab *pVtab){
*/
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
wholenumber_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+31 -37
View File
@@ -393,7 +393,7 @@ static int zipfileConnect(
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
if( rc==SQLITE_OK ){
pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile);
pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, nByte+nFile);
pNew->db = db;
@@ -456,7 +456,7 @@ static int zipfileDisconnect(sqlite3_vtab *pVtab){
static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
ZipfileTab *pTab = (ZipfileTab*)p;
ZipfileCsr *pCsr;
pCsr = sqlite3_malloc64(sizeof(*pCsr));
pCsr = sqlite3_malloc(sizeof(*pCsr));
*ppCsr = (sqlite3_vtab_cursor*)pCsr;
if( pCsr==0 ){
return SQLITE_NOMEM;
@@ -539,15 +539,14 @@ static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
static int zipfileReadData(
FILE *pFile, /* Read from this file */
u8 *aRead, /* Read into this buffer */
i64 nRead, /* Number of bytes to read */
int nRead, /* Number of bytes to read */
i64 iOff, /* Offset to read from */
char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
){
size_t n;
fseek(pFile, (long)iOff, SEEK_SET);
n = fread(aRead, 1, (long)nRead, pFile);
if( n!=(size_t)nRead ){
sqlite3_free(*pzErrmsg);
n = fread(aRead, 1, nRead, pFile);
if( (int)n!=nRead ){
*pzErrmsg = sqlite3_mprintf("error in fread()");
return SQLITE_ERROR;
}
@@ -564,7 +563,7 @@ static int zipfileAppendData(
fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
if( (int)n!=nWrite ){
zipfileTableErr(pTab,"error in fwrite()");
pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
return SQLITE_ERROR;
}
pTab->szCurrent += nWrite;
@@ -705,12 +704,7 @@ static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
u8 *p = aExtra;
u8 *pEnd = &aExtra[nExtra];
/* Stop when there are less than 9 bytes left to scan in the buffer. This
** is because the timestamp field requires exactly 9 bytes - 4 bytes of
** header fields and 5 bytes of data. If there are less than 9 bytes
** remaining, either it is some other field or else the extra data
** is corrupt. Either way, do not process it. */
while( p+(2*sizeof(u16) + 1 + sizeof(u32))<=pEnd ){
while( p<pEnd ){
u16 id = zipfileRead16(p);
u16 nByte = zipfileRead16(p);
@@ -816,7 +810,6 @@ static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
** generic corruption message and return SQLITE_CORRUPT;
*/
static int zipfileCorrupt(char **pzErr){
sqlite3_free(*pzErr);
*pzErr = sqlite3_mprintf("zip archive is corrupt");
return SQLITE_CORRUPT;
}
@@ -835,7 +828,7 @@ static int zipfileCorrupt(char **pzErr){
static int zipfileGetEntry(
ZipfileTab *pTab, /* Store any error message here */
const u8 *aBlob, /* Pointer to in-memory file image */
i64 nBlob, /* Size of aBlob[] in bytes */
int nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* If aBlob==0, read from this file */
i64 iOff, /* Offset of CDS record */
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
@@ -875,14 +868,14 @@ static int zipfileGetEntry(
memset(pNew, 0, sizeof(ZipfileEntry));
rc = zipfileReadCDS(aRead, &pNew->cds);
if( rc!=SQLITE_OK ){
zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff);
*pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
}else if( aBlob==0 ){
rc = zipfileReadData(
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
);
}else{
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){
if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
@@ -907,15 +900,14 @@ static int zipfileGetEntry(
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
memset(&lfh, 0, sizeof(lfh));
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if( aBlob && pNew->cds.szCompressed ){
if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
@@ -926,7 +918,7 @@ static int zipfileGetEntry(
}
}
}else{
zipfileTableErr(pTab, "failed to read LFH at offset %d",
*pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
(int)pNew->cds.iOffset
);
}
@@ -950,7 +942,7 @@ static int zipfileNext(sqlite3_vtab_cursor *cur){
int rc = SQLITE_OK;
if( pCsr->pFile ){
i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize;
i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
zipfileEntryFree(pCsr->pCurrent);
pCsr->pCurrent = 0;
if( pCsr->iNextOff>=iEof ){
@@ -995,7 +987,7 @@ static void zipfileInflate(
int nIn, /* Size of buffer aIn[] in bytes */
int nOut /* Expected output size */
){
u8 *aRes = sqlite3_malloc64(nOut);
u8 *aRes = sqlite3_malloc(nOut);
if( aRes==0 ){
sqlite3_result_error_nomem(pCtx);
}else{
@@ -1016,7 +1008,7 @@ static void zipfileInflate(
if( err!=Z_STREAM_END ){
zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
}else{
sqlite3_result_blob(pCtx, aRes, (int)str.total_out, zipfileFree);
sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
aRes = 0;
}
}
@@ -1188,12 +1180,12 @@ static int zipfileEof(sqlite3_vtab_cursor *cur){
static int zipfileReadEOCD(
ZipfileTab *pTab, /* Return errors here */
const u8 *aBlob, /* Pointer to in-memory file image */
i64 nBlob, /* Size of aBlob[] in bytes */
int nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* Read from this file if aBlob==0 */
ZipfileEOCD *pEOCD /* Object to populate */
){
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
i64 nRead; /* Bytes to read from file */
int nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
memset(pEOCD, 0, sizeof(ZipfileEOCD));
@@ -1214,7 +1206,7 @@ static int zipfileReadEOCD(
}
if( rc==SQLITE_OK ){
i64 i;
int i;
/* Scan backwards looking for the signature bytes */
for(i=nRead-20; i>=0; i--){
@@ -1225,7 +1217,9 @@ static int zipfileReadEOCD(
}
}
if( i<0 ){
zipfileTableErr(pTab, "cannot find end of central directory record");
pTab->base.zErrMsg = sqlite3_mprintf(
"cannot find end of central directory record"
);
return SQLITE_ERROR;
}
@@ -1270,7 +1264,7 @@ static void zipfileAddEntry(
}
}
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
ZipfileEOCD eocd;
int rc;
int i;
@@ -1318,7 +1312,7 @@ static int zipfileFilter(
}else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
static const u8 aEmptyBlob = 0;
const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
i64 nBlob = sqlite3_value_bytes(argv[0]);
int nBlob = sqlite3_value_bytes(argv[0]);
assert( pTab->pFirstEntry==0 );
if( aBlob==0 ){
aBlob = &aEmptyBlob;
@@ -1392,7 +1386,7 @@ static int zipfileBestIndex(
static ZipfileEntry *zipfileNewEntry(const char *zPath){
ZipfileEntry *pNew;
pNew = sqlite3_malloc64(sizeof(ZipfileEntry));
pNew = sqlite3_malloc(sizeof(ZipfileEntry));
if( pNew ){
memset(pNew, 0, sizeof(ZipfileEntry));
pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
@@ -1516,7 +1510,7 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
assert( pTab->pWriteFd==0 );
if( pTab->zFile==0 || pTab->zFile[0]==0 ){
zipfileTableErr(pTab, "zipfile: missing filename");
pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename");
return SQLITE_ERROR;
}
@@ -1526,9 +1520,9 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
** in main-memory until the transaction is committed. */
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+");
if( pTab->pWriteFd==0 ){
zipfileTableErr(pTab,
"zipfile: failed to open file %s for writing", pTab->zFile
);
pTab->base.zErrMsg = sqlite3_mprintf(
"zipfile: failed to open file %s for writing", pTab->zFile
);
rc = SQLITE_ERROR;
}else{
fseek(pTab->pWriteFd, 0, SEEK_END);
@@ -1993,7 +1987,7 @@ struct ZipfileCtx {
ZipfileBuffer cds;
};
static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){
static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
if( pBuf->n+nByte>pBuf->nAlloc ){
u8 *aNew;
sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
@@ -2042,7 +2036,7 @@ static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
char *zName = 0; /* Path (name) of new entry */
int nName = 0; /* Size of zName in bytes */
char *zFree = 0; /* Free this before returning */
i64 nByte;
int nByte;
memset(&e, 0, sizeof(e));
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
+50 -151
View File
@@ -19,10 +19,10 @@ that can be incorporated and reused by other applications.
## 1.0 Overview Of Operation
Suppose variable `sqlite3_stmt *pStmt` is a pointer to an SQLite
prepared statement that has been reset and bound and is ready to run.
Then to format the output from this prepared statement, use code
similar to the following:
Suppose `pStmt` is a pointer to an SQLite prepared statement
(a pointer to an `sqlite3_stmt` object) that has been reset and
bound and is ready to run. Then to format the output from this
prepared statement, use code similar to the following:
> ~~~
sqlite3_qrf_spec spec; /* Format specification */
@@ -41,59 +41,26 @@ if( rc ){
sqlite3_free(zErrMsg); /* Free the error message text */
}else{
printf("%s", zResult); /* Report the results */
sqlite3_free(zResult); /* Free memory used to hold results */
}
sqlite3_free(zResult); /* Free memory used to hold results */
~~~
The `sqlite3_qrf_spec` object describes the desired output format
and where to send the generated output. Most of the work in using
and what to do with the generated output. Most of the work in using
the QRF involves filling out the sqlite3_qrf_spec.
### 1.1 Using QRF with SQL text
If you start with SQL text instead of an sqlite3_stmt pointer, and
especially if the SQL text might comprise two or more statements, then
the SQL text needs to be converted into sqlite3_stmt objects separately.
If the original SQL text is in a variable `const char *zSql` and the
database connection is in variable `sqlite3 *db`, then code
similar to the following should work:
> ~~~
sqlite3_qrf_spec spec; /* Format specification */
char *zErrMsg; /* Text error message (optional) */
char *zResult = 0; /* Formatted output written here */
sqlite3_stmt *pStmt; /* Next prepared statement */
int rc; /* Result code */
memset(&spec, 0, sizeof(spec)); /* Initialize the spec */
spec.iVersion = 1; /* Version number must be 1 */
spec.pzOutput = &zResult; /* Write results in variable zResult */
/* Optionally fill in other settings in spec here, as needed */
zErrMsg = 0; /* Not required; just being pedantic */
while( zSql && zSql[0] ){
pStmt = 0; /* Not required; just being pedantic */
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
if( rc!=SQLITE_OK ){
printf("Error: %s\n", sqlite3_errmsg(db));
}else{
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Get results */
if( rc ){
printf("Error (%d): %s\n", rc, zErrMsg); /* Report an error */
sqlite3_free(zErrMsg); /* Free the error message text */
}else{
printf("%s", zResult); /* Report the results */
sqlite3_free(zResult); /* Free memory used to hold results */
zResult = 0;
}
}
sqlite3_finalize(pStmt);
}
~~~
<a id="spec"></a>
## 2.0 The `sqlite3_qrf_spec` object
The `sqlite3_qrf_spec` looks like this:
The `sqlite3_qrf_spec` structure defines how the results of a query
are to be formatted, and what to do with the formatted text. The
most recent definition of `sqlite3_qrf_spec` is shown below.
Do not be alarmed by the complexity of this structure. You only have
to understand the properties you want to modify. Zero is always a good
default for all of the attributes (except iVersion and pzOutput/xWrite)
and so simply zeroing out the bulk of this structure is a good start.
You can then slowly make adjustments to individual fields to get the
results you desire.
> ~~~
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
@@ -107,15 +74,12 @@ struct sqlite3_qrf_spec {
unsigned char bTitles; /* True to show column names */
unsigned char bWordWrap; /* Try to wrap on word boundaries */
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
unsigned char eTitleAlign; /* Alignment for column headers */
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
unsigned char bBorder; /* Show outer border in Box and Table styles */
short int nWrap; /* Wrap columns wider than this */
short int nScreenWidth; /* Maximum overall table width */
short int nLineLimit; /* Maximum number of lines for any row */
short int nTitleLimit; /* Maximum number of characters in a title */
unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */
int nCharLimit; /* Maximum number of characters in a cell */
int nWidth; /* Number of entries in aWidth[] */
int nAlign; /* Number of entries in aAlignment[] */
@@ -134,24 +98,21 @@ struct sqlite3_qrf_spec {
};
~~~
Do not be alarmed by the complexity of this structure. Everything can
be zeroed except for:
Again, the only fields that must initialized are:
* `.iVersion`
* One of `.pzOutput` or `.xWrite`.
You do not need to understand and configure every field of this object
in order to use QRF effectively. Start by zeroing out the whole structure,
then initializing iVersion and one of pzOutput or xWrite. Then maybe
tweak one or two other settings to get the output you want.
All other fields can be zeroed. Or they can contain other values to
alter the formatting of the query results.
Further detail on the meanings of each of the fields in the
`sqlite3_qrf_spec` object is in the subsequent sections.
`sqlite3_qrf_spec` object are described below.
### 2.1 Structure Version Number
The sqlite3_qrf_spec.iVersion field must be 1. Future enhancements to
the QRF might add new fields to the bottom of the sqlite3_qrf_spec
the QRF might add new fields onto the bottom of the sqlite3_qrf_spec
object. Those new fields will only be accessible if the iVersion is greater
than 1. Thus the iVersion field is used to support upgradability.
@@ -165,7 +126,7 @@ first argument) to transmit the formatted output. Or, if
sqlite3_qrf_spec.pzOutput points to a pointer to a character, then that
pointer is made to point to memory obtained from sqlite3_malloc() that
contains the complete text of the formatted output. If spec.pzOutput\[0\]
is initially non-NULL, then it is assumed to already point to memory obtained
is initially non-NULL, then it is assumed to point to memory obtained
from sqlite3_malloc(). In that case, the buffer is resized using
sqlite3_realloc() and the new text is appended.
@@ -179,8 +140,8 @@ value might be returned.
### 2.3 Output Format
The sqlite3_qrf_spec.eStyle field is an integer code that defines the
specific output format that will be generated. See [section 4.0](#style)
below for details on the meaning of the various style options.
specific output format that will be generated. See section 4.0 below
for details on the meaning of the various style options.
Other fields in sqlite3_qrf_spec might be used or might be
ignored, depending on the value of eStyle.
@@ -253,7 +214,6 @@ Both fields can have one of the following values:
#define QRF_TEXT_Html 4 /* HTML-style quoting */
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
#define QRF_TEXT_Json 6 /* JSON quoting */
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
~~~
A value of QRF_TEXT_Auto means that the query result formatter will choose
@@ -267,14 +227,6 @@ look like SQL literals. That means the value will be surrounded by
single-quotes (U+0027) and any single-quotes contained within the text
will be doubled.
QRF_TEXT_Relaxed is similar to QRF_TEXT_Sql, except that it automatically
reverts to QRF_TEXT_Plain if the value to be displayed does not contain
special characters and is not easily confused with a NULL or a numeric
value. QRF_TEXT_Relaxed strives to minimize the amount of quoting syntax
while keeping the result unambiguous and easy for humans to read. The
precise rules for when quoting is omitted in QRF_TEXT_Relaxed, and when
it is applied, might be adjusted in future releases.
A value of QRF_TEXT_Csv means that text values are escaped in accordance
with RFC&nbsp;4180, which defines Comma-Separated-Value or CSV files.
Text strings that contain no special values appears as-is. Text strings
@@ -313,7 +265,6 @@ how the BLOB value is formatted. The following options are available;
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
#define QRF_BLOB_Size 6 /* Display the blob size only */
~~~
A value of QRF_BLOB_Auto means that display format is selected automatically
@@ -341,11 +292,7 @@ the C/Tcl/Perl octal backslash escapes. So the string from the
previous paragraph would be shown as
`"\u0005\u0028\u0081\u00f3"`.
A value of QRF_BLOB_Size does not show any BLOB content at all.
Instead, it substitutes a text string that says how many bytes
the BLOB contains.
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit, nTitleLimit)
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit)
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
will display only the first nCharLimit characters of each value.
@@ -361,37 +308,14 @@ If the sqlite3_qrf_spec.nLineLimit setting is non-zero, then the
formatter will only display the first nLineLimit lines of each value.
It does not matter if the value is split because it contains a newline
character, or if it split by wrapping. This setting merely limits
the number of displayed lines. The nLineLimit setting currently only
works for **Box**, **Column**, **Line**, **Markdown**, and **Table**
styles, though that limitation might change in future releases.
the number of displayed lines. This setting only works for
**Box**, **Column**, **Line**, **Markdown**, and **Table** styles.
The idea behind both of these settings is to prevent large renderings
when doing a query that (unexpectedly) contains very large text or
blob values: perhaps megabyes of text.
If the sqlite3_qrf_spec.nTitleLimit is non-zero, then the formatter
attempts to limits the size of column titles to at most nTitleLimit
display characters in width and a single line of text. The nTitleLimit
is useful for queries that have result columns that are scalar
subqueries or complex expressions. If those columns lack an AS
clause, then the name of the column will be a copy of the expression
that defines the column, which in some queries can be hundreds of
characters and multiple lines in length, which can reduce the readability
of tabular displays. An nTitleLimit somewhere in the range of 10 to 20.
can improve readability. The nTitleLimit setting currently only
works for **Box**, **Column**, **Line**, **Markdown**, and **Table**
styles, though that limitation might change in future releases.
### 2.9 Multiple Tuples Per INSERT In QRF_STYLE_Insert (nMultiInsert)
If the sqlite3_qrf_spec.nMultiInsert value is positive, then the
QRF_STYLE_Insert output mode will generate multiple tuples in
each INSERT statement until the total number of bytes in the
statement exceeds nMultiInsert. A value of a few thousand is
recommended here, in order to generate SQL output that is parsed
and inserted at maximum speed by SQLite.
### 2.10 Word Wrapping In Columnar Styles (nWrap, bWordWrap)
### 2.9 Word Wrapping In Columnar Styles (nWrap, bWordWrap)
When using columnar formatting modes (QRF_STYLE_Box, QRF_STYLE_Column,
QRF_STYLE_Markdown, or QRF_STYLE_Table), the formatter attempts to limit
@@ -410,7 +334,7 @@ anywhere, including in the middle of a word.
For narrow columns and wide words, it might sometimes be necessary to split
a column in the middle of a word, even when bWordWrap is QRF_Yes.
### 2.11 Helping The Output To Fit On The Terminal (nScreenWidth)
### 2.10 Helping The Output To Fit On The Terminal (nScreenWidth)
The sqlite3_qrf_spec.nScreenWidth field can be set the number of
characters that will fit on one line on the viewer output device.
@@ -426,11 +350,7 @@ The nScreenWidth is a hint to the formatter, not a requirement.
The formatter trieds to keep lines below the nScreenWidth limit,
but it does not guarantee that it will.
The nScreenWidth field currently only makes a difference in
columnar styles (**Box**, **Column**, **Markdown**, and **Table**)
and in the **Line** style.
### 2.12 Individual Column Width (nWidth and aWidth)
### 2.11 Individual Column Width (nWidth and aWidth)
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
signed 16-bit integers that control the width of individual columns
@@ -468,7 +388,7 @@ Again, negative values for aWidth\[\] entries are supported for
backwards compatibility only, and are not recommended for new
applications.
### 2.13 Alignment (nAlignment, aAlignment, eDfltAlign, eTitleAlign)
### 2.12 Alignment (nAlignment, aAlignment, eDfltAlign, eTitleAlign)
Some cells in a display table might contain a lot of text and thus
be wide, or they might contain newline characters or be wrapped by
@@ -537,9 +457,8 @@ entry for that column takes precedence. If either the horizontal
or vertical alignment has an "auto" value for that column or if
a column is beyond the first nAlign entries, then eDfltAlign
is used as a backup. If neither aAlign\[\] nor eDfltAlign
specify a horizontal alignment, then values are right-aligned
(QRF_ALIGN_Right) if they are numeric and left-aligned
(QRF_ALIGN_Left) otherwise. If neither aAlign\[\] nor eDfltAlign
specify a horizontal alignment, then values are left-aligned
(QRF_ALIGN_Left). If neither aAlign\[\] nor eDfltAlign
specify a vertical alignment, then values are top-aligned
(QRF_ALIGN_Top).
@@ -547,7 +466,7 @@ specify a vertical alignment, then values are top-aligned
The vertical alignment settings are currently ignored and
the vertical alignment is always QRF_ALIGN_Top.*
### 2.14 Row and Column Separator Strings
### 2.13 Row and Column Separator Strings
The sqlite3_qrf_spec.zColumnSep and sqlite3_qrf_spec.zRowSep strings
are alternative column and row separator character sequences. If not
@@ -555,18 +474,23 @@ specified (if these pointers are left as NULL) then appropriate defaults
are used. Some output styles have hard-coded column and row separators
and these settings are ignored for those styles.
### 2.15 The Output Table Name
### 2.14 The Output Table Name
The sqlite3_qrf_spec.zTableName value is the name of the output table
when eStyle is QRF_STYLE_Insert.
### 2.16 The Rendering Of NULL (zNull)
### 2.15 The Rendering Of NULL (zNull, eTextNull)
If a value is NULL then show the NULL using the string
found in sqlite3_qrf_spec.zNull. If zNull is itself a NULL pointer
then NULL values are rendered as an empty string.
### 2.17 Optional Value Rendering Callback
If the sqlite3_qrf_spec.bTextNull field is QRF_Yes, then the
text encoding specified by eText is applied to the value in
zNull. If bTextNull is QRF_No or QRF_Auto, then the value
in zNull is shown verbatim.
### 2.16 Optional Value Rendering Callback
If the sqlite3_qrf_spec.xRender field is not NULL, then each
sqlite3_value coming out of the query is first passed to the
@@ -587,13 +511,6 @@ The eText, eBlob, and eEsc settings above become no-ops if the xRender
routine returns non-NULL. In other words, the application-supplied
xRender routine is expected to do all of its own quoting and formatting.
The xRender routine is expected to do character length limiting itself.
So the nCharLimit setting becomes a no-op if xRender is used. However
the nLineLimit setting is still applied. The nTitleLimit setting is
not applicable to xRender because title values come from the
sqlite3_column_name() interface not from sqlite3_column_value(),
and so that names of columns are never processed by xRender.
## 3.0 The `sqlite3_format_query_result()` Interface
Invoke the `sqlite3_format_query_result(P,S,E)` interface to run
@@ -606,13 +523,12 @@ into *E. Any error message text will be stored in memory obtained
from sqlite3_malloc() and it is the responsibility of the caller to
free that memory by a subsequent call to sqlite3_free().
<a id="style"></a>
## 4.0 Output Styles
The result formatter supports a variety of output styles. The
output style (sometimes called "output mode") is determined by
the eStyle field of the sqlite3_qrf_spec object. The set of
supported output modes might increase in future versions.
output style used is determined by the eStyle setting of the
sqlite3_qrf_spec object. The set of supported output modes
might increase in future versions.
The following output modes are currently defined:
> ~~~
@@ -668,22 +584,8 @@ to draw the grid. The **Column** arranges the results in neat columns
but does not draw in column or row separator, except that it does draw
lines horizontal lines using "`-`" characters to separate the column names
from the data below. This is very similar to default output styling in
psql. The **Markdown** renders its result in the Markdown table format.
The **Box** and **Table** styles normally have a border that surrounds
the entire result. However, if sqlite3_qrf_spec.bBorder is QRF_No, then
that border is omitted, saving a little space both horizontally and
vertically.
#### 4.2.1 Split Column Mode
If the bSplitColumn field is QRF_Yes, and eStyle is QRF_STYLE_Column,
and bTitles is QRF_No, and nScreenWidth is greater than zero, and if
the query only returns a single column, then a special rendering known
as "Split Column Mode" will be used. In split column mode, instead
of showing all results in one tall column, the content wraps vertically
so that it appears on the screen as multiple columns, as many as will
fit in the available screen width.
psql. The **Markdown** renders its result in the
Markdown table format.
### 4.3 Line-oriented Styles
@@ -719,10 +621,7 @@ the `<TABLE>..</TABLE>` around the outside.
The **Insert** style generates a series of SQL "INSERT" statements
that will inserts the data that is output into a table whose name is defined
by the zTableName field of `sqlite3_qrf_spec`. If zTableName is NULL,
then a substitute name is used. If nMultiInsert is positive, then the
output will add multiple rows to each INSERT statement until the size
of the INSERT statement exceeds nMultiInsert bytes before starting
a new INSERT statement.
then a substitute name is used.
The **Json** and **JObject** styles generates JSON text for the query result.
The **Json** style produces a JSON array of structures with one
@@ -769,7 +668,7 @@ The SQLite Query Result Formatter is implemented in three source code files:
* `qrf.c` &rarr; The implementation, written in portable C99
* `qrf.h` &rarr; A header file defining interfaces
* `README.md` &rarr; This documentation
* `README.md` &rarr; This documentation, in Markdown
To use the SQLite result formatter, include the "`qrf.h`" header file
and link the application against the "`qrf.c`" source file.
-14
View File
@@ -1,14 +0,0 @@
# Developer Notes
## Measuring Test Coverage On Linux
On Mint Linux, as of 2025-12-02:
> ~~~
./configure --dev CFLAGS='-O0 -g -fprofile-arcs -ftest-coverage'
make clean testfixture
./testfixture test/qrf*.test
gcov -b -c testfixture-tclsqlite-ex.c
~~~
View results in tclsqlite-ex.c.gcov
+145 -488
View File
File diff suppressed because it is too large Load Diff
+4 -16
View File
@@ -9,8 +9,8 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** Header file for the Query Result-Format or "qrf" utility library for
** SQLite. See the README.md documentation for additional information.
** Header file for the Result-Format or "resfmt" utility library for SQLite.
** See the resfmt.md documentation for additional information.
*/
#ifndef SQLITE_QRF_H
#define SQLITE_QRF_H
@@ -34,15 +34,13 @@ struct sqlite3_qrf_spec {
unsigned char bTitles; /* True to show column names */
unsigned char bWordWrap; /* Try to wrap on word boundaries */
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
unsigned char eTitleAlign; /* Alignment for column headers */
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
unsigned char bBorder; /* Show outer border in Box and Table styles */
short int nWrap; /* Wrap columns wider than this */
short int nScreenWidth; /* Maximum overall table width */
short int nLineLimit; /* Maximum number of lines for any row */
short int nTitleLimit; /* Maximum number of characters in a title */
unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */
int nCharLimit; /* Maximum number of characters in a cell */
int nWidth; /* Number of entries in aWidth[] */
int nAlign; /* Number of entries in aAlignment[] */
@@ -111,7 +109,6 @@ int sqlite3_format_query_result(
#define QRF_TEXT_Html 4 /* HTML-style quoting */
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
#define QRF_TEXT_Json 6 /* JSON quoting */
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
/*
** Quoting styles for BLOBs
@@ -123,7 +120,6 @@ int sqlite3_format_query_result(
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
#define QRF_BLOB_Size 6 /* Display the blob size only */
/*
** Control-character escape modes.
@@ -179,21 +175,13 @@ int sqlite3_format_query_result(
** character c. For normal characters, the answer is always 1. But the
** estimate might be 0 or 2 for zero-width and double-width characters.
**
** Different devices display unicode using different widths. So
** Different display devices display unicode using different widths. So
** it is impossible to know that true display width with 100% accuracy.
** Inaccuracies in the width estimates might cause columns to be misaligned.
** Unfortunately, there is nothing we can do about that.
*/
int sqlite3_qrf_wcwidth(int c);
/*
** Return an estimate of the number of display columns used by the
** string in the argument. The width of individual characters is
** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences
** are assigned a width of zero.
*/
size_t sqlite3_qrf_wcswidth(const char*);
#ifdef __cplusplus
}
-28
View File
@@ -192,32 +192,4 @@ do_test 4.7.2 {
list [catch {rbu close} msg] $msg
} {1 {SQLITE_ERROR - rbu_state mismatch error}}
#-------------------------------------------------------------------------
# https://sqlite.org/forum/info/6d0a31e22a435877
#
reset_db
forcedelete rbu.db
do_execsql_test 5.0 {
CREATE TABLE t1(a BLOB);
INSERT INTO t1 VALUES(x'41');
}
sqlite3 dbRbu rbu.db
dbRbu eval {
CREATE TABLE data_t1(a, rbu_control, rbu_rowid);
INSERT INTO data_t1 VALUES(X'310a313a5a337e7e7e7e7e40302c303b','f',1);
}
dbRbu close
do_test 5.1 {
sqlite3rbu rbu test.db rbu.db
rbu step
} {SQLITE_ERROR}
do_test 5.2 {
list [catch {rbu close} msg] $msg
} {1 {SQLITE_ERROR - corrupt fossil delta}}
finish_test
+3 -3
View File
@@ -623,7 +623,7 @@ static int rbuDeltaApply(
/* ERROR: copy exceeds output file size */
return -1;
}
if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
if( (int)(ofst+cnt) > lenSrc ){
/* ERROR: copy extends past end of input */
return -1;
}
@@ -2269,8 +2269,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
/* If necessary, grow the pIter->aIdxCol[] array */
if( iIdxCol==nIdxAlloc ){
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc64(
pIter->aIdxCol, nIdxAlloc*sizeof(RbuSpan) + 16*sizeof(RbuSpan)
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
);
if( aIdxCol==0 ){
rc = SQLITE_NOMEM;
+16
View File
@@ -0,0 +1,16 @@
This folder contains extensions and utility programs intended to analyze
live database files, detect problems, and possibly fix them.
As SQLite is being used on larger and larger databases, database sizes
are growing into the terabyte range. At that size, hardware malfunctions
and/or cosmic rays will occasionally corrupt a database file. Detecting
problems and fixing errors a terabyte-sized databases can take hours or days,
and it is undesirable to take applications that depend on the databases
off-line for such a long time.
The utilities in the folder are intended to provide mechanisms for
detecting and fixing problems in large databases while those databases
are in active use.
The utilities and extensions in this folder are experimental and under
active development at the time of this writing (2017-10-12). If and when
they stabilize, this README will be updated to reflect that fact.
+310
View File
@@ -0,0 +1,310 @@
/*
** 2017 October 11
**
** 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 module exports a single C function:
**
** int sqlite3_check_freelist(sqlite3 *db, const char *zDb);
**
** This function checks the free-list in database zDb (one of "main",
** "temp", etc.) and reports any errors by invoking the sqlite3_log()
** function. It returns SQLITE_OK if successful, or an SQLite error
** code otherwise. It is not an error if the free-list is corrupted but
** no IO or OOM errors occur.
**
** If this file is compiled and loaded as an SQLite loadable extension,
** it adds an SQL function "checkfreelist" to the database handle, to
** be invoked as follows:
**
** SELECT checkfreelist(<database-name>);
**
** This function performs the same checks as sqlite3_check_freelist(),
** except that it returns all error messages as a single text value,
** separated by newline characters. If the freelist is not corrupted
** in any way, an empty string is returned.
**
** To compile this module for use as an SQLite loadable extension:
**
** gcc -Os -fPIC -shared checkfreelist.c -o checkfreelist.so
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
# endif
# if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
# elif !defined(NDEBUG)
# define ALWAYS(X) ((X)?1:(assert(0),0))
# define NEVER(X) ((X)?(assert(0),1):0)
# else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
# endif
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define get4byte(x) ( \
((u32)((x)[0])<<24) + \
((u32)((x)[1])<<16) + \
((u32)((x)[2])<<8) + \
((u32)((x)[3])) \
)
#endif
/*
** Execute a single PRAGMA statement and return the integer value returned
** via output parameter (*pnOut).
**
** The SQL statement passed as the third argument should be a printf-style
** format string containing a single "%s" which will be replace by the
** value passed as the second argument. e.g.
**
** sqlGetInteger(db, "main", "PRAGMA %s.page_count", pnOut)
**
** executes "PRAGMA main.page_count" and stores the results in (*pnOut).
*/
static int sqlGetInteger(
sqlite3 *db, /* Database handle */
const char *zDb, /* Database name ("main", "temp" etc.) */
const char *zFmt, /* SQL statement format */
u32 *pnOut /* OUT: Integer value */
){
int rc, rc2;
char *zSql;
sqlite3_stmt *pStmt = 0;
int bOk = 0;
zSql = sqlite3_mprintf(zFmt, zDb);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
*pnOut = (u32)sqlite3_column_int(pStmt, 0);
bOk = 1;
}
rc2 = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_ERROR;
return rc;
}
/*
** Argument zFmt must be a printf-style format string and must be
** followed by its required arguments. If argument pzOut is NULL,
** then the results of printf()ing the format string are passed to
** sqlite3_log(). Otherwise, they are appended to the string
** at (*pzOut).
*/
static int checkFreelistError(char **pzOut, const char *zFmt, ...){
int rc = SQLITE_OK;
char *zErr = 0;
va_list ap;
va_start(ap, zFmt);
zErr = sqlite3_vmprintf(zFmt, ap);
if( zErr==0 ){
rc = SQLITE_NOMEM;
}else{
if( pzOut ){
*pzOut = sqlite3_mprintf("%s%z%s", *pzOut?"\n":"", *pzOut, zErr);
if( *pzOut==0 ) rc = SQLITE_NOMEM;
}else{
sqlite3_log(SQLITE_ERROR, "checkfreelist: %s", zErr);
}
sqlite3_free(zErr);
}
va_end(ap);
return rc;
}
static int checkFreelist(
sqlite3 *db,
const char *zDb,
char **pzOut
){
/* This query returns one row for each page on the free list. Each row has
** two columns - the page number and page content. */
const char *zTrunk =
"WITH freelist_trunk(i, d, n) AS ("
"SELECT 1, NULL, sqlite_readint32(data, 32) "
"FROM sqlite_dbpage(:1) WHERE pgno=1 "
"UNION ALL "
"SELECT n, data, sqlite_readint32(data) "
"FROM freelist_trunk, sqlite_dbpage(:1) WHERE pgno=n "
")"
"SELECT i, d FROM freelist_trunk WHERE i!=1;";
int rc, rc2; /* Return code */
sqlite3_stmt *pTrunk = 0; /* Compilation of zTrunk */
u32 nPage = 0; /* Number of pages in db */
u32 nExpected = 0; /* Expected number of free pages */
u32 nFree = 0; /* Number of pages on free list */
if( zDb==0 ) zDb = "main";
if( (rc = sqlGetInteger(db, zDb, "PRAGMA %s.page_count", &nPage))
|| (rc = sqlGetInteger(db, zDb, "PRAGMA %s.freelist_count", &nExpected))
){
return rc;
}
rc = sqlite3_prepare_v2(db, zTrunk, -1, &pTrunk, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_text(pTrunk, 1, zDb, -1, SQLITE_STATIC);
while( rc==SQLITE_OK && sqlite3_step(pTrunk)==SQLITE_ROW ){
u32 i;
u32 iTrunk = (u32)sqlite3_column_int(pTrunk, 0);
const u8 *aData = (const u8*)sqlite3_column_blob(pTrunk, 1);
u32 nData = (u32)sqlite3_column_bytes(pTrunk, 1);
u32 iNext = get4byte(&aData[0]);
u32 nLeaf = get4byte(&aData[4]);
if( nLeaf>((nData/4)-2-6) ){
rc = checkFreelistError(pzOut,
"leaf count out of range (%d) on trunk page %d",
(int)nLeaf, (int)iTrunk
);
nLeaf = (nData/4) - 2 - 6;
}
nFree += 1+nLeaf;
if( iNext>nPage ){
rc = checkFreelistError(pzOut,
"trunk page %d is out of range", (int)iNext
);
}
for(i=0; rc==SQLITE_OK && i<nLeaf; i++){
u32 iLeaf = get4byte(&aData[8 + 4*i]);
if( iLeaf==0 || iLeaf>nPage ){
rc = checkFreelistError(pzOut,
"leaf page %d is out of range (child %d of trunk page %d)",
(int)iLeaf, (int)i, (int)iTrunk
);
}
}
}
if( rc==SQLITE_OK && nFree!=nExpected ){
rc = checkFreelistError(pzOut,
"free-list count mismatch: actual=%d header=%d",
(int)nFree, (int)nExpected
);
}
rc2 = sqlite3_finalize(pTrunk);
if( rc==SQLITE_OK ) rc = rc2;
return rc;
}
int sqlite3_check_freelist(sqlite3 *db, const char *zDb){
return checkFreelist(db, zDb, 0);
}
static void checkfreelist_function(
sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
const char *zDb;
int rc;
char *zOut = 0;
sqlite3 *db = sqlite3_context_db_handle(pCtx);
assert( nArg==1 );
zDb = (const char*)sqlite3_value_text(apArg[0]);
rc = checkFreelist(db, zDb, &zOut);
if( rc==SQLITE_OK ){
sqlite3_result_text(pCtx, zOut?zOut:"ok", -1, SQLITE_TRANSIENT);
}else{
sqlite3_result_error_code(pCtx, rc);
}
sqlite3_free(zOut);
}
/*
** An SQL function invoked as follows:
**
** sqlite_readint32(BLOB) -- Decode 32-bit integer from start of blob
*/
static void readint_function(
sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
const u8 *zBlob;
int nBlob;
int iOff = 0;
u32 iRet = 0;
if( nArg!=1 && nArg!=2 ){
sqlite3_result_error(
pCtx, "wrong number of arguments to function sqlite_readint32()", -1
);
return;
}
if( nArg==2 ){
iOff = sqlite3_value_int(apArg[1]);
}
zBlob = sqlite3_value_blob(apArg[0]);
nBlob = sqlite3_value_bytes(apArg[0]);
if( nBlob>=(iOff+4) ){
iRet = get4byte(&zBlob[iOff]);
}
sqlite3_result_int64(pCtx, (sqlite3_int64)iRet);
}
/*
** Register the SQL functions.
*/
static int cflRegister(sqlite3 *db){
int rc = sqlite3_create_function(
db, "sqlite_readint32", -1, SQLITE_UTF8, 0, readint_function, 0, 0
);
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3_create_function(
db, "checkfreelist", 1, SQLITE_UTF8, 0, checkfreelist_function, 0, 0
);
return rc;
}
/*
** Extension load function.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_checkfreelist_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
return cflRegister(db);
}
+929
View File
@@ -0,0 +1,929 @@
/*
** 2017 October 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.
**
*************************************************************************
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
/*
** Stuff that is available inside the amalgamation, but which we need to
** declare ourselves if this module is compiled separately.
*/
#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define get4byte(x) ( \
((u32)((x)[0])<<24) + \
((u32)((x)[1])<<16) + \
((u32)((x)[2])<<8) + \
((u32)((x)[3])) \
)
#endif
typedef struct CidxTable CidxTable;
typedef struct CidxCursor CidxCursor;
struct CidxTable {
sqlite3_vtab base; /* Base class. Must be first */
sqlite3 *db;
};
struct CidxCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
sqlite3_int64 iRowid; /* Row number of the output */
char *zIdxName; /* Copy of the index_name parameter */
char *zAfterKey; /* Copy of the after_key parameter */
sqlite3_stmt *pStmt; /* SQL statement that generates the output */
};
typedef struct CidxColumn CidxColumn;
struct CidxColumn {
char *zExpr; /* Text for indexed expression */
int bDesc; /* True for DESC columns, otherwise false */
int bKey; /* Part of index, not PK */
};
typedef struct CidxIndex CidxIndex;
struct CidxIndex {
char *zWhere; /* WHERE clause, if any */
int nCol; /* Elements in aCol[] array */
CidxColumn aCol[1]; /* Array of indexed columns */
};
static void *cidxMalloc(int *pRc, int n){
void *pRet = 0;
assert( n!=0 );
if( *pRc==SQLITE_OK ){
pRet = sqlite3_malloc(n);
if( pRet ){
memset(pRet, 0, n);
}else{
*pRc = SQLITE_NOMEM;
}
}
return pRet;
}
static void cidxCursorError(CidxCursor *pCsr, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
assert( pCsr->base.pVtab->zErrMsg==0 );
pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
/*
** Connect to the incremental_index_check virtual table.
*/
static int cidxConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
int rc = SQLITE_OK;
CidxTable *pRet;
#define IIC_ERRMSG 0
#define IIC_CURRENT_KEY 1
#define IIC_INDEX_NAME 2
#define IIC_AFTER_KEY 3
#define IIC_SCANNER_SQL 4
rc = sqlite3_declare_vtab(db,
"CREATE TABLE xyz("
" errmsg TEXT," /* Error message or NULL if everything is ok */
" current_key TEXT," /* SQLite quote() text of key values */
" index_name HIDDEN," /* IN: name of the index being scanned */
" after_key HIDDEN," /* IN: Start scanning after this key */
" scanner_sql HIDDEN" /* debugging info: SQL used for scanner */
")"
);
pRet = cidxMalloc(&rc, sizeof(CidxTable));
if( pRet ){
pRet->db = db;
}
*ppVtab = (sqlite3_vtab*)pRet;
return rc;
}
/*
** Disconnect from or destroy an incremental_index_check virtual table.
*/
static int cidxDisconnect(sqlite3_vtab *pVtab){
CidxTable *pTab = (CidxTable*)pVtab;
sqlite3_free(pTab);
return SQLITE_OK;
}
/*
** idxNum and idxStr are not used. There are only three possible plans,
** which are all distinguished by the number of parameters.
**
** No parameters: A degenerate plan. The result is zero rows.
** 1 Parameter: Scan all of the index starting with first entry
** 2 parameters: Scan the index starting after the "after_key".
**
** Provide successively smaller costs for each of these plans to encourage
** the query planner to select the one with the most parameters.
*/
static int cidxBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
int iIdxName = -1;
int iAfterKey = -1;
int i;
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
if( p->usable==0 ) continue;
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( p->iColumn==IIC_INDEX_NAME ){
iIdxName = i;
}
if( p->iColumn==IIC_AFTER_KEY ){
iAfterKey = i;
}
}
if( iIdxName<0 ){
pInfo->estimatedCost = 1000000000.0;
}else{
pInfo->aConstraintUsage[iIdxName].argvIndex = 1;
pInfo->aConstraintUsage[iIdxName].omit = 1;
if( iAfterKey<0 ){
pInfo->estimatedCost = 1000000.0;
}else{
pInfo->aConstraintUsage[iAfterKey].argvIndex = 2;
pInfo->aConstraintUsage[iAfterKey].omit = 1;
pInfo->estimatedCost = 1000.0;
}
}
return SQLITE_OK;
}
/*
** Open a new btreeinfo cursor.
*/
static int cidxOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
CidxCursor *pRet;
int rc = SQLITE_OK;
pRet = cidxMalloc(&rc, sizeof(CidxCursor));
*ppCursor = (sqlite3_vtab_cursor*)pRet;
return rc;
}
/*
** Close a btreeinfo cursor.
*/
static int cidxClose(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->zIdxName);
sqlite3_free(pCsr->zAfterKey);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** Move a btreeinfo cursor to the next entry in the file.
*/
static int cidxNext(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
int rc = sqlite3_step(pCsr->pStmt);
if( rc!=SQLITE_ROW ){
rc = sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
if( rc!=SQLITE_OK ){
sqlite3 *db = ((CidxTable*)pCsr->base.pVtab)->db;
cidxCursorError(pCsr, "Cursor error: %s", sqlite3_errmsg(db));
}
}else{
pCsr->iRowid++;
rc = SQLITE_OK;
}
return rc;
}
/* We have reached EOF if previous sqlite3_step() returned
** anything other than SQLITE_ROW;
*/
static int cidxEof(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
return pCsr->pStmt==0;
}
static char *cidxMprintf(int *pRc, const char *zFmt, ...){
char *zRet = 0;
va_list ap;
va_start(ap, zFmt);
zRet = sqlite3_vmprintf(zFmt, ap);
if( *pRc==SQLITE_OK ){
if( zRet==0 ){
*pRc = SQLITE_NOMEM;
}
}else{
sqlite3_free(zRet);
zRet = 0;
}
va_end(ap);
return zRet;
}
static sqlite3_stmt *cidxPrepare(
int *pRc, CidxCursor *pCsr, const char *zFmt, ...
){
sqlite3_stmt *pRet = 0;
char *zSql;
va_list ap; /* ... printf arguments */
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if( *pRc==SQLITE_OK ){
if( zSql==0 ){
*pRc = SQLITE_NOMEM;
}else{
sqlite3 *db = ((CidxTable*)pCsr->base.pVtab)->db;
*pRc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0);
if( *pRc!=SQLITE_OK ){
cidxCursorError(pCsr, "SQL error: %s", sqlite3_errmsg(db));
}
}
}
sqlite3_free(zSql);
va_end(ap);
return pRet;
}
static void cidxFinalize(int *pRc, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ) *pRc = rc;
}
char *cidxStrdup(int *pRc, const char *zStr){
char *zRet = 0;
if( *pRc==SQLITE_OK ){
int n = (int)strlen(zStr);
zRet = cidxMalloc(pRc, n+1);
if( zRet ) memcpy(zRet, zStr, n+1);
}
return zRet;
}
static void cidxFreeIndex(CidxIndex *pIdx){
if( pIdx ){
int i;
for(i=0; i<pIdx->nCol; i++){
sqlite3_free(pIdx->aCol[i].zExpr);
}
sqlite3_free(pIdx->zWhere);
sqlite3_free(pIdx);
}
}
static int cidx_isspace(char c){
return c==' ' || c=='\t' || c=='\r' || c=='\n';
}
static int cidx_isident(char c){
return c<0
|| (c>='0' && c<='9') || (c>='a' && c<='z')
|| (c>='A' && c<='Z') || c=='_';
}
#define CIDX_PARSE_EOF 0
#define CIDX_PARSE_COMMA 1 /* "," */
#define CIDX_PARSE_OPEN 2 /* "(" */
#define CIDX_PARSE_CLOSE 3 /* ")" */
/*
** Argument zIn points into the start, middle or end of a CREATE INDEX
** statement. If argument pbDoNotTrim is non-NULL, then this function
** scans the input until it finds EOF, a comma (",") or an open or
** close parenthesis character. It then sets (*pzOut) to point to said
** character and returns a CIDX_PARSE_XXX constant as appropriate. The
** parser is smart enough that special characters inside SQL strings
** or comments are not returned for.
**
** Or, if argument pbDoNotTrim is NULL, then this function sets *pzOut
** to point to the first character of the string that is not whitespace
** or part of an SQL comment and returns CIDX_PARSE_EOF.
**
** Additionally, if pbDoNotTrim is not NULL and the element immediately
** before (*pzOut) is an SQL comment of the form "-- comment", then
** (*pbDoNotTrim) is set before returning. In all other cases it is
** cleared.
*/
static int cidxFindNext(
const char *zIn,
const char **pzOut,
int *pbDoNotTrim /* OUT: True if prev is -- comment */
){
const char *z = zIn;
while( 1 ){
while( cidx_isspace(*z) ) z++;
if( z[0]=='-' && z[1]=='-' ){
z += 2;
while( z[0]!='\n' ){
if( z[0]=='\0' ) return CIDX_PARSE_EOF;
z++;
}
while( cidx_isspace(*z) ) z++;
if( pbDoNotTrim ) *pbDoNotTrim = 1;
}else
if( z[0]=='/' && z[1]=='*' ){
z += 2;
while( z[0]!='*' || z[1]!='/' ){
if( z[1]=='\0' ) return CIDX_PARSE_EOF;
z++;
}
z += 2;
}else{
*pzOut = z;
if( pbDoNotTrim==0 ) return CIDX_PARSE_EOF;
switch( *z ){
case '\0':
return CIDX_PARSE_EOF;
case '(':
return CIDX_PARSE_OPEN;
case ')':
return CIDX_PARSE_CLOSE;
case ',':
return CIDX_PARSE_COMMA;
case '"':
case '\'':
case '`': {
char q = *z;
z++;
while( *z ){
if( *z==q ){
z++;
if( *z!=q ) break;
}
z++;
}
break;
}
case '[':
while( *z++!=']' );
break;
default:
z++;
break;
}
*pbDoNotTrim = 0;
}
}
assert( 0 );
return -1;
}
static int cidxParseSQL(CidxCursor *pCsr, CidxIndex *pIdx, const char *zSql){
const char *z = zSql;
const char *z1;
int e;
int rc = SQLITE_OK;
int nParen = 1;
int bDoNotTrim = 0;
CidxColumn *pCol = pIdx->aCol;
e = cidxFindNext(z, &z, &bDoNotTrim);
if( e!=CIDX_PARSE_OPEN ) goto parse_error;
z1 = z+1;
z++;
while( nParen>0 ){
e = cidxFindNext(z, &z, &bDoNotTrim);
if( e==CIDX_PARSE_EOF ) goto parse_error;
if( (e==CIDX_PARSE_COMMA || e==CIDX_PARSE_CLOSE) && nParen==1 ){
const char *z2 = z;
if( pCol->zExpr ) goto parse_error;
if( bDoNotTrim==0 ){
while( cidx_isspace(z[-1]) ) z--;
if( !sqlite3_strnicmp(&z[-3], "asc", 3) && 0==cidx_isident(z[-4]) ){
z -= 3;
while( cidx_isspace(z[-1]) ) z--;
}else
if( !sqlite3_strnicmp(&z[-4], "desc", 4) && 0==cidx_isident(z[-5]) ){
z -= 4;
while( cidx_isspace(z[-1]) ) z--;
}
while( cidx_isspace(z1[0]) ) z1++;
}
pCol->zExpr = cidxMprintf(&rc, "%.*s", z-z1, z1);
pCol++;
z = z1 = z2+1;
}
if( e==CIDX_PARSE_OPEN ) nParen++;
if( e==CIDX_PARSE_CLOSE ) nParen--;
z++;
}
/* Search for a WHERE clause */
cidxFindNext(z, &z, 0);
if( 0==sqlite3_strnicmp(z, "where", 5) ){
pIdx->zWhere = cidxMprintf(&rc, "%s\n", &z[5]);
}else if( z[0]!='\0' ){
goto parse_error;
}
return rc;
parse_error:
cidxCursorError(pCsr, "Parse error in: %s", zSql);
return SQLITE_ERROR;
}
static int cidxLookupIndex(
CidxCursor *pCsr, /* Cursor object */
const char *zIdx, /* Name of index to look up */
CidxIndex **ppIdx, /* OUT: Description of columns */
char **pzTab /* OUT: Table name */
){
int rc = SQLITE_OK;
char *zTab = 0;
CidxIndex *pIdx = 0;
sqlite3_stmt *pFindTab = 0;
sqlite3_stmt *pInfo = 0;
/* Find the table for this index. */
pFindTab = cidxPrepare(&rc, pCsr,
"SELECT tbl_name, sql FROM sqlite_schema WHERE name=%Q AND type='index'",
zIdx
);
if( rc==SQLITE_OK && sqlite3_step(pFindTab)==SQLITE_ROW ){
const char *zSql = (const char*)sqlite3_column_text(pFindTab, 1);
zTab = cidxStrdup(&rc, (const char*)sqlite3_column_text(pFindTab, 0));
pInfo = cidxPrepare(&rc, pCsr, "PRAGMA index_xinfo(%Q)", zIdx);
if( rc==SQLITE_OK ){
int nAlloc = 0;
int iCol = 0;
while( sqlite3_step(pInfo)==SQLITE_ROW ){
const char *zName = (const char*)sqlite3_column_text(pInfo, 2);
const char *zColl = (const char*)sqlite3_column_text(pInfo, 4);
CidxColumn *p;
if( zName==0 ) zName = "rowid";
if( iCol==nAlloc ){
int nByte = sizeof(CidxIndex) + sizeof(CidxColumn)*(nAlloc+8);
pIdx = (CidxIndex*)sqlite3_realloc(pIdx, nByte);
nAlloc += 8;
}
p = &pIdx->aCol[iCol++];
p->bDesc = sqlite3_column_int(pInfo, 3);
p->bKey = sqlite3_column_int(pInfo, 5);
if( zSql==0 || p->bKey==0 ){
p->zExpr = cidxMprintf(&rc, "\"%w\" COLLATE %s",zName,zColl);
}else{
p->zExpr = 0;
}
pIdx->nCol = iCol;
pIdx->zWhere = 0;
}
cidxFinalize(&rc, pInfo);
}
if( rc==SQLITE_OK && zSql ){
rc = cidxParseSQL(pCsr, pIdx, zSql);
}
}
cidxFinalize(&rc, pFindTab);
if( rc==SQLITE_OK && zTab==0 ){
rc = SQLITE_ERROR;
}
if( rc!=SQLITE_OK ){
sqlite3_free(zTab);
cidxFreeIndex(pIdx);
}else{
*pzTab = zTab;
*ppIdx = pIdx;
}
return rc;
}
static int cidxDecodeAfter(
CidxCursor *pCsr,
int nCol,
const char *zAfterKey,
char ***pazAfter
){
char **azAfter;
int rc = SQLITE_OK;
int nAfterKey = (int)strlen(zAfterKey);
azAfter = cidxMalloc(&rc, sizeof(char*)*nCol + nAfterKey+1);
if( rc==SQLITE_OK ){
int i;
char *zCopy = (char*)&azAfter[nCol];
char *p = zCopy;
memcpy(zCopy, zAfterKey, nAfterKey+1);
for(i=0; i<nCol; i++){
while( *p==' ' ) p++;
/* Check NULL values */
if( *p=='N' ){
if( memcmp(p, "NULL", 4) ) goto parse_error;
p += 4;
}
/* Check strings and blob literals */
else if( *p=='X' || *p=='\'' ){
azAfter[i] = p;
if( *p=='X' ) p++;
if( *p!='\'' ) goto parse_error;
p++;
while( 1 ){
if( *p=='\0' ) goto parse_error;
if( *p=='\'' ){
p++;
if( *p!='\'' ) break;
}
p++;
}
}
/* Check numbers */
else{
azAfter[i] = p;
while( (*p>='0' && *p<='9')
|| *p=='.' || *p=='+' || *p=='-' || *p=='e' || *p=='E'
){
p++;
}
}
while( *p==' ' ) p++;
if( *p!=(i==(nCol-1) ? '\0' : ',') ){
goto parse_error;
}
*p++ = '\0';
}
}
*pazAfter = azAfter;
return rc;
parse_error:
sqlite3_free(azAfter);
*pazAfter = 0;
cidxCursorError(pCsr, "%s", "error parsing after value");
return SQLITE_ERROR;
}
static char *cidxWhere(
int *pRc, CidxColumn *aCol, char **azAfter, int iGt, int bLastIsNull
){
char *zRet = 0;
const char *zSep = "";
int i;
for(i=0; i<iGt; i++){
zRet = cidxMprintf(pRc, "%z%s(%s) IS %s", zRet,
zSep, aCol[i].zExpr, (azAfter[i] ? azAfter[i] : "NULL")
);
zSep = " AND ";
}
if( bLastIsNull ){
zRet = cidxMprintf(pRc, "%z%s(%s) IS NULL", zRet, zSep, aCol[iGt].zExpr);
}
else if( azAfter[iGt] ){
zRet = cidxMprintf(pRc, "%z%s(%s) %s %s", zRet,
zSep, aCol[iGt].zExpr, (aCol[iGt].bDesc ? "<" : ">"),
azAfter[iGt]
);
}else{
zRet = cidxMprintf(pRc, "%z%s(%s) IS NOT NULL", zRet, zSep,aCol[iGt].zExpr);
}
return zRet;
}
#define CIDX_CLIST_ALL 0
#define CIDX_CLIST_ORDERBY 1
#define CIDX_CLIST_CURRENT_KEY 2
#define CIDX_CLIST_SUBWHERE 3
#define CIDX_CLIST_SUBEXPR 4
/*
** This function returns various strings based on the contents of the
** CidxIndex structure and the eType parameter.
*/
static char *cidxColumnList(
int *pRc, /* IN/OUT: Error code */
const char *zIdx,
CidxIndex *pIdx, /* Indexed columns */
int eType /* True to include ASC/DESC */
){
char *zRet = 0;
if( *pRc==SQLITE_OK ){
const char *aDir[2] = {"", " DESC"};
int i;
const char *zSep = "";
for(i=0; i<pIdx->nCol; i++){
CidxColumn *p = &pIdx->aCol[i];
assert( pIdx->aCol[i].bDesc==0 || pIdx->aCol[i].bDesc==1 );
switch( eType ){
case CIDX_CLIST_ORDERBY:
zRet = cidxMprintf(pRc, "%z%s%d%s", zRet, zSep, i+1, aDir[p->bDesc]);
zSep = ",";
break;
case CIDX_CLIST_CURRENT_KEY:
zRet = cidxMprintf(pRc, "%z%squote(i%d)", zRet, zSep, i);
zSep = "||','||";
break;
case CIDX_CLIST_SUBWHERE:
if( p->bKey==0 ){
zRet = cidxMprintf(pRc, "%z%s%s IS i.i%d", zRet,
zSep, p->zExpr, i
);
zSep = " AND ";
}
break;
case CIDX_CLIST_SUBEXPR:
if( p->bKey==1 ){
zRet = cidxMprintf(pRc, "%z%s%s IS i.i%d", zRet,
zSep, p->zExpr, i
);
zSep = " AND ";
}
break;
default:
assert( eType==CIDX_CLIST_ALL );
zRet = cidxMprintf(pRc, "%z%s(%s) AS i%d", zRet, zSep, p->zExpr, i);
zSep = ", ";
break;
}
}
}
return zRet;
}
/*
** Generate SQL (in memory obtained from sqlite3_malloc()) that will
** continue the index scan for zIdxName starting after zAfterKey.
*/
int cidxGenerateScanSql(
CidxCursor *pCsr, /* The cursor which needs the new statement */
const char *zIdxName, /* index to be scanned */
const char *zAfterKey, /* start after this key, if not NULL */
char **pzSqlOut /* OUT: Write the generated SQL here */
){
int rc;
char *zTab = 0;
char *zCurrentKey = 0;
char *zOrderBy = 0;
char *zSubWhere = 0;
char *zSubExpr = 0;
char *zSrcList = 0;
char **azAfter = 0;
CidxIndex *pIdx = 0;
*pzSqlOut = 0;
rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);
zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);
if( rc==SQLITE_OK && zAfterKey ){
rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
}
if( rc==SQLITE_OK ){
if( zAfterKey==0 ){
*pzSqlOut = cidxMprintf(&rc,
"SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
"FROM (SELECT %s FROM %Q INDEXED BY %Q %s%sORDER BY %s) AS i",
zSubExpr, zTab, zSubWhere, zCurrentKey,
zSrcList, zTab, zIdxName,
(pIdx->zWhere ? "WHERE " : ""), (pIdx->zWhere ? pIdx->zWhere : ""),
zOrderBy
);
}else{
const char *zSep = "";
char *zSql;
int i;
zSql = cidxMprintf(&rc,
"SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
zSubExpr, zTab, zSubWhere, zCurrentKey
);
for(i=pIdx->nCol-1; i>=0; i--){
int j;
if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
for(j=0; j<2; j++){
char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
zSql = cidxMprintf(&rc, "%z"
"%sSELECT * FROM ("
"SELECT %s FROM %Q INDEXED BY %Q WHERE %s%s%z ORDER BY %s"
")",
zSql, zSep, zSrcList, zTab, zIdxName,
pIdx->zWhere ? pIdx->zWhere : "",
pIdx->zWhere ? " AND " : "",
zWhere, zOrderBy
);
zSep = " UNION ALL ";
if( pIdx->aCol[i].bDesc==0 ) break;
}
}
*pzSqlOut = cidxMprintf(&rc, "%z) AS i", zSql);
}
}
sqlite3_free(zTab);
sqlite3_free(zCurrentKey);
sqlite3_free(zOrderBy);
sqlite3_free(zSubWhere);
sqlite3_free(zSubExpr);
sqlite3_free(zSrcList);
cidxFreeIndex(pIdx);
sqlite3_free(azAfter);
return rc;
}
/*
** Position a cursor back to the beginning.
*/
static int cidxFilter(
sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
int rc = SQLITE_OK;
CidxCursor *pCsr = (CidxCursor*)pCursor;
const char *zIdxName = 0;
const char *zAfterKey = 0;
sqlite3_free(pCsr->zIdxName);
pCsr->zIdxName = 0;
sqlite3_free(pCsr->zAfterKey);
pCsr->zAfterKey = 0;
sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
if( argc>0 ){
zIdxName = (const char*)sqlite3_value_text(argv[0]);
if( argc>1 ){
zAfterKey = (const char*)sqlite3_value_text(argv[1]);
}
}
if( zIdxName ){
char *zSql = 0;
pCsr->zIdxName = sqlite3_mprintf("%s", zIdxName);
pCsr->zAfterKey = zAfterKey ? sqlite3_mprintf("%s", zAfterKey) : 0;
rc = cidxGenerateScanSql(pCsr, zIdxName, zAfterKey, &zSql);
if( zSql ){
pCsr->pStmt = cidxPrepare(&rc, pCsr, "%z", zSql);
}
}
if( pCsr->pStmt ){
assert( rc==SQLITE_OK );
rc = cidxNext(pCursor);
}
pCsr->iRowid = 1;
return rc;
}
/*
** Return a column value.
*/
static int cidxColumn(
sqlite3_vtab_cursor *pCursor,
sqlite3_context *ctx,
int iCol
){
CidxCursor *pCsr = (CidxCursor*)pCursor;
assert( iCol>=IIC_ERRMSG && iCol<=IIC_SCANNER_SQL );
switch( iCol ){
case IIC_ERRMSG: {
const char *zVal = 0;
if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
zVal = "row data mismatch";
}
}else{
zVal = "row missing";
}
sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);
break;
}
case IIC_CURRENT_KEY: {
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));
break;
}
case IIC_INDEX_NAME: {
sqlite3_result_text(ctx, pCsr->zIdxName, -1, SQLITE_TRANSIENT);
break;
}
case IIC_AFTER_KEY: {
sqlite3_result_text(ctx, pCsr->zAfterKey, -1, SQLITE_TRANSIENT);
break;
}
case IIC_SCANNER_SQL: {
char *zSql = 0;
cidxGenerateScanSql(pCsr, pCsr->zIdxName, pCsr->zAfterKey, &zSql);
sqlite3_result_text(ctx, zSql, -1, sqlite3_free);
break;
}
}
return SQLITE_OK;
}
/* Return the ROWID for the sqlite_btreeinfo table */
static int cidxRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
CidxCursor *pCsr = (CidxCursor*)pCursor;
*pRowid = pCsr->iRowid;
return SQLITE_OK;
}
/*
** Register the virtual table modules with the database handle passed
** as the only argument.
*/
static int ciInit(sqlite3 *db){
static sqlite3_module cidx_module = {
0, /* iVersion */
0, /* xCreate */
cidxConnect, /* xConnect */
cidxBestIndex, /* xBestIndex */
cidxDisconnect, /* xDisconnect */
0, /* xDestroy */
cidxOpen, /* xOpen - open a cursor */
cidxClose, /* xClose - close a cursor */
cidxFilter, /* xFilter - configure scan constraints */
cidxNext, /* xNext - advance a cursor */
cidxEof, /* xEof - check for end of scan */
cidxColumn, /* xColumn - read data */
cidxRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
return sqlite3_create_module(db, "incremental_index_check", &cidx_module, 0);
}
/*
** Extension load function.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_checkindex_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
return ciInit(db);
}
+85
View File
@@ -0,0 +1,85 @@
/*
** Read an SQLite database file and analyze its space utilization. Generate
** text on standard output.
*/
#define TCLSH_INIT_PROC sqlite3_checker_init_proc
#define SQLITE_ENABLE_DBPAGE_VTAB 1
#undef SQLITE_THREADSAFE
#define SQLITE_THREADSAFE 0
#undef SQLITE_ENABLE_COLUMN_METADATA
#define SQLITE_OMIT_DECLTYPE 1
#define SQLITE_OMIT_DEPRECATED 1
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
#define SQLITE_OMIT_SHARED_CACHE 1
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_MAX_EXPR_DEPTH 0
INCLUDE sqlite3.c
INCLUDE $ROOT/src/tclsqlite.c
INCLUDE $ROOT/ext/misc/btreeinfo.c
INCLUDE $ROOT/ext/repair/checkindex.c
INCLUDE $ROOT/ext/repair/checkfreelist.c
/*
** Decode a pointer to an sqlite3 object.
*/
int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
struct SqliteDb *p;
Tcl_CmdInfo cmdInfo;
if( Tcl_GetCommandInfo(interp, zA, &cmdInfo) ){
p = (struct SqliteDb*)cmdInfo.objClientData;
*ppDb = p->db;
return TCL_OK;
}else{
*ppDb = 0;
return TCL_ERROR;
}
return TCL_OK;
}
/*
** sqlite3_imposter db main rootpage {CREATE TABLE...} ;# setup an imposter
** sqlite3_imposter db main ;# rm all imposters
*/
static int sqlite3_imposter(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
const char *zSchema;
int iRoot;
const char *zSql;
if( objc!=3 && objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB SCHEMA [ROOTPAGE SQL]");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zSchema = Tcl_GetString(objv[2]);
if( objc==3 ){
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 0, 1);
}else{
if( Tcl_GetIntFromObj(interp, objv[3], &iRoot) ) return TCL_ERROR;
zSql = Tcl_GetString(objv[4]);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 1, iRoot);
sqlite3_exec(db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 0, 0);
}
return TCL_OK;
}
#include <stdio.h>
const char *sqlite3_checker_init_proc(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3_imposter",
(Tcl_ObjCmdProc*)sqlite3_imposter, 0, 0);
sqlite3_auto_extension((void(*)(void))sqlite3_btreeinfo_init);
sqlite3_auto_extension((void(*)(void))sqlite3_checkindex_init);
sqlite3_auto_extension((void(*)(void))sqlite3_checkfreelist_init);
return
BEGIN_STRING
INCLUDE $ROOT/ext/repair/sqlite3_checker.tcl
END_STRING
;
}
+264
View File
@@ -0,0 +1,264 @@
# This TCL script is the main driver script for the sqlite3_checker utility
# program.
#
# Special case:
#
# sqlite3_checker --test FILENAME ARGS
#
# uses FILENAME in place of this script.
#
if {[lindex $argv 0]=="--test" && [llength $argv]>1} {
set ::argv0 [lindex $argv 1]
set argv [lrange $argv 2 end]
source $argv0
exit 0
}
# Emulate a TCL shell
#
proc tclsh {} {
set line {}
while {![eof stdin]} {
if {$line!=""} {
puts -nonewline "> "
} else {
puts -nonewline "% "
}
flush stdout
append line [gets stdin]
if {[info complete $line]} {
if {[catch {uplevel #0 $line} result]} {
puts stderr "Error: $result"
} elseif {$result!=""} {
puts $result
}
set line {}
} else {
append line \n
}
}
}
# Do an incremental integrity check of a single index
#
proc check_index {idxname batchsize bTrace} {
set i 0
set more 1
set nerr 0
set pct 00.0
set max [db one {SELECT nEntry FROM sqlite_btreeinfo('main')
WHERE name=$idxname}]
puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
flush stdout
if {$bTrace} {
set sql {SELECT errmsg, current_key AS key,
CASE WHEN rowid=1 THEN scanner_sql END AS traceOut
FROM incremental_index_check($idxname)
WHERE after_key=$key
LIMIT $batchsize}
} else {
set sql {SELECT errmsg, current_key AS key, NULL AS traceOut
FROM incremental_index_check($idxname)
WHERE after_key=$key
LIMIT $batchsize}
}
while {$more} {
set more 0
db eval $sql {
set more 1
if {$errmsg!=""} {
incr nerr
puts "$idxname: key($key): $errmsg"
} elseif {$traceOut!=""} {
puts "$idxname: $traceOut"
}
incr i
}
set x [format {%.1f} [expr {($i*100.0)/$max}]]
if {$x!=$pct} {
puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
flush stdout
set pct $x
}
}
puts "$idxname: $nerr errors out of $i entries"
}
# Print a usage message on standard error, then quit.
#
proc usage {} {
set argv0 [file rootname [file tail [info nameofexecutable]]]
puts stderr "Usage: $argv0 OPTIONS database-filename"
puts stderr {
Do sanity checking on a live SQLite3 database file specified by the
"database-filename" argument.
Options:
--batchsize N Number of rows to check per transaction
--freelist Perform a freelist check
--index NAME Run a check of the index NAME
--summary Print summary information about the database
--table NAME Run a check of all indexes for table NAME
--tclsh Run the built-in TCL interpreter (for debugging)
--trace (Debugging only:) Output trace information on the scan
--version Show the version number of SQLite
}
exit 1
}
set file_to_analyze {}
append argv {}
set bFreelistCheck 0
set bSummary 0
set zIndex {}
set zTable {}
set batchsize 1000
set bAll 1
set bTrace 0
set argc [llength $argv]
for {set i 0} {$i<$argc} {incr i} {
set arg [lindex $argv $i]
if {[regexp {^-+tclsh$} $arg]} {
tclsh
exit 0
}
if {[regexp {^-+version$} $arg]} {
sqlite3 mem :memory:
puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
mem close
exit 0
}
if {[regexp {^-+freelist$} $arg]} {
set bFreelistCheck 1
set bAll 0
continue
}
if {[regexp {^-+summary$} $arg]} {
set bSummary 1
set bAll 0
continue
}
if {[regexp {^-+trace$} $arg]} {
set bTrace 1
continue
}
if {[regexp {^-+batchsize$} $arg]} {
incr i
if {$i>=$argc} {
puts stderr "missing argument on $arg"
exit 1
}
set batchsize [lindex $argv $i]
continue
}
if {[regexp {^-+index$} $arg]} {
incr i
if {$i>=$argc} {
puts stderr "missing argument on $arg"
exit 1
}
set zIndex [lindex $argv $i]
set bAll 0
continue
}
if {[regexp {^-+table$} $arg]} {
incr i
if {$i>=$argc} {
puts stderr "missing argument on $arg"
exit 1
}
set zTable [lindex $argv $i]
set bAll 0
continue
}
if {[regexp {^-} $arg]} {
puts stderr "Unknown option: $arg"
usage
}
if {$file_to_analyze!=""} {
usage
} else {
set file_to_analyze $arg
}
}
if {$file_to_analyze==""} usage
# If a TCL script is specified on the command-line, then run that
# script.
#
if {[file extension $file_to_analyze]==".tcl"} {
source $file_to_analyze
exit 0
}
set root_filename $file_to_analyze
regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
if {![file exists $root_filename]} {
puts stderr "No such file: $root_filename"
exit 1
}
if {![file readable $root_filename]} {
puts stderr "File is not readable: $root_filename"
exit 1
}
if {[catch {sqlite3 db $file_to_analyze} res]} {
puts stderr "Cannot open datababase $root_filename: $res"
exit 1
}
if {$bFreelistCheck || $bAll} {
puts -nonewline "freelist-check: "
flush stdout
db eval BEGIN
puts [db one {SELECT checkfreelist('main')}]
db eval END
}
if {$bSummary} {
set scale 0
set pgsz [db one {PRAGMA page_size}]
db eval {SELECT nPage*$pgsz AS sz, name, tbl_name
FROM sqlite_btreeinfo
WHERE type='index'
ORDER BY 1 DESC, name} {
if {$scale==0} {
if {$sz>10000000} {
set scale 1000000.0
set unit MB
} else {
set scale 1000.0
set unit KB
}
}
puts [format {%7.1f %s index %s of table %s} \
[expr {$sz/$scale}] $unit $name $tbl_name]
}
}
if {$zIndex!=""} {
check_index $zIndex $batchsize $bTrace
}
if {$zTable!=""} {
foreach idx [db eval {SELECT name FROM sqlite_master
WHERE type='index' AND rootpage>0
AND tbl_name=$zTable}] {
check_index $idx $batchsize $bTrace
}
}
if {$bAll} {
set allidx [db eval {SELECT name FROM sqlite_btreeinfo('main')
WHERE type='index' AND rootpage>0
ORDER BY nEntry}]
foreach idx $allidx {
check_index $idx $batchsize $bTrace
}
}
+13
View File
@@ -0,0 +1,13 @@
To run these tests, first build sqlite3_checker:
> make sqlite3_checker
Then run the "test.tcl" script using:
> ./sqlite3_checker --test $path/test.tcl
Optionally add the full pathnames of individual *.test modules
+92
View File
@@ -0,0 +1,92 @@
# 2017-10-11
set testprefix checkfreelist
do_execsql_test 1.0 {
PRAGMA page_size=1024;
CREATE TABLE t1(a, b);
}
do_execsql_test 1.2 { SELECT checkfreelist('main') } {ok}
do_execsql_test 1.3 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
DELETE FROM t1 WHERE rowid%3;
PRAGMA freelist_count;
} {6726}
do_execsql_test 1.4 { SELECT checkfreelist('main') } {ok}
do_execsql_test 1.5 {
WITH freelist_trunk(i, d, n) AS (
SELECT 1, NULL, sqlite_readint32(data, 32) FROM sqlite_dbpage WHERE pgno=1
UNION ALL
SELECT n, data, sqlite_readint32(data)
FROM freelist_trunk, sqlite_dbpage WHERE pgno=n
)
SELECT i FROM freelist_trunk WHERE i!=1;
} {
10009 9715 9343 8969 8595 8222 7847 7474 7102 6727 6354 5982 5608 5234
4860 4487 4112 3740 3367 2992 2619 2247 1872 1499 1125 752 377 5
}
do_execsql_test 1.6 { SELECT checkfreelist('main') } {ok}
proc set_int {blob idx newval} {
binary scan $blob I* ints
lset ints $idx $newval
binary format I* $ints
}
db func set_int set_int
proc get_int {blob idx} {
binary scan $blob I* ints
lindex $ints $idx
}
db func get_int get_int
do_execsql_test 1.7 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 1, get_int(data, 1)-1)
WHERE pgno=4860;
SELECT checkfreelist('main');
ROLLBACK;
} {{free-list count mismatch: actual=6725 header=6726}}
do_execsql_test 1.8 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 5, (SELECT * FROM pragma_page_count)+1)
WHERE pgno=4860;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf page 10092 is out of range (child 3 of trunk page 4860)}}
do_execsql_test 1.9 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 5, 0)
WHERE pgno=4860;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf page 0 is out of range (child 3 of trunk page 4860)}}
do_execsql_test 1.10 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, get_int(data, 1)+1, 0)
WHERE pgno=5;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf page 0 is out of range (child 247 of trunk page 5)}}
do_execsql_test 1.11 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 1, 249)
WHERE pgno=5;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf count out of range (249) on trunk page 5}}
+349
View File
@@ -0,0 +1,349 @@
# 2017-10-11
#
set testprefix checkindex
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
INSERT INTO t1 VALUES('one', 2);
INSERT INTO t1 VALUES('two', 4);
INSERT INTO t1 VALUES('three', 6);
INSERT INTO t1 VALUES('four', 8);
INSERT INTO t1 VALUES('five', 10);
CREATE INDEX i2 ON t1(a DESC);
} {}
proc incr_index_check {idx nStep} {
set Q {
SELECT errmsg, current_key FROM incremental_index_check($idx, $after)
LIMIT $nStep
}
set res [list]
while {1} {
unset -nocomplain current_key
set res1 [db eval $Q]
if {[llength $res1]==0} break
set res [concat $res $res1]
set after [lindex $res end]
}
return $res
}
proc do_index_check_test {tn idx res} {
uplevel [list do_execsql_test $tn.1 "
SELECT errmsg, current_key FROM incremental_index_check('$idx');
" $res]
uplevel [list do_test $tn.2 "incr_index_check $idx 1" [list {*}$res]]
uplevel [list do_test $tn.3 "incr_index_check $idx 2" [list {*}$res]]
uplevel [list do_test $tn.4 "incr_index_check $idx 5" [list {*}$res]]
}
do_execsql_test 1.2.1 {
SELECT rowid, errmsg IS NULL, current_key FROM incremental_index_check('i1');
} {
1 1 'five',5
2 1 'four',4
3 1 'one',1
4 1 'three',3
5 1 'two',2
}
do_execsql_test 1.2.2 {
SELECT errmsg IS NULL, current_key, index_name, after_key, scanner_sql
FROM incremental_index_check('i1') LIMIT 1;
} {
1
'five',5
i1
{}
{SELECT (SELECT a IS i.i0 FROM 't1' AS t WHERE "rowid" COLLATE BINARY IS i.i1), quote(i0)||','||quote(i1) FROM (SELECT (a) AS i0, ("rowid" COLLATE BINARY) AS i1 FROM 't1' INDEXED BY 'i1' ORDER BY 1,2) AS i}
}
do_index_check_test 1.3 i1 {
{} 'five',5
{} 'four',4
{} 'one',1
{} 'three',3
{} 'two',2
}
do_index_check_test 1.4 i2 {
{} 'two',2
{} 'three',3
{} 'one',1
{} 'four',4
{} 'five',5
}
do_test 1.5 {
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t1' }]
sqlite3_imposter db main $tblroot {CREATE TABLE xt1(a,b)}
db eval {
UPDATE xt1 SET a='six' WHERE rowid=3;
DELETE FROM xt1 WHERE rowid = 5;
}
sqlite3_imposter db main
} {}
do_index_check_test 1.6 i1 {
{row missing} 'five',5
{} 'four',4
{} 'one',1
{row data mismatch} 'three',3
{} 'two',2
}
do_index_check_test 1.7 i2 {
{} 'two',2
{row data mismatch} 'three',3
{} 'one',1
{} 'four',4
{row missing} 'five',5
}
#--------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c, d);
INSERT INTO t2 VALUES(1, NULL, 1, 1);
INSERT INTO t2 VALUES(2, 1, NULL, 1);
INSERT INTO t2 VALUES(3, 1, 1, NULL);
INSERT INTO t2 VALUES(4, 2, 2, 1);
INSERT INTO t2 VALUES(5, 2, 2, 2);
INSERT INTO t2 VALUES(6, 2, 2, 3);
INSERT INTO t2 VALUES(7, 2, 2, 1);
INSERT INTO t2 VALUES(8, 2, 2, 2);
INSERT INTO t2 VALUES(9, 2, 2, 3);
CREATE INDEX i3 ON t2(b, c, d);
CREATE INDEX i4 ON t2(b DESC, c DESC, d DESC);
CREATE INDEX i5 ON t2(d, c DESC, b);
} {}
do_index_check_test 2.1 i3 {
{} NULL,1,1,1
{} 1,NULL,1,2
{} 1,1,NULL,3
{} 2,2,1,4
{} 2,2,1,7
{} 2,2,2,5
{} 2,2,2,8
{} 2,2,3,6
{} 2,2,3,9
}
do_index_check_test 2.2 i4 {
{} 2,2,3,6
{} 2,2,3,9
{} 2,2,2,5
{} 2,2,2,8
{} 2,2,1,4
{} 2,2,1,7
{} 1,1,NULL,3
{} 1,NULL,1,2
{} NULL,1,1,1
}
do_index_check_test 2.3 i5 {
{} NULL,1,1,3
{} 1,2,2,4
{} 1,2,2,7
{} 1,1,NULL,1
{} 1,NULL,1,2
{} 2,2,2,5
{} 2,2,2,8
{} 3,2,2,6
{} 3,2,2,9
}
#--------------------------------------------------------------------------
do_execsql_test 3.0 {
CREATE TABLE t3(w, x, y, z PRIMARY KEY) WITHOUT ROWID;
CREATE INDEX t3wxy ON t3(w, x, y);
CREATE INDEX t3wxy2 ON t3(w DESC, x DESC, y DESC);
INSERT INTO t3 VALUES(NULL, NULL, NULL, 1);
INSERT INTO t3 VALUES(NULL, NULL, NULL, 2);
INSERT INTO t3 VALUES(NULL, NULL, NULL, 3);
INSERT INTO t3 VALUES('a', NULL, NULL, 4);
INSERT INTO t3 VALUES('a', NULL, NULL, 5);
INSERT INTO t3 VALUES('a', NULL, NULL, 6);
INSERT INTO t3 VALUES('a', 'b', NULL, 7);
INSERT INTO t3 VALUES('a', 'b', NULL, 8);
INSERT INTO t3 VALUES('a', 'b', NULL, 9);
} {}
do_index_check_test 3.1 t3wxy {
{} NULL,NULL,NULL,1 {} NULL,NULL,NULL,2 {} NULL,NULL,NULL,3
{} 'a',NULL,NULL,4 {} 'a',NULL,NULL,5 {} 'a',NULL,NULL,6
{} 'a','b',NULL,7 {} 'a','b',NULL,8 {} 'a','b',NULL,9
}
do_index_check_test 3.2 t3wxy2 {
{} 'a','b',NULL,7 {} 'a','b',NULL,8 {} 'a','b',NULL,9
{} 'a',NULL,NULL,4 {} 'a',NULL,NULL,5 {} 'a',NULL,NULL,6
{} NULL,NULL,NULL,1 {} NULL,NULL,NULL,2 {} NULL,NULL,NULL,3
}
#--------------------------------------------------------------------------
# Test with an index that uses non-default collation sequences.
#
do_execsql_test 4.0 {
CREATE TABLE t4(a INTEGER PRIMARY KEY, c1 TEXT, c2 TEXT);
INSERT INTO t4 VALUES(1, 'aaa', 'bbb');
INSERT INTO t4 VALUES(2, 'AAA', 'CCC');
INSERT INTO t4 VALUES(3, 'aab', 'ddd');
INSERT INTO t4 VALUES(4, 'AAB', 'EEE');
CREATE INDEX t4cc ON t4(c1 COLLATE nocase, c2 COLLATE nocase);
}
do_index_check_test 4.1 t4cc {
{} 'aaa','bbb',1
{} 'AAA','CCC',2
{} 'aab','ddd',3
{} 'AAB','EEE',4
}
do_test 4.2 {
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t4' }]
sqlite3_imposter db main $tblroot \
{CREATE TABLE xt4(a INTEGER PRIMARY KEY, c1 TEXT, c2 TEXT)}
db eval {
UPDATE xt4 SET c1='hello' WHERE rowid=2;
DELETE FROM xt4 WHERE rowid = 3;
}
sqlite3_imposter db main
} {}
do_index_check_test 4.3 t4cc {
{} 'aaa','bbb',1
{row data mismatch} 'AAA','CCC',2
{row missing} 'aab','ddd',3
{} 'AAB','EEE',4
}
#--------------------------------------------------------------------------
# Test an index on an expression.
#
do_execsql_test 5.0 {
CREATE TABLE t5(x INTEGER PRIMARY KEY, y TEXT, UNIQUE(y));
INSERT INTO t5 VALUES(1, '{"x":1, "y":1}');
INSERT INTO t5 VALUES(2, '{"x":2, "y":2}');
INSERT INTO t5 VALUES(3, '{"x":3, "y":3}');
INSERT INTO t5 VALUES(4, '{"w":4, "z":4}');
INSERT INTO t5 VALUES(5, '{"x":5, "y":5}');
CREATE INDEX t5x ON t5( json_extract(y, '$.x') );
CREATE INDEX t5y ON t5( json_extract(y, '$.y') DESC );
}
do_index_check_test 5.1.1 t5x {
{} NULL,4 {} 1,1 {} 2,2 {} 3,3 {} 5,5
}
do_index_check_test 5.1.2 t5y {
{} 5,5 {} 3,3 {} 2,2 {} 1,1 {} NULL,4
}
do_index_check_test 5.1.3 sqlite_autoindex_t5_1 {
{} {'{"w":4, "z":4}',4}
{} {'{"x":1, "y":1}',1}
{} {'{"x":2, "y":2}',2}
{} {'{"x":3, "y":3}',3}
{} {'{"x":5, "y":5}',5}
}
do_test 5.2 {
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t5' }]
sqlite3_imposter db main $tblroot \
{CREATE TABLE xt5(a INTEGER PRIMARY KEY, c1 TEXT);}
db eval {
UPDATE xt5 SET c1='{"x":22, "y":11}' WHERE rowid=1;
DELETE FROM xt5 WHERE rowid = 4;
}
sqlite3_imposter db main
} {}
do_index_check_test 5.3.1 t5x {
{row missing} NULL,4
{row data mismatch} 1,1
{} 2,2
{} 3,3
{} 5,5
}
do_index_check_test 5.3.2 sqlite_autoindex_t5_1 {
{row missing} {'{"w":4, "z":4}',4}
{row data mismatch} {'{"x":1, "y":1}',1}
{} {'{"x":2, "y":2}',2}
{} {'{"x":3, "y":3}',3}
{} {'{"x":5, "y":5}',5}
}
#-------------------------------------------------------------------------
#
do_execsql_test 6.0 {
CREATE TABLE t6(x INTEGER PRIMARY KEY, y, z);
CREATE INDEX t6x1 ON t6(y, /* one,two,three */ z);
CREATE INDEX t6x2 ON t6(z, -- hello,world,
y);
CREATE INDEX t6x3 ON t6(z -- hello,world
, y);
INSERT INTO t6 VALUES(1, 2, 3);
INSERT INTO t6 VALUES(4, 5, 6);
}
do_index_check_test 6.1 t6x1 {
{} 2,3,1
{} 5,6,4
}
do_index_check_test 6.2 t6x2 {
{} 3,2,1
{} 6,5,4
}
do_index_check_test 6.2 t6x3 {
{} 3,2,1
{} 6,5,4
}
#-------------------------------------------------------------------------
#
do_execsql_test 7.0 {
CREATE TABLE t7(x INTEGER PRIMARY KEY, y, z);
INSERT INTO t7 VALUES(1, 1, 1);
INSERT INTO t7 VALUES(2, 2, 0);
INSERT INTO t7 VALUES(3, 3, 1);
INSERT INTO t7 VALUES(4, 4, 0);
CREATE INDEX t7i1 ON t7(y) WHERE z=1;
CREATE INDEX t7i2 ON t7(y) /* hello,world */ WHERE z=1;
CREATE INDEX t7i3 ON t7(y) WHERE -- yep
z=1;
CREATE INDEX t7i4 ON t7(y) WHERE z=1 -- yep;
}
do_index_check_test 7.1 t7i1 {
{} 1,1 {} 3,3
}
do_index_check_test 7.2 t7i2 {
{} 1,1 {} 3,3
}
do_index_check_test 7.3 t7i3 {
{} 1,1 {} 3,3
}
do_index_check_test 7.4 t7i4 {
{} 1,1 {} 3,3
}
+67
View File
@@ -0,0 +1,67 @@
# Run this script using
#
# sqlite3_checker --test $thisscript $testscripts
#
# The $testscripts argument is optional. If omitted, all *.test files
# in the same directory as $thisscript are run.
#
set NTEST 0
set NERR 0
# Invoke the do_test procedure to run a single test
#
# The $expected parameter is the expected result. The result is the return
# value from the last TCL command in $cmd.
#
# Normally, $expected must match exactly. But if $expected is of the form
# "/regexp/" then regular expression matching is used. If $expected is
# "~/regexp/" then the regular expression must NOT match. If $expected is
# of the form "#/value-list/" then each term in value-list must be numeric
# and must approximately match the corresponding numeric term in $result.
# Values must match within 10%. Or if the $expected term is A..B then the
# $result term must be in between A and B.
#
proc do_test {name cmd expected} {
if {[info exists ::testprefix]} {
set name "$::testprefix$name"
}
incr ::NTEST
puts -nonewline $name...
flush stdout
if {[catch {uplevel #0 "$cmd;\n"} result]} {
puts -nonewline $name...
puts "\nError: $result"
incr ::NERR
} else {
set ok [expr {[string compare $result $expected]==0}]
if {!$ok} {
puts "\n! $name expected: \[$expected\]\n! $name got: \[$result\]"
incr ::NERR
} else {
puts " Ok"
}
}
flush stdout
}
#
# do_execsql_test TESTNAME SQL RES
#
proc do_execsql_test {testname sql {result {}}} {
uplevel [list do_test $testname [list db eval $sql] [list {*}$result]]
}
if {[llength $argv]==0} {
set dir [file dirname $argv0]
set argv [glob -nocomplain $dir/*.test]
}
foreach testfile $argv {
file delete -force test.db
sqlite3 db test.db
source $testfile
catch {db close}
}
puts "$NERR errors out of $NTEST tests"
+1 -1
View File
@@ -200,7 +200,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
/* The sqlite3AtoF() routine is much much faster than atof(), if it
** is available */
double r;
(void)sqlite3AtoF((const char*)p->z, &r);
(void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
*pVal = r;
#else
*pVal = (GeoCoord)atof((const char*)p->z);
+13 -14
View File
@@ -1037,17 +1037,7 @@ static void rtreeRelease(Rtree *pRtree){
pRtree->inWrTrans = 0;
assert( pRtree->nCursor==0 );
nodeBlobReset(pRtree);
if( pRtree->nNodeRef ){
int i;
assert( pRtree->bCorrupt );
for(i=0; i<HASHSIZE; i++){
while( pRtree->aHash[i] ){
RtreeNode *pNext = pRtree->aHash[i]->pNext;
sqlite3_free(pRtree->aHash[i]);
pRtree->aHash[i] = pNext;
}
}
}
assert( pRtree->nNodeRef==0 || pRtree->bCorrupt );
sqlite3_finalize(pRtree->pWriteNode);
sqlite3_finalize(pRtree->pDeleteNode);
sqlite3_finalize(pRtree->pReadRowid);
@@ -2339,7 +2329,7 @@ static int AdjustTree(
int iCell;
cnt++;
if( cnt>100 ){
if( NEVER(cnt>100) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
@@ -2697,6 +2687,15 @@ static int SplitNode(
rc = updateMapping(pRtree, pCell->iRowid, pLeft, iHeight);
}
if( rc==SQLITE_OK ){
rc = nodeRelease(pRtree, pRight);
pRight = 0;
}
if( rc==SQLITE_OK ){
rc = nodeRelease(pRtree, pLeft);
pLeft = 0;
}
splitnode_out:
nodeRelease(pRtree, pRight);
nodeRelease(pRtree, pLeft);
@@ -2881,7 +2880,7 @@ static int rtreeInsertCell(
rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
rc = AdjustTree(pRtree, pNode, pCell);
if( rc==SQLITE_OK ){
if( ALWAYS(rc==SQLITE_OK) ){
if( iHeight==0 ){
rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode);
}else{
@@ -3776,7 +3775,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
if( node.zData==0 ) return;
nData = sqlite3_value_bytes(apArg[1]);
if( nData<4 ) return;
if( nData<4+NCELL(&node)*tree.nBytesPerCell ) return;
if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
pOut = sqlite3_str_new(0);
for(ii=0; ii<NCELL(&node); ii++){
-10
View File
@@ -47,14 +47,4 @@ ifcapable rtree_int_only {
do_rtree_integrity_test rtreeB-1.2 t1
# https://sqlite.org/forum/forumpost/2026-01-08T23:32:19Z
#
db null NULL
do_execsql_test rtreeB-2.1 {
SELECT rtreenode(1,x'00000001'||randomblob(15)) IS NULL;
} {1}
do_execsql_test rtreeB-2.2 {
SELECT rtreenode(1,x'00000001'||randomblob(16)) IS NOT NULL;
} {1}
finish_test
-1
View File
@@ -135,7 +135,6 @@ foreach {tn blob} {
54 540101743400120003001200010000000000000002120002400C000000000002120002400C00000000000050040100000074310017FF0050040100000074310017FF7F00000000000000050100000000000000030100000003001700010000666F7572
55 540101743400120003001200010000000000000002120002400C00000000000050040100000074310017000100010080000001000000020003010100000300170100000003001700010000666F7572
56 5487ffffff7f
57 54015052494e5446110017120004
} {
do_test 2.$tn {
set changeset [binary decode hex $blob]
-67
View File
@@ -192,73 +192,6 @@ do_test 3.3 {
}
} {1 1 3 3}
#-------------------------------------------------------------------------
#
reset_db
set C [binary format c* {0x54 0x01 0x01 0x00 0x12 0x00 0x05}]
do_test 4.0 {
sqlite3changegroup grp
list [catch { grp add $C } msg] $msg
} {0 {}}
grp delete
#-------------------------------------------------------------------------
#
reset_db
set C [binary format c* {0x54 0xda 0xda 0xda 0xda 0xda}]
do_execsql_test 5.0 {
CREATE TABLE t1(a PRIMARY KEY, b, c, d);
}
do_test 5.1 {
list [catch { sqlite3changeset_apply db $C noop xFilter } msg] $msg
} {1 SQLITE_CORRUPT}
#-------------------------------------------------------------------------
#
foreach {tn type C2hex C3hex} {
1 changeset 54020100743100170001000000000000000A030374656E000306656C6576656E
54020100743100170001000000000000000A030374656E0003FFFF01656C6576656E
2 patchset 50020100743100170001000000000000000A0306656C6576656E
50020100743100170001000000000000000A03FFFF06656C6576656E
3 changeset 54020100743100170001000000000000000A030374656E000306656C6576656E
54020100743100170001000000000000000A030374656E0003FFFF
} {
reset_db
do_execsql_test 6.$tn.0 {
CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'three');
} {}
do_test 6.$tn.1 {
set C1 [${type}_from_sql {
INSERT INTO t1 VALUES(10, 'ten');
}]
set C2 [${type}_from_sql {
UPDATE t1 SET y='eleven' WHERE x=10;
}]
db one { SELECT quote($C2) }
} "X'$C2hex'"
do_test 6.$tn.2 {
changeset_to_list [sqlite3changeset_concat $C1 $C2]
} {{INSERT t1 0 X. {} {i 10 t eleven}}}
set C3 [db one {SELECT unhex($C3hex)}]
do_test 6.$tn.3 {
list [catch { sqlite3changeset_concat $C1 $C3 } msg] $msg
} {1 SQLITE_CORRUPT}
}
finish_test
-400
View File
@@ -1,400 +0,0 @@
# 2026 January 09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix sessionchange2
do_test 1.1.1 {
sqlite3changegroup grp
list [catch { grp change_begin INSERT "nosuchtable" 0 } msg] $msg
} {1 {SQLITE_ERROR - no such table: nosuchtable}}
do_test 1.1.2 {
grp schema db main
list [catch { grp change_begin INSERT "nosuchtable" 0 } msg] $msg
} {1 {SQLITE_ERROR - no such table: nosuchtable}}
do_test 1.1.3 {
list [catch { grp change_begin_ne INSERT "nosuchtable" 0 } msg] $msg
} {1 SQLITE_ERROR}
do_execsql_test 1.2.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
}
do_test 1.2.2 {
list [catch { grp change_begin 435 "t1" 0 } msg] $msg
} {1 SQLITE_ERROR}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a, b);
}
do_test 2.1.1 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT "t1" 0
grp change_int64 new 0 55
grp change_int64 new 1 101
grp change_finish false
} {}
do_test 2.1.2 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{INSERT t1 0 X. {} {i 55 i 101}}
}
do_test 2.2.1 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT "t2" 0
grp change_int64 new 0 -5
grp change_int64 new 1 -10
grp change_int64 new 2 -20
grp change_finish false
} {}
do_test 2.2.2 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{INSERT t2 0 X.. {} {i -5 i -10 i -20}}
}
do_test 2.2.3 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT "t1" 0
grp change_int64 new 0 223344
grp change_null new 1
grp change_finish false
} {}
do_test 2.2.4 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{INSERT t1 0 X. {} {i 223344 n {}}}
}
do_test 2.2.5 {
sqlite3changegroup grp
grp schema db main
grp change_begin DELETE "t1" 0
grp change_int64 old 0 1
grp change_int64 old 1 123
grp change_finish false
} {}
do_test 2.2.6 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{DELETE t1 0 X. {i 1 i 123} {}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE TABLE t2(x, y);
}
foreach {tn script error} {
1 {
grp change_begin UPDATE t1 0
} {SQLITE_ERROR - invalid change: undefined value in PK of old.* record}
2 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 1234
grp change_int64 new 0 5678
} {SQLITE_ERROR - invalid change: defined value in PK of new.* record}
3 {
grp change_begin UPDATE t1 0
grp change_null old 0
} {SQLITE_ERROR - invalid change: null value in PK of old.* record}
4 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 1234
grp change_int64 old 1 20
} {SQLITE_ERROR - invalid change: column 1 - old.* value is defined but new.* is undefined}
5 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 1234
grp change_int64 new 1 20
} {SQLITE_ERROR - invalid change: column 1 - old.* value is undefined but new.* is defined}
6 {
grp change_begin INSERT t1 0
grp change_null new 0
grp change_int64 new 1 20
} {SQLITE_ERROR - invalid change: null value in PK}
7 {
grp change_begin INSERT t1 0
grp change_int64 new 1 20
} {SQLITE_ERROR - invalid change: column 0 is undefined}
8 {
grp change_begin INSERT t1 0
grp change_int64 new 0 20
} {SQLITE_ERROR - invalid change: column 1 is undefined}
9 {
grp change_begin DELETE t1 0
grp change_int64 old 0 20
} {SQLITE_ERROR - invalid change: column 1 is undefined}
10 {
grp change_begin DELETE t1 0
grp change_int64 old 1 20
} {SQLITE_ERROR - invalid change: column 0 is undefined}
} {
sqlite3changegroup grp
grp schema db main
eval $script
do_test 3.1.$tn {
list [catch { grp change_finish false } msg] $msg
} [list 1 $error]
grp delete
}
do_test 3.2.1 {
sqlite3changegroup grp
grp schema db main
grp change_begin DELETE t1 0
list [catch {grp change_int64 new 0 20} msg] $msg
} {1 SQLITE_ERROR}
do_test 3.2.2 {
grp change_finish true
grp change_begin INSERT t1 0
list [catch {grp change_int64 old 0 20} msg] $msg
} {1 SQLITE_ERROR}
grp delete
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a, b);
}
proc do_changegroup_script_test {tn script changeset patchset} {
breakpoint
sqlite3changegroup grp
grp schema db main
eval $script
do_test $tn.1 {
changeset_to_list [grp output]
} [list {*}$changeset]
grp delete
sqlite3changegroup grp
grp config patchset 1
grp schema db main
eval $script
do_test $tn.2 {
changeset_to_list [grp output]
} [list {*}$patchset]
grp delete
}
proc do_changegroup_patchset_test {tn script patchset} {
sqlite3changegroup grp
grp config patchset 1
grp schema db main
eval $script
do_test $tn {
changeset_to_list [grp output]
} [list {*}$patchset]
grp delete
}
do_changegroup_script_test 3.1 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 123
grp change_int64 old 1 456
grp change_int64 new 1 789
grp change_finish false
grp change_begin INSERT t1 0
grp change_int64 new 0 124
grp change_text new 1 hello_world
grp change_finish false
grp change_begin DELETE t1 0
grp change_int64 old 0 125
grp change_double old 1 45.67
grp change_finish false
} {
{DELETE t1 0 X. {i 125 f 45.67} {}}
{INSERT t1 0 X. {} {i 124 t hello_world}}
{UPDATE t1 0 X. {i 123 i 456} {{} {} i 789}}
} {
{DELETE t1 0 X. {i 125 {} {}} {}}
{INSERT t1 0 X. {} {i 124 t hello_world}}
{UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
}
do_changegroup_script_test 3.1.2 {
grp change_begin INSERT t1 0
grp change_int64 new 0 124
grp change_text new 1 hello_world
grp change_finish false
} {
{INSERT t1 0 X. {} {i 124 t hello_world}}
} {
{INSERT t1 0 X. {} {i 124 t hello_world}}
}
do_changegroup_patchset_test 3.2 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 123
grp change_int64 new 1 789
grp change_finish false
grp change_begin DELETE t1 1
grp change_int64 old 0 122
grp change_finish false
} {
{DELETE t1 1 X. {i 122 {} {}} {}}
{UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
}
do_changegroup_patchset_test 3.3 {
grp change_begin DELETE t1 0
grp change_int64 old 0 123
grp change_finish false
} {
{DELETE t1 0 X. {i 123 {} {}} {}}
}
do_changegroup_script_test 3.4 {
grp change_begin INSERT t1 0
grp change_int64 new 0 124
grp change_text new 1 hello_world
grp change_finish false
grp change_begin DELETE t1 0
grp change_int64 old 0 124
grp change_text old 1 hello_world
grp change_finish false
grp change_begin INSERT t1 0
grp change_int64 new 0 1000
grp change_blob new 1 abcd
grp change_finish false
grp change_begin UPDATE t1 0
grp change_int64 old 0 1000
grp change_blob old 1 abcd
grp change_blob new 1 bcda
grp change_finish false
} {
{INSERT t1 0 X. {} {i 1000 b bcda}}
} {
{INSERT t1 0 X. {} {i 1000 b bcda}}
}
do_changegroup_script_test 3.5 {
grp change_begin DELETE t1 0
grp change_int64 old 0 1000
grp change_text-1 old 1 "hello world"
grp change_finish false
} {
{DELETE t1 0 X. {i 1000 t {hello world}} {}}
} {
{DELETE t1 0 X. {i 1000 {} {}} {}}
}
#-------------------------------------------------------------------------
# Calling sqlite3changegroup_change_finish() if a change has not been
# started is a no-op.
#
do_test 4.0 {
sqlite3changegroup grp
grp change_finish false
} {}
# But calling any other sqlite3changegroup_change_xxx() function if a
# change has not been started is a misuse.
#
foreach {tn cmd} {
1 { grp change_blob old 0 abcd }
2 { grp change_text new 1 helloworld }
3 { grp change_int64 old 2 12345678 }
4 { grp change_double new 3 12.345 }
5 { grp change_null old 4 }
} {
do_test 4.1.$tn {
list [catch $::cmd msg] $msg
} {1 SQLITE_MISUSE}
}
grp delete
do_execsql_test 4.2.1 {
CREATE TABLE t3(a, b PRIMARY KEY);
}
do_test 4.2 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT t3 0
list [catch { grp change_begin INSERT t3 0 } msg] $msg
} {1 SQLITE_MISUSE}
grp delete
do_test 4.3 {
sqlite3changegroup grp
grp schema db main
grp change_begin UPDATE t3 0
} {}
foreach {tn cmd} {
1 { grp change_blob old -1 abcd }
2 { grp change_text new 2 helloworld }
} {
do_test 4.4.$tn {
list [catch $::cmd msg] $msg
} {1 SQLITE_RANGE}
}
grp delete
finish_test
+1 -15
View File
@@ -43,7 +43,7 @@ set C2 [changeset_from_sql {
INSERT INTO t1 VALUES(0, 0, 0, 0);
}]
do_faultsim_test 1.2 -faults oom* -prep {
do_faultsim_test 1 -faults oom* -prep {
sqlite3changegroup G
} -body {
G schema db main
@@ -56,20 +56,6 @@ do_faultsim_test 1.2 -faults oom* -prep {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
do_faultsim_test 1.3 -faults oom* -prep {
sqlite3changegroup G
} -body {
G schema db main
G change_begin INSERT t1 0
G change_int64 new 0 12345
G change_int64 new 1 67890
G output
set {} {}
} -test {
catch { G delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
+79 -525
View File
@@ -349,21 +349,6 @@ static int sessionVarintGet(const u8 *aBuf, int *piVal){
return getVarint32(aBuf, *piVal);
}
/*
** Read a varint value from buffer aBuf[], size nBuf bytes, into *piVal.
** Return the number of bytes read.
*/
static int sessionVarintGetSafe(const u8 *aBuf, int nBuf, int *piVal){
u8 aCopy[9];
const u8 *aRead = aBuf;
memset(aCopy, 0, sizeof(aCopy));
if( nBuf<sizeof(aCopy) ){
memcpy(aCopy, aBuf, nBuf);
aRead = aCopy;
}
return getVarint32(aRead, *piVal);
}
/* Load an unaligned and unsigned 32-bit integer */
#define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
@@ -392,19 +377,6 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
aBuf[7] = (i>> 0) & 0xFF;
}
/*
** Write a double value to the buffer aBuf[].
*/
static void sessionPutDouble(u8 *aBuf, double r){
/* TODO: SQLite does something special to deal with mixed-endian
** floating point values (e.g. ARM7). This code probably should
** too. */
u64 i;
assert( sizeof(double)==8 && sizeof(u64)==8 );
memcpy(&i, &r, 8);
sessionPutI64(aBuf, i);
}
/*
** This function is used to serialize the contents of value pValue (see
** comment titled "RECORD FORMAT" above).
@@ -442,13 +414,16 @@ static int sessionSerializeValue(
/* TODO: SQLite does something special to deal with mixed-endian
** floating point values (e.g. ARM7). This code probably should
** too. */
u64 i;
if( eType==SQLITE_INTEGER ){
u64 i = (u64)sqlite3_value_int64(pValue);
sessionPutI64(&aBuf[1], i);
i = (u64)sqlite3_value_int64(pValue);
}else{
double r = sqlite3_value_double(pValue);
sessionPutDouble(&aBuf[1], r);
double r;
assert( sizeof(double)==8 && sizeof(u64)==8 );
r = sqlite3_value_double(pValue);
memcpy(&i, &r, 8);
}
sessionPutI64(&aBuf[1], i);
}
nByte = 9;
break;
@@ -668,10 +643,14 @@ static unsigned int sessionChangeHash(
int isPK = pTab->abPK[i];
if( bPkOnly && isPK==0 ) continue;
/* It is not possible for eType to be SQLITE_NULL here. The session
** module does not record changes for rows with NULL values stored in
** primary key columns. */
assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
|| eType==SQLITE_TEXT || eType==SQLITE_BLOB
|| eType==SQLITE_NULL || eType==0
);
assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
if( isPK ){
a++;
@@ -679,16 +658,12 @@ static unsigned int sessionChangeHash(
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
h = sessionHashAppendI64(h, sessionGetI64(a));
a += 8;
}else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
}else{
int n;
a += sessionVarintGet(a, &n);
h = sessionHashAppendBlob(h, n, a);
a += n;
}
/* It should not be possible for eType to be SQLITE_NULL or 0x00 here,
** as the session module does not record changes for rows with NULL
** values stored in primary key columns. But a corrupt changesets
** may contain such a value. */
}else{
a += sessionSerialLen(a);
}
@@ -1380,7 +1355,9 @@ static void sessionUpdateOneChange(
case SQLITE_FLOAT: {
double rVal = sqlite3_column_double(pDflt, iField);
sessionPutDouble(&pNew->aRecord[pNew->nRecord], rVal);
i64 iVal = 0;
memcpy(&iVal, &rVal, sizeof(rVal));
sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
pNew->nRecord += 8;
break;
}
@@ -2637,14 +2614,15 @@ static void sessionAppendCol(
int eType = sqlite3_column_type(pStmt, iCol);
sessionAppendByte(p, (u8)eType, pRc);
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
sqlite3_int64 i;
u8 aBuf[8];
if( eType==SQLITE_INTEGER ){
sqlite3_int64 i = sqlite3_column_int64(pStmt, iCol);
sessionPutI64(aBuf, i);
i = sqlite3_column_int64(pStmt, iCol);
}else{
double r = sqlite3_column_double(pStmt, iCol);
sessionPutDouble(aBuf, r);
memcpy(&i, &r, 8);
}
sessionPutI64(aBuf, i);
sessionAppendBlob(p, aBuf, 8, pRc);
}
if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
@@ -3094,13 +3072,10 @@ static int sessionGenerateChangeset(
}
if( pSession->rc ) return pSession->rc;
rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_mutex_enter(sqlite3_db_mutex(db));
rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
if( rc!=SQLITE_OK ){
sqlite3_mutex_leave(sqlite3_db_mutex(db));
return rc;
}
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
if( pTab->nEntry ){
@@ -3583,8 +3558,7 @@ static int sessionReadRecord(
u8 *aVal = &pIn->aData[pIn->iNext];
if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
int nByte;
int nRem = pIn->nData - pIn->iNext;
pIn->iNext += sessionVarintGetSafe(aVal, nRem, &nByte);
pIn->iNext += sessionVarintGet(aVal, &nByte);
rc = sessionInputBuffer(pIn, nByte);
if( rc==SQLITE_OK ){
if( nByte<0 || nByte>pIn->nData-pIn->iNext ){
@@ -3637,8 +3611,7 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
rc = sessionInputBuffer(pIn, 9);
if( rc==SQLITE_OK ){
int nBuf = pIn->nData - pIn->iNext;
nRead += sessionVarintGetSafe(&pIn->aData[pIn->iNext], nBuf, &nCol);
nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
/* The hard upper limit for the number of columns in an SQLite
** database table is, according to sqliteLimit.h, 32676. So
** consider any table-header that purports to have more than 65536
@@ -3658,15 +3631,8 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){
nRead++;
}
/* Break out of the loop if if the nul-terminator byte has been found.
** Otherwise, read some more input data and keep seeking. If there is
** no more input data, consider the changeset corrupt. */
if( (pIn->iNext + nRead)<pIn->nData ) break;
rc = sessionInputBuffer(pIn, nRead + 100);
if( rc==SQLITE_OK && (pIn->iNext + nRead)>=pIn->nData ){
rc = SQLITE_CORRUPT_BKPT;
}
}
*pnByte = nRead+1;
return rc;
@@ -3687,7 +3653,7 @@ static int sessionChangesetBufferRecord(
int *pnByte /* OUT: Size of record in bytes */
){
int rc = SQLITE_OK;
i64 nByte = 0;
int nByte = 0;
int i;
for(i=0; rc==SQLITE_OK && i<nCol; i++){
int eType;
@@ -3696,17 +3662,13 @@ static int sessionChangesetBufferRecord(
eType = pIn->aData[pIn->iNext + nByte++];
if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
int n;
int nRem = pIn->nData - (pIn->iNext + nByte);
nByte += sessionVarintGetSafe(&pIn->aData[pIn->iNext+nByte], nRem, &n);
nByte += sessionVarintGet(&pIn->aData[pIn->iNext+nByte], &n);
nByte += n;
rc = sessionInputBuffer(pIn, nByte);
}else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
nByte += 8;
}
}
if( (pIn->iNext+nByte)>pIn->nData ){
rc = SQLITE_CORRUPT_BKPT;
}
}
*pnByte = nByte;
return rc;
@@ -3802,10 +3764,10 @@ static int sessionChangesetNextOne(
memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
}
/* Make sure the buffer contains at least 2 bytes of input data, or all
** remaining data if there are less than 2 bytes available. This is
** sufficient either for the 'T' or 'P' byte that begins a new table,
** or for the "op" and "bIndirect" single bytes otherwise. */
/* Make sure the buffer contains at least 10 bytes of input data, or all
** remaining data if there are less than 10 bytes available. This is
** sufficient either for the 'T' or 'P' byte and the varint that follows
** it, or for the two single byte values otherwise. */
p->rc = sessionInputBuffer(&p->in, 2);
if( p->rc!=SQLITE_OK ) return p->rc;
@@ -3835,13 +3797,11 @@ static int sessionChangesetNextOne(
return (p->rc = SQLITE_CORRUPT_BKPT);
}
if( (op!=SQLITE_UPDATE && op!=SQLITE_DELETE && op!=SQLITE_INSERT)
|| (p->in.iNext>=p->in.nData)
){
return (p->rc = SQLITE_CORRUPT_BKPT);
}
p->op = op;
p->bIndirect = p->in.aData[p->in.iNext++];
if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
return (p->rc = SQLITE_CORRUPT_BKPT);
}
if( paRec ){
int nVal; /* Number of values to buffer */
@@ -5689,21 +5649,6 @@ int sqlite3changeset_apply_strm(
);
}
/*
** The parts of the sqlite3_changegroup structure used by the
** sqlite3changegroup_change_xxx() APIs.
*/
typedef struct ChangeData ChangeData;
struct ChangeData {
SessionTable *pTab;
int bIndirect;
int eOp;
int nBufAlloc;
SessionBuffer *aBuf;
SessionBuffer record;
};
/*
** sqlite3_changegroup handle.
*/
@@ -5715,17 +5660,12 @@ struct sqlite3_changegroup {
sqlite3 *db; /* Configured by changegroup_schema() */
char *zDb; /* Configured by changegroup_schema() */
ChangeData cd; /* Used by changegroup_change_xxx() APIs. */
};
/*
** This function is called to merge two changes to the same row together as
** part of an sqlite3changeset_concat() operation. A new change object is
** allocated and a pointer to it stored in *ppNew.
**
** Because they have been vetted by sqlite3changegroup_add() or similar,
** both the aRec[] change and the pExist change are safe to use without
** checking for buffer overflows.
*/
static int sessionChangeMerge(
SessionTable *pTab, /* Table structure */
@@ -5866,7 +5806,7 @@ static int sessionChangeMerge(
memcpy(aCsr, aRec, nRec);
aCsr += nRec;
}else{
if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist,0,aRec,0) ){
if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0,aRec,0) ){
sqlite3_free(pNew);
pNew = 0;
}
@@ -5959,14 +5899,15 @@ static int sessionChangesetExtendRecord(
switch( eType ){
case SQLITE_FLOAT:
case SQLITE_INTEGER: {
i64 iVal;
if( eType==SQLITE_INTEGER ){
iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
}else{
double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
memcpy(&iVal, &rVal, sizeof(i64));
}
if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
if( eType==SQLITE_INTEGER ){
sqlite3_int64 iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
}else{
double rVal = sqlite3_column_double(pTab->pDfltStmt, ii);
sessionPutDouble(&pOut->aBuf[pOut->nBuf], rVal);
}
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
pOut->nBuf += 8;
}
break;
@@ -6037,19 +5978,13 @@ static int sessionChangesetFindTable(
int nCol = 0;
*ppTab = 0;
sqlite3changeset_pk(pIter, &abPK, &nCol);
/* Search the list for an existing table */
for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
}
if( pIter ){
sqlite3changeset_pk(pIter, &abPK, &nCol);
}else if( !pTab && !pGrp->db ){
return SQLITE_OK;
}
/* If one was not found above, create a new table now */
if( !pTab ){
SessionTable **ppNew;
@@ -6061,17 +5996,15 @@ static int sessionChangesetFindTable(
memset(pTab, 0, sizeof(SessionTable));
pTab->nCol = nCol;
pTab->abPK = (u8*)&pTab[1];
if( nCol>0 ){
memcpy(pTab->abPK, abPK, nCol);
}
memcpy(pTab->abPK, abPK, nCol);
pTab->zName = (char*)&pTab->abPK[nCol];
memcpy(pTab->zName, zTab, nTab+1);
if( pGrp->db ){
pTab->nCol = 0;
rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
if( rc || pTab->nCol==0 ){
sqlite3_free(pTab->azCol);
if( rc ){
assert( pTab->azCol==0 );
sqlite3_free(pTab);
return rc;
}
@@ -6086,7 +6019,7 @@ static int sessionChangesetFindTable(
}
/* Check that the table is compatible. */
if( pIter && !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
rc = SQLITE_SCHEMA;
}
@@ -6095,27 +6028,44 @@ static int sessionChangesetFindTable(
}
/*
** Add a single change to the changegroup pGrp.
** Add the change currently indicated by iterator pIter to the hash table
** belonging to changegroup pGrp.
*/
static int sessionOneChangeToHash(
sqlite3_changegroup *pGrp, /* Changegroup to update */
SessionTable *pTab, /* Table change pertains to */
int op, /* One of SQLITE_INSERT, UPDATE, DELETE */
int bIndirect, /* True to flag change as "indirect" */
int nCol, /* Number of columns in record(s) */
u8 *aRec, /* Serialized change record(s) */
int nRec, /* Size of aRec[] in bytes */
int bRebase /* True if this is a rebase blob */
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter,
int bRebase
){
int rc = SQLITE_OK;
int nCol = 0;
int op = 0;
int iHash = 0;
int bIndirect = 0;
SessionChange *pChange = 0;
SessionChange *pExist = 0;
SessionChange **pp = 0;
SessionTable *pTab = 0;
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
assert( nRec>0 );
if( nCol<pTab->nCol ){
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
const char *zTab = 0;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
}
if( rc==SQLITE_OK && nCol<pTab->nCol ){
SessionBuffer *pBuf = &pGrp->rec;
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
aRec = pBuf->aBuf;
@@ -6123,7 +6073,7 @@ static int sessionOneChangeToHash(
assert( pGrp->db );
}
if( rc==SQLITE_OK && sessionGrowHash(0, pGrp->bPatch, pTab) ){
if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
}
@@ -6131,12 +6081,12 @@ static int sessionOneChangeToHash(
/* Search for existing entry. If found, remove it from the hash table.
** Code below may link it back in. */
iHash = sessionChangeHash(
pTab, (pGrp->bPatch && op==SQLITE_DELETE), aRec, pTab->nChange
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
);
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
int bPkOnly1 = 0;
int bPkOnly2 = 0;
if( pGrp->bPatch ){
if( pIter->bPatchset ){
bPkOnly1 = (*pp)->op==SQLITE_DELETE;
bPkOnly2 = op==SQLITE_DELETE;
}
@@ -6151,7 +6101,7 @@ static int sessionOneChangeToHash(
if( rc==SQLITE_OK ){
rc = sessionChangeMerge(pTab, bRebase,
pGrp->bPatch, pExist, op, bIndirect, aRec, nRec, &pChange
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
);
}
if( rc==SQLITE_OK && pChange ){
@@ -6160,47 +6110,6 @@ static int sessionOneChangeToHash(
pTab->nEntry++;
}
return rc;
}
/*
** Add the change currently indicated by iterator pIter to the hash table
** belonging to changegroup pGrp.
*/
static int sessionOneChangeIterToHash(
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter,
int bRebase
){
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
const char *zTab = 0;
int nCol = 0;
int op = 0;
int bIndirect = 0;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
}
if( rc==SQLITE_OK ){
rc = sessionOneChangeToHash(
pGrp, pTab, op, bIndirect, nCol, aRec, nRec, bRebase
);
}
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
@@ -6220,7 +6129,7 @@ static int sessionChangesetToHash(
pIter->in.bNoDiscard = 1;
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase);
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
if( rc!=SQLITE_OK ) break;
}
@@ -6310,33 +6219,6 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp){
return rc;
}
/*
** Configure a changegroup object.
*/
int sqlite3changegroup_config(
sqlite3_changegroup *pGrp,
int op,
void *pArg
){
int rc = SQLITE_OK;
switch( op ){
case SQLITE_CHANGEGROUP_CONFIG_PATCHSET: {
int arg = *(int*)pArg;
if( pGrp->pList==0 && arg>=0 ){
pGrp->bPatch = (arg>0);
}
*(int*)pArg = pGrp->bPatch;
break;
}
default:
rc = SQLITE_MISUSE;
break;
}
return rc;
}
/*
** Provide a database schema to the changegroup object.
*/
@@ -6395,7 +6277,7 @@ int sqlite3changegroup_add_change(
rc = SQLITE_ERROR;
}else{
pIter->in.bNoDiscard = 1;
rc = sessionOneChangeIterToHash(pGrp, pIter, 0);
rc = sessionOneChangeToHash(pGrp, pIter, 0);
}
return rc;
}
@@ -6447,12 +6329,6 @@ int sqlite3changegroup_output_strm(
*/
void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
int ii;
for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
sqlite3_free(pGrp->cd.aBuf[ii].aBuf);
}
sqlite3_free(pGrp->cd.record.aBuf);
sqlite3_free(pGrp->cd.aBuf);
sqlite3_free(pGrp->zDb);
sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp->rec.aBuf);
@@ -6883,326 +6759,4 @@ int sqlite3session_config(int op, void *pArg){
return rc;
}
/*
** Begin adding a change to a changegroup object.
*/
int sqlite3changegroup_change_begin(
sqlite3_changegroup *pGrp,
int eOp,
const char *zTab,
int bIndirect,
char **pzErr
){
SessionTable *pTab = 0;
int rc = SQLITE_OK;
if( pGrp->cd.pTab ){
rc = SQLITE_MISUSE;
}else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_UPDATE && eOp!=SQLITE_DELETE ){
rc = SQLITE_ERROR;
}else{
rc = sessionChangesetFindTable(pGrp, zTab, 0, &pTab);
}
if( rc==SQLITE_OK ){
if( pTab==0 ){
if( pzErr ){
*pzErr = sqlite3_mprintf("no such table: %s", zTab);
}
rc = SQLITE_ERROR;
}else{
int nReq = pTab->nCol * (eOp==SQLITE_UPDATE ? 2 : 1);
pGrp->cd.pTab = pTab;
pGrp->cd.eOp = eOp;
pGrp->cd.bIndirect = bIndirect;
if( pGrp->cd.nBufAlloc<nReq ){
SessionBuffer *aBuf = (SessionBuffer*)sqlite3_realloc(
pGrp->cd.aBuf, nReq * sizeof(SessionBuffer)
);
if( aBuf==0 ){
rc = SQLITE_NOMEM;
}else{
memset(&aBuf[pGrp->cd.nBufAlloc], 0,
sizeof(SessionBuffer) * (nReq - pGrp->cd.nBufAlloc)
);
pGrp->cd.aBuf = aBuf;
pGrp->cd.nBufAlloc = nReq;
}
}
#ifdef SQLITE_DEBUG
{
/* Assert that all column values are currently undefined */
int ii;
for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
assert( pGrp->cd.aBuf[ii].nBuf==0 );
}
}
#endif
}
}
return rc;
}
/*
** This function does processing common to the _change_int64(), _change_text()
** and other similar APIs.
*/
static int checkChangeParams(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
sqlite3_int64 nReq,
SessionBuffer **ppBuf
){
int rc = SQLITE_OK;
if( pGrp->cd.pTab==0 ){
rc = SQLITE_MISUSE;
}else if( iCol<0 || iCol>=pGrp->cd.pTab->nCol ){
rc = SQLITE_RANGE;
}else if(
(bNew && pGrp->cd.eOp==SQLITE_DELETE)
|| (!bNew && pGrp->cd.eOp==SQLITE_INSERT)
){
rc = SQLITE_ERROR;
}else{
SessionBuffer *pBuf = &pGrp->cd.aBuf[iCol];
if( pGrp->cd.eOp==SQLITE_UPDATE && bNew ){
pBuf += pGrp->cd.pTab->nCol;
}
pBuf->nBuf = 0;
sessionBufferGrow(pBuf, nReq, &rc);
pBuf->nBuf = nReq;
*ppBuf = pBuf;
}
return rc;
}
/*
** Configure the change currently under construction with an integer value.
*/
int sqlite3changegroup_change_int64(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
sqlite3_int64 iVal
){
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_INTEGER;
sessionPutI64(&pBuf->aBuf[1], iVal);
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a null value.
*/
int sqlite3changegroup_change_null(
sqlite3_changegroup *pGrp,
int bNew,
int iCol
){
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 1, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_NULL;
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a real value.
*/
int sqlite3changegroup_change_double(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
double fVal
){
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_FLOAT;
sessionPutDouble(&pBuf->aBuf[1], fVal);
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a text value.
*/
int sqlite3changegroup_change_text(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
const char *pVal,
int nVal
){
int nText = nVal>=0 ? nVal : strlen(pVal);
sqlite3_int64 nByte = 1 + sessionVarintLen(nText) + nText;
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_TEXT;
pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nText));
memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nText);
pBuf->nBuf += nText;
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a blob value.
*/
int sqlite3changegroup_change_blob(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
const void *pVal,
int nVal
){
sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + nVal;
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_BLOB;
pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nVal));
memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nVal);
pBuf->nBuf += nVal;
return SQLITE_OK;
}
/*
** Finish any change currently being constructed by the changegroup object.
*/
int sqlite3changegroup_change_finish(
sqlite3_changegroup *pGrp,
int bDiscard,
char **pzErr
){
int rc = SQLITE_OK;
if( pGrp->cd.pTab ){
SessionBuffer *aBuf = pGrp->cd.aBuf;
int ii;
if( bDiscard==0 ){
int nBuf = pGrp->cd.pTab->nCol;
u8 eUndef = SQLITE_NULL;
if( pGrp->cd.eOp==SQLITE_UPDATE ){
for(ii=0; ii<nBuf; ii++){
if( pGrp->cd.pTab->abPK[ii] ){
if( aBuf[ii].nBuf<=1 ){
*pzErr = sqlite3_mprintf(
"invalid change: %s value in PK of old.* record",
aBuf[ii].nBuf==1 ? "null" : "undefined"
);
rc = SQLITE_ERROR;
break;
}else if( aBuf[ii + nBuf].nBuf>0 ){
*pzErr = sqlite3_mprintf(
"invalid change: defined value in PK of new.* record"
);
rc = SQLITE_ERROR;
break;
}
}else
if( pGrp->bPatch==0 && (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){
*pzErr = sqlite3_mprintf(
"invalid change: column %d "
"- old.* value is %sdefined but new.* is %sdefined",
ii, aBuf[ii].nBuf ? "" : "un", aBuf[ii+nBuf].nBuf ? "" : "un"
);
rc = SQLITE_ERROR;
break;
}
}
eUndef = 0x00;
if( pGrp->bPatch==0 ) nBuf = nBuf * 2;
}else{
for(ii=0; ii<nBuf; ii++){
int isPK = pGrp->cd.pTab->abPK[ii];
if( (pGrp->cd.eOp==SQLITE_INSERT || pGrp->bPatch==0 || isPK)
&& aBuf[ii].nBuf==0
){
*pzErr = sqlite3_mprintf(
"invalid change: column %d is undefined", ii
);
rc = SQLITE_ERROR;
break;
}
if( aBuf[ii].nBuf==1 && isPK ){
*pzErr = sqlite3_mprintf(
"invalid change: null value in PK"
);
rc = SQLITE_ERROR;
break;
}
}
}
pGrp->cd.record.nBuf = 0;
for(ii=0; ii<nBuf; ii++){
SessionBuffer *p = &pGrp->cd.aBuf[ii];
if( pGrp->bPatch ){
if( pGrp->cd.pTab->abPK[ii]==0 ){
if( pGrp->cd.eOp==SQLITE_UPDATE ){
p += pGrp->cd.pTab->nCol;
}else if( pGrp->cd.eOp==SQLITE_DELETE ){
continue;
}
}
}
if( 0==sessionBufferGrow(&pGrp->cd.record, p->nBuf?p->nBuf:1, &rc) ){
if( p->nBuf ){
memcpy(&pGrp->cd.record.aBuf[pGrp->cd.record.nBuf],p->aBuf,p->nBuf);
pGrp->cd.record.nBuf += p->nBuf;
}else{
pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef;
}
}
}
if( rc==SQLITE_OK ){
rc = sessionOneChangeToHash(
pGrp, pGrp->cd.pTab,
pGrp->cd.eOp, pGrp->cd.bIndirect, pGrp->cd.pTab->nCol,
pGrp->cd.record.aBuf, pGrp->cd.record.nBuf, 0
);
}
}
/* Reset all aBuf[] entries to "undefined". */
{
int nZero = pGrp->cd.pTab->nCol;
if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero;
for(ii=0; ii<nZero; ii++){
pGrp->cd.aBuf[ii].nBuf = 0;
}
}
pGrp->cd.pTab = 0;
}
return rc;
}
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
-226
View File
@@ -1853,232 +1853,6 @@ int sqlite3session_config(int op, void *pArg);
*/
#define SQLITE_SESSION_CONFIG_STRMSIZE 1
/*
** CAPI3REF: Configure a changegroup object
**
** Configure the changegroup object passed as the first argument.
** At present the only valid value for the second parameter is
** [SQLITE_CHANGEGROUP_CONFIG_PATCHSET].
*/
int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg);
/*
** CAPI3REF: Options for sqlite3changegroup_config().
**
** The following values may be passed as the 2nd parameter to
** sqlite3changegroup_config().
**
** <dt>SQLITE_CHANGEGROUP_CONFIG_PATCHSET <dd>
** A changegroup object generates either a changeset or patchset. Usually,
** this is determined by whether the first call to sqlite3changegroup_add()
** is passed a changeset or a patchset. Or, if the first changes are added
** to the changegroup object using the sqlite3changegroup_change_xxx()
** APIs, then this option may be used to configure whether the changegroup
** object generates a changeset or patchset.
**
** When this option is invoked, parameter pArg must point to a value of
** type int. If the changegroup currently contains zero changes, and the
** value of the int variable is zero or greater than zero, then the
** changegroup is configured to generate a changeset or patchset,
** respectively. It is a no-op, not an error, if the changegroup is not
** configured because it has already started accumulating changes.
**
** Before returning, the int variable is set to 0 if the changegroup is
** configured to generate a changeset, or 1 if it is configured to generate
** a patchset.
*/
#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1
/*
** CAPI3REF: Begin adding a change to a changegroup
**
** This API is used, in concert with other sqlite3changegroup_change_xxx()
** APIs, to add changes to a changegroup object one at a time. To add a
** single change, the caller must:
**
** 1. Invoke sqlite3changegroup_change_begin() to indicate the type of
** change (INSERT, UPDATE or DELETE), the affected table and whether
** or not the change should be marked as indirect.
**
** 2. Invoke sqlite3changegroup_change_int64() or one of the other four
** value functions - _null(), _double(), _text() or _blob() - one or
** more times to specify old.* and new.* values for the change being
** constructed.
**
** 3. Invoke sqlite3changegroup_change_finish() to either finish adding
** the change to the group, or to discard the change altogether.
**
** The first argument to this function must be a pointer to the existing
** changegroup object that the change will be added to. The second argument
** must be SQLITE_INSERT, SQLITE_UPDATE or SQLITE_DELETE. The third is the
** name of the table that the change affects, and the fourth is a boolean
** flag specifying whether the change should be marked as "indirect" (if
** bIndirect is non-zero) or not indirect (if bIndirect is zero).
**
** Following a successful call to this function, this function may not be
** called again on the same changegroup object until after
** sqlite3changegroup_change_finish() has been called. Doing so is an
** SQLITE_MISUSE error.
**
** The changegroup object passed as the first argument must be already
** configured with schema data for the specified table. It may be configured
** either by calling sqlite3changegroup_schema() with a database that contains
** the table, or sqlite3changegroup_add() with a changeset that contains the
** table. If the changegroup object has not been configured with a schema for
** the specified table when this function is called, SQLITE_ERROR is returned.
**
** If successful, SQLITE_OK is returned. Otherwise, if an error occurs, an
** SQLite error code is returned. In this case, if argument pzErr is non-NULL,
** then (*pzErr) may be set to point to a buffer containing a utf-8 formated,
** nul-terminated, English language error message. It is the responsibility
** of the caller to eventually free this buffer using sqlite3_free().
*/
int sqlite3changegroup_change_begin(
sqlite3_changegroup*,
int eOp,
const char *zTab,
int bIndirect,
char **pzErr
);
/*
** CAPI3REF: Add a 64-bit integer to a changegroup
**
** This function may only be called between a successful call to
** sqlite3changegroup_change_begin() and its matching
** sqlite3changegroup_change_finish() call. If it is called at any
** other time, it is an SQLITE_MISUSE error. Calling this function
** specifies a 64-bit integer value to be used in the change currently being
** added to the changegroup object passed as the first argument.
**
** The second parameter, bNew, specifies whether the value is to be part of
** the new.* (if bNew is non-zero) or old.* (if bNew is zero) record of
** the change under construction. If this does not match the type of change
** specified by the preceding call to sqlite3changegroup_change_begin() (i.e.
** an old.* value for an SQLITE_INSERT change, or a new.* value for an
** SQLITE_DELETE), then SQLITE_ERROR is returned.
**
** The third parameter specifies the column of the old.* or new.* record that
** the value will be a part of. If the specified table has an explicit primary
** key, then this is the index of the table column, numbered from 0 in the order
** specified within the CREATE TABLE statement. Or, if the table uses an
** implicit rowid key, then the column 0 is the rowid and the explicit columns
** are numbered starting from 1. If the iCol parameter is less than 0 or greater
** than the index of the last column in the table, SQLITE_RANGE is returned.
**
** The fourth parameter is the integer value to use as part of the old.* or
** new.* record.
**
** If this call is successful, SQLITE_OK is returned. Otherwise, if an
** error occurs, an SQLite error code is returned.
*/
int sqlite3changegroup_change_int64(
sqlite3_changegroup*,
int bNew,
int iCol,
sqlite3_int64 iVal
);
/*
** CAPI3REF: Add a NULL to a changegroup
**
** This function is similar to sqlite3changegroup_change_int64(). Except that
** it configures the change currently under construction with a NULL value
** instead of a 64-bit integer.
*/
int sqlite3changegroup_change_null(sqlite3_changegroup*, int, int);
/*
** CAPI3REF: Add an double to a changegroup
**
** This function is similar to sqlite3changegroup_change_int64(). Except that
** it configures the change currently being constructed with a real value
** instead of a 64-bit integer.
*/
int sqlite3changegroup_change_double(sqlite3_changegroup*, int, int, double);
/*
** CAPI3REF: Add a text value to a changegroup
**
** This function is similar to sqlite3changegroup_change_int64(). It configures
** the currently accumulated change with a text value instead of a 64-bit
** integer. Parameter pVal points to a buffer containing the text encoded using
** utf-8. Parameter nVal may either be the size of the text value in bytes, or
** else a negative value, in which case the buffer pVal points to is assumed to
** be nul-terminated.
*/
int sqlite3changegroup_change_text(
sqlite3_changegroup*, int, int, const char *pVal, int nVal
);
/*
** CAPI3REF: Add a blob to a changegroup
**
** This function is similar to sqlite3changegroup_change_int64(). It configures
** the currently accumulated change with a blob value instead of a 64-bit
** integer. Parameter pVal points to a buffer containing the blob. Parameter
** nVal is the size of the blob in bytes.
*/
int sqlite3changegroup_change_blob(
sqlite3_changegroup*, int, int, const void *pVal, int nVal
);
/*
** CAPI3REF: Finish adding one-at-at-time changes to a changegroup
**
** This function may only be called following a successful call to
** sqlite3changegroup_change_begin(). Otherwise, it is an SQLITE_MISUSE error.
**
** If parameter bDiscard is non-zero, then the current change is simply
** discarded. In this case this function is always successful and SQLITE_OK
** returned.
**
** If parameter bDiscard is zero, then an attempt is made to add the current
** change to the changegroup. Assuming the changegroup is configured to
** produce a changeset (not a patchset), this requires that:
**
** * If the change is an INSERT or DELETE, then a value must be specified
** for all columns of the new.* or old.* record, respectively.
**
** * If the change is an UPDATE record, then values must be provided for
** the PRIMARY KEY columns of the old.* record, but must not be provided
** for PRIMARY KEY columns of the new.* record.
**
** * If the change is an UPDATE record, then for each non-PRIMARY KEY
** column in the old.* record for which a value has been provided, a
** value must also be provided for the same column in the new.* record.
** Similarly, for each non-PK column in the old.* record for which
** a value is not provided, a value must not be provided for the same
** column in the new.* record.
**
** * All values specified for PRIMARY KEY columns must be non-NULL.
**
** Otherwise, it is an error.
**
** If the changegroup already contains a change for the same row (identified
** by PRIMARY KEY columns), then the current change is combined with the
** existing change in the same way as for sqlite3changegroup_add().
**
** For a patchset, all of the above rules apply except that it doesn't matter
** whether or not values are provided for the non-PK old.* record columns
** for an UPDATE or DELETE change. This means that code used to produce
** a changeset using the sqlite3changegroup_change_xxx() APIs may also
** be used to produce patchsets.
**
** If the call is successful, SQLITE_OK is returned. Otherwise, if an error
** occurs, an SQLite error code is returned. If an error is returned and
** parameter pzErr is not NULL, then (*pzErr) may be set to point to a buffer
** containing a nul-terminated, utf-8 encoded, English language error message.
** It is the responsibility of the caller to eventually free any such error
** message buffer using sqlite3_free().
*/
int sqlite3changegroup_change_finish(
sqlite3_changegroup*,
int bDiscard,
char **pzErr
);
/*
** Make sure we can call this stuff from C++.
*/
+33 -285
View File
@@ -7,14 +7,10 @@
#include <string.h>
#include "tclsqlite.h"
#include <stdlib.h>
#ifndef SQLITE_AMALGAMATION
typedef unsigned char u8;
#endif
extern const char *sqlite3ErrName(int);
typedef struct TestSession TestSession;
struct TestSession {
sqlite3_session *pSession;
@@ -399,6 +395,7 @@ static int SQLITE_TCLAPI test_session_cmd(
}
rc = sqlite3session_object_config(pSession, aOpt[iOpt].opt, &iArg);
if( rc!=SQLITE_OK ){
extern const char *sqlite3ErrName(int);
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
}else{
Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
@@ -859,21 +856,6 @@ static int testStreamInput(
return SQLITE_OK;
}
/*
** This works like Tcl_GetByteArrayFromObj(), except that it returns a buffer
** allocated using malloc() that must be freed by the caller. This is done
** because Tcl's buffers are often padded by a few bytes, which prevents
** small overreads from being detected when tests are run under asan.
*/
static void *testGetByteArrayFromObj(Tcl_Obj *p, Tcl_Size *pnByte){
Tcl_Size nByte = 0;
void *aByte = Tcl_GetByteArrayFromObj(p, &nByte);
void *aCopy = malloc(nByte ? (size_t)nByte : 1);
memcpy(aCopy, aByte, (size_t)nByte);
*pnByte = nByte;
return aCopy;
}
static int SQLITE_TCLAPI testSqlite3changesetApply(
int iVersion,
@@ -938,7 +920,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
return TCL_ERROR;
}
db = *(sqlite3 **)info.objClientData;
pChangeset = (void *)testGetByteArrayFromObj(objv[2], &nChangeset);
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
ctx.pConflictScript = objv[3];
ctx.pFilterScript = objc==5 ? objv[4] : 0;
ctx.interp = interp;
@@ -990,7 +972,6 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
}
}
free(pChangeset);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc, 0);
}else{
@@ -1115,21 +1096,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_invert(
return rc;
}
/*
** Copy buffer aIn[] to a new nIn byte buffer obtained from malloc(). Use
** plain malloc() instead of any Tcl function because valgrind and asan are
** better at detecting small overflows in that case. Avoid sqlite3_malloc()
** here because that means dealing with injected OOM errors.
**
** The caller is responsible for eventually calling free() on the returned
** value.
*/
static u8 *copyToMalloc(const u8 *aIn, int nIn){
u8 *pRet = malloc(nIn);
memcpy(pRet, aIn, nIn);
return pRet;
}
/*
** sqlite3changeset_concat LEFT RIGHT
*/
@@ -1160,9 +1126,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
sLeft.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
sRight.nStream = sLeft.nStream;
sLeft.aData = copyToMalloc(sLeft.aData, sLeft.nData);
sRight.aData = copyToMalloc(sRight.aData, sRight.nData);
if( sLeft.nStream>0 ){
rc = sqlite3changeset_concat_strm(
testStreamInput, (void*)&sLeft,
@@ -1175,9 +1138,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
);
}
free(sLeft.aData);
free(sRight.aData);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}else{
@@ -1235,12 +1195,7 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
pCS = objv[2];
pScript = objv[3];
/* Take a copy of the changeset into an exact sized buffer allocated
** using malloc(). The Tcl buffer will be padded by a few bytes, which
** prevents small overreads from being detected by ASAN when the tests
** are run. */
pChangeset = (void*)testGetByteArrayFromObj(pCS, &nChangeset);
pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
if( isInvert ){
int f = SQLITE_CHANGESETSTART_INVERT;
@@ -1261,33 +1216,32 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
}
}
if( rc==SQLITE_OK ){
while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
pVar = testIterData(pIter);
Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
rc = Tcl_EvalObjEx(interp, pScript, 0);
if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
sqlite3changeset_finalize(pIter);
free(pChangeset);
return rc==TCL_BREAK ? TCL_OK : rc;
}
}
if( isCheckNext ){
int rc2 = sqlite3changeset_next(pIter);
rc = sqlite3changeset_finalize(pIter);
assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
}else{
rc = sqlite3changeset_finalize(pIter);
}
}
free(pChangeset);
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc, 0);
}
while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
pVar = testIterData(pIter);
Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
rc = Tcl_EvalObjEx(interp, pScript, 0);
if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
sqlite3changeset_finalize(pIter);
return rc==TCL_BREAK ? TCL_OK : rc;
}
}
if( isCheckNext ){
int rc2 = sqlite3changeset_next(pIter);
rc = sqlite3changeset_finalize(pIter);
assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
}else{
rc = sqlite3changeset_finalize(pIter);
}
if( rc!=SQLITE_OK ){
return test_session_error(interp, rc, 0);
}
return TCL_OK;
}
@@ -1576,14 +1530,6 @@ static void test_changegroup_del(void *clientData){
ckfree(pGrp);
}
static int testGetNewOrOld(Tcl_Interp *interp, Tcl_Obj *pObj, int *pbNew){
const char *azVal[] = { "old", "new", 0 };
int iIdx = 0;
int rc = Tcl_GetIndexFromObj(interp, pObj, azVal, "record", 0, &iIdx);
*pbNew = iIdx;
return rc;
}
/*
** Tclcmd: $changegroup schema DB DBNAME
** Tclcmd: $changegroup add CHANGESET
@@ -1601,25 +1547,14 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
const char *zSub;
int nArg;
const char *zMsg;
int iSub;
} aSub[] = {
{ "schema", 2, "DB DBNAME" }, /* 0 */
{ "add", 1, "CHANGESET" }, /* 1 */
{ "output", 0, "" }, /* 2 */
{ "delete", 0, "" }, /* 3 */
{ "add_change", 1, "ITERATOR" }, /* 4 */
{ "change_begin", 3, "TYPE TABLE INDIRECT" }, /* 5 */
{ "change_int64", 3, "[new|old] ICOL VALUE" }, /* 6 */
{ "change_null", 2, "[new|old] ICOL" }, /* 7 */
{ "change_double", 3, "[new|old] ICOL VALUE" }, /* 8 */
{ "change_text", 3, "[new|old] ICOL VALUE" }, /* 9 */
{ "change_blob", 3, "[new|old] ICOL VALUE" }, /* 10 */
{ "change_finish", 1, "BDISCARD" }, /* 11 */
{ "config", 2, "OPTION INTVAL" }, /* 12 */
{ "change_text-1", 3, "[new|old] ICOL VALUE" }, /* 13 */
{ "change_begin_ne", 3, "TYPE TABLE INDIRECT" }, /* 14 */
{ 0, 0, 0 }
{ "schema", 2, "DB DBNAME", }, /* 0 */
{ "add", 1, "CHANGESET", }, /* 1 */
{ "output", 0, "", }, /* 2 */
{ "delete", 0, "", }, /* 3 */
{ "add_change", 1, "ITERATOR", }, /* 4 */
{ 0 }
};
int rc = TCL_OK;
int iSub = 0;
@@ -1688,193 +1623,6 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
break;
};
case 14: /* change_beginne */
case 5: { /* change_begin */
struct ChangeType {
const char *zType;
int eType;
} aType[] = {
{ "INSERT", SQLITE_INSERT },
{ "UPDATE", SQLITE_UPDATE },
{ "DELETE", SQLITE_DELETE },
{ 0, 0 }
};
int eType = 0;
const char *zTab = 0;
int bIndirect;
int iIdx = 0;
char *zErr = 0;
char **pz = ((iSub==5) ? &zErr : 0);
if( TCL_OK!=Tcl_GetIntFromObj(0, objv[2], &eType) ){
rc = Tcl_GetIndexFromObjStruct(
interp, objv[2], aType, sizeof(aType[0]), "TYPE", 0, &iIdx
);
if( rc!=TCL_OK ) return rc;
eType = aType[iIdx].eType;
}
zTab = Tcl_GetString(objv[3]);
if( Tcl_GetBooleanFromObj(interp, objv[4], &bIndirect) ){
return TCL_ERROR;
}
rc = sqlite3changegroup_change_begin(p->pGrp, eType, zTab, bIndirect, pz);
assert( zErr==0 || rc!=SQLITE_OK );
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, zErr);
}
break;
}
case 6: { /* change_int64 */
int bNew = 0;
int iCol = 0;
sqlite3_int64 iVal = 0;
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|| TCL_OK!=Tcl_GetWideIntFromObj(interp, objv[4], &iVal)
){
rc = TCL_ERROR;
}else{
rc = sqlite3changegroup_change_int64(p->pGrp, bNew, iCol, iVal);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}
}
break;
}
case 7: { /* change_null */
int bNew = 0;
int iCol = 0;
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
){
rc = TCL_ERROR;
}else{
rc = sqlite3changegroup_change_null(p->pGrp, bNew, iCol);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}
}
break;
}
case 8: { /* change_double */
int bNew = 0;
int iCol = 0;
double rVal = 0;
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|| TCL_OK!=Tcl_GetDoubleFromObj(interp, objv[4], &rVal)
){
rc = TCL_ERROR;
}else{
rc = sqlite3changegroup_change_double(p->pGrp, bNew, iCol, rVal);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}
}
break;
}
case 9: { /* change_text */
int bNew = 0;
int iCol = 0;
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
){
rc = TCL_ERROR;
}else{
Tcl_Size nVal = 0;
const char *pVal = Tcl_GetStringFromObj(objv[4], &nVal);
rc = sqlite3changegroup_change_text(p->pGrp, bNew, iCol, pVal, nVal);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}
}
break;
}
case 10: { /* change_blob */
int bNew = 0;
int iCol = 0;
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
){
rc = TCL_ERROR;
}else{
Tcl_Size nVal = 0;
const u8 *pVal = Tcl_GetByteArrayFromObj(objv[4], &nVal);
rc = sqlite3changegroup_change_blob(p->pGrp, bNew, iCol, pVal, nVal);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}
}
break;
}
case 11: { /* change_finish */
int bDiscard = 0;
if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[2], &bDiscard) ){
rc = TCL_ERROR;
}else{
char *zErr = 0;
rc = sqlite3changegroup_change_finish(p->pGrp, bDiscard, &zErr);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, zErr);
}
}
break;
}
case 12: { /* config */
struct OptionName {
const char *zOpt;
int op;
} aOp[] = {
{ "patchset", SQLITE_CHANGEGROUP_CONFIG_PATCHSET },
{ 0, 0 }
};
int iIdx = 0;
int iArg = 0;
rc = Tcl_GetIndexFromObjStruct(
interp, objv[2], aOp, sizeof(aOp[0]), "option", 0, &iIdx
);
if( rc==TCL_OK
&& (rc = Tcl_GetIntFromObj(interp, objv[3], &iArg))==TCL_OK
){
int op = aOp[iIdx].op;
void *pArg = (void*)&iArg;
rc = sqlite3changegroup_config(p->pGrp, op, pArg);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}else{
Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
}
}
break;
}
case 13: { /* change_text-1 */
int bNew = 0;
int iCol = 0;
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
){
rc = TCL_ERROR;
}else{
const char *pVal = Tcl_GetString(objv[4]);
rc = sqlite3changegroup_change_text(p->pGrp, bNew, iCol, pVal, -1);
if( rc!=SQLITE_OK ){
rc = test_session_error(interp, rc, 0);
}
}
break;
}
default: { /* delete */
assert( iSub==3 );
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
+33 -137
View File
@@ -86,7 +86,6 @@ emo.strip = 💈
emo.test = 🧪
emo.tool = 🔨
emo.wasm-opt = 🧼
emo.cleanup = 🧼
# 👷🪄🧮🧫🧽🍿⛽🚧🎱🪚🏆🧼
#
@@ -206,7 +205,7 @@ b.mkdir@ = if [ ! -d $(dir $@) ]; then \
# $1 = logtag, $2 = src file(s). $3 = dest dir
b.cp = $(call b.mkdir@); \
echo '$(logtag.$(1)) $(emo.disk) $(2) ==> $3'; \
cp -f -p $(2) $(3) || exit
cp -p $(2) $(3) || exit
#
# $(call b.c-pp.shcmd,LOGTAG,src,dest,-Dx=y...)
@@ -318,7 +317,7 @@ endif
#
sqlite3.canonical.c = $(dir.top)/sqlite3.c
sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c))
sqlite3.h = $(dir $(sqlite3.c))sqlite3.h
sqlite3.h = $(dir $(sqlite3.c))/sqlite3.h
#
# bin.version-info = binary to output various sqlite3 version info for
@@ -566,14 +565,12 @@ WASM_CUSTOM_INSTANTIATE = 1
# -D... flags which should be included in all invocations should be
# appended to $(b.c-pp.target.flags).
#
bin.c-pp = ./c-pp
$(bin.c-pp): libcmpp.c $(sqlite3.c) $(MAKEFILE)
$(CC) -O0 -o $@ libcmpp.c $(dir.top)/sqlite3.c -I$(dir.top) \
bin.c-pp = ./c-pp-lite
$(bin.c-pp): c-pp-lite.c $(sqlite3.c) $(MAKEFILE)
$(CC) -O0 -o $@ c-pp-lite.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \
-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \
-DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \
-DSQLITE_TEMP_STORE=3 \
'-DCMPP_DEFAULT_DELIM="//#"' -DCMPP_MAIN -DCMPP_OMIT_D_MODULE \
-DCMPP_OMIT_D_PIPE
-DSQLITE_TEMP_STORE=3
DISTCLEAN_FILES += $(bin.c-pp)
b.c-pp.target.flags ?=
ifeq (1,$(SQLITE_C_IS_SEE))
@@ -871,12 +868,13 @@ $(sqlite3-license-version.js): $(bin.version-info) \
$(dir.api)/sqlite3-license-version-header.js $(MAKEFILE)
@$(call b.mkdir@); echo '$(logtag.@) $(emo.disk)'; { \
cat $(dir.api)/sqlite3-license-version-header.js || exit $$?; \
echo '/* @preserve'; \
echo '/*'; \
echo '** This code was built from sqlite3 version...'; \
echo '**'; \
awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \
awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \
echo '**'; echo '** Emscripten SDK: $(emcc.version)'; \
echo '**'; echo '** Preprocessed by:'; echo '** @c-pp::ARGV@'; \
echo '*/'; \
} > $@
@@ -913,10 +911,8 @@ ifeq (0,$(wasm-bare-bones))
sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js
endif
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-kvvfs.c-pp.js
sqlite3-api.jses += $(dir.api)/opfs-common-shared.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-wl.c-pp.js
# Parallel builds can fail if $(sqlite3-license-version.js) is not
# created early enough, so make all files in $(sqlite-api.jses) except
@@ -1001,21 +997,14 @@ endif
# /shell.c
########################################################################
#
# Fiddle-related decls we need before .wasmbuilds is included
#
fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c)
EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) \
$(MAKEFILE_LIST) $(bin.c-pp)
@$(call b.mkdir@)
@$(call b.c-pp.shcmd,fiddle,$(EXPORTED_FUNCTIONS.api.in),\
@$(call b.c-pp.shcmd,filter,$(EXPORTED_FUNCTIONS.api.in),\
$@,$(EXPORTED_FUNCTIONS.c-pp.flags) -Dfiddle)
@echo $(logtag.@) $(emo.disk)
emcc.flags.fiddle = \
$(emcc.cflags) $(emcc_opt_full) \
--minify 0 \
@@ -1040,23 +1029,21 @@ emcc.flags.fiddle = \
-USQLITE_WASM_BARE_BONES \
-DSQLITE_SHELL_FIDDLE
clean: clean-fiddle
clean-fiddle:
rm -f $(dir.fiddle)/fiddle-module.js \
$(dir.fiddle)/*.wasm \
$(dir.fiddle)/sqlite3-opfs-*.js \
$(dir.fiddle)/*.gz \
$(EXPORTED_FUNCTIONS.fiddle)
rm -fr $(dir.fiddle-debug)
emcc.flags.fiddle.debug = $(emcc.flags.fiddle) \
-DSQLITE_DEBUG \
-DSQLITE_ENABLE_SELECTTRACE \
-DSQLITE_ENABLE_WHERETRACE
clean: clean-fiddle
clean-fiddle:
rm -f $(dir.fiddle)/fiddle-module.js \
$(dir.fiddle)/*.wasm \
$(dir.fiddle)/sqlite3-opfs-*.js \
$(dir.fiddle)/*.gz \
$(dir.fiddle)/index.html \
$(EXPORTED_FUNCTIONS.fiddle)
rm -fr $(dir.fiddle-debug)
distclean: distclean-fiddle
distclean-fiddle:
rm -fr $(dir.fiddle)/jqterm
fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c)
#
# WASMFS build - unsupported and untested. We used WASMFS
@@ -1186,13 +1173,13 @@ all: demos
#######################################################################
#
# "SOAP" is not part of the amalgamation but gets copied into the
# build output folder and into each of the fiddle builds.
# "SOAP" is a static file which is not part of the amalgamation but
# gets copied into the build output folder and into each of the fiddle
# builds.
#
sqlite3.ext.js += $(dir.dout)/sqlite3-opfs-async-proxy.js
$(eval $(call b.c-pp.target,soap,\
$(dir.api)/sqlite3-opfs-async-proxy.c-pp.js,\
$(dir.dout)/sqlite3-opfs-async-proxy.js))
$(dir.dout)/sqlite3-opfs-async-proxy.js: $(dir.api)/sqlite3-opfs-async-proxy.js
@$(call b.cp,@,$<,$@)
#
# Add a dep of $(sqlite3.ext.js) on every individual build's JS file.
@@ -1225,7 +1212,7 @@ $(foreach B,$(b.names),$(eval post: $(post-js.$(B).js)))
# These flags get applied via $(bin.mkwb).
emcc.speedtest1.common = $(emcc_opt_full)
emcc.speedtest1 = -I. -I$(dir $(sqlite3.canonical.c))
emcc.speedtest1 += -sENVIRONMENT=web,worker
emcc.speedtest1 += -sENVIRONMENT=web
emcc.speedtest1 += -sALLOW_MEMORY_GROWTH
emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.32)
emcc.speedtest1.common += -sINVOKE_RUN=0
@@ -1268,33 +1255,23 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api)
@$(call b.mkdir@); $(call b.echo,@,$(emo.disk)); \
{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api); } > $@ || exit
speedtest1: b-speedtest1
st: speedtest1
#
# Generate 64-bit variants of speedtest1*.{js,html}
#
# $1 = input file
# $2 = output file
#
# TODO: preprocess these like we do the rest.
#
define gen-st64
$(2): $(1)
@$$(call b.echo,speedtest164,$$(emo.disk)$(emo.lock) Creating from $$<)
rm -f $$@; \
sed -e 's/speedtest1\.js/speedtest1-64bit\.js/' \
-e 's/speedtest1-worker\.js/speedtest1-worker-64bit\.js/' \
< $$< > $$@; \
@rm -f $$@; \
sed -e 's/$(3)\.js/$(3)-64bit\.js/' < $$< > $$@; \
chmod -w $$@
$(2): b-speedtest164
speedtest1: $(1) $(2)
b-speedtest164: $(2)
CLEAN_FILES += $(2)
endef
speedtest1: b-speedtest164
$(eval $(call gen-st64,speedtest1.html,speedtest1-64bit.html))
$(eval $(call gen-st64,speedtest1-worker.html,speedtest1-worker-64bit.html))
$(eval $(call gen-st64,speedtest1-worker.js,speedtest1-worker-64bit.js))
$(eval $(call gen-st64,speedtest1.html,speedtest1-64bit.html,speedtest1))
$(eval $(call gen-st64,speedtest1-worker.html,speedtest1-worker-64bit.html,speedtest1-worker))
$(eval $(call gen-st64,speedtest1-worker.js,speedtest1-worker-64bit.js,speedtest1-worker))
# end speedtest1.js
########################################################################
@@ -1395,54 +1372,6 @@ all: tester1
# end tester1
#
#
# jquery.terminal support for fiddle:
#
# If a clone of https://github.com/jcubic/jquery.terminal
# is found in $(JQTERM), defaulting to $(HOME)/src/jquery.terminal
# then add jquery.terminal support to fiddle.
#
# To build that package, from its checkout dir:
#
# npm install
# make
#
c-pp.D.fiddle ?=
JQTERM ?= $(HOME)/src/jquery.terminal
dir.jqtermExt = $(firstword $(wildcard $(JQTERM)))
#$(info dir.jqtermExt=$(dir.jqtermExt))
ifeq (0,$(MAKING_CLEAN))
ifeq (,$(wildcard $(dir.jqtermExt)/js/jquery.terminal.min.js))
$(info $(emo.magic) To add jquery.terminal support to fiddle, set JQTERM=/path/to/its/built/checkout)
else
$(info $(emo.magic) jquery.terminal found in $(dir.jqtermExt) - adding it to fiddle. Make sure it is built!)
dir.jqterm = $(dir.fiddle)/jqterm
$(dir.fiddle)/jqterm/jquery.terminal.bundle.min.js:
@$(call b.mkdir@)
cat $(dir.jqtermExt)/js/jquery-1*.min.js \
$(dir.jqtermExt)/js/jquery.terminal.min.js > $@
$(dir.fiddle)/jqterm/jquery.terminal.min.css: $(dir.jqtermExt)/css/jquery.terminal.min.css
@$(call b.mkdir@)
@$(call b.cp,fiddle,$<,$(dir $@))
$(dir.fiddle)/index.html: $(dir.fiddle)/jqterm/jquery.terminal.bundle.min.js \
$(dir.fiddle)/jqterm/jquery.terminal.min.css
c-pp.D.fiddle += -Djqterm
endif
endif
# ^^^ JQTERM/MAKING_CLEAN
#
# Generate fiddle/index.html. Must come after JQTERM is handled.
#
$(dir.fiddle)/index.html: $(dir.fiddle)/index.c-pp.html
$(eval $(call b.c-pp.target,fiddle,\
$(dir.fiddle)/index.c-pp.html,$(dir.fiddle)/index.html,$(c-pp.D.fiddle)))
$(out.fiddle.wasm): $(dir.fiddle)/index.html
#
# Convenience rules to rebuild with various -Ox levels. Much
# experimentation shows -O2 to be the clear winner in terms of speed.
@@ -1532,9 +1461,9 @@ update-docs:
exit 127
else
wasm.docs.jswasm = $(wasm.docs.home)/jswasm
update-docs: $(bin.stripccomments) $(out.vanilla.js) $(out.vanilla.wasm)
update-docs: $(bin.stripccomments) $(out.sqlite3.js) $(out.sqlite3.wasm)
@echo "Copying files to the /wasm docs. Be sure to use an -Oz build for this!";
cp -p $(out.vanilla.wasm) $(wasm.docs.jswasm)/.
cp -p $(sqlite3.wasm) $(wasm.docs.jswasm)/.
$(bin.stripccomments) -k -k < $(out.vanilla.js) \
| sed -e '/^[ \t]*$$/d' > $(wasm.docs.jswasm)/sqlite3.js
cp -p demo-123.js demo-123.html demo-123-worker.html $(wasm.docs.home)/.
@@ -1597,39 +1526,6 @@ endif
# ^^^ making dist/snapshot
CLEAN_FILES += $(wildcard sqlite-wasm-*.zip)
########################################################################
# The npm target is specifically for preparing files for the downstream
# https://github.com/sqlite/sqlite-wasm (npm) distribution.
#
# Per agreement with that project's maintainers, these filenames need
# to remain stable. To avoid breakage in their deployment process, any
# changes (like renaming files) which potentially break their deployment
# needs to be communicated to that project via opening a new ticket or
# direct coordination with its maintainers.
#
# This target does a full clean/rebuild so that we can ensure that the
# optimization level is set consistently across all files.
#
npm.bundle.zip = npm-bundle.zip
CLEAN_FILES += $(npm.bundle.zip)
# Distributables which need to be built for npm:
npm_files = $(addprefix $(dir.dout)/, \
sqlite3-bundler-friendly.mjs \
sqlite3-opfs-async-proxy.js \
sqlite3-worker1-bundler-friendly.mjs \
sqlite3-worker1-promiser.mjs \
sqlite3-worker1.mjs \
sqlite3.mjs \
sqlite3.wasm \
sqlite3-node.mjs \
)
npm: $(sqlite3.canonical.c)
@echo "$(emo.cleanup) Forcing a clean rebuild to ensure consistent optimization flags."
$(MAKE) clean
$(MAKE) -e "emcc_opt=-Oz $(emcc-opt-extra)" $(npm_files)
rm -f $(npm.bundle.zip); zip -r $(npm.bundle.zip) $(npm_files)
unzip -l $(npm.bundle.zip)
########################################################################
# Explanation of, and some commentary on, various emcc build flags
# follows. Full docs for these can be found at:
+3 -4
View File
@@ -13,7 +13,6 @@ _sqlite3_bind_parameter_index
_sqlite3_bind_parameter_name
_sqlite3_bind_pointer
_sqlite3_bind_text
_sqlite3_bind_zeroblob
_sqlite3_busy_handler
_sqlite3_busy_timeout
_sqlite3_cancel_auto_extension
@@ -226,14 +225,14 @@ _sqlite3session_object_config
_sqlite3session_patchset
_sqlite3session_patchset_strm
_sqlite3session_table_filter
//#/if not bare-bones
//#endif not bare-bones
//#if enable-see
_sqlite3_key
_sqlite3_key_v2
_sqlite3_rekey
_sqlite3_rekey_v2
_sqlite3_activate_see
//#/if enable-see
//#endif enable-see
//#if fiddle
_fiddle_db_arg
_fiddle_db_filename
@@ -245,4 +244,4 @@ _fiddle_reset_db
_fiddle_db_handle
_fiddle_db_vfs
_fiddle_export_db
//#/if fiddle
//#endif fiddle
+3 -3
View File
@@ -62,7 +62,7 @@ by concatenating the following files together in their listed order:
- **`sqlite3-api-prologue.js`**
Contains the initial bootstrap setup of the sqlite3 API
objects. This is exposed as a bootstrapping function so that
objects. This is exposed as a function, rather than objects, so that
the next step can pass in a config object which abstracts away parts
of the WASM environment, to facilitate plugging it in to arbitrary
WASM toolchains. The bootstrapping function gets removed from the
@@ -116,10 +116,10 @@ by concatenating the following files together in their listed order:
a Promise-based interface into the Worker #1 API. This is
a far user-friendlier way to interface with databases running
in a Worker thread.
- **`sqlite3-vfs-helper.c-pp.js`**
- **`sqlite3-vfs-helper.js`**
Installs the `sqlite3.vfs` namespace, which contain helpers for use
by downstream code which creates `sqlite3_vfs` implementations.
- **`sqlite3-vtab-helper.c-pp.js`**
- **`sqlite3-vtab-helper.js`**
Installs the `sqlite3.vtab` namespace, which contain helpers for use
by downstream code which creates `sqlite3_module` implementations.
- **`sqlite3-vfs-opfs.c-pp.js`**
+4 -6
View File
@@ -14,7 +14,7 @@
*/
//#if target:es6-module
const toExportForESM =
//#/if
//#endif
(function(){
//console.warn("this is extern-post-js");
/**
@@ -73,8 +73,6 @@ const toExportForESM =
const sIM = globalThis.sqlite3InitModule = function ff(...args){
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
sIMS.emscriptenLocateFile = args[0]?.locateFile /* see pre-js.c-pp.js [tag:locateFile] */;
sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm /* see pre-js.c-pp.js [tag:locateFile] */;
return originalInit(...args).then((EmscriptenModule)=>{
sIMS.debugModule("sqlite3InitModule() sIMS =",sIMS);
sIMS.debugModule("sqlite3InitModule() EmscriptenModule =",EmscriptenModule);
@@ -97,7 +95,7 @@ const toExportForESM =
//console.warn("sqlite3InitModule() returning E-module.",EmscriptenModule);
return EmscriptenModule;
}
//#/if
//#endif
return s;
}).catch((e)=>{
console.error("Exception loading sqlite3 module:",e);
@@ -128,10 +126,10 @@ const toExportForESM =
}
/* AMD modules get injected in a way we cannot override,
so we can't handle those here. */
//#/if // !target:es6-module
//#endif // !target:es6-module
return sIM;
})();
//#if target:es6-module
sqlite3InitModule = toExportForESM;
export default sqlite3InitModule;
//#/if
//#endif
-191
View File
@@ -1,191 +0,0 @@
//#if 0
/**
This file is for preprocessor #include into the "opfs" and
"opfs-wl" impls, as well as their async-proxy part. It must be
inlined in those files, as opposed to being a shared copy in the
library, because (A) the async proxy does not load the library and
(B) it references an object which is local to each of those files
but which has a 99% identical structure for each.
*/
//#/if
//#// vfs.metrics.enable is a refactoring crutch.
//#define vfs.metrics.enable 0
const initS11n = function(){
/**
This proxy de/serializes cross-thread function arguments and
output-pointer values via the state.sabIO SharedArrayBuffer,
using the region defined by (state.sabS11nOffset,
state.sabS11nOffset + state.sabS11nSize]. Only one dataset is
recorded at a time.
This is not a general-purpose format. It only supports the
range of operations, and data sizes, needed by the
sqlite3_vfs and sqlite3_io_methods operations. Serialized
data are transient and this serialization algorithm may
change at any time.
The data format can be succinctly summarized as:
Nt...Td...D
Where:
- N = number of entries (1 byte)
- t = type ID of first argument (1 byte)
- ...T = type IDs of the 2nd and subsequent arguments (1 byte
each).
- d = raw bytes of first argument (per-type size).
- ...D = raw bytes of the 2nd and subsequent arguments (per-type
size).
All types except strings have fixed sizes. Strings are stored
using their TextEncoder/TextDecoder representations. It would
arguably make more sense to store them as Int16Arrays of
their JS character values, but how best/fastest to get that
in and out of string form is an open point. Initial
experimentation with that approach did not gain us any speed.
Historical note: this impl was initially about 1% this size by
using using JSON.stringify/parse(), but using fit-to-purpose
serialization saves considerable runtime.
*/
if(state.s11n) return state.s11n;
const textDecoder = new TextDecoder(),
textEncoder = new TextEncoder('utf-8'),
viewU8 = new Uint8Array(state.sabIO, state.sabS11nOffset, state.sabS11nSize),
viewDV = new DataView(state.sabIO, state.sabS11nOffset, state.sabS11nSize);
state.s11n = Object.create(null);
/* Only arguments and return values of these types may be
serialized. This covers the whole range of types needed by the
sqlite3_vfs API. */
const TypeIds = Object.create(null);
TypeIds.number = { id: 1, size: 8, getter: 'getFloat64', setter: 'setFloat64' };
TypeIds.bigint = { id: 2, size: 8, getter: 'getBigInt64', setter: 'setBigInt64' };
TypeIds.boolean = { id: 3, size: 4, getter: 'getInt32', setter: 'setInt32' };
TypeIds.string = { id: 4 };
const getTypeId = (v)=>(
TypeIds[typeof v]
|| toss("Maintenance required: this value type cannot be serialized.",v)
);
const getTypeIdById = (tid)=>{
switch(tid){
case TypeIds.number.id: return TypeIds.number;
case TypeIds.bigint.id: return TypeIds.bigint;
case TypeIds.boolean.id: return TypeIds.boolean;
case TypeIds.string.id: return TypeIds.string;
default: toss("Invalid type ID:",tid);
}
};
/**
Returns an array of the deserialized state stored by the most
recent serialize() operation (from this thread or the
counterpart thread), or null if the serialization buffer is
empty. If passed a truthy argument, the serialization buffer
is cleared after deserialization.
*/
state.s11n.deserialize = function(clear=false){
//#if vfs.metrics.enable
++metrics.s11n.deserialize.count;
//#/if
const t = performance.now();
const argc = viewU8[0];
const rc = argc ? [] : null;
if(argc){
const typeIds = [];
let offset = 1, i, n, v;
for(i = 0; i < argc; ++i, ++offset){
typeIds.push(getTypeIdById(viewU8[offset]));
}
for(i = 0; i < argc; ++i){
const t = typeIds[i];
if(t.getter){
v = viewDV[t.getter](offset, state.littleEndian);
offset += t.size;
}else{/*String*/
n = viewDV.getInt32(offset, state.littleEndian);
offset += 4;
v = textDecoder.decode(viewU8.slice(offset, offset+n));
offset += n;
}
rc.push(v);
}
}
if(clear) viewU8[0] = 0;
//log("deserialize:",argc, rc);
//#if vfs.metrics.enable
metrics.s11n.deserialize.time += performance.now() - t;
//#/if
return rc;
};
/**
Serializes all arguments to the shared buffer for consumption
by the counterpart thread.
This routine is only intended for serializing OPFS VFS
arguments and (in at least one special case) result values,
and the buffer is sized to be able to comfortably handle
those.
If passed no arguments then it zeroes out the serialization
state.
*/
state.s11n.serialize = function(...args){
const t = performance.now();
//#if vfs.metrics.enable
++metrics.s11n.serialize.count;
//#/if
if(args.length){
//log("serialize():",args);
const typeIds = [];
let i = 0, offset = 1;
viewU8[0] = args.length & 0xff /* header = # of args */;
for(; i < args.length; ++i, ++offset){
/* Write the TypeIds.id value into the next args.length
bytes. */
typeIds.push(getTypeId(args[i]));
viewU8[offset] = typeIds[i].id;
}
for(i = 0; i < args.length; ++i) {
/* Deserialize the following bytes based on their
corresponding TypeIds.id from the header. */
const t = typeIds[i];
if(t.setter){
viewDV[t.setter](offset, args[i], state.littleEndian);
offset += t.size;
}else{/*String*/
const s = textEncoder.encode(args[i]);
viewDV.setInt32(offset, s.byteLength, state.littleEndian);
offset += 4;
viewU8.set(s, offset);
offset += s.byteLength;
}
}
//log("serialize() result:",viewU8.slice(0,offset));
}else{
viewU8[0] = 0;
}
//#if vfs.metrics.enable
metrics.s11n.serialize.time += performance.now() - t;
//#/if
};
//#if defined opfs-async-proxy
state.s11n.storeException = state.asyncS11nExceptions
? ((priority,e)=>{
if(priority<=state.asyncS11nExceptions){
state.s11n.serialize([e.name,': ',e.message].join(""));
}
})
: ()=>{};
//#/if
return state.s11n;
//#undef vfs.metrics.enable
}/*initS11n()*/;

Some files were not shown because too many files have changed in this diff Show More