Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a1de06a4c6 | |||
| 158436f53d | |||
| d261437615 | |||
| 8c7f142470 | |||
| 525d297450 | |||
| 2605119d14 | |||
| de223ffbbd | |||
| ea0cda00d6 |
+3
-11
@@ -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@
|
||||
|
||||
+160
-247
@@ -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 \
|
||||
@@ -1396,7 +1459,7 @@ SRC01 = \
|
||||
$(TOP)\src\status.c \
|
||||
$(TOP)\src\table.c \
|
||||
$(TOP)\src\threads.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\treeview.c \
|
||||
$(TOP)\src\trigger.c \
|
||||
@@ -1707,7 +1770,6 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_PERCENTILE=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1
|
||||
!ENDIF
|
||||
|
||||
@@ -1719,7 +1781,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 +1827,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 +1928,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)
|
||||
@@ -1968,18 +2019,8 @@ sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-v
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\split-sqlite3c.tcl
|
||||
|
||||
TCLSQLITEEX = \
|
||||
$(TOP)\ext\qrf\qrf.h \
|
||||
$(TOP)\ext\qrf\qrf.c \
|
||||
$(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-ex.c: $(TCLSQLITEEX) $(TOP)\tool\mkcombo.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\mkcombo.tcl $(TCLSQLITEEX) -o $@
|
||||
# <</mark>>
|
||||
|
||||
tclsqlite-ex.c:
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: $(SQLITE3C)
|
||||
@@ -2279,11 +2320,11 @@ whereexpr.lo: $(TOP)\src\whereexpr.c $(HDR)
|
||||
window.lo: $(TOP)\src\window.c $(HDR)
|
||||
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\window.c
|
||||
|
||||
tclsqlite.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c /OUT:tclsqlite.lo
|
||||
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-shell.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c
|
||||
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
@@ -2336,8 +2377,6 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\qrf\qrf.c \
|
||||
$(TOP)\ext\qrf\qrf.h \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\intck\sqlite3intck.c \
|
||||
@@ -2377,7 +2416,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.
|
||||
#
|
||||
@@ -2501,9 +2540,9 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STRICT_SUBTYPE=1
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
|
||||
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC0)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
|
||||
!ELSE
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC1)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
|
||||
!ENDIF
|
||||
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
@@ -2551,19 +2590,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 +2651,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,23 +2674,17 @@ 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: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\permutations.test shell
|
||||
|
||||
shelltest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release 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)
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.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 > $@
|
||||
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqltclsh.c: sqlite3.c tclsqlite-ex.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
sqltclsh.c: sqlite3.c $(TOP)\src\tclsqlite.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
|
||||
@@ -2653,6 +2694,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 \
|
||||
$(TOP)\src\tclsqlite.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 +2743,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 +2780,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)
|
||||
@@ -2741,9 +2788,6 @@ rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H
|
||||
$(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \
|
||||
$(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
$(AUXTEST).exe: $(TOP)\test\c\$(AUXTEST).c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\test\c\$(AUXTEST).c sqlite3.lo /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
THREADTEST3_SRC = \
|
||||
$(TOP)\test\threadtest3.c \
|
||||
$(TOP)\test\tt3_checkpoint.c \
|
||||
@@ -2767,135 +2811,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>>
|
||||
@@ -2925,7 +2840,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
|
||||
@@ -2940,5 +2854,4 @@ clean:
|
||||
del /Q fts5.* fts5parse.* 2>NUL
|
||||
del /q src-verify.exe 2>NUL
|
||||
del /q jimsh.exe jimsh0.exe 2>NUL
|
||||
-$(TCLSH_CMD) test/testrunner.tcl clean
|
||||
# <</mark>>
|
||||
|
||||
@@ -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.
|
||||
|
||||
+14
-1
@@ -117,6 +117,19 @@ sqlite_cfg.h: $(AS_AUTO_DEF)
|
||||
# CFLAGS for sqlite3$(T.exe)
|
||||
#
|
||||
SHELL_OPT ?= @OPT_SHELL@
|
||||
SHELL_OPT += -DSQLITE_DQS=0
|
||||
SHELL_OPT += -DSQLITE_ENABLE_FTS4
|
||||
#SHELL_OPT += -DSQLITE_ENABLE_FTS5
|
||||
SHELL_OPT += -DSQLITE_ENABLE_RTREE
|
||||
SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
SHELL_OPT += -DSQLITE_ENABLE_PERCENTILE
|
||||
SHELL_OPT += -DSQLITE_STRICT_SUBTYPE=1
|
||||
|
||||
#
|
||||
# Library-level feature flags
|
||||
@@ -282,7 +295,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
-14
@@ -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
|
||||
|
||||
|
||||
@@ -941,7 +1020,6 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_PERCENTILE=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_STRICT_SUBTYPE=1
|
||||
!ENDIF
|
||||
|
||||
@@ -990,8 +1068,6 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
|
||||
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
|
||||
tclsqlite-ex.c:
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: $(SQLITE3C)
|
||||
|
||||
+1
-11
@@ -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
@@ -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
@@ -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 ""
|
||||
|
||||
@@ -557,25 +557,7 @@ proc proc-debug {msg} {
|
||||
}
|
||||
|
||||
define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags.
|
||||
#
|
||||
# OPT_SHELL = feature-related CFLAGS for the sqlite3 CLI app. The
|
||||
# list's initial values are defaults which are always applied and not
|
||||
# affected by --feature-flags. The list is appended to by various
|
||||
# --feature-flags.
|
||||
define OPT_SHELL {
|
||||
-DSQLITE_DQS=0
|
||||
-DSQLITE_ENABLE_FTS4
|
||||
-DSQLITE_ENABLE_RTREE
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
-DSQLITE_ENABLE_STMTVTAB
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
-DSQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
-DSQLITE_ENABLE_PERCENTILE
|
||||
-DSQLITE_STRICT_SUBTYPE=1
|
||||
}
|
||||
define OPT_SHELL {} ; # Feature-related CFLAGS for the sqlite3 CLI app
|
||||
########################################################################
|
||||
# Adds $args, if not empty, to OPT_FEATURE_FLAGS. If the first arg is
|
||||
# -shell then it strips that arg and passes the remaining args the
|
||||
@@ -679,7 +661,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 +724,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]
|
||||
@@ -805,8 +787,7 @@ proc sqlite-handle-common-feature-flags {} {
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
|
||||
}
|
||||
}
|
||||
bytecode-vtab -DSQLITE_ENABLE_BYTECODE_VTAB {}
|
||||
scanstatus {-DSQLITE_ENABLE_STMT_SCANSTATUS -DSQLITE_ENABLE_BYTECODE_VTAB} {}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
|
||||
dbpage -DSQLITE_ENABLE_DBPAGE_VTAB {}
|
||||
dbstat -DSQLITE_ENABLE_DBSTAT_VTAB {}
|
||||
@@ -1137,27 +1118,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 +1153,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
@@ -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:   `mkdir $HOME/local`
|
||||
<li>Run:   `./configure --prefix=$HOME/local`<br>
|
||||
SQLite deliverable builds add:   `--static CFLAGS=-Os`
|
||||
<li>Run:   `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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
+8
-22
@@ -696,7 +696,6 @@ other than that, the order of directives in Lemon is arbitrary.</p>
|
||||
<li><tt><a href='#pright'>%right</a></tt>
|
||||
<li><tt><a href='#reallc'>%realloc</a></tt>
|
||||
<li><tt><a href='#stack_overflow'>%stack_overflow</a></tt>
|
||||
<li><tt><a href='#reallc'>%stack_size_limit</a></tt>
|
||||
<li><tt><a href='#stack_size'>%stack_size</a></tt>
|
||||
<li><tt><a href='#start_symbol'>%start_symbol</a></tt>
|
||||
<li><tt><a href='#syntax_error'>%syntax_error</a></tt>
|
||||
@@ -1204,33 +1203,20 @@ the wildcard token and some other token, the other token is always used.
|
||||
The wildcard token is only matched if there are no alternatives.</p>
|
||||
|
||||
<a id='reallc'></a>
|
||||
<h4>4.4.26 The <tt>%realloc</tt>, <tt>%free</tt>, and
|
||||
<tt>%stack_size_limit</tt> directives</h4>
|
||||
<h4>4.4.26 The <tt>%realloc</tt> and <tt>%free</tt> directives</h4>
|
||||
|
||||
<p>The <tt>%realloc</tt> and <tt>%free</tt> directives defines function
|
||||
that allocate and free heap memory. The signatures and semantics of
|
||||
these functions are similar to the realloc() and free() functions from
|
||||
the standard C library, except that these functions take an extra
|
||||
parameter at the end that is determined by %extra_context. If
|
||||
%extra_context is not defined, then the extra argument is 0. The
|
||||
extra parameter provides the capability to do better error reporting
|
||||
in the event of a memory allocation error, and/or to use an alternative
|
||||
private application heap.
|
||||
that allocate and free heap memory. The signatures of these functions
|
||||
should be the same as the realloc() and free() functions from the standard
|
||||
C library.
|
||||
|
||||
<p>If both of these functions are defined then they are used to
|
||||
allocate and free memory for supplemental parser stack space, if
|
||||
the initial parse stack space is exceeded. The initial parser stack size
|
||||
<p>If both of these functions are defined
|
||||
then these functions are used to allocate and free
|
||||
memory for supplemental parser stack space, if the initial
|
||||
parse stack space is exceeded. The initial parser stack size
|
||||
is specified by either <tt>%stack_size</tt> or the
|
||||
-DYYSTACKDEPTH compile-time flag.
|
||||
|
||||
<p>The <tt>%stack_size_limit</tt> directive defines a function that returns
|
||||
the maximum allowed parser stack size. If this diretive does not exist,
|
||||
no size limit is enforced. This function takes a single argument which
|
||||
is the %extra_context value or "0" if %extra_context is not defined.
|
||||
The function should return an integer that is the maximum
|
||||
number of parser stack entries. If more stack space
|
||||
than this is needed, the %stack_overflow code is invoked.
|
||||
|
||||
<a id='errors'></a>
|
||||
<h2>5.0 Error Processing</h2>
|
||||
|
||||
|
||||
+57
-102
@@ -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 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
|
||||
```
|
||||
|
||||
@@ -90,7 +90,7 @@ foreach {tn setup} {
|
||||
proc do_rec_test {tn sql res} {
|
||||
set res [squish [string trim $res]]
|
||||
set tst [subst -nocommands {
|
||||
squish [string trim [exec $::CLI -noinit test.db ".expert" {$sql;}]]
|
||||
squish [string trim [exec $::CLI test.db ".expert" {$sql;}]]
|
||||
}]
|
||||
uplevel [list do_test $tn $tst $res]
|
||||
}
|
||||
|
||||
+6
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ){
|
||||
|
||||
@@ -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
-41
@@ -2093,7 +2093,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 +5240,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 +5319,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 +5399,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 +5454,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 +5465,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 +5493,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 +5931,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 +6413,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 +6478,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 +6508,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 +7042,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 +7141,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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -666,7 +666,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{
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+16
-19
@@ -13,14 +13,11 @@ Technical support is available in the forum:
|
||||
<https://sqlite.org/forum>
|
||||
|
||||
|
||||
> **FOREWARNING:** the JNI subproject is experimental and subject to
|
||||
any number of changes. This API is "feature-complete", with only a
|
||||
few difficult-to-reach corners of the C API not represented here,
|
||||
but it is not a supported deliverable of the project so does not
|
||||
have same backward compatibility guarantees which the C APIs
|
||||
do. That said: the [C-style API](#1to1ish) is especially resistent
|
||||
to compatibility breakage because it's designed to be as close to
|
||||
the C API as feasible.
|
||||
> **FOREWARNING:** this subproject is very much in development and
|
||||
subject to any number of changes. Please do not rely on any
|
||||
information about its API until this disclaimer is removed. The JNI
|
||||
bindings released with version 3.43 are a "tech preview." Once
|
||||
finalized, strong backward compatibility guarantees will apply.
|
||||
|
||||
Project goals/requirements:
|
||||
|
||||
@@ -165,13 +162,11 @@ or propagate exceptions and must return error information (if any) via
|
||||
result codes or `null`. The only cases where the C-style APIs may
|
||||
throw is through client-side misuse, e.g. passing in a null where it
|
||||
may cause a `NullPointerException`. The APIs clearly mark function
|
||||
parameters which should not be null, and it internally uses the
|
||||
`SQLITE_API_ARMOR` mechanism to help product against such misuse. Some
|
||||
C-style APIs explicitly accept `null` as a no-op for usability's sake,
|
||||
and some of the JNI APIs deliberately return an error code, instead of
|
||||
segfaulting, when passed a `null`. There are no known cases where it
|
||||
will misuse memory if passed a `null` or out-of-range value from
|
||||
client code.
|
||||
parameters which should not be null, but does not generally actively
|
||||
defend itself against such misuse. Some C-style APIs explicitly accept
|
||||
`null` as a no-op for usability's sake, and some of the JNI APIs
|
||||
deliberately return an error code, instead of segfaulting, when passed
|
||||
a `null`.
|
||||
|
||||
Client-defined callbacks _must never throw exceptions_ unless _very
|
||||
explicitly documented_ as being throw-safe. Exceptions are generally
|
||||
@@ -199,8 +194,7 @@ Some constructs, when modelled 1-to-1 from C to Java, are unduly
|
||||
clumsy to work with in Java because they try to shoehorn C's way of
|
||||
doing certain things into Java's wildly different ways. The following
|
||||
subsections cover those, starting with a verbose explanation and
|
||||
demonstration of where such changes are "really necessary" for
|
||||
usability's sake...
|
||||
demonstration of where such changes are "really necessary"...
|
||||
|
||||
### Custom Collations
|
||||
|
||||
@@ -292,9 +286,12 @@ binding. The Java API has only one core function-registration function:
|
||||
|
||||
```java
|
||||
int sqlite3_create_function(sqlite3 db, String funcName, int nArgs,
|
||||
int flags, SQLFunction func);
|
||||
int encoding, SQLFunction func);
|
||||
```
|
||||
|
||||
> Design question: does the encoding argument serve any purpose in
|
||||
Java? That's as-yet undetermined. If not, it will be removed.
|
||||
|
||||
`SQLFunction` is not used directly, but is instead instantiated via
|
||||
one of its three subclasses:
|
||||
|
||||
@@ -316,4 +313,4 @@ in-flux nature of this API.
|
||||
Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and
|
||||
`sqlite3_update_hook()`, use interfaces similar to those shown above.
|
||||
Despite the changes in signature, the JNI layer makes every effort to
|
||||
provide the same semantics as the C API documentation describes.
|
||||
provide the same semantics as the C API documentation suggests.
|
||||
|
||||
+51
-43
@@ -133,9 +133,11 @@
|
||||
** Which sqlite3.c we're using needs to be configurable to enable
|
||||
** building against a custom copy, e.g. the SEE variant. We have to
|
||||
** include sqlite3.c, as opposed to sqlite3.h, in order to get access
|
||||
** to some internal details like SQLITE_MAX_... and friends, and keep
|
||||
** those consistent with this build. This increases the rebuild time
|
||||
** considerably, however.
|
||||
** to some internal details like SQLITE_MAX_... and friends. This
|
||||
** increases the rebuild time considerably but we need this in order
|
||||
** to access some internal functionality and keep the to-Java-exported
|
||||
** values of SQLITE_MAX_... and SQLITE_LIMIT_... in sync with the C
|
||||
** build.
|
||||
*/
|
||||
#ifndef SQLITE_C
|
||||
# define SQLITE_C sqlite3.c
|
||||
@@ -333,7 +335,7 @@ struct S3JniNphOp {
|
||||
const char * const zMember /* Name of member property */;
|
||||
const char * const zTypeSig /* JNI type signature of zMember */;
|
||||
/*
|
||||
** klazz is a global ref to the class represented by zName.
|
||||
** klazz is a global ref to the class represented by pRef.
|
||||
**
|
||||
** According to:
|
||||
**
|
||||
@@ -997,20 +999,19 @@ static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
|
||||
** JNI bindings such as sqlite3_prepare_v2/v3(), and definitely not
|
||||
** from client code.
|
||||
**
|
||||
** Returns err_code _unless_ err_code is 0 and sqlite3_set_errmsg()
|
||||
** fails with OOM, in which case it may return SQLITE_OOM or fail
|
||||
** fatally.
|
||||
**
|
||||
** This function predates sqlite3_set_errmsg(), which is why it has a
|
||||
** slightly different interface. Before that function was introduced,
|
||||
** this code used the SQLite-internal APIs to do this.
|
||||
** Returns err_code.
|
||||
*/
|
||||
static int s3jni_db_error(JNIEnv * env, sqlite3* const db,
|
||||
int err_code, const char * const zMsg){
|
||||
static int s3jni_db_error(sqlite3* const db, int err_code,
|
||||
const char * const zMsg){
|
||||
if( db!=0 ){
|
||||
int const rc = sqlite3_set_errmsg(db, err_code, zMsg);
|
||||
s3jni_oom_fatal(0==rc);
|
||||
if( rc && !err_code ) err_code=rc;
|
||||
if( 0==zMsg ){
|
||||
sqlite3Error(db, err_code);
|
||||
}else{
|
||||
const int nMsg = sqlite3Strlen30(zMsg);
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg);
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
}
|
||||
}
|
||||
return err_code;
|
||||
}
|
||||
@@ -1234,11 +1235,11 @@ static int s3jni__db_exception(JNIEnv * const env, sqlite3 * const pDb,
|
||||
char * zMsg;
|
||||
S3JniExceptionClear;
|
||||
zMsg = s3jni_exception_error_msg(env, ex);
|
||||
s3jni_db_error(env, pDb, errCode, zMsg ? zMsg : zDfltMsg);
|
||||
s3jni_db_error(pDb, errCode, zMsg ? zMsg : zDfltMsg);
|
||||
sqlite3_free(zMsg);
|
||||
S3JniUnrefLocal(ex);
|
||||
}else if( zDfltMsg ){
|
||||
s3jni_db_error(env, pDb, errCode, zDfltMsg);
|
||||
s3jni_db_error(pDb, errCode, zDfltMsg);
|
||||
}
|
||||
return errCode;
|
||||
}
|
||||
@@ -1955,6 +1956,15 @@ static void S3JniUdf_finalizer(void * s){
|
||||
S3JniUdf_free(s3jni_env(), (S3JniUdf*)s, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Helper for processing args to UDF handlers with signature
|
||||
** (sqlite3_context*,int,sqlite3_value**).
|
||||
*/
|
||||
typedef struct {
|
||||
jobject jcx /* sqlite3_context */;
|
||||
jobjectArray jargv /* sqlite3_value[] */;
|
||||
} udf_jargs;
|
||||
|
||||
/*
|
||||
** Converts the given (cx, argc, argv) into arguments for the given
|
||||
** UDF, writing the result (Java wrappers for cx and argv) in the
|
||||
@@ -1996,7 +2006,7 @@ error_oom:
|
||||
/*
|
||||
** Requires that jCx and jArgv are sqlite3_context
|
||||
** resp. array-of-sqlite3_value values initialized by udf_args(). The
|
||||
** (argc,argv) are (0,NULL) for UDF types with no arguments. This
|
||||
** latter will be 0-and-NULL for UDF types with no arguments. This
|
||||
** function zeroes out the nativePointer member of jCx and each entry
|
||||
** in jArgv. This is a safety-net precaution to avoid undefined
|
||||
** behavior if a Java-side UDF holds a reference to its context or one
|
||||
@@ -2089,19 +2099,19 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc,
|
||||
sqlite3_value** const argv, S3JniUdf * const s,
|
||||
jmethodID xMethodID, const char * const zFuncType){
|
||||
S3JniDeclLocal_env;
|
||||
jobject jcx = 0 /* sqlite3_context */;
|
||||
jobjectArray jargv = 0 /* sqlite3_value[] */;
|
||||
int rc = udf_args(env, pCx, argc, argv, &jcx, &jargv);
|
||||
udf_jargs args = {0,0};
|
||||
int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv);
|
||||
|
||||
if( 0 == rc ){
|
||||
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx, jargv);
|
||||
(*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv);
|
||||
S3JniIfThrew{
|
||||
rc = udf_report_exception(env, 'F'==zFuncType[1]/*xFunc*/, pCx,
|
||||
s->zFuncName, zFuncType);
|
||||
}
|
||||
udf_unargs(env, jcx, argc, jargv);
|
||||
udf_unargs(env, args.jcx, argc, args.jargv);
|
||||
}
|
||||
S3JniUnrefLocal(jcx);
|
||||
S3JniUnrefLocal(jargv);
|
||||
S3JniUnrefLocal(args.jcx);
|
||||
S3JniUnrefLocal(args.jargv);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3290,7 +3300,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
|
||||
S3JniDb_mutex_enter;
|
||||
ps = S3JniDb_from_jlong(jpDb);
|
||||
if( !ps ){
|
||||
s3jni_db_error(env, ps->pDb, SQLITE_MISUSE, 0);
|
||||
s3jni_db_error(ps->pDb, SQLITE_MISUSE, 0);
|
||||
S3JniDb_mutex_leave;
|
||||
return 0;
|
||||
}
|
||||
@@ -3310,14 +3320,13 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
|
||||
else sqlite3_rollback_hook(ps->pDb, 0, 0);
|
||||
}else{
|
||||
jclass const klazz = (*env)->GetObjectClass(env, jHook);
|
||||
jmethodID const xCallback =
|
||||
(*env)->GetMethodID(env, klazz, "call",
|
||||
isCommit ? "()I" : "()V");
|
||||
jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call",
|
||||
isCommit ? "()I" : "()V");
|
||||
S3JniUnrefLocal(klazz);
|
||||
S3JniIfThrew {
|
||||
S3JniExceptionReport;
|
||||
S3JniExceptionClear;
|
||||
s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
|
||||
s3jni_db_error(ps->pDb, SQLITE_ERROR,
|
||||
"Cannot not find matching call() method in"
|
||||
"hook object.");
|
||||
}else{
|
||||
@@ -3597,7 +3606,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
|
||||
(*env)->GetMethodID(env, klazz, "call", "([B[B)I");
|
||||
S3JniUnrefLocal(klazz);
|
||||
S3JniIfThrew{
|
||||
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
|
||||
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
|
||||
"Could not get call() method from "
|
||||
"CollationCallback object.");
|
||||
}else{
|
||||
@@ -3636,15 +3645,15 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2()
|
||||
|
||||
if( !pDb || !jFuncName ){
|
||||
return SQLITE_MISUSE;
|
||||
}else if( !encodingTypeIsValid(eTextRep & 0x0f) ){
|
||||
return s3jni_db_error(env, pDb, SQLITE_FORMAT,
|
||||
}else if( !encodingTypeIsValid(eTextRep) ){
|
||||
return s3jni_db_error(pDb, SQLITE_FORMAT,
|
||||
"Invalid function encoding option.");
|
||||
}
|
||||
s = S3JniUdf_alloc(env, jFunctor);
|
||||
if( !s ) return SQLITE_NOMEM;
|
||||
|
||||
if( UDF_UNKNOWN_TYPE==s->type ){
|
||||
rc = s3jni_db_error(env, pDb, SQLITE_MISUSE,
|
||||
rc = s3jni_db_error(pDb, SQLITE_MISUSE,
|
||||
"Cannot unambiguously determine function type.");
|
||||
S3JniUdf_free(env, s, 1);
|
||||
goto error_cleanup;
|
||||
@@ -4003,7 +4012,7 @@ S3JniApi(sqlite3_jni_db_error(), jint, 1jni_1db_1error)(
|
||||
zStr = jStr
|
||||
? s3jni_jstring_to_utf8( jStr, 0)
|
||||
: NULL;
|
||||
rc = s3jni_db_error(env, ps->pDb, (int)jRc, zStr );
|
||||
rc = s3jni_db_error( ps->pDb, (int)jRc, zStr );
|
||||
sqlite3_free(zStr);
|
||||
}
|
||||
return rc;
|
||||
@@ -4312,7 +4321,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
|
||||
jTable = jDbName ? s3jni_utf8_to_jstring( zTable, -1) : 0;
|
||||
S3JniIfThrew {
|
||||
S3JniExceptionClear;
|
||||
s3jni_db_error(env, ps->pDb, SQLITE_NOMEM, 0);
|
||||
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
|
||||
}else{
|
||||
assert( hook.jObj );
|
||||
assert( hook.midCallback );
|
||||
@@ -4414,7 +4423,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jlong jpDb, jobject
|
||||
S3JniUnrefLocal(klazz);
|
||||
S3JniIfThrew {
|
||||
S3JniExceptionClear;
|
||||
s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
|
||||
s3jni_db_error(ps->pDb, SQLITE_ERROR,
|
||||
"Cannot not find matching callback on "
|
||||
"(pre)update hook object.");
|
||||
}else{
|
||||
@@ -4523,7 +4532,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
|
||||
S3JniUnrefLocal(klazz);
|
||||
S3JniIfThrew {
|
||||
S3JniExceptionClear;
|
||||
s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
|
||||
s3jni_db_error(ps->pDb, SQLITE_ERROR,
|
||||
"Cannot not find matching xCallback() on "
|
||||
"ProgressHandler object.");
|
||||
}else{
|
||||
@@ -4897,9 +4906,8 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
|
||||
")I");
|
||||
S3JniUnrefLocal(klazz);
|
||||
S3JniIfThrew {
|
||||
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
|
||||
"Error setting up Java parts of "
|
||||
"authorizer hook.");
|
||||
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
|
||||
"Error setting up Java parts of authorizer hook.");
|
||||
}else{
|
||||
rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps);
|
||||
}
|
||||
@@ -5183,7 +5191,7 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
|
||||
S3JniUnrefLocal(klazz);
|
||||
S3JniIfThrew {
|
||||
S3JniExceptionClear;
|
||||
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR,
|
||||
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
|
||||
"Cannot not find matching call() on "
|
||||
"TracerCallback object.");
|
||||
}else{
|
||||
|
||||
@@ -245,10 +245,8 @@ extern "C" {
|
||||
#define org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_CACHE_USED_SHARED 11L
|
||||
#undef org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_CACHE_SPILL
|
||||
#define org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_CACHE_SPILL 12L
|
||||
#undef org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_TEMPBUF_SPILL
|
||||
#define org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_TEMPBUF_SPILL 13L
|
||||
#undef org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_MAX
|
||||
#define org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_MAX 13L
|
||||
#define org_sqlite_jni_capi_CApi_SQLITE_DBSTATUS_MAX 12L
|
||||
#undef org_sqlite_jni_capi_CApi_SQLITE_UTF8
|
||||
#define org_sqlite_jni_capi_CApi_SQLITE_UTF8 1L
|
||||
#undef org_sqlite_jni_capi_CApi_SQLITE_UTF16LE
|
||||
|
||||
@@ -115,9 +115,8 @@ public final class CApi {
|
||||
JNIEnv is not cached, else returns true, but this information is
|
||||
primarily for testing of the JNI bindings and is not information
|
||||
which client-level code can use to make any informed
|
||||
decisions. The semantics of its return type and value are not
|
||||
considered stable and may change at any time. i.e. act as if it
|
||||
returns null.
|
||||
decisions. Its return type and semantics are not considered
|
||||
stable and may change at any time.
|
||||
*/
|
||||
public static native boolean sqlite3_java_uncache_thread();
|
||||
|
||||
@@ -2586,8 +2585,7 @@ public final class CApi {
|
||||
public static final int SQLITE_DBSTATUS_DEFERRED_FKS = 10;
|
||||
public static final int SQLITE_DBSTATUS_CACHE_USED_SHARED = 11;
|
||||
public static final int SQLITE_DBSTATUS_CACHE_SPILL = 12;
|
||||
public static final int SQLITE_DBSTATUS_TEMPBUF_SPILL = 13;
|
||||
public static final int SQLITE_DBSTATUS_MAX = 13;
|
||||
public static final int SQLITE_DBSTATUS_MAX = 12;
|
||||
|
||||
// encodings
|
||||
public static final int SQLITE_UTF8 = 1;
|
||||
|
||||
@@ -815,9 +815,7 @@ public class Tester1 implements Runnable {
|
||||
};
|
||||
|
||||
// Register and use the function...
|
||||
int rc = sqlite3_create_function(db, "myfunc", -1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS,
|
||||
func);
|
||||
int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func);
|
||||
affirm(0 == rc);
|
||||
affirm(0 == xFuncAccum.value);
|
||||
final sqlite3_stmt stmt = prepare(db, "SELECT myfunc(1,2,3)");
|
||||
|
||||
@@ -171,7 +171,6 @@ public final class Sqlite implements AutoCloseable {
|
||||
public static final int DBSTATUS_DEFERRED_FKS = CApi.SQLITE_DBSTATUS_DEFERRED_FKS;
|
||||
public static final int DBSTATUS_CACHE_USED_SHARED = CApi.SQLITE_DBSTATUS_CACHE_USED_SHARED;
|
||||
public static final int DBSTATUS_CACHE_SPILL = CApi.SQLITE_DBSTATUS_CACHE_SPILL;
|
||||
public static final int DBSTATUS_TEMPBUF_SPILL = CApi.SQLITE_DBSTATUS_TEMPBUF_SPILL;
|
||||
|
||||
// Limits
|
||||
public static final int LIMIT_LENGTH = CApi.SQLITE_LIMIT_LENGTH;
|
||||
|
||||
+2
-2
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
+74
-176
@@ -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,13 @@ 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;
|
||||
}
|
||||
|
||||
#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
|
||||
/* To allow a standalone DLL, make test_windirent.c use the same
|
||||
* redefined SQLite API calls as the above extension code does.
|
||||
* Just pull in this .c to accomplish this. As a beneficial side
|
||||
* effect, this extension becomes a single translation unit. */
|
||||
# include "test_windirent.c"
|
||||
#endif
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) ){
|
||||
|
||||
@@ -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;
|
||||
@@ -258,7 +258,7 @@ int sqlite3_fputs(const char *z, FILE *out){
|
||||
|
||||
|
||||
/*
|
||||
** Work-alikes for fprintf() and vfprintf() from the standard C library.
|
||||
** Work-alike for fprintf() from the standard C library.
|
||||
*/
|
||||
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
int rc;
|
||||
@@ -285,24 +285,6 @@ int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){
|
||||
int rc;
|
||||
if( UseWtextForOutput(out) ){
|
||||
/* When writing to the command-prompt in Windows, it is necessary
|
||||
** to use _O_WTEXT input mode and write UTF-16 characters.
|
||||
*/
|
||||
char *z;
|
||||
z = sqlite3_vmprintf(zFormat, ap);
|
||||
sqlite3_fputs(z, out);
|
||||
rc = (int)strlen(z);
|
||||
sqlite3_free(z);
|
||||
}else{
|
||||
/* Writing to a file or other destination, just write bytes without
|
||||
** any translation. */
|
||||
rc = vfprintf(out, zFormat, ap);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the mode for an output stream. mode argument is typically _O_BINARY or
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#ifdef _WIN32
|
||||
/**** Definitions For Windows ****/
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <windows.h>
|
||||
|
||||
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
|
||||
@@ -39,7 +38,6 @@ FILE *sqlite3_popen(const char *zCommand, const char *type);
|
||||
char *sqlite3_fgets(char *s, int size, FILE *stream);
|
||||
int sqlite3_fputs(const char *s, FILE *stream);
|
||||
int sqlite3_fprintf(FILE *stream, const char *format, ...);
|
||||
int sqlite3_vfprintf(FILE *stream, const char *format, va_list);
|
||||
void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
|
||||
|
||||
@@ -51,7 +49,6 @@ void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
#define sqlite3_fgets fgets
|
||||
#define sqlite3_fputs fputs
|
||||
#define sqlite3_fprintf fprintf
|
||||
#define sqlite3_vfprintf vfprintf
|
||||
#define sqlite3_fsetmode(F,X) /*no-op*/
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -1,775 +0,0 @@
|
||||
# SQLite Query Result Formatting Subsystem
|
||||
|
||||
The "Query Result Formatter" or "QRF" subsystem is a C-language
|
||||
subroutine that formats the output from an SQLite query for display using
|
||||
a fix-width font, for example on a terminal window over an SSH connection.
|
||||
The output format is configurable. The application can request various
|
||||
table formats, with flexible column widths and alignments, row-oriented
|
||||
formats, such as CSV and similar, as well as various special purpose formats
|
||||
like JSON.
|
||||
|
||||
For the first 25 years of SQLite's existance, the
|
||||
[command-line interface](https://sqlite.org/cli.html) (CLI)
|
||||
formatted query results using a hodge-podge of routines
|
||||
that had grown slowly by accretion. The QRF was created
|
||||
in fall of 2025 to refactor and reorganize this code into
|
||||
a more usable form. The idea behind QRF is to implement all the
|
||||
query result formatting capabilities of the CLI in a subroutine
|
||||
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:
|
||||
|
||||
> ~~~
|
||||
sqlite3_qrf_spec spec; /* Format specification */
|
||||
char *zErrMsg; /* Text error message (optional) */
|
||||
char *zResult = 0; /* Formatted output written here */
|
||||
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 */
|
||||
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Format 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 */
|
||||
~~~
|
||||
|
||||
The `sqlite3_qrf_spec` object describes the desired output format
|
||||
and where to send 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:
|
||||
|
||||
> ~~~
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
struct sqlite3_qrf_spec {
|
||||
unsigned char iVersion; /* Version number of this structure */
|
||||
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
|
||||
unsigned char eEsc; /* How to escape control characters in text */
|
||||
unsigned char eText; /* Quoting style for text */
|
||||
unsigned char eTitle; /* Quating style for the text of column names */
|
||||
unsigned char eBlob; /* Quoting style for BLOBs */
|
||||
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 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[] */
|
||||
short int *aWidth; /* Column widths */
|
||||
unsigned char *aAlign; /* Column alignments */
|
||||
char *zColumnSep; /* Alternative column separator */
|
||||
char *zRowSep; /* Alternative row separator */
|
||||
char *zTableName; /* Output table name */
|
||||
char *zNull; /* Rendering of NULL */
|
||||
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
|
||||
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
|
||||
void *pRenderArg; /* First argument to the xRender callback */
|
||||
void *pWriteArg; /* First argument to the xWrite callback */
|
||||
char **pzOutput; /* Storage location for output string */
|
||||
/* Additional fields may be added in the future */
|
||||
};
|
||||
~~~
|
||||
|
||||
Do not be alarmed by the complexity of this structure. Everything can
|
||||
be zeroed except for:
|
||||
|
||||
* `.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.
|
||||
|
||||
Further detail on the meanings of each of the fields in the
|
||||
`sqlite3_qrf_spec` object is in the subsequent sections.
|
||||
|
||||
### 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
|
||||
object. Those new fields will only be accessible if the iVersion is greater
|
||||
than 1. Thus the iVersion field is used to support upgradability.
|
||||
|
||||
### 2.2 Output Deposition (xWrite and pzOutput)
|
||||
|
||||
The formatted output can either be sent to a callback function
|
||||
or accumulated into an output buffer in memory obtained
|
||||
from sqlite3_malloc(). If the sqlite3_qrf_spec.xWrite column is not NULL,
|
||||
then that function is invoked (using sqlite3_qrf_spec.xWriteArg as its
|
||||
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
|
||||
from sqlite3_malloc(). In that case, the buffer is resized using
|
||||
sqlite3_realloc() and the new text is appended.
|
||||
|
||||
One of either sqlite3_qrf_spec.xWrite and sqlite3_qrf_spec.pzOutput must be
|
||||
non-NULL and the other must be NULL.
|
||||
|
||||
The return value from xWrite is an SQLITE result code. The usual return
|
||||
should be SQLITE_OK. But if for some reason the write fails, a different
|
||||
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.
|
||||
|
||||
Other fields in sqlite3_qrf_spec might be used or might be
|
||||
ignored, depending on the value of eStyle.
|
||||
|
||||
### 2.4 Show Column Names (bTitles)
|
||||
|
||||
The sqlite3_qrf_spec.bTitles field can be either QRF_SW_Auto,
|
||||
QRF_SW_On, or QRF_SW_Off. Those three constants also have shorter
|
||||
alternative spellings: QRF_Auto, QRF_No, and
|
||||
QRF_Yes.
|
||||
|
||||
> ~~~
|
||||
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
|
||||
#define QRF_SW_Off 1 /* This setting is forced off */
|
||||
#define QRF_SW_On 2 /* This setting is forced on */
|
||||
#define QRF_Auto 0 /* Alternate spelling for QRF_SW_Auto and others */
|
||||
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
|
||||
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
|
||||
~~~
|
||||
|
||||
If the value is QRF_Yes, then column names appear in the output.
|
||||
If the value is QRF_No, column names are omitted. If the
|
||||
value is QRF_Auto, then an appropriate default is chosen.
|
||||
|
||||
### 2.5 Control Character Escapes (eEsc)
|
||||
|
||||
The sqlite3_qrf_spec.eEsc determines how ASCII control characters are
|
||||
formatted when displaying TEXT values in the result. These are the allowed
|
||||
values:
|
||||
|
||||
> ~~~
|
||||
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
|
||||
#define QRF_ESC_Off 1 /* Do not escape control characters */
|
||||
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
|
||||
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
|
||||
~~~
|
||||
|
||||
If the value of eEsc is QRF_ESC_Ascii, then the control character
|
||||
with value X is displayed as ^Y where Y is X+0x40. Hence, a
|
||||
backspace character (U+0008) is shown as "^H".
|
||||
|
||||
If eEsc is QRF_ESC_Symbol, then control characters in the range of U+0001
|
||||
through U+001f are mapped into U+2401 through U+241f, respectively.
|
||||
|
||||
If the value of eEsc is QRF_ESC_Off, then no translation occurs
|
||||
and control characters that appear in TEXT strings are transmitted
|
||||
to the formatted output as-is. This can be dangerous in applications,
|
||||
since an adversary who can control TEXT values might be able to
|
||||
inject ANSI cursor movement sequences to hide nefarious values.
|
||||
|
||||
The QRF_ESC_Auto value for eEsc means that the query result formatter
|
||||
gets to pick whichever control-character encoding it thinks is best for
|
||||
the situation. This will usually be QRF_ESC_Ascii.
|
||||
|
||||
The TAB (U+0009), LF (U+000a) and CR-LF (U+000d,U+000a) character
|
||||
sequence are always output literally and are not mapped to alternative
|
||||
display values, regardless of this setting.
|
||||
|
||||
### 2.6 Display of TEXT values (eText, eTitle)
|
||||
|
||||
The sqlite3_qrf_spec.eText controls how text values are rendered in the
|
||||
display. sqlite3_qrf_spec.eTitle controls how column names are rendered.
|
||||
Both fields can have one of the following values:
|
||||
|
||||
> ~~~
|
||||
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
|
||||
#define QRF_TEXT_Plain 1 /* Literal text */
|
||||
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
|
||||
#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
|
||||
what it thinks will be the best text encoding.
|
||||
|
||||
A value of QRF_TEXT_Plain means that text values appear in the output exactly
|
||||
as they are found in the database file, with no translation.
|
||||
|
||||
A value of QRF_TEXT_Sql means that text values are escaped so that they
|
||||
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 4180, which defines Comma-Separated-Value or CSV files.
|
||||
Text strings that contain no special values appears as-is. Text strings
|
||||
that contain special values are contained in double-quotes (U+0022) and
|
||||
any double-quotes within the value are doubled.
|
||||
|
||||
A value of QRF_TEXT_Html means that text values are escaped for use in
|
||||
HTML. Special characters "<", "&", ">", """, and "'"
|
||||
are displayed as "&lt;", "&amp;", "&gt;", "&quot;",
|
||||
and "&#39;", respectively.
|
||||
|
||||
A value of QRF_TEXT_Tcl means that text values are displayed inside of
|
||||
double-quotes and special characters within the string are escaped using
|
||||
backslash escape, as in ANSI-C or TCL or Perl or other popular programming
|
||||
languages.
|
||||
|
||||
A value of QRF_TEXT_Json gives similar results as QRF_TEXT_Tcl except that the
|
||||
rules are adjusted so that the displayed string is strictly conforming
|
||||
the JSON specification.
|
||||
|
||||
### 2.7 How to display BLOB values (eBlob and bTextJsonb)
|
||||
|
||||
If the sqlite3_qrf_spec.bTextJsonb flag is QRF_SW_On and if the value to be
|
||||
displayed is JSONB, then the JSONB is translated into text JSON and the
|
||||
text is shown according to the sqlite3_qrf_spec.eText setting as
|
||||
described in the previous section.
|
||||
|
||||
If the bTextJsonb flag is QRF_SW_Off (the usual case) or if the BLOB value to
|
||||
be displayed is not JSONB, then the sqlite3_qrf_spec.eBlob field determines
|
||||
how the BLOB value is formatted. The following options are available;
|
||||
|
||||
> ~~~
|
||||
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
|
||||
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
|
||||
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
|
||||
#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
|
||||
by sqlite3_format_query_result() based on eStyle and eText.
|
||||
|
||||
A value of QRF_BLOB_Text means that BLOB values are interpreted as UTF8
|
||||
text and are displayed using formatting results set by eEsc and
|
||||
eText.
|
||||
|
||||
A value of QRF_BLOB_Sql means that BLOB values are shown as SQL BLOB
|
||||
literals: a prefix "`x'`" following by hexadecimal and ending with a
|
||||
final "`'`".
|
||||
|
||||
A value of QRF_BLOB_Hex means that BLOB values are shown as
|
||||
hexadecimal text with no delimiters.
|
||||
|
||||
A value of QRF_BLOB_Tcl means that BLOB values are shown as a
|
||||
C/Tcl/Perl string literal where every byte is an octal backslash
|
||||
escape. So a BLOB of `x'052881f3'` would be displayed as
|
||||
`"\005\050\201\363"`.
|
||||
|
||||
A value of QRF_BLOB_Json is similar to QRF_BLOB_Tcl except that is
|
||||
uses unicode backslash escapes, since JSON does not understand
|
||||
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)
|
||||
|
||||
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
|
||||
will display only the first nCharLimit characters of each value.
|
||||
Only characters that take up space are counted when enforcing this
|
||||
limit. Zero-width characters and VT100 escape sequences do not count
|
||||
toward this limit. The count is in characters, not bytes. When
|
||||
imposing this limit, the formatter adds the three characters "..."
|
||||
to the end of the value. Those added characters are not counted
|
||||
as part of the limit. Very small limits still result in truncation,
|
||||
but might render a few more characters than the limit.
|
||||
|
||||
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 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)
|
||||
|
||||
When using columnar formatting modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Markdown, or QRF_STYLE_Table), the formatter attempts to limit
|
||||
the width of any individual column to sqlite3_qrf_spec.nWrap characters
|
||||
if nWrap is non-zero. A zero value for nWrap means "unlimited".
|
||||
The nWrap limit might be exceeded if the limit is very small.
|
||||
|
||||
In order to keep individual columns within requested width limits,
|
||||
it is sometimes necessary to wrap the content for a single row of
|
||||
a single column across multiple lines. When this
|
||||
becomes necessary and if the bWordWrap setting is QRF_Yes, then the
|
||||
formatter attempts to split the content on whitespace or at a word boundary.
|
||||
If bWordWrap is QRF_No, then the formatter is free to split content
|
||||
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)
|
||||
|
||||
The sqlite3_qrf_spec.nScreenWidth field can be set the number of
|
||||
characters that will fit on one line on the viewer output device.
|
||||
This is typically a number like 80 or 132. The formatter will attempt
|
||||
to reduce the length of output lines, depending on the style, so
|
||||
that all output fits on that screen.
|
||||
|
||||
A value of zero for nScreenWidth means "unknown" or "no width limit".
|
||||
When the value is zero, the formatter makes no attempt to keep the
|
||||
lines of output short.
|
||||
|
||||
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)
|
||||
|
||||
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
|
||||
signed 16-bit integers that control the width of individual columns
|
||||
in columnar output modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Markdown, or QRF_STYLE_Table). The sqlite3_qrf_spec.nWidth
|
||||
field is the number of integers in the aWidth array.
|
||||
|
||||
If aWidth is a NULL pointer or if nWidth is zero, then the array is
|
||||
assumed to be all zeros. If nWidth is less then the number of
|
||||
columns in the output, then zero is used for the width
|
||||
for all columns past then end of the aWidth array.
|
||||
|
||||
The aWidth array is deliberately an array of 16-bit signed integers.
|
||||
Only 16 bits are used because no good comes for having very large
|
||||
column widths. The range if further restricted as follows:
|
||||
|
||||
> ~~~
|
||||
#define QRF_MAX_WIDTH 10000 /* Maximum column width */
|
||||
#define QRF_MIN_WIDTH 0 /* Minimum column width */
|
||||
~~~
|
||||
|
||||
A width greater than then QRF_MAX_WIDTH is interpreted as QRF_MAX_WIDTH.
|
||||
|
||||
Any aWidth\[\] value of zero means the formatter should use a flexible
|
||||
width column (limited only by sqlite_qrf_spec.mxWidth) that is just
|
||||
big enough to hold the largest row.
|
||||
|
||||
For historical compatibility, aWidth\[\] can contain negative values,
|
||||
down to -QRF_MAX_WIDTH. The column width used is the absolute value
|
||||
of the number in aWidth\[\]. The only difference is that negative
|
||||
values cause the default horizontal alignment to be QRF_ALIGN_Right.
|
||||
The sign of the aWidth\[\] values only affects alignment if the
|
||||
alignment is not otherwise specified by aAlign\[\] or eDfltAlign.
|
||||
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)
|
||||
|
||||
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
|
||||
width constraints so that they span many rows of text. Other cells
|
||||
might be narrower and shorter. In columnar formats, the display width
|
||||
of a cell is the maximum of the widest value in the same column, and the
|
||||
display height is the height of the tallest value in the same row.
|
||||
So some cells might be much taller and wider than necessary to hold
|
||||
their values.
|
||||
|
||||
Alignment determines where smaller values are placed within larger cells.
|
||||
|
||||
The sqlite3_qrf_spec.aAlign field points to an array of unsigned characters
|
||||
that specifies alignment (both vertical and horizontal) of individual
|
||||
columns within the table. The sqlite3_qrf_spec.nAlign fields holds
|
||||
the number of entries in the aAlign\[\] array.
|
||||
|
||||
If sqlite3_qrf_spec.aAlign is a NULL pointer or if sqlite3_qrf_spec.nAlign
|
||||
is zero, or for columns to the right of what are specified by
|
||||
sqlite3_qrf_spec.nAlign, the sqlite3_qrf_spec.eDfltAlign value is used
|
||||
for the alignment. Column names can be (and often are) aligned
|
||||
differently, as specified by sqlite3_qrf_spec.eTitleAlign.
|
||||
|
||||
Each alignment value specifies both vertical and horizontal alignment.
|
||||
Horizontal alignment can be left, center, right, or no preference.
|
||||
Vertical alignment can be top, middle, bottom, or no preference.
|
||||
Thus there are 16 possible alignment values, as follows:
|
||||
|
||||
> ~~~
|
||||
/*
|
||||
** Horizontal Vertial
|
||||
** ---------- -------- */
|
||||
#define QRF_ALIGN_Auto 0 /* auto auto */
|
||||
#define QRF_ALIGN_Left 1 /* left auto */
|
||||
#define QRF_ALIGN_Center 2 /* center auto */
|
||||
#define QRF_ALIGN_Right 3 /* right auto */
|
||||
#define QRF_ALIGN_Top 4 /* auto top */
|
||||
#define QRF_ALIGN_NW 5 /* left top */
|
||||
#define QRF_ALIGN_N 6 /* center top */
|
||||
#define QRF_ALIGN_NE 7 /* right top */
|
||||
#define QRF_ALIGN_Middle 8 /* auto middle */
|
||||
#define QRF_ALIGN_W 9 /* left middle */
|
||||
#define QRF_ALIGN_C 10 /* center middle */
|
||||
#define QRF_ALIGN_E 11 /* right middle */
|
||||
#define QRF_ALIGN_Bottom 12 /* auto bottom */
|
||||
#define QRF_ALIGN_SW 13 /* left bottom */
|
||||
#define QRF_ALIGN_S 14 /* center bottom */
|
||||
#define QRF_ALIGN_SE 15 /* right bottom */
|
||||
~~~
|
||||
|
||||
Notice how alignment values with an unspecified horizontal
|
||||
or vertical component can be added to another alignment value
|
||||
for which that component is specified, to get a fully
|
||||
specified alignment. For eample:
|
||||
|
||||
> QRF_ALIGN_Center + QRF_ALIGN_Bottom == QRF_ALIGN_S.
|
||||
|
||||
The alignment for column names is always determined by the
|
||||
eTitleAlign setting. If eTitleAlign is QRF_Auto, then column
|
||||
names use center-bottom alignment, QRF_ALIGN_W, value 14.
|
||||
The aAlign\[\] and eDfltAlign settings have no affect on
|
||||
column names.
|
||||
|
||||
For data in the first nAlign columns, the aAlign\[\] array
|
||||
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 vertical alignment, then values are top-aligned
|
||||
(QRF_ALIGN_Top).
|
||||
|
||||
*As of 2025-11-08, only horizontal alignment is implemented.
|
||||
The vertical alignment settings are currently ignored and
|
||||
the vertical alignment is always QRF_ALIGN_Top.*
|
||||
|
||||
### 2.14 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
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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.xRender field is not NULL, then each
|
||||
sqlite3_value coming out of the query is first passed to the
|
||||
xRender function, giving that function an opportunity to render
|
||||
the results itself, using whatever custom format is desired.
|
||||
If xRender chooses to render, it should write the rendering
|
||||
into memory obtained from sqlite3_malloc() and return a pointer
|
||||
to that memory. The xRender function can decline
|
||||
to render (for example, based on the sqlite3_value_type() or other
|
||||
characteristics of the value) in which case it can simply return a
|
||||
NULL pointer and the usual default rendering will be used instead.
|
||||
|
||||
The sqlite3_format_query_result() function (which calls xRender)
|
||||
will take responsibility for freeing the string returned by xRender
|
||||
after it has finished using it.
|
||||
|
||||
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
|
||||
the prepared statement P and format its results according to the
|
||||
specification found in S. The sqlite3_format_query_result() function
|
||||
will return an SQLite result code, usually SQLITE_OK, but perhaps
|
||||
SQLITE_NOMEM or SQLITE_ERROR or similar. If an error occurs and if
|
||||
the E parameter is not NULL, then error message text might be written
|
||||
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.
|
||||
The following output modes are currently defined:
|
||||
|
||||
> ~~~
|
||||
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
|
||||
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
|
||||
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
|
||||
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
|
||||
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
|
||||
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
|
||||
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
|
||||
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
|
||||
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
|
||||
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
|
||||
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
|
||||
#define QRF_STYLE_Line 11 /* One column per line. */
|
||||
#define QRF_STYLE_List 12 /* One record per line with a separator */
|
||||
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
|
||||
#define QRF_STYLE_Off 14 /* No query output shown */
|
||||
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
|
||||
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
|
||||
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
|
||||
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
|
||||
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
|
||||
~~~
|
||||
|
||||
In the following subsections, these styles will often be referred
|
||||
to without the "QRF_STYLE_" prefix.
|
||||
|
||||
### 4.1 Default Style (Auto)
|
||||
|
||||
The **Auto** style means QRF gets to choose an appropriate output
|
||||
style. It will usually choose **Box**, but might also pick one of
|
||||
**Explain** or **Eqp** if the `sqlite3_stmt_explain()` function
|
||||
returns 1 or 2, respectively.
|
||||
|
||||
### 4.2 Columnar Styles (Box, Column, Markdown, Table)
|
||||
|
||||
The **Box**, **Column**, **Markdown**, and **Table**
|
||||
modes are columnar. This means the output is arranged into neat,
|
||||
uniform-width columns. These styles can use more memory, especially when
|
||||
the query result has many rows, because they need to load the entire output
|
||||
into memory first in order to determine how wide to make each column.
|
||||
|
||||
The nWidth, aWidth, and mxWidth fields of the `sqlite3_qrf_spec` object
|
||||
are used by these styles only, and are ignored by all other styles.
|
||||
The zRowSep and zColumnSep settings are ignored by these styles. The
|
||||
bTitles setting is honored by these styles; it defaults to QRF_SW_On.
|
||||
|
||||
The **Box** style uses Unicode box-drawing character to draw a grid
|
||||
of columns and rows to show the result. The **Table** is the same,
|
||||
except that it uses ASCII-art rather than Unicode box-drawing characters
|
||||
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.
|
||||
|
||||
### 4.3 Line-oriented Styles
|
||||
|
||||
The line-oriented styles output each row of result as it is received from
|
||||
the prepared statement.
|
||||
|
||||
The **List** style is the most familiar line-oriented output format.
|
||||
The **List** style shows output columns for each row on the
|
||||
same line, each separated by a single "`|`" character and with lines
|
||||
terminated by a single newline (\\u000a or \\n). These column
|
||||
and row separator choices can be overridden using the zColumnSep
|
||||
and zRowSep fields of the `sqlite3_qrf_spec` structure. The text
|
||||
formatting is QRF_TEXT_Plain, and BLOB encoding is QRF_BLOB_Text. So
|
||||
characters appear in the output exactly as they appear in the database.
|
||||
Except the eEsp mode defaults to `QRF_ESC_On`, so that control
|
||||
characters are escaped, for safety.
|
||||
|
||||
The **Csv** and **Quote** styles are simply variations on **List**
|
||||
with hard-coded values for some of the sqlite3_qrf_spec settings:
|
||||
|
||||
<table border=1 cellpadding=2 cellspacing=0>
|
||||
<tr><th> <th>Quote<th>Csv
|
||||
<tr><td>zColumnSep<td>","<td>","
|
||||
<tr><td>zRowSep<td>"\\n"<td>"\\r\\n"
|
||||
<tr><td>zNull<td>"NULL"<td>""
|
||||
<tr><td>eText<td>QRF_TEXT_Sql<td>QRF_TEXT_Csv
|
||||
<tr><td>eBlob<td>QRF_BLOB_Sql<td>QRF_BLOB_Text
|
||||
</table>
|
||||
|
||||
The **Html** style generates HTML table content, just without
|
||||
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.
|
||||
|
||||
The **Json** and **JObject** styles generates JSON text for the query result.
|
||||
The **Json** style produces a JSON array of structures with one
|
||||
structure per row. **JObject** outputs independent JSON objects, one per
|
||||
row, with each structure on a separate line all by itself, and not
|
||||
part of a larger array. In both cases, the labels on the elements of the
|
||||
JSON objects are taken from the column names of the SQL query. So if
|
||||
you have an SQL query that has two or more output columns with the same
|
||||
name, you will end up with JSON structures that have duplicate elements.
|
||||
|
||||
Finally, the **Line** style paints each column of a row on a
|
||||
separate line with the column name on the left and a "`=`" separating the
|
||||
column name from its value. A single blank line appears between rows.
|
||||
|
||||
### 4.4 EXPLAIN Styles (Eqp, Explain)
|
||||
|
||||
The **Eqp** and **Explain** styles format output for
|
||||
EXPLAIN QUERY PLAN and EXPLAIN statements, respectively. If the input
|
||||
statement is not already an EXPLAIN QUERY PLAN or EXPLAIN statement is
|
||||
is temporarily converted for the duration of the rendering, but
|
||||
is converted back before `sqlite3_format_query_result()` returns.
|
||||
|
||||
### 4.5 ScanStatus Styles (Stats, StatsEst, StatsVm)
|
||||
|
||||
The **Stats**, **StatsEst**, and **StatsVm** styles are similar to **Eqp**
|
||||
and **Explain** except that they include profiling information
|
||||
from prior executions of the input prepared statement.
|
||||
These modes only work if SQLite has been compiled with
|
||||
-DSQLITE_ENABLE_STMT_SCANSTATUS and if the SQLITE_DBCONFIG_STMT_SCANSTATUS
|
||||
is enabled for the database connection. The **StatsVm** style
|
||||
also requires the bytecode() virtual table which is enabled using
|
||||
the -DSQLITE_ENABLE_BYTECODE_VTAB compile-time option.
|
||||
|
||||
### 4.6 Other Styles (Count, Off)
|
||||
|
||||
The **Count** style discards all query results and returns
|
||||
a count of the number of rows of output at the end. The **Off**
|
||||
style is completely silent; it generates no output. These corner-case
|
||||
modes are sometimes useful for debugging.
|
||||
|
||||
### 5.0 Source Code Files
|
||||
|
||||
The SQLite Query Result Formatter is implemented in three source code files:
|
||||
|
||||
* `qrf.c` → The implementation, written in portable C99
|
||||
* `qrf.h` → A header file defining interfaces
|
||||
* `README.md` → This documentation
|
||||
|
||||
To use the SQLite result formatter, include the "`qrf.h`" header file
|
||||
and link the application against the "`qrf.c`" source 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
|
||||
-3010
File diff suppressed because it is too large
Load Diff
-201
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
** 2025-10-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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Header file for the Query Result-Format or "qrf" utility library for
|
||||
** SQLite. See the README.md documentation for additional information.
|
||||
*/
|
||||
#ifndef SQLITE_QRF_H
|
||||
#define SQLITE_QRF_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Specification used by clients to define the output format they want
|
||||
*/
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
struct sqlite3_qrf_spec {
|
||||
unsigned char iVersion; /* Version number of this structure */
|
||||
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
|
||||
unsigned char eEsc; /* How to escape control characters in text */
|
||||
unsigned char eText; /* Quoting style for text */
|
||||
unsigned char eTitle; /* Quating style for the text of column names */
|
||||
unsigned char eBlob; /* Quoting style for BLOBs */
|
||||
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 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[] */
|
||||
short int *aWidth; /* Column widths */
|
||||
unsigned char *aAlign; /* Column alignments */
|
||||
char *zColumnSep; /* Alternative column separator */
|
||||
char *zRowSep; /* Alternative row separator */
|
||||
char *zTableName; /* Output table name */
|
||||
char *zNull; /* Rendering of NULL */
|
||||
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
|
||||
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
|
||||
void *pRenderArg; /* First argument to the xRender callback */
|
||||
void *pWriteArg; /* First argument to the xWrite callback */
|
||||
char **pzOutput; /* Storage location for output string */
|
||||
/* Additional fields may be added in the future */
|
||||
};
|
||||
|
||||
/*
|
||||
** Interfaces
|
||||
*/
|
||||
int sqlite3_format_query_result(
|
||||
sqlite3_stmt *pStmt, /* SQL statement to run */
|
||||
const sqlite3_qrf_spec *pSpec, /* Result format specification */
|
||||
char **pzErr /* OUT: Write error message here */
|
||||
);
|
||||
|
||||
/*
|
||||
** Range of values for sqlite3_qrf_spec.aWidth[] entries and for
|
||||
** sqlite3_qrf_spec.mxColWidth and .nScreenWidth
|
||||
*/
|
||||
#define QRF_MAX_WIDTH 10000
|
||||
#define QRF_MIN_WIDTH 0
|
||||
|
||||
/*
|
||||
** Output styles:
|
||||
*/
|
||||
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
|
||||
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
|
||||
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
|
||||
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
|
||||
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
|
||||
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
|
||||
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
|
||||
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
|
||||
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
|
||||
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
|
||||
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
|
||||
#define QRF_STYLE_Line 11 /* One column per line. */
|
||||
#define QRF_STYLE_List 12 /* One record per line with a separator */
|
||||
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
|
||||
#define QRF_STYLE_Off 14 /* No query output shown */
|
||||
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
|
||||
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
|
||||
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
|
||||
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
|
||||
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
|
||||
|
||||
/*
|
||||
** Quoting styles for text.
|
||||
** Allowed values for sqlite3_qrf_spec.eText
|
||||
*/
|
||||
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
|
||||
#define QRF_TEXT_Plain 1 /* Literal text */
|
||||
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
|
||||
#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
|
||||
** Allowed values for sqlite3_qrf_spec.eBlob
|
||||
*/
|
||||
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
|
||||
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
|
||||
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
|
||||
#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.
|
||||
** Allowed values for sqlite3_qrf_spec.eEsc
|
||||
*/
|
||||
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
|
||||
#define QRF_ESC_Off 1 /* Do not escape control characters */
|
||||
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
|
||||
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
|
||||
|
||||
/*
|
||||
** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap",
|
||||
** and "bTextJsonb". There is an extra "auto" variants so these are actually
|
||||
** tri-state settings, not booleans.
|
||||
*/
|
||||
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
|
||||
#define QRF_SW_Off 1 /* This setting is forced off */
|
||||
#define QRF_SW_On 2 /* This setting is forced on */
|
||||
#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */
|
||||
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
|
||||
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
|
||||
|
||||
/*
|
||||
** Possible alignment values alignment settings
|
||||
**
|
||||
** Horizontal Vertial
|
||||
** ---------- -------- */
|
||||
#define QRF_ALIGN_Auto 0 /* auto auto */
|
||||
#define QRF_ALIGN_Left 1 /* left auto */
|
||||
#define QRF_ALIGN_Center 2 /* center auto */
|
||||
#define QRF_ALIGN_Right 3 /* right auto */
|
||||
#define QRF_ALIGN_Top 4 /* auto top */
|
||||
#define QRF_ALIGN_NW 5 /* left top */
|
||||
#define QRF_ALIGN_N 6 /* center top */
|
||||
#define QRF_ALIGN_NE 7 /* right top */
|
||||
#define QRF_ALIGN_Middle 8 /* auto middle */
|
||||
#define QRF_ALIGN_W 9 /* left middle */
|
||||
#define QRF_ALIGN_C 10 /* center middle */
|
||||
#define QRF_ALIGN_E 11 /* right middle */
|
||||
#define QRF_ALIGN_Bottom 12 /* auto bottom */
|
||||
#define QRF_ALIGN_SW 13 /* left bottom */
|
||||
#define QRF_ALIGN_S 14 /* center bottom */
|
||||
#define QRF_ALIGN_SE 15 /* right bottom */
|
||||
#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */
|
||||
#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */
|
||||
|
||||
/*
|
||||
** Auxiliary routines contined within this module that might be useful
|
||||
** in other contexts, and which are therefore exported.
|
||||
*/
|
||||
/*
|
||||
** Return an estimate of the width, in columns, for the single Unicode
|
||||
** 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
|
||||
** 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
|
||||
}
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_QRF_H) */
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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}}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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++){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user