Compare commits

..

8 Commits

Author SHA1 Message Date
drh a1de06a4c6 Version 3.51.1
FossilOrigin-Name: 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af38a88
2025-11-28 17:28:25 +00:00
drh 158436f53d A better fix for the issue with RETURNING triggers on eponymous
virtual tables and SQLITE_SCHEMA errors.

FossilOrigin-Name: 40ddaca3fb752425c26570365a9f31820786d21d043c1c0a4b49746ff9bc0782
2025-11-28 13:04:31 +00:00
drh d261437615 Fix a buffer overread in fts5 that might occur while processing a corrupt db.
FossilOrigin-Name: 712e31c59254f82e62a19e41d3b6ac15391dd6e9df966cacadfa9ae79415b243
2025-11-28 12:36:04 +00:00
drh 8c7f142470 Fix a problem in the xFilter() method of the fts5_vocab virtual table causing it to omit some result rows in some cases where a virtual cursor is used more than once by SQLite.
FossilOrigin-Name: 0c836387518dee719eaac8992c63da955630ac073a5e4ba4d887160dff42b898
2025-11-28 12:00:14 +00:00
drh 525d297450 CLI shell: ensure that .dump propagates its error code on error.
FossilOrigin-Name: 61222410c503916b832c1b76b028f1c73f4839a2067902f5db1d6a4a4459cb2b
2025-11-13 12:22:14 +00:00
drh 2605119d14 Fix a problem in the EXISTS-to-JOIN optimization so
that it works with nested WHERE and EXISTS statements.

FossilOrigin-Name: 728fac52017de1309c84c96e33ed99d6472d6b4831587312944b8d85a68d3343
2025-11-13 11:42:16 +00:00
drh de223ffbbd Merge various minor code and documentation fixes from trunk into the
patch branch for 3.51.

FossilOrigin-Name: 9a00fe5eb69050ddc569d2a857309c2779a48414135f0967531999b7afd84283
2025-11-13 11:34:38 +00:00
stephan ea0cda00d6 Kick off branch-3.51: bump version to 3.51.1 and cherrypick [0f712b6b0516dc151d].
FossilOrigin-Name: 1b908c439a4d2614cb6a4cec36d8d876eec68b1e2e80d6aad7e21d61d159df7d
2025-11-09 06:38:48 +00:00
340 changed files with 17138 additions and 51439 deletions
+3 -11
View File
@@ -131,15 +131,9 @@ libsqlite3.DLL.install-rules = @SQLITE_DLL_INSTALL_RULES@
CFLAGS.fuzzcheck-asan.fsanitize = @CFLAGS_ASAN_FSANITIZE@
#
# TARGET_DEBUG is intended to either be empty or be set to -g
# -DSQLITE_DEBUG=1, plus any other flags relevant for debug-only
# builds. These get added near the front of $(T.cc) so that they can
# be overridden by CFLAGS passed directly to make. Historically (prior
# to 2026-03-30) these were at/near the end of the flags but that made
# it impossible to pass custom -O* flags to specific binaries without
# reconfiguring.
# Intended to either be empty or be set to -g -DSQLITE_DEBUG=1.
#
T.cc += @TARGET_DEBUG@
T.cc.TARGET_DEBUG = @TARGET_DEBUG@
#
# $(JIMSH) and $(CFLAGS.jimsh) are documented in main.mk. $(JIMSH)
@@ -170,9 +164,7 @@ OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ $(OPTIONS)
PACKAGE_VERSION = @PACKAGE_VERSION@
#
# Filename extensions for binaries and libraries. B.* are for the
# being-built-on platform and T.* are for the build's target platform.
# i.e. B.* is for build-time tooling and T.* is for deliverables.
# Filename extensions for binaries and libraries
#
B.exe = @BUILD_EXEEXT@
T.exe = @TARGET_EXEEXT@
+160 -247
View File
@@ -11,8 +11,6 @@
TOP = .
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Set this non-0 to create and use the SQLite amalgamation file.
#
!IFNDEF USE_AMALGAMATION
@@ -103,6 +101,13 @@ NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
# Set this non-0 to use the library paths and other options necessary for
# Windows Phone 8.1.
#
!IFNDEF USE_WP81_OPTS
USE_WP81_OPTS = 0
!ENDIF
# Set this non-0 to split the SQLite amalgamation file into chunks to
# be used for debugging with Visual Studio.
#
@@ -111,8 +116,14 @@ SPLIT_AMALGAMATION = 0
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
# Set this non-0 to have this makefile assume the Tcl shell executable
# (tclsh*.exe) is available in the PATH. By default, this is disabled
# for compatibility with older build environments. This setting only
# applies if TCLSH_CMD is not set manually.
#
!IFNDEF USE_TCLSH_IN_PATH
USE_TCLSH_IN_PATH = 0
!ENDIF
# Set this non-0 to use zlib, possibly compiling it from source code.
#
@@ -175,6 +186,14 @@ USE_NATIVE_LIBPATHS = 0
USE_RC = 1
!ENDIF
# Set this non-0 to compile binaries suitable for the WinRT environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
#
!IFNDEF FOR_WINRT
FOR_WINRT = 0
!ENDIF
# Set this non-0 to compile binaries suitable for the UWP environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
@@ -190,8 +209,6 @@ FOR_WIN10 = 0
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Set this non-0 to skip attempting to look for and/or link with the Tcl
# runtime library.
#
@@ -248,8 +265,6 @@ DEBUG = 0
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# By default, use --linemacros=1 argument to the mksqlite3c.tcl tool, which
# is used to build the amalgamation. This can be turned off to ease debug
# of the amalgamation away from the source tree.
@@ -345,8 +360,6 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# These are the names of the customized Tcl header files used by various parts
# of this makefile when the stdcall calling convention is in use. It is not
# used for any other purpose.
@@ -628,24 +641,16 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
!IF "$(PLATFORM)"=="x86"
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
# <</mark>>
!ELSE
!IFNDEF PLATFORM
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
TEST_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall -DINCLUDE_SQLITE_TCL_H=1 -DSQLITE_TCLAPI=__cdecl
# <</mark>>
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
@@ -772,6 +777,18 @@ SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
TCC = $(TCC) -FAcs
!ENDIF
# When compiling the library for use in the WinRT environment,
# the following compile-time options must be used as well to
# disable use of Win32 APIs that are not available and to enable
# use of Win32 APIs that are specific to Windows 8 and/or WinRT.
#
!IF $(FOR_WINRT)!=0
TCC = $(TCC) -DSQLITE_OS_WINRT=1
RCC = $(RCC) -DSQLITE_OS_WINRT=1
TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
!ENDIF
# C compiler options for the Windows 10 platform (needs MSVC 2015).
#
!IF $(FOR_WIN10)!=0
@@ -784,31 +801,25 @@ BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
# USE_CRT_DLL option is set to force dynamically linking to the
# MSVC runtime library.
#
!IF $(USE_CRT_DLL)!=0
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
!IF $(DEBUG)>1
TCC = $(TCC) -MDd
BCC = $(BCC) -MDd
ZLIBCFLAGS = -nologo -MDd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MD
BCC = $(BCC) -MD
ZLIBCFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi
!ENDIF
!ELSE
!IF $(DEBUG)>1
TCC = $(TCC) -MTd
BCC = $(BCC) -MTd
ZLIBCFLAGS = -nologo -MTd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MT
BCC = $(BCC) -MT
ZLIBCFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi
!ENDIF
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# The mksqlite3c.tcl and mksqlite3h.tcl scripts will pull in
# any extension header files by default. For non-amalgamation
# builds, we need to make sure the compiler can find these.
@@ -859,6 +870,16 @@ MKSQLITE3H_ARGS =
!ENDIF
# <</mark>>
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
# Omitting the define will cause extra debugging code to be inserted and
# includes extra comments when "EXPLAIN stmt" is used.
#
!IF $(DEBUG)==0
TCC = $(TCC) -DNDEBUG
BCC = $(BCC) -DNDEBUG
RCC = $(RCC) -DNDEBUG
!ENDIF
!IF $(DEBUG)>0 || $(API_ARMOR)!=0 || $(FOR_WIN10)!=0
TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1
RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
@@ -924,8 +945,6 @@ TCC = $(TCC) /fsanitize=address
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# The locations of the Tcl header and library files. Also, the library that
# non-stubs enabled programs using Tcl must link against. These variables
# (TCLINCDIR, TCLLIBDIR, and LIBTCL) may be overridden via the environment
@@ -1165,8 +1184,6 @@ BCC = $(BCC) -Zi
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# If zlib support is enabled, add the compiler options for it.
#
!IF $(USE_ZLIB)!=0
@@ -1219,6 +1236,56 @@ LTLINKOPTS = /NOLOGO
LTLIBOPTS = /NOLOGO
!ENDIF
# When compiling for use in the WinRT environment, the following
# linker option must be used to mark the executable as runnable
# only in the context of an application container.
#
!IF $(FOR_WINRT)!=0
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
!IF "$(VISUALSTUDIOVERSION)"=="12.0" || "$(VISUALSTUDIOVERSION)"=="14.0"
!IFNDEF STORELIBPATH
!IF "$(PLATFORM)"=="x86"
STORELIBPATH = $(CRTLIBPATH)\store
!ELSEIF "$(PLATFORM)"=="x64"
STORELIBPATH = $(CRTLIBPATH)\store\amd64
!ELSEIF "$(PLATFORM)"=="ARM"
STORELIBPATH = $(CRTLIBPATH)\store\arm
!ELSE
STORELIBPATH = $(CRTLIBPATH)\store
!ENDIF
!ENDIF
STORELIBPATH = $(STORELIBPATH:\\=\)
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
!ENDIF
!ENDIF
# When compiling for Windows Phone 8.1, an extra library path is
# required.
#
!IF $(USE_WP81_OPTS)!=0
!IFNDEF WP81LIBPATH
!IF "$(PLATFORM)"=="x86"
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
!ELSEIF "$(PLATFORM)"=="ARM"
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
!ELSE
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
!ENDIF
!ENDIF
!ENDIF
# When compiling for Windows Phone 8.1, some extra linker options
# are also required.
#
!IF $(USE_WP81_OPTS)!=0
!IFDEF WP81LIBPATH
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
!ENDIF
LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
!ENDIF
# When compiling for UWP or the Windows 10 platform, some extra linker
# options are also required.
#
@@ -1242,14 +1309,12 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
# If either debugging or symbols are enabled, enable PDBs.
#
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
LDFLAGS = /NODEFAULTLIB:msvcrt /DEBUG $(LDOPTS)
LDFLAGS = /DEBUG $(LDOPTS)
!ELSE
LDFLAGS = /NODEFAULTLIB:msvcrt $(LDOPTS)
LDFLAGS = $(LDOPTS)
!ENDIF
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Start with the Tcl related linker options.
#
!IF $(NO_TCL)==0
@@ -1276,8 +1341,6 @@ LTLIBS = $(LTLIBS) $(LIBICU)
###############################################################################
# <<mark>>
# ^^^^^^^^-- content between mark and /mark omitted from autoconf/Makefile.msc
#
# Object files for the SQLite library (non-amalgamation).
#
LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
@@ -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>>
+48 -49
View File
@@ -1,9 +1,8 @@
<h1 align="center">SQLite Source Repository</h1>
This repository contains the complete source code for the
[SQLite database engine](https://sqlite.org/) going back
to 2000-05-29. The tree includes many tests and some
documentation, though additional tests and most documentation
[SQLite database engine](https://sqlite.org/), including
many tests. Additional tests and most documentation
are managed separately.
See the [on-line documentation](https://sqlite.org/) for more information
@@ -97,21 +96,19 @@ the build products. It is recommended, but not required, that the
build directory be separate from the source directory. Cd into the
build directory and then from the build directory run the configure
script found at the root of the source tree. Then run "make".
See the [compile-for-unix.md](doc/compile-for-unix.md) document for
more detail.
For example:
apt install gcc make tcl-dev ;# Install the necessary build tools
apt install gcc make tcl-dev ;# Make sure you have all the necessary build tools
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
mkdir bld ;# Build happens in a sibling directory
mkdir bld ;# Build will occur in a sibling directory
cd bld ;# Change to the build directory
../sqlite/configure ;# Run the configure script
make sqlite3 ;# The "sqlite3" command-line tool
make sqlite3.c ;# The "amalgamation" source file
make sqldiff ;# The "sqldiff" command-line tool
#### Targets below require tcl-dev ####
make tclextension-install ;# Install the SQLite TCL extension
make sqlite3 ;# Builds the "sqlite3" command-line tool
make sqlite3.c ;# Build the "amalgamation" source file
make sqldiff ;# Builds the "sqldiff" command-line tool
# Makefile targets below this point require tcl-dev
make tclextension-install ;# Build and install the SQLite TCL extension
make devtest ;# Run development tests
make releasetest ;# Run full release tests
make sqlite3_analyzer ;# Builds the "sqlite3_analyzer" tool
@@ -119,15 +116,14 @@ For example:
See the makefile for additional targets. For debugging builds, the
core developers typically run "configure" with options like this:
../sqlite/configure --all --debug CFLAGS='-O0 -g'
../sqlite/configure --enable-all --enable-debug CFLAGS='-O0 -g'
For release builds, the core developers usually do:
../sqlite/configure --all
../sqlite/configure --enable-all
Core deliverables (sqlite3.c, sqlite3) can be built without a TCL, but
many makefile targets require a "tclsh" TCL interpreter version 8.6
or later. The "tclextension-install" target and the test targets that follow
Almost all makefile targets require a "tclsh" TCL interpreter version 8.6 or
later. The "tclextension-install" target and the test targets that follow
all require TCL development libraries too. ("apt install tcl-dev"). It is
helpful, but is not required, to install the SQLite TCL extension (the
"tclextension-install" target) prior to running tests. The "releasetest"
@@ -137,20 +133,20 @@ On "make" command-lines, one can add "OPTIONS=..." to specify additional
compile-time options over and above those set by ./configure. For example,
to compile with the SQLITE_OMIT_DEPRECATED compile-time option, one could say:
./configure --all
./configure --enable-all
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
The configure script uses [autosetup](https://msteveb.github.io/autosetup/).
If the configure script does not work out for you, there is a generic
makefile named "Makefile.linux-gcc" in the top directory of the source tree
that you can copy and edit to suit your needs. Comments on the generic
makefile show what changes are needed.
The configure script uses autoconf 2.61 and libtool. If the configure
script does not work out for you, there is a generic makefile named
"Makefile.linux-gcc" in the top directory of the source tree that you
can copy and edit to suit your needs. Comments on the generic makefile
show what changes are needed.
## Compiling for Windows Using MSVC
On Windows, everything can be compiled with MSVC.
You will also need a working installation of TCL if you want to run tests,
though TCL is not required if you just want to build SQLite itself.
You will also need a working installation of TCL if you want to run tests.
TCL is not required if you just want to build SQLite itself.
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
additional information about how to install MSVC and TCL and configure your
build environment.
@@ -160,25 +156,24 @@ TCL library, using a command like this:
set TCLDIR=c:\Tcl
SQLite itself does not contain any TCL code, but it does use TCL to run
tests. You may need to install TCL development libraries in order to
successfully complete some makefile targets. It is helpful, but is not
required, to install the SQLite TCL extension (the "tclextension-install"
target) prior to running tests.
SQLite uses "tclsh.exe" as part of the build process, and so that
program will need to be somewhere on your %PATH%. SQLite itself
does not contain any TCL code, but it does use TCL to run tests.
You may need to install TCL development
libraries in order to successfully complete some makefile targets.
It is helpful, but is not required, to install the SQLite TCL extension
(the "tclextension-install" target) prior to running tests.
The source tree contains a "make.bat" file that allows the same "make"
commands of Unix to work on Windows. In the following, you can substitute
"nmake /f Makefile.msc" in place of "make", if you prefer to avoid this BAT
file:
Build using Makefile.msc. Example:
make sqlite3.exe
make sqlite3.c
make sqldiff.exe
#### Targets below require TCL development libraries ####
make tclextension-install
make devtest
make releasetest
make sqlite3_analyzer.exe
nmake /f Makefile.msc sqlite3.exe
nmake /f Makefile.msc sqlite3.c
nmake /f Makefile.msc sqldiff.exe
# Makefile targets below this point require TCL development libraries
nmake /f Makefile.msc tclextension-install
nmake /f Makefile.msc devtest
nmake /f Makefile.msc releasetest
nmake /f Makefile.msc sqlite3_analyzer.exe
There are many other makefile targets. See comments in Makefile.msc for
details.
@@ -186,7 +181,7 @@ details.
As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
command-line to enable new compile-time options. For example:
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
## Source Tree Map
@@ -199,7 +194,8 @@ command-line to enable new compile-time options. For example:
* **test/** - This directory and its subdirectories contains code used
for testing. Files that end in "`.test`" are TCL scripts that run
tests using an augmented TCL interpreter named "testfixture". Use
a command like "`make testfixture`" to build that
a command like "`make testfixture`" (unix) or
"`nmake /f Makefile.msc testfixture.exe`" (windows) to build that
augmented TCL interpreter, then run individual tests using commands like
"`testfixture test/main.test`". This test/ subdirectory also contains
additional C code modules and scripts for other kinds of testing.
@@ -381,11 +377,10 @@ implementation. It will not be the easiest library in the world to hack.
(and some other test programs too) is built and run when you type
"make test".
* **VERSION**, **manifest**, **manifest.tags**, and **manifest.uuid** -
These files define the current SQLite version number. The "VERSION" file
is human generated, but the "manifest", "manifest.tags", and
"manifest.uuid" files are automatically generated by the
[Fossil version control system](https://fossil-scm.org/).
* **VERSION**, **manifest**, and **manifest.uuid** - These files define
the current SQLite version number. The "VERSION" file is human generated,
but the "manifest" and "manifest.uuid" files are automatically generated
by the [Fossil version control system](https://fossil-scm.org/).
There are many other source files. Each has a succinct header comment that
describes its purpose and role within the larger system.
@@ -411,6 +406,10 @@ makefile:
> make verify-source
Or on windows:
> nmake /f Makefile.msc verify-source
Using the makefile to verify source integrity is good for detecting
accidental changes to the source tree, but malicious changes could be
hidden by also modifying the makefiles.
+1 -1
View File
@@ -1 +1 @@
3.54.0
3.51.1
+14 -1
View File
@@ -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
View File
@@ -101,6 +101,13 @@ NO_WARN = $(NO_WARN) -wd4210 -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706
!ENDIF
!ENDIF
# Set this non-0 to use the library paths and other options necessary for
# Windows Phone 8.1.
#
!IFNDEF USE_WP81_OPTS
USE_WP81_OPTS = 0
!ENDIF
# Set this non-0 to split the SQLite amalgamation file into chunks to
# be used for debugging with Visual Studio.
#
@@ -149,6 +156,14 @@ USE_NATIVE_LIBPATHS = 0
USE_RC = 1
!ENDIF
# Set this non-0 to compile binaries suitable for the WinRT environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
#
!IFNDEF FOR_WINRT
FOR_WINRT = 0
!ENDIF
# Set this non-0 to compile binaries suitable for the UWP environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
@@ -548,14 +563,10 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
!IF "$(PLATFORM)"=="x86"
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
!ELSE
!IFNDEF PLATFORM
CORE_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
SHELL_CCONV_OPTS = -Gz -guard:cf -DSQLITE_CDECL=__cdecl -DSQLITE_APICALL=__stdcall -DSQLITE_CALLBACK=__stdcall -DSQLITE_SYSAPI=__stdcall
!ELSE
CORE_CCONV_OPTS =
SHELL_CCONV_OPTS =
@@ -656,6 +667,18 @@ SHELL_LINK_OPTS = $(SHELL_CORE_LIB)
TCC = $(TCC) -FAcs
!ENDIF
# When compiling the library for use in the WinRT environment,
# the following compile-time options must be used as well to
# disable use of Win32 APIs that are not available and to enable
# use of Win32 APIs that are specific to Windows 8 and/or WinRT.
#
!IF $(FOR_WINRT)!=0
TCC = $(TCC) -DSQLITE_OS_WINRT=1
RCC = $(RCC) -DSQLITE_OS_WINRT=1
TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
!ENDIF
# C compiler options for the Windows 10 platform (needs MSVC 2015).
#
!IF $(FOR_WIN10)!=0
@@ -668,29 +691,35 @@ BCC = $(BCC) /d2guard4 -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
# USE_CRT_DLL option is set to force dynamically linking to the
# MSVC runtime library.
#
!IF $(USE_CRT_DLL)!=0
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
!IF $(DEBUG)>1
TCC = $(TCC) -MDd
BCC = $(BCC) -MDd
ZLIBCFLAGS = -nologo -MDd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MD
BCC = $(BCC) -MD
ZLIBCFLAGS = -nologo -MD -W3 -O2 -Oy- -Zi
!ENDIF
!ELSE
!IF $(DEBUG)>1
TCC = $(TCC) -MTd
BCC = $(BCC) -MTd
ZLIBCFLAGS = -nologo -MTd -W3 -O2 -Oy- -Zi
!ELSE
TCC = $(TCC) -MT
BCC = $(BCC) -MT
ZLIBCFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi
!ENDIF
!ENDIF
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
# Omitting the define will cause extra debugging code to be inserted and
# includes extra comments when "EXPLAIN stmt" is used.
#
!IF $(DEBUG)==0
TCC = $(TCC) -DNDEBUG
BCC = $(BCC) -DNDEBUG
RCC = $(RCC) -DNDEBUG
!ENDIF
!IF $(DEBUG)>0 || $(API_ARMOR)!=0 || $(FOR_WIN10)!=0
TCC = $(TCC) -DSQLITE_ENABLE_API_ARMOR=1
RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
@@ -880,6 +909,56 @@ LTLINKOPTS = /NOLOGO
LTLIBOPTS = /NOLOGO
!ENDIF
# When compiling for use in the WinRT environment, the following
# linker option must be used to mark the executable as runnable
# only in the context of an application container.
#
!IF $(FOR_WINRT)!=0
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
!IF "$(VISUALSTUDIOVERSION)"=="12.0" || "$(VISUALSTUDIOVERSION)"=="14.0"
!IFNDEF STORELIBPATH
!IF "$(PLATFORM)"=="x86"
STORELIBPATH = $(CRTLIBPATH)\store
!ELSEIF "$(PLATFORM)"=="x64"
STORELIBPATH = $(CRTLIBPATH)\store\amd64
!ELSEIF "$(PLATFORM)"=="ARM"
STORELIBPATH = $(CRTLIBPATH)\store\arm
!ELSE
STORELIBPATH = $(CRTLIBPATH)\store
!ENDIF
!ENDIF
STORELIBPATH = $(STORELIBPATH:\\=\)
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
!ENDIF
!ENDIF
# When compiling for Windows Phone 8.1, an extra library path is
# required.
#
!IF $(USE_WP81_OPTS)!=0
!IFNDEF WP81LIBPATH
!IF "$(PLATFORM)"=="x86"
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
!ELSEIF "$(PLATFORM)"=="ARM"
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
!ELSE
WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
!ENDIF
!ENDIF
!ENDIF
# When compiling for Windows Phone 8.1, some extra linker options
# are also required.
#
!IF $(USE_WP81_OPTS)!=0
!IFDEF WP81LIBPATH
LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
!ENDIF
LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
!ENDIF
# When compiling for UWP or the Windows 10 platform, some extra linker
# options are also required.
#
@@ -903,9 +982,9 @@ LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:libucrt.lib /DEFAULTLIB:ucrt.lib
# If either debugging or symbols are enabled, enable PDBs.
#
!IF $(DEBUG)>1 || $(SYMBOLS)!=0
LDFLAGS = /NODEFAULTLIB:msvcrt /DEBUG $(LDOPTS)
LDFLAGS = /DEBUG $(LDOPTS)
!ELSE
LDFLAGS = /NODEFAULTLIB:msvcrt $(LDOPTS)
LDFLAGS = $(LDOPTS)
!ENDIF
@@ -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
View File
@@ -375,11 +375,6 @@ configure process, and check it in.
Patching Autosetup for Project-local Changes
------------------------------------------------------------------------
The autosetup files require the following patches after updating
from their upstream sources:
### `--debug` flag
Autosetup reserves the flag name **`--debug`** for its own purposes,
and its own special handling of `--enable-...` flags makes `--debug`
an alias for `--enable-debug`. As this project has a long history of
@@ -393,11 +388,6 @@ If autosetup is upgraded and this patch is _not_ applied the invoking
`./configure` will fail loudly because of the declaration of the
`debug` flag in `auto.def` - duplicated flags are not permitted.
### Fail on `malloc()` error
See [check-in 72c8a5b94cdf5d](/info/72c8a5b94cdf5d).
<a name="branch-customization"></a>
Branch-specific Customization
========================================================================
@@ -461,4 +451,4 @@ all other significant processing.
[sqlite-config.tcl]: /file/autosetup/sqlite-config.tcl
[Makefile.in]: /file/Makefile.in
[main.mk]: /file/main.mk
[JimTCL]: https://msteveb.github.io/jimtcl/
[JimTCL]: https://jim.tcl.tk
+4 -6
View File
@@ -7409,13 +7409,11 @@ void *JimDefaultAllocator(void *ptr, size_t size)
free(ptr);
return NULL;
}
else if (ptr) {
return realloc(ptr, size);
}
else {
void *p = realloc(ptr, size);
if( p==0 ){
fprintf(stderr,"Out of memory\n");
exit(1);
}
return p;
return malloc(size);
}
}
+2 -2
View File
@@ -1842,7 +1842,7 @@ proc proj-setup-autoreconfig {defName} {
}
#
# @prop-define-append defineName args...
# @prop-append-to defineName args...
#
# A proxy for Autosetup's [define-append]. Appends all non-empty $args
# to [define-append $defineName].
@@ -1873,7 +1873,7 @@ proc proj-define-append {defineName args} {
# but it is technically correct and still relevant on some
# environments.
#
# See: proj-define-append
# See: proj-append-to
#
proc proj-define-amend {args} {
set defName ""
+6 -48
View File
@@ -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
View File
@@ -22,10 +22,9 @@ guidance on building for Windows.
or <https://sqlite.org/tmp/tcl9.0.0.tar.gz>.
<li>Untar the source archive. CD into the "unix/" subfolder
of the source tree.
<li>Run: &ensp; `mkdir $HOME/local`
<li>Run: &ensp; `./configure --prefix=$HOME/local`<br>
SQLite deliverable builds add: &emsp; `--static CFLAGS=-Os`
<li>Run: &ensp; `make install`
<li>Run: `mkdir $HOME/local`
<li>Run: `./configure --prefix=$HOME/local`
<li>Run: `make install`
</ol>
<p>
As of 2024-10-25, TCL is not longer required for many
@@ -37,35 +36,12 @@ guidance on building for Windows.
4. Download the SQLite source tree and unpack it. CD into the
toplevel directory of the source tree.
5. *(Optional):* Download Antirez's "linenoise" command-line editing
library to provide command-line editing in the CLI. You can find
a suitable copy of the linenoise sources at
<https://sqlite.org/linenoise.tar.gz> or at various other locations
on the internet. If you put the linenoise source tree in
a directory named $HOME/linenoise or a directory "linenoise" which
is a sibling of the SQLite source tree, then the SQLite ./configure
script will automatically find and use that source code to provide
command-line editing in the CLI. If you would rather use the readline
or editline libraries or a precompiled linenoise library, there are
./configure options to accommodate that choice. The SQLite developers
typically use $HOME/linenoise since linenoise is small, has no
external dependencies, "just works", and the ./configure script
will pick it up and use it automatically. But you do what works
best for you.
<p>
You are not required to have any command-line editing support in
order to use SQLite. But command-line editing does make the
interactive experience more enjoyable.
6. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
5. Run: `./configure --enable-all --with-tclsh=$HOME/local/bin/tclsh9.0`
You do not need to use --with-tclsh if the tclsh you want to use is the
first one on your PATH or if you are building without TCL.
Lots of other options to ./configure are available.
Run `./configure --help` for further guidance.
7. Run the "`Makefile`" makefile with an appropriate target.
6. Run the "`Makefile`" makefile with an appropriate target.
Examples:
<ul>
<li> `make sqlite3.c`
@@ -90,7 +66,5 @@ guidance on building for Windows.
of SQLite.
8. For a debugging build of the CLI, use `./configure --dev`. A debugging
build contains lots of extra debugging code, so it is slow and a lot
bigger. You probably do not want to deploy a debugging build. But if
you are working on the code, a debugging build works much better.
7. For a debugging build of the CLI, where the ".treetrace" and ".wheretrace"
commands work, add the the --with-debug argument to configure.
-36
View File
@@ -180,39 +180,3 @@ statically linked so that it does not depend on separate DLL:
6. After your executable is built, you can verify that it does not
depend on the TCL DLL by running:
<blockquote><pre>dumpbin /dependents sqlite3_analyzer.exe</pre></blockquote>
## Linking Against ZLIB
Some feature (such as zip-file support in the CLI) require the ZLIB
compression library. That library is more or less universally available
on unix platforms, but is seldom provided on Windows. You will probably
need to provide it yourself. Here the the steps needed:
1. Download the zlib-1.3.1.tar.gz tarball (or a similar version).
Unpack the tarball sources. You can put them wherever you like.
For the purposes of this document, let's assume you put the source
tree in c:\\zlib-64. Note: If you are building for both x64 and
x86, you will need separate builds of ZLIB for each, thus separate
build directories.
2. Before building SQLite (as described above) first make these
environment changes. The lead-programmer for SQLite (who writes
these words) has BAT files named "env-x64.bat" and "env-x32.bat"
and "env-arm64.bat" that make these changes, and he runs those
BAT file whenever he starts a new shell. These are the settings
needed:
<blockquote>
set USE_ZLIB=1<br>
set BUILD_ZLIB=0<br>
set ZLIBDIR=c:\\zlib-64
</blockquote>
3. Because the settings in step 2 specify "BUILD_ZLIB=0", you will need
to build the library at least once. I recommand:
<blockquote>
make clean sqlite3.exe BUILD_ZLIB=1
</blockquote>
4. After making the environment changes specified in steps 1 through 3
above, you then build and test SQLite as you normally would. The
environment variable changes will cause ZLIB to be linked automatically.
+8 -22
View File
@@ -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
View File
@@ -4,12 +4,6 @@
<ul type=none>
<li> 1. <a href=#overview>Overview</a>
<ul type=none>
<li> 1.1. <a href=#runtr>Running testrunner.tcl</a>
<li> 1.2. <a href=#runviamake>Run using "make"</a>
<li> 1.3. <a href=#outputs>Outputs from testrunner.tcl</a>
<li> 1.4. <a href=#help>Built-in help</a>
</ul>
<li> 2. <a href=#binary_tests>Binary Tests</a>
<ul type=none>
<li> 2.1. <a href=#organization_tests>Organization of Tcl Tests</a>
@@ -32,40 +26,17 @@
The testrunner.tcl program is a Tcl script used to run multiple SQLite
tests in parallel, thus reducing testing time on multi-core machines.
The testrunner.tcl supports running tests that based on `testfixture`,
`sqlite3`, and `fuzzcheck`.
It supports the following types of tests:
<a name="runtr"></a>
## 1.1 Running testrunner.tcl
* Tcl test scripts.
The testrunner.tcl script is located in the "test" subdirectory of the
SQLite source tree. So if your shell is current positioned at the top
of the source tree, you would normally run the script using the command:
"<tt>test/testrunner.tcl</tt>". On Windows, you have to specify the
`tclsh` interpreter command first, like this:
"<tt>tclsh&nbsp;test/testrunner.tcl</tt>".
* Fuzzcheck tests, including using an external fuzzcheck database.
In this document, we will assume that you are on a unix-like OS
(not on Windows) and that your current directory is the root
of the SQLite source tree, and so all invocations of the testrunner.tcl
script will be of the form "<tt>test/testrunner.tcl</tt>". If you
are in a different directory, then make appropriate adjustments to
the path. On Windows, add the "<tt>tclsh</tt>" interpreter command
up front.
<a name="runviamake"></a>
## 1.2 Run using make
The standard Makefiles for SQLite include targets that invoke
testrunner.tcl. So the following commands also run testrunner.tcl:
* `make devtest`
* `make releasetest`
* `make sdevtest`
* `make testrunner`
<a name="outputs"></a>
## 1.3 Outputs from testrunner.tcl
* Tests run with `make` commands. Examples:
- `make devtest`
- `make releasetest`
- `make sdevtest`
- `make testrunner`
The testrunner.tcl program stores output of all tests and builds run in
log file **testrunner.log**, created in the current working directory.
@@ -83,19 +54,17 @@ A useful query might be:
```
You can get a summary of errors in a prior run by invoking commands like
those shown below. Note that the testrunner.tcl script can be run directly
on unix systems (including Macs) but you will need to add <tt>tclsh</tt>
to the front on Windows.
these:
```
test/testrunner.tcl errors
test/testrunner.tcl errors -v
tclsh $(TESTDIR)/testrunner.tcl errors
tclsh $(TESTDIR)/testrunner.tcl errors -v
```
Running the command:
```
test/testrunner.tcl status
tclsh $(TESTDIR)/testrunner.tcl status
```
in the directory containing the testrunner.db database runs various queries
@@ -104,43 +73,32 @@ A good way to keep and eye on test progress is to run either of the two
following commands:
```
watch test/testrunner.tcl status
test/testrunner.tcl status -d 2
watch tclsh $(TESTDIR)/testrunner.tcl status
tclsh $(TESTDIR)/testrunner.tcl status -d 2
```
Both of the commands above accomplish about the same thing, but the second
one has the advantage of not requiring "watch" to be installed on your
system.
Sometimes testrunner.tcl uses the `testfixture` and `sqlite3` binaries that
are in the directory from which testrunner.tcl is run.
(see "Binary Tests" below). Sometimes it builds testfixture and
Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
other binaries in specific configurations to test (see "Source Tests").
<a name=help></a>
## 1.4 Built-in help
Run this command:
```
test/testrunner.tcl help
```
To get a summary of all of the various command-line options available
with testrunner.tcl
<a name=binary_tests></a>
# 2. Binary Tests
The commands described in this section all run various combinations of the Tcl
test scripts using whatever `testfixture` binary (and maybe also the `sqlite3`
binary, depending on the test) that is found in the directory from which
testrunner.tcl is launched. So typically, one must first run something
like "`make testfixture sqlite3`" before launching binary tests. In other
words, testrunner.tcl does not automatically build the binaries under test
for binary tests. The testrunner.tcl expects the binaries to be available
already.
test scripts using the `testfixture` binary used to run the testrunner.tcl
script (i.e. they do not invoke the compiler to build new binaries, or the
`make` command to run tests that are not Tcl scripts). The procedure to run
these tests is therefore:
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
whatever method seems convenient.
2. Test the binary built in step 1 by running testrunner.tcl with it,
perhaps with various options.
The following sub-sections describe the various options that can be
passed to testrunner.tcl to test binary testfixture builds.
@@ -182,22 +140,22 @@ are defined in file *testrunner_data.tcl*.
To run the "veryquick" test set, use either of the following:
```
test/testrunner.tcl
test/testrunner.tcl veryquick
./testfixture $TESTDIR/testrunner.tcl
./testfixture $TESTDIR/testrunner.tcl veryquick
```
To run the "full" test suite:
```
test/testrunner.tcl full
./testfixture $TESTDIR/testrunner.tcl full
```
To run the subset of the "full" test suite for which the test file name matches
a specified pattern (e.g. all tests that start with "fts5"), either of:
```
test/testrunner.tcl fts5%
test/testrunner.tcl 'fts5*'
./testfixture $TESTDIR/testrunner.tcl fts5%
./testfixture $TESTDIR/testrunner.tcl 'fts5*'
```
Strictly speaking, for a test to be run the pattern must match the script
@@ -209,7 +167,7 @@ characters specified as part of the pattern are transformed to "\*".
To run "all" tests (full + permutations):
```
test/testrunner.tcl all
./testfixture $TESTDIR/testrunner.tcl all
```
<a name=binary_test_failures></a>
@@ -228,15 +186,10 @@ If there is no permutation, the individual test script may be run with:
Or, if the failure occured as part of a permutation:
```
test/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
```
One can also rerun all tests that failed or did not complete
in the previous invocation by typing:
```
test/testrunner.tcl retest
```
TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
<a name=source_code_tests></a>
# 3. Source Code Tests
@@ -251,9 +204,11 @@ other tests. The advantages of this are that:
* it ensures that tests are always run using binaries created with the
same set of compiler options.
The testrunner.tcl commands described in this section do not require that
the testfixture and/or sqlite3 binaries be built ahead of time. Those
binaries will be constructed automatically.
The testrunner.tcl commands described in this section may be run using
either a *testfixture* (or testfixture.exe) build, or with any other Tcl
shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
TODO: ./configure + Makefile.msc build systems.
<a name=commands_to_run_tests></a>
## 3.1. Commands to Run SQLite Tests
@@ -263,7 +218,7 @@ the `make fuzztest` target once for each of two --enable-all builds - one
with debugging enabled and one without:
```
test/testrunner.tcl mdevtest
tclsh $TESTDIR/testrunner.tcl mdevtest
```
In other words, it is equivalent to running:
@@ -272,13 +227,13 @@ In other words, it is equivalent to running:
$TOP/configure --enable-all --enable-debug
make fuzztest
make testfixture
$TOP/test/testrunner.tcl veryquick
./testfixture $TOP/test/testrunner.tcl veryquick
# Then, after removing files created by the tests above:
$TOP/configure --enable-all OPTS="-O0"
make fuzztest
make testfixture
$TOP/test/testrunner.tcl veryquick
./testfixture $TOP/test/testrunner.tcl veryquick
```
The **sdevtest** command is identical to the mdevtest command, except that the
@@ -286,7 +241,7 @@ second of the two builds is a sanitizer build. Specifically, this means that
OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
```
test/testrunner.tcl sdevtest
tclsh $TESTDIR/testrunner.tcl sdevtest
```
The **release** command runs lots of tests under lots of builds. It runs
@@ -295,7 +250,7 @@ on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
of the specific tests run.
```
test/testrunner.tcl release
tclsh $TESTDIR/testrunner.tcl release
```
As with <a href=#source code tests>source code tests</a>, one or more patterns
@@ -303,7 +258,7 @@ may be appended to any of the above commands (mdevtest, sdevtest or release).
Pattern matching is used for both Tcl tests and fuzz tests.
```
test/testrunner.tcl release rtree%
tclsh $TESTDIR/testrunner.tcl release rtree%
```
<a name=zipvfs_tests></a>
@@ -313,14 +268,14 @@ testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
tests from the Zipvfs project with the following command:
```
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
```
This can be combined with any of "mdevtest", "sdevtest" or "release" to
test both SQLite and Zipvfs with a single command:
```
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
```
<a name=source_code_test_failures></a>
@@ -340,10 +295,10 @@ a dos \*.bat file on windows. For example:
```
# Create a script that recreates build configuration "Device-One" on
# Linux or OSX:
test/testrunner.tcl script Device-One > make.sh
tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
# Create a script that recreates build configuration "Have-Not" on Windows:
test/testrunner.tcl script Have-Not > make.bat
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
```
The generated bash or \*.bat file script accepts a single argument - a makefile
@@ -366,7 +321,7 @@ Thus, for example, to run a full releasetest including an external
dbsqlfuzz database, run a command like one of these:
```
test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
tclsh test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
FUZZDB=../fuzz/20250415.db make releasetest
nmake /f Makefile.msc FUZZDB=../fuzz/20250415.db releasetest
```
@@ -376,7 +331,7 @@ databases. So if you want to run *only* tests involving the external
database, you can use a command something like this:
```
test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
tclsh test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
```
<a name=testrunner_options></a>
@@ -390,7 +345,7 @@ required by a test, not to run any actual tests. For example:
```
# Build binaries required by release test.
test/testrunner.tcl --buildonly release"
tclsh $TESTDIR/testrunner.tcl --buildonly release"
```
The **--dryrun** option prevents testrunner.tcl from building any binaries
@@ -399,7 +354,7 @@ would normally execute into the testrunner.log file. Example:
```
# Log the shell commmands that make up the mdevtest test.
test/testrunner.tcl --dryrun mdevtest"
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
```
The **--explain** option is similar to --dryrun in that it prevents
@@ -409,7 +364,7 @@ summary of all the builds and tests that would have been run.
```
# Show what builds and tests would have been run
test/testrunner.tcl --explain mdevtest
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
```
The **--status** option uses VT100 escape sequences to display the test
@@ -425,7 +380,7 @@ When running either binary or source code tests, testrunner.tcl reports the
number of jobs it intends to use to stdout. e.g.
```
$ test/testrunner.tcl
$ ./testfixture $TESTDIR/testrunner.tcl
splitting work across 16 jobs
... more output ...
```
@@ -435,7 +390,7 @@ of real cores on the machine. This can be overridden using the "--jobs" (or -j)
switch:
```
$ test/testrunner.tcl --jobs 8
$ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
splitting work across 8 jobs
... more output ...
```
@@ -445,5 +400,5 @@ running by exucuting the following command from the directory containing the
testrunner.log and testrunner.db files:
```
$ test/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
```
+1 -1
View File
@@ -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
View File
@@ -1816,7 +1816,9 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
if( !zSql ) return SQLITE_NOMEM;
p->bLock++;
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
rc = sqlite3_prepare_v3(
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
p->bLock--;
sqlite3_free(zSql);
}
@@ -3391,7 +3393,9 @@ static int fts3FilterMethod(
}
if( zSql ){
p->bLock++;
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
rc = sqlite3_prepare_v3(
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
p->bLock--;
sqlite3_free(zSql);
}else{
@@ -4014,7 +4018,6 @@ static int fts3IntegrityMethod(
UNUSED_PARAMETER(isQuick);
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK );
assert( rc!=SQLITE_CORRUPT_VTAB );
if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+1 -19
View File
@@ -203,16 +203,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
#if !defined(deliberate_fall_through)
# if defined(__has_attribute)
# if __has_attribute(fallthrough)
# define deliberate_fall_through __attribute__((fallthrough));
# endif
# endif
#endif
#if !defined(deliberate_fall_through)
# define deliberate_fall_through
#endif
#define deliberate_fall_through
/*
** Macros needed to provide flexible arrays in a portable way
@@ -610,15 +601,6 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
(*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
)
int sqlite3Fts3PrepareStmt(
Fts3Table *p, /* Prepare for this connection */
const char *zSql, /* SQL to prepare */
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
sqlite3_stmt **pp /* OUT: Prepared statement */
);
/* fts3.c */
void sqlite3Fts3ErrMsg(char**,const char*,...);
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
+1 -1
View File
@@ -325,7 +325,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
pCsr->aStat[1].nDoc++;
}
eState = 2;
/* no break */ deliberate_fall_through
/* fall through */
case 2:
if( v==0 ){ /* 0x00. Next integer will be a docid. */
+10 -32
View File
@@ -98,9 +98,9 @@ typedef struct SegmentWriter SegmentWriter;
** incrementally. See function fts3PendingListAppend() for details.
*/
struct PendingList {
sqlite3_int64 nData;
int nData;
char *aData;
sqlite3_int64 nSpace;
int nSpace;
sqlite3_int64 iLastDocid;
sqlite3_int64 iLastCol;
sqlite3_int64 iLastPos;
@@ -273,24 +273,6 @@ struct SegmentNode {
#define SQL_UPDATE_LEVEL_IDX 38
#define SQL_UPDATE_LEVEL 39
/*
** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL
** is always set.
*/
int sqlite3Fts3PrepareStmt(
Fts3Table *p, /* Prepare for this connection */
const char *zSql, /* SQL to prepare */
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
sqlite3_stmt **pp /* OUT: Prepared statement */
){
int f = SQLITE_PREPARE_FROM_DDL
|((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0)
|(bPersist ? SQLITE_PREPARE_PERSISTENT : 0);
return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL);
}
/*
** This function is used to obtain an SQLite prepared statement handle
** for the statement identified by the second argument. If successful,
@@ -416,12 +398,12 @@ static int fts3SqlStmt(
pStmt = p->aStmt[eStmt];
if( !pStmt ){
int bAllowVtab = 0;
int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
char *zSql;
if( eStmt==SQL_CONTENT_INSERT ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
bAllowVtab = 1;
f &= ~SQLITE_PREPARE_NO_VTAB;
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
@@ -429,7 +411,7 @@ static int fts3SqlStmt(
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt);
rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
sqlite3_free(zSql);
assert( rc==SQLITE_OK || pStmt==0 );
p->aStmt[eStmt] = pStmt;
@@ -778,9 +760,7 @@ static int fts3PendingTermsAddOne(
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
if( pList ){
assert( (i64)pList->nData+(i64)nToken+(i64)sizeof(Fts3HashElem)
<= (i64)p->nPendingData );
p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
}
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
@@ -793,9 +773,7 @@ static int fts3PendingTermsAddOne(
}
}
if( rc==SQLITE_OK ){
assert( (i64)p->nPendingData + pList->nData + nToken
+ sizeof(Fts3HashElem) <= 0x3fffffff );
p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
}
return rc;
}
@@ -3596,7 +3574,7 @@ static int fts3DoRebuild(Fts3Table *p){
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
@@ -5349,7 +5327,7 @@ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
@@ -5479,7 +5457,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
v = atoi(&zVal[9]);
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
v = atoi(&zVal[11]);
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
rc = SQLITE_OK;
+1 -7
View File
@@ -81,13 +81,7 @@ typedef sqlite3_uint64 u64;
# define FLEXARRAY 1
#endif
#endif /* SQLITE_AMALGAMATION */
/*
** Constants for the largest and smallest possible 32-bit signed integers.
*/
# define LARGEST_INT32 ((int)(0x7fffffff))
# define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32))
#endif
/* Truncate very long tokens to this many bytes. Hard limit is
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
+1 -1
View File
@@ -455,7 +455,7 @@ static void fts5SnippetFunction(
iBestCol = (iCol>=0 ? iCol : 0);
nPhrase = pApi->xPhraseCount(pFts);
aSeen = sqlite3_malloc64(nPhrase);
aSeen = sqlite3_malloc(nPhrase);
if( aSeen==0 ){
rc = SQLITE_NOMEM;
}
+1 -1
View File
@@ -288,7 +288,7 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
if( nIn<0 ){
nIn = (int)strlen(pIn);
}
zRet = (char*)sqlite3_malloc64((i64)nIn+1);
zRet = (char*)sqlite3_malloc(nIn+1);
if( zRet ){
memcpy(zRet, pIn, nIn);
zRet[nIn] = '\0';
+3 -1
View File
@@ -576,7 +576,7 @@ int sqlite3Fts5ConfigParse(
sqlite3_int64 nByte;
int bUnindexed = 0; /* True if there are one or more UNINDEXED */
*ppOut = pRet = (Fts5Config*)sqlite3_malloc64(sizeof(Fts5Config));
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
if( pRet==0 ) return SQLITE_NOMEM;
memset(pRet, 0, sizeof(Fts5Config));
pRet->pGlobal = pGlobal;
@@ -1123,3 +1123,5 @@ void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){
va_end(ap);
}
+2 -2
View File
@@ -314,7 +314,7 @@ int sqlite3Fts5ExprNew(
assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
if( sParse.rc==SQLITE_OK ){
*ppNew = pNew = sqlite3_malloc64(sizeof(Fts5Expr));
*ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
if( pNew==0 ){
sParse.rc = SQLITE_NOMEM;
sqlite3Fts5ParseNodeFree(sParse.pExpr);
@@ -466,7 +466,7 @@ int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
p2->pRoot = 0;
if( sParse.rc==SQLITE_OK ){
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64(
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
);
if( ap==0 ){
+1 -1
View File
@@ -91,7 +91,7 @@ int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
int rc = SQLITE_OK;
Fts5Hash *pNew;
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc64(sizeof(Fts5Hash));
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
+35 -41
View File
@@ -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
View File
@@ -517,7 +517,7 @@ static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
if( sqlite3_libversion_number()>=3008002 )
#endif
{
pIdxInfo->estimatedRows = MAX(1, nRow);
pIdxInfo->estimatedRows = nRow;
}
#endif
}
@@ -586,30 +586,19 @@ static int fts5UsePatternMatch(
** a) If a MATCH operator is present, the cost depends on the other
** constraints also present. As follows:
**
** * No other constraints: cost=50000.0
** * One rowid range constraint: cost=37500.0
** * Both rowid range constraints: cost=30000.0
** * An == rowid constraint: cost=25000.0
** * No other constraints: cost=1000.0
** * One rowid range constraint: cost=750.0
** * Both rowid range constraints: cost=500.0
** * An == rowid constraint: cost=100.0
**
** b) Otherwise, if there is no MATCH:
**
** * No other constraints: cost=3000000.0
** * One rowid range constraints: cost=2250000.0
** * Both rowid range constraint: cost=750000.0
** * An == rowid constraint: cost=25.0
** * No other constraints: cost=1000000.0
** * One rowid range constraint: cost=750000.0
** * Both rowid range constraints: cost=250000.0
** * An == rowid constraint: cost=10.0
**
** Costs are not modified by the ORDER BY clause.
**
** The ratios used in case (a) are based on informal results obtained from
** the tool/fts5cost.tcl script. The "MATCH and ==" combination has the
** cost set quite high because the query may be a prefix query. Unless
** there is a prefix index, prefix queries with rowid constraints are much
** more expensive than non-prefix queries with rowid constraints.
**
** The estimated rows returned is set to the cost/40. For simple queries,
** experimental results show that cost/4 might be about right. But for
** more complex queries that use multiple terms the number of rows might
** be far fewer than this. So we compromise and use cost/40.
*/
static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts5Table *pTab = (Fts5Table*)pVTab;
@@ -642,7 +631,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
return SQLITE_ERROR;
}
idxStr = (char*)sqlite3_malloc64((i64)pInfo->nConstraint * 8 + 1);
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
if( idxStr==0 ) return SQLITE_NOMEM;
pInfo->idxStr = idxStr;
pInfo->needToFreeIdxStr = 1;
@@ -735,35 +724,21 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
/* Calculate the estimated cost based on the flags set in idxFlags. */
if( bSeenEq ){
pInfo->estimatedCost = nSeenMatch ? 25000.0 : 25.0;
fts5SetEstimatedRows(pInfo, 1);
pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
fts5SetUniqueFlag(pInfo);
fts5SetEstimatedRows(pInfo, 1);
}else{
i64 nEstRows;
if( nSeenMatch ){
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = 50000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = 37500.0;
}else{
pInfo->estimatedCost = 50000.0;
}
nEstRows = (i64)(pInfo->estimatedCost / 40.0);
for(i=1; i<nSeenMatch; i++){
pInfo->estimatedCost *= 2.5;
nEstRows = nEstRows / 2;
}
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0;
}else{
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = 750000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = 2250000.0;
}else{
pInfo->estimatedCost = 3000000.0;
}
nEstRows = (i64)(pInfo->estimatedCost / 4.0);
pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
}
fts5SetEstimatedRows(pInfo, nEstRows);
for(i=1; i<nSeenMatch; i++){
pInfo->estimatedCost *= 0.4;
}
fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
}
pInfo->idxNum = idxFlags;
@@ -2106,7 +2081,6 @@ static int fts5UpdateMethod(
}
update_out:
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
@@ -3788,7 +3762,7 @@ static int fts5Init(sqlite3 *db){
int rc;
Fts5Global *pGlobal = 0;
pGlobal = (Fts5Global*)sqlite3_malloc64(sizeof(Fts5Global));
pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
if( pGlobal==0 ){
rc = SQLITE_NOMEM;
}else{
+4 -4
View File
@@ -391,7 +391,7 @@ static int SQLITE_TCLAPI xF5tApi(
break;
}
CASE(12, "xSetAuxdata") {
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc64(sizeof(F5tAuxData));
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
if( pData==0 ){
Tcl_AppendResult(interp, "out of memory", (char*)0);
return TCL_ERROR;
@@ -780,7 +780,7 @@ static int SQLITE_TCLAPI f5tTokenize(
}
if( nText>0 ){
pCopy = sqlite3_malloc64(nText);
pCopy = sqlite3_malloc(nText);
if( pCopy==0 ){
tokenizer.xDelete(pTok);
Tcl_AppendResult(interp, "error in sqlite3_malloc()", (char*)0);
@@ -1420,7 +1420,7 @@ static int f5tOrigintextCreate(
void *pTokCtx = 0;
int rc = SQLITE_OK;
pTok = (OriginTextTokenizer*)sqlite3_malloc64(sizeof(OriginTextTokenizer));
pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer));
if( pTok==0 ){
rc = SQLITE_NOMEM;
}else if( nArg<1 ){
@@ -1480,7 +1480,7 @@ static int xOriginToken(
int nReq = nToken + 1 + (iEnd-iStart);
if( nReq>p->nBuf ){
sqlite3_free(p->aBuf);
p->aBuf = sqlite3_malloc64(nReq*2);
p->aBuf = sqlite3_malloc(nReq*2);
if( p->aBuf==0 ) return SQLITE_NOMEM;
p->nBuf = nReq*2;
}
+5 -5
View File
@@ -194,7 +194,7 @@ static int fts5tokConnectMethod(
}
if( rc==SQLITE_OK ){
pTab = (Fts5tokTable*)sqlite3_malloc64(sizeof(Fts5tokTable));
pTab = (Fts5tokTable*)sqlite3_malloc(sizeof(Fts5tokTable));
if( pTab==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -275,7 +275,7 @@ static int fts5tokBestIndexMethod(
static int fts5tokOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
Fts5tokCursor *pCsr;
pCsr = (Fts5tokCursor *)sqlite3_malloc64(sizeof(Fts5tokCursor));
pCsr = (Fts5tokCursor *)sqlite3_malloc(sizeof(Fts5tokCursor));
if( pCsr==0 ){
return SQLITE_NOMEM;
}
@@ -347,7 +347,7 @@ static int fts5tokCb(
if( pCsr->nRow ){
pRow->iPos = pRow[-1].iPos + ((tflags & FTS5_TOKEN_COLOCATED) ? 0 : 1);
}
pRow->zToken = sqlite3_malloc64((sqlite3_int64)nToken+1);
pRow->zToken = sqlite3_malloc(nToken+1);
if( pRow->zToken==0 ) return SQLITE_NOMEM;
memcpy(pRow->zToken, pToken, nToken);
pRow->zToken[nToken] = 0;
@@ -373,8 +373,8 @@ static int fts5tokFilterMethod(
fts5tokResetCursor(pCsr);
if( idxNum==1 ){
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]);
pCsr->zInput = sqlite3_malloc64(nByte+1);
int nByte = sqlite3_value_bytes(apVal[0]);
pCsr->zInput = sqlite3_malloc(nByte+1);
if( pCsr->zInput==0 ){
rc = SQLITE_NOMEM;
}else{
+4 -4
View File
@@ -72,7 +72,7 @@ static int fts5AsciiCreate(
if( nArg%2 ){
rc = SQLITE_ERROR;
}else{
p = sqlite3_malloc64(sizeof(AsciiTokenizer));
p = sqlite3_malloc(sizeof(AsciiTokenizer));
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -367,7 +367,7 @@ static int fts5UnicodeCreate(
if( nArg%2 ){
rc = SQLITE_ERROR;
}else{
p = (Unicode61Tokenizer*)sqlite3_malloc64(sizeof(Unicode61Tokenizer));
p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
if( p ){
const char *zCat = "L* N* Co";
int i;
@@ -590,7 +590,7 @@ static int fts5PorterCreate(
zBase = azArg[0];
}
pRet = (PorterTokenizer*)sqlite3_malloc64(sizeof(PorterTokenizer));
pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
if( pRet ){
memset(pRet, 0, sizeof(PorterTokenizer));
rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2);
@@ -1297,7 +1297,7 @@ static int fts5TriCreate(
rc = SQLITE_ERROR;
}else{
int i;
pNew = (TrigramTokenizer*)sqlite3_malloc64(sizeof(*pNew));
pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
+1 -1
View File
@@ -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
+3
View File
@@ -379,6 +379,9 @@ do_execsql_test 12.2 {
db close
sqlite3 db test.db -readonly 1
explain_i {
PRAGMA integrity_check
}
do_execsql_test 12.3 {
PRAGMA integrity_check
} {ok}
-17
View File
@@ -64,21 +64,4 @@ foreach {tn sql} {
}
}
#-------------------------------------------------------------------------
# Verify that https://sqlite.org/forum/forumpost/95413eb410 has been
# fixed.
#
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE f1 USING fts5(x);
BEGIN TRANSACTION;
INSERT INTO f1(x) VALUES('abc def ghi');
}
do_test 2.1 {
sqlite3_interrupt db
} {}
do_execsql_test 2.2 {
ROLLBACK
}
finish_test
-9
View File
@@ -65,14 +65,5 @@ do_eqp_test 1.4 {
`--SCAN vt VIRTUAL TABLE INDEX 0:=
}
do_eqp_test 1.5 {
SELECT * FROM vt, t1
WHERE vt.rowid = t1.rowid AND vt MATCH ? AND b = ?
} {
QUERY PLAN
|--SCAN vt VIRTUAL TABLE INDEX 0:M1
`--SEARCH t1 USING INTEGER PRIMARY KEY (rowid=?)
}
finish_test
-16
View File
@@ -238,22 +238,6 @@ do_execsql_test 6.3 {
INSERT INTO g1(g1) VALUES('integrity-check');
}
#--------------------------------------------------------------------------
# Check that passing -2147483648 as the parameter to a merge command
# does not cause a signed integer overflow error.
#
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE f1 USING fts5(a);
}
do_execsql_test 7.1 {
INSERT INTO f1 VALUES('one two three');
INSERT INTO f1 VALUES('four five six');
INSERT INTO f1 VALUES('seven eight nine');
}
do_execsql_test 7.2 {
INSERT INTO f1(f1, rank) VALUES('merge', -2147483648);
}
finish_test
-153
View File
@@ -1,153 +0,0 @@
#
# 2026 March 20
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#--------------------------------------------------------------------------
#
# This script extracts the documentation for the API used by fts5 auxiliary
# functions from header file fts5.h. It outputs html text on stdout that
# is included in the documentation on the web.
#
sqlite3 db fts5cost.db
# Create an IPK table with 1,000,000 entries. Short records.
#
set res [list [catch { db eval {SELECT count(*) FROM t1} } msg] $msg]
if {$res!="0 1000000"} {
db eval {
PRAGMA mmap_size = 1000000000; -- 1GB
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000_000
)
INSERT INTO t1 SELECT i, hex(randomblob(8)) FROM s;
}
}
# Create an FTS5 table with 1,000,000 entries. Each row contains a single
# column containing a document of 100 terms chosen pseudo-randomly from
# a vocabularly of 2000.
set res [list [catch { db eval {SELECT count(*) FROM f1} } msg] $msg]
if {$res!="0 1000000"} {
set nVocab 2000
set nTerm 100
db eval {
BEGIN;
DROP TABLE IF EXISTS vocab1;
CREATE TABLE vocab1(w);
}
for {set ii 0} {$ii<$nVocab} {incr ii} {
set word [format %06x [expr {int(abs(rand()) * 0xFFFFFF)}]]
db eval { INSERT INTO vocab1 VALUES($word) }
lappend lVocab $word
}
db func doc doc
proc doc {} {
for {set ii 0} {$ii<$::nTerm} {incr ii} {
lappend ret [lindex $::lVocab [expr int(abs(rand())*$::nVocab)]]
}
set ret
}
db eval {
DROP TABLE IF EXISTS f1;
CREATE VIRTUAL TABLE f1 USING fts5(x);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1_000_000
)
INSERT INTO f1(rowid, x) SELECT i, doc() FROM s;
COMMIT;
}
} else {
set lVocab [db eval { SELECT * FROM vocab1 }]
set nVocab [llength $lVocab]
}
proc rowid_query {n} {
set rowid 654
for {set ii 0} {$ii<$n} {incr ii} {
db eval { SELECT b FROM t1 WHERE a = $rowid }
set rowid [expr {($rowid + 7717) % 1000000}]
}
}
proc rowid_query_fts {n} {
set rowid 654
for {set ii 0} {$ii<$n} {incr ii} {
db eval { SELECT * FROM f1 WHERE rowid = $rowid }
set rowid [expr {($rowid + 7717) % 1000000}]
}
}
proc match_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match [lrange $::lVocab $idx $idx+1]
db eval { SELECT * FROM f1($match) }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc prefix_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match "[lindex $::lVocab $idx]*"
db eval { SELECT * FROM f1($match) }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc match_rowid_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match "[lindex $::lVocab $idx]"
db eval { SELECT * FROM f1($match) WHERE rowid=500000 }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc prefix_rowid_query_fts {n} {
set idx 654
for {set ii 0} {$ii<$n} {incr ii} {
set match "[lindex $::lVocab $idx]*"
db eval { SELECT * FROM f1($match) WHERE rowid=500000 }
set idx [expr {($idx + 7717) % $::nVocab}]
}
}
proc mytime {cmd div} {
set tm [time $cmd]
expr {[lindex $tm 0] / $div}
}
#set us [mytime { match_rowid_query_fts 1000 } 1000]
#puts "1000 match/rowid queries on fts5 table: ${us} per query"
set us [mytime { prefix_rowid_query_fts 1000 } 1000]
puts "1000 prefix/rowid queries on fts5 table: ${us} per query"
set us [mytime { match_query_fts 10 } 10]
puts "10 match queries on fts5 table: ${us} per query"
set us [mytime { prefix_query_fts 10 } 10]
puts "10 prefix queries on fts5 table: ${us} per query"
set us [mytime { prefix_rowid_query_fts 1000 } 1000]
puts "1000 prefix/rowid queries on fts5 table: ${us} per query"
set us [mytime { rowid_query 10000 } 10000]
puts "10000 by-rowid queries on normal table: ${us} per query"
set us [mytime { rowid_query_fts 10000 } 10000]
puts "10000 by-rowid queries on fts5 table: ${us} per query"
+1 -1
View File
@@ -319,7 +319,7 @@ static int intckGetToken(const char *z){
char c = z[0];
int iRet = 1;
if( c=='\'' || c=='"' || c=='`' ){
while( z[iRet] ){
while( 1 ){
if( z[iRet]==c ){
iRet++;
if( z[iRet]!=c ) break;
+16 -19
View File
@@ -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
View File
@@ -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{
+1 -3
View File
@@ -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
+3 -5
View File
@@ -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;
+1 -3
View File
@@ -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
View File
@@ -847,7 +847,7 @@ static int amatchConnect(
(void)pAux;
*ppVtab = 0;
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
rc = SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -928,7 +928,7 @@ amatchConnectError:
static int amatchOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
amatch_vtab *p = (amatch_vtab*)pVTab;
amatch_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
+3 -3
View File
@@ -232,7 +232,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
cBuf = sqlite3_malloc64(nc);
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
@@ -254,7 +254,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_zeroblob(context, 0);
break;
}
bBuf = sqlite3_malloc64(nb);
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
@@ -275,7 +275,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_base64_init
int sqlite3_base_init
#else
static int sqlite3_base64_init
#endif
+9 -9
View File
@@ -262,7 +262,7 @@ static int allBase85( char *p, int len ){
#ifndef BASE85_STANDALONE
#ifndef OMIT_BASE85_CHECKER
# ifndef OMIT_BASE85_CHECKER
/* This function does the work for the SQLite is_base85(t) UDF. */
static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
assert(na==1);
@@ -282,7 +282,7 @@ static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
return;
}
}
#endif
# endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
@@ -309,7 +309,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
cBuf = sqlite3_malloc64(nc);
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
@@ -331,7 +331,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_zeroblob(context, 0);
break;
}
bBuf = sqlite3_malloc64(nb);
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
@@ -352,14 +352,14 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_base85_init
int sqlite3_base_init
#else
static int sqlite3_base85_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErr;
#ifndef OMIT_BASE85_CHECKER
# ifndef OMIT_BASE85_CHECKER
{
int rc = sqlite3_create_function
(db, "is_base85", 1,
@@ -367,7 +367,7 @@ static int sqlite3_base85_init
0, is_base85, 0, 0);
if( rc!=SQLITE_OK ) return rc;
}
#endif
# endif
return sqlite3_create_function
(db, "base85", 1,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
@@ -432,9 +432,9 @@ int main(int na, char *av[]){
int nc = strlen(cBuf);
size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1;
#ifndef OMIT_BASE85_CHECKER
# ifndef OMIT_BASE85_CHECKER
b85Clean &= allBase85( cBuf, nc );
#endif
# endif
}
break;
default:
+1 -5
View File
@@ -306,10 +306,6 @@ static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
nEntry *= (nCell+1);
if( aData[0]==10 || aData[0]==13 ) break;
nPage *= (nCell+1);
if( 14+2*(nCell/2)>=pgsz ){
rc = SQLITE_CORRUPT;
break;
}
if( nCell<=1 ){
pgno = get_uint32(aData+8);
}else{
@@ -343,7 +339,7 @@ static int binfoColumn(
sqlite3 *db = sqlite3_context_db_handle(ctx);
int rc = binfoCompute(db, pgno, pCsr);
if( rc ){
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errstr(rc));
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
return SQLITE_ERROR;
}
}
+3 -3
View File
@@ -516,7 +516,7 @@ static int closureConnect(
(void)pAux;
*ppVtab = 0;
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
rc = SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -579,7 +579,7 @@ closureConnectError:
static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
closure_vtab *p = (closure_vtab*)pVTab;
closure_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
@@ -638,7 +638,7 @@ static int closureInsertNode(
sqlite3_int64 id, /* The node ID */
int iGeneration /* The generation number for this node */
){
closure_avl *pNew = sqlite3_malloc64( sizeof(*pNew) );
closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->id = id;
+15 -27
View File
@@ -132,7 +132,7 @@ static int completionConnect(
" phase INT HIDDEN" /* Used for debugging only */
")");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -154,7 +154,7 @@ static int completionDisconnect(sqlite3_vtab *pVtab){
*/
static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
completion_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((completion_vtab*)p)->db;
@@ -199,7 +199,6 @@ static int completionNext(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
int eNextPhase = 0; /* Next phase to try if current phase reaches end */
int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
int rc;
pCur->iRowid++;
while( pCur->ePhase!=COMPLETION_EOF ){
switch( pCur->ePhase ){
@@ -225,27 +224,22 @@ static int completionNext(sqlite3_vtab_cursor *cur){
case COMPLETION_TABLES: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
sqlite3_str* pStr = sqlite3_str_new(pCur->db);
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
sqlite3_str_appendf(pStr,
"%s"
zSql = sqlite3_mprintf(
"%z%s"
"SELECT name FROM \"%w\".sqlite_schema",
zSep, zDb
zSql, zSep, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
rc = sqlite3_finalize(pS2);
zSql = sqlite3_str_finish(pStr);
if( zSql==0 ) return SQLITE_NOMEM;
if( rc==SQLITE_OK ){
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
}
sqlite3_finalize(pS2);
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
sqlite3_free(zSql);
if( rc ) return rc;
}
iCol = 0;
eNextPhase = COMPLETION_COLUMNS;
@@ -254,29 +248,24 @@ static int completionNext(sqlite3_vtab_cursor *cur){
case COMPLETION_COLUMNS: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
sqlite3_str *pStr = sqlite3_str_new(pCur->db);
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
sqlite3_str_appendf(pStr,
"%s"
zSql = sqlite3_mprintf(
"%z%s"
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
" WHERE sm.type='table'",
zSep, zDb, zDb
zSql, zSep, zDb, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
rc = sqlite3_finalize(pS2);
zSql = sqlite3_str_finish(pStr);
if( zSql==0 ) return SQLITE_NOMEM;
if( rc==SQLITE_OK ){
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
}
sqlite3_finalize(pS2);
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
sqlite3_free(zSql);
if( rc ) return rc;
}
iCol = 0;
eNextPhase = COMPLETION_EOF;
@@ -293,10 +282,9 @@ static int completionNext(sqlite3_vtab_cursor *cur){
pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
}else{
/* When all rows are finished, advance to the next phase */
rc = sqlite3_finalize(pCur->pStmt);
sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0;
pCur->ePhase = eNextPhase;
if( rc ) return rc;
continue;
}
}
+2 -2
View File
@@ -59,7 +59,7 @@ static void compressFunc(
pIn = sqlite3_value_blob(argv[0]);
nIn = sqlite3_value_bytes(argv[0]);
nOut = 13 + nIn + (nIn+999)/1000;
pOut = sqlite3_malloc64( nOut+5 );
pOut = sqlite3_malloc( nOut+5 );
for(i=4; i>=0; i--){
x[i] = (nIn >> (7*(4-i)))&0x7f;
}
@@ -98,7 +98,7 @@ static void uncompressFunc(
nOut = (nOut<<7) | (pIn[i]&0x7f);
if( (pIn[i]&0x80)!=0 ){ i++; break; }
}
pOut = sqlite3_malloc64( nOut+1 );
pOut = sqlite3_malloc( nOut+1 );
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
if( rc==Z_OK ){
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
+15 -18
View File
@@ -24,8 +24,8 @@
** schema= parameter, like this:
**
** CREATE VIRTUAL TABLE temp.csv2 USING csv(
** filename = '../http.log',
** schema = 'CREATE TABLE x(date,ipaddr,url,referrer,userAgent)'
** filename = "../http.log",
** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)"
** );
**
** Instead of specifying a file, the text of the CSV can be loaded using
@@ -62,10 +62,6 @@ SQLITE_EXTENSION_INIT1
# define CSV_NOINLINE
#endif
#ifndef SQLITEINT_H
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
#endif
/* Max size of the error message in a CsvReader */
#define CSV_MXERR 200
@@ -78,9 +74,9 @@ typedef struct CsvReader CsvReader;
struct CsvReader {
FILE *in; /* Read the CSV text from this input stream */
char *z; /* Accumulated text for a field */
i64 n; /* Number of bytes in z */
i64 nAlloc; /* Space allocated for z[] */
i64 nLine; /* Current line number */
int n; /* Number of bytes in z */
int nAlloc; /* Space allocated for z[] */
int nLine; /* Current line number */
int bNotFirst; /* True if prior text has been seen */
int cTerm; /* Character that terminated the most recent field */
size_t iIn; /* Next unread character in the input buffer */
@@ -129,7 +125,7 @@ static int csv_reader_open(
const char *zData /* ... or use this data */
){
if( zFilename ){
p->zIn = sqlite3_malloc64( CSV_INBUFSZ );
p->zIn = sqlite3_malloc( CSV_INBUFSZ );
if( p->zIn==0 ){
csv_errmsg(p, "out of memory");
return 1;
@@ -178,7 +174,7 @@ static int csv_getc(CsvReader *p){
** Return 0 on success and non-zero if there is an OOM error */
static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){
char *zNew;
i64 nNew = p->nAlloc*2 + 100;
int nNew = p->nAlloc*2 + 100;
zNew = sqlite3_realloc64(p->z, nNew);
if( zNew ){
p->z = zNew;
@@ -222,7 +218,7 @@ static char *csv_read_one_field(CsvReader *p){
}
if( c=='"' ){
int pc, ppc;
i64 startLine = p->nLine;
int startLine = p->nLine;
pc = ppc = 0;
while( 1 ){
c = csv_getc(p);
@@ -326,7 +322,7 @@ typedef struct CsvCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
CsvReader rdr; /* The CsvReader object */
char **azVal; /* Value of the current row */
i64 *aLen; /* Length of each entry */
int *aLen; /* Length of each entry */
sqlite3_int64 iRowid; /* The current rowid. Negative for EOF */
} CsvCursor;
@@ -498,7 +494,7 @@ static int csvtabConnect(
CsvTable *pNew = 0; /* The CsvTable object to construct */
int bHeader = -1; /* header= flags. -1 means not seen yet */
int rc = SQLITE_OK; /* Result code from this routine */
u64 i, j; /* Loop counters */
int i, j; /* Loop counters */
#ifdef SQLITE_TEST
int tstFlags = 0; /* Value for testflags=N parameter */
#endif
@@ -514,10 +510,11 @@ static int csvtabConnect(
# define CSV_DATA (azPValue[1])
# define CSV_SCHEMA (azPValue[2])
assert( sizeof(azPValue)==sizeof(azParam) );
memset(&sRdr, 0, sizeof(sRdr));
memset(azPValue, 0, sizeof(azPValue));
for(i=3; i<(u64)argc; i++){
for(i=3; i<argc; i++){
const char *z = argv[i];
const char *zValue;
for(j=0; j<sizeof(azParam)/sizeof(azParam[0]); j++){
@@ -564,7 +561,7 @@ static int csvtabConnect(
){
goto csvtab_connect_error;
}
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) goto csvtab_connect_oom;
memset(pNew, 0, sizeof(*pNew));
@@ -710,12 +707,12 @@ static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
CsvTable *pTab = (CsvTable*)p;
CsvCursor *pCur;
size_t nByte;
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(i64))*pTab->nCol;
nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol;
pCur = sqlite3_malloc64( nByte );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, nByte);
pCur->azVal = (char**)&pCur[1];
pCur->aLen = (i64*)&pCur->azVal[pTab->nCol];
pCur->aLen = (int*)&pCur->azVal[pTab->nCol];
*ppCursor = &pCur->base;
if( csv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData) ){
csv_xfer_error(pTab, &pCur->rdr);
+15 -65
View File
@@ -31,10 +31,6 @@ SQLITE_EXTENSION_INIT1
#define IsSpace(X) isspace((unsigned char)X)
#endif
#ifndef SQLITE_DECIMAL_MAX_DIGIT
# define SQLITE_DECIMAL_MAX_DIGIT 10000000
#endif
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
@@ -73,8 +69,7 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
int i;
int iExp = 0;
if( zIn==0 ) goto new_from_text_failed;
p = sqlite3_malloc64( sizeof(*p) );
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ) goto new_from_text_failed;
p->sign = 0;
p->oom = 0;
@@ -133,10 +128,9 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( a==0 ) goto new_from_text_failed;
p->a = a;
if( p->a==0 ) goto new_from_text_failed;
memset(p->a+p->nDigit, 0, iExp);
p->nDigit += iExp;
}
@@ -154,10 +148,9 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( a==0 ) goto new_from_text_failed;
p->a = a;
if( p->a==0 ) goto new_from_text_failed;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0, iExp);
p->nDigit += iExp;
@@ -168,7 +161,6 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
for(i=0; i<p->nDigit && p->a[i]==0; i++){}
if( i>=p->nDigit ) p->sign = 0;
}
if( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed;
return p;
new_from_text_failed:
@@ -299,38 +291,12 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
sqlite3_result_text(pCtx, z, i, sqlite3_free);
}
/*
** Round a decimal value to N significant digits. N must be positive.
*/
static void decimal_round(Decimal *p, int N){
int i;
int nZero;
if( N<1 ) return;
if( p==0 ) return;
if( p->nDigit<=N ) return;
for(nZero=0; nZero<p->nDigit && p->a[nZero]==0; nZero++){}
N += nZero;
if( p->nDigit<=N ) return;
if( p->a[N]>4 ){
p->a[N-1]++;
for(i=N-1; i>0 && p->a[i]>9; i--){
p->a[i] = 0;
p->a[i-1]++;
}
if( p->a[0]>9 ){
p->a[0] = 1;
p->nFrac--;
}
}
memset(&p->a[N], 0, p->nDigit - N);
}
/*
** Make the given Decimal the result in an format similar to '%+#e'.
** In other words, show exponential notation with leading and trailing
** zeros omitted.
*/
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
char *z; /* The output buffer */
int i; /* Loop counter */
int nZero; /* Number of leading zeros */
@@ -348,8 +314,7 @@ static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
sqlite3_result_null(pCtx);
return;
}
if( N<1 ) N = 0;
for(nDigit=p->nDigit; nDigit>N && p->a[nDigit-1]==0; nDigit--){}
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
@@ -465,18 +430,15 @@ cmp_done:
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
int nAddSig;
int nAddFrac;
signed char *a;
if( p==0 ) return;
nAddFrac = nFrac - p->nFrac;
nAddSig = (nDigit - p->nDigit) - nAddFrac;
if( nAddFrac==0 && nAddSig==0 ) return;
if( nDigit+1>SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1; return; }
a = sqlite3_realloc64(p->a, nDigit+1);
if( a==0 ){
p->a = sqlite3_realloc64(p->a, nDigit+1);
if( p->a==0 ){
p->oom = 1;
return;
}
p->a = a;
if( nAddSig ){
memmove(p->a+nAddSig, p->a, p->nDigit);
memset(p->a, 0, nAddSig);
@@ -571,18 +533,14 @@ static void decimalMul(Decimal *pA, Decimal *pB){
signed char *acc = 0;
int i, j, k;
int minFrac;
sqlite3_int64 sumDigit;
if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
sumDigit = pA->nDigit;
sumDigit += pB->nDigit;
sumDigit += 2;
if( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1; return; }
acc = sqlite3_malloc64( sumDigit );
acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
(sqlite3_int64)pB->nDigit + 2 );
if( acc==0 ){
pA->oom = 1;
goto mul_end;
@@ -719,16 +677,10 @@ static void decimalFunc(
sqlite3_value **argv
){
Decimal *p = decimal_new(context, argv[0], 0);
int N;
if( argc==2 ){
N = sqlite3_value_int(argv[1]);
if( N>0 ) decimal_round(p, N);
}else{
N = 0;
}
UNUSED_PARAMETER(argc);
if( p ){
if( sqlite3_user_data(context)!=0 ){
decimal_result_sci(context, p, N);
decimal_result_sci(context, p);
}else{
decimal_result(context, p);
}
@@ -814,7 +766,7 @@ static void decimalSumStep(
if( p==0 ) return;
if( !p->isInit ){
p->isInit = 1;
p->a = sqlite3_malloc64(2);
p->a = sqlite3_malloc(2);
if( p->a==0 ){
p->oom = 1;
}else{
@@ -898,7 +850,7 @@ static void decimalPow2Func(
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0]));
decimal_result_sci(context, pA, 0);
decimal_result_sci(context, pA);
decimal_free(pA);
}
}
@@ -919,9 +871,7 @@ int sqlite3_decimal_init(
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "decimal", 1, 0, decimalFunc },
{ "decimal", 2, 0, decimalFunc },
{ "decimal_exp", 1, 1, decimalFunc },
{ "decimal_exp", 2, 1, decimalFunc },
{ "decimal_cmp", 2, 0, decimalCmpFunc },
{ "decimal_add", 2, 0, decimalAddFunc },
{ "decimal_sub", 2, 0, decimalSubFunc },
+2 -2
View File
@@ -92,7 +92,7 @@ static int explainConnect(
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(addr,opcode,p1,p2,p3,p4,p5,comment,sql HIDDEN)");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -114,7 +114,7 @@ static int explainDisconnect(sqlite3_vtab *pVtab){
*/
static int explainOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
explain_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((explain_vtab*)p)->db;
+74 -176
View File
@@ -73,6 +73,11 @@
** $path is a relative path, then $path is interpreted relative to $dir.
** And the paths returned in the "name" column of the table are also
** relative to directory $dir.
**
** Notes on building this extension for Windows:
** Unless linked statically with the SQLite library, a preprocessor
** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone
** DLL form of this extension for WIN32. See its use below for details.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -89,16 +94,12 @@ SQLITE_EXTENSION_INIT1
# include <utime.h>
# include <sys/time.h>
# define STRUCT_STAT struct stat
# include <limits.h>
# include <stdlib.h>
#else
# include "windirent.h"
# include <direct.h>
# define STRUCT_STAT struct _stat
# define chmod(path,mode) fileio_chmod(path,mode)
# define mkdir(path,mode) fileio_mkdir(path)
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
#endif
#include <time.h>
#include <errno.h>
@@ -130,9 +131,12 @@ SQLITE_EXTENSION_INIT1
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_chmod(const char *zPath, int pmode){
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return -1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wchmod(b1, pmode);
sqlite3_free(b1);
return rc;
@@ -144,9 +148,12 @@ static int fileio_chmod(const char *zPath, int pmode){
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_mkdir(const char *zPath){
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return -1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wmkdir(b1);
sqlite3_free(b1);
return rc;
@@ -259,7 +266,50 @@ static sqlite3_uint64 fileTimeToUnixTime(
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
#endif /* _WIN32 */
#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
# /* To allow a standalone DLL, use this next replacement function: */
# undef sqlite3_win32_utf8_to_unicode
# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
#
LPWSTR utf8_to_utf16(const char *z){
int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
return rv;
sqlite3_free(rv);
return 0;
}
#endif
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
** appears to return these values as local times.
*/
static void statTimesToUtc(
const char *zPath,
STRUCT_STAT *pStatBuf
){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
if( zUnicodeName ){
memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
hFindFile = FindFirstFileW(zUnicodeName, &fd);
if( hFindFile!=NULL ){
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
FindClose(hFindFile);
}
sqlite3_free(zUnicodeName);
}
}
#endif
/*
** This function is used in place of stat(). On Windows, special handling
@@ -271,23 +321,14 @@ static int fileStat(
STRUCT_STAT *pStatBuf
){
#if defined(_WIN32)
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return 1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wstat(b1, pStatBuf);
if( rc==0 ){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
hFindFile = FindFirstFileW(b1, &fd);
if( hFindFile!=NULL ){
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
FindClose(hFindFile);
}
}
sqlite3_free(b1);
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
return rc;
#else
return stat(zPath, pStatBuf);
@@ -418,6 +459,7 @@ static int writeFile(
if( mtime>=0 ){
#if defined(_WIN32)
#if !SQLITE_OS_WINRT
/* Windows */
FILETIME lastAccess;
FILETIME lastWrite;
@@ -448,6 +490,7 @@ static int writeFile(
}else{
return 1;
}
#endif
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
/* Recent unix */
struct timespec times[2];
@@ -613,7 +656,7 @@ static int fsdirConnect(
(void)pzErr;
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
if( rc==SQLITE_OK ){
pNew = (fsdir_tab*)sqlite3_malloc64( sizeof(*pNew) );
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
@@ -636,7 +679,7 @@ static int fsdirDisconnect(sqlite3_vtab *pVtab){
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
fsdir_cursor *pCur;
(void)p;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->iLvl = -1;
@@ -1051,154 +1094,6 @@ static int fsdirRegister(sqlite3 *db){
# define fsdirRegister(x) SQLITE_OK
#endif
/*
** This version of realpath() works on any system. The string
** returned is held in memory allocated using sqlite3_malloc64().
** The caller is responsible for calling sqlite3_free().
*/
static char *portable_realpath(const char *zPath){
#if !defined(_WIN32) /* BEGIN unix */
char *zOut = 0; /* Result */
char *z; /* Temporary buffer */
#if defined(PATH_MAX)
char zBuf[PATH_MAX+1]; /* Space for the temporary buffer */
#endif
if( zPath==0 ) return 0;
#if defined(PATH_MAX)
z = realpath(zPath, zBuf);
if( z ){
zOut = sqlite3_mprintf("%s", zBuf);
}
#endif /* defined(PATH_MAX) */
if( zOut==0 ){
/* Try POSIX.1-2008 malloc behavior */
z = realpath(zPath, NULL);
if( z ){
zOut = sqlite3_mprintf("%s", z);
free(z);
}
}
return zOut;
#else /* End UNIX, Begin WINDOWS */
wchar_t *zPath16; /* UTF16 translation of zPath */
char *zOut = 0; /* Result */
wchar_t *z = 0; /* Temporary buffer */
if( zPath==0 ) return 0;
zPath16 = sqlite3_win32_utf8_to_unicode(zPath);
if( zPath16==0 ) return 0;
z = _wfullpath(NULL, zPath16, 0);
sqlite3_free(zPath16);
if( z ){
zOut = sqlite3_win32_unicode_to_utf8(z);
free(z);
}
return zOut;
#endif /* End WINDOWS, Begin common code */
}
/*
** SQL function: realpath(X)
**
** Try to convert file or pathname X into its real, absolute pathname.
** Return NULL if unable.
**
** The file or directory X is not required to exist. The answer is formed
** by calling system realpath() on the prefix of X that does exist and
** appending the tail of X that does not (yet) exist.
*/
static void realpathFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zPath; /* Original input path */
char *zCopy; /* An editable copy of zPath */
char *zOut; /* The result */
char cSep = 0; /* Separator turned into \000 */
size_t len; /* Prefix length before cSep */
#ifdef _WIN32
const int isWin = 1;
#else
const int isWin = 0;
#endif
(void)argc;
zPath = (const char*)sqlite3_value_text(argv[0]);
if( zPath==0 ) return;
if( zPath[0]==0 ) zPath = ".";
zCopy = sqlite3_mprintf("%s",zPath);
len = strlen(zCopy);
while( len>1 && (zCopy[len-1]=='/' || (isWin && zCopy[len-1]=='\\')) ){
len--;
}
zCopy[len] = 0;
while( 1 /*exit-by-break*/ ){
zOut = portable_realpath(zCopy);
zCopy[len] = cSep;
if( zOut ){
if( cSep ){
zOut = sqlite3_mprintf("%z%s",zOut,&zCopy[len]);
}
break;
}else{
size_t i = len-1;
while( i>0 ){
if( zCopy[i]=='/' || (isWin && zCopy[i]=='\\') ) break;
i--;
}
if( i<=0 ){
if( zCopy[0]=='/' ){
zOut = zCopy;
zCopy = 0;
}else if( (zOut = portable_realpath("."))!=0 ){
zOut = sqlite3_mprintf("%z/%s", zOut, zCopy);
}
break;
}
cSep = zCopy[i];
zCopy[i] = 0;
len = i;
}
}
sqlite3_free(zCopy);
if( zOut ){
/* Simplify any "/./" or "/../" that might have snuck into the
** pathname due to appending of zCopy. We only have to consider
** unix "/" separators, because the _wfilepath() system call on
** Windows will have already done this simplification for us. */
size_t i, j, n;
n = strlen(zOut);
for(i=j=0; i<n; i++){
if( zOut[i]=='/' ){
if( zOut[i+1]=='/' ) continue;
if( zOut[i+1]=='.' && i+2<n && zOut[i+2]=='/' ){
i += 1;
continue;
}
if( zOut[i+1]=='.' && i+3<n && zOut[i+2]=='.' && zOut[i+3]=='/' ){
while( j>0 && zOut[j-1]!='/' ){ j--; }
if( j>0 ){ j--; }
i += 2;
continue;
}
}
zOut[j++] = zOut[i];
}
zOut[j] = 0;
/* Return the result */
sqlite3_result_text(context, zOut, -1, sqlite3_free);
}
}
#ifdef _WIN32
__declspec(dllexport)
#endif
@@ -1225,10 +1120,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
+5 -7
View File
@@ -38,11 +38,9 @@ SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
/*
** The "u32" type must be an unsigned 32-bit integer. "u64" is
** an unsigned 64-bit integer.
** The "u32" type must be an unsigned 32-bit integer. Adjust this
*/
typedef unsigned int u32;
typedef sqlite3_uint64 u64;
/*
** Must be a 16-bit value
@@ -543,8 +541,8 @@ static int delta_apply(
int lenDelta, /* Length of the delta */
char *zOut /* Write the output into this preallocated buffer */
){
sqlite3_uint64 limit;
sqlite3_uint64 total = 0;
unsigned int limit;
unsigned int total = 0;
#ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
char *zOrigOut = zOut;
#endif
@@ -572,7 +570,7 @@ static int delta_apply(
/* ERROR: copy exceeds output file size */
return -1;
}
if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
if( ofst+cnt > lenSrc ){
/* ERROR: copy extends past end of input */
return -1;
}
@@ -843,7 +841,7 @@ static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
*/
static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
deltaparsevtab_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+3 -3
View File
@@ -556,7 +556,7 @@ static int fuzzerConnect(
static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
fuzzer_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p;
@@ -617,12 +617,12 @@ static int fuzzerRender(
int *pnBuf /* Size of the buffer */
){
const fuzzer_rule *pRule = pStem->pRule;
sqlite3_int64 n; /* Size of output term without nul-term */
int n; /* Size of output term without nul-term */
char *z; /* Buffer to assemble output term in */
n = pStem->nBasis + pRule->nTo - pRule->nFrom;
if( (*pnBuf)<n+1 ){
(*pzBuf) = sqlite3_realloc64((*pzBuf), n+100);
(*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
if( (*pzBuf)==0 ) return SQLITE_NOMEM;
(*pnBuf) = n+100;
}
+1 -35
View File
@@ -179,9 +179,9 @@ static void ieee754func(
}
if( m<0 ){
if( m<(-9223372036854775807LL) ) return;
isNeg = 1;
m = -m;
if( m<0 ) return;
}else if( m==0 && e>-1000 && e<1000 ){
sqlite3_result_double(context, 0.0);
return;
@@ -259,38 +259,6 @@ static void ieee754func_to_blob(
}
}
/*
** Functions to convert between 64-bit integers and floats.
**
** The bit patterns are copied. The numeric values are different.
*/
static void ieee754func_from_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
double r;
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(context, r);
}
}
static void ieee754func_to_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[0]);
sqlite3_uint64 v;
memcpy(&v, &r, sizeof(v));
sqlite3_result_int64(context, v);
}
}
/*
** SQL Function: ieee754_inc(r,N)
**
@@ -343,8 +311,6 @@ int sqlite3_ieee_init(
{ "ieee754_exponent", 1, 2, ieee754func },
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
{ "ieee754_to_int", 1, 0, ieee754func_to_int },
{ "ieee754_from_int", 1, 0, ieee754func_from_int },
{ "ieee754_inc", 2, 0, ieee754inc },
};
unsigned int i;
+2 -2
View File
@@ -85,7 +85,7 @@ static int memstatConnect(
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(name,schema,value,hiwtr)");
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -107,7 +107,7 @@ static int memstatDisconnect(sqlite3_vtab *pVtab){
*/
static int memstatOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
memstat_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((memstat_vtab*)p)->db;
+2 -2
View File
@@ -75,7 +75,7 @@ static int prefixesConnect(
"CREATE TABLE prefixes(prefix TEXT, original_string TEXT HIDDEN)"
);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -98,7 +98,7 @@ static int prefixesDisconnect(sqlite3_vtab *pVtab){
*/
static int prefixesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
prefixes_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+2 -2
View File
@@ -152,7 +152,7 @@ static int qpvtabConnect(
#define QPVTAB_FLAGS 11
#define QPVTAB_NONE 12
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -174,7 +174,7 @@ static int qpvtabDisconnect(sqlite3_vtab *pVtab){
*/
static int qpvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
qpvtab_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+9 -23
View File
@@ -32,7 +32,7 @@
** ^X X occurring at the beginning of the string
** X$ X occurring at the end of the string
** . Match any single character
** \c Character c where c is one of \{}()[]|*+?-.
** \c Character c where c is one of \{}()[]|*+?.
** \c C-language escapes for c in afnrtv. ex: \t or \n
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
** \xXX Where XX is exactly 2 hex digits, unicode value XX
@@ -417,7 +417,7 @@ static int re_hex(int c, int *pV){
** return its interpretation.
*/
static unsigned re_esc_char(ReCompiled *p){
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-";
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
static const char zTrans[] = "\a\f\n\r\t\v";
int i, v = 0;
char c;
@@ -676,7 +676,7 @@ static const char *re_compile(
int i, j;
*ppRe = 0;
pRe = sqlite3_malloc64( sizeof(*pRe) );
pRe = sqlite3_malloc( sizeof(*pRe) );
if( pRe==0 ){
return "out of memory";
}
@@ -740,18 +740,11 @@ static const char *re_compile(
}
/*
** The value of LIMIT_MAX_PATTERN_LENGTH.
** Compute a reasonable limit on the length of the REGEXP NFA.
*/
static int re_maxlen(sqlite3_context *context){
sqlite3 *db = sqlite3_context_db_handle(context);
return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1);
}
/*
** Maximum NFA size given a maximum pattern length.
*/
static int re_maxnfa(int mxlen){
return 75+mxlen/2;
return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2;
}
/*
@@ -777,17 +770,10 @@ static void re_sql_func(
(void)argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
int mxLen = re_maxlen(context);
int nPattern;
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
nPattern = sqlite3_value_bytes(argv[0]);
if( nPattern>mxLen ){
zErr = "REGEXP pattern too big";
}else{
zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen),
sqlite3_user_data(context)!=0);
}
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
@@ -828,6 +814,7 @@ static void re_bytecode_func(
int i;
int n;
char *z;
(void)argc;
static const char *ReOpName[] = {
"EOF",
"MATCH",
@@ -850,10 +837,9 @@ static void re_bytecode_func(
"ATSTART",
};
(void)argc;
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)),
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
+1 -1
View File
@@ -92,7 +92,7 @@ static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){
static u8 *scrubBackupAllocPage(ScrubState *p){
u8 *pPage;
if( p->rcErr ) return 0;
pPage = sqlite3_malloc64( p->szPage );
pPage = sqlite3_malloc( p->szPage );
if( pPage==0 ) p->rcErr = SQLITE_NOMEM;
return pPage;
}
+2 -2
View File
@@ -239,7 +239,7 @@ static int seriesConnect(
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
@@ -261,7 +261,7 @@ static int seriesDisconnect(sqlite3_vtab *pVtab){
static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
series_cursor *pCur;
(void)pUnused;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+7 -16
View File
@@ -230,16 +230,13 @@ static void hash_finish(
*****************************************************************************/
/*
** Two SQL functions: sha1(X) and sha1b(X).
** Implementation of the sha1(X) function.
**
** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
** of the argument X. If X is a BLOB, it is hashed as is. For all other
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
** argument X. If X is a BLOB, it is hashed as is. For all other
** types of input, X is converted into a UTF-8 string and the string
** is hashed without the trailing 0x00 terminator. The hash of a NULL
** is hash without the trailing 0x00 terminator. The hash of a NULL
** value is NULL.
**
** sha1b(X) is the same except that it returns a 20-byte BLOB containing
** the binary hash instead of a hexadecimal string.
*/
static void sha1Func(
sqlite3_context *context,
@@ -249,27 +246,22 @@ static void sha1Func(
SHA1Context cx;
int eType = sqlite3_value_type(argv[0]);
int nByte = sqlite3_value_bytes(argv[0]);
const unsigned char *pData;
char zOut[44];
assert( argc==1 );
if( eType==SQLITE_NULL ) return;
hash_init(&cx);
if( eType==SQLITE_BLOB ){
pData = (const unsigned char*)sqlite3_value_blob(argv[0]);
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
}else{
pData = (const unsigned char*)sqlite3_value_text(argv[0]);
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
}
if( pData==0 ) return;
hash_step(&cx, pData, nByte);
if( sqlite3_user_data(context)!=0 ){
/* sha1b() - binary result */
hash_finish(&cx, zOut, 1);
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
}else{
/* sha1() - hexadecimal text result */
hash_finish(&cx, zOut, 0);
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
}
}
@@ -323,7 +315,6 @@ static void sha1QueryFunc(
}
nCol = sqlite3_column_count(pStmt);
z = sqlite3_sql(pStmt);
if( z==0 ) z = "";
n = (int)strlen(z);
hash_step_vformat(&cx,"S%d:",n);
hash_step(&cx,(unsigned char*)z,n);
+3 -3
View File
@@ -46,7 +46,7 @@ static void sqlarCompressFunc(
uLongf nOut = compressBound(nData);
Bytef *pOut;
pOut = (Bytef*)sqlite3_malloc64(nOut);
pOut = (Bytef*)sqlite3_malloc(nOut);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -84,14 +84,14 @@ static void sqlarUncompressFunc(
sqlite3_int64 sz;
assert( argc==2 );
sz = sqlite3_value_int64(argv[1]);
sz = sqlite3_value_int(argv[1]);
if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
sqlite3_result_value(context, argv[0]);
}else{
uLongf szf = sz;
const Bytef *pData= sqlite3_value_blob(argv[0]);
Bytef *pOut = sqlite3_malloc64(sz);
Bytef *pOut = sqlite3_malloc(sz);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
}else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
+7 -25
View File
@@ -101,8 +101,8 @@ FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
sz1 = (int)strlen(zFilename);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
b1[sz1] = 0;
@@ -127,8 +127,8 @@ FILE *sqlite3_popen(const char *zCommand, const char *zMode){
sz1 = (int)strlen(zCommand);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
b1[sz1] = 0;
@@ -151,7 +151,7 @@ char *sqlite3_fgets(char *buf, int sz, FILE *in){
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = sqlite3_malloc64( sz*sizeof(wchar_t) );
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nRead = 0;
@@ -226,7 +226,7 @@ int sqlite3_fputs(const char *z, FILE *out){
** to the console on Windows.
*/
int sz = (int)strlen(z);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(wchar_t) );
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
@@ -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
-3
View File
@@ -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
View File
@@ -52,7 +52,7 @@ static void stmtrandFunc(
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
if( p==0 ){
unsigned int seed;
p = sqlite3_malloc64( sizeof(*p) );
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
+2 -2
View File
@@ -101,7 +101,7 @@ static int templatevtabConnect(
#define TEMPLATEVTAB_A 0
#define TEMPLATEVTAB_B 1
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -123,7 +123,7 @@ static int templatevtabDisconnect(sqlite3_vtab *pVtab){
*/
static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
templatevtab_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
-1042
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -613,7 +613,7 @@ static int vstattabConnect(
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
@@ -633,7 +633,7 @@ static int vstattabDisconnect(sqlite3_vtab *pVtab){
*/
static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
VfsStatCursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+1 -1
View File
@@ -892,7 +892,7 @@ static int vfstraceOpen(
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
sqlite3_io_methods *pNew = sqlite3_malloc64( sizeof(*pNew) );
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
+8 -50
View File
@@ -14,8 +14,6 @@
** on stdout when its key interfaces are called. This is intended for
** interactive analysis and debugging of virtual table interfaces.
**
** HOW TO COMPILE:
**
** To build this extension as a separately loaded shared library or
** DLL, use compiler command-lines similar to the following:
**
@@ -23,7 +21,7 @@
** (mac) clang -fPIC -dynamiclib vtablog.c -o vtablog.dylib
** (windows) cl vtablog.c -link -dll -out:vtablog.dll
**
** USAGE EXAMPLE:
** Usage example:
**
** .load ./vtablog
** CREATE VIRTUAL TABLE temp.log USING vtablog(
@@ -31,23 +29,6 @@
** rows=25
** );
** SELECT * FROM log;
**
** ARGUMENTS TO CREATE VIRTUAL TABLE:
**
** In "CREATE VIRTUAL TABLE temp.log AS vtablog(ARGS....)" statement, the
** ARGS argument is a list of key-value pairs that can be any of the
** following.
**
** schema=TEXT Text is a CREATE TABLE statement that defines
** the schema of the new virtual table.
**
** rows=N The table as N rows.
**
** consume_order_by=N If the left-most ORDER BY terms is ASC and
** against column N (where the leftmost column
** is #1) then set the orderByConsumed=1 flag in
** xBestIndex. Or if the left-most ORDER BY is
** DESC and against column -N, do likewise.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -68,8 +49,6 @@ struct vtablog_vtab {
char *zName; /* Table name. argv[2] of xConnect/xCreate */
int nRow; /* Number of rows in the table */
int nCursor; /* Number of cursors created */
int iConsumeOB; /* Consume the ORDER BY clause if on column N-th
** and consumeOB=N or consumeOB=(-N) and DESC */
};
/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
@@ -201,7 +180,6 @@ static int vtablogConnectCreate(
int rc;
char *zSchema = 0;
char *zNRow = 0;
char *zConsumeOB = 0;
printf("%s.%s.%s():\n", argv[1], argv[2],
isCreate ? "xCreate" : "xConnect");
@@ -225,10 +203,6 @@ static int vtablogConnectCreate(
rc = SQLITE_ERROR;
goto vtablog_end_connect;
}
if( vtablog_string_parameter(pzErr, "consume_order_by", z, &zConsumeOB) ){
rc = SQLITE_ERROR;
goto vtablog_end_connect;
}
}
if( zSchema==0 ){
zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
@@ -240,17 +214,13 @@ static int vtablogConnectCreate(
printf(" schema = '%s'\n", zSchema);
rc = sqlite3_declare_vtab(db, zSchema);
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
pNew->nRow = 10;
if( zNRow ) pNew->nRow = atoi(zNRow);
printf(" nrow = %d\n", pNew->nRow);
if( zConsumeOB ) pNew->iConsumeOB = atoi(zConsumeOB);
if( pNew->iConsumeOB ){
printf(" consume_order_by = %d\n", pNew->iConsumeOB);
}
pNew->zDb = sqlite3_mprintf("%s", argv[1]);
pNew->zName = sqlite3_mprintf("%s", argv[2]);
}
@@ -258,7 +228,6 @@ static int vtablogConnectCreate(
vtablog_end_connect:
sqlite3_free(zSchema);
sqlite3_free(zNRow);
sqlite3_free(zConsumeOB);
return rc;
}
static int vtablogCreate(
@@ -313,7 +282,7 @@ static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
vtablog_cursor *pCur;
printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName,
++pTab->nCursor);
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->iCursor = pTab->nCursor;
@@ -545,27 +514,16 @@ static int vtablogBestIndex(
}
}
printf(" nOrderBy: %d\n", p->nOrderBy);
if( p->nOrderBy ){
for(i=0; i<p->nOrderBy; i++){
printf(" orderby[%d]: col=%d desc=%d\n",
i,
p->aOrderBy[i].iColumn,
p->aOrderBy[i].desc);
}
if( pTab->iConsumeOB ){
int N = p->aOrderBy[0].iColumn+1;
if( (p->aOrderBy[0].desc && N==-pTab->iConsumeOB)
|| (!p->aOrderBy[0].desc && N==pTab->iConsumeOB)
){
p->orderByConsumed = 1;
}
}
for(i=0; i<p->nOrderBy; i++){
printf(" orderby[%d]: col=%d desc=%d\n",
i,
p->aOrderBy[i].iColumn,
p->aOrderBy[i].desc);
}
p->estimatedCost = (double)500;
p->estimatedRows = 500;
printf(" idxNum=%d\n", p->idxNum);
printf(" idxStr=NULL\n");
printf(" sqlite3_vtab_distinct()=%d\n", sqlite3_vtab_distinct(p));
printf(" orderByConsumed=%d\n", p->orderByConsumed);
printf(" estimatedCost=%g\n", p->estimatedCost);
printf(" estimatedRows=%lld\n", p->estimatedRows);
+5 -5
View File
@@ -86,7 +86,7 @@ static int vtshimCreate(
}
return SQLITE_ERROR;
}
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -125,7 +125,7 @@ static int vtshimConnect(
}
return SQLITE_ERROR;
}
pNew = sqlite3_malloc64( sizeof(*pNew) );
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -192,7 +192,7 @@ static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
int rc;
*ppCursor = 0;
if( pAux->bDisposed ) return SQLITE_ERROR;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
@@ -444,7 +444,7 @@ static int vtshimCopyModule(
){
sqlite3_module *p;
if( !pMod || !ppMod ) return SQLITE_ERROR;
p = sqlite3_malloc64( sizeof(*p) );
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ) return SQLITE_NOMEM;
memcpy(p, pMod, sizeof(*p));
*ppMod = p;
@@ -464,7 +464,7 @@ void *sqlite3_create_disposable_module(
vtshim_aux *pAux;
sqlite3_module *pMod;
int rc;
pAux = sqlite3_malloc64( sizeof(*pAux) );
pAux = sqlite3_malloc( sizeof(*pAux) );
if( pAux==0 ){
if( xDestroy ) xDestroy(pClientData);
return 0;
+2 -2
View File
@@ -47,7 +47,7 @@ static int wholenumberConnect(
char **pzErr
){
sqlite3_vtab *pNew;
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
@@ -69,7 +69,7 @@ static int wholenumberDisconnect(sqlite3_vtab *pVtab){
*/
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
wholenumber_cursor *pCur;
pCur = sqlite3_malloc64( sizeof(*pCur) );
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
+31 -37
View File
@@ -393,7 +393,7 @@ static int zipfileConnect(
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
if( rc==SQLITE_OK ){
pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile);
pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, nByte+nFile);
pNew->db = db;
@@ -456,7 +456,7 @@ static int zipfileDisconnect(sqlite3_vtab *pVtab){
static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
ZipfileTab *pTab = (ZipfileTab*)p;
ZipfileCsr *pCsr;
pCsr = sqlite3_malloc64(sizeof(*pCsr));
pCsr = sqlite3_malloc(sizeof(*pCsr));
*ppCsr = (sqlite3_vtab_cursor*)pCsr;
if( pCsr==0 ){
return SQLITE_NOMEM;
@@ -539,15 +539,14 @@ static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
static int zipfileReadData(
FILE *pFile, /* Read from this file */
u8 *aRead, /* Read into this buffer */
i64 nRead, /* Number of bytes to read */
int nRead, /* Number of bytes to read */
i64 iOff, /* Offset to read from */
char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
){
size_t n;
fseek(pFile, (long)iOff, SEEK_SET);
n = fread(aRead, 1, (long)nRead, pFile);
if( n!=(size_t)nRead ){
sqlite3_free(*pzErrmsg);
n = fread(aRead, 1, nRead, pFile);
if( (int)n!=nRead ){
*pzErrmsg = sqlite3_mprintf("error in fread()");
return SQLITE_ERROR;
}
@@ -564,7 +563,7 @@ static int zipfileAppendData(
fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
if( (int)n!=nWrite ){
zipfileTableErr(pTab,"error in fwrite()");
pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
return SQLITE_ERROR;
}
pTab->szCurrent += nWrite;
@@ -705,12 +704,7 @@ static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
u8 *p = aExtra;
u8 *pEnd = &aExtra[nExtra];
/* Stop when there are less than 9 bytes left to scan in the buffer. This
** is because the timestamp field requires exactly 9 bytes - 4 bytes of
** header fields and 5 bytes of data. If there are less than 9 bytes
** remaining, either it is some other field or else the extra data
** is corrupt. Either way, do not process it. */
while( p+(2*sizeof(u16) + 1 + sizeof(u32))<=pEnd ){
while( p<pEnd ){
u16 id = zipfileRead16(p);
u16 nByte = zipfileRead16(p);
@@ -816,7 +810,6 @@ static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
** generic corruption message and return SQLITE_CORRUPT;
*/
static int zipfileCorrupt(char **pzErr){
sqlite3_free(*pzErr);
*pzErr = sqlite3_mprintf("zip archive is corrupt");
return SQLITE_CORRUPT;
}
@@ -835,7 +828,7 @@ static int zipfileCorrupt(char **pzErr){
static int zipfileGetEntry(
ZipfileTab *pTab, /* Store any error message here */
const u8 *aBlob, /* Pointer to in-memory file image */
i64 nBlob, /* Size of aBlob[] in bytes */
int nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* If aBlob==0, read from this file */
i64 iOff, /* Offset of CDS record */
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
@@ -875,14 +868,14 @@ static int zipfileGetEntry(
memset(pNew, 0, sizeof(ZipfileEntry));
rc = zipfileReadCDS(aRead, &pNew->cds);
if( rc!=SQLITE_OK ){
zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff);
*pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
}else if( aBlob==0 ){
rc = zipfileReadData(
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
);
}else{
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){
if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
@@ -907,15 +900,14 @@ static int zipfileGetEntry(
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
memset(&lfh, 0, sizeof(lfh));
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if( aBlob && pNew->cds.szCompressed ){
if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
@@ -926,7 +918,7 @@ static int zipfileGetEntry(
}
}
}else{
zipfileTableErr(pTab, "failed to read LFH at offset %d",
*pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
(int)pNew->cds.iOffset
);
}
@@ -950,7 +942,7 @@ static int zipfileNext(sqlite3_vtab_cursor *cur){
int rc = SQLITE_OK;
if( pCsr->pFile ){
i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize;
i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
zipfileEntryFree(pCsr->pCurrent);
pCsr->pCurrent = 0;
if( pCsr->iNextOff>=iEof ){
@@ -995,7 +987,7 @@ static void zipfileInflate(
int nIn, /* Size of buffer aIn[] in bytes */
int nOut /* Expected output size */
){
u8 *aRes = sqlite3_malloc64(nOut);
u8 *aRes = sqlite3_malloc(nOut);
if( aRes==0 ){
sqlite3_result_error_nomem(pCtx);
}else{
@@ -1016,7 +1008,7 @@ static void zipfileInflate(
if( err!=Z_STREAM_END ){
zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
}else{
sqlite3_result_blob(pCtx, aRes, (int)str.total_out, zipfileFree);
sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
aRes = 0;
}
}
@@ -1188,12 +1180,12 @@ static int zipfileEof(sqlite3_vtab_cursor *cur){
static int zipfileReadEOCD(
ZipfileTab *pTab, /* Return errors here */
const u8 *aBlob, /* Pointer to in-memory file image */
i64 nBlob, /* Size of aBlob[] in bytes */
int nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* Read from this file if aBlob==0 */
ZipfileEOCD *pEOCD /* Object to populate */
){
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
i64 nRead; /* Bytes to read from file */
int nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
memset(pEOCD, 0, sizeof(ZipfileEOCD));
@@ -1214,7 +1206,7 @@ static int zipfileReadEOCD(
}
if( rc==SQLITE_OK ){
i64 i;
int i;
/* Scan backwards looking for the signature bytes */
for(i=nRead-20; i>=0; i--){
@@ -1225,7 +1217,9 @@ static int zipfileReadEOCD(
}
}
if( i<0 ){
zipfileTableErr(pTab, "cannot find end of central directory record");
pTab->base.zErrMsg = sqlite3_mprintf(
"cannot find end of central directory record"
);
return SQLITE_ERROR;
}
@@ -1270,7 +1264,7 @@ static void zipfileAddEntry(
}
}
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
ZipfileEOCD eocd;
int rc;
int i;
@@ -1318,7 +1312,7 @@ static int zipfileFilter(
}else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
static const u8 aEmptyBlob = 0;
const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
i64 nBlob = sqlite3_value_bytes(argv[0]);
int nBlob = sqlite3_value_bytes(argv[0]);
assert( pTab->pFirstEntry==0 );
if( aBlob==0 ){
aBlob = &aEmptyBlob;
@@ -1392,7 +1386,7 @@ static int zipfileBestIndex(
static ZipfileEntry *zipfileNewEntry(const char *zPath){
ZipfileEntry *pNew;
pNew = sqlite3_malloc64(sizeof(ZipfileEntry));
pNew = sqlite3_malloc(sizeof(ZipfileEntry));
if( pNew ){
memset(pNew, 0, sizeof(ZipfileEntry));
pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
@@ -1516,7 +1510,7 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
assert( pTab->pWriteFd==0 );
if( pTab->zFile==0 || pTab->zFile[0]==0 ){
zipfileTableErr(pTab, "zipfile: missing filename");
pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename");
return SQLITE_ERROR;
}
@@ -1526,9 +1520,9 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
** in main-memory until the transaction is committed. */
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+");
if( pTab->pWriteFd==0 ){
zipfileTableErr(pTab,
"zipfile: failed to open file %s for writing", pTab->zFile
);
pTab->base.zErrMsg = sqlite3_mprintf(
"zipfile: failed to open file %s for writing", pTab->zFile
);
rc = SQLITE_ERROR;
}else{
fseek(pTab->pWriteFd, 0, SEEK_END);
@@ -1993,7 +1987,7 @@ struct ZipfileCtx {
ZipfileBuffer cds;
};
static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){
static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
if( pBuf->n+nByte>pBuf->nAlloc ){
u8 *aNew;
sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
@@ -2042,7 +2036,7 @@ static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
char *zName = 0; /* Path (name) of new entry */
int nName = 0; /* Size of zName in bytes */
char *zFree = 0; /* Free this before returning */
i64 nByte;
int nByte;
memset(&e, 0, sizeof(e));
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
-775
View File
@@ -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&nbsp;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 "&lt;", "&amp;", "&gt;", "&quot;", and "&#39;"
are displayed as "&amp;lt;", "&amp;amp;", "&amp;gt;", "&amp;quot;",
and "&amp;#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>&nbsp;<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` &rarr; The implementation, written in portable C99
* `qrf.h` &rarr; A header file defining interfaces
* `README.md` &rarr; This documentation
To use the SQLite result formatter, include the "`qrf.h`" header file
and link the application against the "`qrf.c`" source file.
-14
View File
@@ -1,14 +0,0 @@
# Developer Notes
## Measuring Test Coverage On Linux
On Mint Linux, as of 2025-12-02:
> ~~~
./configure --dev CFLAGS='-O0 -g -fprofile-arcs -ftest-coverage'
make clean testfixture
./testfixture test/qrf*.test
gcov -b -c testfixture-tclsqlite-ex.c
~~~
View results in tclsqlite-ex.c.gcov
-3010
View File
File diff suppressed because it is too large Load Diff
-201
View File
@@ -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) */
-28
View File
@@ -192,32 +192,4 @@ do_test 4.7.2 {
list [catch {rbu close} msg] $msg
} {1 {SQLITE_ERROR - rbu_state mismatch error}}
#-------------------------------------------------------------------------
# https://sqlite.org/forum/info/6d0a31e22a435877
#
reset_db
forcedelete rbu.db
do_execsql_test 5.0 {
CREATE TABLE t1(a BLOB);
INSERT INTO t1 VALUES(x'41');
}
sqlite3 dbRbu rbu.db
dbRbu eval {
CREATE TABLE data_t1(a, rbu_control, rbu_rowid);
INSERT INTO data_t1 VALUES(X'310a313a5a337e7e7e7e7e40302c303b','f',1);
}
dbRbu close
do_test 5.1 {
sqlite3rbu rbu test.db rbu.db
rbu step
} {SQLITE_ERROR}
do_test 5.2 {
list [catch {rbu close} msg] $msg
} {1 {SQLITE_ERROR - corrupt fossil delta}}
finish_test
+3 -3
View File
@@ -623,7 +623,7 @@ static int rbuDeltaApply(
/* ERROR: copy exceeds output file size */
return -1;
}
if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
if( (int)(ofst+cnt) > lenSrc ){
/* ERROR: copy extends past end of input */
return -1;
}
@@ -2269,8 +2269,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
/* If necessary, grow the pIter->aIdxCol[] array */
if( iIdxCol==nIdxAlloc ){
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc64(
pIter->aIdxCol, nIdxAlloc*sizeof(RbuSpan) + 16*sizeof(RbuSpan)
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
);
if( aIdxCol==0 ){
rc = SQLITE_NOMEM;
+16
View File
@@ -0,0 +1,16 @@
This folder contains extensions and utility programs intended to analyze
live database files, detect problems, and possibly fix them.
As SQLite is being used on larger and larger databases, database sizes
are growing into the terabyte range. At that size, hardware malfunctions
and/or cosmic rays will occasionally corrupt a database file. Detecting
problems and fixing errors a terabyte-sized databases can take hours or days,
and it is undesirable to take applications that depend on the databases
off-line for such a long time.
The utilities in the folder are intended to provide mechanisms for
detecting and fixing problems in large databases while those databases
are in active use.
The utilities and extensions in this folder are experimental and under
active development at the time of this writing (2017-10-12). If and when
they stabilize, this README will be updated to reflect that fact.
+310
View File
@@ -0,0 +1,310 @@
/*
** 2017 October 11
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This module exports a single C function:
**
** int sqlite3_check_freelist(sqlite3 *db, const char *zDb);
**
** This function checks the free-list in database zDb (one of "main",
** "temp", etc.) and reports any errors by invoking the sqlite3_log()
** function. It returns SQLITE_OK if successful, or an SQLite error
** code otherwise. It is not an error if the free-list is corrupted but
** no IO or OOM errors occur.
**
** If this file is compiled and loaded as an SQLite loadable extension,
** it adds an SQL function "checkfreelist" to the database handle, to
** be invoked as follows:
**
** SELECT checkfreelist(<database-name>);
**
** This function performs the same checks as sqlite3_check_freelist(),
** except that it returns all error messages as a single text value,
** separated by newline characters. If the freelist is not corrupted
** in any way, an empty string is returned.
**
** To compile this module for use as an SQLite loadable extension:
**
** gcc -Os -fPIC -shared checkfreelist.c -o checkfreelist.so
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
# endif
# if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
# elif !defined(NDEBUG)
# define ALWAYS(X) ((X)?1:(assert(0),0))
# define NEVER(X) ((X)?(assert(0),1):0)
# else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
# endif
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define get4byte(x) ( \
((u32)((x)[0])<<24) + \
((u32)((x)[1])<<16) + \
((u32)((x)[2])<<8) + \
((u32)((x)[3])) \
)
#endif
/*
** Execute a single PRAGMA statement and return the integer value returned
** via output parameter (*pnOut).
**
** The SQL statement passed as the third argument should be a printf-style
** format string containing a single "%s" which will be replace by the
** value passed as the second argument. e.g.
**
** sqlGetInteger(db, "main", "PRAGMA %s.page_count", pnOut)
**
** executes "PRAGMA main.page_count" and stores the results in (*pnOut).
*/
static int sqlGetInteger(
sqlite3 *db, /* Database handle */
const char *zDb, /* Database name ("main", "temp" etc.) */
const char *zFmt, /* SQL statement format */
u32 *pnOut /* OUT: Integer value */
){
int rc, rc2;
char *zSql;
sqlite3_stmt *pStmt = 0;
int bOk = 0;
zSql = sqlite3_mprintf(zFmt, zDb);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
}
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
*pnOut = (u32)sqlite3_column_int(pStmt, 0);
bOk = 1;
}
rc2 = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_ERROR;
return rc;
}
/*
** Argument zFmt must be a printf-style format string and must be
** followed by its required arguments. If argument pzOut is NULL,
** then the results of printf()ing the format string are passed to
** sqlite3_log(). Otherwise, they are appended to the string
** at (*pzOut).
*/
static int checkFreelistError(char **pzOut, const char *zFmt, ...){
int rc = SQLITE_OK;
char *zErr = 0;
va_list ap;
va_start(ap, zFmt);
zErr = sqlite3_vmprintf(zFmt, ap);
if( zErr==0 ){
rc = SQLITE_NOMEM;
}else{
if( pzOut ){
*pzOut = sqlite3_mprintf("%s%z%s", *pzOut?"\n":"", *pzOut, zErr);
if( *pzOut==0 ) rc = SQLITE_NOMEM;
}else{
sqlite3_log(SQLITE_ERROR, "checkfreelist: %s", zErr);
}
sqlite3_free(zErr);
}
va_end(ap);
return rc;
}
static int checkFreelist(
sqlite3 *db,
const char *zDb,
char **pzOut
){
/* This query returns one row for each page on the free list. Each row has
** two columns - the page number and page content. */
const char *zTrunk =
"WITH freelist_trunk(i, d, n) AS ("
"SELECT 1, NULL, sqlite_readint32(data, 32) "
"FROM sqlite_dbpage(:1) WHERE pgno=1 "
"UNION ALL "
"SELECT n, data, sqlite_readint32(data) "
"FROM freelist_trunk, sqlite_dbpage(:1) WHERE pgno=n "
")"
"SELECT i, d FROM freelist_trunk WHERE i!=1;";
int rc, rc2; /* Return code */
sqlite3_stmt *pTrunk = 0; /* Compilation of zTrunk */
u32 nPage = 0; /* Number of pages in db */
u32 nExpected = 0; /* Expected number of free pages */
u32 nFree = 0; /* Number of pages on free list */
if( zDb==0 ) zDb = "main";
if( (rc = sqlGetInteger(db, zDb, "PRAGMA %s.page_count", &nPage))
|| (rc = sqlGetInteger(db, zDb, "PRAGMA %s.freelist_count", &nExpected))
){
return rc;
}
rc = sqlite3_prepare_v2(db, zTrunk, -1, &pTrunk, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_text(pTrunk, 1, zDb, -1, SQLITE_STATIC);
while( rc==SQLITE_OK && sqlite3_step(pTrunk)==SQLITE_ROW ){
u32 i;
u32 iTrunk = (u32)sqlite3_column_int(pTrunk, 0);
const u8 *aData = (const u8*)sqlite3_column_blob(pTrunk, 1);
u32 nData = (u32)sqlite3_column_bytes(pTrunk, 1);
u32 iNext = get4byte(&aData[0]);
u32 nLeaf = get4byte(&aData[4]);
if( nLeaf>((nData/4)-2-6) ){
rc = checkFreelistError(pzOut,
"leaf count out of range (%d) on trunk page %d",
(int)nLeaf, (int)iTrunk
);
nLeaf = (nData/4) - 2 - 6;
}
nFree += 1+nLeaf;
if( iNext>nPage ){
rc = checkFreelistError(pzOut,
"trunk page %d is out of range", (int)iNext
);
}
for(i=0; rc==SQLITE_OK && i<nLeaf; i++){
u32 iLeaf = get4byte(&aData[8 + 4*i]);
if( iLeaf==0 || iLeaf>nPage ){
rc = checkFreelistError(pzOut,
"leaf page %d is out of range (child %d of trunk page %d)",
(int)iLeaf, (int)i, (int)iTrunk
);
}
}
}
if( rc==SQLITE_OK && nFree!=nExpected ){
rc = checkFreelistError(pzOut,
"free-list count mismatch: actual=%d header=%d",
(int)nFree, (int)nExpected
);
}
rc2 = sqlite3_finalize(pTrunk);
if( rc==SQLITE_OK ) rc = rc2;
return rc;
}
int sqlite3_check_freelist(sqlite3 *db, const char *zDb){
return checkFreelist(db, zDb, 0);
}
static void checkfreelist_function(
sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
const char *zDb;
int rc;
char *zOut = 0;
sqlite3 *db = sqlite3_context_db_handle(pCtx);
assert( nArg==1 );
zDb = (const char*)sqlite3_value_text(apArg[0]);
rc = checkFreelist(db, zDb, &zOut);
if( rc==SQLITE_OK ){
sqlite3_result_text(pCtx, zOut?zOut:"ok", -1, SQLITE_TRANSIENT);
}else{
sqlite3_result_error_code(pCtx, rc);
}
sqlite3_free(zOut);
}
/*
** An SQL function invoked as follows:
**
** sqlite_readint32(BLOB) -- Decode 32-bit integer from start of blob
*/
static void readint_function(
sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
const u8 *zBlob;
int nBlob;
int iOff = 0;
u32 iRet = 0;
if( nArg!=1 && nArg!=2 ){
sqlite3_result_error(
pCtx, "wrong number of arguments to function sqlite_readint32()", -1
);
return;
}
if( nArg==2 ){
iOff = sqlite3_value_int(apArg[1]);
}
zBlob = sqlite3_value_blob(apArg[0]);
nBlob = sqlite3_value_bytes(apArg[0]);
if( nBlob>=(iOff+4) ){
iRet = get4byte(&zBlob[iOff]);
}
sqlite3_result_int64(pCtx, (sqlite3_int64)iRet);
}
/*
** Register the SQL functions.
*/
static int cflRegister(sqlite3 *db){
int rc = sqlite3_create_function(
db, "sqlite_readint32", -1, SQLITE_UTF8, 0, readint_function, 0, 0
);
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3_create_function(
db, "checkfreelist", 1, SQLITE_UTF8, 0, checkfreelist_function, 0, 0
);
return rc;
}
/*
** Extension load function.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_checkfreelist_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
return cflRegister(db);
}
+929
View File
@@ -0,0 +1,929 @@
/*
** 2017 October 27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
/*
** Stuff that is available inside the amalgamation, but which we need to
** declare ourselves if this module is compiled separately.
*/
#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define get4byte(x) ( \
((u32)((x)[0])<<24) + \
((u32)((x)[1])<<16) + \
((u32)((x)[2])<<8) + \
((u32)((x)[3])) \
)
#endif
typedef struct CidxTable CidxTable;
typedef struct CidxCursor CidxCursor;
struct CidxTable {
sqlite3_vtab base; /* Base class. Must be first */
sqlite3 *db;
};
struct CidxCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
sqlite3_int64 iRowid; /* Row number of the output */
char *zIdxName; /* Copy of the index_name parameter */
char *zAfterKey; /* Copy of the after_key parameter */
sqlite3_stmt *pStmt; /* SQL statement that generates the output */
};
typedef struct CidxColumn CidxColumn;
struct CidxColumn {
char *zExpr; /* Text for indexed expression */
int bDesc; /* True for DESC columns, otherwise false */
int bKey; /* Part of index, not PK */
};
typedef struct CidxIndex CidxIndex;
struct CidxIndex {
char *zWhere; /* WHERE clause, if any */
int nCol; /* Elements in aCol[] array */
CidxColumn aCol[1]; /* Array of indexed columns */
};
static void *cidxMalloc(int *pRc, int n){
void *pRet = 0;
assert( n!=0 );
if( *pRc==SQLITE_OK ){
pRet = sqlite3_malloc(n);
if( pRet ){
memset(pRet, 0, n);
}else{
*pRc = SQLITE_NOMEM;
}
}
return pRet;
}
static void cidxCursorError(CidxCursor *pCsr, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
assert( pCsr->base.pVtab->zErrMsg==0 );
pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
/*
** Connect to the incremental_index_check virtual table.
*/
static int cidxConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
int rc = SQLITE_OK;
CidxTable *pRet;
#define IIC_ERRMSG 0
#define IIC_CURRENT_KEY 1
#define IIC_INDEX_NAME 2
#define IIC_AFTER_KEY 3
#define IIC_SCANNER_SQL 4
rc = sqlite3_declare_vtab(db,
"CREATE TABLE xyz("
" errmsg TEXT," /* Error message or NULL if everything is ok */
" current_key TEXT," /* SQLite quote() text of key values */
" index_name HIDDEN," /* IN: name of the index being scanned */
" after_key HIDDEN," /* IN: Start scanning after this key */
" scanner_sql HIDDEN" /* debugging info: SQL used for scanner */
")"
);
pRet = cidxMalloc(&rc, sizeof(CidxTable));
if( pRet ){
pRet->db = db;
}
*ppVtab = (sqlite3_vtab*)pRet;
return rc;
}
/*
** Disconnect from or destroy an incremental_index_check virtual table.
*/
static int cidxDisconnect(sqlite3_vtab *pVtab){
CidxTable *pTab = (CidxTable*)pVtab;
sqlite3_free(pTab);
return SQLITE_OK;
}
/*
** idxNum and idxStr are not used. There are only three possible plans,
** which are all distinguished by the number of parameters.
**
** No parameters: A degenerate plan. The result is zero rows.
** 1 Parameter: Scan all of the index starting with first entry
** 2 parameters: Scan the index starting after the "after_key".
**
** Provide successively smaller costs for each of these plans to encourage
** the query planner to select the one with the most parameters.
*/
static int cidxBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
int iIdxName = -1;
int iAfterKey = -1;
int i;
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
if( p->usable==0 ) continue;
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( p->iColumn==IIC_INDEX_NAME ){
iIdxName = i;
}
if( p->iColumn==IIC_AFTER_KEY ){
iAfterKey = i;
}
}
if( iIdxName<0 ){
pInfo->estimatedCost = 1000000000.0;
}else{
pInfo->aConstraintUsage[iIdxName].argvIndex = 1;
pInfo->aConstraintUsage[iIdxName].omit = 1;
if( iAfterKey<0 ){
pInfo->estimatedCost = 1000000.0;
}else{
pInfo->aConstraintUsage[iAfterKey].argvIndex = 2;
pInfo->aConstraintUsage[iAfterKey].omit = 1;
pInfo->estimatedCost = 1000.0;
}
}
return SQLITE_OK;
}
/*
** Open a new btreeinfo cursor.
*/
static int cidxOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
CidxCursor *pRet;
int rc = SQLITE_OK;
pRet = cidxMalloc(&rc, sizeof(CidxCursor));
*ppCursor = (sqlite3_vtab_cursor*)pRet;
return rc;
}
/*
** Close a btreeinfo cursor.
*/
static int cidxClose(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->zIdxName);
sqlite3_free(pCsr->zAfterKey);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** Move a btreeinfo cursor to the next entry in the file.
*/
static int cidxNext(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
int rc = sqlite3_step(pCsr->pStmt);
if( rc!=SQLITE_ROW ){
rc = sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
if( rc!=SQLITE_OK ){
sqlite3 *db = ((CidxTable*)pCsr->base.pVtab)->db;
cidxCursorError(pCsr, "Cursor error: %s", sqlite3_errmsg(db));
}
}else{
pCsr->iRowid++;
rc = SQLITE_OK;
}
return rc;
}
/* We have reached EOF if previous sqlite3_step() returned
** anything other than SQLITE_ROW;
*/
static int cidxEof(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
return pCsr->pStmt==0;
}
static char *cidxMprintf(int *pRc, const char *zFmt, ...){
char *zRet = 0;
va_list ap;
va_start(ap, zFmt);
zRet = sqlite3_vmprintf(zFmt, ap);
if( *pRc==SQLITE_OK ){
if( zRet==0 ){
*pRc = SQLITE_NOMEM;
}
}else{
sqlite3_free(zRet);
zRet = 0;
}
va_end(ap);
return zRet;
}
static sqlite3_stmt *cidxPrepare(
int *pRc, CidxCursor *pCsr, const char *zFmt, ...
){
sqlite3_stmt *pRet = 0;
char *zSql;
va_list ap; /* ... printf arguments */
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if( *pRc==SQLITE_OK ){
if( zSql==0 ){
*pRc = SQLITE_NOMEM;
}else{
sqlite3 *db = ((CidxTable*)pCsr->base.pVtab)->db;
*pRc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0);
if( *pRc!=SQLITE_OK ){
cidxCursorError(pCsr, "SQL error: %s", sqlite3_errmsg(db));
}
}
}
sqlite3_free(zSql);
va_end(ap);
return pRet;
}
static void cidxFinalize(int *pRc, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ) *pRc = rc;
}
char *cidxStrdup(int *pRc, const char *zStr){
char *zRet = 0;
if( *pRc==SQLITE_OK ){
int n = (int)strlen(zStr);
zRet = cidxMalloc(pRc, n+1);
if( zRet ) memcpy(zRet, zStr, n+1);
}
return zRet;
}
static void cidxFreeIndex(CidxIndex *pIdx){
if( pIdx ){
int i;
for(i=0; i<pIdx->nCol; i++){
sqlite3_free(pIdx->aCol[i].zExpr);
}
sqlite3_free(pIdx->zWhere);
sqlite3_free(pIdx);
}
}
static int cidx_isspace(char c){
return c==' ' || c=='\t' || c=='\r' || c=='\n';
}
static int cidx_isident(char c){
return c<0
|| (c>='0' && c<='9') || (c>='a' && c<='z')
|| (c>='A' && c<='Z') || c=='_';
}
#define CIDX_PARSE_EOF 0
#define CIDX_PARSE_COMMA 1 /* "," */
#define CIDX_PARSE_OPEN 2 /* "(" */
#define CIDX_PARSE_CLOSE 3 /* ")" */
/*
** Argument zIn points into the start, middle or end of a CREATE INDEX
** statement. If argument pbDoNotTrim is non-NULL, then this function
** scans the input until it finds EOF, a comma (",") or an open or
** close parenthesis character. It then sets (*pzOut) to point to said
** character and returns a CIDX_PARSE_XXX constant as appropriate. The
** parser is smart enough that special characters inside SQL strings
** or comments are not returned for.
**
** Or, if argument pbDoNotTrim is NULL, then this function sets *pzOut
** to point to the first character of the string that is not whitespace
** or part of an SQL comment and returns CIDX_PARSE_EOF.
**
** Additionally, if pbDoNotTrim is not NULL and the element immediately
** before (*pzOut) is an SQL comment of the form "-- comment", then
** (*pbDoNotTrim) is set before returning. In all other cases it is
** cleared.
*/
static int cidxFindNext(
const char *zIn,
const char **pzOut,
int *pbDoNotTrim /* OUT: True if prev is -- comment */
){
const char *z = zIn;
while( 1 ){
while( cidx_isspace(*z) ) z++;
if( z[0]=='-' && z[1]=='-' ){
z += 2;
while( z[0]!='\n' ){
if( z[0]=='\0' ) return CIDX_PARSE_EOF;
z++;
}
while( cidx_isspace(*z) ) z++;
if( pbDoNotTrim ) *pbDoNotTrim = 1;
}else
if( z[0]=='/' && z[1]=='*' ){
z += 2;
while( z[0]!='*' || z[1]!='/' ){
if( z[1]=='\0' ) return CIDX_PARSE_EOF;
z++;
}
z += 2;
}else{
*pzOut = z;
if( pbDoNotTrim==0 ) return CIDX_PARSE_EOF;
switch( *z ){
case '\0':
return CIDX_PARSE_EOF;
case '(':
return CIDX_PARSE_OPEN;
case ')':
return CIDX_PARSE_CLOSE;
case ',':
return CIDX_PARSE_COMMA;
case '"':
case '\'':
case '`': {
char q = *z;
z++;
while( *z ){
if( *z==q ){
z++;
if( *z!=q ) break;
}
z++;
}
break;
}
case '[':
while( *z++!=']' );
break;
default:
z++;
break;
}
*pbDoNotTrim = 0;
}
}
assert( 0 );
return -1;
}
static int cidxParseSQL(CidxCursor *pCsr, CidxIndex *pIdx, const char *zSql){
const char *z = zSql;
const char *z1;
int e;
int rc = SQLITE_OK;
int nParen = 1;
int bDoNotTrim = 0;
CidxColumn *pCol = pIdx->aCol;
e = cidxFindNext(z, &z, &bDoNotTrim);
if( e!=CIDX_PARSE_OPEN ) goto parse_error;
z1 = z+1;
z++;
while( nParen>0 ){
e = cidxFindNext(z, &z, &bDoNotTrim);
if( e==CIDX_PARSE_EOF ) goto parse_error;
if( (e==CIDX_PARSE_COMMA || e==CIDX_PARSE_CLOSE) && nParen==1 ){
const char *z2 = z;
if( pCol->zExpr ) goto parse_error;
if( bDoNotTrim==0 ){
while( cidx_isspace(z[-1]) ) z--;
if( !sqlite3_strnicmp(&z[-3], "asc", 3) && 0==cidx_isident(z[-4]) ){
z -= 3;
while( cidx_isspace(z[-1]) ) z--;
}else
if( !sqlite3_strnicmp(&z[-4], "desc", 4) && 0==cidx_isident(z[-5]) ){
z -= 4;
while( cidx_isspace(z[-1]) ) z--;
}
while( cidx_isspace(z1[0]) ) z1++;
}
pCol->zExpr = cidxMprintf(&rc, "%.*s", z-z1, z1);
pCol++;
z = z1 = z2+1;
}
if( e==CIDX_PARSE_OPEN ) nParen++;
if( e==CIDX_PARSE_CLOSE ) nParen--;
z++;
}
/* Search for a WHERE clause */
cidxFindNext(z, &z, 0);
if( 0==sqlite3_strnicmp(z, "where", 5) ){
pIdx->zWhere = cidxMprintf(&rc, "%s\n", &z[5]);
}else if( z[0]!='\0' ){
goto parse_error;
}
return rc;
parse_error:
cidxCursorError(pCsr, "Parse error in: %s", zSql);
return SQLITE_ERROR;
}
static int cidxLookupIndex(
CidxCursor *pCsr, /* Cursor object */
const char *zIdx, /* Name of index to look up */
CidxIndex **ppIdx, /* OUT: Description of columns */
char **pzTab /* OUT: Table name */
){
int rc = SQLITE_OK;
char *zTab = 0;
CidxIndex *pIdx = 0;
sqlite3_stmt *pFindTab = 0;
sqlite3_stmt *pInfo = 0;
/* Find the table for this index. */
pFindTab = cidxPrepare(&rc, pCsr,
"SELECT tbl_name, sql FROM sqlite_schema WHERE name=%Q AND type='index'",
zIdx
);
if( rc==SQLITE_OK && sqlite3_step(pFindTab)==SQLITE_ROW ){
const char *zSql = (const char*)sqlite3_column_text(pFindTab, 1);
zTab = cidxStrdup(&rc, (const char*)sqlite3_column_text(pFindTab, 0));
pInfo = cidxPrepare(&rc, pCsr, "PRAGMA index_xinfo(%Q)", zIdx);
if( rc==SQLITE_OK ){
int nAlloc = 0;
int iCol = 0;
while( sqlite3_step(pInfo)==SQLITE_ROW ){
const char *zName = (const char*)sqlite3_column_text(pInfo, 2);
const char *zColl = (const char*)sqlite3_column_text(pInfo, 4);
CidxColumn *p;
if( zName==0 ) zName = "rowid";
if( iCol==nAlloc ){
int nByte = sizeof(CidxIndex) + sizeof(CidxColumn)*(nAlloc+8);
pIdx = (CidxIndex*)sqlite3_realloc(pIdx, nByte);
nAlloc += 8;
}
p = &pIdx->aCol[iCol++];
p->bDesc = sqlite3_column_int(pInfo, 3);
p->bKey = sqlite3_column_int(pInfo, 5);
if( zSql==0 || p->bKey==0 ){
p->zExpr = cidxMprintf(&rc, "\"%w\" COLLATE %s",zName,zColl);
}else{
p->zExpr = 0;
}
pIdx->nCol = iCol;
pIdx->zWhere = 0;
}
cidxFinalize(&rc, pInfo);
}
if( rc==SQLITE_OK && zSql ){
rc = cidxParseSQL(pCsr, pIdx, zSql);
}
}
cidxFinalize(&rc, pFindTab);
if( rc==SQLITE_OK && zTab==0 ){
rc = SQLITE_ERROR;
}
if( rc!=SQLITE_OK ){
sqlite3_free(zTab);
cidxFreeIndex(pIdx);
}else{
*pzTab = zTab;
*ppIdx = pIdx;
}
return rc;
}
static int cidxDecodeAfter(
CidxCursor *pCsr,
int nCol,
const char *zAfterKey,
char ***pazAfter
){
char **azAfter;
int rc = SQLITE_OK;
int nAfterKey = (int)strlen(zAfterKey);
azAfter = cidxMalloc(&rc, sizeof(char*)*nCol + nAfterKey+1);
if( rc==SQLITE_OK ){
int i;
char *zCopy = (char*)&azAfter[nCol];
char *p = zCopy;
memcpy(zCopy, zAfterKey, nAfterKey+1);
for(i=0; i<nCol; i++){
while( *p==' ' ) p++;
/* Check NULL values */
if( *p=='N' ){
if( memcmp(p, "NULL", 4) ) goto parse_error;
p += 4;
}
/* Check strings and blob literals */
else if( *p=='X' || *p=='\'' ){
azAfter[i] = p;
if( *p=='X' ) p++;
if( *p!='\'' ) goto parse_error;
p++;
while( 1 ){
if( *p=='\0' ) goto parse_error;
if( *p=='\'' ){
p++;
if( *p!='\'' ) break;
}
p++;
}
}
/* Check numbers */
else{
azAfter[i] = p;
while( (*p>='0' && *p<='9')
|| *p=='.' || *p=='+' || *p=='-' || *p=='e' || *p=='E'
){
p++;
}
}
while( *p==' ' ) p++;
if( *p!=(i==(nCol-1) ? '\0' : ',') ){
goto parse_error;
}
*p++ = '\0';
}
}
*pazAfter = azAfter;
return rc;
parse_error:
sqlite3_free(azAfter);
*pazAfter = 0;
cidxCursorError(pCsr, "%s", "error parsing after value");
return SQLITE_ERROR;
}
static char *cidxWhere(
int *pRc, CidxColumn *aCol, char **azAfter, int iGt, int bLastIsNull
){
char *zRet = 0;
const char *zSep = "";
int i;
for(i=0; i<iGt; i++){
zRet = cidxMprintf(pRc, "%z%s(%s) IS %s", zRet,
zSep, aCol[i].zExpr, (azAfter[i] ? azAfter[i] : "NULL")
);
zSep = " AND ";
}
if( bLastIsNull ){
zRet = cidxMprintf(pRc, "%z%s(%s) IS NULL", zRet, zSep, aCol[iGt].zExpr);
}
else if( azAfter[iGt] ){
zRet = cidxMprintf(pRc, "%z%s(%s) %s %s", zRet,
zSep, aCol[iGt].zExpr, (aCol[iGt].bDesc ? "<" : ">"),
azAfter[iGt]
);
}else{
zRet = cidxMprintf(pRc, "%z%s(%s) IS NOT NULL", zRet, zSep,aCol[iGt].zExpr);
}
return zRet;
}
#define CIDX_CLIST_ALL 0
#define CIDX_CLIST_ORDERBY 1
#define CIDX_CLIST_CURRENT_KEY 2
#define CIDX_CLIST_SUBWHERE 3
#define CIDX_CLIST_SUBEXPR 4
/*
** This function returns various strings based on the contents of the
** CidxIndex structure and the eType parameter.
*/
static char *cidxColumnList(
int *pRc, /* IN/OUT: Error code */
const char *zIdx,
CidxIndex *pIdx, /* Indexed columns */
int eType /* True to include ASC/DESC */
){
char *zRet = 0;
if( *pRc==SQLITE_OK ){
const char *aDir[2] = {"", " DESC"};
int i;
const char *zSep = "";
for(i=0; i<pIdx->nCol; i++){
CidxColumn *p = &pIdx->aCol[i];
assert( pIdx->aCol[i].bDesc==0 || pIdx->aCol[i].bDesc==1 );
switch( eType ){
case CIDX_CLIST_ORDERBY:
zRet = cidxMprintf(pRc, "%z%s%d%s", zRet, zSep, i+1, aDir[p->bDesc]);
zSep = ",";
break;
case CIDX_CLIST_CURRENT_KEY:
zRet = cidxMprintf(pRc, "%z%squote(i%d)", zRet, zSep, i);
zSep = "||','||";
break;
case CIDX_CLIST_SUBWHERE:
if( p->bKey==0 ){
zRet = cidxMprintf(pRc, "%z%s%s IS i.i%d", zRet,
zSep, p->zExpr, i
);
zSep = " AND ";
}
break;
case CIDX_CLIST_SUBEXPR:
if( p->bKey==1 ){
zRet = cidxMprintf(pRc, "%z%s%s IS i.i%d", zRet,
zSep, p->zExpr, i
);
zSep = " AND ";
}
break;
default:
assert( eType==CIDX_CLIST_ALL );
zRet = cidxMprintf(pRc, "%z%s(%s) AS i%d", zRet, zSep, p->zExpr, i);
zSep = ", ";
break;
}
}
}
return zRet;
}
/*
** Generate SQL (in memory obtained from sqlite3_malloc()) that will
** continue the index scan for zIdxName starting after zAfterKey.
*/
int cidxGenerateScanSql(
CidxCursor *pCsr, /* The cursor which needs the new statement */
const char *zIdxName, /* index to be scanned */
const char *zAfterKey, /* start after this key, if not NULL */
char **pzSqlOut /* OUT: Write the generated SQL here */
){
int rc;
char *zTab = 0;
char *zCurrentKey = 0;
char *zOrderBy = 0;
char *zSubWhere = 0;
char *zSubExpr = 0;
char *zSrcList = 0;
char **azAfter = 0;
CidxIndex *pIdx = 0;
*pzSqlOut = 0;
rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);
zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);
if( rc==SQLITE_OK && zAfterKey ){
rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
}
if( rc==SQLITE_OK ){
if( zAfterKey==0 ){
*pzSqlOut = cidxMprintf(&rc,
"SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
"FROM (SELECT %s FROM %Q INDEXED BY %Q %s%sORDER BY %s) AS i",
zSubExpr, zTab, zSubWhere, zCurrentKey,
zSrcList, zTab, zIdxName,
(pIdx->zWhere ? "WHERE " : ""), (pIdx->zWhere ? pIdx->zWhere : ""),
zOrderBy
);
}else{
const char *zSep = "";
char *zSql;
int i;
zSql = cidxMprintf(&rc,
"SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
zSubExpr, zTab, zSubWhere, zCurrentKey
);
for(i=pIdx->nCol-1; i>=0; i--){
int j;
if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
for(j=0; j<2; j++){
char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
zSql = cidxMprintf(&rc, "%z"
"%sSELECT * FROM ("
"SELECT %s FROM %Q INDEXED BY %Q WHERE %s%s%z ORDER BY %s"
")",
zSql, zSep, zSrcList, zTab, zIdxName,
pIdx->zWhere ? pIdx->zWhere : "",
pIdx->zWhere ? " AND " : "",
zWhere, zOrderBy
);
zSep = " UNION ALL ";
if( pIdx->aCol[i].bDesc==0 ) break;
}
}
*pzSqlOut = cidxMprintf(&rc, "%z) AS i", zSql);
}
}
sqlite3_free(zTab);
sqlite3_free(zCurrentKey);
sqlite3_free(zOrderBy);
sqlite3_free(zSubWhere);
sqlite3_free(zSubExpr);
sqlite3_free(zSrcList);
cidxFreeIndex(pIdx);
sqlite3_free(azAfter);
return rc;
}
/*
** Position a cursor back to the beginning.
*/
static int cidxFilter(
sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
int rc = SQLITE_OK;
CidxCursor *pCsr = (CidxCursor*)pCursor;
const char *zIdxName = 0;
const char *zAfterKey = 0;
sqlite3_free(pCsr->zIdxName);
pCsr->zIdxName = 0;
sqlite3_free(pCsr->zAfterKey);
pCsr->zAfterKey = 0;
sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
if( argc>0 ){
zIdxName = (const char*)sqlite3_value_text(argv[0]);
if( argc>1 ){
zAfterKey = (const char*)sqlite3_value_text(argv[1]);
}
}
if( zIdxName ){
char *zSql = 0;
pCsr->zIdxName = sqlite3_mprintf("%s", zIdxName);
pCsr->zAfterKey = zAfterKey ? sqlite3_mprintf("%s", zAfterKey) : 0;
rc = cidxGenerateScanSql(pCsr, zIdxName, zAfterKey, &zSql);
if( zSql ){
pCsr->pStmt = cidxPrepare(&rc, pCsr, "%z", zSql);
}
}
if( pCsr->pStmt ){
assert( rc==SQLITE_OK );
rc = cidxNext(pCursor);
}
pCsr->iRowid = 1;
return rc;
}
/*
** Return a column value.
*/
static int cidxColumn(
sqlite3_vtab_cursor *pCursor,
sqlite3_context *ctx,
int iCol
){
CidxCursor *pCsr = (CidxCursor*)pCursor;
assert( iCol>=IIC_ERRMSG && iCol<=IIC_SCANNER_SQL );
switch( iCol ){
case IIC_ERRMSG: {
const char *zVal = 0;
if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
zVal = "row data mismatch";
}
}else{
zVal = "row missing";
}
sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);
break;
}
case IIC_CURRENT_KEY: {
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));
break;
}
case IIC_INDEX_NAME: {
sqlite3_result_text(ctx, pCsr->zIdxName, -1, SQLITE_TRANSIENT);
break;
}
case IIC_AFTER_KEY: {
sqlite3_result_text(ctx, pCsr->zAfterKey, -1, SQLITE_TRANSIENT);
break;
}
case IIC_SCANNER_SQL: {
char *zSql = 0;
cidxGenerateScanSql(pCsr, pCsr->zIdxName, pCsr->zAfterKey, &zSql);
sqlite3_result_text(ctx, zSql, -1, sqlite3_free);
break;
}
}
return SQLITE_OK;
}
/* Return the ROWID for the sqlite_btreeinfo table */
static int cidxRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
CidxCursor *pCsr = (CidxCursor*)pCursor;
*pRowid = pCsr->iRowid;
return SQLITE_OK;
}
/*
** Register the virtual table modules with the database handle passed
** as the only argument.
*/
static int ciInit(sqlite3 *db){
static sqlite3_module cidx_module = {
0, /* iVersion */
0, /* xCreate */
cidxConnect, /* xConnect */
cidxBestIndex, /* xBestIndex */
cidxDisconnect, /* xDisconnect */
0, /* xDestroy */
cidxOpen, /* xOpen - open a cursor */
cidxClose, /* xClose - close a cursor */
cidxFilter, /* xFilter - configure scan constraints */
cidxNext, /* xNext - advance a cursor */
cidxEof, /* xEof - check for end of scan */
cidxColumn, /* xColumn - read data */
cidxRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
return sqlite3_create_module(db, "incremental_index_check", &cidx_module, 0);
}
/*
** Extension load function.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_checkindex_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
return ciInit(db);
}
+85
View File
@@ -0,0 +1,85 @@
/*
** Read an SQLite database file and analyze its space utilization. Generate
** text on standard output.
*/
#define TCLSH_INIT_PROC sqlite3_checker_init_proc
#define SQLITE_ENABLE_DBPAGE_VTAB 1
#undef SQLITE_THREADSAFE
#define SQLITE_THREADSAFE 0
#undef SQLITE_ENABLE_COLUMN_METADATA
#define SQLITE_OMIT_DECLTYPE 1
#define SQLITE_OMIT_DEPRECATED 1
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
#define SQLITE_OMIT_SHARED_CACHE 1
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_MAX_EXPR_DEPTH 0
INCLUDE sqlite3.c
INCLUDE $ROOT/src/tclsqlite.c
INCLUDE $ROOT/ext/misc/btreeinfo.c
INCLUDE $ROOT/ext/repair/checkindex.c
INCLUDE $ROOT/ext/repair/checkfreelist.c
/*
** Decode a pointer to an sqlite3 object.
*/
int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
struct SqliteDb *p;
Tcl_CmdInfo cmdInfo;
if( Tcl_GetCommandInfo(interp, zA, &cmdInfo) ){
p = (struct SqliteDb*)cmdInfo.objClientData;
*ppDb = p->db;
return TCL_OK;
}else{
*ppDb = 0;
return TCL_ERROR;
}
return TCL_OK;
}
/*
** sqlite3_imposter db main rootpage {CREATE TABLE...} ;# setup an imposter
** sqlite3_imposter db main ;# rm all imposters
*/
static int sqlite3_imposter(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
const char *zSchema;
int iRoot;
const char *zSql;
if( objc!=3 && objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB SCHEMA [ROOTPAGE SQL]");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zSchema = Tcl_GetString(objv[2]);
if( objc==3 ){
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 0, 1);
}else{
if( Tcl_GetIntFromObj(interp, objv[3], &iRoot) ) return TCL_ERROR;
zSql = Tcl_GetString(objv[4]);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 1, iRoot);
sqlite3_exec(db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 0, 0);
}
return TCL_OK;
}
#include <stdio.h>
const char *sqlite3_checker_init_proc(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3_imposter",
(Tcl_ObjCmdProc*)sqlite3_imposter, 0, 0);
sqlite3_auto_extension((void(*)(void))sqlite3_btreeinfo_init);
sqlite3_auto_extension((void(*)(void))sqlite3_checkindex_init);
sqlite3_auto_extension((void(*)(void))sqlite3_checkfreelist_init);
return
BEGIN_STRING
INCLUDE $ROOT/ext/repair/sqlite3_checker.tcl
END_STRING
;
}
+264
View File
@@ -0,0 +1,264 @@
# This TCL script is the main driver script for the sqlite3_checker utility
# program.
#
# Special case:
#
# sqlite3_checker --test FILENAME ARGS
#
# uses FILENAME in place of this script.
#
if {[lindex $argv 0]=="--test" && [llength $argv]>1} {
set ::argv0 [lindex $argv 1]
set argv [lrange $argv 2 end]
source $argv0
exit 0
}
# Emulate a TCL shell
#
proc tclsh {} {
set line {}
while {![eof stdin]} {
if {$line!=""} {
puts -nonewline "> "
} else {
puts -nonewline "% "
}
flush stdout
append line [gets stdin]
if {[info complete $line]} {
if {[catch {uplevel #0 $line} result]} {
puts stderr "Error: $result"
} elseif {$result!=""} {
puts $result
}
set line {}
} else {
append line \n
}
}
}
# Do an incremental integrity check of a single index
#
proc check_index {idxname batchsize bTrace} {
set i 0
set more 1
set nerr 0
set pct 00.0
set max [db one {SELECT nEntry FROM sqlite_btreeinfo('main')
WHERE name=$idxname}]
puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
flush stdout
if {$bTrace} {
set sql {SELECT errmsg, current_key AS key,
CASE WHEN rowid=1 THEN scanner_sql END AS traceOut
FROM incremental_index_check($idxname)
WHERE after_key=$key
LIMIT $batchsize}
} else {
set sql {SELECT errmsg, current_key AS key, NULL AS traceOut
FROM incremental_index_check($idxname)
WHERE after_key=$key
LIMIT $batchsize}
}
while {$more} {
set more 0
db eval $sql {
set more 1
if {$errmsg!=""} {
incr nerr
puts "$idxname: key($key): $errmsg"
} elseif {$traceOut!=""} {
puts "$idxname: $traceOut"
}
incr i
}
set x [format {%.1f} [expr {($i*100.0)/$max}]]
if {$x!=$pct} {
puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
flush stdout
set pct $x
}
}
puts "$idxname: $nerr errors out of $i entries"
}
# Print a usage message on standard error, then quit.
#
proc usage {} {
set argv0 [file rootname [file tail [info nameofexecutable]]]
puts stderr "Usage: $argv0 OPTIONS database-filename"
puts stderr {
Do sanity checking on a live SQLite3 database file specified by the
"database-filename" argument.
Options:
--batchsize N Number of rows to check per transaction
--freelist Perform a freelist check
--index NAME Run a check of the index NAME
--summary Print summary information about the database
--table NAME Run a check of all indexes for table NAME
--tclsh Run the built-in TCL interpreter (for debugging)
--trace (Debugging only:) Output trace information on the scan
--version Show the version number of SQLite
}
exit 1
}
set file_to_analyze {}
append argv {}
set bFreelistCheck 0
set bSummary 0
set zIndex {}
set zTable {}
set batchsize 1000
set bAll 1
set bTrace 0
set argc [llength $argv]
for {set i 0} {$i<$argc} {incr i} {
set arg [lindex $argv $i]
if {[regexp {^-+tclsh$} $arg]} {
tclsh
exit 0
}
if {[regexp {^-+version$} $arg]} {
sqlite3 mem :memory:
puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
mem close
exit 0
}
if {[regexp {^-+freelist$} $arg]} {
set bFreelistCheck 1
set bAll 0
continue
}
if {[regexp {^-+summary$} $arg]} {
set bSummary 1
set bAll 0
continue
}
if {[regexp {^-+trace$} $arg]} {
set bTrace 1
continue
}
if {[regexp {^-+batchsize$} $arg]} {
incr i
if {$i>=$argc} {
puts stderr "missing argument on $arg"
exit 1
}
set batchsize [lindex $argv $i]
continue
}
if {[regexp {^-+index$} $arg]} {
incr i
if {$i>=$argc} {
puts stderr "missing argument on $arg"
exit 1
}
set zIndex [lindex $argv $i]
set bAll 0
continue
}
if {[regexp {^-+table$} $arg]} {
incr i
if {$i>=$argc} {
puts stderr "missing argument on $arg"
exit 1
}
set zTable [lindex $argv $i]
set bAll 0
continue
}
if {[regexp {^-} $arg]} {
puts stderr "Unknown option: $arg"
usage
}
if {$file_to_analyze!=""} {
usage
} else {
set file_to_analyze $arg
}
}
if {$file_to_analyze==""} usage
# If a TCL script is specified on the command-line, then run that
# script.
#
if {[file extension $file_to_analyze]==".tcl"} {
source $file_to_analyze
exit 0
}
set root_filename $file_to_analyze
regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
if {![file exists $root_filename]} {
puts stderr "No such file: $root_filename"
exit 1
}
if {![file readable $root_filename]} {
puts stderr "File is not readable: $root_filename"
exit 1
}
if {[catch {sqlite3 db $file_to_analyze} res]} {
puts stderr "Cannot open datababase $root_filename: $res"
exit 1
}
if {$bFreelistCheck || $bAll} {
puts -nonewline "freelist-check: "
flush stdout
db eval BEGIN
puts [db one {SELECT checkfreelist('main')}]
db eval END
}
if {$bSummary} {
set scale 0
set pgsz [db one {PRAGMA page_size}]
db eval {SELECT nPage*$pgsz AS sz, name, tbl_name
FROM sqlite_btreeinfo
WHERE type='index'
ORDER BY 1 DESC, name} {
if {$scale==0} {
if {$sz>10000000} {
set scale 1000000.0
set unit MB
} else {
set scale 1000.0
set unit KB
}
}
puts [format {%7.1f %s index %s of table %s} \
[expr {$sz/$scale}] $unit $name $tbl_name]
}
}
if {$zIndex!=""} {
check_index $zIndex $batchsize $bTrace
}
if {$zTable!=""} {
foreach idx [db eval {SELECT name FROM sqlite_master
WHERE type='index' AND rootpage>0
AND tbl_name=$zTable}] {
check_index $idx $batchsize $bTrace
}
}
if {$bAll} {
set allidx [db eval {SELECT name FROM sqlite_btreeinfo('main')
WHERE type='index' AND rootpage>0
ORDER BY nEntry}]
foreach idx $allidx {
check_index $idx $batchsize $bTrace
}
}
+13
View File
@@ -0,0 +1,13 @@
To run these tests, first build sqlite3_checker:
> make sqlite3_checker
Then run the "test.tcl" script using:
> ./sqlite3_checker --test $path/test.tcl
Optionally add the full pathnames of individual *.test modules
+92
View File
@@ -0,0 +1,92 @@
# 2017-10-11
set testprefix checkfreelist
do_execsql_test 1.0 {
PRAGMA page_size=1024;
CREATE TABLE t1(a, b);
}
do_execsql_test 1.2 { SELECT checkfreelist('main') } {ok}
do_execsql_test 1.3 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
DELETE FROM t1 WHERE rowid%3;
PRAGMA freelist_count;
} {6726}
do_execsql_test 1.4 { SELECT checkfreelist('main') } {ok}
do_execsql_test 1.5 {
WITH freelist_trunk(i, d, n) AS (
SELECT 1, NULL, sqlite_readint32(data, 32) FROM sqlite_dbpage WHERE pgno=1
UNION ALL
SELECT n, data, sqlite_readint32(data)
FROM freelist_trunk, sqlite_dbpage WHERE pgno=n
)
SELECT i FROM freelist_trunk WHERE i!=1;
} {
10009 9715 9343 8969 8595 8222 7847 7474 7102 6727 6354 5982 5608 5234
4860 4487 4112 3740 3367 2992 2619 2247 1872 1499 1125 752 377 5
}
do_execsql_test 1.6 { SELECT checkfreelist('main') } {ok}
proc set_int {blob idx newval} {
binary scan $blob I* ints
lset ints $idx $newval
binary format I* $ints
}
db func set_int set_int
proc get_int {blob idx} {
binary scan $blob I* ints
lindex $ints $idx
}
db func get_int get_int
do_execsql_test 1.7 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 1, get_int(data, 1)-1)
WHERE pgno=4860;
SELECT checkfreelist('main');
ROLLBACK;
} {{free-list count mismatch: actual=6725 header=6726}}
do_execsql_test 1.8 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 5, (SELECT * FROM pragma_page_count)+1)
WHERE pgno=4860;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf page 10092 is out of range (child 3 of trunk page 4860)}}
do_execsql_test 1.9 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 5, 0)
WHERE pgno=4860;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf page 0 is out of range (child 3 of trunk page 4860)}}
do_execsql_test 1.10 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, get_int(data, 1)+1, 0)
WHERE pgno=5;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf page 0 is out of range (child 247 of trunk page 5)}}
do_execsql_test 1.11 {
BEGIN;
UPDATE sqlite_dbpage
SET data = set_int(data, 1, 249)
WHERE pgno=5;
SELECT checkfreelist('main');
ROLLBACK;
} {{leaf count out of range (249) on trunk page 5}}
+349
View File
@@ -0,0 +1,349 @@
# 2017-10-11
#
set testprefix checkindex
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
INSERT INTO t1 VALUES('one', 2);
INSERT INTO t1 VALUES('two', 4);
INSERT INTO t1 VALUES('three', 6);
INSERT INTO t1 VALUES('four', 8);
INSERT INTO t1 VALUES('five', 10);
CREATE INDEX i2 ON t1(a DESC);
} {}
proc incr_index_check {idx nStep} {
set Q {
SELECT errmsg, current_key FROM incremental_index_check($idx, $after)
LIMIT $nStep
}
set res [list]
while {1} {
unset -nocomplain current_key
set res1 [db eval $Q]
if {[llength $res1]==0} break
set res [concat $res $res1]
set after [lindex $res end]
}
return $res
}
proc do_index_check_test {tn idx res} {
uplevel [list do_execsql_test $tn.1 "
SELECT errmsg, current_key FROM incremental_index_check('$idx');
" $res]
uplevel [list do_test $tn.2 "incr_index_check $idx 1" [list {*}$res]]
uplevel [list do_test $tn.3 "incr_index_check $idx 2" [list {*}$res]]
uplevel [list do_test $tn.4 "incr_index_check $idx 5" [list {*}$res]]
}
do_execsql_test 1.2.1 {
SELECT rowid, errmsg IS NULL, current_key FROM incremental_index_check('i1');
} {
1 1 'five',5
2 1 'four',4
3 1 'one',1
4 1 'three',3
5 1 'two',2
}
do_execsql_test 1.2.2 {
SELECT errmsg IS NULL, current_key, index_name, after_key, scanner_sql
FROM incremental_index_check('i1') LIMIT 1;
} {
1
'five',5
i1
{}
{SELECT (SELECT a IS i.i0 FROM 't1' AS t WHERE "rowid" COLLATE BINARY IS i.i1), quote(i0)||','||quote(i1) FROM (SELECT (a) AS i0, ("rowid" COLLATE BINARY) AS i1 FROM 't1' INDEXED BY 'i1' ORDER BY 1,2) AS i}
}
do_index_check_test 1.3 i1 {
{} 'five',5
{} 'four',4
{} 'one',1
{} 'three',3
{} 'two',2
}
do_index_check_test 1.4 i2 {
{} 'two',2
{} 'three',3
{} 'one',1
{} 'four',4
{} 'five',5
}
do_test 1.5 {
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t1' }]
sqlite3_imposter db main $tblroot {CREATE TABLE xt1(a,b)}
db eval {
UPDATE xt1 SET a='six' WHERE rowid=3;
DELETE FROM xt1 WHERE rowid = 5;
}
sqlite3_imposter db main
} {}
do_index_check_test 1.6 i1 {
{row missing} 'five',5
{} 'four',4
{} 'one',1
{row data mismatch} 'three',3
{} 'two',2
}
do_index_check_test 1.7 i2 {
{} 'two',2
{row data mismatch} 'three',3
{} 'one',1
{} 'four',4
{row missing} 'five',5
}
#--------------------------------------------------------------------------
do_execsql_test 2.0 {
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c, d);
INSERT INTO t2 VALUES(1, NULL, 1, 1);
INSERT INTO t2 VALUES(2, 1, NULL, 1);
INSERT INTO t2 VALUES(3, 1, 1, NULL);
INSERT INTO t2 VALUES(4, 2, 2, 1);
INSERT INTO t2 VALUES(5, 2, 2, 2);
INSERT INTO t2 VALUES(6, 2, 2, 3);
INSERT INTO t2 VALUES(7, 2, 2, 1);
INSERT INTO t2 VALUES(8, 2, 2, 2);
INSERT INTO t2 VALUES(9, 2, 2, 3);
CREATE INDEX i3 ON t2(b, c, d);
CREATE INDEX i4 ON t2(b DESC, c DESC, d DESC);
CREATE INDEX i5 ON t2(d, c DESC, b);
} {}
do_index_check_test 2.1 i3 {
{} NULL,1,1,1
{} 1,NULL,1,2
{} 1,1,NULL,3
{} 2,2,1,4
{} 2,2,1,7
{} 2,2,2,5
{} 2,2,2,8
{} 2,2,3,6
{} 2,2,3,9
}
do_index_check_test 2.2 i4 {
{} 2,2,3,6
{} 2,2,3,9
{} 2,2,2,5
{} 2,2,2,8
{} 2,2,1,4
{} 2,2,1,7
{} 1,1,NULL,3
{} 1,NULL,1,2
{} NULL,1,1,1
}
do_index_check_test 2.3 i5 {
{} NULL,1,1,3
{} 1,2,2,4
{} 1,2,2,7
{} 1,1,NULL,1
{} 1,NULL,1,2
{} 2,2,2,5
{} 2,2,2,8
{} 3,2,2,6
{} 3,2,2,9
}
#--------------------------------------------------------------------------
do_execsql_test 3.0 {
CREATE TABLE t3(w, x, y, z PRIMARY KEY) WITHOUT ROWID;
CREATE INDEX t3wxy ON t3(w, x, y);
CREATE INDEX t3wxy2 ON t3(w DESC, x DESC, y DESC);
INSERT INTO t3 VALUES(NULL, NULL, NULL, 1);
INSERT INTO t3 VALUES(NULL, NULL, NULL, 2);
INSERT INTO t3 VALUES(NULL, NULL, NULL, 3);
INSERT INTO t3 VALUES('a', NULL, NULL, 4);
INSERT INTO t3 VALUES('a', NULL, NULL, 5);
INSERT INTO t3 VALUES('a', NULL, NULL, 6);
INSERT INTO t3 VALUES('a', 'b', NULL, 7);
INSERT INTO t3 VALUES('a', 'b', NULL, 8);
INSERT INTO t3 VALUES('a', 'b', NULL, 9);
} {}
do_index_check_test 3.1 t3wxy {
{} NULL,NULL,NULL,1 {} NULL,NULL,NULL,2 {} NULL,NULL,NULL,3
{} 'a',NULL,NULL,4 {} 'a',NULL,NULL,5 {} 'a',NULL,NULL,6
{} 'a','b',NULL,7 {} 'a','b',NULL,8 {} 'a','b',NULL,9
}
do_index_check_test 3.2 t3wxy2 {
{} 'a','b',NULL,7 {} 'a','b',NULL,8 {} 'a','b',NULL,9
{} 'a',NULL,NULL,4 {} 'a',NULL,NULL,5 {} 'a',NULL,NULL,6
{} NULL,NULL,NULL,1 {} NULL,NULL,NULL,2 {} NULL,NULL,NULL,3
}
#--------------------------------------------------------------------------
# Test with an index that uses non-default collation sequences.
#
do_execsql_test 4.0 {
CREATE TABLE t4(a INTEGER PRIMARY KEY, c1 TEXT, c2 TEXT);
INSERT INTO t4 VALUES(1, 'aaa', 'bbb');
INSERT INTO t4 VALUES(2, 'AAA', 'CCC');
INSERT INTO t4 VALUES(3, 'aab', 'ddd');
INSERT INTO t4 VALUES(4, 'AAB', 'EEE');
CREATE INDEX t4cc ON t4(c1 COLLATE nocase, c2 COLLATE nocase);
}
do_index_check_test 4.1 t4cc {
{} 'aaa','bbb',1
{} 'AAA','CCC',2
{} 'aab','ddd',3
{} 'AAB','EEE',4
}
do_test 4.2 {
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t4' }]
sqlite3_imposter db main $tblroot \
{CREATE TABLE xt4(a INTEGER PRIMARY KEY, c1 TEXT, c2 TEXT)}
db eval {
UPDATE xt4 SET c1='hello' WHERE rowid=2;
DELETE FROM xt4 WHERE rowid = 3;
}
sqlite3_imposter db main
} {}
do_index_check_test 4.3 t4cc {
{} 'aaa','bbb',1
{row data mismatch} 'AAA','CCC',2
{row missing} 'aab','ddd',3
{} 'AAB','EEE',4
}
#--------------------------------------------------------------------------
# Test an index on an expression.
#
do_execsql_test 5.0 {
CREATE TABLE t5(x INTEGER PRIMARY KEY, y TEXT, UNIQUE(y));
INSERT INTO t5 VALUES(1, '{"x":1, "y":1}');
INSERT INTO t5 VALUES(2, '{"x":2, "y":2}');
INSERT INTO t5 VALUES(3, '{"x":3, "y":3}');
INSERT INTO t5 VALUES(4, '{"w":4, "z":4}');
INSERT INTO t5 VALUES(5, '{"x":5, "y":5}');
CREATE INDEX t5x ON t5( json_extract(y, '$.x') );
CREATE INDEX t5y ON t5( json_extract(y, '$.y') DESC );
}
do_index_check_test 5.1.1 t5x {
{} NULL,4 {} 1,1 {} 2,2 {} 3,3 {} 5,5
}
do_index_check_test 5.1.2 t5y {
{} 5,5 {} 3,3 {} 2,2 {} 1,1 {} NULL,4
}
do_index_check_test 5.1.3 sqlite_autoindex_t5_1 {
{} {'{"w":4, "z":4}',4}
{} {'{"x":1, "y":1}',1}
{} {'{"x":2, "y":2}',2}
{} {'{"x":3, "y":3}',3}
{} {'{"x":5, "y":5}',5}
}
do_test 5.2 {
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t5' }]
sqlite3_imposter db main $tblroot \
{CREATE TABLE xt5(a INTEGER PRIMARY KEY, c1 TEXT);}
db eval {
UPDATE xt5 SET c1='{"x":22, "y":11}' WHERE rowid=1;
DELETE FROM xt5 WHERE rowid = 4;
}
sqlite3_imposter db main
} {}
do_index_check_test 5.3.1 t5x {
{row missing} NULL,4
{row data mismatch} 1,1
{} 2,2
{} 3,3
{} 5,5
}
do_index_check_test 5.3.2 sqlite_autoindex_t5_1 {
{row missing} {'{"w":4, "z":4}',4}
{row data mismatch} {'{"x":1, "y":1}',1}
{} {'{"x":2, "y":2}',2}
{} {'{"x":3, "y":3}',3}
{} {'{"x":5, "y":5}',5}
}
#-------------------------------------------------------------------------
#
do_execsql_test 6.0 {
CREATE TABLE t6(x INTEGER PRIMARY KEY, y, z);
CREATE INDEX t6x1 ON t6(y, /* one,two,three */ z);
CREATE INDEX t6x2 ON t6(z, -- hello,world,
y);
CREATE INDEX t6x3 ON t6(z -- hello,world
, y);
INSERT INTO t6 VALUES(1, 2, 3);
INSERT INTO t6 VALUES(4, 5, 6);
}
do_index_check_test 6.1 t6x1 {
{} 2,3,1
{} 5,6,4
}
do_index_check_test 6.2 t6x2 {
{} 3,2,1
{} 6,5,4
}
do_index_check_test 6.2 t6x3 {
{} 3,2,1
{} 6,5,4
}
#-------------------------------------------------------------------------
#
do_execsql_test 7.0 {
CREATE TABLE t7(x INTEGER PRIMARY KEY, y, z);
INSERT INTO t7 VALUES(1, 1, 1);
INSERT INTO t7 VALUES(2, 2, 0);
INSERT INTO t7 VALUES(3, 3, 1);
INSERT INTO t7 VALUES(4, 4, 0);
CREATE INDEX t7i1 ON t7(y) WHERE z=1;
CREATE INDEX t7i2 ON t7(y) /* hello,world */ WHERE z=1;
CREATE INDEX t7i3 ON t7(y) WHERE -- yep
z=1;
CREATE INDEX t7i4 ON t7(y) WHERE z=1 -- yep;
}
do_index_check_test 7.1 t7i1 {
{} 1,1 {} 3,3
}
do_index_check_test 7.2 t7i2 {
{} 1,1 {} 3,3
}
do_index_check_test 7.3 t7i3 {
{} 1,1 {} 3,3
}
do_index_check_test 7.4 t7i4 {
{} 1,1 {} 3,3
}
+67
View File
@@ -0,0 +1,67 @@
# Run this script using
#
# sqlite3_checker --test $thisscript $testscripts
#
# The $testscripts argument is optional. If omitted, all *.test files
# in the same directory as $thisscript are run.
#
set NTEST 0
set NERR 0
# Invoke the do_test procedure to run a single test
#
# The $expected parameter is the expected result. The result is the return
# value from the last TCL command in $cmd.
#
# Normally, $expected must match exactly. But if $expected is of the form
# "/regexp/" then regular expression matching is used. If $expected is
# "~/regexp/" then the regular expression must NOT match. If $expected is
# of the form "#/value-list/" then each term in value-list must be numeric
# and must approximately match the corresponding numeric term in $result.
# Values must match within 10%. Or if the $expected term is A..B then the
# $result term must be in between A and B.
#
proc do_test {name cmd expected} {
if {[info exists ::testprefix]} {
set name "$::testprefix$name"
}
incr ::NTEST
puts -nonewline $name...
flush stdout
if {[catch {uplevel #0 "$cmd;\n"} result]} {
puts -nonewline $name...
puts "\nError: $result"
incr ::NERR
} else {
set ok [expr {[string compare $result $expected]==0}]
if {!$ok} {
puts "\n! $name expected: \[$expected\]\n! $name got: \[$result\]"
incr ::NERR
} else {
puts " Ok"
}
}
flush stdout
}
#
# do_execsql_test TESTNAME SQL RES
#
proc do_execsql_test {testname sql {result {}}} {
uplevel [list do_test $testname [list db eval $sql] [list {*}$result]]
}
if {[llength $argv]==0} {
set dir [file dirname $argv0]
set argv [glob -nocomplain $dir/*.test]
}
foreach testfile $argv {
file delete -force test.db
sqlite3 db test.db
source $testfile
catch {db close}
}
puts "$NERR errors out of $NTEST tests"
+1 -1
View File
@@ -200,7 +200,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
/* The sqlite3AtoF() routine is much much faster than atof(), if it
** is available */
double r;
(void)sqlite3AtoF((const char*)p->z, &r);
(void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
*pVal = r;
#else
*pVal = (GeoCoord)atof((const char*)p->z);
+13 -14
View File
@@ -1037,17 +1037,7 @@ static void rtreeRelease(Rtree *pRtree){
pRtree->inWrTrans = 0;
assert( pRtree->nCursor==0 );
nodeBlobReset(pRtree);
if( pRtree->nNodeRef ){
int i;
assert( pRtree->bCorrupt );
for(i=0; i<HASHSIZE; i++){
while( pRtree->aHash[i] ){
RtreeNode *pNext = pRtree->aHash[i]->pNext;
sqlite3_free(pRtree->aHash[i]);
pRtree->aHash[i] = pNext;
}
}
}
assert( pRtree->nNodeRef==0 || pRtree->bCorrupt );
sqlite3_finalize(pRtree->pWriteNode);
sqlite3_finalize(pRtree->pDeleteNode);
sqlite3_finalize(pRtree->pReadRowid);
@@ -2339,7 +2329,7 @@ static int AdjustTree(
int iCell;
cnt++;
if( cnt>100 ){
if( NEVER(cnt>100) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
@@ -2697,6 +2687,15 @@ static int SplitNode(
rc = updateMapping(pRtree, pCell->iRowid, pLeft, iHeight);
}
if( rc==SQLITE_OK ){
rc = nodeRelease(pRtree, pRight);
pRight = 0;
}
if( rc==SQLITE_OK ){
rc = nodeRelease(pRtree, pLeft);
pLeft = 0;
}
splitnode_out:
nodeRelease(pRtree, pRight);
nodeRelease(pRtree, pLeft);
@@ -2881,7 +2880,7 @@ static int rtreeInsertCell(
rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
rc = AdjustTree(pRtree, pNode, pCell);
if( rc==SQLITE_OK ){
if( ALWAYS(rc==SQLITE_OK) ){
if( iHeight==0 ){
rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode);
}else{
@@ -3776,7 +3775,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
if( node.zData==0 ) return;
nData = sqlite3_value_bytes(apArg[1]);
if( nData<4 ) return;
if( nData<4+NCELL(&node)*tree.nBytesPerCell ) return;
if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
pOut = sqlite3_str_new(0);
for(ii=0; ii<NCELL(&node); ii++){
-10
View File
@@ -47,14 +47,4 @@ ifcapable rtree_int_only {
do_rtree_integrity_test rtreeB-1.2 t1
# https://sqlite.org/forum/forumpost/2026-01-08T23:32:19Z
#
db null NULL
do_execsql_test rtreeB-2.1 {
SELECT rtreenode(1,x'00000001'||randomblob(15)) IS NULL;
} {1}
do_execsql_test rtreeB-2.2 {
SELECT rtreenode(1,x'00000001'||randomblob(16)) IS NOT NULL;
} {1}
finish_test
-1
View File
@@ -135,7 +135,6 @@ foreach {tn blob} {
54 540101743400120003001200010000000000000002120002400C000000000002120002400C00000000000050040100000074310017FF0050040100000074310017FF7F00000000000000050100000000000000030100000003001700010000666F7572
55 540101743400120003001200010000000000000002120002400C00000000000050040100000074310017000100010080000001000000020003010100000300170100000003001700010000666F7572
56 5487ffffff7f
57 54015052494e5446110017120004
} {
do_test 2.$tn {
set changeset [binary decode hex $blob]
-67
View File
@@ -192,73 +192,6 @@ do_test 3.3 {
}
} {1 1 3 3}
#-------------------------------------------------------------------------
#
reset_db
set C [binary format c* {0x54 0x01 0x01 0x00 0x12 0x00 0x05}]
do_test 4.0 {
sqlite3changegroup grp
list [catch { grp add $C } msg] $msg
} {0 {}}
grp delete
#-------------------------------------------------------------------------
#
reset_db
set C [binary format c* {0x54 0xda 0xda 0xda 0xda 0xda}]
do_execsql_test 5.0 {
CREATE TABLE t1(a PRIMARY KEY, b, c, d);
}
do_test 5.1 {
list [catch { sqlite3changeset_apply db $C noop xFilter } msg] $msg
} {1 SQLITE_CORRUPT}
#-------------------------------------------------------------------------
#
foreach {tn type C2hex C3hex} {
1 changeset 54020100743100170001000000000000000A030374656E000306656C6576656E
54020100743100170001000000000000000A030374656E0003FFFF01656C6576656E
2 patchset 50020100743100170001000000000000000A0306656C6576656E
50020100743100170001000000000000000A03FFFF06656C6576656E
3 changeset 54020100743100170001000000000000000A030374656E000306656C6576656E
54020100743100170001000000000000000A030374656E0003FFFF
} {
reset_db
do_execsql_test 6.$tn.0 {
CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'three');
} {}
do_test 6.$tn.1 {
set C1 [${type}_from_sql {
INSERT INTO t1 VALUES(10, 'ten');
}]
set C2 [${type}_from_sql {
UPDATE t1 SET y='eleven' WHERE x=10;
}]
db one { SELECT quote($C2) }
} "X'$C2hex'"
do_test 6.$tn.2 {
changeset_to_list [sqlite3changeset_concat $C1 $C2]
} {{INSERT t1 0 X. {} {i 10 t eleven}}}
set C3 [db one {SELECT unhex($C3hex)}]
do_test 6.$tn.3 {
list [catch { sqlite3changeset_concat $C1 $C3 } msg] $msg
} {1 SQLITE_CORRUPT}
}
finish_test
-400
View File
@@ -1,400 +0,0 @@
# 2026 January 09
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}
set testprefix sessionchange2
do_test 1.1.1 {
sqlite3changegroup grp
list [catch { grp change_begin INSERT "nosuchtable" 0 } msg] $msg
} {1 {SQLITE_ERROR - no such table: nosuchtable}}
do_test 1.1.2 {
grp schema db main
list [catch { grp change_begin INSERT "nosuchtable" 0 } msg] $msg
} {1 {SQLITE_ERROR - no such table: nosuchtable}}
do_test 1.1.3 {
list [catch { grp change_begin_ne INSERT "nosuchtable" 0 } msg] $msg
} {1 SQLITE_ERROR}
do_execsql_test 1.2.1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
}
do_test 1.2.2 {
list [catch { grp change_begin 435 "t1" 0 } msg] $msg
} {1 SQLITE_ERROR}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a, b);
}
do_test 2.1.1 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT "t1" 0
grp change_int64 new 0 55
grp change_int64 new 1 101
grp change_finish false
} {}
do_test 2.1.2 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{INSERT t1 0 X. {} {i 55 i 101}}
}
do_test 2.2.1 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT "t2" 0
grp change_int64 new 0 -5
grp change_int64 new 1 -10
grp change_int64 new 2 -20
grp change_finish false
} {}
do_test 2.2.2 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{INSERT t2 0 X.. {} {i -5 i -10 i -20}}
}
do_test 2.2.3 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT "t1" 0
grp change_int64 new 0 223344
grp change_null new 1
grp change_finish false
} {}
do_test 2.2.4 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{INSERT t1 0 X. {} {i 223344 n {}}}
}
do_test 2.2.5 {
sqlite3changegroup grp
grp schema db main
grp change_begin DELETE "t1" 0
grp change_int64 old 0 1
grp change_int64 old 1 123
grp change_finish false
} {}
do_test 2.2.6 {
set C [grp output]
grp delete
changeset_to_list $C
} {
{DELETE t1 0 X. {i 1 i 123} {}}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE TABLE t2(x, y);
}
foreach {tn script error} {
1 {
grp change_begin UPDATE t1 0
} {SQLITE_ERROR - invalid change: undefined value in PK of old.* record}
2 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 1234
grp change_int64 new 0 5678
} {SQLITE_ERROR - invalid change: defined value in PK of new.* record}
3 {
grp change_begin UPDATE t1 0
grp change_null old 0
} {SQLITE_ERROR - invalid change: null value in PK of old.* record}
4 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 1234
grp change_int64 old 1 20
} {SQLITE_ERROR - invalid change: column 1 - old.* value is defined but new.* is undefined}
5 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 1234
grp change_int64 new 1 20
} {SQLITE_ERROR - invalid change: column 1 - old.* value is undefined but new.* is defined}
6 {
grp change_begin INSERT t1 0
grp change_null new 0
grp change_int64 new 1 20
} {SQLITE_ERROR - invalid change: null value in PK}
7 {
grp change_begin INSERT t1 0
grp change_int64 new 1 20
} {SQLITE_ERROR - invalid change: column 0 is undefined}
8 {
grp change_begin INSERT t1 0
grp change_int64 new 0 20
} {SQLITE_ERROR - invalid change: column 1 is undefined}
9 {
grp change_begin DELETE t1 0
grp change_int64 old 0 20
} {SQLITE_ERROR - invalid change: column 1 is undefined}
10 {
grp change_begin DELETE t1 0
grp change_int64 old 1 20
} {SQLITE_ERROR - invalid change: column 0 is undefined}
} {
sqlite3changegroup grp
grp schema db main
eval $script
do_test 3.1.$tn {
list [catch { grp change_finish false } msg] $msg
} [list 1 $error]
grp delete
}
do_test 3.2.1 {
sqlite3changegroup grp
grp schema db main
grp change_begin DELETE t1 0
list [catch {grp change_int64 new 0 20} msg] $msg
} {1 SQLITE_ERROR}
do_test 3.2.2 {
grp change_finish true
grp change_begin INSERT t1 0
list [catch {grp change_int64 old 0 20} msg] $msg
} {1 SQLITE_ERROR}
grp delete
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE t2(a, b);
}
proc do_changegroup_script_test {tn script changeset patchset} {
breakpoint
sqlite3changegroup grp
grp schema db main
eval $script
do_test $tn.1 {
changeset_to_list [grp output]
} [list {*}$changeset]
grp delete
sqlite3changegroup grp
grp config patchset 1
grp schema db main
eval $script
do_test $tn.2 {
changeset_to_list [grp output]
} [list {*}$patchset]
grp delete
}
proc do_changegroup_patchset_test {tn script patchset} {
sqlite3changegroup grp
grp config patchset 1
grp schema db main
eval $script
do_test $tn {
changeset_to_list [grp output]
} [list {*}$patchset]
grp delete
}
do_changegroup_script_test 3.1 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 123
grp change_int64 old 1 456
grp change_int64 new 1 789
grp change_finish false
grp change_begin INSERT t1 0
grp change_int64 new 0 124
grp change_text new 1 hello_world
grp change_finish false
grp change_begin DELETE t1 0
grp change_int64 old 0 125
grp change_double old 1 45.67
grp change_finish false
} {
{DELETE t1 0 X. {i 125 f 45.67} {}}
{INSERT t1 0 X. {} {i 124 t hello_world}}
{UPDATE t1 0 X. {i 123 i 456} {{} {} i 789}}
} {
{DELETE t1 0 X. {i 125 {} {}} {}}
{INSERT t1 0 X. {} {i 124 t hello_world}}
{UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
}
do_changegroup_script_test 3.1.2 {
grp change_begin INSERT t1 0
grp change_int64 new 0 124
grp change_text new 1 hello_world
grp change_finish false
} {
{INSERT t1 0 X. {} {i 124 t hello_world}}
} {
{INSERT t1 0 X. {} {i 124 t hello_world}}
}
do_changegroup_patchset_test 3.2 {
grp change_begin UPDATE t1 0
grp change_int64 old 0 123
grp change_int64 new 1 789
grp change_finish false
grp change_begin DELETE t1 1
grp change_int64 old 0 122
grp change_finish false
} {
{DELETE t1 1 X. {i 122 {} {}} {}}
{UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
}
do_changegroup_patchset_test 3.3 {
grp change_begin DELETE t1 0
grp change_int64 old 0 123
grp change_finish false
} {
{DELETE t1 0 X. {i 123 {} {}} {}}
}
do_changegroup_script_test 3.4 {
grp change_begin INSERT t1 0
grp change_int64 new 0 124
grp change_text new 1 hello_world
grp change_finish false
grp change_begin DELETE t1 0
grp change_int64 old 0 124
grp change_text old 1 hello_world
grp change_finish false
grp change_begin INSERT t1 0
grp change_int64 new 0 1000
grp change_blob new 1 abcd
grp change_finish false
grp change_begin UPDATE t1 0
grp change_int64 old 0 1000
grp change_blob old 1 abcd
grp change_blob new 1 bcda
grp change_finish false
} {
{INSERT t1 0 X. {} {i 1000 b bcda}}
} {
{INSERT t1 0 X. {} {i 1000 b bcda}}
}
do_changegroup_script_test 3.5 {
grp change_begin DELETE t1 0
grp change_int64 old 0 1000
grp change_text-1 old 1 "hello world"
grp change_finish false
} {
{DELETE t1 0 X. {i 1000 t {hello world}} {}}
} {
{DELETE t1 0 X. {i 1000 {} {}} {}}
}
#-------------------------------------------------------------------------
# Calling sqlite3changegroup_change_finish() if a change has not been
# started is a no-op.
#
do_test 4.0 {
sqlite3changegroup grp
grp change_finish false
} {}
# But calling any other sqlite3changegroup_change_xxx() function if a
# change has not been started is a misuse.
#
foreach {tn cmd} {
1 { grp change_blob old 0 abcd }
2 { grp change_text new 1 helloworld }
3 { grp change_int64 old 2 12345678 }
4 { grp change_double new 3 12.345 }
5 { grp change_null old 4 }
} {
do_test 4.1.$tn {
list [catch $::cmd msg] $msg
} {1 SQLITE_MISUSE}
}
grp delete
do_execsql_test 4.2.1 {
CREATE TABLE t3(a, b PRIMARY KEY);
}
do_test 4.2 {
sqlite3changegroup grp
grp schema db main
grp change_begin INSERT t3 0
list [catch { grp change_begin INSERT t3 0 } msg] $msg
} {1 SQLITE_MISUSE}
grp delete
do_test 4.3 {
sqlite3changegroup grp
grp schema db main
grp change_begin UPDATE t3 0
} {}
foreach {tn cmd} {
1 { grp change_blob old -1 abcd }
2 { grp change_text new 2 helloworld }
} {
do_test 4.4.$tn {
list [catch $::cmd msg] $msg
} {1 SQLITE_RANGE}
}
grp delete
finish_test
+1 -15
View File
@@ -43,7 +43,7 @@ set C2 [changeset_from_sql {
INSERT INTO t1 VALUES(0, 0, 0, 0);
}]
do_faultsim_test 1.2 -faults oom* -prep {
do_faultsim_test 1 -faults oom* -prep {
sqlite3changegroup G
} -body {
G schema db main
@@ -56,20 +56,6 @@ do_faultsim_test 1.2 -faults oom* -prep {
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
do_faultsim_test 1.3 -faults oom* -prep {
sqlite3changegroup G
} -body {
G schema db main
G change_begin INSERT t1 0
G change_int64 new 0 12345
G change_int64 new 1 67890
G output
set {} {}
} -test {
catch { G delete }
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
+79 -525
View File
@@ -349,21 +349,6 @@ static int sessionVarintGet(const u8 *aBuf, int *piVal){
return getVarint32(aBuf, *piVal);
}
/*
** Read a varint value from buffer aBuf[], size nBuf bytes, into *piVal.
** Return the number of bytes read.
*/
static int sessionVarintGetSafe(const u8 *aBuf, int nBuf, int *piVal){
u8 aCopy[9];
const u8 *aRead = aBuf;
memset(aCopy, 0, sizeof(aCopy));
if( nBuf<sizeof(aCopy) ){
memcpy(aCopy, aBuf, nBuf);
aRead = aCopy;
}
return getVarint32(aRead, *piVal);
}
/* Load an unaligned and unsigned 32-bit integer */
#define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
@@ -392,19 +377,6 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
aBuf[7] = (i>> 0) & 0xFF;
}
/*
** Write a double value to the buffer aBuf[].
*/
static void sessionPutDouble(u8 *aBuf, double r){
/* TODO: SQLite does something special to deal with mixed-endian
** floating point values (e.g. ARM7). This code probably should
** too. */
u64 i;
assert( sizeof(double)==8 && sizeof(u64)==8 );
memcpy(&i, &r, 8);
sessionPutI64(aBuf, i);
}
/*
** This function is used to serialize the contents of value pValue (see
** comment titled "RECORD FORMAT" above).
@@ -442,13 +414,16 @@ static int sessionSerializeValue(
/* TODO: SQLite does something special to deal with mixed-endian
** floating point values (e.g. ARM7). This code probably should
** too. */
u64 i;
if( eType==SQLITE_INTEGER ){
u64 i = (u64)sqlite3_value_int64(pValue);
sessionPutI64(&aBuf[1], i);
i = (u64)sqlite3_value_int64(pValue);
}else{
double r = sqlite3_value_double(pValue);
sessionPutDouble(&aBuf[1], r);
double r;
assert( sizeof(double)==8 && sizeof(u64)==8 );
r = sqlite3_value_double(pValue);
memcpy(&i, &r, 8);
}
sessionPutI64(&aBuf[1], i);
}
nByte = 9;
break;
@@ -668,10 +643,14 @@ static unsigned int sessionChangeHash(
int isPK = pTab->abPK[i];
if( bPkOnly && isPK==0 ) continue;
/* It is not possible for eType to be SQLITE_NULL here. The session
** module does not record changes for rows with NULL values stored in
** primary key columns. */
assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
|| eType==SQLITE_TEXT || eType==SQLITE_BLOB
|| eType==SQLITE_NULL || eType==0
);
assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
if( isPK ){
a++;
@@ -679,16 +658,12 @@ static unsigned int sessionChangeHash(
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
h = sessionHashAppendI64(h, sessionGetI64(a));
a += 8;
}else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
}else{
int n;
a += sessionVarintGet(a, &n);
h = sessionHashAppendBlob(h, n, a);
a += n;
}
/* It should not be possible for eType to be SQLITE_NULL or 0x00 here,
** as the session module does not record changes for rows with NULL
** values stored in primary key columns. But a corrupt changesets
** may contain such a value. */
}else{
a += sessionSerialLen(a);
}
@@ -1380,7 +1355,9 @@ static void sessionUpdateOneChange(
case SQLITE_FLOAT: {
double rVal = sqlite3_column_double(pDflt, iField);
sessionPutDouble(&pNew->aRecord[pNew->nRecord], rVal);
i64 iVal = 0;
memcpy(&iVal, &rVal, sizeof(rVal));
sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
pNew->nRecord += 8;
break;
}
@@ -2637,14 +2614,15 @@ static void sessionAppendCol(
int eType = sqlite3_column_type(pStmt, iCol);
sessionAppendByte(p, (u8)eType, pRc);
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
sqlite3_int64 i;
u8 aBuf[8];
if( eType==SQLITE_INTEGER ){
sqlite3_int64 i = sqlite3_column_int64(pStmt, iCol);
sessionPutI64(aBuf, i);
i = sqlite3_column_int64(pStmt, iCol);
}else{
double r = sqlite3_column_double(pStmt, iCol);
sessionPutDouble(aBuf, r);
memcpy(&i, &r, 8);
}
sessionPutI64(aBuf, i);
sessionAppendBlob(p, aBuf, 8, pRc);
}
if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
@@ -3094,13 +3072,10 @@ static int sessionGenerateChangeset(
}
if( pSession->rc ) return pSession->rc;
rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_mutex_enter(sqlite3_db_mutex(db));
rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
if( rc!=SQLITE_OK ){
sqlite3_mutex_leave(sqlite3_db_mutex(db));
return rc;
}
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
if( pTab->nEntry ){
@@ -3583,8 +3558,7 @@ static int sessionReadRecord(
u8 *aVal = &pIn->aData[pIn->iNext];
if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
int nByte;
int nRem = pIn->nData - pIn->iNext;
pIn->iNext += sessionVarintGetSafe(aVal, nRem, &nByte);
pIn->iNext += sessionVarintGet(aVal, &nByte);
rc = sessionInputBuffer(pIn, nByte);
if( rc==SQLITE_OK ){
if( nByte<0 || nByte>pIn->nData-pIn->iNext ){
@@ -3637,8 +3611,7 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
rc = sessionInputBuffer(pIn, 9);
if( rc==SQLITE_OK ){
int nBuf = pIn->nData - pIn->iNext;
nRead += sessionVarintGetSafe(&pIn->aData[pIn->iNext], nBuf, &nCol);
nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
/* The hard upper limit for the number of columns in an SQLite
** database table is, according to sqliteLimit.h, 32676. So
** consider any table-header that purports to have more than 65536
@@ -3658,15 +3631,8 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){
nRead++;
}
/* Break out of the loop if if the nul-terminator byte has been found.
** Otherwise, read some more input data and keep seeking. If there is
** no more input data, consider the changeset corrupt. */
if( (pIn->iNext + nRead)<pIn->nData ) break;
rc = sessionInputBuffer(pIn, nRead + 100);
if( rc==SQLITE_OK && (pIn->iNext + nRead)>=pIn->nData ){
rc = SQLITE_CORRUPT_BKPT;
}
}
*pnByte = nRead+1;
return rc;
@@ -3687,7 +3653,7 @@ static int sessionChangesetBufferRecord(
int *pnByte /* OUT: Size of record in bytes */
){
int rc = SQLITE_OK;
i64 nByte = 0;
int nByte = 0;
int i;
for(i=0; rc==SQLITE_OK && i<nCol; i++){
int eType;
@@ -3696,17 +3662,13 @@ static int sessionChangesetBufferRecord(
eType = pIn->aData[pIn->iNext + nByte++];
if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
int n;
int nRem = pIn->nData - (pIn->iNext + nByte);
nByte += sessionVarintGetSafe(&pIn->aData[pIn->iNext+nByte], nRem, &n);
nByte += sessionVarintGet(&pIn->aData[pIn->iNext+nByte], &n);
nByte += n;
rc = sessionInputBuffer(pIn, nByte);
}else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
nByte += 8;
}
}
if( (pIn->iNext+nByte)>pIn->nData ){
rc = SQLITE_CORRUPT_BKPT;
}
}
*pnByte = nByte;
return rc;
@@ -3802,10 +3764,10 @@ static int sessionChangesetNextOne(
memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
}
/* Make sure the buffer contains at least 2 bytes of input data, or all
** remaining data if there are less than 2 bytes available. This is
** sufficient either for the 'T' or 'P' byte that begins a new table,
** or for the "op" and "bIndirect" single bytes otherwise. */
/* Make sure the buffer contains at least 10 bytes of input data, or all
** remaining data if there are less than 10 bytes available. This is
** sufficient either for the 'T' or 'P' byte and the varint that follows
** it, or for the two single byte values otherwise. */
p->rc = sessionInputBuffer(&p->in, 2);
if( p->rc!=SQLITE_OK ) return p->rc;
@@ -3835,13 +3797,11 @@ static int sessionChangesetNextOne(
return (p->rc = SQLITE_CORRUPT_BKPT);
}
if( (op!=SQLITE_UPDATE && op!=SQLITE_DELETE && op!=SQLITE_INSERT)
|| (p->in.iNext>=p->in.nData)
){
return (p->rc = SQLITE_CORRUPT_BKPT);
}
p->op = op;
p->bIndirect = p->in.aData[p->in.iNext++];
if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
return (p->rc = SQLITE_CORRUPT_BKPT);
}
if( paRec ){
int nVal; /* Number of values to buffer */
@@ -5689,21 +5649,6 @@ int sqlite3changeset_apply_strm(
);
}
/*
** The parts of the sqlite3_changegroup structure used by the
** sqlite3changegroup_change_xxx() APIs.
*/
typedef struct ChangeData ChangeData;
struct ChangeData {
SessionTable *pTab;
int bIndirect;
int eOp;
int nBufAlloc;
SessionBuffer *aBuf;
SessionBuffer record;
};
/*
** sqlite3_changegroup handle.
*/
@@ -5715,17 +5660,12 @@ struct sqlite3_changegroup {
sqlite3 *db; /* Configured by changegroup_schema() */
char *zDb; /* Configured by changegroup_schema() */
ChangeData cd; /* Used by changegroup_change_xxx() APIs. */
};
/*
** This function is called to merge two changes to the same row together as
** part of an sqlite3changeset_concat() operation. A new change object is
** allocated and a pointer to it stored in *ppNew.
**
** Because they have been vetted by sqlite3changegroup_add() or similar,
** both the aRec[] change and the pExist change are safe to use without
** checking for buffer overflows.
*/
static int sessionChangeMerge(
SessionTable *pTab, /* Table structure */
@@ -5866,7 +5806,7 @@ static int sessionChangeMerge(
memcpy(aCsr, aRec, nRec);
aCsr += nRec;
}else{
if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist,0,aRec,0) ){
if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0,aRec,0) ){
sqlite3_free(pNew);
pNew = 0;
}
@@ -5959,14 +5899,15 @@ static int sessionChangesetExtendRecord(
switch( eType ){
case SQLITE_FLOAT:
case SQLITE_INTEGER: {
i64 iVal;
if( eType==SQLITE_INTEGER ){
iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
}else{
double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
memcpy(&iVal, &rVal, sizeof(i64));
}
if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
if( eType==SQLITE_INTEGER ){
sqlite3_int64 iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
}else{
double rVal = sqlite3_column_double(pTab->pDfltStmt, ii);
sessionPutDouble(&pOut->aBuf[pOut->nBuf], rVal);
}
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
pOut->nBuf += 8;
}
break;
@@ -6037,19 +5978,13 @@ static int sessionChangesetFindTable(
int nCol = 0;
*ppTab = 0;
sqlite3changeset_pk(pIter, &abPK, &nCol);
/* Search the list for an existing table */
for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
}
if( pIter ){
sqlite3changeset_pk(pIter, &abPK, &nCol);
}else if( !pTab && !pGrp->db ){
return SQLITE_OK;
}
/* If one was not found above, create a new table now */
if( !pTab ){
SessionTable **ppNew;
@@ -6061,17 +5996,15 @@ static int sessionChangesetFindTable(
memset(pTab, 0, sizeof(SessionTable));
pTab->nCol = nCol;
pTab->abPK = (u8*)&pTab[1];
if( nCol>0 ){
memcpy(pTab->abPK, abPK, nCol);
}
memcpy(pTab->abPK, abPK, nCol);
pTab->zName = (char*)&pTab->abPK[nCol];
memcpy(pTab->zName, zTab, nTab+1);
if( pGrp->db ){
pTab->nCol = 0;
rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
if( rc || pTab->nCol==0 ){
sqlite3_free(pTab->azCol);
if( rc ){
assert( pTab->azCol==0 );
sqlite3_free(pTab);
return rc;
}
@@ -6086,7 +6019,7 @@ static int sessionChangesetFindTable(
}
/* Check that the table is compatible. */
if( pIter && !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
rc = SQLITE_SCHEMA;
}
@@ -6095,27 +6028,44 @@ static int sessionChangesetFindTable(
}
/*
** Add a single change to the changegroup pGrp.
** Add the change currently indicated by iterator pIter to the hash table
** belonging to changegroup pGrp.
*/
static int sessionOneChangeToHash(
sqlite3_changegroup *pGrp, /* Changegroup to update */
SessionTable *pTab, /* Table change pertains to */
int op, /* One of SQLITE_INSERT, UPDATE, DELETE */
int bIndirect, /* True to flag change as "indirect" */
int nCol, /* Number of columns in record(s) */
u8 *aRec, /* Serialized change record(s) */
int nRec, /* Size of aRec[] in bytes */
int bRebase /* True if this is a rebase blob */
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter,
int bRebase
){
int rc = SQLITE_OK;
int nCol = 0;
int op = 0;
int iHash = 0;
int bIndirect = 0;
SessionChange *pChange = 0;
SessionChange *pExist = 0;
SessionChange **pp = 0;
SessionTable *pTab = 0;
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
assert( nRec>0 );
if( nCol<pTab->nCol ){
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
const char *zTab = 0;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
}
if( rc==SQLITE_OK && nCol<pTab->nCol ){
SessionBuffer *pBuf = &pGrp->rec;
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
aRec = pBuf->aBuf;
@@ -6123,7 +6073,7 @@ static int sessionOneChangeToHash(
assert( pGrp->db );
}
if( rc==SQLITE_OK && sessionGrowHash(0, pGrp->bPatch, pTab) ){
if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
}
@@ -6131,12 +6081,12 @@ static int sessionOneChangeToHash(
/* Search for existing entry. If found, remove it from the hash table.
** Code below may link it back in. */
iHash = sessionChangeHash(
pTab, (pGrp->bPatch && op==SQLITE_DELETE), aRec, pTab->nChange
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
);
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
int bPkOnly1 = 0;
int bPkOnly2 = 0;
if( pGrp->bPatch ){
if( pIter->bPatchset ){
bPkOnly1 = (*pp)->op==SQLITE_DELETE;
bPkOnly2 = op==SQLITE_DELETE;
}
@@ -6151,7 +6101,7 @@ static int sessionOneChangeToHash(
if( rc==SQLITE_OK ){
rc = sessionChangeMerge(pTab, bRebase,
pGrp->bPatch, pExist, op, bIndirect, aRec, nRec, &pChange
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
);
}
if( rc==SQLITE_OK && pChange ){
@@ -6160,47 +6110,6 @@ static int sessionOneChangeToHash(
pTab->nEntry++;
}
return rc;
}
/*
** Add the change currently indicated by iterator pIter to the hash table
** belonging to changegroup pGrp.
*/
static int sessionOneChangeIterToHash(
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter,
int bRebase
){
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
const char *zTab = 0;
int nCol = 0;
int op = 0;
int bIndirect = 0;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
}
if( rc==SQLITE_OK ){
rc = sessionOneChangeToHash(
pGrp, pTab, op, bIndirect, nCol, aRec, nRec, bRebase
);
}
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
@@ -6220,7 +6129,7 @@ static int sessionChangesetToHash(
pIter->in.bNoDiscard = 1;
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase);
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
if( rc!=SQLITE_OK ) break;
}
@@ -6310,33 +6219,6 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp){
return rc;
}
/*
** Configure a changegroup object.
*/
int sqlite3changegroup_config(
sqlite3_changegroup *pGrp,
int op,
void *pArg
){
int rc = SQLITE_OK;
switch( op ){
case SQLITE_CHANGEGROUP_CONFIG_PATCHSET: {
int arg = *(int*)pArg;
if( pGrp->pList==0 && arg>=0 ){
pGrp->bPatch = (arg>0);
}
*(int*)pArg = pGrp->bPatch;
break;
}
default:
rc = SQLITE_MISUSE;
break;
}
return rc;
}
/*
** Provide a database schema to the changegroup object.
*/
@@ -6395,7 +6277,7 @@ int sqlite3changegroup_add_change(
rc = SQLITE_ERROR;
}else{
pIter->in.bNoDiscard = 1;
rc = sessionOneChangeIterToHash(pGrp, pIter, 0);
rc = sessionOneChangeToHash(pGrp, pIter, 0);
}
return rc;
}
@@ -6447,12 +6329,6 @@ int sqlite3changegroup_output_strm(
*/
void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
int ii;
for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
sqlite3_free(pGrp->cd.aBuf[ii].aBuf);
}
sqlite3_free(pGrp->cd.record.aBuf);
sqlite3_free(pGrp->cd.aBuf);
sqlite3_free(pGrp->zDb);
sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp->rec.aBuf);
@@ -6883,326 +6759,4 @@ int sqlite3session_config(int op, void *pArg){
return rc;
}
/*
** Begin adding a change to a changegroup object.
*/
int sqlite3changegroup_change_begin(
sqlite3_changegroup *pGrp,
int eOp,
const char *zTab,
int bIndirect,
char **pzErr
){
SessionTable *pTab = 0;
int rc = SQLITE_OK;
if( pGrp->cd.pTab ){
rc = SQLITE_MISUSE;
}else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_UPDATE && eOp!=SQLITE_DELETE ){
rc = SQLITE_ERROR;
}else{
rc = sessionChangesetFindTable(pGrp, zTab, 0, &pTab);
}
if( rc==SQLITE_OK ){
if( pTab==0 ){
if( pzErr ){
*pzErr = sqlite3_mprintf("no such table: %s", zTab);
}
rc = SQLITE_ERROR;
}else{
int nReq = pTab->nCol * (eOp==SQLITE_UPDATE ? 2 : 1);
pGrp->cd.pTab = pTab;
pGrp->cd.eOp = eOp;
pGrp->cd.bIndirect = bIndirect;
if( pGrp->cd.nBufAlloc<nReq ){
SessionBuffer *aBuf = (SessionBuffer*)sqlite3_realloc(
pGrp->cd.aBuf, nReq * sizeof(SessionBuffer)
);
if( aBuf==0 ){
rc = SQLITE_NOMEM;
}else{
memset(&aBuf[pGrp->cd.nBufAlloc], 0,
sizeof(SessionBuffer) * (nReq - pGrp->cd.nBufAlloc)
);
pGrp->cd.aBuf = aBuf;
pGrp->cd.nBufAlloc = nReq;
}
}
#ifdef SQLITE_DEBUG
{
/* Assert that all column values are currently undefined */
int ii;
for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
assert( pGrp->cd.aBuf[ii].nBuf==0 );
}
}
#endif
}
}
return rc;
}
/*
** This function does processing common to the _change_int64(), _change_text()
** and other similar APIs.
*/
static int checkChangeParams(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
sqlite3_int64 nReq,
SessionBuffer **ppBuf
){
int rc = SQLITE_OK;
if( pGrp->cd.pTab==0 ){
rc = SQLITE_MISUSE;
}else if( iCol<0 || iCol>=pGrp->cd.pTab->nCol ){
rc = SQLITE_RANGE;
}else if(
(bNew && pGrp->cd.eOp==SQLITE_DELETE)
|| (!bNew && pGrp->cd.eOp==SQLITE_INSERT)
){
rc = SQLITE_ERROR;
}else{
SessionBuffer *pBuf = &pGrp->cd.aBuf[iCol];
if( pGrp->cd.eOp==SQLITE_UPDATE && bNew ){
pBuf += pGrp->cd.pTab->nCol;
}
pBuf->nBuf = 0;
sessionBufferGrow(pBuf, nReq, &rc);
pBuf->nBuf = nReq;
*ppBuf = pBuf;
}
return rc;
}
/*
** Configure the change currently under construction with an integer value.
*/
int sqlite3changegroup_change_int64(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
sqlite3_int64 iVal
){
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_INTEGER;
sessionPutI64(&pBuf->aBuf[1], iVal);
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a null value.
*/
int sqlite3changegroup_change_null(
sqlite3_changegroup *pGrp,
int bNew,
int iCol
){
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 1, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_NULL;
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a real value.
*/
int sqlite3changegroup_change_double(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
double fVal
){
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_FLOAT;
sessionPutDouble(&pBuf->aBuf[1], fVal);
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a text value.
*/
int sqlite3changegroup_change_text(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
const char *pVal,
int nVal
){
int nText = nVal>=0 ? nVal : strlen(pVal);
sqlite3_int64 nByte = 1 + sessionVarintLen(nText) + nText;
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_TEXT;
pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nText));
memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nText);
pBuf->nBuf += nText;
return SQLITE_OK;
}
/*
** Configure the change currently under construction with a blob value.
*/
int sqlite3changegroup_change_blob(
sqlite3_changegroup *pGrp,
int bNew,
int iCol,
const void *pVal,
int nVal
){
sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + nVal;
int rc = SQLITE_OK;
SessionBuffer *pBuf = 0;
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
return rc;
}
pBuf->aBuf[0] = SQLITE_BLOB;
pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nVal));
memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nVal);
pBuf->nBuf += nVal;
return SQLITE_OK;
}
/*
** Finish any change currently being constructed by the changegroup object.
*/
int sqlite3changegroup_change_finish(
sqlite3_changegroup *pGrp,
int bDiscard,
char **pzErr
){
int rc = SQLITE_OK;
if( pGrp->cd.pTab ){
SessionBuffer *aBuf = pGrp->cd.aBuf;
int ii;
if( bDiscard==0 ){
int nBuf = pGrp->cd.pTab->nCol;
u8 eUndef = SQLITE_NULL;
if( pGrp->cd.eOp==SQLITE_UPDATE ){
for(ii=0; ii<nBuf; ii++){
if( pGrp->cd.pTab->abPK[ii] ){
if( aBuf[ii].nBuf<=1 ){
*pzErr = sqlite3_mprintf(
"invalid change: %s value in PK of old.* record",
aBuf[ii].nBuf==1 ? "null" : "undefined"
);
rc = SQLITE_ERROR;
break;
}else if( aBuf[ii + nBuf].nBuf>0 ){
*pzErr = sqlite3_mprintf(
"invalid change: defined value in PK of new.* record"
);
rc = SQLITE_ERROR;
break;
}
}else
if( pGrp->bPatch==0 && (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){
*pzErr = sqlite3_mprintf(
"invalid change: column %d "
"- old.* value is %sdefined but new.* is %sdefined",
ii, aBuf[ii].nBuf ? "" : "un", aBuf[ii+nBuf].nBuf ? "" : "un"
);
rc = SQLITE_ERROR;
break;
}
}
eUndef = 0x00;
if( pGrp->bPatch==0 ) nBuf = nBuf * 2;
}else{
for(ii=0; ii<nBuf; ii++){
int isPK = pGrp->cd.pTab->abPK[ii];
if( (pGrp->cd.eOp==SQLITE_INSERT || pGrp->bPatch==0 || isPK)
&& aBuf[ii].nBuf==0
){
*pzErr = sqlite3_mprintf(
"invalid change: column %d is undefined", ii
);
rc = SQLITE_ERROR;
break;
}
if( aBuf[ii].nBuf==1 && isPK ){
*pzErr = sqlite3_mprintf(
"invalid change: null value in PK"
);
rc = SQLITE_ERROR;
break;
}
}
}
pGrp->cd.record.nBuf = 0;
for(ii=0; ii<nBuf; ii++){
SessionBuffer *p = &pGrp->cd.aBuf[ii];
if( pGrp->bPatch ){
if( pGrp->cd.pTab->abPK[ii]==0 ){
if( pGrp->cd.eOp==SQLITE_UPDATE ){
p += pGrp->cd.pTab->nCol;
}else if( pGrp->cd.eOp==SQLITE_DELETE ){
continue;
}
}
}
if( 0==sessionBufferGrow(&pGrp->cd.record, p->nBuf?p->nBuf:1, &rc) ){
if( p->nBuf ){
memcpy(&pGrp->cd.record.aBuf[pGrp->cd.record.nBuf],p->aBuf,p->nBuf);
pGrp->cd.record.nBuf += p->nBuf;
}else{
pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef;
}
}
}
if( rc==SQLITE_OK ){
rc = sessionOneChangeToHash(
pGrp, pGrp->cd.pTab,
pGrp->cd.eOp, pGrp->cd.bIndirect, pGrp->cd.pTab->nCol,
pGrp->cd.record.aBuf, pGrp->cd.record.nBuf, 0
);
}
}
/* Reset all aBuf[] entries to "undefined". */
{
int nZero = pGrp->cd.pTab->nCol;
if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero;
for(ii=0; ii<nZero; ii++){
pGrp->cd.aBuf[ii].nBuf = 0;
}
}
pGrp->cd.pTab = 0;
}
return rc;
}
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */

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