Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 843418ae70 | |||
| 0ec8c56567 |
+119
-187
@@ -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 \
|
||||
@@ -1866,8 +1929,8 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
|
||||
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
# <<mark>>
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS) $(TOP)\tool\winmain.c
|
||||
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(TOP)\tool\winmain.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\misc $(TOP)\tool\sqldiff.c $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
|
||||
dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -2366,7 +2429,7 @@ shell.c: $(SHELL_DEP) $(TOP)\tool\mkshellc.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\mkshellc.tcl shell.c
|
||||
|
||||
zlib:
|
||||
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) CFLAGS="$(ZLIBCFLAGS)" && popd
|
||||
pushd $(ZLIBDIR) && $(MAKE) /f win32\Makefile.msc clean $(ZLIBLIB) && popd
|
||||
|
||||
# Rules to build the extension objects.
|
||||
#
|
||||
@@ -2601,22 +2664,6 @@ devtest: srctree-check sourcetest
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
|
||||
|
||||
# Rerun test cases that failed on the previous testrunner invocation
|
||||
#
|
||||
retest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl retest
|
||||
|
||||
# Show all errors from the most reason testrunner invocation
|
||||
#
|
||||
errors:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl errors
|
||||
|
||||
# Show the status of the current testrunner invocation,
|
||||
# updated every couple of seconds
|
||||
#
|
||||
status:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl status -d 2
|
||||
|
||||
# Validate that various generated files in the source tree
|
||||
# are up-to-date.
|
||||
#
|
||||
@@ -2640,8 +2687,8 @@ smoketest: $(TESTPROGS)
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
|
||||
|
||||
shelltest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release shell
|
||||
shelltest: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\permutations.test shell
|
||||
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) tclsqlite-ex.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl -DINCLUDE_SQLITE3_C $(TOP)\tool\sqlite3_analyzer.c.in > $@
|
||||
@@ -2660,6 +2707,23 @@ sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
|
||||
sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
|
||||
|
||||
CHECKER_DEPS =\
|
||||
$(TOP)\tool\mkccode.tcl \
|
||||
sqlite3.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.tcl \
|
||||
$(TOP)\ext\repair\checkindex.c \
|
||||
$(TOP)\ext\repair\checkfreelist.c \
|
||||
$(TOP)\ext\misc\btreeinfo.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.c.in
|
||||
|
||||
sqlite3_checker.c: $(CHECKER_DEPS)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\ext\repair\sqlite3_checker.c.in > $@
|
||||
|
||||
sqlite3_checker.exe: sqlite3_checker.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_checker.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
dbdump.exe: $(TOP)\ext\misc\dbdump.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DDBDUMP_STANDALONE $(TOP)\ext\misc\dbdump.c $(SQLITE3C) \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS)
|
||||
@@ -2692,9 +2756,6 @@ showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C) $(SQLITE3H)
|
||||
showshm.exe: $(TOP)\tool\showshm.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\showshm.c /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
showtmlog.exe: $(TOP)\tool\showtmlog.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\showtmlog.c /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
index_usage.exe: $(TOP)\tool\index_usage.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
$(TOP)\tool\index_usage.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -2763,135 +2824,6 @@ tcl-env:
|
||||
@echo JIM_TCLSH = $(JIM_TCLSH)
|
||||
@echo VISUALSTUDIOVERSION = $(VISUALSTUDIOVERSION)
|
||||
|
||||
env:
|
||||
@echo ALL_TCL_TARGETS = $(ALL_TCL_TARGETS)
|
||||
@echo API_ARMOR = $(API_ARMOR)
|
||||
@echo ASAN = $(ASAN)
|
||||
@echo BCC = $(BCC)
|
||||
@echo BUILD_ZLIB = $(BUILD_ZLIB)
|
||||
@echo CC = $(CC)
|
||||
@echo CCOPTS = $(CCOPTS)
|
||||
@echo CHECKER_DEPS = $(CHECKER_DEPS)
|
||||
@echo CORE_CCONV_OPTS = $(CORE_CCONV_OPTS)
|
||||
@echo CORE_COMPILE_OPTS = $(CORE_COMPILE_OPTS)
|
||||
@echo CORE_LINK_DEP = $(CORE_LINK_DEP)
|
||||
@echo CORE_LINK_OPTS = $(CORE_LINK_OPTS)
|
||||
@echo CRTLIBPATH = $(CRTLIBPATH)
|
||||
@echo CSC = $(CSC)
|
||||
@echo DBFUZZ_COMPILE_OPTS = $(DBFUZZ_COMPILE_OPTS)
|
||||
@echo DEBUG = $(DEBUG)
|
||||
@echo DYNAMIC_SHELL = $(DYNAMIC_SHELL)
|
||||
@echo EXT_FEATURE_FLAGS = $(EXT_FEATURE_FLAGS)
|
||||
@echo EXTHDR = $(EXTHDR)
|
||||
@echo EXTRA_SRC = $(EXTRA_SRC)
|
||||
@echo FOR_UWP = $(FOR_UWP)
|
||||
@echo FUZZCHECK_OPTS = $(FUZZCHECK_OPTS)
|
||||
@echo FUZZCHECK_SRC = $(FUZZCHECK_SRC)
|
||||
@echo FUZZDATA = $(FUZZDATA)
|
||||
@echo FUZZERSHELL_COMPILE_OPTS = $(FUZZERSHELL_COMPILE_OPTS)
|
||||
@echo HDR = $(HDR)
|
||||
@echo ICUDIR = $(ICUDIR)
|
||||
@echo ICUINCDIR = $(ICUINCDIR)
|
||||
@echo ICULIBDIR = $(ICULIBDIR)
|
||||
@echo JIM_TCLSH = $(JIM_TCLSH)
|
||||
@echo KV_COMPILE_OPTS = $(KV_COMPILE_OPTS)
|
||||
@echo LDFLAGS = $(LDFLAGS)
|
||||
@echo LD = $(LD)
|
||||
@echo LIBICU = $(LIBICU)
|
||||
@echo LIBOBJ = $(LIBOBJ)
|
||||
@echo LIBREADLINE = $(LIBREADLINE)
|
||||
@echo LIBRESOBJS = $(LIBRESOBJS)
|
||||
@echo LIBTCLPATH = $(LIBTCLPATH)
|
||||
@echo LIBTCLSTUB = $(LIBTCLSTUB)
|
||||
@echo LIBTCL = $(LIBTCL)
|
||||
@echo LTCOMPILE = $(LTCOMPILE)
|
||||
@echo LTLIB = $(LTLIB)
|
||||
@echo LTLIBOPTS = $(LTLIBOPTS)
|
||||
@echo LTLIBPATHS = $(LTLIBPATHS)
|
||||
@echo LTLIBS = $(LTLIBS)
|
||||
@echo LTLINK = $(LTLINK)
|
||||
@echo LTLINKOPTS = $(LTLINKOPTS)
|
||||
@echo LTRCOMPILE = $(LTRCOMPILE)
|
||||
@echo MEMDEBUG = $(MEMDEBUG)
|
||||
@echo MINIMAL_AMALGAMATION = $(MINIMAL_AMALGAMATION)
|
||||
@echo MPTESTER_COMPILE_OPTS = $(MPTESTER_COMPILE_OPTS)
|
||||
@echo NCC = $(NCC)
|
||||
@echo NCRTLIBPATH = $(NCRTLIBPATH)
|
||||
@echo NLTLIBPATHS = $(NLTLIBPATHS)
|
||||
@echo NO_LINEMACROS = $(NO_LINEMACROS)
|
||||
@echo NO_TCL = $(NO_TCL)
|
||||
@echo NO_WARN = $(NO_WARN)
|
||||
@echo NSDKLIBPATH = $(NSDKLIBPATH)
|
||||
@echo NUCRTLIBPATH = $(NUCRTLIBPATH)
|
||||
@echo OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS)
|
||||
@echo OPTIMIZATIONS = $(OPTIMIZATIONS)
|
||||
@echo OSSSHELL_SRC = $(OSSSHELL_SRC)
|
||||
@echo OSTRACE = $(OSTRACE)
|
||||
@echo RBU = $(RBU)
|
||||
@echo RCC = $(RCC)
|
||||
@echo RC = $(RC)
|
||||
@echo READLINE_FLAGS = $(READLINE_FLAGS)
|
||||
@echo REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS)
|
||||
@echo RSYNC_OPT = $(RSYNC_OPT)
|
||||
@echo RSYNC_SRC = $(RSYNC_SRC)
|
||||
@echo SESSION = $(SESSION)
|
||||
@echo SETLK_TIMEOUT = $(SETLK_TIMEOUT)
|
||||
@echo SHELL_CCONV_OPTS = $(SHELL_CCONV_OPTS)
|
||||
@echo SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS)
|
||||
@echo SHELL_CORE_DEP = $(SHELL_CORE_DEP)
|
||||
@echo SHELL_CORE_LIB = $(SHELL_CORE_LIB)
|
||||
@echo SHELL_CORE_SRC = $(SHELL_CORE_SRC)
|
||||
@echo SHELL_DEP = $(SHELL_DEP)
|
||||
@echo SHELL_LINK_OPTS = $(SHELL_LINK_OPTS)
|
||||
@echo SPLIT_AMALGAMATION = $(SPLIT_AMALGAMATION)
|
||||
@echo SQLITETCLDECLSH = $(SQLITETCLDECLSH)
|
||||
@echo SQLITE_TCL_DEP = $(SQLITE_TCL_DEP)
|
||||
@echo SQLITETCLH = $(SQLITETCLH)
|
||||
@echo SRC = $(SRC)
|
||||
@echo STATICALLY_LINK_TCL = $(STATICALLY_LINK_TCL)
|
||||
@echo ST_COMPILE_OPTS = $(ST_COMPILE_OPTS)
|
||||
@echo STORELIBPATH = $(STORELIBPATH)
|
||||
@echo SYMBOLS = $(SYMBOLS)
|
||||
@echo TCC = $(TCC)
|
||||
@echo TCLDIR = $(TCLDIR)
|
||||
@echo TCLINCDIR = $(TCLINCDIR)
|
||||
@echo TCLLIBDIR = $(TCLLIBDIR)
|
||||
@echo TCLLIBPATHS = $(TCLLIBPATHS)
|
||||
@echo TCLLIBS = $(TCLLIBS)
|
||||
@echo TCLSH_CMD = $(TCLSH_CMD)
|
||||
@echo TCLSQLITEEX = $(TCLSQLITEEX)
|
||||
@echo TCLSUFFIX = $(TCLSUFFIX)
|
||||
@echo TCLVERSION = $(TCLVERSION)
|
||||
@echo TEST_CCONV_OPTS = $(TEST_CCONV_OPTS)
|
||||
@echo TESTEXT = $(TESTEXT)
|
||||
@echo TESTFIXTURE_DEP = $(TESTFIXTURE_DEP)
|
||||
@echo TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS)
|
||||
@echo TESTFIXTURE_SRC = $(TESTFIXTURE_SRC)
|
||||
@echo TESTOPTS = $(TESTOPTS)
|
||||
@echo TESTPROGS = $(TESTPROGS)
|
||||
@echo TESTSRC = $(TESTSRC)
|
||||
@echo TLIBS = $(TLIBS)
|
||||
@echo TOP = $(TOP)
|
||||
@echo UCRTLIBPATH = $(UCRTLIBPATH)
|
||||
@echo USE_AMALGAMATION = $(USE_AMALGAMATION)
|
||||
@echo USE_CRT_DLL = $(USE_CRT_DLL)
|
||||
@echo USE_FATAL_WARN = $(USE_FATAL_WARN)
|
||||
@echo USE_FULLWARN = $(USE_FULLWARN)
|
||||
@echo USE_ICU = $(USE_ICU)
|
||||
@echo USE_LISTINGS = $(USE_LISTINGS)
|
||||
@echo USE_NATIVE_LIBPATHS = $(USE_NATIVE_LIBPATHS)
|
||||
@echo USE_RC = $(USE_RC)
|
||||
@echo USE_RUNTIME_CHECKS = $(USE_RUNTIME_CHECKS)
|
||||
@echo USE_SEH = $(USE_SEH)
|
||||
@echo USE_STDCALL = $(USE_STDCALL)
|
||||
@echo USE_ZLIB = $(USE_ZLIB)
|
||||
@echo XCOMPILE = $(XCOMPILE)
|
||||
@echo ZLIBCFLAGS = $(ZLIBCFLAGS)
|
||||
@echo ZLIBDIR = $(ZLIBDIR)
|
||||
@echo ZLIBINCDIR = $(ZLIBINCDIR)
|
||||
@echo ZLIBLIBDIR = $(ZLIBLIBDIR)
|
||||
@echo ZLIBLIB = $(ZLIBLIB)
|
||||
|
||||
moreclean: clean
|
||||
del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL
|
||||
# <</mark>>
|
||||
|
||||
@@ -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
|
||||
@@ -100,16 +99,16 @@ script found at the root of the source tree. Then run "make".
|
||||
|
||||
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
|
||||
@@ -117,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"
|
||||
@@ -135,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.
|
||||
@@ -158,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.
|
||||
@@ -184,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
|
||||
|
||||
@@ -197,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.
|
||||
@@ -379,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.
|
||||
@@ -409,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.
|
||||
|
||||
@@ -282,7 +282,7 @@ DIST_FILES := \
|
||||
README.txt VERSION \
|
||||
auto.def autosetup configure tea \
|
||||
sqlite3.h sqlite3.c shell.c sqlite3ext.h \
|
||||
Makefile.in Makefile.msc Makefile.fallback make.bat \
|
||||
Makefile.in Makefile.msc Makefile.fallback \
|
||||
sqlite3.rc sqlite3rc.h Replace.cs \
|
||||
sqlite3.pc.in sqlite3.1
|
||||
|
||||
|
||||
+90
-11
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
========================================================================
|
||||
|
||||
+4
-6
@@ -7409,13 +7409,11 @@ void *JimDefaultAllocator(void *ptr, size_t size)
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
else if (ptr) {
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
else {
|
||||
void *p = realloc(ptr, size);
|
||||
if( p==0 ){
|
||||
fprintf(stderr,"Out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return p;
|
||||
return malloc(size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1842,7 +1842,7 @@ proc proj-setup-autoreconfig {defName} {
|
||||
}
|
||||
|
||||
#
|
||||
# @prop-define-append defineName args...
|
||||
# @prop-append-to defineName args...
|
||||
#
|
||||
# A proxy for Autosetup's [define-append]. Appends all non-empty $args
|
||||
# to [define-append $defineName].
|
||||
@@ -1873,7 +1873,7 @@ proc proj-define-append {defineName args} {
|
||||
# but it is technically correct and still relevant on some
|
||||
# environments.
|
||||
#
|
||||
# See: proj-define-append
|
||||
# See: proj-append-to
|
||||
#
|
||||
proc proj-define-amend {args} {
|
||||
set defName ""
|
||||
|
||||
@@ -679,7 +679,6 @@ proc sqlite-check-common-system-deps {} {
|
||||
define HAVE_ZLIB 1
|
||||
define LDFLAGS_ZLIB -lz
|
||||
sqlite-add-shell-opt -DSQLITE_HAVE_ZLIB=1
|
||||
sqlite-add-feature-flag -DSQLITE_HAVE_ZLIB=1
|
||||
} else {
|
||||
define HAVE_ZLIB 0
|
||||
define LDFLAGS_ZLIB ""
|
||||
|
||||
+3
-10
@@ -22,10 +22,9 @@ guidance on building for Windows.
|
||||
or <https://sqlite.org/tmp/tcl9.0.0.tar.gz>.
|
||||
<li>Untar the source archive. CD into the "unix/" subfolder
|
||||
of the source tree.
|
||||
<li>Run:   `mkdir $HOME/local`
|
||||
<li>Run:   `./configure --prefix=$HOME/local`<br>
|
||||
SQLite deliverable builds add:   `--static CFLAGS=-Os`
|
||||
<li>Run:   `make install`
|
||||
<li>Run: `mkdir $HOME/local`
|
||||
<li>Run: `./configure --prefix=$HOME/local`
|
||||
<li>Run: `make install`
|
||||
</ol>
|
||||
<p>
|
||||
As of 2024-10-25, TCL is not longer required for many
|
||||
@@ -42,12 +41,6 @@ guidance on building for Windows.
|
||||
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.
|
||||
|
||||
The SQLite developers typically add the
|
||||
  `--with-linenoise=$HOME/linenoise`   option
|
||||
to provide command-line editing. "$HOME/linenoise" is a directory
|
||||
that contains [linenoise](https://github.com/antirez/linenoise) source
|
||||
code files, `linenoise.c` and `linenoise.h`.
|
||||
|
||||
6. Run the "`Makefile`" makefile with an appropriate target.
|
||||
Examples:
|
||||
<ul>
|
||||
|
||||
@@ -180,39 +180,3 @@ statically linked so that it does not depend on separate DLL:
|
||||
6. After your executable is built, you can verify that it does not
|
||||
depend on the TCL DLL by running:
|
||||
<blockquote><pre>dumpbin /dependents sqlite3_analyzer.exe</pre></blockquote>
|
||||
|
||||
## Linking Against ZLIB
|
||||
|
||||
Some feature (such as zip-file support in the CLI) require the ZLIB
|
||||
compression library. That library is more or less universally available
|
||||
on unix platforms, but is seldom provided on Windows. You will probably
|
||||
need to provide it yourself. Here the the steps needed:
|
||||
|
||||
1. Download the zlib-1.3.1.tar.gz tarball (or a similar version).
|
||||
Unpack the tarball sources. You can put them wherever you like.
|
||||
For the purposes of this document, let's assume you put the source
|
||||
tree in c:\\zlib-64. Note: If you are building for both x64 and
|
||||
x86, you will need separate builds of ZLIB for each, thus separate
|
||||
build directories.
|
||||
|
||||
2. Before building SQLite (as described above) first make these
|
||||
environment changes. The lead-programmer for SQLite (who writes
|
||||
these words) has BAT files named "env-x64.bat" and "env-x32.bat"
|
||||
and "env-arm64.bat" that make these changes, and he runs those
|
||||
BAT file whenever he starts a new shell. These are the settings
|
||||
needed:
|
||||
<blockquote>
|
||||
set USE_ZLIB=1<br>
|
||||
set BUILD_ZLIB=0<br>
|
||||
set ZLIBDIR=c:\\zlib-64
|
||||
</blockquote>
|
||||
|
||||
3. Because the settings in step 2 specify "BUILD_ZLIB=0", you will need
|
||||
to build the library at least once. I recommand:
|
||||
<blockquote>
|
||||
make clean sqlite3.exe BUILD_ZLIB=1
|
||||
</blockquote>
|
||||
|
||||
4. After making the environment changes specified in steps 1 through 3
|
||||
above, you then build and test SQLite as you normally would. The
|
||||
environment variable changes will cause ZLIB to be linked automatically.
|
||||
|
||||
+57
-102
@@ -4,12 +4,6 @@
|
||||
|
||||
<ul type=none>
|
||||
<li> 1. <a href=#overview>Overview</a>
|
||||
<ul type=none>
|
||||
<li> 1.1. <a href=#runtr>Running testrunner.tcl</a>
|
||||
<li> 1.2. <a href=#runviamake>Run using "make"</a>
|
||||
<li> 1.3. <a href=#outputs>Outputs from testrunner.tcl</a>
|
||||
<li> 1.4. <a href=#help>Built-in help</a>
|
||||
</ul>
|
||||
<li> 2. <a href=#binary_tests>Binary Tests</a>
|
||||
<ul type=none>
|
||||
<li> 2.1. <a href=#organization_tests>Organization of Tcl Tests</a>
|
||||
@@ -32,40 +26,17 @@
|
||||
|
||||
The testrunner.tcl program is a Tcl script used to run multiple SQLite
|
||||
tests in parallel, thus reducing testing time on multi-core machines.
|
||||
The testrunner.tcl supports running tests that based on `testfixture`,
|
||||
`sqlite3`, and `fuzzcheck`.
|
||||
It supports the following types of tests:
|
||||
|
||||
<a name="runtr"></a>
|
||||
## 1.1 Running testrunner.tcl
|
||||
* Tcl test scripts.
|
||||
|
||||
The testrunner.tcl script is located in the "test" subdirectory of the
|
||||
SQLite source tree. So if your shell is current positioned at the top
|
||||
of the source tree, you would normally run the script using the command:
|
||||
"<tt>test/testrunner.tcl</tt>". On Windows, you have to specify the
|
||||
`tclsh` interpreter command first, like this:
|
||||
"<tt>tclsh test/testrunner.tcl</tt>".
|
||||
* Fuzzcheck tests, including using an external fuzzcheck database.
|
||||
|
||||
In this document, we will assume that you are on a unix-like OS
|
||||
(not on Windows) and that your current directory is the root
|
||||
of the SQLite source tree, and so all invocations of the testrunner.tcl
|
||||
script will be of the form "<tt>test/testrunner.tcl</tt>". If you
|
||||
are in a different directory, then make appropriate adjustments to
|
||||
the path. On Windows, add the "<tt>tclsh</tt>" interpreter command
|
||||
up front.
|
||||
|
||||
<a name="runviamake"></a>
|
||||
## 1.2 Run using make
|
||||
|
||||
The standard Makefiles for SQLite include targets that invoke
|
||||
testrunner.tcl. So the following commands also run testrunner.tcl:
|
||||
|
||||
* `make devtest`
|
||||
* `make releasetest`
|
||||
* `make sdevtest`
|
||||
* `make testrunner`
|
||||
|
||||
<a name="outputs"></a>
|
||||
## 1.3 Outputs from testrunner.tcl
|
||||
* Tests run with `make` commands. Examples:
|
||||
- `make devtest`
|
||||
- `make releasetest`
|
||||
- `make sdevtest`
|
||||
- `make testrunner`
|
||||
|
||||
The testrunner.tcl program stores output of all tests and builds run in
|
||||
log file **testrunner.log**, created in the current working directory.
|
||||
@@ -83,19 +54,17 @@ A useful query might be:
|
||||
```
|
||||
|
||||
You can get a summary of errors in a prior run by invoking commands like
|
||||
those shown below. Note that the testrunner.tcl script can be run directly
|
||||
on unix systems (including Macs) but you will need to add <tt>tclsh</tt>
|
||||
to the front on Windows.
|
||||
these:
|
||||
|
||||
```
|
||||
test/testrunner.tcl errors
|
||||
test/testrunner.tcl errors -v
|
||||
tclsh $(TESTDIR)/testrunner.tcl errors
|
||||
tclsh $(TESTDIR)/testrunner.tcl errors -v
|
||||
```
|
||||
|
||||
Running the command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl status
|
||||
tclsh $(TESTDIR)/testrunner.tcl status
|
||||
```
|
||||
|
||||
in the directory containing the testrunner.db database runs various queries
|
||||
@@ -104,43 +73,32 @@ A good way to keep and eye on test progress is to run either of the two
|
||||
following commands:
|
||||
|
||||
```
|
||||
watch test/testrunner.tcl status
|
||||
test/testrunner.tcl status -d 2
|
||||
watch tclsh $(TESTDIR)/testrunner.tcl status
|
||||
tclsh $(TESTDIR)/testrunner.tcl status -d 2
|
||||
```
|
||||
|
||||
Both of the commands above accomplish about the same thing, but the second
|
||||
one has the advantage of not requiring "watch" to be installed on your
|
||||
system.
|
||||
|
||||
Sometimes testrunner.tcl uses the `testfixture` and `sqlite3` binaries that
|
||||
are in the directory from which testrunner.tcl is run.
|
||||
(see "Binary Tests" below). Sometimes it builds testfixture and
|
||||
Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
|
||||
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
|
||||
other binaries in specific configurations to test (see "Source Tests").
|
||||
|
||||
<a name=help></a>
|
||||
## 1.4 Built-in help
|
||||
|
||||
Run this command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl help
|
||||
```
|
||||
|
||||
To get a summary of all of the various command-line options available
|
||||
with testrunner.tcl
|
||||
|
||||
|
||||
<a name=binary_tests></a>
|
||||
# 2. Binary Tests
|
||||
|
||||
The commands described in this section all run various combinations of the Tcl
|
||||
test scripts using whatever `testfixture` binary (and maybe also the `sqlite3`
|
||||
binary, depending on the test) that is found in the directory from which
|
||||
testrunner.tcl is launched. So typically, one must first run something
|
||||
like "`make testfixture sqlite3`" before launching binary tests. In other
|
||||
words, testrunner.tcl does not automatically build the binaries under test
|
||||
for binary tests. The testrunner.tcl expects the binaries to be available
|
||||
already.
|
||||
test scripts using the `testfixture` binary used to run the testrunner.tcl
|
||||
script (i.e. they do not invoke the compiler to build new binaries, or the
|
||||
`make` command to run tests that are not Tcl scripts). The procedure to run
|
||||
these tests is therefore:
|
||||
|
||||
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
|
||||
whatever method seems convenient.
|
||||
|
||||
2. Test the binary built in step 1 by running testrunner.tcl with it,
|
||||
perhaps with various options.
|
||||
|
||||
The following sub-sections describe the various options that can be
|
||||
passed to testrunner.tcl to test binary testfixture builds.
|
||||
@@ -182,22 +140,22 @@ are defined in file *testrunner_data.tcl*.
|
||||
To run the "veryquick" test set, use either of the following:
|
||||
|
||||
```
|
||||
test/testrunner.tcl
|
||||
test/testrunner.tcl veryquick
|
||||
./testfixture $TESTDIR/testrunner.tcl
|
||||
./testfixture $TESTDIR/testrunner.tcl veryquick
|
||||
```
|
||||
|
||||
To run the "full" test suite:
|
||||
|
||||
```
|
||||
test/testrunner.tcl full
|
||||
./testfixture $TESTDIR/testrunner.tcl full
|
||||
```
|
||||
|
||||
To run the subset of the "full" test suite for which the test file name matches
|
||||
a specified pattern (e.g. all tests that start with "fts5"), either of:
|
||||
|
||||
```
|
||||
test/testrunner.tcl fts5%
|
||||
test/testrunner.tcl 'fts5*'
|
||||
./testfixture $TESTDIR/testrunner.tcl fts5%
|
||||
./testfixture $TESTDIR/testrunner.tcl 'fts5*'
|
||||
```
|
||||
|
||||
Strictly speaking, for a test to be run the pattern must match the script
|
||||
@@ -209,7 +167,7 @@ characters specified as part of the pattern are transformed to "\*".
|
||||
To run "all" tests (full + permutations):
|
||||
|
||||
```
|
||||
test/testrunner.tcl all
|
||||
./testfixture $TESTDIR/testrunner.tcl all
|
||||
```
|
||||
|
||||
<a name=binary_test_failures></a>
|
||||
@@ -228,15 +186,10 @@ If there is no permutation, the individual test script may be run with:
|
||||
Or, if the failure occured as part of a permutation:
|
||||
|
||||
```
|
||||
test/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
|
||||
./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
|
||||
```
|
||||
|
||||
One can also rerun all tests that failed or did not complete
|
||||
in the previous invocation by typing:
|
||||
|
||||
```
|
||||
test/testrunner.tcl retest
|
||||
```
|
||||
TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
|
||||
|
||||
<a name=source_code_tests></a>
|
||||
# 3. Source Code Tests
|
||||
@@ -251,9 +204,11 @@ other tests. The advantages of this are that:
|
||||
* it ensures that tests are always run using binaries created with the
|
||||
same set of compiler options.
|
||||
|
||||
The testrunner.tcl commands described in this section do not require that
|
||||
the testfixture and/or sqlite3 binaries be built ahead of time. Those
|
||||
binaries will be constructed automatically.
|
||||
The testrunner.tcl commands described in this section may be run using
|
||||
either a *testfixture* (or testfixture.exe) build, or with any other Tcl
|
||||
shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
|
||||
|
||||
TODO: ./configure + Makefile.msc build systems.
|
||||
|
||||
<a name=commands_to_run_tests></a>
|
||||
## 3.1. Commands to Run SQLite Tests
|
||||
@@ -263,7 +218,7 @@ the `make fuzztest` target once for each of two --enable-all builds - one
|
||||
with debugging enabled and one without:
|
||||
|
||||
```
|
||||
test/testrunner.tcl mdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl mdevtest
|
||||
```
|
||||
|
||||
In other words, it is equivalent to running:
|
||||
@@ -272,13 +227,13 @@ In other words, it is equivalent to running:
|
||||
$TOP/configure --enable-all --enable-debug
|
||||
make fuzztest
|
||||
make testfixture
|
||||
$TOP/test/testrunner.tcl veryquick
|
||||
./testfixture $TOP/test/testrunner.tcl veryquick
|
||||
|
||||
# Then, after removing files created by the tests above:
|
||||
$TOP/configure --enable-all OPTS="-O0"
|
||||
make fuzztest
|
||||
make testfixture
|
||||
$TOP/test/testrunner.tcl veryquick
|
||||
./testfixture $TOP/test/testrunner.tcl veryquick
|
||||
```
|
||||
|
||||
The **sdevtest** command is identical to the mdevtest command, except that the
|
||||
@@ -286,7 +241,7 @@ second of the two builds is a sanitizer build. Specifically, this means that
|
||||
OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
|
||||
|
||||
```
|
||||
test/testrunner.tcl sdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl sdevtest
|
||||
```
|
||||
|
||||
The **release** command runs lots of tests under lots of builds. It runs
|
||||
@@ -295,7 +250,7 @@ on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
|
||||
of the specific tests run.
|
||||
|
||||
```
|
||||
test/testrunner.tcl release
|
||||
tclsh $TESTDIR/testrunner.tcl release
|
||||
```
|
||||
|
||||
As with <a href=#source code tests>source code tests</a>, one or more patterns
|
||||
@@ -303,7 +258,7 @@ may be appended to any of the above commands (mdevtest, sdevtest or release).
|
||||
Pattern matching is used for both Tcl tests and fuzz tests.
|
||||
|
||||
```
|
||||
test/testrunner.tcl release rtree%
|
||||
tclsh $TESTDIR/testrunner.tcl release rtree%
|
||||
```
|
||||
|
||||
<a name=zipvfs_tests></a>
|
||||
@@ -313,14 +268,14 @@ testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
|
||||
tests from the Zipvfs project with the following command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
|
||||
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
|
||||
```
|
||||
|
||||
This can be combined with any of "mdevtest", "sdevtest" or "release" to
|
||||
test both SQLite and Zipvfs with a single command:
|
||||
|
||||
```
|
||||
test/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
|
||||
```
|
||||
|
||||
<a name=source_code_test_failures></a>
|
||||
@@ -340,10 +295,10 @@ a dos \*.bat file on windows. For example:
|
||||
```
|
||||
# Create a script that recreates build configuration "Device-One" on
|
||||
# Linux or OSX:
|
||||
test/testrunner.tcl script Device-One > make.sh
|
||||
tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
|
||||
|
||||
# Create a script that recreates build configuration "Have-Not" on Windows:
|
||||
test/testrunner.tcl script Have-Not > make.bat
|
||||
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
|
||||
```
|
||||
|
||||
The generated bash or \*.bat file script accepts a single argument - a makefile
|
||||
@@ -366,7 +321,7 @@ Thus, for example, to run a full releasetest including an external
|
||||
dbsqlfuzz database, run a command like one of these:
|
||||
|
||||
```
|
||||
test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
|
||||
tclsh test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
|
||||
FUZZDB=../fuzz/20250415.db make releasetest
|
||||
nmake /f Makefile.msc FUZZDB=../fuzz/20250415.db releasetest
|
||||
```
|
||||
@@ -376,7 +331,7 @@ databases. So if you want to run *only* tests involving the external
|
||||
database, you can use a command something like this:
|
||||
|
||||
```
|
||||
test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
|
||||
tclsh test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
|
||||
```
|
||||
|
||||
<a name=testrunner_options></a>
|
||||
@@ -390,7 +345,7 @@ required by a test, not to run any actual tests. For example:
|
||||
|
||||
```
|
||||
# Build binaries required by release test.
|
||||
test/testrunner.tcl --buildonly release"
|
||||
tclsh $TESTDIR/testrunner.tcl --buildonly release"
|
||||
```
|
||||
|
||||
The **--dryrun** option prevents testrunner.tcl from building any binaries
|
||||
@@ -399,7 +354,7 @@ would normally execute into the testrunner.log file. Example:
|
||||
|
||||
```
|
||||
# Log the shell commmands that make up the mdevtest test.
|
||||
test/testrunner.tcl --dryrun mdevtest"
|
||||
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
|
||||
```
|
||||
|
||||
The **--explain** option is similar to --dryrun in that it prevents
|
||||
@@ -409,7 +364,7 @@ summary of all the builds and tests that would have been run.
|
||||
|
||||
```
|
||||
# Show what builds and tests would have been run
|
||||
test/testrunner.tcl --explain mdevtest
|
||||
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
|
||||
```
|
||||
|
||||
The **--status** option uses VT100 escape sequences to display the test
|
||||
@@ -425,7 +380,7 @@ When running either binary or source code tests, testrunner.tcl reports the
|
||||
number of jobs it intends to use to stdout. e.g.
|
||||
|
||||
```
|
||||
$ test/testrunner.tcl
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl
|
||||
splitting work across 16 jobs
|
||||
... more output ...
|
||||
```
|
||||
@@ -435,7 +390,7 @@ of real cores on the machine. This can be overridden using the "--jobs" (or -j)
|
||||
switch:
|
||||
|
||||
```
|
||||
$ test/testrunner.tcl --jobs 8
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
|
||||
splitting work across 8 jobs
|
||||
... more output ...
|
||||
```
|
||||
@@ -445,5 +400,5 @@ running by exucuting the following command from the directory containing the
|
||||
testrunner.log and testrunner.db files:
|
||||
|
||||
```
|
||||
$ test/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
|
||||
```
|
||||
|
||||
+6
-3
@@ -1816,7 +1816,9 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
|
||||
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
|
||||
if( !zSql ) return SQLITE_NOMEM;
|
||||
p->bLock++;
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
|
||||
rc = sqlite3_prepare_v3(
|
||||
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
|
||||
);
|
||||
p->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
@@ -3391,7 +3393,9 @@ static int fts3FilterMethod(
|
||||
}
|
||||
if( zSql ){
|
||||
p->bLock++;
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt);
|
||||
rc = sqlite3_prepare_v3(
|
||||
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
|
||||
);
|
||||
p->bLock--;
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
@@ -4014,7 +4018,6 @@ static int fts3IntegrityMethod(
|
||||
|
||||
UNUSED_PARAMETER(isQuick);
|
||||
rc = sqlite3Fts3IntegrityCheck(p, &bOk);
|
||||
assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK );
|
||||
assert( rc!=SQLITE_CORRUPT_VTAB );
|
||||
if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
|
||||
@@ -601,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);
|
||||
|
||||
+10
-32
@@ -98,9 +98,9 @@ typedef struct SegmentWriter SegmentWriter;
|
||||
** incrementally. See function fts3PendingListAppend() for details.
|
||||
*/
|
||||
struct PendingList {
|
||||
sqlite3_int64 nData;
|
||||
int nData;
|
||||
char *aData;
|
||||
sqlite3_int64 nSpace;
|
||||
int nSpace;
|
||||
sqlite3_int64 iLastDocid;
|
||||
sqlite3_int64 iLastCol;
|
||||
sqlite3_int64 iLastPos;
|
||||
@@ -273,24 +273,6 @@ struct SegmentNode {
|
||||
#define SQL_UPDATE_LEVEL_IDX 38
|
||||
#define SQL_UPDATE_LEVEL 39
|
||||
|
||||
/*
|
||||
** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL
|
||||
** is always set.
|
||||
*/
|
||||
int sqlite3Fts3PrepareStmt(
|
||||
Fts3Table *p, /* Prepare for this connection */
|
||||
const char *zSql, /* SQL to prepare */
|
||||
int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */
|
||||
int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */
|
||||
sqlite3_stmt **pp /* OUT: Prepared statement */
|
||||
){
|
||||
int f = SQLITE_PREPARE_FROM_DDL
|
||||
|((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0)
|
||||
|(bPersist ? SQLITE_PREPARE_PERSISTENT : 0);
|
||||
|
||||
return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to obtain an SQLite prepared statement handle
|
||||
** for the statement identified by the second argument. If successful,
|
||||
@@ -416,12 +398,12 @@ static int fts3SqlStmt(
|
||||
|
||||
pStmt = p->aStmt[eStmt];
|
||||
if( !pStmt ){
|
||||
int bAllowVtab = 0;
|
||||
int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
|
||||
char *zSql;
|
||||
if( eStmt==SQL_CONTENT_INSERT ){
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
|
||||
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
|
||||
bAllowVtab = 1;
|
||||
f &= ~SQLITE_PREPARE_NO_VTAB;
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
|
||||
@@ -429,7 +411,7 @@ static int fts3SqlStmt(
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt);
|
||||
rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
|
||||
sqlite3_free(zSql);
|
||||
assert( rc==SQLITE_OK || pStmt==0 );
|
||||
p->aStmt[eStmt] = pStmt;
|
||||
@@ -778,9 +760,7 @@ static int fts3PendingTermsAddOne(
|
||||
|
||||
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
|
||||
if( pList ){
|
||||
assert( (i64)pList->nData+(i64)nToken+(i64)sizeof(Fts3HashElem)
|
||||
<= (i64)p->nPendingData );
|
||||
p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
}
|
||||
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
|
||||
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
|
||||
@@ -793,9 +773,7 @@ static int fts3PendingTermsAddOne(
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( (i64)p->nPendingData + pList->nData + nToken
|
||||
+ sizeof(Fts3HashElem) <= 0x3fffffff );
|
||||
p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -3596,7 +3574,7 @@ static int fts3DoRebuild(Fts3Table *p){
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
@@ -5349,7 +5327,7 @@ int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
@@ -5479,7 +5457,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
v = atoi(&zVal[9]);
|
||||
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
|
||||
rc = SQLITE_OK;
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){
|
||||
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
|
||||
v = atoi(&zVal[11]);
|
||||
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
|
||||
rc = SQLITE_OK;
|
||||
|
||||
+1
-7
@@ -81,13 +81,7 @@ typedef sqlite3_uint64 u64;
|
||||
# define FLEXARRAY 1
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
/*
|
||||
** Constants for the largest and smallest possible 32-bit signed integers.
|
||||
*/
|
||||
# define LARGEST_INT32 ((int)(0x7fffffff))
|
||||
# define SMALLEST_INT32 ((int)((-1) - LARGEST_INT32))
|
||||
#endif
|
||||
|
||||
/* Truncate very long tokens to this many bytes. Hard limit is
|
||||
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
|
||||
|
||||
+1
-1
@@ -455,7 +455,7 @@ static void fts5SnippetFunction(
|
||||
|
||||
iBestCol = (iCol>=0 ? iCol : 0);
|
||||
nPhrase = pApi->xPhraseCount(pFts);
|
||||
aSeen = sqlite3_malloc64(nPhrase);
|
||||
aSeen = sqlite3_malloc(nPhrase);
|
||||
if( aSeen==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
|
||||
if( nIn<0 ){
|
||||
nIn = (int)strlen(pIn);
|
||||
}
|
||||
zRet = (char*)sqlite3_malloc64((i64)nIn+1);
|
||||
zRet = (char*)sqlite3_malloc(nIn+1);
|
||||
if( zRet ){
|
||||
memcpy(zRet, pIn, nIn);
|
||||
zRet[nIn] = '\0';
|
||||
|
||||
@@ -576,7 +576,7 @@ int sqlite3Fts5ConfigParse(
|
||||
sqlite3_int64 nByte;
|
||||
int bUnindexed = 0; /* True if there are one or more UNINDEXED */
|
||||
|
||||
*ppOut = pRet = (Fts5Config*)sqlite3_malloc64(sizeof(Fts5Config));
|
||||
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
|
||||
if( pRet==0 ) return SQLITE_NOMEM;
|
||||
memset(pRet, 0, sizeof(Fts5Config));
|
||||
pRet->pGlobal = pGlobal;
|
||||
@@ -1123,3 +1123,5 @@ void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ int sqlite3Fts5ExprNew(
|
||||
|
||||
assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
|
||||
if( sParse.rc==SQLITE_OK ){
|
||||
*ppNew = pNew = sqlite3_malloc64(sizeof(Fts5Expr));
|
||||
*ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
|
||||
if( pNew==0 ){
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
sqlite3Fts5ParseNodeFree(sParse.pExpr);
|
||||
@@ -466,7 +466,7 @@ int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
|
||||
p2->pRoot = 0;
|
||||
|
||||
if( sParse.rc==SQLITE_OK ){
|
||||
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64(
|
||||
Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
|
||||
p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
|
||||
);
|
||||
if( ap==0 ){
|
||||
|
||||
@@ -91,7 +91,7 @@ int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Hash *pNew;
|
||||
|
||||
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc64(sizeof(Fts5Hash));
|
||||
*ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
+35
-42
@@ -2074,7 +2074,6 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
while( 1 ){
|
||||
u64 iDelta = 0;
|
||||
|
||||
if( i>=n ) break;
|
||||
if( eDetail==FTS5_DETAIL_NONE ){
|
||||
/* todo */
|
||||
if( i<n && a[i]==0 ){
|
||||
@@ -2093,7 +2092,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
|
||||
/* If necessary, grow the pIter->aRowidOffset[] array. */
|
||||
if( iRowidOffset>=pIter->nRowidOffset ){
|
||||
i64 nNew = pIter->nRowidOffset + 8;
|
||||
int nNew = pIter->nRowidOffset + 8;
|
||||
int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int));
|
||||
if( aNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
@@ -5240,7 +5239,7 @@ static void fts5DoSecureDelete(
|
||||
int iSegid = pSeg->pSeg->iSegid;
|
||||
u8 *aPg = pSeg->pLeaf->p;
|
||||
int nPg = pSeg->pLeaf->nn;
|
||||
int iPgIdx = pSeg->pLeaf->szLeaf; /* Offset of page footer */
|
||||
int iPgIdx = pSeg->pLeaf->szLeaf;
|
||||
|
||||
u64 iDelta = 0;
|
||||
int iNextOff = 0;
|
||||
@@ -5319,7 +5318,7 @@ static void fts5DoSecureDelete(
|
||||
iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
|
||||
}
|
||||
assert_nc( iSOP==pSeg->iLeafOffset );
|
||||
iNextOff = iSOP + pSeg->nPos;
|
||||
iNextOff = pSeg->iLeafOffset + pSeg->nPos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5399,31 +5398,31 @@ static void fts5DoSecureDelete(
|
||||
** is another term following it on this page. So the subsequent term
|
||||
** needs to be moved to replace the term associated with the entry
|
||||
** being removed. */
|
||||
u64 nPrefix = 0;
|
||||
u64 nSuffix = 0;
|
||||
u64 nPrefix2 = 0;
|
||||
u64 nSuffix2 = 0;
|
||||
int nPrefix = 0;
|
||||
int nSuffix = 0;
|
||||
int nPrefix2 = 0;
|
||||
int nSuffix2 = 0;
|
||||
|
||||
iDelKeyOff = iNextOff;
|
||||
iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2);
|
||||
iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2);
|
||||
iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2);
|
||||
iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2);
|
||||
|
||||
if( iKey!=1 ){
|
||||
iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix);
|
||||
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix);
|
||||
}
|
||||
iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix);
|
||||
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix);
|
||||
|
||||
nPrefix = MIN(nPrefix, nPrefix2);
|
||||
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
|
||||
|
||||
if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){
|
||||
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
if( iKey!=1 ){
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
|
||||
}
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
|
||||
if( nPrefix2>(u64)pSeg->term.n ){
|
||||
if( nPrefix2>pSeg->term.n ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else if( nPrefix2>nPrefix ){
|
||||
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
|
||||
@@ -5454,7 +5453,7 @@ static void fts5DoSecureDelete(
|
||||
u8 *aTermIdx = &pTerm->p[pTerm->szLeaf];
|
||||
int nTermIdx = pTerm->nn - pTerm->szLeaf;
|
||||
int iTermIdx = 0;
|
||||
i64 iTermOff = 0;
|
||||
int iTermOff = 0;
|
||||
|
||||
while( 1 ){
|
||||
u32 iVal = 0;
|
||||
@@ -5465,15 +5464,12 @@ static void fts5DoSecureDelete(
|
||||
}
|
||||
nTermIdx = iTermIdx;
|
||||
|
||||
if( iTermOff>pTerm->szLeaf ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
|
||||
fts5PutU16(&pTerm->p[2], iTermOff);
|
||||
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
|
||||
if( nTermIdx==0 ){
|
||||
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
|
||||
}
|
||||
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
|
||||
fts5PutU16(&pTerm->p[2], iTermOff);
|
||||
|
||||
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
|
||||
if( nTermIdx==0 ){
|
||||
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
|
||||
}
|
||||
}
|
||||
fts5DataRelease(pTerm);
|
||||
@@ -5496,9 +5492,7 @@ static void fts5DoSecureDelete(
|
||||
int iPrevKeyOut = 0;
|
||||
int iKeyIn = 0;
|
||||
|
||||
if( nMove>0 ){
|
||||
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
|
||||
}
|
||||
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
|
||||
iPgIdx -= nShift;
|
||||
nPg = iPgIdx;
|
||||
fts5PutU16(&aPg[2], iPgIdx);
|
||||
@@ -5936,7 +5930,7 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
|
||||
fts5StructureRelease(pStruct);
|
||||
pStruct = pNew;
|
||||
nMin = 1;
|
||||
nMerge = (nMerge==SMALLEST_INT32 ? LARGEST_INT32 : (nMerge*-1));
|
||||
nMerge = nMerge*-1;
|
||||
}
|
||||
if( pStruct && pStruct->nLevel ){
|
||||
if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){
|
||||
@@ -6418,16 +6412,16 @@ struct Fts5TokenDataMap {
|
||||
** aMap[] variables.
|
||||
*/
|
||||
struct Fts5TokenDataIter {
|
||||
i64 nMapAlloc; /* Allocated size of aMap[] in entries */
|
||||
i64 nMap; /* Number of valid entries in aMap[] */
|
||||
int nMapAlloc; /* Allocated size of aMap[] in entries */
|
||||
int nMap; /* Number of valid entries in aMap[] */
|
||||
Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
|
||||
|
||||
/* The following are used for prefix-queries only. */
|
||||
Fts5Buffer terms;
|
||||
|
||||
/* The following are used for other full-token tokendata queries only. */
|
||||
i64 nIter;
|
||||
i64 nIterAlloc;
|
||||
int nIter;
|
||||
int nIterAlloc;
|
||||
Fts5PoslistReader *aPoslistReader;
|
||||
int *aPoslistToIter;
|
||||
Fts5Iter *apIter[FLEXARRAY];
|
||||
@@ -6483,11 +6477,11 @@ static void fts5TokendataIterAppendMap(
|
||||
){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pT->nMap==pT->nMapAlloc ){
|
||||
i64 nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
|
||||
i64 nAlloc = nNew * sizeof(Fts5TokenDataMap);
|
||||
int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
|
||||
int nAlloc = nNew * sizeof(Fts5TokenDataMap);
|
||||
Fts5TokenDataMap *aNew;
|
||||
|
||||
aNew = (Fts5TokenDataMap*)sqlite3_realloc64(pT->aMap, nAlloc);
|
||||
aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
|
||||
if( aNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
return;
|
||||
@@ -6513,7 +6507,7 @@ static void fts5TokendataIterAppendMap(
|
||||
*/
|
||||
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
|
||||
Fts5TokenDataMap *aTmp = 0;
|
||||
i64 nByte = pT->nMap * sizeof(Fts5TokenDataMap);
|
||||
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
|
||||
|
||||
aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
|
||||
if( aTmp ){
|
||||
@@ -7047,10 +7041,9 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
|
||||
i64 nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
|
||||
i64 nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
|
||||
Fts5TokenDataIter *pNew;
|
||||
pNew = (Fts5TokenDataIter*)sqlite3_realloc64(pIn, nByte);
|
||||
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
|
||||
int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
|
||||
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
|
||||
|
||||
if( pNew==0 ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
@@ -7147,8 +7140,8 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
|
||||
|
||||
/* Ensure the token-mapping is large enough */
|
||||
if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
|
||||
i64 nNew = (pT->nMapAlloc + nByte) * 2;
|
||||
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc64(
|
||||
int nNew = (pT->nMapAlloc + nByte) * 2;
|
||||
Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
|
||||
pT->aMap, nNew*sizeof(Fts5TokenDataMap)
|
||||
);
|
||||
if( aNew==0 ){
|
||||
|
||||
@@ -631,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;
|
||||
@@ -2081,7 +2081,6 @@ static int fts5UpdateMethod(
|
||||
}
|
||||
|
||||
update_out:
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
return rc;
|
||||
}
|
||||
@@ -3763,7 +3762,7 @@ static int fts5Init(sqlite3 *db){
|
||||
int rc;
|
||||
Fts5Global *pGlobal = 0;
|
||||
|
||||
pGlobal = (Fts5Global*)sqlite3_malloc64(sizeof(Fts5Global));
|
||||
pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
|
||||
if( pGlobal==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
+4
-4
@@ -391,7 +391,7 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
break;
|
||||
}
|
||||
CASE(12, "xSetAuxdata") {
|
||||
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc64(sizeof(F5tAuxData));
|
||||
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
|
||||
if( pData==0 ){
|
||||
Tcl_AppendResult(interp, "out of memory", (char*)0);
|
||||
return TCL_ERROR;
|
||||
@@ -780,7 +780,7 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
}
|
||||
|
||||
if( nText>0 ){
|
||||
pCopy = sqlite3_malloc64(nText);
|
||||
pCopy = sqlite3_malloc(nText);
|
||||
if( pCopy==0 ){
|
||||
tokenizer.xDelete(pTok);
|
||||
Tcl_AppendResult(interp, "error in sqlite3_malloc()", (char*)0);
|
||||
@@ -1420,7 +1420,7 @@ static int f5tOrigintextCreate(
|
||||
void *pTokCtx = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
pTok = (OriginTextTokenizer*)sqlite3_malloc64(sizeof(OriginTextTokenizer));
|
||||
pTok = (OriginTextTokenizer*)sqlite3_malloc(sizeof(OriginTextTokenizer));
|
||||
if( pTok==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else if( nArg<1 ){
|
||||
@@ -1480,7 +1480,7 @@ static int xOriginToken(
|
||||
int nReq = nToken + 1 + (iEnd-iStart);
|
||||
if( nReq>p->nBuf ){
|
||||
sqlite3_free(p->aBuf);
|
||||
p->aBuf = sqlite3_malloc64(nReq*2);
|
||||
p->aBuf = sqlite3_malloc(nReq*2);
|
||||
if( p->aBuf==0 ) return SQLITE_NOMEM;
|
||||
p->nBuf = nReq*2;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ static int fts5tokConnectMethod(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (Fts5tokTable*)sqlite3_malloc64(sizeof(Fts5tokTable));
|
||||
pTab = (Fts5tokTable*)sqlite3_malloc(sizeof(Fts5tokTable));
|
||||
if( pTab==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -275,7 +275,7 @@ static int fts5tokBestIndexMethod(
|
||||
static int fts5tokOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
||||
Fts5tokCursor *pCsr;
|
||||
|
||||
pCsr = (Fts5tokCursor *)sqlite3_malloc64(sizeof(Fts5tokCursor));
|
||||
pCsr = (Fts5tokCursor *)sqlite3_malloc(sizeof(Fts5tokCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ static int fts5tokCb(
|
||||
if( pCsr->nRow ){
|
||||
pRow->iPos = pRow[-1].iPos + ((tflags & FTS5_TOKEN_COLOCATED) ? 0 : 1);
|
||||
}
|
||||
pRow->zToken = sqlite3_malloc64((sqlite3_int64)nToken+1);
|
||||
pRow->zToken = sqlite3_malloc(nToken+1);
|
||||
if( pRow->zToken==0 ) return SQLITE_NOMEM;
|
||||
memcpy(pRow->zToken, pToken, nToken);
|
||||
pRow->zToken[nToken] = 0;
|
||||
@@ -373,8 +373,8 @@ static int fts5tokFilterMethod(
|
||||
fts5tokResetCursor(pCsr);
|
||||
if( idxNum==1 ){
|
||||
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
|
||||
sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]);
|
||||
pCsr->zInput = sqlite3_malloc64(nByte+1);
|
||||
int nByte = sqlite3_value_bytes(apVal[0]);
|
||||
pCsr->zInput = sqlite3_malloc(nByte+1);
|
||||
if( pCsr->zInput==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
@@ -72,7 +72,7 @@ static int fts5AsciiCreate(
|
||||
if( nArg%2 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p = sqlite3_malloc64(sizeof(AsciiTokenizer));
|
||||
p = sqlite3_malloc(sizeof(AsciiTokenizer));
|
||||
if( p==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -367,7 +367,7 @@ static int fts5UnicodeCreate(
|
||||
if( nArg%2 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p = (Unicode61Tokenizer*)sqlite3_malloc64(sizeof(Unicode61Tokenizer));
|
||||
p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
|
||||
if( p ){
|
||||
const char *zCat = "L* N* Co";
|
||||
int i;
|
||||
@@ -590,7 +590,7 @@ static int fts5PorterCreate(
|
||||
zBase = azArg[0];
|
||||
}
|
||||
|
||||
pRet = (PorterTokenizer*)sqlite3_malloc64(sizeof(PorterTokenizer));
|
||||
pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
|
||||
if( pRet ){
|
||||
memset(pRet, 0, sizeof(PorterTokenizer));
|
||||
rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2);
|
||||
@@ -1297,7 +1297,7 @@ static int fts5TriCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
int i;
|
||||
pNew = (TrigramTokenizer*)sqlite3_malloc64(sizeof(*pNew));
|
||||
pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
+1
-12
@@ -396,12 +396,7 @@ static int fts5VocabOpenMethod(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Restore cursor pCsr to the state it was in immediately after being
|
||||
** created by the xOpen() method.
|
||||
*/
|
||||
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
|
||||
int nCol = pCsr->pFts5->pConfig->nCol;
|
||||
pCsr->rowid = 0;
|
||||
sqlite3Fts5IterClose(pCsr->pIter);
|
||||
sqlite3Fts5StructureRelease(pCsr->pStruct);
|
||||
@@ -411,12 +406,6 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
|
||||
pCsr->nLeTerm = -1;
|
||||
pCsr->zLeTerm = 0;
|
||||
pCsr->bEof = 0;
|
||||
pCsr->iCol = 0;
|
||||
pCsr->iInstPos = 0;
|
||||
pCsr->iInstOff = 0;
|
||||
pCsr->colUsed = 0;
|
||||
memset(pCsr->aCnt, 0, sizeof(i64)*nCol);
|
||||
memset(pCsr->aDoc, 0, sizeof(i64)*nCol);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -666,7 +655,7 @@ static int fts5VocabFilterMethod(
|
||||
const char *zCopy = (const char *)sqlite3_value_text(pLe);
|
||||
if( zCopy==0 ) zCopy = "";
|
||||
pCsr->nLeTerm = sqlite3_value_bytes(pLe);
|
||||
pCsr->zLeTerm = sqlite3_malloc64((i64)pCsr->nLeTerm+1);
|
||||
pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
|
||||
if( pCsr->zLeTerm==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
|
||||
@@ -16124,367 +16124,5 @@ do_catchsql_test 83.1 {
|
||||
SELECT * FROM t1('R*R*R*R*R*R*R*R*') WHERE (a,b)<=(current_date,0 BETWEEN 'a'<>11 AND '') ORDER BY rowid DESC;
|
||||
} {/.*fts5: corruption found/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 84.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 53248 pagesize 4096 filename c1a.txt.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 0d .....@ ........
|
||||
| 32: 00 00 00 02 00 00 00 01 00 00 00 09 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6
|
||||
| 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 0d 92 00 00 00 00 ...k............
|
||||
| 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
|
||||
| 3488: 32 74 32 0d 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
|
||||
| 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
|
||||
| 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
|
||||
| 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
|
||||
| 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
|
||||
| 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[.
|
||||
| 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
|
||||
| 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
|
||||
| 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
|
||||
| 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
|
||||
| 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...!
|
||||
| 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont
|
||||
| 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR
|
||||
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c
|
||||
| 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG
|
||||
| 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
|
||||
| 3776: 63 30 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c0, c1, c2)i....
|
||||
| 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt
|
||||
| 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB
|
||||
| 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi
|
||||
| 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P
|
||||
| 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid
|
||||
| 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT
|
||||
| 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t
|
||||
| 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da
|
||||
| 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE
|
||||
| 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT
|
||||
| 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
|
||||
| 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8..
|
||||
| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR
|
||||
| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB
|
||||
| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5
|
||||
| 4032: 28 61 2c 62 2c 63 29 00 00 00 39 00 00 00 00 00 (a,b,c)...9.....
|
||||
| page 3 offset 8192
|
||||
| 0: 05 00 00 00 02 0f f1 00 00 00 00 0c 0f fb 0f f1 ................
|
||||
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0a ........:.^.:.:.
|
||||
| 4080: 0a 00 00 00 0b 84 80 80 80 80 01 00 00 00 0a 0a ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 7 offset 24576
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| page 10 offset 36864
|
||||
| 0: 0d 00 00 00 02 0f e2 00 0f e2 0f ef 00 00 00 00 ................
|
||||
| 4064: 00 00 0b 01 03 00 1c 81 3a 84 5e 81 3a 81 3a 0f ........:.^.:.:.
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 02 00 01 01 01 09 ...$............
|
||||
| page 11 offset 40960
|
||||
| 0: 0d 00 00 00 01 00 22 00 00 22 00 00 00 00 00 00 ................
|
||||
| 32: 00 00 9f 56 84 80 80 80 80 01 04 00 bf 30 00 00 ...V.........0..
|
||||
| 48: 0f 58 02 30 30 19 02 05 01 02 05 01 02 05 16 02 .X.00...........
|
||||
| 64: 05 01 02 05 01 02 05 61 02 05 01 02 05 01 02 05 .......a........
|
||||
| 80: 13 02 05 01 02 05 01 02 05 0d 02 03 01 02 03 01 ................
|
||||
| 96: 02 03 02 09 78 66 66 66 66 66 66 66 65 81 17 02 ....xfffffffe...
|
||||
| 112: 05 01 02 05 01 02 05 01 01 31 04 02 04 01 02 04 .........1......
|
||||
| 128: 01 02 04 01 02 05 01 02 05 01 02 05 0d 02 06 01 ................
|
||||
| 144: 02 06 01 02 06 02 01 30 79 02 04 01 02 04 01 02 .......0y.......
|
||||
| 160: 04 03 02 30 30 2b 02 05 01 02 05 01 02 05 58 02 ...00+........X.
|
||||
| 176: 05 01 02 05 01 02 05 01 02 05 01 02 05 01 02 05 ................
|
||||
| 192: 16 02 05 01 02 05 01 02 05 05 06 30 30 30 30 30 ...........00000
|
||||
| 208: 30 81 0b 02 04 01 02 04 01 02 04 10 02 05 01 02 0...............
|
||||
| 224: 05 01 02 05 03 02 32 34 76 02 05 01 02 05 01 02 ......24v.......
|
||||
| 240: 05 02 01 38 07 02 04 01 02 04 01 02 04 01 01 32 ...8...........2
|
||||
| 256: 28 02 04 01 02 04 01 02 04 04 02 05 01 02 05 01 (...............
|
||||
| 272: 02 05 02 01 30 1f 02 05 01 02 05 01 02 05 03 02 ....0...........
|
||||
| 288: 30 30 10 02 05 01 02 05 01 02 05 6a 02 04 01 02 00.........j....
|
||||
| 304: 04 01 02 04 02 08 35 30 30 30 30 30 30 30 81 26 ......50000000.&
|
||||
| 320: 02 05 01 02 05 01 02 05 01 01 33 07 02 06 01 02 ..........3.....
|
||||
| 336: 06 01 02 06 81 2c 02 04 01 02 04 01 02 04 02 04 .....,..........
|
||||
| 352: 32 37 36 36 81 23 02 05 01 02 05 01 02 05 01 01 2766.#..........
|
||||
| 368: 34 13 02 05 01 02 05 01 02 05 02 03 30 39 36 1c 4...........096.
|
||||
| 384: 02 05 01 02 05 01 02 05 07 02 05 01 02 05 01 02 ................
|
||||
| 400: 05 01 03 35 30 30 7f 02 05 01 02 05 01 02 05 04 ...500..........
|
||||
| 416: 02 30 30 81 0e 02 06 01 02 06 01 02 06 06 03 30 .00............0
|
||||
| 432: 30 30 81 11 02 04 01 02 04 01 02 04 01 05 36 35 00............65
|
||||
| 448: 35 33 36 81 1a 02 05 01 02 05 01 02 05 01 04 38 536............8
|
||||
| 464: 31 39 32 81 02 02 06 01 02 06 01 02 06 01 05 61 192............a
|
||||
| 480: 6c 6c 6f 77 01 02 02 01 02 02 01 02 02 02 02 72 llow...........r
|
||||
| 496: 67 81 08 02 04 01 02 04 01 02 04 02 05 74 6f 6d g............tom
|
||||
| 512: 69 63 04 02 02 01 02 02 01 02 02 03 06 74 61 63 ic...........tac
|
||||
| 528: 68 65 64 79 02 03 01 02 03 01 02 03 02 0d 75 74 hedy..........ut
|
||||
| 544: 6f 63 68 65 63 6b 70 6f 69 6e 74 2b 02 04 01 02 ocheckpoint+....
|
||||
| 560: 04 01 02 04 05 06 76 61 63 75 75 6d 0d 02 03 01 ......vacuum....
|
||||
| 576: 02 03 01 02 03 01 06 62 69 6e 61 72 79 03 06 01 .......binary...
|
||||
| 592: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 608: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 624: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 640: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 656: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 672: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 688: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 704: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 720: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 736: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 752: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 768: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 784: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 800: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 816: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 832: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 848: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 864: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 880: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 896: 01 02 02 02 07 79 74 65 63 6f 64 65 37 02 03 01 .....ytecode7...
|
||||
| 912: 02 03 01 02 03 01 05 63 61 63 68 65 10 02 03 01 .......cache....
|
||||
| 928: 02 03 01 01 03 02 04 6c 61 6e 67 07 02 03 01 02 .......lang.....
|
||||
| 944: 03 01 02 03 02 05 6f 6c 75 6d 6e 7c 02 03 01 02 ......olumn|....
|
||||
| 960: 03 01 02 03 03 06 6d 6d 65 6e 74 73 43 02 04 01 ......mmentsC...
|
||||
| 976: 02 04 01 02 04 04 05 70 69 6c 65 72 07 02 02 01 .......piler....
|
||||
| 992: 02 02 01 02 02 05 04 6f 75 6e 64 7f 02 03 01 02 .......ound.....
|
||||
| 1008: 03 01 02 03 03 03 75 6e 74 81 17 02 04 01 02 04 ......unt.......
|
||||
| 1024: 01 02 04 02 05 75 72 73 6f 72 3a 02 03 01 02 03 .....ursor:.....
|
||||
| 1040: 01 02 03 01 06 64 62 70 61 67 65 3d 02 03 01 02 .....dbpage=....
|
||||
| 1056: 03 01 02 03 03 04 73 74 61 74 40 02 03 01 02 03 ......stat@.....
|
||||
| 1072: 01 02 03 02 04 65 62 75 67 0a 02 02 01 02 02 01 .....ebug.......
|
||||
| 1088: 02 02 03 05 66 61 75 6c 74 0d 02 02 01 02 02 01 ....fault.......
|
||||
| 1104: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1120: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1136: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1152: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1168: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1184: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1200: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 4f 02 ..............O.
|
||||
| 1216: 03 01 02 03 01 02 03 03 03 70 74 68 81 05 02 04 .........pth....
|
||||
| 1232: 01 02 04 01 02 04 19 02 04 01 02 04 01 02 04 02 ................
|
||||
| 1248: 05 69 72 65 63 74 34 02 02 01 02 02 01 02 02 01 .irect4.........
|
||||
| 1264: 06 65 6e 61 62 6c 65 37 02 02 01 02 02 01 02 02 .enable7........
|
||||
| 1280: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1296: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1312: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1328: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1344: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1360: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1376: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1392: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1408: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1424: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1440: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1456: 02 01 02 02 02 06 78 70 6c 61 69 6e 43 02 03 01 ......xplainC...
|
||||
| 1472: 02 03 01 02 03 04 01 72 81 05 02 03 01 02 03 01 .......r........
|
||||
| 1488: 02 03 03 07 74 65 6e 73 69 6f 6e 81 2f 02 04 01 ....tension./...
|
||||
| 1504: 02 04 01 02 04 01 04 66 69 6c 65 13 02 03 01 02 .......file.....
|
||||
| 1520: 03 01 02 03 02 05 6f 72 6d 61 74 13 02 04 01 02 ......ormat.....
|
||||
| 1536: 04 01 02 04 02 03 74 73 33 46 02 03 01 02 03 01 ......ts3F......
|
||||
| 1552: 02 03 01 02 03 01 02 03 01 02 03 04 01 34 4c 02 .............4L.
|
||||
| 1568: 03 01 02 03 01 02 03 04 01 35 4f 02 03 01 02 03 .........5O.....
|
||||
| 1584: 01 02 03 02 03 75 6e 63 5e 02 05 01 02 05 01 02 .....unc^.......
|
||||
| 1600: 05 05 04 74 69 6f 6e 73 02 05 01 02 05 01 02 05 ...tions........
|
||||
| 1616: 13 02 03 01 02 03 01 02 03 09 01 73 55 02 04 01 ...........sU...
|
||||
| 1632: 02 04 01 02 04 01 07 67 65 6f 70 6f 6c 79 52 02 .......geopolyR.
|
||||
| 1648: 03 01 02 03 01 02 03 01 05 68 69 6e 74 73 3a 02 .........hints:.
|
||||
| 1664: 04 01 02 04 01 02 04 02 03 6f 6f 6b 61 02 04 01 .........ooka...
|
||||
| 1680: 02 04 01 02 04 01 02 69 6e 01 02 04 01 02 04 01 .......in.......
|
||||
| 1696: 02 04 03 04 69 74 73 7a 1f 02 04 01 02 04 01 02 ....itsz........
|
||||
| 1712: 04 03 08 74 72 69 6e 73 69 63 73 04 02 03 01 02 ...trinsics.....
|
||||
| 1728: 03 01 02 03 01 07 6a 6f 75 72 6e 61 6c 16 02 03 ......journal...
|
||||
| 1744: 01 02 03 01 02 03 01 06 6c 65 6e 67 74 68 81 0b ........length..
|
||||
| 1760: 02 03 01 02 03 01 02 03 01 02 05 01 02 05 01 02 ................
|
||||
| 1776: 05 0d 02 04 01 02 04 01 02 04 02 03 69 6b 65 81 ............ike.
|
||||
| 1792: 0e 02 03 01 02 03 01 02 03 03 03 6d 69 74 16 02 ...........mit..
|
||||
| 1808: 05 01 02 05 01 02 05 5e 02 04 01 02 04 01 02 04 .......^........
|
||||
| 1824: 02 03 6f 61 64 81 2f 02 03 01 02 03 01 02 03 01 ..oad./.........
|
||||
| 1840: 06 6d 61 6c 6c 6f 63 76 02 02 01 02 02 01 02 02 .mallocv........
|
||||
| 1856: 3a 02 03 01 02 03 01 02 03 03 02 74 68 55 02 03 :..........thU..
|
||||
| 1872: 01 02 03 01 02 03 03 01 78 79 02 02 01 02 02 01 ........xy......
|
||||
| 1888: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1904: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1920: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1936: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 1952: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 1968: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 1984: 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 2000: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 2016: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 2032: 02 02 02 05 65 6d 6f 72 79 81 11 02 03 01 02 03 ....emory.......
|
||||
| 2048: 01 02 03 04 04 73 79 73 35 58 02 03 01 02 03 01 .....sys5X......
|
||||
| 2064: 02 03 02 03 6d 61 70 19 02 03 01 02 03 01 02 03 ....map.........
|
||||
| 2080: 79 02 03 01 02 03 01 02 03 02 04 75 74 65 78 81 y..........utex.
|
||||
| 2096: 2c 02 02 01 02 02 01 02 02 01 06 6e 6f 63 61 73 ,..........nocas
|
||||
| 2112: 65 02 06 01 02 02 03 06 01 02 02 03 06 01 02 02 e...............
|
||||
| 2128: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2144: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2160: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2176: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2192: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2208: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2224: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2240: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2256: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2272: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2288: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2304: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2320: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2336: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2352: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2368: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2384: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2400: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2416: 02 02 03 06 01 02 02 03 07 72 6d 61 6c 69 7a 65 .........rmalize
|
||||
| 2432: 5b 02 03 01 02 03 01 02 03 02 05 75 6d 62 65 72 [..........umber
|
||||
| 2448: 81 23 02 04 01 02 04 01 02 04 01 06 6f 66 66 73 .#..........offs
|
||||
| 2464: 65 74 5e 02 03 01 02 03 01 02 03 02 03 6d 69 74 et^..........mit
|
||||
| 2480: 81 2c 02 03 01 02 03 01 02 03 01 02 02 01 02 02 .,..............
|
||||
| 2496: 01 02 02 02 01 70 81 26 02 04 01 02 04 01 02 04 .....p.&........
|
||||
| 2512: 02 07 76 65 72 66 6c 6f 77 34 02 03 01 02 03 01 ..verflow4......
|
||||
| 2528: 02 03 01 04 70 61 67 65 1c 02 03 01 02 03 01 02 ....page........
|
||||
| 2544: 03 64 02 04 01 02 04 01 02 04 13 02 03 01 02 03 .d..............
|
||||
| 2560: 01 02 03 01 02 03 01 02 03 01 02 03 03 09 72 65 ..............re
|
||||
| 2576: 6e 74 68 65 73 69 73 49 02 04 01 02 04 01 02 04 nthesisI........
|
||||
| 2592: 03 05 74 74 65 72 6e 81 0e 02 04 01 02 04 01 02 ..ttern.........
|
||||
| 2608: 04 02 05 63 61 63 68 65 1f 02 03 01 02 03 01 02 ...cache........
|
||||
| 2624: 03 02 08 72 65 75 70 64 61 74 65 61 02 03 01 02 ...reupdatea....
|
||||
| 2640: 03 01 02 03 01 04 72 65 61 64 34 02 04 01 02 04 ......read4.....
|
||||
| 2656: 01 02 04 03 07 63 75 72 73 69 76 65 22 02 03 01 .....cursive....
|
||||
| 2672: 02 03 01 02 03 02 04 6f 77 69 64 01 02 03 01 02 .......owid.....
|
||||
| 2688: 03 01 02 03 02 04 74 72 65 65 64 02 03 01 02 03 ......treed.....
|
||||
| 2704: 01 02 03 04 02 69 6d 01 06 01 02 02 03 06 01 02 .....im.........
|
||||
| 2720: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2736: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2752: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2768: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2784: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2800: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2816: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2832: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2848: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2864: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2880: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2896: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2912: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 2928: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 2944: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 2960: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 2976: 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 ................
|
||||
| 2992: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 3008: 01 02 02 03 06 01 02 02 03 06 01 02 02 01 0a 73 ...............s
|
||||
| 3024: 63 61 6e 73 74 61 74 75 73 70 02 04 01 02 04 01 canstatusp......
|
||||
| 3040: 02 04 02 05 65 63 74 6f 72 25 02 03 01 02 03 01 ....ector%......
|
||||
| 3056: 02 03 03 04 6c 65 63 74 7f 02 04 01 02 04 01 02 ....lect........
|
||||
| 3072: 04 03 05 73 73 69 6f 6e 67 02 03 01 02 03 01 02 ...ssiong.......
|
||||
| 3088: 03 02 03 69 7a 65 10 02 04 01 02 04 01 02 04 04 ...ize..........
|
||||
| 3104: 02 04 01 02 04 01 02 04 01 02 04 01 02 04 01 02 ................
|
||||
| 3120: 04 01 02 04 01 02 04 01 02 04 07 02 04 01 02 04 ................
|
||||
| 3136: 01 02 04 5b 02 05 01 02 05 01 02 05 10 02 04 01 ...[............
|
||||
| 3152: 02 04 01 02 04 04 02 04 01 02 04 01 02 04 02 03 ................
|
||||
| 3168: 6f 66 74 76 02 03 01 02 03 01 02 03 02 02 71 6c oftv..........ql
|
||||
| 3184: 5e 02 04 01 02 04 01 02 04 13 02 04 01 02 04 01 ^...............
|
||||
| 3200: 02 04 28 02 03 01 02 03 01 02 03 02 04 74 61 74 ..(..........tat
|
||||
| 3216: 34 6a 02 03 01 02 03 01 02 03 03 02 6d 74 70 02 4j..........mtp.
|
||||
| 3232: 03 01 02 03 01 02 03 05 04 76 74 61 62 6d 02 03 .........vtabm..
|
||||
| 3248: 01 02 03 01 02 03 03 03 6f 72 65 81 35 02 03 01 ........ore.5...
|
||||
| 3264: 02 03 01 02 03 02 0a 79 6e 63 68 72 6f 6e 6f 75 .......ynchronou
|
||||
| 3280: 73 28 02 03 01 02 03 01 02 03 04 02 04 01 02 04 s(..............
|
||||
| 3296: 01 02 04 03 04 73 74 65 6d 81 32 02 02 01 02 02 .....stem.2.....
|
||||
| 3312: 01 02 02 01 04 74 65 6d 70 81 35 02 02 01 02 02 .....temp.5.....
|
||||
| 3328: 01 02 02 02 06 68 72 65 61 64 73 31 02 04 01 02 .....hreads1....
|
||||
| 3344: 04 01 02 04 76 02 04 01 02 04 01 02 04 08 03 61 ....v..........a
|
||||
| 3360: 66 65 81 38 02 02 01 02 02 01 02 02 02 06 72 69 fe.8..........ri
|
||||
| 3376: 67 67 65 72 81 20 02 03 01 02 03 01 02 03 08 01 gger. ..........
|
||||
| 3392: 73 22 02 04 01 02 04 01 02 04 01 07 75 6e 6b 6e s...........unkn
|
||||
| 3408: 6f 77 6e 73 02 03 01 02 03 01 02 03 01 08 76 61 owns..........va
|
||||
| 3424: 72 69 61 62 6c 65 81 23 02 03 01 02 03 01 02 03 riable.#........
|
||||
| 3440: 02 03 64 62 65 81 26 02 03 01 02 03 01 02 03 02 ..dbe.&.........
|
||||
| 3456: 03 69 65 77 01 02 05 01 02 05 01 02 05 02 03 74 .iew...........t
|
||||
| 3472: 61 62 37 02 04 01 02 04 01 02 04 04 02 04 01 02 ab7.............
|
||||
| 3488: 04 01 02 04 01 02 04 01 02 04 01 02 04 01 03 77 ...............w
|
||||
| 3504: 61 6c 2b 02 03 01 02 03 01 02 03 01 02 03 01 02 al+.............
|
||||
| 3520: 03 01 02 03 02 05 6f 72 6b 65 72 31 02 03 01 02 ......orker1....
|
||||
| 3536: 03 01 02 03 76 02 03 01 02 03 01 02 03 01 01 78 ....v..........x
|
||||
| 3552: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3568: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3584: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3600: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3616: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3632: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3648: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3664: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3680: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3696: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3712: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3728: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3744: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3760: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3776: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3792: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3808: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3824: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3840: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3856: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3872: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3888: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3904: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3920: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3936: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3952: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3968: 06 01 01 02 01 06 04 30 15 1e 0c 28 1b 0d 0c 15 .......0...(....
|
||||
| 3984: 0c 16 14 16 10 0c 17 0e 0e 0f 11 10 10 0e 10 11 ................
|
||||
| 4000: 18 11 82 3e 12 10 0f 10 11 10 0f 0f 10 11 0f 0f ...>............
|
||||
| 4016: 81 05 18 10 81 45 11 0d 13 0f 10 17 0c 0c 0e 18 .....E..........
|
||||
| 4032: 0c 12 10 0e 0d 0f 13 12 24 0f 17 0f 1a 0d 81 1c ........$.......
|
||||
| 4048: 11 0f 17 10 82 3e 12 11 11 18 0d 12 2a 14 11 10 .....>......*...
|
||||
| 4064: 13 0f 12 0f 0f 82 3a 15 10 0f 10 4d 0e 1f 0f 0d ......:....M....
|
||||
| 4080: 0f 0f 1e 10 10 1a 0f 12 0c 12 14 0f 0e 20 17 19 ............. ..
|
||||
| page 12 offset 45056
|
||||
| 0: 0d 00 00 00 01 0d f4 00 0d f4 00 00 00 00 00 00 ................
|
||||
| 3568: 00 00 00 00 84 04 84 80 80 80 80 02 04 00 88 0c ................
|
||||
| 3584: 00 07 02 00 01 01 02 56 06 01 01 02 01 06 01 01 .......V........
|
||||
| 3600: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3616: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3632: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3648: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3664: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3680: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3696: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3712: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3728: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3744: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3760: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3776: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3792: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3808: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3824: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3840: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3856: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3872: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3888: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3904: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3920: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3936: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3952: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3968: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3984: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 4000: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 4016: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 4032: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 4048: 52 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 R...............
|
||||
| 4064: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 4080: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| end c1a.txt.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 84.1 {
|
||||
SELECT * FROM t1('R*R*x') ORDER BY rowid DESC;
|
||||
} {1 {fts5: corruption found reading blob 137438953475 from table "t1"}}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -305,28 +305,6 @@ do_catchsql_test 6.2 {
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, col);
|
||||
|
||||
INSERT INTO t1 VALUES('xx', 'xx');
|
||||
|
||||
CREATE TABLE x1(t);
|
||||
INSERT INTO x1 VALUES('xx');
|
||||
INSERT INTO x1 VALUES('xx');
|
||||
|
||||
SELECT term, col FROM v1;
|
||||
} {
|
||||
xx a xx b
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT * FROM x1 WHERE 'a'=(SELECT col FROM v1 WHERE term=t)
|
||||
} {xx xx}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -59,7 +59,7 @@ static void compressFunc(
|
||||
pIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 13 + nIn + (nIn+999)/1000;
|
||||
pOut = sqlite3_malloc64( nOut+5 );
|
||||
pOut = sqlite3_malloc( nOut+5 );
|
||||
for(i=4; i>=0; i--){
|
||||
x[i] = (nIn >> (7*(4-i)))&0x7f;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ static void uncompressFunc(
|
||||
nOut = (nOut<<7) | (pIn[i]&0x7f);
|
||||
if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
||||
}
|
||||
pOut = sqlite3_malloc64( nOut+1 );
|
||||
pOut = sqlite3_malloc( nOut+1 );
|
||||
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
|
||||
if( rc==Z_OK ){
|
||||
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||||
|
||||
+7
-9
@@ -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,9 +62,6 @@ SQLITE_EXTENSION_INIT1
|
||||
# define CSV_NOINLINE
|
||||
#endif
|
||||
|
||||
#ifndef SQLITEINT_H
|
||||
typedef sqlite3_int64 i64;
|
||||
#endif
|
||||
|
||||
/* Max size of the error message in a CsvReader */
|
||||
#define CSV_MXERR 200
|
||||
@@ -77,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 */
|
||||
@@ -177,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;
|
||||
@@ -513,6 +510,7 @@ 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));
|
||||
|
||||
+5
-38
@@ -291,36 +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;
|
||||
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 */
|
||||
@@ -338,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;
|
||||
@@ -702,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);
|
||||
}
|
||||
@@ -881,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);
|
||||
}
|
||||
}
|
||||
@@ -902,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 },
|
||||
|
||||
+59
-174
@@ -94,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>
|
||||
@@ -135,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;
|
||||
@@ -149,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;
|
||||
@@ -264,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
|
||||
@@ -276,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);
|
||||
@@ -423,6 +459,7 @@ static int writeFile(
|
||||
|
||||
if( mtime>=0 ){
|
||||
#if defined(_WIN32)
|
||||
#if !SQLITE_OS_WINRT
|
||||
/* Windows */
|
||||
FILETIME lastAccess;
|
||||
FILETIME lastWrite;
|
||||
@@ -453,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];
|
||||
@@ -1056,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
|
||||
@@ -1230,10 +1120,5 @@ int sqlite3_fileio_init(
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fsdirRegister(db);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "realpath", 1,
|
||||
SQLITE_UTF8, 0,
|
||||
realpathFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -617,7 +617,7 @@ 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;
|
||||
|
||||
+1
-35
@@ -179,9 +179,9 @@ static void ieee754func(
|
||||
}
|
||||
|
||||
if( m<0 ){
|
||||
if( m<(-9223372036854775807LL) ) return;
|
||||
isNeg = 1;
|
||||
m = -m;
|
||||
if( m<0 ) return;
|
||||
}else if( m==0 && e>-1000 && e<1000 ){
|
||||
sqlite3_result_double(context, 0.0);
|
||||
return;
|
||||
@@ -259,38 +259,6 @@ static void ieee754func_to_blob(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Functions to convert between 64-bit integers and floats.
|
||||
**
|
||||
** The bit patterns are copied. The numeric values are different.
|
||||
*/
|
||||
static void ieee754func_from_int(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
|
||||
double r;
|
||||
sqlite3_int64 v = sqlite3_value_int64(argv[0]);
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
sqlite3_result_double(context, r);
|
||||
}
|
||||
}
|
||||
static void ieee754func_to_int(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){
|
||||
double r = sqlite3_value_double(argv[0]);
|
||||
sqlite3_uint64 v;
|
||||
memcpy(&v, &r, sizeof(v));
|
||||
sqlite3_result_int64(context, v);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: ieee754_inc(r,N)
|
||||
**
|
||||
@@ -343,8 +311,6 @@ int sqlite3_ieee_init(
|
||||
{ "ieee754_exponent", 1, 2, ieee754func },
|
||||
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
|
||||
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
|
||||
{ "ieee754_to_int", 1, 0, ieee754func_to_int },
|
||||
{ "ieee754_from_int", 1, 0, ieee754func_from_int },
|
||||
{ "ieee754_inc", 2, 0, ieee754inc },
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
+7
-21
@@ -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;
|
||||
@@ -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);
|
||||
@@ -853,7 +839,7 @@ static void re_bytecode_func(
|
||||
|
||||
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);
|
||||
|
||||
+5
-10
@@ -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,
|
||||
@@ -260,13 +257,11 @@ static void sha1Func(
|
||||
hash_step(&cx, sqlite3_value_text(argv[0]), 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+6
-48
@@ -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);");
|
||||
@@ -247,10 +221,6 @@ static int vtablogConnectCreate(
|
||||
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(
|
||||
@@ -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);
|
||||
|
||||
+28
-34
@@ -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;
|
||||
@@ -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 ){
|
||||
@@ -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;
|
||||
@@ -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));
|
||||
|
||||
+42
-130
@@ -19,10 +19,10 @@ that can be incorporated and reused by other applications.
|
||||
|
||||
## 1.0 Overview Of Operation
|
||||
|
||||
Suppose variable `sqlite3_stmt *pStmt` is a pointer to an SQLite
|
||||
prepared statement that has been reset and bound and is ready to run.
|
||||
Then to format the output from this prepared statement, use code
|
||||
similar to the following:
|
||||
Suppose `pStmt` is a pointer to an SQLite prepared statement
|
||||
(a pointer to an `sqlite3_stmt` object) that has been reset and
|
||||
bound and is ready to run. Then to format the output from this
|
||||
prepared statement, use code similar to the following:
|
||||
|
||||
> ~~~
|
||||
sqlite3_qrf_spec spec; /* Format specification */
|
||||
@@ -41,59 +41,26 @@ if( rc ){
|
||||
sqlite3_free(zErrMsg); /* Free the error message text */
|
||||
}else{
|
||||
printf("%s", zResult); /* Report the results */
|
||||
sqlite3_free(zResult); /* Free memory used to hold results */
|
||||
}
|
||||
sqlite3_free(zResult); /* Free memory used to hold results */
|
||||
~~~
|
||||
|
||||
The `sqlite3_qrf_spec` object describes the desired output format
|
||||
and where to send the generated output. Most of the work in using
|
||||
and what to do with the generated output. Most of the work in using
|
||||
the QRF involves filling out the sqlite3_qrf_spec.
|
||||
|
||||
### 1.1 Using QRF with SQL text
|
||||
|
||||
If you start with SQL text instead of an sqlite3_stmt pointer, and
|
||||
especially if the SQL text might comprise two or more statements, then
|
||||
the SQL text needs to be converted into sqlite3_stmt objects separately.
|
||||
If the original SQL text is in a variable `const char *zSql` and the
|
||||
database connection is in variable `sqlite3 *db`, then code
|
||||
similar to the following should work:
|
||||
|
||||
> ~~~
|
||||
sqlite3_qrf_spec spec; /* Format specification */
|
||||
char *zErrMsg; /* Text error message (optional) */
|
||||
char *zResult = 0; /* Formatted output written here */
|
||||
sqlite3_stmt *pStmt; /* Next prepared statement */
|
||||
int rc; /* Result code */
|
||||
|
||||
memset(&spec, 0, sizeof(spec)); /* Initialize the spec */
|
||||
spec.iVersion = 1; /* Version number must be 1 */
|
||||
spec.pzOutput = &zResult; /* Write results in variable zResult */
|
||||
/* Optionally fill in other settings in spec here, as needed */
|
||||
zErrMsg = 0; /* Not required; just being pedantic */
|
||||
while( zSql && zSql[0] ){
|
||||
pStmt = 0; /* Not required; just being pedantic */
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
|
||||
if( rc!=SQLITE_OK ){
|
||||
printf("Error: %s\n", sqlite3_errmsg(db));
|
||||
}else{
|
||||
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Get results */
|
||||
if( rc ){
|
||||
printf("Error (%d): %s\n", rc, zErrMsg); /* Report an error */
|
||||
sqlite3_free(zErrMsg); /* Free the error message text */
|
||||
}else{
|
||||
printf("%s", zResult); /* Report the results */
|
||||
sqlite3_free(zResult); /* Free memory used to hold results */
|
||||
zResult = 0;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
~~~
|
||||
|
||||
<a id="spec"></a>
|
||||
## 2.0 The `sqlite3_qrf_spec` object
|
||||
|
||||
The `sqlite3_qrf_spec` looks like this:
|
||||
The `sqlite3_qrf_spec` structure defines how the results of a query
|
||||
are to be formatted, and what to do with the formatted text. The
|
||||
most recent definition of `sqlite3_qrf_spec` is shown below.
|
||||
|
||||
Do not be alarmed by the complexity of this structure. You only have
|
||||
to understand the properties you want to modify. Zero is always a good
|
||||
default for all of the attributes (except iVersion and pzOutput/xWrite)
|
||||
and so simply zeroing out the bulk of this structure is a good start.
|
||||
You can then slowly make adjustments to individual fields to get the
|
||||
results you desire.
|
||||
|
||||
> ~~~
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
@@ -107,14 +74,12 @@ struct sqlite3_qrf_spec {
|
||||
unsigned char bTitles; /* True to show column names */
|
||||
unsigned char bWordWrap; /* Try to wrap on word boundaries */
|
||||
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
|
||||
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
|
||||
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
|
||||
unsigned char eTitleAlign; /* Alignment for column headers */
|
||||
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
|
||||
unsigned char bBorder; /* Show outer border in Box and Table styles */
|
||||
short int nWrap; /* Wrap columns wider than this */
|
||||
short int nScreenWidth; /* Maximum overall table width */
|
||||
short int nLineLimit; /* Maximum number of lines for any row */
|
||||
short int nTitleLimit; /* Maximum number of characters in a title */
|
||||
int nCharLimit; /* Maximum number of characters in a cell */
|
||||
int nWidth; /* Number of entries in aWidth[] */
|
||||
int nAlign; /* Number of entries in aAlignment[] */
|
||||
@@ -133,24 +98,21 @@ struct sqlite3_qrf_spec {
|
||||
};
|
||||
~~~
|
||||
|
||||
Do not be alarmed by the complexity of this structure. Everything can
|
||||
be zeroed except for:
|
||||
Again, the only fields that must initialized are:
|
||||
|
||||
* `.iVersion`
|
||||
* One of `.pzOutput` or `.xWrite`.
|
||||
|
||||
You do not need to understand and configure every field of this object
|
||||
in order to use QRF effectively. Start by zeroing out the whole structure,
|
||||
then initializing iVersion and one of pzOutput or xWrite. Then maybe
|
||||
tweak one or two other settings to get the output you want.
|
||||
All other fields can be zeroed. Or they can contain other values to
|
||||
alter the formatting of the query results.
|
||||
|
||||
Further detail on the meanings of each of the fields in the
|
||||
`sqlite3_qrf_spec` object is in the subsequent sections.
|
||||
`sqlite3_qrf_spec` object are described below.
|
||||
|
||||
### 2.1 Structure Version Number
|
||||
|
||||
The sqlite3_qrf_spec.iVersion field must be 1. Future enhancements to
|
||||
the QRF might add new fields to the bottom of the sqlite3_qrf_spec
|
||||
the QRF might add new fields onto the bottom of the sqlite3_qrf_spec
|
||||
object. Those new fields will only be accessible if the iVersion is greater
|
||||
than 1. Thus the iVersion field is used to support upgradability.
|
||||
|
||||
@@ -164,7 +126,7 @@ first argument) to transmit the formatted output. Or, if
|
||||
sqlite3_qrf_spec.pzOutput points to a pointer to a character, then that
|
||||
pointer is made to point to memory obtained from sqlite3_malloc() that
|
||||
contains the complete text of the formatted output. If spec.pzOutput\[0\]
|
||||
is initially non-NULL, then it is assumed to already point to memory obtained
|
||||
is initially non-NULL, then it is assumed to point to memory obtained
|
||||
from sqlite3_malloc(). In that case, the buffer is resized using
|
||||
sqlite3_realloc() and the new text is appended.
|
||||
|
||||
@@ -178,8 +140,8 @@ value might be returned.
|
||||
### 2.3 Output Format
|
||||
|
||||
The sqlite3_qrf_spec.eStyle field is an integer code that defines the
|
||||
specific output format that will be generated. See [section 4.0](#style)
|
||||
below for details on the meaning of the various style options.
|
||||
specific output format that will be generated. See section 4.0 below
|
||||
for details on the meaning of the various style options.
|
||||
|
||||
Other fields in sqlite3_qrf_spec might be used or might be
|
||||
ignored, depending on the value of eStyle.
|
||||
@@ -252,7 +214,6 @@ Both fields can have one of the following values:
|
||||
#define QRF_TEXT_Html 4 /* HTML-style quoting */
|
||||
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
|
||||
#define QRF_TEXT_Json 6 /* JSON quoting */
|
||||
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
|
||||
~~~
|
||||
|
||||
A value of QRF_TEXT_Auto means that the query result formatter will choose
|
||||
@@ -266,14 +227,6 @@ look like SQL literals. That means the value will be surrounded by
|
||||
single-quotes (U+0027) and any single-quotes contained within the text
|
||||
will be doubled.
|
||||
|
||||
QRF_TEXT_Relaxed is similar to QRF_TEXT_Sql, except that it automatically
|
||||
reverts to QRF_TEXT_Plain if the value to be displayed does not contain
|
||||
special characters and is not easily confused with a NULL or a numeric
|
||||
value. QRF_TEXT_Relaxed strives to minimize the amount of quoting syntax
|
||||
while keeping the result unambiguous and easy for humans to read. The
|
||||
precise rules for when quoting is omitted in QRF_TEXT_Relaxed, and when
|
||||
it is applied, might be adjusted in future releases.
|
||||
|
||||
A value of QRF_TEXT_Csv means that text values are escaped in accordance
|
||||
with RFC 4180, which defines Comma-Separated-Value or CSV files.
|
||||
Text strings that contain no special values appears as-is. Text strings
|
||||
@@ -312,7 +265,6 @@ how the BLOB value is formatted. The following options are available;
|
||||
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
|
||||
#define QRF_BLOB_Tcl 4 /* "\000" notation */
|
||||
#define QRF_BLOB_Json 5 /* A JSON string */
|
||||
#define QRF_BLOB_Size 6 /* Display the blob size only */
|
||||
~~~
|
||||
|
||||
A value of QRF_BLOB_Auto means that display format is selected automatically
|
||||
@@ -340,11 +292,7 @@ the C/Tcl/Perl octal backslash escapes. So the string from the
|
||||
previous paragraph would be shown as
|
||||
`"\u0005\u0028\u0081\u00f3"`.
|
||||
|
||||
A value of QRF_BLOB_Size does not show any BLOB content at all.
|
||||
Instead, it substitutes a text string that says how many bytes
|
||||
the BLOB contains.
|
||||
|
||||
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit, nTitleLimit)
|
||||
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit)
|
||||
|
||||
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
|
||||
will display only the first nCharLimit characters of each value.
|
||||
@@ -360,27 +308,13 @@ If the sqlite3_qrf_spec.nLineLimit setting is non-zero, then the
|
||||
formatter will only display the first nLineLimit lines of each value.
|
||||
It does not matter if the value is split because it contains a newline
|
||||
character, or if it split by wrapping. This setting merely limits
|
||||
the number of displayed lines. The nLineLimit setting currently only
|
||||
works for **Box**, **Column**, **Line**, **Markdown**, and **Table**
|
||||
styles, though that limitation might change in future releases.
|
||||
the number of displayed lines. This setting only works for
|
||||
**Box**, **Column**, **Line**, **Markdown**, and **Table** styles.
|
||||
|
||||
The idea behind both of these settings is to prevent large renderings
|
||||
when doing a query that (unexpectedly) contains very large text or
|
||||
blob values: perhaps megabyes of text.
|
||||
|
||||
If the sqlite3_qrf_spec.nTitleLimit is non-zero, then the formatter
|
||||
attempts to limits the size of column titles to at most nTitleLimit
|
||||
display characters in width and a single line of text. The nTitleLimit
|
||||
is useful for queries that have result columns that are scalar
|
||||
subqueries or complex expressions. If those columns lack an AS
|
||||
clause, then the name of the column will be a copy of the expression
|
||||
that defines the column, which in some queries can be hundreds of
|
||||
characters and multiple lines in length, which can reduce the readability
|
||||
of tabular displays. An nTitleLimit somewhere in the range of 10 to 20.
|
||||
can improve readability. The nTitleLimit setting currently only
|
||||
works for **Box**, **Column**, **Line**, **Markdown**, and **Table**
|
||||
styles, though that limitation might change in future releases.
|
||||
|
||||
### 2.9 Word Wrapping In Columnar Styles (nWrap, bWordWrap)
|
||||
|
||||
When using columnar formatting modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
@@ -416,10 +350,6 @@ 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.11 Individual Column Width (nWidth and aWidth)
|
||||
|
||||
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
|
||||
@@ -527,9 +457,8 @@ entry for that column takes precedence. If either the horizontal
|
||||
or vertical alignment has an "auto" value for that column or if
|
||||
a column is beyond the first nAlign entries, then eDfltAlign
|
||||
is used as a backup. If neither aAlign\[\] nor eDfltAlign
|
||||
specify a horizontal alignment, then values are right-aligned
|
||||
(QRF_ALIGN_Right) if they are numeric and left-aligned
|
||||
(QRF_ALIGN_Left) otherwise. If neither aAlign\[\] nor eDfltAlign
|
||||
specify a horizontal alignment, then values are left-aligned
|
||||
(QRF_ALIGN_Left). If neither aAlign\[\] nor eDfltAlign
|
||||
specify a vertical alignment, then values are top-aligned
|
||||
(QRF_ALIGN_Top).
|
||||
|
||||
@@ -550,12 +479,17 @@ and these settings are ignored for those styles.
|
||||
The sqlite3_qrf_spec.zTableName value is the name of the output table
|
||||
when eStyle is QRF_STYLE_Insert.
|
||||
|
||||
### 2.15 The Rendering Of NULL (zNull)
|
||||
### 2.15 The Rendering Of NULL (zNull, eTextNull)
|
||||
|
||||
If a value is NULL then show the NULL using the string
|
||||
found in sqlite3_qrf_spec.zNull. If zNull is itself a NULL pointer
|
||||
then NULL values are rendered as an empty string.
|
||||
|
||||
If the sqlite3_qrf_spec.bTextNull field is QRF_Yes, then the
|
||||
text encoding specified by eText is applied to the value in
|
||||
zNull. If bTextNull is QRF_No or QRF_Auto, then the value
|
||||
in zNull is shown verbatim.
|
||||
|
||||
### 2.16 Optional Value Rendering Callback
|
||||
|
||||
If the sqlite3_qrf_spec.xRender field is not NULL, then each
|
||||
@@ -577,13 +511,6 @@ The eText, eBlob, and eEsc settings above become no-ops if the xRender
|
||||
routine returns non-NULL. In other words, the application-supplied
|
||||
xRender routine is expected to do all of its own quoting and formatting.
|
||||
|
||||
The xRender routine is expected to do character length limiting itself.
|
||||
So the nCharLimit setting becomes a no-op if xRender is used. However
|
||||
the nLineLimit setting is still applied. The nTitleLimit setting is
|
||||
not applicable to xRender because title values come from the
|
||||
sqlite3_column_name() interface not from sqlite3_column_value(),
|
||||
and so that names of columns are never processed by xRender.
|
||||
|
||||
## 3.0 The `sqlite3_format_query_result()` Interface
|
||||
|
||||
Invoke the `sqlite3_format_query_result(P,S,E)` interface to run
|
||||
@@ -596,13 +523,12 @@ into *E. Any error message text will be stored in memory obtained
|
||||
from sqlite3_malloc() and it is the responsibility of the caller to
|
||||
free that memory by a subsequent call to sqlite3_free().
|
||||
|
||||
<a id="style"></a>
|
||||
## 4.0 Output Styles
|
||||
|
||||
The result formatter supports a variety of output styles. The
|
||||
output style (sometimes called "output mode") is determined by
|
||||
the eStyle field of the sqlite3_qrf_spec object. The set of
|
||||
supported output modes might increase in future versions.
|
||||
output style used is determined by the eStyle setting of the
|
||||
sqlite3_qrf_spec object. The set of supported output modes
|
||||
might increase in future versions.
|
||||
The following output modes are currently defined:
|
||||
|
||||
> ~~~
|
||||
@@ -658,22 +584,8 @@ to draw the grid. The **Column** arranges the results in neat columns
|
||||
but does not draw in column or row separator, except that it does draw
|
||||
lines horizontal lines using "`-`" characters to separate the column names
|
||||
from the data below. This is very similar to default output styling in
|
||||
psql. The **Markdown** renders its result in the Markdown table format.
|
||||
|
||||
The **Box** and **Table** styles normally have a border that surrounds
|
||||
the entire result. However, if sqlite3_qrf_spec.bBorder is QRF_No, then
|
||||
that border is omitted, saving a little space both horizontally and
|
||||
vertically.
|
||||
|
||||
#### 4.2.1 Split Column Mode
|
||||
|
||||
If the bSplitColumn field is QRF_Yes, and eStyle is QRF_STYLE_Column,
|
||||
and bTitles is QRF_No, and nScreenWidth is greater than zero, and if
|
||||
the query only returns a single column, then a special rendering known
|
||||
as "Split Column Mode" will be used. In split column mode, instead
|
||||
of showing all results in one tall column, the content wraps vertically
|
||||
so that it appears on the screen as multiple columns, as many as will
|
||||
fit in the available screen width.
|
||||
psql. The **Markdown** renders its result in the
|
||||
Markdown table format.
|
||||
|
||||
### 4.3 Line-oriented Styles
|
||||
|
||||
@@ -756,7 +668,7 @@ The SQLite Query Result Formatter is implemented in three source code files:
|
||||
|
||||
* `qrf.c` → The implementation, written in portable C99
|
||||
* `qrf.h` → A header file defining interfaces
|
||||
* `README.md` → This documentation
|
||||
* `README.md` → This documentation, in Markdown
|
||||
|
||||
To use the SQLite result formatter, include the "`qrf.h`" header file
|
||||
and link the application against the "`qrf.c`" source file.
|
||||
|
||||
@@ -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
|
||||
+120
-439
File diff suppressed because it is too large
Load Diff
+2
-13
@@ -34,14 +34,13 @@ struct sqlite3_qrf_spec {
|
||||
unsigned char bTitles; /* True to show column names */
|
||||
unsigned char bWordWrap; /* Try to wrap on word boundaries */
|
||||
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
|
||||
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
|
||||
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
|
||||
unsigned char eTitleAlign; /* Alignment for column headers */
|
||||
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
|
||||
unsigned char bBorder; /* Show outer border in Box and Table styles */
|
||||
short int nWrap; /* Wrap columns wider than this */
|
||||
short int nScreenWidth; /* Maximum overall table width */
|
||||
short int nLineLimit; /* Maximum number of lines for any row */
|
||||
short int nTitleLimit; /* Maximum number of characters in a title */
|
||||
int nCharLimit; /* Maximum number of characters in a cell */
|
||||
int nWidth; /* Number of entries in aWidth[] */
|
||||
int nAlign; /* Number of entries in aAlignment[] */
|
||||
@@ -110,7 +109,6 @@ int sqlite3_format_query_result(
|
||||
#define QRF_TEXT_Html 4 /* HTML-style quoting */
|
||||
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
|
||||
#define QRF_TEXT_Json 6 /* JSON quoting */
|
||||
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
|
||||
|
||||
/*
|
||||
** Quoting styles for BLOBs
|
||||
@@ -122,7 +120,6 @@ int sqlite3_format_query_result(
|
||||
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
|
||||
#define QRF_BLOB_Tcl 4 /* "\000" notation */
|
||||
#define QRF_BLOB_Json 5 /* A JSON string */
|
||||
#define QRF_BLOB_Size 6 /* Display the blob size only */
|
||||
|
||||
/*
|
||||
** Control-character escape modes.
|
||||
@@ -178,21 +175,13 @@ int sqlite3_format_query_result(
|
||||
** character c. For normal characters, the answer is always 1. But the
|
||||
** estimate might be 0 or 2 for zero-width and double-width characters.
|
||||
**
|
||||
** Different devices display unicode using different widths. So
|
||||
** Different display devices display unicode using different widths. So
|
||||
** it is impossible to know that true display width with 100% accuracy.
|
||||
** Inaccuracies in the width estimates might cause columns to be misaligned.
|
||||
** Unfortunately, there is nothing we can do about that.
|
||||
*/
|
||||
int sqlite3_qrf_wcwidth(int c);
|
||||
|
||||
/*
|
||||
** Return an estimate of the number of display columns used by the
|
||||
** string in the argument. The width of individual characters is
|
||||
** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences
|
||||
** are assigned a width of zero.
|
||||
*/
|
||||
size_t sqlite3_qrf_wcswidth(const char*);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -192,32 +192,4 @@ do_test 4.7.2 {
|
||||
list [catch {rbu close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - rbu_state mismatch error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# https://sqlite.org/forum/info/6d0a31e22a435877
|
||||
#
|
||||
reset_db
|
||||
forcedelete rbu.db
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a BLOB);
|
||||
INSERT INTO t1 VALUES(x'41');
|
||||
}
|
||||
|
||||
sqlite3 dbRbu rbu.db
|
||||
dbRbu eval {
|
||||
CREATE TABLE data_t1(a, rbu_control, rbu_rowid);
|
||||
INSERT INTO data_t1 VALUES(X'310a313a5a337e7e7e7e7e40302c303b','f',1);
|
||||
}
|
||||
dbRbu close
|
||||
|
||||
do_test 5.1 {
|
||||
sqlite3rbu rbu test.db rbu.db
|
||||
rbu step
|
||||
} {SQLITE_ERROR}
|
||||
|
||||
do_test 5.2 {
|
||||
list [catch {rbu close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - corrupt fossil delta}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -623,7 +623,7 @@ static int rbuDeltaApply(
|
||||
/* ERROR: copy exceeds output file size */
|
||||
return -1;
|
||||
}
|
||||
if( (u64)ofst+(u64)cnt > (u64)lenSrc ){
|
||||
if( (int)(ofst+cnt) > lenSrc ){
|
||||
/* ERROR: copy extends past end of input */
|
||||
return -1;
|
||||
}
|
||||
@@ -2269,8 +2269,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
|
||||
/* If necessary, grow the pIter->aIdxCol[] array */
|
||||
if( iIdxCol==nIdxAlloc ){
|
||||
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc64(
|
||||
pIter->aIdxCol, nIdxAlloc*sizeof(RbuSpan) + 16*sizeof(RbuSpan)
|
||||
RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
|
||||
pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
|
||||
);
|
||||
if( aIdxCol==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
This folder contains extensions and utility programs intended to analyze
|
||||
live database files, detect problems, and possibly fix them.
|
||||
|
||||
As SQLite is being used on larger and larger databases, database sizes
|
||||
are growing into the terabyte range. At that size, hardware malfunctions
|
||||
and/or cosmic rays will occasionally corrupt a database file. Detecting
|
||||
problems and fixing errors a terabyte-sized databases can take hours or days,
|
||||
and it is undesirable to take applications that depend on the databases
|
||||
off-line for such a long time.
|
||||
The utilities in the folder are intended to provide mechanisms for
|
||||
detecting and fixing problems in large databases while those databases
|
||||
are in active use.
|
||||
|
||||
The utilities and extensions in this folder are experimental and under
|
||||
active development at the time of this writing (2017-10-12). If and when
|
||||
they stabilize, this README will be updated to reflect that fact.
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
** 2017 October 11
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This module exports a single C function:
|
||||
**
|
||||
** int sqlite3_check_freelist(sqlite3 *db, const char *zDb);
|
||||
**
|
||||
** This function checks the free-list in database zDb (one of "main",
|
||||
** "temp", etc.) and reports any errors by invoking the sqlite3_log()
|
||||
** function. It returns SQLITE_OK if successful, or an SQLite error
|
||||
** code otherwise. It is not an error if the free-list is corrupted but
|
||||
** no IO or OOM errors occur.
|
||||
**
|
||||
** If this file is compiled and loaded as an SQLite loadable extension,
|
||||
** it adds an SQL function "checkfreelist" to the database handle, to
|
||||
** be invoked as follows:
|
||||
**
|
||||
** SELECT checkfreelist(<database-name>);
|
||||
**
|
||||
** This function performs the same checks as sqlite3_check_freelist(),
|
||||
** except that it returns all error messages as a single text value,
|
||||
** separated by newline characters. If the freelist is not corrupted
|
||||
** in any way, an empty string is returned.
|
||||
**
|
||||
** To compile this module for use as an SQLite loadable extension:
|
||||
**
|
||||
** gcc -Os -fPIC -shared checkfreelist.c -o checkfreelist.so
|
||||
*/
|
||||
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# include <string.h>
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <assert.h>
|
||||
# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
|
||||
# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
|
||||
# endif
|
||||
# if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
|
||||
# define ALWAYS(X) (1)
|
||||
# define NEVER(X) (0)
|
||||
# elif !defined(NDEBUG)
|
||||
# define ALWAYS(X) ((X)?1:(assert(0),0))
|
||||
# define NEVER(X) ((X)?(assert(0),1):0)
|
||||
# else
|
||||
# define ALWAYS(X) (X)
|
||||
# define NEVER(X) (X)
|
||||
# endif
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
#define get4byte(x) ( \
|
||||
((u32)((x)[0])<<24) + \
|
||||
((u32)((x)[1])<<16) + \
|
||||
((u32)((x)[2])<<8) + \
|
||||
((u32)((x)[3])) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Execute a single PRAGMA statement and return the integer value returned
|
||||
** via output parameter (*pnOut).
|
||||
**
|
||||
** The SQL statement passed as the third argument should be a printf-style
|
||||
** format string containing a single "%s" which will be replace by the
|
||||
** value passed as the second argument. e.g.
|
||||
**
|
||||
** sqlGetInteger(db, "main", "PRAGMA %s.page_count", pnOut)
|
||||
**
|
||||
** executes "PRAGMA main.page_count" and stores the results in (*pnOut).
|
||||
*/
|
||||
static int sqlGetInteger(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zDb, /* Database name ("main", "temp" etc.) */
|
||||
const char *zFmt, /* SQL statement format */
|
||||
u32 *pnOut /* OUT: Integer value */
|
||||
){
|
||||
int rc, rc2;
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int bOk = 0;
|
||||
|
||||
zSql = sqlite3_mprintf(zFmt, zDb);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
*pnOut = (u32)sqlite3_column_int(pStmt, 0);
|
||||
bOk = 1;
|
||||
}
|
||||
|
||||
rc2 = sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_ERROR;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument zFmt must be a printf-style format string and must be
|
||||
** followed by its required arguments. If argument pzOut is NULL,
|
||||
** then the results of printf()ing the format string are passed to
|
||||
** sqlite3_log(). Otherwise, they are appended to the string
|
||||
** at (*pzOut).
|
||||
*/
|
||||
static int checkFreelistError(char **pzOut, const char *zFmt, ...){
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, zFmt);
|
||||
zErr = sqlite3_vmprintf(zFmt, ap);
|
||||
if( zErr==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
if( pzOut ){
|
||||
*pzOut = sqlite3_mprintf("%s%z%s", *pzOut?"\n":"", *pzOut, zErr);
|
||||
if( *pzOut==0 ) rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
sqlite3_log(SQLITE_ERROR, "checkfreelist: %s", zErr);
|
||||
}
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int checkFreelist(
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
char **pzOut
|
||||
){
|
||||
/* This query returns one row for each page on the free list. Each row has
|
||||
** two columns - the page number and page content. */
|
||||
const char *zTrunk =
|
||||
"WITH freelist_trunk(i, d, n) AS ("
|
||||
"SELECT 1, NULL, sqlite_readint32(data, 32) "
|
||||
"FROM sqlite_dbpage(:1) WHERE pgno=1 "
|
||||
"UNION ALL "
|
||||
"SELECT n, data, sqlite_readint32(data) "
|
||||
"FROM freelist_trunk, sqlite_dbpage(:1) WHERE pgno=n "
|
||||
")"
|
||||
"SELECT i, d FROM freelist_trunk WHERE i!=1;";
|
||||
|
||||
int rc, rc2; /* Return code */
|
||||
sqlite3_stmt *pTrunk = 0; /* Compilation of zTrunk */
|
||||
u32 nPage = 0; /* Number of pages in db */
|
||||
u32 nExpected = 0; /* Expected number of free pages */
|
||||
u32 nFree = 0; /* Number of pages on free list */
|
||||
|
||||
if( zDb==0 ) zDb = "main";
|
||||
|
||||
if( (rc = sqlGetInteger(db, zDb, "PRAGMA %s.page_count", &nPage))
|
||||
|| (rc = sqlGetInteger(db, zDb, "PRAGMA %s.freelist_count", &nExpected))
|
||||
){
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zTrunk, -1, &pTrunk, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_bind_text(pTrunk, 1, zDb, -1, SQLITE_STATIC);
|
||||
while( rc==SQLITE_OK && sqlite3_step(pTrunk)==SQLITE_ROW ){
|
||||
u32 i;
|
||||
u32 iTrunk = (u32)sqlite3_column_int(pTrunk, 0);
|
||||
const u8 *aData = (const u8*)sqlite3_column_blob(pTrunk, 1);
|
||||
u32 nData = (u32)sqlite3_column_bytes(pTrunk, 1);
|
||||
u32 iNext = get4byte(&aData[0]);
|
||||
u32 nLeaf = get4byte(&aData[4]);
|
||||
|
||||
if( nLeaf>((nData/4)-2-6) ){
|
||||
rc = checkFreelistError(pzOut,
|
||||
"leaf count out of range (%d) on trunk page %d",
|
||||
(int)nLeaf, (int)iTrunk
|
||||
);
|
||||
nLeaf = (nData/4) - 2 - 6;
|
||||
}
|
||||
|
||||
nFree += 1+nLeaf;
|
||||
if( iNext>nPage ){
|
||||
rc = checkFreelistError(pzOut,
|
||||
"trunk page %d is out of range", (int)iNext
|
||||
);
|
||||
}
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<nLeaf; i++){
|
||||
u32 iLeaf = get4byte(&aData[8 + 4*i]);
|
||||
if( iLeaf==0 || iLeaf>nPage ){
|
||||
rc = checkFreelistError(pzOut,
|
||||
"leaf page %d is out of range (child %d of trunk page %d)",
|
||||
(int)iLeaf, (int)i, (int)iTrunk
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && nFree!=nExpected ){
|
||||
rc = checkFreelistError(pzOut,
|
||||
"free-list count mismatch: actual=%d header=%d",
|
||||
(int)nFree, (int)nExpected
|
||||
);
|
||||
}
|
||||
|
||||
rc2 = sqlite3_finalize(pTrunk);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3_check_freelist(sqlite3 *db, const char *zDb){
|
||||
return checkFreelist(db, zDb, 0);
|
||||
}
|
||||
|
||||
static void checkfreelist_function(
|
||||
sqlite3_context *pCtx,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
const char *zDb;
|
||||
int rc;
|
||||
char *zOut = 0;
|
||||
sqlite3 *db = sqlite3_context_db_handle(pCtx);
|
||||
|
||||
assert( nArg==1 );
|
||||
zDb = (const char*)sqlite3_value_text(apArg[0]);
|
||||
rc = checkFreelist(db, zDb, &zOut);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_text(pCtx, zOut?zOut:"ok", -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
}
|
||||
|
||||
sqlite3_free(zOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** An SQL function invoked as follows:
|
||||
**
|
||||
** sqlite_readint32(BLOB) -- Decode 32-bit integer from start of blob
|
||||
*/
|
||||
static void readint_function(
|
||||
sqlite3_context *pCtx,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
const u8 *zBlob;
|
||||
int nBlob;
|
||||
int iOff = 0;
|
||||
u32 iRet = 0;
|
||||
|
||||
if( nArg!=1 && nArg!=2 ){
|
||||
sqlite3_result_error(
|
||||
pCtx, "wrong number of arguments to function sqlite_readint32()", -1
|
||||
);
|
||||
return;
|
||||
}
|
||||
if( nArg==2 ){
|
||||
iOff = sqlite3_value_int(apArg[1]);
|
||||
}
|
||||
|
||||
zBlob = sqlite3_value_blob(apArg[0]);
|
||||
nBlob = sqlite3_value_bytes(apArg[0]);
|
||||
|
||||
if( nBlob>=(iOff+4) ){
|
||||
iRet = get4byte(&zBlob[iOff]);
|
||||
}
|
||||
|
||||
sqlite3_result_int64(pCtx, (sqlite3_int64)iRet);
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the SQL functions.
|
||||
*/
|
||||
static int cflRegister(sqlite3 *db){
|
||||
int rc = sqlite3_create_function(
|
||||
db, "sqlite_readint32", -1, SQLITE_UTF8, 0, readint_function, 0, 0
|
||||
);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = sqlite3_create_function(
|
||||
db, "checkfreelist", 1, SQLITE_UTF8, 0, checkfreelist_function, 0, 0
|
||||
);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extension load function.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_checkfreelist_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return cflRegister(db);
|
||||
}
|
||||
@@ -0,0 +1,929 @@
|
||||
/*
|
||||
** 2017 October 27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** Stuff that is available inside the amalgamation, but which we need to
|
||||
** declare ourselves if this module is compiled separately.
|
||||
*/
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# include <string.h>
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <assert.h>
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
#define get4byte(x) ( \
|
||||
((u32)((x)[0])<<24) + \
|
||||
((u32)((x)[1])<<16) + \
|
||||
((u32)((x)[2])<<8) + \
|
||||
((u32)((x)[3])) \
|
||||
)
|
||||
#endif
|
||||
|
||||
typedef struct CidxTable CidxTable;
|
||||
typedef struct CidxCursor CidxCursor;
|
||||
|
||||
struct CidxTable {
|
||||
sqlite3_vtab base; /* Base class. Must be first */
|
||||
sqlite3 *db;
|
||||
};
|
||||
|
||||
struct CidxCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
sqlite3_int64 iRowid; /* Row number of the output */
|
||||
char *zIdxName; /* Copy of the index_name parameter */
|
||||
char *zAfterKey; /* Copy of the after_key parameter */
|
||||
sqlite3_stmt *pStmt; /* SQL statement that generates the output */
|
||||
};
|
||||
|
||||
typedef struct CidxColumn CidxColumn;
|
||||
struct CidxColumn {
|
||||
char *zExpr; /* Text for indexed expression */
|
||||
int bDesc; /* True for DESC columns, otherwise false */
|
||||
int bKey; /* Part of index, not PK */
|
||||
};
|
||||
|
||||
typedef struct CidxIndex CidxIndex;
|
||||
struct CidxIndex {
|
||||
char *zWhere; /* WHERE clause, if any */
|
||||
int nCol; /* Elements in aCol[] array */
|
||||
CidxColumn aCol[1]; /* Array of indexed columns */
|
||||
};
|
||||
|
||||
static void *cidxMalloc(int *pRc, int n){
|
||||
void *pRet = 0;
|
||||
assert( n!=0 );
|
||||
if( *pRc==SQLITE_OK ){
|
||||
pRet = sqlite3_malloc(n);
|
||||
if( pRet ){
|
||||
memset(pRet, 0, n);
|
||||
}else{
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
static void cidxCursorError(CidxCursor *pCsr, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
assert( pCsr->base.pVtab->zErrMsg==0 );
|
||||
pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Connect to the incremental_index_check virtual table.
|
||||
*/
|
||||
static int cidxConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
CidxTable *pRet;
|
||||
|
||||
#define IIC_ERRMSG 0
|
||||
#define IIC_CURRENT_KEY 1
|
||||
#define IIC_INDEX_NAME 2
|
||||
#define IIC_AFTER_KEY 3
|
||||
#define IIC_SCANNER_SQL 4
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE xyz("
|
||||
" errmsg TEXT," /* Error message or NULL if everything is ok */
|
||||
" current_key TEXT," /* SQLite quote() text of key values */
|
||||
" index_name HIDDEN," /* IN: name of the index being scanned */
|
||||
" after_key HIDDEN," /* IN: Start scanning after this key */
|
||||
" scanner_sql HIDDEN" /* debugging info: SQL used for scanner */
|
||||
")"
|
||||
);
|
||||
pRet = cidxMalloc(&rc, sizeof(CidxTable));
|
||||
if( pRet ){
|
||||
pRet->db = db;
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect from or destroy an incremental_index_check virtual table.
|
||||
*/
|
||||
static int cidxDisconnect(sqlite3_vtab *pVtab){
|
||||
CidxTable *pTab = (CidxTable*)pVtab;
|
||||
sqlite3_free(pTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** idxNum and idxStr are not used. There are only three possible plans,
|
||||
** which are all distinguished by the number of parameters.
|
||||
**
|
||||
** No parameters: A degenerate plan. The result is zero rows.
|
||||
** 1 Parameter: Scan all of the index starting with first entry
|
||||
** 2 parameters: Scan the index starting after the "after_key".
|
||||
**
|
||||
** Provide successively smaller costs for each of these plans to encourage
|
||||
** the query planner to select the one with the most parameters.
|
||||
*/
|
||||
static int cidxBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
|
||||
int iIdxName = -1;
|
||||
int iAfterKey = -1;
|
||||
int i;
|
||||
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
|
||||
if( p->usable==0 ) continue;
|
||||
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
|
||||
if( p->iColumn==IIC_INDEX_NAME ){
|
||||
iIdxName = i;
|
||||
}
|
||||
if( p->iColumn==IIC_AFTER_KEY ){
|
||||
iAfterKey = i;
|
||||
}
|
||||
}
|
||||
|
||||
if( iIdxName<0 ){
|
||||
pInfo->estimatedCost = 1000000000.0;
|
||||
}else{
|
||||
pInfo->aConstraintUsage[iIdxName].argvIndex = 1;
|
||||
pInfo->aConstraintUsage[iIdxName].omit = 1;
|
||||
if( iAfterKey<0 ){
|
||||
pInfo->estimatedCost = 1000000.0;
|
||||
}else{
|
||||
pInfo->aConstraintUsage[iAfterKey].argvIndex = 2;
|
||||
pInfo->aConstraintUsage[iAfterKey].omit = 1;
|
||||
pInfo->estimatedCost = 1000.0;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new btreeinfo cursor.
|
||||
*/
|
||||
static int cidxOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
CidxCursor *pRet;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
pRet = cidxMalloc(&rc, sizeof(CidxCursor));
|
||||
|
||||
*ppCursor = (sqlite3_vtab_cursor*)pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a btreeinfo cursor.
|
||||
*/
|
||||
static int cidxClose(sqlite3_vtab_cursor *pCursor){
|
||||
CidxCursor *pCsr = (CidxCursor*)pCursor;
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
sqlite3_free(pCsr->zIdxName);
|
||||
sqlite3_free(pCsr->zAfterKey);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a btreeinfo cursor to the next entry in the file.
|
||||
*/
|
||||
static int cidxNext(sqlite3_vtab_cursor *pCursor){
|
||||
CidxCursor *pCsr = (CidxCursor*)pCursor;
|
||||
int rc = sqlite3_step(pCsr->pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3 *db = ((CidxTable*)pCsr->base.pVtab)->db;
|
||||
cidxCursorError(pCsr, "Cursor error: %s", sqlite3_errmsg(db));
|
||||
}
|
||||
}else{
|
||||
pCsr->iRowid++;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* We have reached EOF if previous sqlite3_step() returned
|
||||
** anything other than SQLITE_ROW;
|
||||
*/
|
||||
static int cidxEof(sqlite3_vtab_cursor *pCursor){
|
||||
CidxCursor *pCsr = (CidxCursor*)pCursor;
|
||||
return pCsr->pStmt==0;
|
||||
}
|
||||
|
||||
static char *cidxMprintf(int *pRc, const char *zFmt, ...){
|
||||
char *zRet = 0;
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
zRet = sqlite3_vmprintf(zFmt, ap);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
if( zRet==0 ){
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}
|
||||
}else{
|
||||
sqlite3_free(zRet);
|
||||
zRet = 0;
|
||||
}
|
||||
va_end(ap);
|
||||
return zRet;
|
||||
}
|
||||
|
||||
static sqlite3_stmt *cidxPrepare(
|
||||
int *pRc, CidxCursor *pCsr, const char *zFmt, ...
|
||||
){
|
||||
sqlite3_stmt *pRet = 0;
|
||||
char *zSql;
|
||||
va_list ap; /* ... printf arguments */
|
||||
va_start(ap, zFmt);
|
||||
|
||||
zSql = sqlite3_vmprintf(zFmt, ap);
|
||||
if( *pRc==SQLITE_OK ){
|
||||
if( zSql==0 ){
|
||||
*pRc = SQLITE_NOMEM;
|
||||
}else{
|
||||
sqlite3 *db = ((CidxTable*)pCsr->base.pVtab)->db;
|
||||
*pRc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0);
|
||||
if( *pRc!=SQLITE_OK ){
|
||||
cidxCursorError(pCsr, "SQL error: %s", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
va_end(ap);
|
||||
|
||||
return pRet;
|
||||
}
|
||||
|
||||
static void cidxFinalize(int *pRc, sqlite3_stmt *pStmt){
|
||||
int rc = sqlite3_finalize(pStmt);
|
||||
if( *pRc==SQLITE_OK ) *pRc = rc;
|
||||
}
|
||||
|
||||
char *cidxStrdup(int *pRc, const char *zStr){
|
||||
char *zRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
int n = (int)strlen(zStr);
|
||||
zRet = cidxMalloc(pRc, n+1);
|
||||
if( zRet ) memcpy(zRet, zStr, n+1);
|
||||
}
|
||||
return zRet;
|
||||
}
|
||||
|
||||
static void cidxFreeIndex(CidxIndex *pIdx){
|
||||
if( pIdx ){
|
||||
int i;
|
||||
for(i=0; i<pIdx->nCol; i++){
|
||||
sqlite3_free(pIdx->aCol[i].zExpr);
|
||||
}
|
||||
sqlite3_free(pIdx->zWhere);
|
||||
sqlite3_free(pIdx);
|
||||
}
|
||||
}
|
||||
|
||||
static int cidx_isspace(char c){
|
||||
return c==' ' || c=='\t' || c=='\r' || c=='\n';
|
||||
}
|
||||
|
||||
static int cidx_isident(char c){
|
||||
return c<0
|
||||
|| (c>='0' && c<='9') || (c>='a' && c<='z')
|
||||
|| (c>='A' && c<='Z') || c=='_';
|
||||
}
|
||||
|
||||
#define CIDX_PARSE_EOF 0
|
||||
#define CIDX_PARSE_COMMA 1 /* "," */
|
||||
#define CIDX_PARSE_OPEN 2 /* "(" */
|
||||
#define CIDX_PARSE_CLOSE 3 /* ")" */
|
||||
|
||||
/*
|
||||
** Argument zIn points into the start, middle or end of a CREATE INDEX
|
||||
** statement. If argument pbDoNotTrim is non-NULL, then this function
|
||||
** scans the input until it finds EOF, a comma (",") or an open or
|
||||
** close parenthesis character. It then sets (*pzOut) to point to said
|
||||
** character and returns a CIDX_PARSE_XXX constant as appropriate. The
|
||||
** parser is smart enough that special characters inside SQL strings
|
||||
** or comments are not returned for.
|
||||
**
|
||||
** Or, if argument pbDoNotTrim is NULL, then this function sets *pzOut
|
||||
** to point to the first character of the string that is not whitespace
|
||||
** or part of an SQL comment and returns CIDX_PARSE_EOF.
|
||||
**
|
||||
** Additionally, if pbDoNotTrim is not NULL and the element immediately
|
||||
** before (*pzOut) is an SQL comment of the form "-- comment", then
|
||||
** (*pbDoNotTrim) is set before returning. In all other cases it is
|
||||
** cleared.
|
||||
*/
|
||||
static int cidxFindNext(
|
||||
const char *zIn,
|
||||
const char **pzOut,
|
||||
int *pbDoNotTrim /* OUT: True if prev is -- comment */
|
||||
){
|
||||
const char *z = zIn;
|
||||
|
||||
while( 1 ){
|
||||
while( cidx_isspace(*z) ) z++;
|
||||
if( z[0]=='-' && z[1]=='-' ){
|
||||
z += 2;
|
||||
while( z[0]!='\n' ){
|
||||
if( z[0]=='\0' ) return CIDX_PARSE_EOF;
|
||||
z++;
|
||||
}
|
||||
while( cidx_isspace(*z) ) z++;
|
||||
if( pbDoNotTrim ) *pbDoNotTrim = 1;
|
||||
}else
|
||||
if( z[0]=='/' && z[1]=='*' ){
|
||||
z += 2;
|
||||
while( z[0]!='*' || z[1]!='/' ){
|
||||
if( z[1]=='\0' ) return CIDX_PARSE_EOF;
|
||||
z++;
|
||||
}
|
||||
z += 2;
|
||||
}else{
|
||||
*pzOut = z;
|
||||
if( pbDoNotTrim==0 ) return CIDX_PARSE_EOF;
|
||||
switch( *z ){
|
||||
case '\0':
|
||||
return CIDX_PARSE_EOF;
|
||||
case '(':
|
||||
return CIDX_PARSE_OPEN;
|
||||
case ')':
|
||||
return CIDX_PARSE_CLOSE;
|
||||
case ',':
|
||||
return CIDX_PARSE_COMMA;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
case '`': {
|
||||
char q = *z;
|
||||
z++;
|
||||
while( *z ){
|
||||
if( *z==q ){
|
||||
z++;
|
||||
if( *z!=q ) break;
|
||||
}
|
||||
z++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '[':
|
||||
while( *z++!=']' );
|
||||
break;
|
||||
|
||||
default:
|
||||
z++;
|
||||
break;
|
||||
}
|
||||
*pbDoNotTrim = 0;
|
||||
}
|
||||
}
|
||||
|
||||
assert( 0 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cidxParseSQL(CidxCursor *pCsr, CidxIndex *pIdx, const char *zSql){
|
||||
const char *z = zSql;
|
||||
const char *z1;
|
||||
int e;
|
||||
int rc = SQLITE_OK;
|
||||
int nParen = 1;
|
||||
int bDoNotTrim = 0;
|
||||
CidxColumn *pCol = pIdx->aCol;
|
||||
|
||||
e = cidxFindNext(z, &z, &bDoNotTrim);
|
||||
if( e!=CIDX_PARSE_OPEN ) goto parse_error;
|
||||
z1 = z+1;
|
||||
z++;
|
||||
while( nParen>0 ){
|
||||
e = cidxFindNext(z, &z, &bDoNotTrim);
|
||||
if( e==CIDX_PARSE_EOF ) goto parse_error;
|
||||
if( (e==CIDX_PARSE_COMMA || e==CIDX_PARSE_CLOSE) && nParen==1 ){
|
||||
const char *z2 = z;
|
||||
if( pCol->zExpr ) goto parse_error;
|
||||
|
||||
if( bDoNotTrim==0 ){
|
||||
while( cidx_isspace(z[-1]) ) z--;
|
||||
if( !sqlite3_strnicmp(&z[-3], "asc", 3) && 0==cidx_isident(z[-4]) ){
|
||||
z -= 3;
|
||||
while( cidx_isspace(z[-1]) ) z--;
|
||||
}else
|
||||
if( !sqlite3_strnicmp(&z[-4], "desc", 4) && 0==cidx_isident(z[-5]) ){
|
||||
z -= 4;
|
||||
while( cidx_isspace(z[-1]) ) z--;
|
||||
}
|
||||
while( cidx_isspace(z1[0]) ) z1++;
|
||||
}
|
||||
|
||||
pCol->zExpr = cidxMprintf(&rc, "%.*s", z-z1, z1);
|
||||
pCol++;
|
||||
z = z1 = z2+1;
|
||||
}
|
||||
if( e==CIDX_PARSE_OPEN ) nParen++;
|
||||
if( e==CIDX_PARSE_CLOSE ) nParen--;
|
||||
z++;
|
||||
}
|
||||
|
||||
/* Search for a WHERE clause */
|
||||
cidxFindNext(z, &z, 0);
|
||||
if( 0==sqlite3_strnicmp(z, "where", 5) ){
|
||||
pIdx->zWhere = cidxMprintf(&rc, "%s\n", &z[5]);
|
||||
}else if( z[0]!='\0' ){
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
parse_error:
|
||||
cidxCursorError(pCsr, "Parse error in: %s", zSql);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static int cidxLookupIndex(
|
||||
CidxCursor *pCsr, /* Cursor object */
|
||||
const char *zIdx, /* Name of index to look up */
|
||||
CidxIndex **ppIdx, /* OUT: Description of columns */
|
||||
char **pzTab /* OUT: Table name */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
char *zTab = 0;
|
||||
CidxIndex *pIdx = 0;
|
||||
|
||||
sqlite3_stmt *pFindTab = 0;
|
||||
sqlite3_stmt *pInfo = 0;
|
||||
|
||||
/* Find the table for this index. */
|
||||
pFindTab = cidxPrepare(&rc, pCsr,
|
||||
"SELECT tbl_name, sql FROM sqlite_schema WHERE name=%Q AND type='index'",
|
||||
zIdx
|
||||
);
|
||||
if( rc==SQLITE_OK && sqlite3_step(pFindTab)==SQLITE_ROW ){
|
||||
const char *zSql = (const char*)sqlite3_column_text(pFindTab, 1);
|
||||
zTab = cidxStrdup(&rc, (const char*)sqlite3_column_text(pFindTab, 0));
|
||||
|
||||
pInfo = cidxPrepare(&rc, pCsr, "PRAGMA index_xinfo(%Q)", zIdx);
|
||||
if( rc==SQLITE_OK ){
|
||||
int nAlloc = 0;
|
||||
int iCol = 0;
|
||||
|
||||
while( sqlite3_step(pInfo)==SQLITE_ROW ){
|
||||
const char *zName = (const char*)sqlite3_column_text(pInfo, 2);
|
||||
const char *zColl = (const char*)sqlite3_column_text(pInfo, 4);
|
||||
CidxColumn *p;
|
||||
if( zName==0 ) zName = "rowid";
|
||||
if( iCol==nAlloc ){
|
||||
int nByte = sizeof(CidxIndex) + sizeof(CidxColumn)*(nAlloc+8);
|
||||
pIdx = (CidxIndex*)sqlite3_realloc(pIdx, nByte);
|
||||
nAlloc += 8;
|
||||
}
|
||||
p = &pIdx->aCol[iCol++];
|
||||
p->bDesc = sqlite3_column_int(pInfo, 3);
|
||||
p->bKey = sqlite3_column_int(pInfo, 5);
|
||||
if( zSql==0 || p->bKey==0 ){
|
||||
p->zExpr = cidxMprintf(&rc, "\"%w\" COLLATE %s",zName,zColl);
|
||||
}else{
|
||||
p->zExpr = 0;
|
||||
}
|
||||
pIdx->nCol = iCol;
|
||||
pIdx->zWhere = 0;
|
||||
}
|
||||
cidxFinalize(&rc, pInfo);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && zSql ){
|
||||
rc = cidxParseSQL(pCsr, pIdx, zSql);
|
||||
}
|
||||
}
|
||||
|
||||
cidxFinalize(&rc, pFindTab);
|
||||
if( rc==SQLITE_OK && zTab==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(zTab);
|
||||
cidxFreeIndex(pIdx);
|
||||
}else{
|
||||
*pzTab = zTab;
|
||||
*ppIdx = pIdx;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cidxDecodeAfter(
|
||||
CidxCursor *pCsr,
|
||||
int nCol,
|
||||
const char *zAfterKey,
|
||||
char ***pazAfter
|
||||
){
|
||||
char **azAfter;
|
||||
int rc = SQLITE_OK;
|
||||
int nAfterKey = (int)strlen(zAfterKey);
|
||||
|
||||
azAfter = cidxMalloc(&rc, sizeof(char*)*nCol + nAfterKey+1);
|
||||
if( rc==SQLITE_OK ){
|
||||
int i;
|
||||
char *zCopy = (char*)&azAfter[nCol];
|
||||
char *p = zCopy;
|
||||
memcpy(zCopy, zAfterKey, nAfterKey+1);
|
||||
for(i=0; i<nCol; i++){
|
||||
while( *p==' ' ) p++;
|
||||
|
||||
/* Check NULL values */
|
||||
if( *p=='N' ){
|
||||
if( memcmp(p, "NULL", 4) ) goto parse_error;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
/* Check strings and blob literals */
|
||||
else if( *p=='X' || *p=='\'' ){
|
||||
azAfter[i] = p;
|
||||
if( *p=='X' ) p++;
|
||||
if( *p!='\'' ) goto parse_error;
|
||||
p++;
|
||||
while( 1 ){
|
||||
if( *p=='\0' ) goto parse_error;
|
||||
if( *p=='\'' ){
|
||||
p++;
|
||||
if( *p!='\'' ) break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check numbers */
|
||||
else{
|
||||
azAfter[i] = p;
|
||||
while( (*p>='0' && *p<='9')
|
||||
|| *p=='.' || *p=='+' || *p=='-' || *p=='e' || *p=='E'
|
||||
){
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
while( *p==' ' ) p++;
|
||||
if( *p!=(i==(nCol-1) ? '\0' : ',') ){
|
||||
goto parse_error;
|
||||
}
|
||||
*p++ = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
*pazAfter = azAfter;
|
||||
return rc;
|
||||
|
||||
parse_error:
|
||||
sqlite3_free(azAfter);
|
||||
*pazAfter = 0;
|
||||
cidxCursorError(pCsr, "%s", "error parsing after value");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
static char *cidxWhere(
|
||||
int *pRc, CidxColumn *aCol, char **azAfter, int iGt, int bLastIsNull
|
||||
){
|
||||
char *zRet = 0;
|
||||
const char *zSep = "";
|
||||
int i;
|
||||
|
||||
for(i=0; i<iGt; i++){
|
||||
zRet = cidxMprintf(pRc, "%z%s(%s) IS %s", zRet,
|
||||
zSep, aCol[i].zExpr, (azAfter[i] ? azAfter[i] : "NULL")
|
||||
);
|
||||
zSep = " AND ";
|
||||
}
|
||||
|
||||
if( bLastIsNull ){
|
||||
zRet = cidxMprintf(pRc, "%z%s(%s) IS NULL", zRet, zSep, aCol[iGt].zExpr);
|
||||
}
|
||||
else if( azAfter[iGt] ){
|
||||
zRet = cidxMprintf(pRc, "%z%s(%s) %s %s", zRet,
|
||||
zSep, aCol[iGt].zExpr, (aCol[iGt].bDesc ? "<" : ">"),
|
||||
azAfter[iGt]
|
||||
);
|
||||
}else{
|
||||
zRet = cidxMprintf(pRc, "%z%s(%s) IS NOT NULL", zRet, zSep,aCol[iGt].zExpr);
|
||||
}
|
||||
|
||||
return zRet;
|
||||
}
|
||||
|
||||
#define CIDX_CLIST_ALL 0
|
||||
#define CIDX_CLIST_ORDERBY 1
|
||||
#define CIDX_CLIST_CURRENT_KEY 2
|
||||
#define CIDX_CLIST_SUBWHERE 3
|
||||
#define CIDX_CLIST_SUBEXPR 4
|
||||
|
||||
/*
|
||||
** This function returns various strings based on the contents of the
|
||||
** CidxIndex structure and the eType parameter.
|
||||
*/
|
||||
static char *cidxColumnList(
|
||||
int *pRc, /* IN/OUT: Error code */
|
||||
const char *zIdx,
|
||||
CidxIndex *pIdx, /* Indexed columns */
|
||||
int eType /* True to include ASC/DESC */
|
||||
){
|
||||
char *zRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
const char *aDir[2] = {"", " DESC"};
|
||||
int i;
|
||||
const char *zSep = "";
|
||||
|
||||
for(i=0; i<pIdx->nCol; i++){
|
||||
CidxColumn *p = &pIdx->aCol[i];
|
||||
assert( pIdx->aCol[i].bDesc==0 || pIdx->aCol[i].bDesc==1 );
|
||||
switch( eType ){
|
||||
|
||||
case CIDX_CLIST_ORDERBY:
|
||||
zRet = cidxMprintf(pRc, "%z%s%d%s", zRet, zSep, i+1, aDir[p->bDesc]);
|
||||
zSep = ",";
|
||||
break;
|
||||
|
||||
case CIDX_CLIST_CURRENT_KEY:
|
||||
zRet = cidxMprintf(pRc, "%z%squote(i%d)", zRet, zSep, i);
|
||||
zSep = "||','||";
|
||||
break;
|
||||
|
||||
case CIDX_CLIST_SUBWHERE:
|
||||
if( p->bKey==0 ){
|
||||
zRet = cidxMprintf(pRc, "%z%s%s IS i.i%d", zRet,
|
||||
zSep, p->zExpr, i
|
||||
);
|
||||
zSep = " AND ";
|
||||
}
|
||||
break;
|
||||
|
||||
case CIDX_CLIST_SUBEXPR:
|
||||
if( p->bKey==1 ){
|
||||
zRet = cidxMprintf(pRc, "%z%s%s IS i.i%d", zRet,
|
||||
zSep, p->zExpr, i
|
||||
);
|
||||
zSep = " AND ";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( eType==CIDX_CLIST_ALL );
|
||||
zRet = cidxMprintf(pRc, "%z%s(%s) AS i%d", zRet, zSep, p->zExpr, i);
|
||||
zSep = ", ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return zRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate SQL (in memory obtained from sqlite3_malloc()) that will
|
||||
** continue the index scan for zIdxName starting after zAfterKey.
|
||||
*/
|
||||
int cidxGenerateScanSql(
|
||||
CidxCursor *pCsr, /* The cursor which needs the new statement */
|
||||
const char *zIdxName, /* index to be scanned */
|
||||
const char *zAfterKey, /* start after this key, if not NULL */
|
||||
char **pzSqlOut /* OUT: Write the generated SQL here */
|
||||
){
|
||||
int rc;
|
||||
char *zTab = 0;
|
||||
char *zCurrentKey = 0;
|
||||
char *zOrderBy = 0;
|
||||
char *zSubWhere = 0;
|
||||
char *zSubExpr = 0;
|
||||
char *zSrcList = 0;
|
||||
char **azAfter = 0;
|
||||
CidxIndex *pIdx = 0;
|
||||
|
||||
*pzSqlOut = 0;
|
||||
rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);
|
||||
|
||||
zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
|
||||
zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
|
||||
zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
|
||||
zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
|
||||
zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);
|
||||
|
||||
if( rc==SQLITE_OK && zAfterKey ){
|
||||
rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( zAfterKey==0 ){
|
||||
*pzSqlOut = cidxMprintf(&rc,
|
||||
"SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
|
||||
"FROM (SELECT %s FROM %Q INDEXED BY %Q %s%sORDER BY %s) AS i",
|
||||
zSubExpr, zTab, zSubWhere, zCurrentKey,
|
||||
zSrcList, zTab, zIdxName,
|
||||
(pIdx->zWhere ? "WHERE " : ""), (pIdx->zWhere ? pIdx->zWhere : ""),
|
||||
zOrderBy
|
||||
);
|
||||
}else{
|
||||
const char *zSep = "";
|
||||
char *zSql;
|
||||
int i;
|
||||
|
||||
zSql = cidxMprintf(&rc,
|
||||
"SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
|
||||
zSubExpr, zTab, zSubWhere, zCurrentKey
|
||||
);
|
||||
for(i=pIdx->nCol-1; i>=0; i--){
|
||||
int j;
|
||||
if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
|
||||
for(j=0; j<2; j++){
|
||||
char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
|
||||
zSql = cidxMprintf(&rc, "%z"
|
||||
"%sSELECT * FROM ("
|
||||
"SELECT %s FROM %Q INDEXED BY %Q WHERE %s%s%z ORDER BY %s"
|
||||
")",
|
||||
zSql, zSep, zSrcList, zTab, zIdxName,
|
||||
pIdx->zWhere ? pIdx->zWhere : "",
|
||||
pIdx->zWhere ? " AND " : "",
|
||||
zWhere, zOrderBy
|
||||
);
|
||||
zSep = " UNION ALL ";
|
||||
if( pIdx->aCol[i].bDesc==0 ) break;
|
||||
}
|
||||
}
|
||||
*pzSqlOut = cidxMprintf(&rc, "%z) AS i", zSql);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(zTab);
|
||||
sqlite3_free(zCurrentKey);
|
||||
sqlite3_free(zOrderBy);
|
||||
sqlite3_free(zSubWhere);
|
||||
sqlite3_free(zSubExpr);
|
||||
sqlite3_free(zSrcList);
|
||||
cidxFreeIndex(pIdx);
|
||||
sqlite3_free(azAfter);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Position a cursor back to the beginning.
|
||||
*/
|
||||
static int cidxFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
CidxCursor *pCsr = (CidxCursor*)pCursor;
|
||||
const char *zIdxName = 0;
|
||||
const char *zAfterKey = 0;
|
||||
|
||||
sqlite3_free(pCsr->zIdxName);
|
||||
pCsr->zIdxName = 0;
|
||||
sqlite3_free(pCsr->zAfterKey);
|
||||
pCsr->zAfterKey = 0;
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
|
||||
if( argc>0 ){
|
||||
zIdxName = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( argc>1 ){
|
||||
zAfterKey = (const char*)sqlite3_value_text(argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if( zIdxName ){
|
||||
char *zSql = 0;
|
||||
pCsr->zIdxName = sqlite3_mprintf("%s", zIdxName);
|
||||
pCsr->zAfterKey = zAfterKey ? sqlite3_mprintf("%s", zAfterKey) : 0;
|
||||
rc = cidxGenerateScanSql(pCsr, zIdxName, zAfterKey, &zSql);
|
||||
if( zSql ){
|
||||
pCsr->pStmt = cidxPrepare(&rc, pCsr, "%z", zSql);
|
||||
}
|
||||
}
|
||||
|
||||
if( pCsr->pStmt ){
|
||||
assert( rc==SQLITE_OK );
|
||||
rc = cidxNext(pCursor);
|
||||
}
|
||||
pCsr->iRowid = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a column value.
|
||||
*/
|
||||
static int cidxColumn(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
int iCol
|
||||
){
|
||||
CidxCursor *pCsr = (CidxCursor*)pCursor;
|
||||
assert( iCol>=IIC_ERRMSG && iCol<=IIC_SCANNER_SQL );
|
||||
switch( iCol ){
|
||||
case IIC_ERRMSG: {
|
||||
const char *zVal = 0;
|
||||
if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
|
||||
if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
|
||||
zVal = "row data mismatch";
|
||||
}
|
||||
}else{
|
||||
zVal = "row missing";
|
||||
}
|
||||
sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
case IIC_CURRENT_KEY: {
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));
|
||||
break;
|
||||
}
|
||||
case IIC_INDEX_NAME: {
|
||||
sqlite3_result_text(ctx, pCsr->zIdxName, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case IIC_AFTER_KEY: {
|
||||
sqlite3_result_text(ctx, pCsr->zAfterKey, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case IIC_SCANNER_SQL: {
|
||||
char *zSql = 0;
|
||||
cidxGenerateScanSql(pCsr, pCsr->zIdxName, pCsr->zAfterKey, &zSql);
|
||||
sqlite3_result_text(ctx, zSql, -1, sqlite3_free);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Return the ROWID for the sqlite_btreeinfo table */
|
||||
static int cidxRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
CidxCursor *pCsr = (CidxCursor*)pCursor;
|
||||
*pRowid = pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the virtual table modules with the database handle passed
|
||||
** as the only argument.
|
||||
*/
|
||||
static int ciInit(sqlite3 *db){
|
||||
static sqlite3_module cidx_module = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
cidxConnect, /* xConnect */
|
||||
cidxBestIndex, /* xBestIndex */
|
||||
cidxDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
cidxOpen, /* xOpen - open a cursor */
|
||||
cidxClose, /* xClose - close a cursor */
|
||||
cidxFilter, /* xFilter - configure scan constraints */
|
||||
cidxNext, /* xNext - advance a cursor */
|
||||
cidxEof, /* xEof - check for end of scan */
|
||||
cidxColumn, /* xColumn - read data */
|
||||
cidxRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
return sqlite3_create_module(db, "incremental_index_check", &cidx_module, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Extension load function.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_checkindex_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return ciInit(db);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
** Read an SQLite database file and analyze its space utilization. Generate
|
||||
** text on standard output.
|
||||
*/
|
||||
#define TCLSH_INIT_PROC sqlite3_checker_init_proc
|
||||
#define SQLITE_ENABLE_DBPAGE_VTAB 1
|
||||
#undef SQLITE_THREADSAFE
|
||||
#define SQLITE_THREADSAFE 0
|
||||
#undef SQLITE_ENABLE_COLUMN_METADATA
|
||||
#define SQLITE_OMIT_DECLTYPE 1
|
||||
#define SQLITE_OMIT_DEPRECATED 1
|
||||
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
|
||||
#define SQLITE_OMIT_SHARED_CACHE 1
|
||||
#define SQLITE_DEFAULT_MEMSTATUS 0
|
||||
#define SQLITE_MAX_EXPR_DEPTH 0
|
||||
INCLUDE sqlite3.c
|
||||
INCLUDE $ROOT/src/tclsqlite.c
|
||||
INCLUDE $ROOT/ext/misc/btreeinfo.c
|
||||
INCLUDE $ROOT/ext/repair/checkindex.c
|
||||
INCLUDE $ROOT/ext/repair/checkfreelist.c
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
|
||||
struct SqliteDb *p;
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
if( Tcl_GetCommandInfo(interp, zA, &cmdInfo) ){
|
||||
p = (struct SqliteDb*)cmdInfo.objClientData;
|
||||
*ppDb = p->db;
|
||||
return TCL_OK;
|
||||
}else{
|
||||
*ppDb = 0;
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_imposter db main rootpage {CREATE TABLE...} ;# setup an imposter
|
||||
** sqlite3_imposter db main ;# rm all imposters
|
||||
*/
|
||||
static int sqlite3_imposter(
|
||||
void *clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zSchema;
|
||||
int iRoot;
|
||||
const char *zSql;
|
||||
|
||||
if( objc!=3 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SCHEMA [ROOTPAGE SQL]");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSchema = Tcl_GetString(objv[2]);
|
||||
if( objc==3 ){
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 0, 1);
|
||||
}else{
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &iRoot) ) return TCL_ERROR;
|
||||
zSql = Tcl_GetString(objv[4]);
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 1, iRoot);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, zSchema, 0, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
const char *sqlite3_checker_init_proc(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_imposter",
|
||||
(Tcl_ObjCmdProc*)sqlite3_imposter, 0, 0);
|
||||
sqlite3_auto_extension((void(*)(void))sqlite3_btreeinfo_init);
|
||||
sqlite3_auto_extension((void(*)(void))sqlite3_checkindex_init);
|
||||
sqlite3_auto_extension((void(*)(void))sqlite3_checkfreelist_init);
|
||||
return
|
||||
BEGIN_STRING
|
||||
INCLUDE $ROOT/ext/repair/sqlite3_checker.tcl
|
||||
END_STRING
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
# This TCL script is the main driver script for the sqlite3_checker utility
|
||||
# program.
|
||||
#
|
||||
|
||||
# Special case:
|
||||
#
|
||||
# sqlite3_checker --test FILENAME ARGS
|
||||
#
|
||||
# uses FILENAME in place of this script.
|
||||
#
|
||||
if {[lindex $argv 0]=="--test" && [llength $argv]>1} {
|
||||
set ::argv0 [lindex $argv 1]
|
||||
set argv [lrange $argv 2 end]
|
||||
source $argv0
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Emulate a TCL shell
|
||||
#
|
||||
proc tclsh {} {
|
||||
set line {}
|
||||
while {![eof stdin]} {
|
||||
if {$line!=""} {
|
||||
puts -nonewline "> "
|
||||
} else {
|
||||
puts -nonewline "% "
|
||||
}
|
||||
flush stdout
|
||||
append line [gets stdin]
|
||||
if {[info complete $line]} {
|
||||
if {[catch {uplevel #0 $line} result]} {
|
||||
puts stderr "Error: $result"
|
||||
} elseif {$result!=""} {
|
||||
puts $result
|
||||
}
|
||||
set line {}
|
||||
} else {
|
||||
append line \n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Do an incremental integrity check of a single index
|
||||
#
|
||||
proc check_index {idxname batchsize bTrace} {
|
||||
set i 0
|
||||
set more 1
|
||||
set nerr 0
|
||||
set pct 00.0
|
||||
set max [db one {SELECT nEntry FROM sqlite_btreeinfo('main')
|
||||
WHERE name=$idxname}]
|
||||
puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
|
||||
flush stdout
|
||||
if {$bTrace} {
|
||||
set sql {SELECT errmsg, current_key AS key,
|
||||
CASE WHEN rowid=1 THEN scanner_sql END AS traceOut
|
||||
FROM incremental_index_check($idxname)
|
||||
WHERE after_key=$key
|
||||
LIMIT $batchsize}
|
||||
} else {
|
||||
set sql {SELECT errmsg, current_key AS key, NULL AS traceOut
|
||||
FROM incremental_index_check($idxname)
|
||||
WHERE after_key=$key
|
||||
LIMIT $batchsize}
|
||||
}
|
||||
while {$more} {
|
||||
set more 0
|
||||
db eval $sql {
|
||||
set more 1
|
||||
if {$errmsg!=""} {
|
||||
incr nerr
|
||||
puts "$idxname: key($key): $errmsg"
|
||||
} elseif {$traceOut!=""} {
|
||||
puts "$idxname: $traceOut"
|
||||
}
|
||||
incr i
|
||||
|
||||
}
|
||||
set x [format {%.1f} [expr {($i*100.0)/$max}]]
|
||||
if {$x!=$pct} {
|
||||
puts -nonewline "$idxname: $i of $max rows ($pct%)\r"
|
||||
flush stdout
|
||||
set pct $x
|
||||
}
|
||||
}
|
||||
puts "$idxname: $nerr errors out of $i entries"
|
||||
}
|
||||
|
||||
# Print a usage message on standard error, then quit.
|
||||
#
|
||||
proc usage {} {
|
||||
set argv0 [file rootname [file tail [info nameofexecutable]]]
|
||||
puts stderr "Usage: $argv0 OPTIONS database-filename"
|
||||
puts stderr {
|
||||
Do sanity checking on a live SQLite3 database file specified by the
|
||||
"database-filename" argument.
|
||||
|
||||
Options:
|
||||
|
||||
--batchsize N Number of rows to check per transaction
|
||||
|
||||
--freelist Perform a freelist check
|
||||
|
||||
--index NAME Run a check of the index NAME
|
||||
|
||||
--summary Print summary information about the database
|
||||
|
||||
--table NAME Run a check of all indexes for table NAME
|
||||
|
||||
--tclsh Run the built-in TCL interpreter (for debugging)
|
||||
|
||||
--trace (Debugging only:) Output trace information on the scan
|
||||
|
||||
--version Show the version number of SQLite
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
set file_to_analyze {}
|
||||
append argv {}
|
||||
set bFreelistCheck 0
|
||||
set bSummary 0
|
||||
set zIndex {}
|
||||
set zTable {}
|
||||
set batchsize 1000
|
||||
set bAll 1
|
||||
set bTrace 0
|
||||
set argc [llength $argv]
|
||||
for {set i 0} {$i<$argc} {incr i} {
|
||||
set arg [lindex $argv $i]
|
||||
if {[regexp {^-+tclsh$} $arg]} {
|
||||
tclsh
|
||||
exit 0
|
||||
}
|
||||
if {[regexp {^-+version$} $arg]} {
|
||||
sqlite3 mem :memory:
|
||||
puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
|
||||
mem close
|
||||
exit 0
|
||||
}
|
||||
if {[regexp {^-+freelist$} $arg]} {
|
||||
set bFreelistCheck 1
|
||||
set bAll 0
|
||||
continue
|
||||
}
|
||||
if {[regexp {^-+summary$} $arg]} {
|
||||
set bSummary 1
|
||||
set bAll 0
|
||||
continue
|
||||
}
|
||||
if {[regexp {^-+trace$} $arg]} {
|
||||
set bTrace 1
|
||||
continue
|
||||
}
|
||||
if {[regexp {^-+batchsize$} $arg]} {
|
||||
incr i
|
||||
if {$i>=$argc} {
|
||||
puts stderr "missing argument on $arg"
|
||||
exit 1
|
||||
}
|
||||
set batchsize [lindex $argv $i]
|
||||
continue
|
||||
}
|
||||
if {[regexp {^-+index$} $arg]} {
|
||||
incr i
|
||||
if {$i>=$argc} {
|
||||
puts stderr "missing argument on $arg"
|
||||
exit 1
|
||||
}
|
||||
set zIndex [lindex $argv $i]
|
||||
set bAll 0
|
||||
continue
|
||||
}
|
||||
if {[regexp {^-+table$} $arg]} {
|
||||
incr i
|
||||
if {$i>=$argc} {
|
||||
puts stderr "missing argument on $arg"
|
||||
exit 1
|
||||
}
|
||||
set zTable [lindex $argv $i]
|
||||
set bAll 0
|
||||
continue
|
||||
}
|
||||
if {[regexp {^-} $arg]} {
|
||||
puts stderr "Unknown option: $arg"
|
||||
usage
|
||||
}
|
||||
if {$file_to_analyze!=""} {
|
||||
usage
|
||||
} else {
|
||||
set file_to_analyze $arg
|
||||
}
|
||||
}
|
||||
if {$file_to_analyze==""} usage
|
||||
|
||||
# If a TCL script is specified on the command-line, then run that
|
||||
# script.
|
||||
#
|
||||
if {[file extension $file_to_analyze]==".tcl"} {
|
||||
source $file_to_analyze
|
||||
exit 0
|
||||
}
|
||||
|
||||
set root_filename $file_to_analyze
|
||||
regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
|
||||
if {![file exists $root_filename]} {
|
||||
puts stderr "No such file: $root_filename"
|
||||
exit 1
|
||||
}
|
||||
if {![file readable $root_filename]} {
|
||||
puts stderr "File is not readable: $root_filename"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if {[catch {sqlite3 db $file_to_analyze} res]} {
|
||||
puts stderr "Cannot open datababase $root_filename: $res"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if {$bFreelistCheck || $bAll} {
|
||||
puts -nonewline "freelist-check: "
|
||||
flush stdout
|
||||
db eval BEGIN
|
||||
puts [db one {SELECT checkfreelist('main')}]
|
||||
db eval END
|
||||
}
|
||||
if {$bSummary} {
|
||||
set scale 0
|
||||
set pgsz [db one {PRAGMA page_size}]
|
||||
db eval {SELECT nPage*$pgsz AS sz, name, tbl_name
|
||||
FROM sqlite_btreeinfo
|
||||
WHERE type='index'
|
||||
ORDER BY 1 DESC, name} {
|
||||
if {$scale==0} {
|
||||
if {$sz>10000000} {
|
||||
set scale 1000000.0
|
||||
set unit MB
|
||||
} else {
|
||||
set scale 1000.0
|
||||
set unit KB
|
||||
}
|
||||
}
|
||||
puts [format {%7.1f %s index %s of table %s} \
|
||||
[expr {$sz/$scale}] $unit $name $tbl_name]
|
||||
}
|
||||
}
|
||||
if {$zIndex!=""} {
|
||||
check_index $zIndex $batchsize $bTrace
|
||||
}
|
||||
if {$zTable!=""} {
|
||||
foreach idx [db eval {SELECT name FROM sqlite_master
|
||||
WHERE type='index' AND rootpage>0
|
||||
AND tbl_name=$zTable}] {
|
||||
check_index $idx $batchsize $bTrace
|
||||
}
|
||||
}
|
||||
if {$bAll} {
|
||||
set allidx [db eval {SELECT name FROM sqlite_btreeinfo('main')
|
||||
WHERE type='index' AND rootpage>0
|
||||
ORDER BY nEntry}]
|
||||
foreach idx $allidx {
|
||||
check_index $idx $batchsize $bTrace
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
To run these tests, first build sqlite3_checker:
|
||||
|
||||
|
||||
> make sqlite3_checker
|
||||
|
||||
|
||||
Then run the "test.tcl" script using:
|
||||
|
||||
|
||||
> ./sqlite3_checker --test $path/test.tcl
|
||||
|
||||
|
||||
Optionally add the full pathnames of individual *.test modules
|
||||
@@ -0,0 +1,92 @@
|
||||
# 2017-10-11
|
||||
|
||||
set testprefix checkfreelist
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size=1024;
|
||||
CREATE TABLE t1(a, b);
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 { SELECT checkfreelist('main') } {ok}
|
||||
do_execsql_test 1.3 {
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
|
||||
)
|
||||
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM s;
|
||||
DELETE FROM t1 WHERE rowid%3;
|
||||
PRAGMA freelist_count;
|
||||
} {6726}
|
||||
|
||||
do_execsql_test 1.4 { SELECT checkfreelist('main') } {ok}
|
||||
do_execsql_test 1.5 {
|
||||
WITH freelist_trunk(i, d, n) AS (
|
||||
SELECT 1, NULL, sqlite_readint32(data, 32) FROM sqlite_dbpage WHERE pgno=1
|
||||
UNION ALL
|
||||
SELECT n, data, sqlite_readint32(data)
|
||||
FROM freelist_trunk, sqlite_dbpage WHERE pgno=n
|
||||
)
|
||||
SELECT i FROM freelist_trunk WHERE i!=1;
|
||||
} {
|
||||
10009 9715 9343 8969 8595 8222 7847 7474 7102 6727 6354 5982 5608 5234
|
||||
4860 4487 4112 3740 3367 2992 2619 2247 1872 1499 1125 752 377 5
|
||||
}
|
||||
|
||||
do_execsql_test 1.6 { SELECT checkfreelist('main') } {ok}
|
||||
|
||||
proc set_int {blob idx newval} {
|
||||
binary scan $blob I* ints
|
||||
lset ints $idx $newval
|
||||
binary format I* $ints
|
||||
}
|
||||
db func set_int set_int
|
||||
|
||||
proc get_int {blob idx} {
|
||||
binary scan $blob I* ints
|
||||
lindex $ints $idx
|
||||
}
|
||||
db func get_int get_int
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
BEGIN;
|
||||
UPDATE sqlite_dbpage
|
||||
SET data = set_int(data, 1, get_int(data, 1)-1)
|
||||
WHERE pgno=4860;
|
||||
SELECT checkfreelist('main');
|
||||
ROLLBACK;
|
||||
} {{free-list count mismatch: actual=6725 header=6726}}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
BEGIN;
|
||||
UPDATE sqlite_dbpage
|
||||
SET data = set_int(data, 5, (SELECT * FROM pragma_page_count)+1)
|
||||
WHERE pgno=4860;
|
||||
SELECT checkfreelist('main');
|
||||
ROLLBACK;
|
||||
} {{leaf page 10092 is out of range (child 3 of trunk page 4860)}}
|
||||
|
||||
do_execsql_test 1.9 {
|
||||
BEGIN;
|
||||
UPDATE sqlite_dbpage
|
||||
SET data = set_int(data, 5, 0)
|
||||
WHERE pgno=4860;
|
||||
SELECT checkfreelist('main');
|
||||
ROLLBACK;
|
||||
} {{leaf page 0 is out of range (child 3 of trunk page 4860)}}
|
||||
|
||||
do_execsql_test 1.10 {
|
||||
BEGIN;
|
||||
UPDATE sqlite_dbpage
|
||||
SET data = set_int(data, get_int(data, 1)+1, 0)
|
||||
WHERE pgno=5;
|
||||
SELECT checkfreelist('main');
|
||||
ROLLBACK;
|
||||
} {{leaf page 0 is out of range (child 247 of trunk page 5)}}
|
||||
|
||||
do_execsql_test 1.11 {
|
||||
BEGIN;
|
||||
UPDATE sqlite_dbpage
|
||||
SET data = set_int(data, 1, 249)
|
||||
WHERE pgno=5;
|
||||
SELECT checkfreelist('main');
|
||||
ROLLBACK;
|
||||
} {{leaf count out of range (249) on trunk page 5}}
|
||||
@@ -0,0 +1,349 @@
|
||||
# 2017-10-11
|
||||
#
|
||||
set testprefix checkindex
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
INSERT INTO t1 VALUES('one', 2);
|
||||
INSERT INTO t1 VALUES('two', 4);
|
||||
INSERT INTO t1 VALUES('three', 6);
|
||||
INSERT INTO t1 VALUES('four', 8);
|
||||
INSERT INTO t1 VALUES('five', 10);
|
||||
|
||||
CREATE INDEX i2 ON t1(a DESC);
|
||||
} {}
|
||||
|
||||
proc incr_index_check {idx nStep} {
|
||||
set Q {
|
||||
SELECT errmsg, current_key FROM incremental_index_check($idx, $after)
|
||||
LIMIT $nStep
|
||||
}
|
||||
|
||||
set res [list]
|
||||
while {1} {
|
||||
unset -nocomplain current_key
|
||||
set res1 [db eval $Q]
|
||||
if {[llength $res1]==0} break
|
||||
set res [concat $res $res1]
|
||||
set after [lindex $res end]
|
||||
}
|
||||
|
||||
return $res
|
||||
}
|
||||
|
||||
proc do_index_check_test {tn idx res} {
|
||||
uplevel [list do_execsql_test $tn.1 "
|
||||
SELECT errmsg, current_key FROM incremental_index_check('$idx');
|
||||
" $res]
|
||||
|
||||
uplevel [list do_test $tn.2 "incr_index_check $idx 1" [list {*}$res]]
|
||||
uplevel [list do_test $tn.3 "incr_index_check $idx 2" [list {*}$res]]
|
||||
uplevel [list do_test $tn.4 "incr_index_check $idx 5" [list {*}$res]]
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 1.2.1 {
|
||||
SELECT rowid, errmsg IS NULL, current_key FROM incremental_index_check('i1');
|
||||
} {
|
||||
1 1 'five',5
|
||||
2 1 'four',4
|
||||
3 1 'one',1
|
||||
4 1 'three',3
|
||||
5 1 'two',2
|
||||
}
|
||||
do_execsql_test 1.2.2 {
|
||||
SELECT errmsg IS NULL, current_key, index_name, after_key, scanner_sql
|
||||
FROM incremental_index_check('i1') LIMIT 1;
|
||||
} {
|
||||
1
|
||||
'five',5
|
||||
i1
|
||||
{}
|
||||
{SELECT (SELECT a IS i.i0 FROM 't1' AS t WHERE "rowid" COLLATE BINARY IS i.i1), quote(i0)||','||quote(i1) FROM (SELECT (a) AS i0, ("rowid" COLLATE BINARY) AS i1 FROM 't1' INDEXED BY 'i1' ORDER BY 1,2) AS i}
|
||||
}
|
||||
|
||||
do_index_check_test 1.3 i1 {
|
||||
{} 'five',5
|
||||
{} 'four',4
|
||||
{} 'one',1
|
||||
{} 'three',3
|
||||
{} 'two',2
|
||||
}
|
||||
|
||||
do_index_check_test 1.4 i2 {
|
||||
{} 'two',2
|
||||
{} 'three',3
|
||||
{} 'one',1
|
||||
{} 'four',4
|
||||
{} 'five',5
|
||||
}
|
||||
|
||||
do_test 1.5 {
|
||||
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t1' }]
|
||||
sqlite3_imposter db main $tblroot {CREATE TABLE xt1(a,b)}
|
||||
db eval {
|
||||
UPDATE xt1 SET a='six' WHERE rowid=3;
|
||||
DELETE FROM xt1 WHERE rowid = 5;
|
||||
}
|
||||
sqlite3_imposter db main
|
||||
} {}
|
||||
|
||||
do_index_check_test 1.6 i1 {
|
||||
{row missing} 'five',5
|
||||
{} 'four',4
|
||||
{} 'one',1
|
||||
{row data mismatch} 'three',3
|
||||
{} 'two',2
|
||||
}
|
||||
|
||||
do_index_check_test 1.7 i2 {
|
||||
{} 'two',2
|
||||
{row data mismatch} 'three',3
|
||||
{} 'one',1
|
||||
{} 'four',4
|
||||
{row missing} 'five',5
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
do_execsql_test 2.0 {
|
||||
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c, d);
|
||||
|
||||
INSERT INTO t2 VALUES(1, NULL, 1, 1);
|
||||
INSERT INTO t2 VALUES(2, 1, NULL, 1);
|
||||
INSERT INTO t2 VALUES(3, 1, 1, NULL);
|
||||
|
||||
INSERT INTO t2 VALUES(4, 2, 2, 1);
|
||||
INSERT INTO t2 VALUES(5, 2, 2, 2);
|
||||
INSERT INTO t2 VALUES(6, 2, 2, 3);
|
||||
|
||||
INSERT INTO t2 VALUES(7, 2, 2, 1);
|
||||
INSERT INTO t2 VALUES(8, 2, 2, 2);
|
||||
INSERT INTO t2 VALUES(9, 2, 2, 3);
|
||||
|
||||
CREATE INDEX i3 ON t2(b, c, d);
|
||||
CREATE INDEX i4 ON t2(b DESC, c DESC, d DESC);
|
||||
CREATE INDEX i5 ON t2(d, c DESC, b);
|
||||
} {}
|
||||
|
||||
do_index_check_test 2.1 i3 {
|
||||
{} NULL,1,1,1
|
||||
{} 1,NULL,1,2
|
||||
{} 1,1,NULL,3
|
||||
{} 2,2,1,4
|
||||
{} 2,2,1,7
|
||||
{} 2,2,2,5
|
||||
{} 2,2,2,8
|
||||
{} 2,2,3,6
|
||||
{} 2,2,3,9
|
||||
}
|
||||
|
||||
do_index_check_test 2.2 i4 {
|
||||
{} 2,2,3,6
|
||||
{} 2,2,3,9
|
||||
{} 2,2,2,5
|
||||
{} 2,2,2,8
|
||||
{} 2,2,1,4
|
||||
{} 2,2,1,7
|
||||
{} 1,1,NULL,3
|
||||
{} 1,NULL,1,2
|
||||
{} NULL,1,1,1
|
||||
}
|
||||
|
||||
do_index_check_test 2.3 i5 {
|
||||
{} NULL,1,1,3
|
||||
{} 1,2,2,4
|
||||
{} 1,2,2,7
|
||||
{} 1,1,NULL,1
|
||||
{} 1,NULL,1,2
|
||||
{} 2,2,2,5
|
||||
{} 2,2,2,8
|
||||
{} 3,2,2,6
|
||||
{} 3,2,2,9
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
do_execsql_test 3.0 {
|
||||
|
||||
CREATE TABLE t3(w, x, y, z PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE INDEX t3wxy ON t3(w, x, y);
|
||||
CREATE INDEX t3wxy2 ON t3(w DESC, x DESC, y DESC);
|
||||
|
||||
INSERT INTO t3 VALUES(NULL, NULL, NULL, 1);
|
||||
INSERT INTO t3 VALUES(NULL, NULL, NULL, 2);
|
||||
INSERT INTO t3 VALUES(NULL, NULL, NULL, 3);
|
||||
|
||||
INSERT INTO t3 VALUES('a', NULL, NULL, 4);
|
||||
INSERT INTO t3 VALUES('a', NULL, NULL, 5);
|
||||
INSERT INTO t3 VALUES('a', NULL, NULL, 6);
|
||||
|
||||
INSERT INTO t3 VALUES('a', 'b', NULL, 7);
|
||||
INSERT INTO t3 VALUES('a', 'b', NULL, 8);
|
||||
INSERT INTO t3 VALUES('a', 'b', NULL, 9);
|
||||
|
||||
} {}
|
||||
|
||||
do_index_check_test 3.1 t3wxy {
|
||||
{} NULL,NULL,NULL,1 {} NULL,NULL,NULL,2 {} NULL,NULL,NULL,3
|
||||
{} 'a',NULL,NULL,4 {} 'a',NULL,NULL,5 {} 'a',NULL,NULL,6
|
||||
{} 'a','b',NULL,7 {} 'a','b',NULL,8 {} 'a','b',NULL,9
|
||||
}
|
||||
do_index_check_test 3.2 t3wxy2 {
|
||||
{} 'a','b',NULL,7 {} 'a','b',NULL,8 {} 'a','b',NULL,9
|
||||
{} 'a',NULL,NULL,4 {} 'a',NULL,NULL,5 {} 'a',NULL,NULL,6
|
||||
{} NULL,NULL,NULL,1 {} NULL,NULL,NULL,2 {} NULL,NULL,NULL,3
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test with an index that uses non-default collation sequences.
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t4(a INTEGER PRIMARY KEY, c1 TEXT, c2 TEXT);
|
||||
INSERT INTO t4 VALUES(1, 'aaa', 'bbb');
|
||||
INSERT INTO t4 VALUES(2, 'AAA', 'CCC');
|
||||
INSERT INTO t4 VALUES(3, 'aab', 'ddd');
|
||||
INSERT INTO t4 VALUES(4, 'AAB', 'EEE');
|
||||
|
||||
CREATE INDEX t4cc ON t4(c1 COLLATE nocase, c2 COLLATE nocase);
|
||||
}
|
||||
|
||||
do_index_check_test 4.1 t4cc {
|
||||
{} 'aaa','bbb',1
|
||||
{} 'AAA','CCC',2
|
||||
{} 'aab','ddd',3
|
||||
{} 'AAB','EEE',4
|
||||
}
|
||||
|
||||
do_test 4.2 {
|
||||
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t4' }]
|
||||
sqlite3_imposter db main $tblroot \
|
||||
{CREATE TABLE xt4(a INTEGER PRIMARY KEY, c1 TEXT, c2 TEXT)}
|
||||
|
||||
db eval {
|
||||
UPDATE xt4 SET c1='hello' WHERE rowid=2;
|
||||
DELETE FROM xt4 WHERE rowid = 3;
|
||||
}
|
||||
sqlite3_imposter db main
|
||||
} {}
|
||||
|
||||
do_index_check_test 4.3 t4cc {
|
||||
{} 'aaa','bbb',1
|
||||
{row data mismatch} 'AAA','CCC',2
|
||||
{row missing} 'aab','ddd',3
|
||||
{} 'AAB','EEE',4
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test an index on an expression.
|
||||
#
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t5(x INTEGER PRIMARY KEY, y TEXT, UNIQUE(y));
|
||||
INSERT INTO t5 VALUES(1, '{"x":1, "y":1}');
|
||||
INSERT INTO t5 VALUES(2, '{"x":2, "y":2}');
|
||||
INSERT INTO t5 VALUES(3, '{"x":3, "y":3}');
|
||||
INSERT INTO t5 VALUES(4, '{"w":4, "z":4}');
|
||||
INSERT INTO t5 VALUES(5, '{"x":5, "y":5}');
|
||||
|
||||
CREATE INDEX t5x ON t5( json_extract(y, '$.x') );
|
||||
CREATE INDEX t5y ON t5( json_extract(y, '$.y') DESC );
|
||||
}
|
||||
|
||||
do_index_check_test 5.1.1 t5x {
|
||||
{} NULL,4 {} 1,1 {} 2,2 {} 3,3 {} 5,5
|
||||
}
|
||||
|
||||
do_index_check_test 5.1.2 t5y {
|
||||
{} 5,5 {} 3,3 {} 2,2 {} 1,1 {} NULL,4
|
||||
}
|
||||
|
||||
do_index_check_test 5.1.3 sqlite_autoindex_t5_1 {
|
||||
{} {'{"w":4, "z":4}',4}
|
||||
{} {'{"x":1, "y":1}',1}
|
||||
{} {'{"x":2, "y":2}',2}
|
||||
{} {'{"x":3, "y":3}',3}
|
||||
{} {'{"x":5, "y":5}',5}
|
||||
}
|
||||
|
||||
do_test 5.2 {
|
||||
set tblroot [db one { SELECT rootpage FROM sqlite_master WHERE name='t5' }]
|
||||
sqlite3_imposter db main $tblroot \
|
||||
{CREATE TABLE xt5(a INTEGER PRIMARY KEY, c1 TEXT);}
|
||||
db eval {
|
||||
UPDATE xt5 SET c1='{"x":22, "y":11}' WHERE rowid=1;
|
||||
DELETE FROM xt5 WHERE rowid = 4;
|
||||
}
|
||||
sqlite3_imposter db main
|
||||
} {}
|
||||
|
||||
do_index_check_test 5.3.1 t5x {
|
||||
{row missing} NULL,4
|
||||
{row data mismatch} 1,1
|
||||
{} 2,2
|
||||
{} 3,3
|
||||
{} 5,5
|
||||
}
|
||||
|
||||
do_index_check_test 5.3.2 sqlite_autoindex_t5_1 {
|
||||
{row missing} {'{"w":4, "z":4}',4}
|
||||
{row data mismatch} {'{"x":1, "y":1}',1}
|
||||
{} {'{"x":2, "y":2}',2}
|
||||
{} {'{"x":3, "y":3}',3}
|
||||
{} {'{"x":5, "y":5}',5}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t6(x INTEGER PRIMARY KEY, y, z);
|
||||
CREATE INDEX t6x1 ON t6(y, /* one,two,three */ z);
|
||||
CREATE INDEX t6x2 ON t6(z, -- hello,world,
|
||||
y);
|
||||
|
||||
CREATE INDEX t6x3 ON t6(z -- hello,world
|
||||
, y);
|
||||
|
||||
INSERT INTO t6 VALUES(1, 2, 3);
|
||||
INSERT INTO t6 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
do_index_check_test 6.1 t6x1 {
|
||||
{} 2,3,1
|
||||
{} 5,6,4
|
||||
}
|
||||
do_index_check_test 6.2 t6x2 {
|
||||
{} 3,2,1
|
||||
{} 6,5,4
|
||||
}
|
||||
do_index_check_test 6.2 t6x3 {
|
||||
{} 3,2,1
|
||||
{} 6,5,4
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE t7(x INTEGER PRIMARY KEY, y, z);
|
||||
INSERT INTO t7 VALUES(1, 1, 1);
|
||||
INSERT INTO t7 VALUES(2, 2, 0);
|
||||
INSERT INTO t7 VALUES(3, 3, 1);
|
||||
INSERT INTO t7 VALUES(4, 4, 0);
|
||||
|
||||
CREATE INDEX t7i1 ON t7(y) WHERE z=1;
|
||||
CREATE INDEX t7i2 ON t7(y) /* hello,world */ WHERE z=1;
|
||||
CREATE INDEX t7i3 ON t7(y) WHERE -- yep
|
||||
z=1;
|
||||
CREATE INDEX t7i4 ON t7(y) WHERE z=1 -- yep;
|
||||
}
|
||||
do_index_check_test 7.1 t7i1 {
|
||||
{} 1,1 {} 3,3
|
||||
}
|
||||
do_index_check_test 7.2 t7i2 {
|
||||
{} 1,1 {} 3,3
|
||||
}
|
||||
do_index_check_test 7.3 t7i3 {
|
||||
{} 1,1 {} 3,3
|
||||
}
|
||||
do_index_check_test 7.4 t7i4 {
|
||||
{} 1,1 {} 3,3
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# Run this script using
|
||||
#
|
||||
# sqlite3_checker --test $thisscript $testscripts
|
||||
#
|
||||
# The $testscripts argument is optional. If omitted, all *.test files
|
||||
# in the same directory as $thisscript are run.
|
||||
#
|
||||
set NTEST 0
|
||||
set NERR 0
|
||||
|
||||
|
||||
# Invoke the do_test procedure to run a single test
|
||||
#
|
||||
# The $expected parameter is the expected result. The result is the return
|
||||
# value from the last TCL command in $cmd.
|
||||
#
|
||||
# Normally, $expected must match exactly. But if $expected is of the form
|
||||
# "/regexp/" then regular expression matching is used. If $expected is
|
||||
# "~/regexp/" then the regular expression must NOT match. If $expected is
|
||||
# of the form "#/value-list/" then each term in value-list must be numeric
|
||||
# and must approximately match the corresponding numeric term in $result.
|
||||
# Values must match within 10%. Or if the $expected term is A..B then the
|
||||
# $result term must be in between A and B.
|
||||
#
|
||||
proc do_test {name cmd expected} {
|
||||
if {[info exists ::testprefix]} {
|
||||
set name "$::testprefix$name"
|
||||
}
|
||||
|
||||
incr ::NTEST
|
||||
puts -nonewline $name...
|
||||
flush stdout
|
||||
|
||||
if {[catch {uplevel #0 "$cmd;\n"} result]} {
|
||||
puts -nonewline $name...
|
||||
puts "\nError: $result"
|
||||
incr ::NERR
|
||||
} else {
|
||||
set ok [expr {[string compare $result $expected]==0}]
|
||||
if {!$ok} {
|
||||
puts "\n! $name expected: \[$expected\]\n! $name got: \[$result\]"
|
||||
incr ::NERR
|
||||
} else {
|
||||
puts " Ok"
|
||||
}
|
||||
}
|
||||
flush stdout
|
||||
}
|
||||
|
||||
#
|
||||
# do_execsql_test TESTNAME SQL RES
|
||||
#
|
||||
proc do_execsql_test {testname sql {result {}}} {
|
||||
uplevel [list do_test $testname [list db eval $sql] [list {*}$result]]
|
||||
}
|
||||
|
||||
if {[llength $argv]==0} {
|
||||
set dir [file dirname $argv0]
|
||||
set argv [glob -nocomplain $dir/*.test]
|
||||
}
|
||||
foreach testfile $argv {
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
source $testfile
|
||||
catch {db close}
|
||||
}
|
||||
puts "$NERR errors out of $NTEST tests"
|
||||
+1
-1
@@ -200,7 +200,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
|
||||
/* The sqlite3AtoF() routine is much much faster than atof(), if it
|
||||
** is available */
|
||||
double r;
|
||||
(void)sqlite3AtoF((const char*)p->z, &r);
|
||||
(void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8);
|
||||
*pVal = r;
|
||||
#else
|
||||
*pVal = (GeoCoord)atof((const char*)p->z);
|
||||
|
||||
+1
-1
@@ -3775,7 +3775,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
|
||||
if( node.zData==0 ) return;
|
||||
nData = sqlite3_value_bytes(apArg[1]);
|
||||
if( nData<4 ) return;
|
||||
if( nData<4+NCELL(&node)*tree.nBytesPerCell ) return;
|
||||
if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
|
||||
|
||||
pOut = sqlite3_str_new(0);
|
||||
for(ii=0; ii<NCELL(&node); ii++){
|
||||
|
||||
@@ -47,14 +47,4 @@ ifcapable rtree_int_only {
|
||||
|
||||
do_rtree_integrity_test rtreeB-1.2 t1
|
||||
|
||||
# https://sqlite.org/forum/forumpost/2026-01-08T23:32:19Z
|
||||
#
|
||||
db null NULL
|
||||
do_execsql_test rtreeB-2.1 {
|
||||
SELECT rtreenode(1,x'00000001'||randomblob(15)) IS NULL;
|
||||
} {1}
|
||||
do_execsql_test rtreeB-2.2 {
|
||||
SELECT rtreenode(1,x'00000001'||randomblob(16)) IS NOT NULL;
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -135,7 +135,6 @@ foreach {tn blob} {
|
||||
54 540101743400120003001200010000000000000002120002400C000000000002120002400C00000000000050040100000074310017FF0050040100000074310017FF7F00000000000000050100000000000000030100000003001700010000666F7572
|
||||
55 540101743400120003001200010000000000000002120002400C00000000000050040100000074310017000100010080000001000000020003010100000300170100000003001700010000666F7572
|
||||
56 5487ffffff7f
|
||||
57 54015052494e5446110017120004
|
||||
} {
|
||||
do_test 2.$tn {
|
||||
set changeset [binary decode hex $blob]
|
||||
|
||||
@@ -192,16 +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
|
||||
} {1 SQLITE_CORRUPT}
|
||||
grp delete
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -349,20 +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[5];
|
||||
const u8 *aRead = aBuf;
|
||||
if( nBuf<5 ){
|
||||
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])
|
||||
|
||||
@@ -657,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++;
|
||||
@@ -668,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);
|
||||
}
|
||||
@@ -3086,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 ){
|
||||
@@ -3575,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 ){
|
||||
@@ -3629,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
|
||||
@@ -3650,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;
|
||||
@@ -3790,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;
|
||||
|
||||
@@ -3823,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 */
|
||||
|
||||
+25
-49
@@ -7,8 +7,6 @@
|
||||
#include <string.h>
|
||||
#include "tclsqlite.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
typedef unsigned char u8;
|
||||
#endif
|
||||
@@ -858,21 +856,6 @@ static int testStreamInput(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This works like Tcl_GetByteArrayFromObj(), except that it returns a buffer
|
||||
** allocated using malloc() that must be freed by the caller. This is done
|
||||
** because Tcl's buffers are often padded by a few bytes, which prevents
|
||||
** small overreads from being detected when tests are run under asan.
|
||||
*/
|
||||
static void *testGetByteArrayFromObj(Tcl_Obj *p, Tcl_Size *pnByte){
|
||||
Tcl_Size nByte = 0;
|
||||
void *aByte = Tcl_GetByteArrayFromObj(p, &nByte);
|
||||
void *aCopy = malloc(nByte ? (size_t)nByte : 1);
|
||||
memcpy(aCopy, aByte, (size_t)nByte);
|
||||
*pnByte = nByte;
|
||||
return aCopy;
|
||||
}
|
||||
|
||||
|
||||
static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
int iVersion,
|
||||
@@ -937,7 +920,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
db = *(sqlite3 **)info.objClientData;
|
||||
pChangeset = (void *)testGetByteArrayFromObj(objv[2], &nChangeset);
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
|
||||
ctx.pConflictScript = objv[3];
|
||||
ctx.pFilterScript = objc==5 ? objv[4] : 0;
|
||||
ctx.interp = interp;
|
||||
@@ -989,7 +972,6 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
}
|
||||
}
|
||||
|
||||
free(pChangeset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
@@ -1213,12 +1195,7 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
pCS = objv[2];
|
||||
pScript = objv[3];
|
||||
|
||||
/* Take a copy of the changeset into an exact sized buffer allocated
|
||||
** using malloc(). The Tcl buffer will be padded by a few bytes, which
|
||||
** prevents small overreads from being detected by ASAN when the tests
|
||||
** are run. */
|
||||
pChangeset = (void*)testGetByteArrayFromObj(pCS, &nChangeset);
|
||||
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
|
||||
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
if( isInvert ){
|
||||
int f = SQLITE_CHANGESETSTART_INVERT;
|
||||
@@ -1239,33 +1216,32 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
|
||||
Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
|
||||
pVar = testIterData(pIter);
|
||||
Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
|
||||
sqlite3changeset_finalize(pIter);
|
||||
free(pChangeset);
|
||||
return rc==TCL_BREAK ? TCL_OK : rc;
|
||||
}
|
||||
}
|
||||
|
||||
if( isCheckNext ){
|
||||
int rc2 = sqlite3changeset_next(pIter);
|
||||
rc = sqlite3changeset_finalize(pIter);
|
||||
assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
|
||||
}else{
|
||||
rc = sqlite3changeset_finalize(pIter);
|
||||
}
|
||||
}
|
||||
|
||||
free(pChangeset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return test_session_error(interp, rc, 0);
|
||||
}
|
||||
|
||||
while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
|
||||
Tcl_Obj *pVar = 0; /* Tcl value to set $VARNAME to */
|
||||
pVar = testIterData(pIter);
|
||||
Tcl_ObjSetVar2(interp, pVarname, 0, pVar, 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc!=TCL_OK && rc!=TCL_CONTINUE ){
|
||||
sqlite3changeset_finalize(pIter);
|
||||
return rc==TCL_BREAK ? TCL_OK : rc;
|
||||
}
|
||||
}
|
||||
|
||||
if( isCheckNext ){
|
||||
int rc2 = sqlite3changeset_next(pIter);
|
||||
rc = sqlite3changeset_finalize(pIter);
|
||||
assert( (rc2==SQLITE_DONE && rc==SQLITE_OK) || rc2==rc );
|
||||
}else{
|
||||
rc = sqlite3changeset_finalize(pIter);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
return test_session_error(interp, rc, 0);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
+24
-124
@@ -86,7 +86,6 @@ emo.strip = 💈
|
||||
emo.test = 🧪
|
||||
emo.tool = 🔨
|
||||
emo.wasm-opt = 🧼
|
||||
emo.cleanup = 🧼
|
||||
# 👷🪄🧮🧫🧽🍿⛽🚧🎱🪚🏆🧼
|
||||
|
||||
#
|
||||
@@ -206,7 +205,7 @@ b.mkdir@ = if [ ! -d $(dir $@) ]; then \
|
||||
# $1 = logtag, $2 = src file(s). $3 = dest dir
|
||||
b.cp = $(call b.mkdir@); \
|
||||
echo '$(logtag.$(1)) $(emo.disk) $(2) ==> $3'; \
|
||||
cp -f -p $(2) $(3) || exit
|
||||
cp -p $(2) $(3) || exit
|
||||
|
||||
#
|
||||
# $(call b.c-pp.shcmd,LOGTAG,src,dest,-Dx=y...)
|
||||
@@ -318,7 +317,7 @@ endif
|
||||
#
|
||||
sqlite3.canonical.c = $(dir.top)/sqlite3.c
|
||||
sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c))
|
||||
sqlite3.h = $(dir $(sqlite3.c))sqlite3.h
|
||||
sqlite3.h = $(dir $(sqlite3.c))/sqlite3.h
|
||||
|
||||
#
|
||||
# bin.version-info = binary to output various sqlite3 version info for
|
||||
@@ -869,12 +868,13 @@ $(sqlite3-license-version.js): $(bin.version-info) \
|
||||
$(dir.api)/sqlite3-license-version-header.js $(MAKEFILE)
|
||||
@$(call b.mkdir@); echo '$(logtag.@) $(emo.disk)'; { \
|
||||
cat $(dir.api)/sqlite3-license-version-header.js || exit $$?; \
|
||||
echo '/* @preserve'; \
|
||||
echo '/*'; \
|
||||
echo '** This code was built from sqlite3 version...'; \
|
||||
echo '**'; \
|
||||
awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \
|
||||
awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \
|
||||
echo '**'; echo '** Emscripten SDK: $(emcc.version)'; \
|
||||
echo '**'; echo '** Preprocessed by:'; echo '** @c-pp::ARGV@'; \
|
||||
echo '*/'; \
|
||||
} > $@
|
||||
|
||||
@@ -997,21 +997,14 @@ endif
|
||||
# /shell.c
|
||||
########################################################################
|
||||
|
||||
#
|
||||
# Fiddle-related decls we need before .wasmbuilds is included
|
||||
#
|
||||
|
||||
fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c)
|
||||
|
||||
EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
|
||||
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) \
|
||||
$(MAKEFILE_LIST) $(bin.c-pp)
|
||||
@$(call b.mkdir@)
|
||||
@$(call b.c-pp.shcmd,fiddle,$(EXPORTED_FUNCTIONS.api.in),\
|
||||
@$(call b.c-pp.shcmd,filter,$(EXPORTED_FUNCTIONS.api.in),\
|
||||
$@,$(EXPORTED_FUNCTIONS.c-pp.flags) -Dfiddle)
|
||||
@echo $(logtag.@) $(emo.disk)
|
||||
|
||||
|
||||
emcc.flags.fiddle = \
|
||||
$(emcc.cflags) $(emcc_opt_full) \
|
||||
--minify 0 \
|
||||
@@ -1036,23 +1029,21 @@ emcc.flags.fiddle = \
|
||||
-USQLITE_WASM_BARE_BONES \
|
||||
-DSQLITE_SHELL_FIDDLE
|
||||
|
||||
clean: clean-fiddle
|
||||
clean-fiddle:
|
||||
rm -f $(dir.fiddle)/fiddle-module.js \
|
||||
$(dir.fiddle)/*.wasm \
|
||||
$(dir.fiddle)/sqlite3-opfs-*.js \
|
||||
$(dir.fiddle)/*.gz \
|
||||
$(EXPORTED_FUNCTIONS.fiddle)
|
||||
rm -fr $(dir.fiddle-debug)
|
||||
|
||||
emcc.flags.fiddle.debug = $(emcc.flags.fiddle) \
|
||||
-DSQLITE_DEBUG \
|
||||
-DSQLITE_ENABLE_SELECTTRACE \
|
||||
-DSQLITE_ENABLE_WHERETRACE
|
||||
|
||||
clean: clean-fiddle
|
||||
clean-fiddle:
|
||||
rm -f $(dir.fiddle)/fiddle-module.js \
|
||||
$(dir.fiddle)/*.wasm \
|
||||
$(dir.fiddle)/sqlite3-opfs-*.js \
|
||||
$(dir.fiddle)/*.gz \
|
||||
$(dir.fiddle)/index.html \
|
||||
$(EXPORTED_FUNCTIONS.fiddle)
|
||||
rm -fr $(dir.fiddle-debug)
|
||||
distclean: distclean-fiddle
|
||||
distclean-fiddle:
|
||||
rm -fr $(dir.fiddle)/jqterm
|
||||
fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c)
|
||||
|
||||
#
|
||||
# WASMFS build - unsupported and untested. We used WASMFS
|
||||
@@ -1221,7 +1212,7 @@ $(foreach B,$(b.names),$(eval post: $(post-js.$(B).js)))
|
||||
# These flags get applied via $(bin.mkwb).
|
||||
emcc.speedtest1.common = $(emcc_opt_full)
|
||||
emcc.speedtest1 = -I. -I$(dir $(sqlite3.canonical.c))
|
||||
emcc.speedtest1 += -sENVIRONMENT=web,worker
|
||||
emcc.speedtest1 += -sENVIRONMENT=web
|
||||
emcc.speedtest1 += -sALLOW_MEMORY_GROWTH
|
||||
emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.32)
|
||||
emcc.speedtest1.common += -sINVOKE_RUN=0
|
||||
@@ -1264,33 +1255,23 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api)
|
||||
@$(call b.mkdir@); $(call b.echo,@,$(emo.disk)); \
|
||||
{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api); } > $@ || exit
|
||||
speedtest1: b-speedtest1
|
||||
st: speedtest1
|
||||
|
||||
#
|
||||
# Generate 64-bit variants of speedtest1*.{js,html}
|
||||
#
|
||||
# $1 = input file
|
||||
# $2 = output file
|
||||
#
|
||||
# TODO: preprocess these like we do the rest.
|
||||
#
|
||||
define gen-st64
|
||||
$(2): $(1)
|
||||
@$$(call b.echo,speedtest164,$$(emo.disk)$(emo.lock) Creating from $$<)
|
||||
rm -f $$@; \
|
||||
sed -e 's/speedtest1\.js/speedtest1-64bit\.js/' \
|
||||
-e 's/speedtest1-worker\.js/speedtest1-worker-64bit\.js/' \
|
||||
< $$< > $$@; \
|
||||
@rm -f $$@; \
|
||||
sed -e 's/$(3)\.js/$(3)-64bit\.js/' < $$< > $$@; \
|
||||
chmod -w $$@
|
||||
$(2): b-speedtest164
|
||||
speedtest1: $(1) $(2)
|
||||
b-speedtest164: $(2)
|
||||
CLEAN_FILES += $(2)
|
||||
endef
|
||||
speedtest1: b-speedtest164
|
||||
|
||||
$(eval $(call gen-st64,speedtest1.html,speedtest1-64bit.html))
|
||||
$(eval $(call gen-st64,speedtest1-worker.html,speedtest1-worker-64bit.html))
|
||||
$(eval $(call gen-st64,speedtest1-worker.js,speedtest1-worker-64bit.js))
|
||||
$(eval $(call gen-st64,speedtest1.html,speedtest1-64bit.html,speedtest1))
|
||||
$(eval $(call gen-st64,speedtest1-worker.html,speedtest1-worker-64bit.html,speedtest1-worker))
|
||||
$(eval $(call gen-st64,speedtest1-worker.js,speedtest1-worker-64bit.js,speedtest1-worker))
|
||||
# end speedtest1.js
|
||||
########################################################################
|
||||
|
||||
@@ -1391,54 +1372,6 @@ all: tester1
|
||||
# end tester1
|
||||
#
|
||||
|
||||
#
|
||||
# jquery.terminal support for fiddle:
|
||||
#
|
||||
# If a clone of https://github.com/jcubic/jquery.terminal
|
||||
# is found in $(JQTERM), defaulting to $(HOME)/src/jquery.terminal
|
||||
# then add jquery.terminal support to fiddle.
|
||||
#
|
||||
# To build that package, from its checkout dir:
|
||||
#
|
||||
# npm install
|
||||
# make
|
||||
#
|
||||
c-pp.D.fiddle ?=
|
||||
JQTERM ?= $(HOME)/src/jquery.terminal
|
||||
dir.jqtermExt = $(firstword $(wildcard $(JQTERM)))
|
||||
#$(info dir.jqtermExt=$(dir.jqtermExt))
|
||||
ifeq (0,$(MAKING_CLEAN))
|
||||
ifeq (,$(wildcard $(dir.jqtermExt)/js/jquery.terminal.min.js))
|
||||
$(info $(emo.magic) To add jquery.terminal support to fiddle, set JQTERM=/path/to/its/built/checkout)
|
||||
else
|
||||
$(info $(emo.magic) jquery.terminal found in $(dir.jqtermExt) - adding it to fiddle. Make sure it is built!)
|
||||
|
||||
dir.jqterm = $(dir.fiddle)/jqterm
|
||||
$(dir.fiddle)/jqterm/jquery.terminal.bundle.min.js:
|
||||
@$(call b.mkdir@)
|
||||
cat $(dir.jqtermExt)/js/jquery-1*.min.js \
|
||||
$(dir.jqtermExt)/js/jquery.terminal.min.js > $@
|
||||
|
||||
$(dir.fiddle)/jqterm/jquery.terminal.min.css: $(dir.jqtermExt)/css/jquery.terminal.min.css
|
||||
@$(call b.mkdir@)
|
||||
@$(call b.cp,fiddle,$<,$(dir $@))
|
||||
|
||||
$(dir.fiddle)/index.html: $(dir.fiddle)/jqterm/jquery.terminal.bundle.min.js \
|
||||
$(dir.fiddle)/jqterm/jquery.terminal.min.css
|
||||
c-pp.D.fiddle += -Djqterm
|
||||
endif
|
||||
endif
|
||||
# ^^^ JQTERM/MAKING_CLEAN
|
||||
|
||||
#
|
||||
# Generate fiddle/index.html. Must come after JQTERM is handled.
|
||||
#
|
||||
$(dir.fiddle)/index.html: $(dir.fiddle)/index.c-pp.html
|
||||
$(eval $(call b.c-pp.target,fiddle,\
|
||||
$(dir.fiddle)/index.c-pp.html,$(dir.fiddle)/index.html,$(c-pp.D.fiddle)))
|
||||
$(out.fiddle.wasm): $(dir.fiddle)/index.html
|
||||
|
||||
|
||||
#
|
||||
# Convenience rules to rebuild with various -Ox levels. Much
|
||||
# experimentation shows -O2 to be the clear winner in terms of speed.
|
||||
@@ -1528,9 +1461,9 @@ update-docs:
|
||||
exit 127
|
||||
else
|
||||
wasm.docs.jswasm = $(wasm.docs.home)/jswasm
|
||||
update-docs: $(bin.stripccomments) $(out.vanilla.js) $(out.vanilla.wasm)
|
||||
update-docs: $(bin.stripccomments) $(out.sqlite3.js) $(out.sqlite3.wasm)
|
||||
@echo "Copying files to the /wasm docs. Be sure to use an -Oz build for this!";
|
||||
cp -p $(out.vanilla.wasm) $(wasm.docs.jswasm)/.
|
||||
cp -p $(sqlite3.wasm) $(wasm.docs.jswasm)/.
|
||||
$(bin.stripccomments) -k -k < $(out.vanilla.js) \
|
||||
| sed -e '/^[ \t]*$$/d' > $(wasm.docs.jswasm)/sqlite3.js
|
||||
cp -p demo-123.js demo-123.html demo-123-worker.html $(wasm.docs.home)/.
|
||||
@@ -1593,39 +1526,6 @@ endif
|
||||
# ^^^ making dist/snapshot
|
||||
CLEAN_FILES += $(wildcard sqlite-wasm-*.zip)
|
||||
|
||||
########################################################################
|
||||
# The npm target is specifically for preparing files for the downstream
|
||||
# https://github.com/sqlite/sqlite-wasm (npm) distribution.
|
||||
#
|
||||
# Per agreement with that project's maintainers, these filenames need
|
||||
# to remain stable. To avoid breakage in their deployment process, any
|
||||
# changes (like renaming files) which potentially break their deployment
|
||||
# needs to be communicated to that project via opening a new ticket or
|
||||
# direct coordination with its maintainers.
|
||||
#
|
||||
# This target does a full clean/rebuild so that we can ensure that the
|
||||
# optimization level is set consistently across all files.
|
||||
#
|
||||
npm.bundle.zip = npm-bundle.zip
|
||||
CLEAN_FILES += $(npm.bundle.zip)
|
||||
# Distributables which need to be built for npm:
|
||||
npm_files = $(addprefix $(dir.dout)/, \
|
||||
sqlite3-bundler-friendly.mjs \
|
||||
sqlite3-opfs-async-proxy.js \
|
||||
sqlite3-worker1-bundler-friendly.mjs \
|
||||
sqlite3-worker1-promiser.mjs \
|
||||
sqlite3-worker1.mjs \
|
||||
sqlite3.mjs \
|
||||
sqlite3.wasm \
|
||||
sqlite3-node.mjs \
|
||||
)
|
||||
npm: $(sqlite3.canonical.c)
|
||||
@echo "$(emo.cleanup) Forcing a clean rebuild to ensure consistent optimization flags."
|
||||
$(MAKE) clean
|
||||
$(MAKE) -e "emcc_opt=-Oz $(emcc-opt-extra)" $(npm_files)
|
||||
rm -f $(npm.bundle.zip); zip -r $(npm.bundle.zip) $(npm_files)
|
||||
unzip -l $(npm.bundle.zip)
|
||||
|
||||
########################################################################
|
||||
# Explanation of, and some commentary on, various emcc build flags
|
||||
# follows. Full docs for these can be found at:
|
||||
|
||||
@@ -62,7 +62,7 @@ by concatenating the following files together in their listed order:
|
||||
|
||||
- **`sqlite3-api-prologue.js`**
|
||||
Contains the initial bootstrap setup of the sqlite3 API
|
||||
objects. This is exposed as a bootstrapping function so that
|
||||
objects. This is exposed as a function, rather than objects, so that
|
||||
the next step can pass in a config object which abstracts away parts
|
||||
of the WASM environment, to facilitate plugging it in to arbitrary
|
||||
WASM toolchains. The bootstrapping function gets removed from the
|
||||
@@ -116,10 +116,10 @@ by concatenating the following files together in their listed order:
|
||||
a Promise-based interface into the Worker #1 API. This is
|
||||
a far user-friendlier way to interface with databases running
|
||||
in a Worker thread.
|
||||
- **`sqlite3-vfs-helper.c-pp.js`**
|
||||
- **`sqlite3-vfs-helper.js`**
|
||||
Installs the `sqlite3.vfs` namespace, which contain helpers for use
|
||||
by downstream code which creates `sqlite3_vfs` implementations.
|
||||
- **`sqlite3-vtab-helper.c-pp.js`**
|
||||
- **`sqlite3-vtab-helper.js`**
|
||||
Installs the `sqlite3.vtab` namespace, which contain helpers for use
|
||||
by downstream code which creates `sqlite3_module` implementations.
|
||||
- **`sqlite3-vfs-opfs.c-pp.js`**
|
||||
|
||||
@@ -73,8 +73,6 @@ const toExportForESM =
|
||||
|
||||
const sIM = globalThis.sqlite3InitModule = function ff(...args){
|
||||
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
|
||||
sIMS.emscriptenLocateFile = args[0]?.locateFile /* see pre-js.c-pp.js [tag:locateFile] */;
|
||||
sIMS.emscriptenInstantiateWasm = args[0]?.instantiateWasm /* see pre-js.c-pp.js [tag:locateFile] */;
|
||||
return originalInit(...args).then((EmscriptenModule)=>{
|
||||
sIMS.debugModule("sqlite3InitModule() sIMS =",sIMS);
|
||||
sIMS.debugModule("sqlite3InitModule() EmscriptenModule =",EmscriptenModule);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
This file was preprocessed using:
|
||||
|
||||
//#@policy error
|
||||
@c-pp::argv@
|
||||
@c-pp::ARGV@
|
||||
//#@policy off
|
||||
*/
|
||||
//#if unsupported-build
|
||||
@@ -27,13 +27,10 @@
|
||||
|
||||
This SQLite JS build configuration is entirely unsupported! It has
|
||||
not been tested beyond the ability to compile it. It may not
|
||||
load. It may not work properly. Only builds _directly_ targeting
|
||||
browser environments ("vanilla" JS and ESM modules) are supported
|
||||
and tested. Builds which _indirectly_ target browsers (namely
|
||||
bundler-friendly builds) are not supported deliverables.
|
||||
load. It may not work properly. Only builds targeting browser
|
||||
environments are supported and tested.
|
||||
*/
|
||||
//#endif
|
||||
//#if not target:es6-bundler-friendly
|
||||
(function(Module){
|
||||
const sIMS =
|
||||
globalThis.sqlite3InitModuleState/*from extern-post-js.c-pp.js*/
|
||||
@@ -71,14 +68,6 @@
|
||||
approach.
|
||||
*/
|
||||
Module['locateFile'] = function(path, prefix) {
|
||||
if( this.emscriptenLocateFile instanceof Function ){
|
||||
/* [tag:locateFile] Client-overridden impl. We do not support
|
||||
this but offer it as a back-door which will go away the
|
||||
moment either Emscripten changes that interface or we manage
|
||||
to get non-Emscripten builds working.
|
||||
https://sqlite.org/forum/forumpost/1eec339854c935bd */
|
||||
return this.emscriptenLocateFile(path, prefix);
|
||||
}
|
||||
//#if target:es6-module
|
||||
return new URL(path, import.meta.url).href;
|
||||
//#else
|
||||
@@ -104,7 +93,7 @@
|
||||
//#endif target:es6-module
|
||||
}.bind(sIMS);
|
||||
|
||||
//#if Module.instantiateWasm and not wasmfs and not target:node
|
||||
//#if Module.instantiateWasm and not wasmfs
|
||||
/**
|
||||
Override Module.instantiateWasm().
|
||||
|
||||
@@ -113,15 +102,8 @@
|
||||
https://github.com/emscripten-core/emscripten/issues/17951
|
||||
|
||||
In such builds we must disable this.
|
||||
|
||||
It's disabled in the (unsupported/untested) node builds because
|
||||
node does not do fetch().
|
||||
*/
|
||||
Module['instantiateWasm'] = function callee(imports,onSuccess){
|
||||
if( this.emscriptenInstantiateWasm instanceof Function ){
|
||||
/* See [tag:locateFile]. Same story here */
|
||||
return this.emscriptenInstantiateWasm(imports, onSuccess);
|
||||
}
|
||||
const sims = this;
|
||||
const uri = Module.locateFile(
|
||||
sims.wasmFilename, (
|
||||
@@ -149,5 +131,4 @@
|
||||
}.bind(sIMS);
|
||||
//#endif Module.instantiateWasm and not wasmfs
|
||||
})(Module);
|
||||
//#endif not target:es6-bundler-friendly
|
||||
/* END FILE: api/pre-js.js. */
|
||||
|
||||
@@ -26,20 +26,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
the sqlite3 binding if, e.g., the wrapper is in the main thread
|
||||
and the sqlite3 API is in a worker. */
|
||||
|
||||
const outWrapper = function(f){
|
||||
return (...args)=>f("sqlite3.oo1:",...args);
|
||||
};
|
||||
|
||||
const debug = sqlite3.__isUnderTest
|
||||
? outWrapper(console.debug.bind(console))
|
||||
: outWrapper(sqlite3.config.debug);
|
||||
const warn = sqlite3.__isUnderTest
|
||||
? outWrapper(console.warn.bind(console))
|
||||
: outWrapper(sqlite3.config.warn);
|
||||
const error = sqlite3.__isUnderTest
|
||||
? outWrapper(console.error.bind(console))
|
||||
: outWrapper(sqlite3.config.error);
|
||||
|
||||
/**
|
||||
In order to keep clients from manipulating, perhaps
|
||||
inadvertently, the underlying pointer values of DB and Stmt
|
||||
@@ -102,8 +88,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
wasm.installFunction('i(ippp)', function(t,c,p,x){
|
||||
if(capi.SQLITE_TRACE_STMT===t){
|
||||
// x == SQL, p == sqlite3_stmt*
|
||||
console.log("SQL TRACE #"+(++this.counter),
|
||||
'via sqlite3@'+c+'['+capi.sqlite3_db_filename(c,null)+']',
|
||||
console.log("SQL TRACE #"+(++this.counter)+' via sqlite3@'+c+':',
|
||||
wasm.cstrToJs(x));
|
||||
}
|
||||
}.bind({counter: 0}));
|
||||
@@ -228,18 +213,41 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
properties:
|
||||
|
||||
- `.filename`: the db filename. It may be a special name like ":memory:"
|
||||
or "". It may also be a URI-style name.
|
||||
or "".
|
||||
|
||||
- `.flags`: as documented in the DB constructor.
|
||||
|
||||
- `.vfs`: as documented in the DB constructor.
|
||||
|
||||
It also accepts those as the first 3 arguments.
|
||||
|
||||
In non-default builds it may accept additional configuration
|
||||
options.
|
||||
*/
|
||||
const dbCtorHelper = function ctor(...args){
|
||||
if(!ctor._name2vfs){
|
||||
/**
|
||||
Map special filenames which we handle here (instead of in C)
|
||||
to some helpful metadata...
|
||||
|
||||
As of 2022-09-20, the C API supports the names :localStorage:
|
||||
and :sessionStorage: for kvvfs. However, C code cannot
|
||||
determine (without embedded JS code, e.g. via Emscripten's
|
||||
EM_JS()) whether the kvvfs is legal in the current browser
|
||||
context (namely the main UI thread). In order to help client
|
||||
code fail early on, instead of it being delayed until they
|
||||
try to read or write a kvvfs-backed db, we'll check for those
|
||||
names here and throw if they're not legal in the current
|
||||
context.
|
||||
*/
|
||||
ctor._name2vfs = Object.create(null);
|
||||
const isWorkerThread = ('function'===typeof importScripts/*===running in worker thread*/)
|
||||
? (n)=>toss3("VFS",n,"is only available in the main window thread.")
|
||||
: false;
|
||||
ctor._name2vfs[':localStorage:'] = {
|
||||
vfs: 'kvvfs', filename: isWorkerThread || (()=>'local')
|
||||
};
|
||||
ctor._name2vfs[':sessionStorage:'] = {
|
||||
vfs: 'kvvfs', filename: isWorkerThread || (()=>'session')
|
||||
};
|
||||
}
|
||||
const opt = ctor.normalizeArgs(...args);
|
||||
//sqlite3.config.debug("DB ctor",opt);
|
||||
let pDb;
|
||||
@@ -261,6 +269,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
sqlite3.config.error("Invalid DB ctor args",opt,arguments);
|
||||
toss3("Invalid arguments for DB constructor:", arguments, "opts:", opt);
|
||||
}
|
||||
let fnJs = wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn;
|
||||
const vfsCheck = ctor._name2vfs[fnJs];
|
||||
if(vfsCheck){
|
||||
vfsName = vfsCheck.vfs;
|
||||
fn = fnJs = vfsCheck.filename(fnJs);
|
||||
}
|
||||
let oflags = 0;
|
||||
if( flagsStr.indexOf('c')>=0 ){
|
||||
oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
|
||||
@@ -285,15 +299,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}finally{
|
||||
wasm.pstack.restore(stack);
|
||||
}
|
||||
this.filename =
|
||||
/* A poor design choice we have to keep: this.filename may be
|
||||
in the form "file:....?....". It really should have been
|
||||
sqlite3_db_filename(pDb) but that discrepancy went too long
|
||||
unnoticed to be able to change without risk of
|
||||
breakage. DB.dbFilename() can be used to fetch _just_ the
|
||||
name part.
|
||||
*/ wasm.isPtr(fn) ? wasm.cstrToJs(fn) : fn;
|
||||
|
||||
this.filename = fnJs;
|
||||
}
|
||||
__ptrMap.set(this, pDb);
|
||||
__stmtMap.set(this, Object.create(null));
|
||||
@@ -384,12 +390,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
The given db filename must be resolvable using whatever
|
||||
filesystem layer (virtual or otherwise) is set up for the default
|
||||
sqlite3 VFS or a VFS which can resolve it must be specified.
|
||||
sqlite3 VFS.
|
||||
|
||||
The special sqlite3 db names ":memory:" and "" (temporary db)
|
||||
have their normal special meanings here and need not resolve to
|
||||
real filenames, but "" uses an on-storage temporary database and
|
||||
requires that the VFS support that.
|
||||
Note that the special sqlite3 db names ":memory:" and ""
|
||||
(temporary db) have their normal special meanings here and need
|
||||
not resolve to real filenames, but "" uses an on-storage
|
||||
temporary database and requires that the VFS support that.
|
||||
|
||||
The second argument specifies the open/create mode for the
|
||||
database. It must be string containing a sequence of letters (in
|
||||
@@ -802,9 +808,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
sqlite3_db_filename() value for the given database name,
|
||||
defaulting to "main". The argument may be either a JS string
|
||||
or a pointer to a WASM-allocated C-string.
|
||||
|
||||
this.filename may be in the form of a URI-style string, whereas
|
||||
the returned string contains only the filename part.
|
||||
*/
|
||||
dbFilename: function(dbName='main'){
|
||||
return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName);
|
||||
@@ -926,15 +929,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
result set, but only if that statement has any result rows. The
|
||||
callback's "this" is the options object, noting that this
|
||||
function synthesizes one if the caller does not pass one to
|
||||
exec(). The first argument passed to the callback is described
|
||||
below. The second argument is always the current Stmt object,
|
||||
as it's needed if the caller wants to fetch the column names or
|
||||
some such (noting that they could also be fetched via
|
||||
`this.columnNames`, if the client provides the `columnNames`
|
||||
option). If the callback returns a literal `false` (as opposed
|
||||
to any other falsy value, e.g. an implicit `undefined` return),
|
||||
any ongoing statement-`step()` iteration stops without an
|
||||
error. The return value of the callback is otherwise ignored.
|
||||
exec(). The second argument passed to the callback is always
|
||||
the current Stmt object, as it's needed if the caller wants to
|
||||
fetch the column names or some such (noting that they could
|
||||
also be fetched via `this.columnNames`, if the client provides
|
||||
the `columnNames` option). If the callback returns a literal
|
||||
`false` (as opposed to any other falsy value, e.g. an implicit
|
||||
`undefined` return), any ongoing statement-`step()` iteration
|
||||
stops without an error. The return value of the callback is
|
||||
otherwise ignored.
|
||||
|
||||
ACHTUNG: The callback MUST NOT modify the Stmt object. Calling
|
||||
any of the Stmt.get() variants, Stmt.getColumnName(), or
|
||||
@@ -967,23 +970,20 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
A.3) `'stmt'` causes the current Stmt to be passed to the
|
||||
callback, but this mode will trigger an exception if
|
||||
`resultRows` is an array because appending the transient
|
||||
statement to the array would be downright unhelpful. This
|
||||
option is a legacy feature, retained for backwards
|
||||
compatibility. The statement object is passed as the second
|
||||
argument to the callback, as described above.
|
||||
statement to the array would be downright unhelpful.
|
||||
|
||||
B) An integer, indicating a zero-based column in the result
|
||||
row. Only that one single value, in JS form, will be passed on.
|
||||
row. Only that one single value will be passed on.
|
||||
|
||||
C) A string with a minimum length of 2 and leading character of
|
||||
'$' will fetch the row as an object, extract that one field,
|
||||
and pass that field's value to the callback. These keys are
|
||||
case-sensitive so must match the case used in the
|
||||
and pass that field's value to the callback. Note that these
|
||||
keys are case-sensitive so must match the case used in the
|
||||
SQL. e.g. `"select a A from t"` with a `rowMode` of `'$A'`
|
||||
would work but `'$a'` would not. A reference to a column not in
|
||||
the result set will trigger an exception on the first row (as
|
||||
the check is not performed until rows are fetched). Note that
|
||||
`$` is a legal identifier character in JS so need not be
|
||||
the check is not performed until rows are fetched). Note also
|
||||
that `$` is a legal identifier character in JS so need not be
|
||||
quoted.
|
||||
|
||||
Any other `rowMode` value triggers an exception.
|
||||
@@ -1023,7 +1023,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
- `callback` and `resultRows`: permit an array entries with
|
||||
semantics similar to those described for `bind` above.
|
||||
|
||||
OTOH, this function already does too much.
|
||||
*/
|
||||
exec: function(/*(sql [,obj]) || (obj)*/){
|
||||
affirmDbOpen(this);
|
||||
@@ -1047,7 +1046,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
/* Optimization: if the SQL is a TypedArray we can save some string
|
||||
conversion costs. */;
|
||||
/* Allocate the two output pointers (ppStmt, pzTail) and heap
|
||||
space for the SQL (pSql). When prepare_v3() returns, pzTail
|
||||
space for the SQL (pSql). When prepare_v2() returns, pzTail
|
||||
will point to somewhere in pSql. */
|
||||
let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql);
|
||||
const ppStmt = wasm.scopedAlloc(
|
||||
@@ -1059,8 +1058,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen);
|
||||
if(isTA) wasm.heap8().set(arg.sql, pSql);
|
||||
else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
|
||||
wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0/*NUL terminator*/);
|
||||
while(pSql && wasm.peek8(pSql)
|
||||
wasm.poke(wasm.ptr.add(pSql, sqlByteLen), 0/*NUL terminator*/);
|
||||
while(pSql && wasm.peek(pSql, 'i8')
|
||||
/* Maintenance reminder:^^^ _must_ be 'i8' or else we
|
||||
will very likely cause an endless loop. What that's
|
||||
doing is checking for a terminating NUL byte. If we
|
||||
@@ -1124,7 +1123,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
/* In order to trigger an exception in the
|
||||
INSERT...RETURNING locking scenario:
|
||||
https://sqlite.org/forum/forumpost/36f7a2e7494897df
|
||||
[tag:insert-returning-reset]
|
||||
*/).finalize();
|
||||
stmt = null;
|
||||
}/*prepare() loop*/
|
||||
@@ -1132,141 +1130,15 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
sqlite3.config.warn("DB.exec() is propagating exception",opt,e);
|
||||
throw e;
|
||||
}*/finally{
|
||||
wasm.scopedAllocPop(stack);
|
||||
if(stmt){
|
||||
__execLock.delete(stmt);
|
||||
stmt.finalize();
|
||||
}
|
||||
wasm.scopedAllocPop(stack);
|
||||
}
|
||||
return arg.returnVal();
|
||||
}/*exec()*/,
|
||||
|
||||
//#if nope
|
||||
/**
|
||||
Experimental and untested - do not use.
|
||||
|
||||
Prepares one or more SQL statements, passing each to a callback
|
||||
for processing.
|
||||
|
||||
It requires an options object with the following properties:
|
||||
|
||||
- "sql": SQL in any format accepted by exec().
|
||||
|
||||
- "callback" (function): gets passed each prepared statement,
|
||||
as described below.
|
||||
|
||||
- "asPointer" (bool=false): if true, the callback is passed the
|
||||
WASM (sqlite3*) pointer instead of a Stmt object.
|
||||
|
||||
- "saveSql" (array): if set, the SQL of each prepared statement
|
||||
is appended to this array. This can be used without a callback
|
||||
to split SQL into its component statements. Purely empty
|
||||
statements (for for which sqlite3_prepare() returns a NULL
|
||||
sqlite3_stmt, i.e. spaces and comments) are not added to this
|
||||
list unless...
|
||||
|
||||
- "saveEmpty" (bool=false): If true, empty statements are
|
||||
retained in opt.saveSql, but their leading/trailing whitespace
|
||||
is trimmed (as for queries) so they may be empty.
|
||||
|
||||
For each statement in the input SQL:
|
||||
|
||||
1) If opt.saveSql is set, the SQL is appended to it.
|
||||
|
||||
2) If callback is set, callback(S) is called, where S is either
|
||||
a Stmt object (by default) or an (sqlite3*) WASM pointer (if
|
||||
opt.asPointer is true). If the callback returns a literal true
|
||||
(as opposed to any other truthy value), ownership of S is
|
||||
transferred to the callback, otherwise S is reset and finalized
|
||||
as soon as the callback returns. If the callback throws, S is
|
||||
unconditionally finalized.
|
||||
|
||||
If neither of opt.saveSql nor opt.callback are set, this
|
||||
function does nothing more than prepare and finalize each
|
||||
statement, which will trigger an exception if any of them
|
||||
contain invalid SQL.
|
||||
*/
|
||||
forEachStmt: function(opt){
|
||||
affirmDbOpen(this);
|
||||
opt ??= Object.create(null);
|
||||
if(!opt.sql){
|
||||
return toss3("exec() requires an SQL string.");
|
||||
}
|
||||
const sql = util.flexibleString(opt.sql);
|
||||
const callback = opt.callback;
|
||||
let stmt, pStmt;
|
||||
const stack = wasm.scopedAllocPush();
|
||||
const saveSql = Array.isArray(opt.saveSql) ? opt.saveSql : undefined;
|
||||
try{
|
||||
const isTA = util.isSQLableTypedArray(opt.sql)
|
||||
/* Optimization: if the SQL is a TypedArray we can save some string
|
||||
conversion costs. */;
|
||||
/* Allocate the two output pointers (ppStmt, pzTail) and heap
|
||||
space for the SQL (pSql). When prepare_v3() returns, pzTail
|
||||
will point to somewhere in pSql. */
|
||||
let sqlByteLen = isTA ? opt.sql.byteLength : wasm.jstrlen(sql);
|
||||
const ppStmt = wasm.scopedAlloc(
|
||||
/* output (sqlite3_stmt**) arg and pzTail */
|
||||
(2 * wasm.ptr.size) + (sqlByteLen + 1/* SQL + NUL */)
|
||||
);
|
||||
const pzTail = wasm.ptr.add(ppStmt, wasm.ptr.size) /* final arg to sqlite3_prepare_v2() */;
|
||||
let pSql = wasm.ptr.add(pzTail, wasm.ptr.size) /* start of the SQL string */;
|
||||
const pSqlEnd = wasm.ptr.add(pSql, sqlByteLen);
|
||||
if(isTA) wasm.heap8().set(sql, pSql);
|
||||
else wasm.jstrcpy(sql, wasm.heap8(), pSql, sqlByteLen, false);
|
||||
wasm.poke8(wasm.ptr.add(pSql, sqlByteLen), 0/*NUL terminator*/);
|
||||
while( pSql && wasm.peek8(pSql) ){
|
||||
pStmt = stmt = null;
|
||||
wasm.pokePtr([ppStmt, pzTail], 0);
|
||||
const zHead = pSql;
|
||||
DB.checkRc(this, capi.sqlite3_prepare_v3(
|
||||
this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail
|
||||
));
|
||||
[pStmt, pSql] = wasm.peekPtr([ppStmt, pzTail]);
|
||||
sqlByteLen = wasm.ptr.addn(pSqlEnd,-pSql);
|
||||
if(opt.saveSql){
|
||||
if( pStmt ) opt.saveSql.push(capi.sqlite3_sql(pStmt).trim());
|
||||
else if( opt.saveEmpty ){
|
||||
saveSql.push(wasm.typedArrayToString(
|
||||
wasm.heap8u(), Number(zHead),
|
||||
wasm.ptr.addn(zHead, sqlByteLen)
|
||||
).trim(/*arguable*/));
|
||||
}
|
||||
}
|
||||
if(!pStmt) continue;
|
||||
//sqlite3.config.debug("forEachStmt() pSql =",capi.sqlite3_sql(pStmt));
|
||||
if( !opt.callback ){
|
||||
capi.sqlite3_finalize(pStmt);
|
||||
pStmt = null;
|
||||
continue;
|
||||
}
|
||||
stmt = opt.asPointer ? null : new Stmt(this, pStmt, BindTypes);
|
||||
if( true===callaback(stmt || pStmt) ){
|
||||
stmt = pStmt = null /*callback took ownership */;
|
||||
}else if(stmt){
|
||||
pStmt = null;
|
||||
stmt.reset(
|
||||
/* See [tag:insert-returning-reset]. The thinking here is
|
||||
that if the callback didn't throw for this, it
|
||||
probably should have.
|
||||
*/).finalize();
|
||||
stmt = null;
|
||||
}else{
|
||||
const rx = capi.sqlite3_reset(pStmt/*[tag:insert-returning-reset]*/);
|
||||
capi.sqlite3_finalize(pStmt);
|
||||
pStmt = null;
|
||||
DB.checkRc(this, rx);
|
||||
}
|
||||
}/*prepare() loop*/
|
||||
}finally{
|
||||
if(stmt) stmt.finalize();
|
||||
else if(pStmt) capi.sqlite3_finalize(pStmt);
|
||||
wasm.scopedAllocPop(stack);
|
||||
}
|
||||
return this;
|
||||
}/*forEachStmt()*/,
|
||||
//#endif nope
|
||||
|
||||
/**
|
||||
Creates a new UDF (User-Defined Function) which is accessible
|
||||
via SQL code. This function may be called in any of the
|
||||
|
||||
@@ -752,11 +752,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|
||||
toss: function(...args){throw new Error(args.join(' '))},
|
||||
toss3,
|
||||
typedArrayPart: wasm.typedArrayPart,
|
||||
assert: function(arg,msg){
|
||||
if( !arg ){
|
||||
util.toss("Assertion failed:",msg);
|
||||
}
|
||||
},
|
||||
/**
|
||||
Given a byte array or ArrayBuffer, this function throws if the
|
||||
lead bytes of that buffer do not hold a SQLite3 database header,
|
||||
@@ -892,7 +887,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|
||||
|
||||
Like this.alloc.impl(), this.realloc.impl() is a direct binding
|
||||
to the underlying realloc() implementation which does not throw
|
||||
exceptions, instead returning 0 (or 0n) on allocation error.
|
||||
exceptions, instead returning 0 on allocation error.
|
||||
*/
|
||||
realloc: undefined/*installed later*/,
|
||||
|
||||
@@ -1413,7 +1408,7 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|
||||
or not provided, then "main" is assumed.
|
||||
*/
|
||||
capi.sqlite3_js_db_vfs =
|
||||
(dbPointer, dbName=wasm.ptr.null)=>util.sqlite3__wasm_db_vfs(dbPointer, dbName);
|
||||
(dbPointer, dbName=0)=>util.sqlite3__wasm_db_vfs(dbPointer, dbName);
|
||||
|
||||
/**
|
||||
A thin wrapper around capi.sqlite3_aggregate_context() which
|
||||
@@ -2034,9 +2029,6 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|
||||
*/
|
||||
scriptInfo: undefined
|
||||
};
|
||||
if( ('undefined'!==typeof sqlite3IsUnderTest/* from post-js-header.js */) ){
|
||||
sqlite3.__isUnderTest = !!sqlite3IsUnderTest;
|
||||
}
|
||||
try{
|
||||
sqlite3ApiBootstrap.initializers.forEach((f)=>{
|
||||
f(sqlite3);
|
||||
@@ -2059,6 +2051,9 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|
||||
builds, this would need to be injected in somewhere to get
|
||||
that VFS loading. */ = sqlite3InitScriptInfo;
|
||||
}
|
||||
sqlite3.__isUnderTest =
|
||||
('undefined'===typeof sqlite3IsUnderTest /* from post-js-header.js */)
|
||||
? false : !!sqlite3IsUnderTest;
|
||||
if( sqlite3.__isUnderTest ){
|
||||
if( 'undefined'!==typeof EmscriptenModule ){
|
||||
sqlite3.config.emscripten = EmscriptenModule;
|
||||
@@ -2095,11 +2090,9 @@ globalThis.sqlite3ApiBootstrap = async function sqlite3ApiBootstrap(
|
||||
delete globalThis.sqlite3ApiBootstrap;
|
||||
delete sqlite3ApiBootstrap.defaultConfig;
|
||||
return sqlite3.asyncPostInit().then((s)=>{
|
||||
if( 'undefined'!==typeof sqlite3InitScriptInfo/* from post-js-header.js */ ){
|
||||
sqlite3InitScriptInfo.debugModule(
|
||||
"sqlite3.asyncPostInit() complete", s
|
||||
);
|
||||
}
|
||||
sqlite3InitScriptInfo.debugModule(
|
||||
"sqlite3.asyncPostInit() complete", sqlite3
|
||||
);
|
||||
delete s.asyncPostInit;
|
||||
delete s.scriptInfo;
|
||||
delete s.emscripten;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* @preserve
|
||||
**
|
||||
/*
|
||||
** LICENSE for the sqlite3 WebAssembly/JavaScript APIs.
|
||||
**
|
||||
** This bundle (typically released as sqlite3.js or sqlite3.mjs)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* @preserve
|
||||
/*
|
||||
2022-09-16
|
||||
|
||||
The author disclaims copyright to this source code. In place of a
|
||||
|
||||
+230
-1992
File diff suppressed because it is too large
Load Diff
@@ -358,7 +358,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
try{
|
||||
const [cMsg, n] = wasm.scopedAllocCString(e.message, true);
|
||||
wasm.cstrncpy(pOut, cMsg, nOut);
|
||||
if(n > nOut) wasm.poke8(wasm.ptr.add(pOut,nOut,-1), 0);
|
||||
if(n > nOut) wasm.poke8(pOut + nOut - 1, 0);
|
||||
}catch(e){
|
||||
return capi.SQLITE_NOMEM;
|
||||
}finally{
|
||||
@@ -410,7 +410,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}/*vfsMethods*/;
|
||||
|
||||
/**
|
||||
Creates, initializes, and returns an sqlite3_vfs instance for an
|
||||
Creates and initializes an sqlite3_vfs instance for an
|
||||
OpfsSAHPool. The argument is the VFS's name (JS string).
|
||||
|
||||
Throws if the VFS name is already registered or if something
|
||||
@@ -1157,9 +1157,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
described at the end of these docs.
|
||||
|
||||
This function accepts an options object to configure certain
|
||||
parts but it is only acknowledged for the very first call for
|
||||
each distinct name and ignored for all subsequent calls with that
|
||||
same name.
|
||||
parts but it is only acknowledged for the very first call and
|
||||
ignored for all subsequent calls.
|
||||
|
||||
The options, in alphabetical order:
|
||||
|
||||
@@ -1225,14 +1224,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
- Paths given to it _must_ be absolute. Relative paths will not
|
||||
be properly recognized. This is arguably a bug but correcting it
|
||||
requires some hoop-jumping in routines which have no business
|
||||
doing such tricks. (2026-01-19 (2.5 years later): the specifics
|
||||
are lost to history, but this was a side effect of xOpen()
|
||||
receiving an immutable C-string filename, to which no implicit
|
||||
"/" can be prefixed without causing a discrepancy between what
|
||||
the user provided and what the VFS stores. Its conceivable that
|
||||
that quirk could be glossed over in xFullPathname(), but
|
||||
regressions when doing so cannot be ruled out, so there are no
|
||||
current plans to change this behavior.)
|
||||
doing such tricks.
|
||||
|
||||
- It is possible to install multiple instances under different
|
||||
names, each sandboxed from one another inside their own private
|
||||
|
||||
@@ -172,7 +172,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Works like unget() plus it calls dispose() on the
|
||||
StructType object.
|
||||
*/
|
||||
dispose: (pCObj)=>__xWrap(pCObj,true)?.dispose?.()
|
||||
dispose: (pCObj)=>{
|
||||
const o = __xWrap(pCObj,true);
|
||||
if(o) o.dispose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+141
-135
@@ -229,18 +229,15 @@
|
||||
** not by client code, so an argument can be made for reducing their
|
||||
** visibility by not including them in any build-time export lists.
|
||||
**
|
||||
** 2025-12-01: for use in non-Emscripten builds, we need a more
|
||||
** invasive macro which explicitly names the export:
|
||||
** SQLITE_WASM_EXPORT2.
|
||||
** 2022-09-11: it's not yet _proven_ that this approach works in
|
||||
** non-Emscripten builds. If not, such builds will need to export
|
||||
** those using the --export=... wasm-ld flag (or equivalent). As of
|
||||
** this writing we are tied to Emscripten for various reasons
|
||||
** and cannot test the library with other build environments.
|
||||
*/
|
||||
#define SQLITE_WASM_EXPORT __attribute__((used,visibility("default")))
|
||||
#define SQLITE_WASM_EXPORT_NAMED(X) __attribute__((export_name(#X),used,visibility("default")))
|
||||
#define SQLITE_WASM_EXPORT2(RETTYPE,NAME,SIG) SQLITE_WASM_EXPORT_NAMED(NAME) RETTYPE NAME SIG
|
||||
|
||||
#if 1
|
||||
/** Increase the kvvfs key size limit from 32. */
|
||||
#define KVRECORD_KEY_SZ 128
|
||||
#endif
|
||||
// See also:
|
||||
//__attribute__((export_name("theExportedName"), used, visibility("default")))
|
||||
|
||||
/*
|
||||
** Which sqlite3.c we're using needs to be configurable to enable
|
||||
@@ -264,6 +261,45 @@
|
||||
#undef INC__STRINGIFY
|
||||
#undef SQLITE_C
|
||||
|
||||
#if 0
|
||||
/*
|
||||
** An EXPERIMENT in implementing a stack-based allocator analog to
|
||||
** Emscripten's stackSave(), stackAlloc(), stackRestore().
|
||||
** Unfortunately, this cannot work together with Emscripten because
|
||||
** Emscripten defines its own native one and we'd stomp on each
|
||||
** other's memory. Other than that complication, basic tests show it
|
||||
** to work just fine.
|
||||
**
|
||||
** Another option is to malloc() a chunk of our own and call that our
|
||||
** "stack".
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_end(void){
|
||||
extern void __heap_base
|
||||
/* see https://stackoverflow.com/questions/10038964 */;
|
||||
return &__heap_base;
|
||||
}
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_begin(void){
|
||||
extern void __data_end;
|
||||
return &__data_end;
|
||||
}
|
||||
static void * pWasmStackPtr = 0;
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_ptr(void){
|
||||
if(!pWasmStackPtr) pWasmStackPtr = sqlite3__wasm_stack_end();
|
||||
return pWasmStackPtr;
|
||||
}
|
||||
SQLITE_WASM_EXPORT void sqlite3__wasm_stack_restore(void * p){
|
||||
pWasmStackPtr = p;
|
||||
}
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_stack_alloc(int n){
|
||||
if(n<=0) return 0;
|
||||
n = (n + 7) & ~7 /* align to 8-byte boundary */;
|
||||
unsigned char * const p = (unsigned char *)sqlite3__wasm_stack_ptr();
|
||||
unsigned const char * const b = (unsigned const char *)sqlite3__wasm_stack_begin();
|
||||
if(b + n >= p || b + n < b/*overflow*/) return 0;
|
||||
return pWasmStackPtr = p - n;
|
||||
}
|
||||
#endif /* stack allocator experiment */
|
||||
|
||||
/*
|
||||
** State for the "pseudo-stack" allocator implemented in
|
||||
** sqlite3__wasm_pstack_xyz(). In order to avoid colliding with
|
||||
@@ -312,10 +348,10 @@ SQLITE_WASM_EXPORT void sqlite3__wasm_pstack_restore(unsigned char * p){
|
||||
** the memory on success, 0 on error (including a negative n value). n
|
||||
** is always adjusted to be a multiple of 8 and returned memory is
|
||||
** always zeroed out before returning (because this keeps the client
|
||||
** JS code from having to do so, and most uses of the pstack call for
|
||||
** doing so).
|
||||
** JS code from having to do so, and most uses of the pstack will
|
||||
** call for doing so).
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(void *,sqlite3__wasm_pstack_alloc,(int n)){
|
||||
SQLITE_WASM_EXPORT void * sqlite3__wasm_pstack_alloc(int n){
|
||||
if( n<=0 ) return 0;
|
||||
n = (n + 7) & ~7 /* align to 8-byte boundary */;
|
||||
if( PStack.pBegin + n > PStack.pPos /*not enough space left*/
|
||||
@@ -327,7 +363,7 @@ SQLITE_WASM_EXPORT2(void *,sqlite3__wasm_pstack_alloc,(int n)){
|
||||
** Return the number of bytes left which can be
|
||||
** sqlite3__wasm_pstack_alloc()'d.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_pstack_remaining,(void)){
|
||||
SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_remaining(void){
|
||||
assert(PStack.pPos >= PStack.pBegin);
|
||||
assert(PStack.pPos <= PStack.pEnd);
|
||||
return (int)(PStack.pPos - PStack.pBegin);
|
||||
@@ -338,7 +374,7 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_pstack_remaining,(void)){
|
||||
** any space which is currently allocated. This value is a
|
||||
** compile-time constant.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_pstack_quota,(void)){
|
||||
SQLITE_WASM_EXPORT int sqlite3__wasm_pstack_quota(void){
|
||||
return (int)(PStack.pEnd - PStack.pBegin);
|
||||
}
|
||||
|
||||
@@ -351,7 +387,8 @@ struct WasmTestStruct {
|
||||
void (*xFunc)(void*);
|
||||
};
|
||||
typedef struct WasmTestStruct WasmTestStruct;
|
||||
SQLITE_WASM_EXPORT2(void,sqlite3__wasm_test_struct,(WasmTestStruct * s)){
|
||||
SQLITE_WASM_EXPORT
|
||||
void sqlite3__wasm_test_struct(WasmTestStruct * s){
|
||||
if(s){
|
||||
if( 0 ){
|
||||
/* Do not be alarmed by the small (and odd) pointer values.
|
||||
@@ -391,7 +428,8 @@ SQLITE_WASM_EXPORT2(void,sqlite3__wasm_test_struct,(WasmTestStruct * s)){
|
||||
** fails to compile with "tables may not be 64-bit" but does not tell
|
||||
** us where it's happening.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
SQLITE_WASM_EXPORT
|
||||
const char * sqlite3__wasm_enum_json(void){
|
||||
static char aBuffer[1024 * 20] =
|
||||
{0} /* where the JSON goes. 2025-09-19: output size=19295, but
|
||||
that can vary slightly from build to build, so a little
|
||||
@@ -594,7 +632,6 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
DefGroup(encodings) {
|
||||
/* Noting that the wasm binding only aims to support UTF-8. */
|
||||
DefInt(SQLITE_UTF8);
|
||||
DefInt(SQLITE_UTF8_ZT);
|
||||
DefInt(SQLITE_UTF16LE);
|
||||
DefInt(SQLITE_UTF16BE);
|
||||
DefInt(SQLITE_UTF16);
|
||||
@@ -982,15 +1019,13 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
/** ^^^ indirection needed to expand CurrentStruct */
|
||||
#define StructBinder StructBinder_(CurrentStruct)
|
||||
#define _StructBinder CloseBrace(2)
|
||||
#define M3(MEMBER,SIG,READONLY) \
|
||||
outf("%s\"%s\": " \
|
||||
"{\"offset\":%d,\"sizeof\":%d,\"signature\":\"%s\"%s}", \
|
||||
(n++ ? ", " : ""), #MEMBER, \
|
||||
(int)offsetof(CurrentStruct,MEMBER), \
|
||||
(int)sizeof(((CurrentStruct*)0)->MEMBER), \
|
||||
SIG, (READONLY ? ",\"readOnly\":true" : ""))
|
||||
#define M(MEMBER,SIG) M3(MEMBER,SIG,0)
|
||||
#define MRO(MEMBER,SIG) M3(MEMBER,SIG,1)
|
||||
#define M(MEMBER,SIG) \
|
||||
outf("%s\"%s\": " \
|
||||
"{\"offset\":%d,\"sizeof\": %d,\"signature\":\"%s\"}", \
|
||||
(n++ ? ", " : ""), #MEMBER, \
|
||||
(int)offsetof(CurrentStruct,MEMBER), \
|
||||
(int)sizeof(((CurrentStruct*)0)->MEMBER), \
|
||||
SIG)
|
||||
|
||||
nStruct = 0;
|
||||
out(", \"structs\": ["); {
|
||||
@@ -1053,30 +1088,11 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
#undef CurrentStruct
|
||||
|
||||
#define CurrentStruct sqlite3_kvvfs_methods
|
||||
/* From os_kv.c */
|
||||
StructBinder {
|
||||
M(xRcrdRead, "i(sspi)");
|
||||
M(xRcrdWrite, "i(sss)");
|
||||
M(xRcrdDelete, "i(ss)");
|
||||
MRO(nKeySize, "i");
|
||||
MRO(nBufferSize, "i");
|
||||
M(pVfs, "p");
|
||||
M(pIoDb, "p");
|
||||
M(pIoJrnl, "p");
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
#define CurrentStruct KVVfsFile
|
||||
/* From os_kv.c */
|
||||
StructBinder {
|
||||
M(base, "p")/*sqlite3_file base*/;
|
||||
M(zClass, "s");
|
||||
M(isJournal, "i");
|
||||
M(nJrnl, "i")/*actually unsigned!*/;
|
||||
M(aJrnl, "p");
|
||||
M(szPage, "i");
|
||||
M(szDb, "j");
|
||||
M(aData, "p");
|
||||
M(xRead, "i(sspi)");
|
||||
M(xWrite, "i(sss)");
|
||||
M(xDelete, "i(ss)");
|
||||
M(nKeySize, "i");
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
@@ -1133,13 +1149,7 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
** sqlite3_index_info, we have to uplift those into constructs we
|
||||
** can access by type name. These structs _must_ match their
|
||||
** in-sqlite3_index_info counterparts byte for byte.
|
||||
**
|
||||
** 2025-11-21: this uplifing is no longer necessary, as Jaccwabyt
|
||||
** can now handle nested structs, but "it ain't broke" so there's
|
||||
** no pressing need to rewire this. Also, it's conceivable that
|
||||
** rewiring it might break downstream vtab impls, so it shouldn't
|
||||
** be rewired.
|
||||
*/
|
||||
*/
|
||||
typedef struct {
|
||||
int iColumn;
|
||||
unsigned char op;
|
||||
@@ -1234,8 +1244,6 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
#undef StructBinder_
|
||||
#undef StructBinder__
|
||||
#undef M
|
||||
#undef MRO
|
||||
#undef M3
|
||||
#undef _StructBinder
|
||||
#undef CloseBrace
|
||||
#undef out
|
||||
@@ -1253,7 +1261,8 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_enum_json,(void)){
|
||||
** method, SQLITE_MISUSE is returned, else the result of the xDelete()
|
||||
** call is returned.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_vfs_unlink,(sqlite3_vfs *pVfs, const char *zName)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_vfs_unlink(sqlite3_vfs *pVfs, const char *zName){
|
||||
int rc = SQLITE_MISUSE /* ??? */;
|
||||
if( 0==pVfs && 0!=zName ) pVfs = sqlite3_vfs_find(0);
|
||||
if( zName && pVfs && pVfs->xDelete ){
|
||||
@@ -1270,7 +1279,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_vfs_unlink,(sqlite3_vfs *pVfs, const char
|
||||
** defaulting to "main" if zDbName is 0. Returns 0 if no db with the
|
||||
** given name is open.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(sqlite3_vfs *,sqlite3__wasm_db_vfs,(sqlite3 *pDb, const char *zDbName)){
|
||||
SQLITE_WASM_EXPORT
|
||||
sqlite3_vfs * sqlite3__wasm_db_vfs(sqlite3 *pDb, const char *zDbName){
|
||||
sqlite3_vfs * pVfs = 0;
|
||||
sqlite3_file_control(pDb, zDbName ? zDbName : "main",
|
||||
SQLITE_FCNTL_VFS_POINTER, &pVfs);
|
||||
@@ -1292,7 +1302,8 @@ SQLITE_WASM_EXPORT2(sqlite3_vfs *,sqlite3__wasm_db_vfs,(sqlite3 *pDb, const char
|
||||
** Returns 0 on success, an SQLITE_xxx code on error. Returns
|
||||
** SQLITE_MISUSE if pDb is NULL.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_reset,(sqlite3 *pDb)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_reset(sqlite3 *pDb){
|
||||
int rc = SQLITE_MISUSE;
|
||||
if( pDb ){
|
||||
sqlite3_table_column_metadata(pDb, "main", 0, 0, 0, 0, 0, 0, 0);
|
||||
@@ -1373,10 +1384,10 @@ int sqlite3__wasm_db_export_chunked( sqlite3* pDb,
|
||||
** If `*pOut` is not NULL, the caller is responsible for passing it to
|
||||
** sqlite3_free() to free it.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_serialize,
|
||||
(sqlite3 *pDb, const char *zSchema,
|
||||
unsigned char **pOut,
|
||||
sqlite3_int64 *nOut, unsigned int mFlags)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
unsigned char **pOut,
|
||||
sqlite3_int64 *nOut, unsigned int mFlags ){
|
||||
unsigned char * z;
|
||||
if( !pDb || !pOut ) return SQLITE_MISUSE;
|
||||
if( nOut ) *nOut = 0;
|
||||
@@ -1437,9 +1448,11 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_serialize,
|
||||
** portability, so that the API can still work in builds where BigInt
|
||||
** support is disabled or unavailable.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_vfs_create_file,
|
||||
(sqlite3_vfs *pVfs, const char *zFilename,
|
||||
const unsigned char * pData, int nData)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
const char *zFilename,
|
||||
const unsigned char * pData,
|
||||
int nData ){
|
||||
int rc;
|
||||
sqlite3_file *pFile = 0;
|
||||
sqlite3_io_methods const *pIo;
|
||||
@@ -1525,9 +1538,10 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_vfs_create_file,
|
||||
** zFilename, appends pData bytes to it, and returns 0 on success or
|
||||
** SQLITE_IOERR on error.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_posix_create_file,
|
||||
(const char *zFilename, const unsigned char * pData,
|
||||
int nData)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_posix_create_file( const char *zFilename,
|
||||
const unsigned char * pData,
|
||||
int nData ){
|
||||
int rc;
|
||||
FILE * pFile = 0;
|
||||
int fileExisted = 0;
|
||||
@@ -1547,30 +1561,22 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_posix_create_file,
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** This returns either a pointer to a static buffer or zKeyIn directly
|
||||
** (if zClass is NULL or empty).
|
||||
** Allocates sqlite3KvvfsMethods.nKeySize bytes from
|
||||
** sqlite3__wasm_pstack_alloc() and returns 0 if that allocation fails,
|
||||
** else it passes that string to kvstorageMakeKey() and returns a
|
||||
** NUL-terminated pointer to that string. It is up to the caller to
|
||||
** use sqlite3__wasm_pstack_restore() to free the returned pointer.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_kvvfsMakeKey,
|
||||
(const char *zClass, const char *zKeyIn)){
|
||||
static char buf[SQLITE_KVOS_SZ+1] = {0};
|
||||
SQLITE_WASM_EXPORT
|
||||
char * sqlite3__wasm_kvvfsMakeKeyOnPstack(const char *zClass,
|
||||
const char *zKeyIn){
|
||||
assert(sqlite3KvvfsMethods.nKeySize>24);
|
||||
if( zClass && *zClass ){
|
||||
kvrecordMakeKey(zClass, zKeyIn, buf);
|
||||
return buf;
|
||||
}else{
|
||||
#if 1
|
||||
/* We can return zKeyIn here only because the JS API takes special
|
||||
** care with its lifetime.*/
|
||||
return zKeyIn;
|
||||
#else
|
||||
/* It would be nice to be able to return zKeyIn directly here, but
|
||||
** it may have been allocated as part of the automated JS-to-WASM
|
||||
** conversions, in which case it will be freed before reaching the
|
||||
** caller. */
|
||||
sqlite3_snprintf(KVRECORD_KEY_SZ, buf, "%s", zKeyIn);
|
||||
return buf;
|
||||
#endif
|
||||
char *zKeyOut =
|
||||
(char *)sqlite3__wasm_pstack_alloc(sqlite3KvvfsMethods.nKeySize);
|
||||
if(zKeyOut){
|
||||
kvstorageMakeKey(zClass, zKeyIn, zKeyOut);
|
||||
}
|
||||
return zKeyOut;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1580,7 +1586,8 @@ SQLITE_WASM_EXPORT2(const char *,sqlite3__wasm_kvvfsMakeKey,
|
||||
** Returns the pointer to the singleton object which holds the kvvfs
|
||||
** I/O methods and associated state.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(sqlite3_kvvfs_methods *,sqlite3__wasm_kvvfs_methods,(void)){
|
||||
SQLITE_WASM_EXPORT
|
||||
sqlite3_kvvfs_methods * sqlite3__wasm_kvvfs_methods(void){
|
||||
return &sqlite3KvvfsMethods;
|
||||
}
|
||||
|
||||
@@ -1595,8 +1602,8 @@ SQLITE_WASM_EXPORT2(sqlite3_kvvfs_methods *,sqlite3__wasm_kvvfs_methods,(void)){
|
||||
** sqlite3_vtab_config(), or SQLITE_MISUSE if the 2nd arg is not a
|
||||
** valid value.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_vtab_config,
|
||||
(sqlite3 *pDb, int op, int arg)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_vtab_config(sqlite3 *pDb, int op, int arg){
|
||||
switch(op){
|
||||
case SQLITE_VTAB_DIRECTONLY:
|
||||
case SQLITE_VTAB_INNOCUOUS:
|
||||
@@ -1616,8 +1623,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_vtab_config,
|
||||
** Wrapper for the variants of sqlite3_db_config() which take
|
||||
** (int,int*) variadic args.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_config_ip,
|
||||
(sqlite3 *pDb, int op, int arg1, int* pArg2)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_config_ip(sqlite3 *pDb, int op, int arg1, int* pArg2){
|
||||
switch(op){
|
||||
case SQLITE_DBCONFIG_ENABLE_FKEY:
|
||||
case SQLITE_DBCONFIG_ENABLE_TRIGGER:
|
||||
@@ -1652,9 +1659,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_config_ip,
|
||||
** Wrapper for the variants of sqlite3_db_config() which take
|
||||
** (void*,int,int) variadic args.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_config_pii,
|
||||
(sqlite3 *pDb, int op, void * pArg1, int arg2,
|
||||
int arg3)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_config_pii(sqlite3 *pDb, int op, void * pArg1, int arg2, int arg3){
|
||||
switch(op){
|
||||
case SQLITE_DBCONFIG_LOOKASIDE:
|
||||
return sqlite3_db_config(pDb, op, pArg1, arg2, arg3);
|
||||
@@ -1669,8 +1675,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_config_pii,
|
||||
** Wrapper for the variants of sqlite3_db_config() which take
|
||||
** (const char *) variadic args.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_config_s,(sqlite3 *pDb, int op,
|
||||
const char *zArg)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_db_config_s(sqlite3 *pDb, int op, const char *zArg){
|
||||
switch(op){
|
||||
case SQLITE_DBCONFIG_MAINDBNAME:
|
||||
return sqlite3_db_config(pDb, op, zArg);
|
||||
@@ -1686,7 +1692,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_db_config_s,(sqlite3 *pDb, int op,
|
||||
** Binding for combinations of sqlite3_config() arguments which take
|
||||
** a single integer argument.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_config_i,(int op, int arg)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_config_i(int op, int arg){
|
||||
return sqlite3_config(op, arg);
|
||||
}
|
||||
|
||||
@@ -1697,7 +1704,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_config_i,(int op, int arg)){
|
||||
** Binding for combinations of sqlite3_config() arguments which take
|
||||
** two int arguments.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_config_ii,(int op, int arg1, int arg2)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_config_ii(int op, int arg1, int arg2){
|
||||
return sqlite3_config(op, arg1, arg2);
|
||||
}
|
||||
|
||||
@@ -1708,7 +1716,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_config_ii,(int op, int arg1, int arg2)){
|
||||
** Binding for combinations of sqlite3_config() arguments which take
|
||||
** a single i64 argument.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_config_j,(int op, sqlite3_int64 arg)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_config_j(int op, sqlite3_int64 arg){
|
||||
return sqlite3_config(op, arg);
|
||||
}
|
||||
|
||||
@@ -1720,7 +1729,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_config_j,(int op, sqlite3_int64 arg)){
|
||||
** sqlite3_mprintf()'s %Q modifier (if addQuotes is true) or %q (if
|
||||
** addQuotes is 0). Returns NULL if z is NULL or on OOM.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(char *,sqlite3__wasm_qfmt_token,(char *z, int addQuotes)){
|
||||
SQLITE_WASM_EXPORT
|
||||
char * sqlite3__wasm_qfmt_token(char *z, int addQuotes){
|
||||
char * rc = 0;
|
||||
if( z ){
|
||||
rc = addQuotes
|
||||
@@ -1730,21 +1740,6 @@ SQLITE_WASM_EXPORT2(char *,sqlite3__wasm_qfmt_token,(char *z, int addQuotes)){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** A WASM wrapper for the interal os_kv.c:kvvfsDecode() for internal
|
||||
** use by the kvvfs v2 API.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_kvvfs_decode,(const char *a, char *aOut, int nOut)){
|
||||
return kvvfsDecode(a, aOut, nOut);
|
||||
}
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_kvvfs_encode,(const char *a, int nA, char *aOut)){
|
||||
return kvvfsEncode(a, nA, aOut);
|
||||
}
|
||||
|
||||
|
||||
#if defined(__EMSCRIPTEN__) && defined(SQLITE_ENABLE_WASMFS)
|
||||
#include <emscripten/console.h>
|
||||
#include <emscripten/wasmfs.h>
|
||||
@@ -1770,7 +1765,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_kvvfs_encode,(const char *a, int nA, char
|
||||
** the virtual FS fails. In builds compiled without SQLITE_ENABLE_WASMFS
|
||||
** defined, SQLITE_NOTFOUND is returned without side effects.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_init_wasmfs,(const char *zMountPoint)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_init_wasmfs(const char *zMountPoint){
|
||||
static backend_t pOpfs = 0;
|
||||
if( !zMountPoint || !*zMountPoint ) zMountPoint = "/opfs";
|
||||
if( !pOpfs ){
|
||||
@@ -1789,7 +1785,8 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_init_wasmfs,(const char *zMountPoint)){
|
||||
return pOpfs ? 0 : SQLITE_NOMEM;
|
||||
}
|
||||
#else
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_init_wasmfs,(const char *zUnused)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_init_wasmfs(const char *zUnused){
|
||||
//emscripten_console_warn("WASMFS OPFS is not compiled in.");
|
||||
(void)zUnused;
|
||||
return SQLITE_NOTFOUND;
|
||||
@@ -1798,43 +1795,52 @@ SQLITE_WASM_EXPORT2(int,sqlite3__wasm_init_wasmfs,(const char *zUnused)){
|
||||
|
||||
#if SQLITE_WASM_ENABLE_C_TESTS
|
||||
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_test_intptr,(int * p)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_test_intptr(int * p){
|
||||
return *p = *p * 2;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(void *,sqlite3__wasm_test_voidptr,(void * p)){
|
||||
SQLITE_WASM_EXPORT
|
||||
void * sqlite3__wasm_test_voidptr(void * p){
|
||||
return p;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(int64_t,sqlite3__wasm_test_int64_max,(void)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64_max(void){
|
||||
return (int64_t)0x7fffffffffffffff;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(int64_t,sqlite3__wasm_test_int64_min,(void)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64_min(void){
|
||||
return ~sqlite3__wasm_test_int64_max();
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(int64_t,sqlite3__wasm_test_int64_times2,(int64_t x)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64_times2(int64_t x){
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(void,sqlite3__wasm_test_int64_minmax,(int64_t * min, int64_t *max)){
|
||||
SQLITE_WASM_EXPORT
|
||||
void sqlite3__wasm_test_int64_minmax(int64_t * min, int64_t *max){
|
||||
*max = sqlite3__wasm_test_int64_max();
|
||||
*min = sqlite3__wasm_test_int64_min();
|
||||
/*printf("minmax: min=%lld, max=%lld\n", *min, *max);*/
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(int64_t,sqlite3__wasm_test_int64ptr,(int64_t * p)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int64_t sqlite3__wasm_test_int64ptr(int64_t * p){
|
||||
/*printf("sqlite3__wasm_test_int64ptr( @%lld = 0x%llx )\n", (int64_t)p, *p);*/
|
||||
return *p = *p * 2;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(void,sqlite3__wasm_test_stack_overflow,(int recurse)){
|
||||
SQLITE_WASM_EXPORT
|
||||
void sqlite3__wasm_test_stack_overflow(int recurse){
|
||||
if(recurse) sqlite3__wasm_test_stack_overflow(recurse);
|
||||
}
|
||||
|
||||
/* For testing the 'string:dealloc' whwasmutil.xWrap() conversion. */
|
||||
SQLITE_WASM_EXPORT2(char *,sqlite3__wasm_test_str_hello,(int fail)){
|
||||
SQLITE_WASM_EXPORT
|
||||
char * sqlite3__wasm_test_str_hello(int fail){
|
||||
char * s = fail ? 0 : (char *)sqlite3_malloc(6);
|
||||
if(s){
|
||||
memcpy(s, "hello", 5);
|
||||
@@ -1948,8 +1954,8 @@ static int sqlite3__wasm_SQLTester_strnotglob(const char *zGlob, const char *z){
|
||||
return *z==0;
|
||||
}
|
||||
|
||||
SQLITE_WASM_EXPORT2(int,sqlite3__wasm_SQLTester_strglob,
|
||||
(const char *zGlob, const char *z)){
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3__wasm_SQLTester_strglob(const char *zGlob, const char *z){
|
||||
return !sqlite3__wasm_SQLTester_strnotglob(zGlob, z);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,10 @@
|
||||
slightly simpler client-side interface than the slightly-lower-level
|
||||
Worker API does.
|
||||
|
||||
In non-ESM builds this file necessarily exposes one global symbol,
|
||||
but clients may freely `delete` that symbol after calling it.
|
||||
This script necessarily exposes one global symbol, but clients may
|
||||
freely `delete` that symbol after calling it.
|
||||
*/
|
||||
//#if not defined target:es6-module
|
||||
'use strict';
|
||||
//#endif
|
||||
/**
|
||||
Configures an sqlite3 Worker API #1 Worker such that it can be
|
||||
manipulated via a Promise-based interface and returns a factory
|
||||
@@ -111,12 +109,10 @@
|
||||
the callback is called one time for each row of the result set,
|
||||
passed the same worker message format as the worker API emits:
|
||||
|
||||
{
|
||||
type:typeString,
|
||||
{type:typeString,
|
||||
row:VALUE,
|
||||
rowNumber:1-based-#,
|
||||
columnNames: array
|
||||
}
|
||||
columnNames: array}
|
||||
|
||||
Where `typeString` is an internally-synthesized message type string
|
||||
used temporarily for worker message dispatching. It can be ignored
|
||||
@@ -127,9 +123,10 @@
|
||||
callback.
|
||||
|
||||
At the end of the result set, the same event is fired with
|
||||
(row=undefined, rowNumber=null) to indicate that the end of the
|
||||
result set has been reached. The rows arrive via worker-posted
|
||||
messages, with all the implications of that.
|
||||
(row=undefined, rowNumber=null) to indicate that
|
||||
the end of the result set has been reached. Note that the rows
|
||||
arrive via worker-posted messages, with all the implications
|
||||
of that.
|
||||
|
||||
Notable shortcomings:
|
||||
|
||||
@@ -260,9 +257,7 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = {
|
||||
type: 'module'
|
||||
});
|
||||
//#elif target:es6-module
|
||||
return new Worker(new URL("sqlite3-worker1.mjs", import.meta.url),{
|
||||
type: 'module'
|
||||
});
|
||||
return new Worker(new URL("sqlite3-worker1.js", import.meta.url));
|
||||
//#else
|
||||
let theJs = "sqlite3-worker1.js";
|
||||
if(this.currentScript){
|
||||
@@ -286,7 +281,7 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = {
|
||||
})
|
||||
//#endif
|
||||
,
|
||||
onerror: (...args)=>console.error('sqlite3Worker1Promiser():',...args)
|
||||
onerror: (...args)=>console.error('worker1 promiser error',...args)
|
||||
}/*defaultConfig*/;
|
||||
|
||||
/**
|
||||
@@ -348,7 +343,6 @@ globalThis.sqlite3Worker1Promiser.v2.defaultConfig =
|
||||
incompatibility.
|
||||
*/
|
||||
export default sqlite3Worker1Promiser.v2;
|
||||
delete globalThis.sqlite3Worker1Promiser;
|
||||
//#endif /* target:es6-module */
|
||||
//#else
|
||||
/* Built with the omit-oo1 flag. */
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
directory from which `sqlite3.js` will be loaded.
|
||||
*/
|
||||
//#if target:es6-bundler-friendly
|
||||
import sqlite3InitModule from './sqlite3-bundler-friendly.mjs';
|
||||
import {default as sqlite3InitModule} from './sqlite3-bundler-friendly.mjs';
|
||||
//#elif target:es6-module
|
||||
import sqlite3InitModule from './sqlite3.mjs';
|
||||
return new Worker(new URL("sqlite3.js", import.meta.url));
|
||||
//#else
|
||||
"use strict";
|
||||
{
|
||||
|
||||
@@ -2603,7 +2603,7 @@ static int arg_is_flag( char const *zFlag, char const *zArg,
|
||||
|
||||
static void define_argv(int argc, char const * const * argv){
|
||||
sqlite3_str * const s = sqlite3_str_new(g.db);
|
||||
sqlite3_str_append(s, "c-pp::argv=", 11);
|
||||
sqlite3_str_append(s, "c-pp::ARGV=", 11);
|
||||
for( int i = 0; i < argc; ++i ){
|
||||
if( i ) sqlite3_str_appendchar(s, 1, ' ');
|
||||
sqlite3_str_appendf(s, "%s", argv[i]);
|
||||
|
||||
@@ -44,8 +44,7 @@
|
||||
/** abort() if expr is false. If expr is a function, it
|
||||
is called and its result is evaluated.
|
||||
*/
|
||||
assert: function f(expr, ...msg){
|
||||
msg = msg?.join?.(' ');
|
||||
assert: function f(expr, msg){
|
||||
if(!f._){
|
||||
f._ = ('undefined'===typeof abort
|
||||
? (msg)=>{throw new Error(msg)}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
(function(){
|
||||
const T = globalThis.SqliteTestUtil;
|
||||
const T = self.SqliteTestUtil;
|
||||
const toss = function(...args){throw new Error(args.join(' '))};
|
||||
const debug = console.debug.bind(console);
|
||||
const eOutput = document.querySelector('#test-output');
|
||||
@@ -40,7 +40,7 @@
|
||||
const error = function(...args){
|
||||
logHtml('error',...args);
|
||||
};
|
||||
|
||||
|
||||
const runTests = function(sqlite3){
|
||||
const capi = sqlite3.capi,
|
||||
oo = sqlite3.oo1,
|
||||
@@ -51,7 +51,7 @@
|
||||
error("This build is not kvvfs-capable.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const dbStorage = 0 ? 'session' : 'local';
|
||||
const theStore = 's'===dbStorage[0] ? sessionStorage : localStorage;
|
||||
const db = new oo.JsStorageDb( dbStorage );
|
||||
@@ -108,7 +108,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
sqlite3InitModule(globalThis.sqlite3TestModule).then((sqlite3)=>{
|
||||
sqlite3InitModule(self.sqlite3TestModule).then((sqlite3)=>{
|
||||
runTests(sqlite3);
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<link rel="stylesheet" href="common/emscripten.css"/>
|
||||
<link rel="stylesheet" href="common/testing.css"/>
|
||||
//#if target:es6-module
|
||||
<title>Worker1-promiser (ESM) tests</title>
|
||||
//#if target=es6-module
|
||||
<title>worker-promise (via ESM) tests</title>
|
||||
//#else
|
||||
<title>Worker1-promiser tests</title>
|
||||
<title>worker-promise tests</title>
|
||||
//#endif
|
||||
</head>
|
||||
<body>
|
||||
@@ -32,7 +32,7 @@
|
||||
<hr>
|
||||
<div id='test-output'></div>
|
||||
<script src="common/SqliteTestUtil.js"></script>
|
||||
//#if target:es6-module
|
||||
//#if target=es6-module
|
||||
<script src="demo-worker1-promiser.mjs" type="module"></script>
|
||||
//#else
|
||||
<script src="jswasm/sqlite3-worker1-promiser.js"></script>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
'use strict';
|
||||
(function(){
|
||||
const T = globalThis.SqliteTestUtil;
|
||||
const T = self.SqliteTestUtil;
|
||||
const SW = new Worker("jswasm/sqlite3-worker1.js");
|
||||
const DbState = {
|
||||
id: undefined
|
||||
@@ -323,7 +323,7 @@
|
||||
switch(ev.result){
|
||||
case 'worker1-ready':
|
||||
log("Message:",ev);
|
||||
globalThis.sqlite3TestModule.setStatus(null);
|
||||
self.sqlite3TestModule.setStatus(null);
|
||||
runTests();
|
||||
return;
|
||||
default:
|
||||
@@ -344,5 +344,5 @@
|
||||
};
|
||||
log("Init complete, but async init bits may still be running.");
|
||||
log("Installing Worker into global scope SW for dev purposes.");
|
||||
globalThis.SW = SW;
|
||||
self.SW = SW;
|
||||
})();
|
||||
|
||||
@@ -175,6 +175,10 @@
|
||||
"features (e.g. upload) do not yet work with OPFS.");
|
||||
}
|
||||
stdout('\nEnter ".help" for usage hints.');
|
||||
this.exec([ // initialization commands...
|
||||
'.nullvalue NULL',
|
||||
'.headers on'
|
||||
].join('\n'));
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -5,29 +5,20 @@
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>SQLite3 Fiddle</title>
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
//#if jqterm
|
||||
<!--
|
||||
To add a terminal-style view using jquery.terminal[^1],
|
||||
uncomment the following two HTML lines and ensure that these
|
||||
files are on the web server.
|
||||
|
||||
jquery.terminal.bundle.min.js is a concatenation of jquery.min.js from
|
||||
jquery-bundle.min.js is a concatenation of jquery.min.js from
|
||||
[^2] and jquery.terminal.min.js from [^1].
|
||||
jquery.terminal.min.css is from [^1]. Alterntely, jquery-VERSION.min.js
|
||||
can be found in jquery.terminal's source tree.
|
||||
|
||||
Fiddle automatically enables support for this if it's installed.
|
||||
|
||||
In the canonical build process, if the JQTERM env var is set
|
||||
to a dir containing a clone of [^1] then this block gets
|
||||
enabled and a copy of [^1] is integrated automatically.
|
||||
jquery.terminal.min.css is from [^1].
|
||||
|
||||
[^1]: https://github.com/jcubic/jquery.terminal
|
||||
[^2]: https://jquery.com
|
||||
-->
|
||||
<script src="jqterm/jquery.terminal.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css">
|
||||
//#endif
|
||||
<!--script src="jqterm/jqterm-bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css"-->
|
||||
<style>
|
||||
/* The following styles are for app-level use. */
|
||||
:root {
|
||||
@@ -105,11 +105,6 @@
|
||||
(<a href='speedtest1-worker.html?size=15'>32-bit</a>,
|
||||
<a href='speedtest1-worker-64bit.html?size=15'>64-bit</a>):
|
||||
an interactive Worker-thread variant of speedtest1.</li>
|
||||
<li>speedtest1-worker?vfs=kvvfs
|
||||
(<a href='speedtest1-worker.html?vfs=kvvfs&size=10'>32-bit</a>,
|
||||
<a href='speedtest1-worker-64bit.html?vfs=kvvfs&size=10'>64-bit</a>):
|
||||
speedtest1-worker with the
|
||||
kvvfs VFS preselected and configured for a moderate workload.</li>
|
||||
<li>speedtest1-worker?vfs=opfs
|
||||
(<a href='speedtest1-worker.html?vfs=opfs&size=10'>32-bit</a>,
|
||||
<a href='speedtest1-worker-64bit.html?vfs=opfs&size=10'>64-bit</a>):
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ for arg in $@; do
|
||||
;;
|
||||
|
||||
--snapshot)
|
||||
snapshotSuffix=-snapshot-$(date +%Y%m%d)
|
||||
snapshotSuffix=$(date +%Y%m%d)
|
||||
;;
|
||||
|
||||
-?|--help)
|
||||
|
||||
+16
-34
@@ -40,22 +40,14 @@
|
||||
/*
|
||||
** Flags for use with BuildDef::flags.
|
||||
**
|
||||
** Maintenance reminder: do not combine F_... flags within this enum,
|
||||
** e.g. F_BUNDLER_FRIENDLY=0x02|F_ESM, as that will lead to breakage
|
||||
** in some of the flag checks.
|
||||
** Maintenance reminder: do not combine flags within this enum,
|
||||
** e.g. F_BUNDLER_FRIENDLY=0x02|F_ESM, as that will lead
|
||||
** to breakage in some of the flag checks.
|
||||
*/
|
||||
enum BuildDefFlags {
|
||||
/* Indicates an ESM module build. */
|
||||
F_ESM = 0x01,
|
||||
/* Indicates a "bundler-friendly" build mode. These are untested and
|
||||
** unsupported, provided solely for the downstream npm subproject
|
||||
** (who is responsible for any testing of these).
|
||||
**
|
||||
** The only difference beween bundler-friendly and esm builds is
|
||||
** that bundlers require static filename strings in a few places due
|
||||
** to limitations of bundler tooling, whereas vanilla and JS can
|
||||
** both work with dynamic strings.
|
||||
*/
|
||||
/* Indicates a "bundler-friendly" build mode. */
|
||||
F_BUNDLER_FRIENDLY = 1<<1,
|
||||
/* Indicates that this build is unsupported. Such builds are not
|
||||
** added to the 'all' target. The unsupported builds exist primarily
|
||||
@@ -65,11 +57,8 @@ enum BuildDefFlags {
|
||||
F_NOT_IN_ALL = 1<<3,
|
||||
/* If it's a 64-bit build. */
|
||||
F_64BIT = 1<<4,
|
||||
/* Indicates a node.js-for-node.js build. This build is very
|
||||
** specificially untested and unsupported, but the downstream npm
|
||||
** project makes use of it. None of our JS code is specific to node,
|
||||
** but Emscripten's generated sqlite3.js differs between
|
||||
** for-the-browser and for-node builds. */
|
||||
/* Indicates a node.js-for-node.js build (untested and
|
||||
** unsupported). */
|
||||
F_NODEJS = 1<<5,
|
||||
/* Indicates a wasmfs build (untested and unsupported). */
|
||||
F_WASMFS = 1<<6,
|
||||
@@ -88,7 +77,7 @@ enum BuildDefFlags {
|
||||
** differ, however.
|
||||
*/
|
||||
CP_JS = 1 << 30, /* X.js or X.mjs, depending on F_ESM */
|
||||
CP_WASM = 1 << 31, /* X.wasm */
|
||||
CP_WASM = 1 << 31, /* X,wasm */
|
||||
CP_ALL = CP_JS | CP_WASM
|
||||
};
|
||||
|
||||
@@ -151,7 +140,7 @@ struct BuildDef {
|
||||
**
|
||||
** The convention for 32- vs 64-bit pairs is to give them similar
|
||||
** emoji, e.g. a cookie for 32-bit and a donut or cake for 64.
|
||||
** Alternately, the same emoji with a "64" suffix, except that that
|
||||
** Alternately, the same emoji a "64" suffix, except that that
|
||||
** throws off the output alignment in parallel builds ;).
|
||||
*/
|
||||
const char *zEmo;
|
||||
@@ -175,7 +164,7 @@ struct BuildDef {
|
||||
** Makefile code "ifeq (...)". If set, this build is enclosed in a
|
||||
** $zIfCond/endif block.
|
||||
*/
|
||||
const char *zIfCond;
|
||||
const char *zIfCond; /* makefile "ifeq (...)" or similar */
|
||||
int flags; /* Flags from BuildDefFlags */
|
||||
};
|
||||
typedef struct BuildDef BuildDef;
|
||||
@@ -191,7 +180,7 @@ typedef struct BuildDef BuildDef;
|
||||
** logtag.NAME = Used for decorating log output
|
||||
**
|
||||
** etc.
|
||||
*/
|
||||
***/
|
||||
#define BuildDefs_map(E) \
|
||||
E(vanilla) E(vanilla64) \
|
||||
E(esm) E(esm64) \
|
||||
@@ -352,7 +341,7 @@ const BuildDefs oBuildDefs = {
|
||||
.zEnv = 0,
|
||||
.zDeps = 0,
|
||||
.zIfCond = 0,
|
||||
.flags = CP_JS | F_BUNDLER_FRIENDLY | F_ESM | F_NOT_IN_ALL
|
||||
.flags = CP_JS | F_BUNDLER_FRIENDLY | F_ESM
|
||||
},
|
||||
|
||||
/* 64-bit bundler-friendly. */
|
||||
@@ -493,8 +482,8 @@ static void mk_prologue(void){
|
||||
** configuration). Comments like "saves nothing" may not be
|
||||
** technically correct: "nothing" means "some neglible amount."
|
||||
**
|
||||
** Performance gains or losses are _not_ taken into account
|
||||
** here, only wasm file size.
|
||||
** Note that performance gains/losses are _not_ taken into
|
||||
** account here: only wasm file size.
|
||||
*/
|
||||
"--enable-bulk-memory-opt " /* required */
|
||||
"--all-features " /* required */
|
||||
@@ -994,17 +983,10 @@ static void mk_fiddle(void){
|
||||
pf("$(out.%s.js): $(MAKEFILE_LIST) "
|
||||
"$(EXPORTED_FUNCTIONS.fiddle) "
|
||||
"$(fiddle.c.in) "
|
||||
"$(pre-post.%s.deps)",
|
||||
"$(pre-post.%s.deps)\n",
|
||||
zBuildName, zBuildName);
|
||||
if( isDebug ){
|
||||
pf(" $(dir.fiddle)/fiddle-worker.js"
|
||||
" $(dir.fiddle)/fiddle.js"
|
||||
" $(dir.fiddle)/index.html");
|
||||
}
|
||||
ps("");
|
||||
emit_compile_start(zBuildName);
|
||||
pf("\t@$(call b.mkdir@)\n"
|
||||
"\t$(b.cmd@)$(bin.emcc) -o $@"
|
||||
pf("\t$(b.cmd@)$(bin.emcc) -o $@"
|
||||
" $(emcc.flags.%s)" /* set in GNUmakefile */
|
||||
" $(pre-post.%s.flags)"
|
||||
" $(fiddle.c.in)"
|
||||
@@ -1070,7 +1052,7 @@ int main(int argc, char const ** argv){
|
||||
BuildDefs_map(E) if( 0==strcmp("prologue",zArg) ){
|
||||
mk_prologue();
|
||||
}else {
|
||||
fprintf(stderr,"Unknown build name: %s\n", zArg);
|
||||
fprintf(stderr,"Unkown build name: %s\n", zArg);
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
(function(){
|
||||
let speedtestJs = 'speedtest1.js';
|
||||
const urlParams = new URL(globalThis.location.href).searchParams;
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
if(urlParams.has('sqlite3.dir')){
|
||||
speedtestJs = urlParams.get('sqlite3.dir') + '/' + speedtestJs;
|
||||
}
|
||||
@@ -14,9 +14,9 @@
|
||||
const wasmfsDir = function f(wasmUtil){
|
||||
if(undefined !== f._) return f._;
|
||||
const pdir = '/opfs';
|
||||
if( !globalThis.FileSystemHandle
|
||||
|| !globalThis.FileSystemDirectoryHandle
|
||||
|| !globalThis.FileSystemFileHandle){
|
||||
if( !self.FileSystemHandle
|
||||
|| !self.FileSystemDirectoryHandle
|
||||
|| !self.FileSystemFileHandle){
|
||||
return f._ = "";
|
||||
}
|
||||
try{
|
||||
@@ -92,7 +92,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
globalThis.onmessage = function(msg){
|
||||
self.onmessage = function(msg){
|
||||
msg = msg.data;
|
||||
switch(msg.type){
|
||||
case 'run':
|
||||
@@ -111,8 +111,7 @@
|
||||
setStatus: (text)=>mPost('load-status',text)
|
||||
};
|
||||
log("Initializing speedtest1 module...");
|
||||
globalThis.sqlite3InitModule.__isUnderTest = true;
|
||||
globalThis.sqlite3InitModule(EmscriptenModule).then(async (sqlite3)=>{
|
||||
self.sqlite3InitModule(EmscriptenModule).then(async (sqlite3)=>{
|
||||
const S = globalThis.S = App.sqlite3 = sqlite3;
|
||||
log("Loaded speedtest1 module. Setting up...");
|
||||
App.pDir = wasmfsDir(S.wasm);
|
||||
|
||||
+99
-104
@@ -44,137 +44,132 @@
|
||||
mounted, else it returns an empty string.
|
||||
*/
|
||||
const wasmfsDir = function f(wasmUtil){
|
||||
if(undefined !== f._) return f._;
|
||||
const pdir = '/persistent';
|
||||
if( !self.FileSystemHandle
|
||||
|| !self.FileSystemDirectoryHandle
|
||||
|| !self.FileSystemFileHandle){
|
||||
return f._ = "";
|
||||
}
|
||||
try{
|
||||
if(0===wasmUtil.xCallWrapped(
|
||||
'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir
|
||||
)){
|
||||
return f._ = pdir;
|
||||
}else{
|
||||
return f._ = "";
|
||||
if(undefined !== f._) return f._;
|
||||
const pdir = '/persistent';
|
||||
if( !self.FileSystemHandle
|
||||
|| !self.FileSystemDirectoryHandle
|
||||
|| !self.FileSystemFileHandle){
|
||||
return f._ = "";
|
||||
}
|
||||
try{
|
||||
if(0===wasmUtil.xCallWrapped(
|
||||
'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir
|
||||
)){
|
||||
return f._ = pdir;
|
||||
}else{
|
||||
return f._ = "";
|
||||
}
|
||||
}catch(e){
|
||||
// sqlite3_wasm_init_wasmfs() is not available
|
||||
return f._ = "";
|
||||
}
|
||||
}catch(e){
|
||||
// sqlite3_wasm_init_wasmfs() is not available
|
||||
return f._ = "";
|
||||
}
|
||||
};
|
||||
wasmfsDir._ = undefined;
|
||||
|
||||
const eOut = document.querySelector('#test-output');
|
||||
const log2 = function(cssClass,...args){
|
||||
const ln = document.createElement('div');
|
||||
if(cssClass) ln.classList.add(cssClass);
|
||||
ln.append(document.createTextNode(args.join(' ')));
|
||||
eOut.append(ln);
|
||||
//this.e.output.lastElementChild.scrollIntoViewIfNeeded();
|
||||
const ln = document.createElement('div');
|
||||
if(cssClass) ln.classList.add(cssClass);
|
||||
ln.append(document.createTextNode(args.join(' ')));
|
||||
eOut.append(ln);
|
||||
//this.e.output.lastElementChild.scrollIntoViewIfNeeded();
|
||||
};
|
||||
const logList = [];
|
||||
const dumpLogList = function(){
|
||||
logList.forEach((v)=>log2('',v));
|
||||
logList.length = 0;
|
||||
logList.forEach((v)=>log2('',v));
|
||||
logList.length = 0;
|
||||
};
|
||||
/* we cannot update DOM while speedtest is running unless we run
|
||||
speedtest in a worker thread. */;
|
||||
const log = (...args)=>{
|
||||
console.log(...args);
|
||||
logList.push(args.join(' '));
|
||||
console.log(...args);
|
||||
logList.push(args.join(' '));
|
||||
};
|
||||
const logErr = function(...args){
|
||||
console.error(...args);
|
||||
logList.push('ERROR: '+args.join(' '));
|
||||
console.error(...args);
|
||||
logList.push('ERROR: '+args.join(' '));
|
||||
};
|
||||
|
||||
const runTests = function(sqlite3){
|
||||
globalThis.S = sqlite3;
|
||||
const capi = sqlite3.capi, wasm = sqlite3.wasm;
|
||||
//console.debug('sqlite3 (global S) =',sqlite3);
|
||||
const pDir = wasmfsDir(wasm);
|
||||
if(pDir){
|
||||
console.warn("wasmfs storage:",pDir);
|
||||
}
|
||||
const scope = wasm.scopedAllocPush();
|
||||
let dbFile = pDir+"/speedtest1.db";
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const argv = ["speedtest1"];
|
||||
if(urlParams.has('flags')){
|
||||
argv.push(...(urlParams.get('flags').split(',')));
|
||||
}
|
||||
const capi = sqlite3.capi, wasm = sqlite3.wasm;
|
||||
//console.debug('sqlite3 =',sqlite3);
|
||||
const pDir = wasmfsDir(wasm);
|
||||
if(pDir){
|
||||
console.warn("Persistent storage:",pDir);
|
||||
}
|
||||
const scope = wasm.scopedAllocPush();
|
||||
let dbFile = pDir+"/speedtest1.db";
|
||||
const urlParams = new URL(self.location.href).searchParams;
|
||||
const argv = ["speedtest1"];
|
||||
if(urlParams.has('flags')){
|
||||
argv.push(...(urlParams.get('flags').split(',')));
|
||||
}
|
||||
|
||||
let forceSize = 0;
|
||||
let vfs, pVfs = 0;
|
||||
if(urlParams.has('vfs')){
|
||||
vfs = urlParams.get('vfs');
|
||||
pVfs = capi.sqlite3_vfs_find(vfs);
|
||||
if(!pVfs){
|
||||
log2('error',"Unknown VFS:",vfs);
|
||||
return;
|
||||
let forceSize = 0;
|
||||
let vfs, pVfs = 0;
|
||||
if(urlParams.has('vfs')){
|
||||
vfs = urlParams.get('vfs');
|
||||
pVfs = capi.sqlite3_vfs_find(vfs);
|
||||
if(!pVfs){
|
||||
log2('error',"Unknown VFS:",vfs);
|
||||
return;
|
||||
}
|
||||
argv.push("--vfs", vfs);
|
||||
log2('',"Using VFS:",vfs);
|
||||
if('kvvfs' === vfs){
|
||||
forceSize = 2 /* >2 is too big as of mid-2025 */;
|
||||
dbFile = 'session';
|
||||
log2('warning',"kvvfs VFS: forcing --size",forceSize,
|
||||
"and filename '"+dbFile+"'.");
|
||||
capi.sqlite3_js_kvvfs_clear(dbFile);
|
||||
}
|
||||
}
|
||||
argv.push("--vfs", vfs);
|
||||
log2('',"Using VFS:",vfs);
|
||||
if('kvvfs' === vfs){
|
||||
forceSize = 5
|
||||
/* --size > 2 is too big for local/session storage
|
||||
as of mid-2025, but kvvfs v2 (late 2025)
|
||||
lets us use transient storage. */;
|
||||
dbFile = 'transient';
|
||||
log2('warning',"kvvfs VFS: forcing --size",forceSize,
|
||||
"and filename '"+dbFile+"'.");
|
||||
capi.sqlite3_js_kvvfs_clear(dbFile);
|
||||
}
|
||||
}
|
||||
if(forceSize){
|
||||
argv.push('--size',forceSize);
|
||||
}else{
|
||||
[
|
||||
'size'
|
||||
].forEach(function(k){
|
||||
const v = urlParams.get(k);
|
||||
if(v && +v) argv.push('--'+k, urlParams[k]);
|
||||
});
|
||||
}
|
||||
argv.push(
|
||||
"--singlethread",
|
||||
//"--nomutex",
|
||||
//"--nosync",
|
||||
//"--memdb", // note that memdb trumps the filename arg
|
||||
"--nomemstat"
|
||||
);
|
||||
argv.push("--big-transactions"/*important for tests 410 and 510!*/,
|
||||
dbFile);
|
||||
console.log("argv =",argv);
|
||||
// These log messages are not emitted to the UI until after main() returns. Fixing that
|
||||
// requires moving the main() call and related cleanup into a timeout handler.
|
||||
if(pDir) wasm.sqlite3_wasm_vfs_unlink(pVfs,dbFile);
|
||||
log2('',"WASM heap size:",sqlite3.wasm.heap8().byteLength,"bytes");
|
||||
log2('',"WASM pointer size:",sqlite3.wasm.ptr.size);
|
||||
log2('',"Starting native app:\n ",argv.join(' '));
|
||||
log2('',"This will take a while and the browser might warn about the runaway JS.",
|
||||
"Give it time...");
|
||||
logList.length = 0;
|
||||
setTimeout(function(){
|
||||
wasm.xCall('wasm_main', argv.length,
|
||||
wasm.scopedAllocMainArgv(argv));
|
||||
wasm.scopedAllocPop(scope);
|
||||
if('kvvfs'===vfs){
|
||||
logList.unshift("KVVFS "+dbFile+" size = "+
|
||||
capi.sqlite3_js_kvvfs_size(dbFile));
|
||||
if(forceSize){
|
||||
argv.push('--size',forceSize);
|
||||
}else{
|
||||
[
|
||||
'size'
|
||||
].forEach(function(k){
|
||||
const v = urlParams.get(k);
|
||||
if(v) argv.push('--'+k, urlParams[k]);
|
||||
});
|
||||
}
|
||||
argv.push(
|
||||
"--singlethread",
|
||||
//"--nomutex",
|
||||
//"--nosync",
|
||||
//"--memdb", // note that memdb trumps the filename arg
|
||||
"--nomemstat"
|
||||
);
|
||||
argv.push("--big-transactions"/*important for tests 410 and 510!*/,
|
||||
dbFile);
|
||||
console.log("argv =",argv);
|
||||
// These log messages are not emitted to the UI until after main() returns. Fixing that
|
||||
// requires moving the main() call and related cleanup into a timeout handler.
|
||||
if(pDir) wasm.sqlite3_wasm_vfs_unlink(pVfs,dbFile);
|
||||
logList.unshift("Done running native main(). Output:");
|
||||
dumpLogList();
|
||||
log2('',"WASM heap size:",sqlite3.wasm.heap8().byteLength,"bytes");
|
||||
}, 50);
|
||||
log2('',"WASM pointer size:",sqlite3.wasm.ptr.size);
|
||||
log2('',"Starting native app:\n ",argv.join(' '));
|
||||
log2('',"This will take a while and the browser might warn about the runaway JS.",
|
||||
"Give it time...");
|
||||
logList.length = 0;
|
||||
setTimeout(function(){
|
||||
wasm.xCall('wasm_main', argv.length,
|
||||
wasm.scopedAllocMainArgv(argv));
|
||||
wasm.scopedAllocPop(scope);
|
||||
if('kvvfs'===vfs){
|
||||
logList.unshift("KVVFS "+dbFile+" size = "+
|
||||
capi.sqlite3_js_kvvfs_size(dbFile));
|
||||
}
|
||||
if(pDir) wasm.sqlite3_wasm_vfs_unlink(pVfs,dbFile);
|
||||
logList.unshift("Done running native main(). Output:");
|
||||
dumpLogList();
|
||||
log2('',"WASM heap size:",sqlite3.wasm.heap8().byteLength,"bytes");
|
||||
}, 50);
|
||||
}/*runTests()*/;
|
||||
|
||||
self.sqlite3TestModule.print = log;
|
||||
self.sqlite3TestModule.printErr = logErr;
|
||||
sqlite3InitModule.__isUnderTest = true;
|
||||
sqlite3InitModule(self.sqlite3TestModule).then(runTests);
|
||||
})();</script>
|
||||
</body>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='color-target'>sqlite3 tester #1: Worker thread (@bitness@-bit WASM)</h1>
|
||||
<div>Variants:
|
||||
<div>Variants (32-bit):
|
||||
conventional UI thread:
|
||||
(<a href='tester1.html' target='tester1.html'>32-bit</a>,
|
||||
<a href='tester1-64bit.html' target='tester1-64bit.html'>64-bit</a>),
|
||||
|
||||
+25
-583
@@ -153,10 +153,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
logClass('error',...args);
|
||||
};
|
||||
|
||||
const debug = (...args)=>{
|
||||
console.debug('tester1',...args);
|
||||
};
|
||||
|
||||
const toss = (...args)=>{
|
||||
error(...args);
|
||||
throw new Error(args.join(' '));
|
||||
@@ -214,9 +210,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
filter.test(error.message) passes. If it's a function, the test
|
||||
passes if filter(error) returns truthy. If it's a string, the
|
||||
test passes if the filter matches the exception message
|
||||
precisely. If filter is a number then it is compared against
|
||||
the resultCode property of the exception. In all other cases
|
||||
the test fails, throwing an Error.
|
||||
precisely. In all other cases the test fails, throwing an
|
||||
Error.
|
||||
|
||||
If it throws, msg is used as the error report unless it's falsy,
|
||||
in which case a default is used.
|
||||
@@ -230,9 +225,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
if(filter instanceof RegExp) pass = filter.test(err.message);
|
||||
else if(filter instanceof Function) pass = filter(err);
|
||||
else if('string' === typeof filter) pass = (err.message === filter);
|
||||
else if('number' === typeof filter) pass = (err.resultCode === filter);
|
||||
if(!pass){
|
||||
console.error("Filter",filter,"rejected exception",err);
|
||||
throw new Error(msg || ("Filter rejected this exception: <<"+err.message+">>"));
|
||||
}
|
||||
return this;
|
||||
@@ -520,7 +513,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
assert(wasmCtypes.structs[1/*sqlite3_io_methods*/
|
||||
].members.xFileSize.offset>0);
|
||||
[ /* Spot-check a handful of constants to make sure they got installed... */
|
||||
'SQLITE_SCHEMA','SQLITE_NULL','SQLITE_UTF8','SQLITE_UTF8_ZT',
|
||||
'SQLITE_SCHEMA','SQLITE_NULL','SQLITE_UTF8',
|
||||
'SQLITE_STATIC', 'SQLITE_DIRECTONLY',
|
||||
'SQLITE_OPEN_CREATE', 'SQLITE_OPEN_DELETEONCLOSE'
|
||||
].forEach((k)=>T.assert('number' === typeof capi[k]));
|
||||
@@ -2635,7 +2628,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
.assert(wasm.isPtr(tmplMod.$xRowid))
|
||||
.assert(wasm.isPtr(tmplMod.$xCreate))
|
||||
.assert(tmplMod.$xCreate === tmplMod.$xConnect,
|
||||
"setupModule() must make these equivalent and "+
|
||||
"setup() must make these equivalent and "+
|
||||
"installMethods() must avoid re-compiling identical functions");
|
||||
tmplMod.$xCreate = wasm.ptr.null /* make tmplMod eponymous-only */;
|
||||
let rc = capi.sqlite3_create_module(
|
||||
@@ -2886,69 +2879,25 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('kvvfs')
|
||||
.t({
|
||||
name: 'kvvfs v1 API availability',
|
||||
name: 'kvvfs is disabled in worker',
|
||||
predicate: ()=>(isWorker() || "test is only valid in a Worker"),
|
||||
test: function(sqlite3){
|
||||
const capi = sqlite3.capi;
|
||||
if( isUIThread() ){
|
||||
T.assert( !!capi.sqlite3_js_kvvfs_size )
|
||||
.assert( !!capi.sqlite3_js_kvvfs_clear );
|
||||
}else{
|
||||
/* Historical behaviour retained not for compatibility but
|
||||
to help avoid some confusion between the v1 and v2 kvvfs
|
||||
APIs (namely in how the v1 variants handle empty
|
||||
strings). */
|
||||
T.assert( !capi.sqlite3_js_kvvfs_clear )
|
||||
.assert( !capi.sqlite3_js_kvvfs_size );
|
||||
}
|
||||
const k = sqlite3.kvvfs;
|
||||
T.assert( k && 'object'===typeof k );
|
||||
for(const n of ['reserve', 'import', 'export',
|
||||
'unlink', 'listen', 'unlisten',
|
||||
'exists',
|
||||
'estimateSize', 'clear'] ){
|
||||
T.assert( k[n] instanceof Function );
|
||||
}
|
||||
|
||||
if( 0 ){
|
||||
const scope = wasm.scopedAllocPush();
|
||||
try{
|
||||
const pg = [
|
||||
"53514C69746520666F726D61742033b20b0101b402020d02d02l01d0",
|
||||
"4l01jb02b2E91E00Dd011FD3b1FD3dxl2B010617171701377461626C",
|
||||
"656B767666736B7676667302435245415445205441424C45206B76766673286129"
|
||||
].join('');
|
||||
const n = pg.length;
|
||||
const pI = wasm.scopedAlloc( n+1 );
|
||||
const nO = 8192 * 2;
|
||||
const pO = wasm.scopedAlloc( nO );
|
||||
const heap = wasm.heap8u();
|
||||
let i;
|
||||
for( i=0; i<n; ++i ){
|
||||
heap[wasm.ptr.add(pI, i)] = pg.codePointAt(i) & 0xff;
|
||||
}
|
||||
heap[wasm.ptr.add(pI, i)] = 0;
|
||||
const rc = wasm.exports.sqlite3__wasm_kvvfs_decode(pI, pO, nO);
|
||||
const u = heap.slice(pO, wasm.ptr.add(pO,rc));
|
||||
debug("decode rc=", rc, u);
|
||||
}finally{
|
||||
wasm.scopedAllocPop(scope);
|
||||
}
|
||||
}
|
||||
T.assert(
|
||||
!capi.sqlite3_vfs_find('kvvfs'),
|
||||
"Expecting kvvfs to be unregistered."
|
||||
);
|
||||
}
|
||||
}/*kvvfs API availability*/)
|
||||
})
|
||||
.t({
|
||||
name: 'kvvfs sessionStorage',
|
||||
predicate: ()=>(globalThis.sessionStorage || "sessionStorage is unavailable"),
|
||||
name: 'kvvfs in main thread',
|
||||
predicate: ()=>(isUIThread()
|
||||
|| "local/sessionStorage are unavailable in a Worker"),
|
||||
test: function(sqlite3){
|
||||
const JDb = sqlite3.oo1.JsStorageDb;
|
||||
const filename = this.kvvfsDbFile = 'session';
|
||||
const pVfs = capi.sqlite3_vfs_find('kvvfs');
|
||||
T.assert(looksLikePtr(pVfs));
|
||||
let x = sqlite3.kvvfs.internal.storageForZClass('session');
|
||||
T.assert( 0 === x.files.length )
|
||||
.assert( globalThis.sessionStorage===x.storage )
|
||||
.assert( 'kvvfs-session-' === x.keyPrefix );
|
||||
const filename = this.kvvfsDbFile = 'session';
|
||||
const unlink = this.kvvfsUnlink = ()=>sqlite3.kvvfs.clear(filename);
|
||||
const JDb = this.JDb = sqlite3.oo1.JsStorageDb;
|
||||
const unlink = this.kvvfsUnlink = ()=>JDb.clearStorage(this.kvvfsDbFile);
|
||||
unlink();
|
||||
let db = new JDb(filename);
|
||||
try {
|
||||
@@ -2956,8 +2905,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
'create table kvvfs(a);',
|
||||
'insert into kvvfs(a) values(1),(2),(3)'
|
||||
]);
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'))
|
||||
.assert( db.storageSize() > 0, "Db size counting is broken" );
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
|
||||
db.close();
|
||||
db = undefined;
|
||||
db = new JDb(filename);
|
||||
@@ -2966,523 +2914,17 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
}finally{
|
||||
if( db ) db.close();
|
||||
}
|
||||
//console.debug("sessionStorage",globalThis.sessionStorage);
|
||||
}
|
||||
}/*kvvfs sanity checks*/)
|
||||
.t({
|
||||
name: 'transient kvvfs',
|
||||
//predicate: ()=>false,
|
||||
test: function(sqlite3){
|
||||
const filename = '.' /* preinstalled instance */;
|
||||
const JDb = sqlite3.oo1.JsStorageDb;
|
||||
const DB = sqlite3.oo1.DB;
|
||||
T.mustThrowMatching(()=>new JDb(""), capi.SQLITE_MISUSE);
|
||||
T.mustThrowMatching(()=>{
|
||||
new JDb("this\ns an illegal - contains control characters");
|
||||
/* We don't have a way to get error strings from xOpen()
|
||||
to this point? xOpen() does not have a handle to the
|
||||
db and SQLite is not calling xGetLastError() to fetch
|
||||
the error string. */
|
||||
}, capi.SQLITE_RANGE);
|
||||
T.mustThrowMatching(()=>{new JDb("foo-journal");},
|
||||
capi.SQLITE_MISUSE);
|
||||
T.mustThrowMatching(()=>{new JDb("foo-wal");},
|
||||
capi.SQLITE_MISUSE);
|
||||
T.mustThrowMatching(()=>{new JDb("foo-shm");},
|
||||
capi.SQLITE_MISUSE);
|
||||
T.mustThrowMatching(()=>{
|
||||
new JDb("01234567890123456789"+
|
||||
"01234567890123456789"+
|
||||
"01234567890123456789"+
|
||||
"01234567890123456789"+
|
||||
"01234567890123456789"+
|
||||
"01234567890123456789"+
|
||||
"0"/*too long*/);
|
||||
}, capi.SQLITE_RANGE);
|
||||
{
|
||||
const name = "01234567890123456789012" /* max name length */;
|
||||
(new JDb(name)).close();
|
||||
T.assert( sqlite3.kvvfs.unlink(name) );
|
||||
}
|
||||
|
||||
sqlite3.kvvfs.clear(filename);
|
||||
let db = new JDb(filename);
|
||||
const sqlSetup = [
|
||||
'create table kvvfs(a);',
|
||||
'insert into kvvfs(a) values(1),(2),(3)'
|
||||
];
|
||||
try {
|
||||
T.assert( 0===db.storageSize(), "expecting 0 storage size" );
|
||||
T.mustThrowMatching(()=>db.clearStorage(), /in-use/);
|
||||
//db.clearStorage();
|
||||
T.assert( 0===db.storageSize(), "expecting 0 storage size" );
|
||||
db.exec(sqlSetup);
|
||||
T.assert( 0<db.storageSize(), "expecting non-0 db size" );
|
||||
T.mustThrowMatching(()=>db.clearStorage(), /in-use/);
|
||||
//db.clearStorage(/*wiping everything out from under it*/);
|
||||
T.assert( 0<db.storageSize(), "expecting non-0 storage size" );
|
||||
//db.exec(sqlSetup/*that actually worked*/);
|
||||
/* Clearing the storage out from under the db does actually
|
||||
work so long as we re-initialize it before reading.
|
||||
It is tentatively disallowed for sanity's sake rather
|
||||
than it not working.
|
||||
*/
|
||||
T.assert( 0<db.storageSize(), "expecting non-0 db size" );
|
||||
const close = ()=>{
|
||||
db.close();
|
||||
db = undefined;
|
||||
};
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
|
||||
close();
|
||||
|
||||
const exportDb = sqlite3.kvvfs.export;
|
||||
db = new JDb(filename);
|
||||
db.exec('insert into kvvfs(a) values(4),(5),(6)');
|
||||
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
|
||||
const exp = exportDb({name:filename,includeJournal:true});
|
||||
T.assert( filename===exp.name, "Broken export filename" )
|
||||
.assert( exp?.size > 0, "Missing db size" )
|
||||
.assert( exp?.pages?.length > 0, "Missing db pages" );
|
||||
console.debug("kvvfs to Object:",exp);
|
||||
close();
|
||||
|
||||
const dbFileRaw = 'file:new-storage?vfs=kvvfs&delete-on-close=1';
|
||||
db = new DB({
|
||||
filename: dbFileRaw,
|
||||
//flags: 'ct'
|
||||
});
|
||||
db.exec(sqlSetup);
|
||||
const dbFilename = db.dbFilename();
|
||||
//console.warn("db.dbFilename() =",dbFilename);
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
|
||||
debug("kvvfs to Object:",exportDb(dbFilename));
|
||||
const n = sqlite3.kvvfs.estimateSize( dbFilename );
|
||||
T.assert( n>0, "Db size count failed" );
|
||||
|
||||
if( 1 ){
|
||||
// Concurrent open of that same name uses the same storage
|
||||
const x = new JDb(dbFilename);
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
|
||||
x.close();
|
||||
}
|
||||
close();
|
||||
// When the final instance of a name is closed, the storage
|
||||
// disappears...
|
||||
T.mustThrowMatching(function(){
|
||||
/* Ensure that 'new-storage' was deleted when its refcount
|
||||
went to 0, because of its 'transient' flag. By default
|
||||
the objects are retained, like a filesystem would. */
|
||||
let ddb = new JDb(dbFilename);
|
||||
try{ddb.selectValue('select a from kvvfs')}
|
||||
finally{ddb.close()}
|
||||
}, /no such table: kvvfs/);
|
||||
}finally{
|
||||
if( db ) db.close();
|
||||
}
|
||||
}
|
||||
}/*transient kvvfs*/)
|
||||
.t({
|
||||
name: 'concurrent transient kvvfs',
|
||||
//predicate: ()=>false,
|
||||
test: function(sqlite3){
|
||||
const filename = 'myStorage';
|
||||
const kvvfs = sqlite3.kvvfs;
|
||||
const DB = sqlite3.oo1.DB;
|
||||
const JDb = sqlite3.oo1.JsStorageDb;
|
||||
let db;
|
||||
let duo;
|
||||
let q1, q2;
|
||||
const sqlSetup = [
|
||||
'create table kvvfs(a);',
|
||||
'insert into kvvfs(a) values(1),(2),(3)'
|
||||
];
|
||||
const sqlCount = 'select count(*) from kvvfs';
|
||||
|
||||
try {
|
||||
const exportDb = sqlite3.kvvfs.export;
|
||||
const dbFileRaw = 'file:'+filename+'?vfs=kvvfs&delete-on-close=1';
|
||||
sqlite3.kvvfs.clear(filename);
|
||||
db = new DB(dbFileRaw);
|
||||
db.exec(sqlSetup);
|
||||
T.assert(3 === db.selectValue(sqlCount));
|
||||
|
||||
duo = new JDb(filename);
|
||||
duo.exec('insert into kvvfs(a) values(4),(5),(6)');
|
||||
T.assert(6 === db.selectValue(sqlCount));
|
||||
const expOpt = {
|
||||
name: filename,
|
||||
decodePages: true
|
||||
};
|
||||
let exp = exportDb(expOpt);
|
||||
let expectRows = 6;
|
||||
debug("exported db",exp);
|
||||
db.close();
|
||||
T.assert(expectRows === duo.selectValue(sqlCount));
|
||||
duo.close();
|
||||
T.mustThrowMatching(function(){
|
||||
let ddb = new JDb(filename);
|
||||
try{ddb.selectValue('select a from kvvfs')}
|
||||
finally{ddb.close()}
|
||||
}, /.*no such table: kvvfs.*/);
|
||||
|
||||
T.assert( kvvfs.unlink(filename) )
|
||||
.assert( !kvvfs.exists(filename) );
|
||||
|
||||
const importDb = sqlite3.kvvfs.import;
|
||||
duo = new JDb(dbFileRaw);
|
||||
T.mustThrowMatching(()=>importDb(exp,true), /.*in use.*/);
|
||||
duo.close();
|
||||
importDb(exp, true);
|
||||
duo = new JDb(dbFileRaw);
|
||||
T.assert(expectRows === duo.selectValue(sqlCount));
|
||||
let newCount;
|
||||
try{
|
||||
duo.transaction(()=>{
|
||||
duo.exec("insert into kvvfs(a) values(7)");
|
||||
newCount = duo.selectValue(sqlCount);
|
||||
T.assert(false, "rolling back");
|
||||
});
|
||||
}catch(e){/*ignored*/}
|
||||
T.assert(7===newCount, "Unexpected row count before rollback")
|
||||
.assert(expectRows === duo.selectValue(sqlCount),
|
||||
"Unexpected row count after rollback");
|
||||
duo.close();
|
||||
|
||||
T.assert( kvvfs.unlink(filename) )
|
||||
.assert( !kvvfs.exists(filename) );
|
||||
|
||||
importDb(exp, true);
|
||||
db = new JDb({
|
||||
filename,
|
||||
flags: 'c'
|
||||
/* BUG: without the 'c' flag, the db works until we try to
|
||||
vacuum, at which point it fails with "read only db". */
|
||||
});
|
||||
duo = new JDb(filename);
|
||||
T.assert(expectRows === duo.selectValue(sqlCount));
|
||||
const sqlIns1 = "insert into kvvfs(a) values(?)";
|
||||
q1 = db.prepare(sqlIns1);
|
||||
q2 = duo.prepare(sqlIns1);
|
||||
if( 0 ){
|
||||
q1.bind('from q1').stepFinalize();
|
||||
++expectRows;
|
||||
T.assert(expectRows === duo.selectValue(sqlCount),
|
||||
"Unexpected record count.");
|
||||
q2.bind('from q1').stepFinalize();
|
||||
++expectRows;
|
||||
}else{
|
||||
q1.bind('from q1');
|
||||
T.assert(capi.SQLITE_DONE===capi.sqlite3_step(q1),
|
||||
"Unexpected step result");
|
||||
++expectRows;
|
||||
T.assert(expectRows === duo.selectValue(sqlCount),
|
||||
"Unexpected record count.");
|
||||
q2.bind('from q1').step();
|
||||
++expectRows;
|
||||
}
|
||||
T.assert(expectRows === db.selectValue(sqlCount),
|
||||
"Unexpected record count.");
|
||||
q1.finalize();
|
||||
q2.finalize();
|
||||
|
||||
if( 1 ){
|
||||
debug("Begin vacuum/page size test...");
|
||||
const defaultPageSize = 1024 * 8 /* build-time default */;
|
||||
const pageSize = 0
|
||||
? defaultPageSize
|
||||
: 1024 * 16 /* any valid value other than the default */;
|
||||
if( 0 ){
|
||||
debug("Export before vacuum", exportDb(expOpt));
|
||||
debug("page size before vacuum",
|
||||
db.selectArray(
|
||||
"select page_size from pragma_page_size()"
|
||||
));
|
||||
}
|
||||
//kvvfs.log.xFileControl = true;
|
||||
//kvvfs.log.xAccess = true;
|
||||
db.exec([
|
||||
"BEGIN;",
|
||||
"insert into kvvfs(a) values(randomblob(16000/*>pg size*/));",
|
||||
"COMMIT;",
|
||||
"delete from kvvfs where octet_length(a)>100;",
|
||||
"pragma page_size="+pageSize+";",
|
||||
"vacuum;",
|
||||
"select 1;"
|
||||
]);
|
||||
const expectPageSize = kvvfs.internal.disablePageSizeChange
|
||||
? defaultPageSize
|
||||
: pageSize;
|
||||
const gotPageSize = db.selectValue(
|
||||
"select page_size from pragma_page_size()"
|
||||
);
|
||||
T.assert(+gotPageSize === expectPageSize,
|
||||
"Expecting page size",expectPageSize,
|
||||
"got",gotPageSize);
|
||||
T.assert(expectRows === duo.selectValue(sqlCount),
|
||||
"Unexpected record count.");
|
||||
kvvfs.log.xAccess = kvvfs.log.xFileControl = false;
|
||||
T.assert(expectRows === duo.selectValue(sqlCount),
|
||||
"Unexpected record count.");
|
||||
exp = exportDb(expOpt);
|
||||
debug("Exported page-expanded db",exp);
|
||||
if( 0 ){
|
||||
debug("vacuumed export",exp);
|
||||
}
|
||||
debug("End vacuum/page size test.");
|
||||
}else{
|
||||
expectRows = 6;
|
||||
}
|
||||
|
||||
db.close();
|
||||
duo.close();
|
||||
T.assert( kvvfs.unlink(exp.name) )
|
||||
.assert( !kvvfs.exists(exp.name) );
|
||||
importDb(exp);
|
||||
T.assert( kvvfs.exists(exp.name) );
|
||||
db = new JDb(exp.name);
|
||||
//debug("column count after export",db.selectValue(sqlCount));
|
||||
T.assert(expectRows === db.selectValue(sqlCount),
|
||||
"Unexpected record count.");
|
||||
|
||||
/*
|
||||
TODO: more advanced concurrent use tests, e.g. looping
|
||||
over a query in one connection while writing from
|
||||
another. Currently that will probably corrupt the db, and
|
||||
kvvfs's journaling does not support multiple journals per
|
||||
storage unit. We need to test the locking and fix it as
|
||||
appropriate.
|
||||
*/
|
||||
}finally{
|
||||
q1?.finalize?.();
|
||||
q2?.finalize?.();
|
||||
db?.close?.();
|
||||
duo?.close?.();
|
||||
}
|
||||
}
|
||||
}/*concurrent transient kvvfs*/)
|
||||
|
||||
.t({
|
||||
name: 'kvvfs listeners (experiment)',
|
||||
test: function(sqlite3){
|
||||
const kvvfs = sqlite3.kvvfs;
|
||||
const filename = 'listen';
|
||||
let db;
|
||||
try {
|
||||
const DB = sqlite3.oo1.DB;
|
||||
const sqlSetup = [
|
||||
'create table kvvfs(a);',
|
||||
'insert into kvvfs(a) values(1),(2),(3)'
|
||||
];
|
||||
const sqlCount = "select count(*) from kvvfs";
|
||||
const sqlSelectSchema = "select * from sqlite_schema";
|
||||
const counts = Object.create(null);
|
||||
const incr = (key)=>counts[key] = 1 + (counts[key] ?? 0);
|
||||
const pglog = Object.assign(Object.create(null),{
|
||||
pages: [],
|
||||
jrnl: undefined,
|
||||
size: undefined,
|
||||
includeJournal: false,
|
||||
decodePages: true,
|
||||
exception: new Error("Testing that exceptions from listeners do not interfere")
|
||||
});
|
||||
const toss = ()=>{
|
||||
if( pglog.exception ){
|
||||
const e = pglog.exception;
|
||||
delete pglog.exception;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const listener = {
|
||||
storage: filename,
|
||||
reserve: true,
|
||||
includeJournal: pglog.includeJournal,
|
||||
decodePages: pglog.decodePages,
|
||||
events: {
|
||||
/**
|
||||
These may be async but must not be in this case
|
||||
because we can't test their result without a lot of
|
||||
hoop-jumping if they are. Kvvfs calls these
|
||||
asynchronously, though.
|
||||
*/
|
||||
'open': (ev)=>{
|
||||
//console.warn('open',ev);
|
||||
incr(ev.type);
|
||||
T.assert(filename===ev.storageName)
|
||||
.assert('number'===typeof ev.data);
|
||||
},
|
||||
'close': (ev)=>{
|
||||
//console.warn('close',ev);
|
||||
incr(ev.type);
|
||||
T.assert('number'===typeof ev.data);
|
||||
toss();
|
||||
},
|
||||
'delete': (ev)=>{
|
||||
//console.warn('delete',ev);
|
||||
incr(ev.type);
|
||||
T.assert('string'===typeof ev.data);
|
||||
switch(ev.data){
|
||||
case 'jrnl':
|
||||
T.assert(pglog.includeJournal);
|
||||
pglog.jrnl = null;
|
||||
break;
|
||||
default:{
|
||||
const n = +ev.data;
|
||||
T.assert( n>0, "Expecting positive db page number" );
|
||||
if( n < pglog.pages.length ){
|
||||
pglog.size = undefined;
|
||||
}
|
||||
pglog.pages[n] = undefined;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
'sync': (ev)=>{
|
||||
incr(ev.data ? 'xSync' : 'xFileControlSync');
|
||||
},
|
||||
'write': (ev)=>{
|
||||
//console.warn('write',ev);
|
||||
incr(ev.type);
|
||||
T.assert(Array.isArray(ev.data));
|
||||
const key = ev.data[0], val = ev.data[1];
|
||||
T.assert('string'===typeof key);
|
||||
switch( key ){
|
||||
case 'jrnl':
|
||||
T.assert(pglog.includeJournal);
|
||||
pglog.jrnl = val;
|
||||
break;
|
||||
case 'sz':{
|
||||
const sz = +val;
|
||||
T.assert( sz>0, "Expecting a db page number" );
|
||||
if( sz < pglog.sz ){
|
||||
pglog.pages.length = sz / pglog.pages.length;
|
||||
}
|
||||
pglog.size = sz;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
T.assert( +key>0, "Expecting a positive db page number" );
|
||||
pglog.pages[+key] = val;
|
||||
if( pglog.decodePages ){
|
||||
T.assert( val instanceof Uint8Array );
|
||||
}else{
|
||||
T.assert( 'string'===typeof val );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kvvfs.listen(listener);
|
||||
const dbFileRaw = 'file:'+filename+'?vfs=kvvfs&delete-on-close=1';
|
||||
const expOpt = {
|
||||
name: filename,
|
||||
//decodePages: true
|
||||
};
|
||||
db = new DB(dbFileRaw);
|
||||
db.exec(sqlSetup);
|
||||
T.assert(db.selectObjects(sqlSelectSchema)?.length>0,
|
||||
"Unexpected empty schema");
|
||||
db.close();
|
||||
debug("kvvfs listener counts:",counts);
|
||||
T.assert( counts.open );
|
||||
T.assert( counts.close );
|
||||
T.assert( listener.includeJournal ? counts.delete : !counts.delete );
|
||||
T.assert( counts.write );
|
||||
T.assert( counts.xSync );
|
||||
T.assert( counts.xFileControlSync>=counts.xSync );
|
||||
T.assert( counts.open===counts.close );
|
||||
T.assert( pglog.includeJournal
|
||||
? (null===pglog.jrnl)
|
||||
: (undefined===pglog.jrnl),
|
||||
"Unexpected pglog.jrnl value: "+pglog.jrnl );
|
||||
if( 1 ){
|
||||
T.assert(undefined===pglog.pages[0], "Expecting empty slot 0");
|
||||
pglog.pages.shift();
|
||||
//debug("kvvfs listener pageLog", pglog);
|
||||
}
|
||||
const before = JSON.stringify(counts);
|
||||
T.assert( kvvfs.unlisten(listener) );
|
||||
T.assert( !kvvfs.unlisten(listener) );
|
||||
db = new DB(dbFileRaw);
|
||||
T.assert( db.selectObjects(sqlSelectSchema)?.length>0 );
|
||||
const exp = kvvfs.export(expOpt);
|
||||
const expectRows = db.selectValue(sqlCount);
|
||||
db.exec("delete from kvvfs");
|
||||
db.close();
|
||||
const after = JSON.stringify(counts);
|
||||
T.assert( before===after, "Expecting no events after unlistening." );
|
||||
if( 0 ){
|
||||
exp = kvvfs.export(expOpt);
|
||||
debug("Post-delete export:",exp);
|
||||
}
|
||||
if( 1 ){
|
||||
// Replace the storage with the pglog state...
|
||||
const bogoExp = {
|
||||
name: filename,
|
||||
size: pglog.size,
|
||||
timestamp: Date.now(),
|
||||
pages: pglog.pages
|
||||
};
|
||||
//debug("exp",exp);
|
||||
//debug("bogoExp",bogoExp);
|
||||
kvvfs.import(bogoExp, true);
|
||||
//debug("Re-exported", kvvfs.export(expOpt));
|
||||
db = new DB(dbFileRaw);
|
||||
// Failing on the next line despite exports looking good
|
||||
T.assert(db.selectObjects(sqlSelectSchema)?.length>0,
|
||||
"Empty schema on imported db");
|
||||
T.assert(expectRows===db.selectValue(sqlCount));
|
||||
db.close();
|
||||
}
|
||||
}finally{
|
||||
db?.close?.();
|
||||
kvvfs.unlink(filename);
|
||||
}
|
||||
}
|
||||
})/*kvvfs listeners */
|
||||
|
||||
.t({
|
||||
name: 'kvvfs vtab',
|
||||
predicate: (sqlite3)=>!!sqlite3.kvvfs.create_module,
|
||||
test: function(sqlite3){
|
||||
const kvvfs = sqlite3.kvvfs;
|
||||
const db = new sqlite3.oo1.DB();
|
||||
const db2 = new sqlite3.oo1.DB('file:foo?vfs=kvvfs&delete-on-close=1');
|
||||
try{
|
||||
kvvfs.create_module(db);
|
||||
let rc = db.selectObjects("select * from sqlite_kvvfs order by name");
|
||||
debug("sqlite_kvvfs vtab:", rc);
|
||||
const nDb = rc.length;
|
||||
rc = db.selectObject("select * from sqlite_kvvfs where name='foo'");
|
||||
T.assert(rc, "Expecting foo storage record")
|
||||
.assert('foo'===rc.name, "Unexpected name")
|
||||
.assert(1===rc.nRef, "Unexpected refcount");
|
||||
db2.close();
|
||||
rc = db.selectObjects("select * from sqlite_kvvfs");
|
||||
T.assert( !rc.filter((o)=>o.name==='foo').length,
|
||||
"Expecting foo storage to be gone");
|
||||
debug("sqlite_kvvfs vtab:", rc);
|
||||
T.assert( rc.length+1 === nDb,
|
||||
"Unexpected storage count: got",rc.length,"expected",nDb);
|
||||
}finally{
|
||||
db.close();
|
||||
db2.close();
|
||||
}
|
||||
}
|
||||
})/* kvvfs vtab */
|
||||
|
||||
//#if enable-see
|
||||
.t({
|
||||
name: 'kvvfs SEE encryption in sessionStorage',
|
||||
predicate: ()=>(!!globalThis.sessionStorage
|
||||
|| "sessionStorage is not available"),
|
||||
name: 'kvvfs with SEE encryption',
|
||||
predicate: ()=>(isUIThread()
|
||||
|| "Only available in main thread."),
|
||||
test: function(sqlite3){
|
||||
const JDb = sqlite3.oo1.JsStorageDb;
|
||||
T.seeBaseCheck(JDb,
|
||||
(isInit)=>return {filename: "session"},
|
||||
()=>JDb.clearStorage('session'));
|
||||
T.seeBaseCheck(sqlite3.oo1.JsStorageDb, (isInit)=>{
|
||||
return {filename: "session"};
|
||||
}, ()=>this.kvvfsUnlink());
|
||||
}
|
||||
})/*kvvfs with SEE*/
|
||||
//#endif enable-see
|
||||
@@ -3497,7 +2939,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
let countCommit = 0, countRollback = 0;;
|
||||
const db = new sqlite3.oo1.DB(':memory:',1 ? 'c' : 'ct');
|
||||
let rc = capi.sqlite3_commit_hook(db, (p)=>{
|
||||
//debug("commit hook",arguments);
|
||||
//console.debug("commit hook",arguments);
|
||||
++countCommit;
|
||||
return (17 == p) ? 0 : capi.SQLITE_ERROR;
|
||||
}, 17);
|
||||
|
||||
@@ -1796,7 +1796,7 @@ TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
|
||||
testfixture$(T.exe): $(T.tcl.env.sh) has_tclsh85 $(TESTFIXTURE_SRC)
|
||||
$(T.link.tcl) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-o $@ $(TESTFIXTURE_SRC) \
|
||||
$$TCL_LIB_SPEC $$TCL_INCLUDE_SPEC $$TCL_LIBS \
|
||||
$$TCL_LIB_SPEC $$TCL_INCLUDE_SPEC \
|
||||
$(LDFLAGS.libsqlite3)
|
||||
|
||||
coretestprogs: testfixture$(B.exe) sqlite3$(B.exe)
|
||||
@@ -1863,15 +1863,6 @@ mdevtest: srctree-check has_tclsh85
|
||||
sdevtest: has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest $(TSTRNNR_OPTS)
|
||||
|
||||
retest: has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl retest
|
||||
|
||||
errors:
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl errors
|
||||
|
||||
status:
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl status -d 2
|
||||
|
||||
# Like releasetest, except it omits srctree-check and verify-source so
|
||||
# that it can be used on a modified source tree.
|
||||
#
|
||||
@@ -1971,6 +1962,24 @@ sqlite3_expert$(T.exe): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqli
|
||||
$(TOP)/ext/expert/expert.c sqlite3.c -o sqlite3_expert $(LDFLAGS.libsqlite3)
|
||||
xbin: sqlite3_expert$(T.exe)
|
||||
|
||||
CHECKER_DEPS =\
|
||||
$(TOP)/tool/mkccode.tcl \
|
||||
sqlite3.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)/ext/repair/sqlite3_checker.tcl \
|
||||
$(TOP)/ext/repair/checkindex.c \
|
||||
$(TOP)/ext/repair/checkfreelist.c \
|
||||
$(TOP)/ext/misc/btreeinfo.c \
|
||||
$(TOP)/ext/repair/sqlite3_checker.c.in
|
||||
|
||||
sqlite3_checker.c: $(CHECKER_DEPS)
|
||||
$(B.tclsh) $(TOP)/tool/mkccode.tcl $(TOP)/ext/repair/sqlite3_checker.c.in >$@
|
||||
|
||||
sqlite3_checker$(T.exe): $(T.tcl.env.sh) sqlite3_checker.c
|
||||
$(T.link.tcl) sqlite3_checker.c -o $@ $$TCL_INCLUDE_SPEC \
|
||||
$$TCL_LIB_SPEC $(LDFLAGS.libsqlite3)
|
||||
xbin: sqlite3_checker$(T.exe)
|
||||
|
||||
dbdump$(T.exe): $(TOP)/ext/misc/dbdump.c sqlite3.o
|
||||
$(T.link) -DDBDUMP_STANDALONE -o $@ \
|
||||
$(TOP)/ext/misc/dbdump.c sqlite3.o $(LDFLAGS.libsqlite3)
|
||||
@@ -2000,10 +2009,6 @@ showshm$(T.exe): $(TOP)/tool/showshm.c
|
||||
$(T.link) -o $@ $(TOP)/tool/showshm.c $(LDFLAGS.configure)
|
||||
xbin: showshm$(T.exe)
|
||||
|
||||
showtmlog$(T.exe): $(TOP)/tool/showtmlog.c
|
||||
$(T.link) -o $@ $(TOP)/tool/showtmlog.c $(LDFLAGS.configure)
|
||||
xbin: showshm$(T.exe)
|
||||
|
||||
index_usage$(T.exe): $(TOP)/tool/index_usage.c sqlite3.o
|
||||
$(T.link) $(SHELL_OPT) -o $@ $(TOP)/tool/index_usage.c sqlite3.o \
|
||||
$(LDFLAGS.libsqlite3)
|
||||
@@ -2102,19 +2107,16 @@ src-archives: sqlite-amalgamation.zip amalgamation-tarball sqlite-src.zip
|
||||
|
||||
# Build a ZIP archive containing various command-line tools.
|
||||
#
|
||||
tool-zip: sqlite3$(T.exe) sqldiff$(T.exe) \
|
||||
tool-zip: testfixture$(T.exe) sqlite3$(T.exe) sqldiff$(T.exe) \
|
||||
sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe) $(TOP)/tool/mktoolzip.tcl
|
||||
strip sqlite3$(T.exe) sqldiff$(T.exe) sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe)
|
||||
$(TCLSH_CMD) $(TOP)/tool/mktoolzip.tcl
|
||||
|
||||
./testfixture$(T.exe) $(TOP)/tool/mktoolzip.tcl
|
||||
snapshot-zip: testfixture$(T.exe) sqlite3$(T.exe) sqldiff$(T.exe) \
|
||||
sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe) $(TOP)/tool/mktoolzip.tcl
|
||||
strip sqlite3$(T.exe) sqldiff$(T.exe) sqlite3_analyzer$(T.exe) sqlite3_rsync$(T.exe)
|
||||
$(TCLSH_CMD) $(TOP)/tool/mktoolzip.tcl --snapshot
|
||||
|
||||
./testfixture$(T.exe) $(TOP)/tool/mktoolzip.tcl --snapshot
|
||||
clean-tool-zip:
|
||||
rm -f sqlite-tools-*.zip
|
||||
|
||||
clean: clean-tool-zip
|
||||
|
||||
#
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
branch realloc64
|
||||
tag realloc64
|
||||
branch remove-sync-dataonly
|
||||
tag remove-sync-dataonly
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
a391f5646926787fd9a004225ea406b61d20f282c852c0282fd26cada644b601
|
||||
caefe4c746599b22aec446f3aeb296e53edaf8c54f8ae803381e8c21d5e29ab4
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user