Compare commits
384 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b041fb97a | |||
| b7ceffdadf | |||
| 66b3ed318b | |||
| 878db7b099 | |||
| 7119a6c16b | |||
| 95f35b64da | |||
| 62d96919f6 | |||
| 2a9254cf2c | |||
| 9e59c06fb1 | |||
| 853520ddbf | |||
| 38b4158aba | |||
| 8f1f9ef09f | |||
| 8cd30e3f5b | |||
| f4382b2415 | |||
| 18e2ace468 | |||
| 697c3fb25b | |||
| 63558eae54 | |||
| 6ba175f0c8 | |||
| a55901a27c | |||
| 0be2dd9a1b | |||
| 29057f3d18 | |||
| 57b32ef924 | |||
| 3c90b86372 | |||
| 4e34558f1b | |||
| 118ac6a7ad | |||
| 7d24e6b080 | |||
| 415acd09d7 | |||
| 1d9db01065 | |||
| 253695caf3 | |||
| 5e419c1048 | |||
| 38f2c5f81c | |||
| 8a894cd804 | |||
| 7f0e0c7dbf | |||
| db467f5777 | |||
| f8305e4616 | |||
| 8513eb6ba8 | |||
| 0e8cbd5e78 | |||
| 704561f346 | |||
| ae2a4acf8d | |||
| 38b31a93b3 | |||
| eaa50b8660 | |||
| 87dce45f96 | |||
| ff60709d68 | |||
| 3b204ec8bb | |||
| 61e3067529 | |||
| d70fbffc1a | |||
| f8cafd7b11 | |||
| a95620c141 | |||
| d87a4f8bdd | |||
| dbff02abc5 | |||
| 11e1defc8f | |||
| 3d56d59adc | |||
| 7154803d4c | |||
| df9b488799 | |||
| 15e05eab32 | |||
| 9d3047bd0d | |||
| 2b30518804 | |||
| 33f0a3ed13 | |||
| 762946b236 | |||
| 8550e4a9f8 | |||
| 99f50dd219 | |||
| 7316b6c072 | |||
| d61d934f9b | |||
| d0d3182fa8 | |||
| e92f8e565b | |||
| 5c6dc45a10 | |||
| 74c8e234cd | |||
| 5735921b0f | |||
| 6677cfeda7 | |||
| 40b831851e | |||
| 6df1de1c73 | |||
| 0b8c8c861b | |||
| f7c96ee98b | |||
| 165daef043 | |||
| f1fb5ce10f | |||
| dc3bec34a6 | |||
| 2a4a4ec408 | |||
| 79254dc363 | |||
| 0c8144ab4f | |||
| 27119c56bd | |||
| a9c8f7cf34 | |||
| 923423c16f | |||
| 7a41b48cf9 | |||
| 38b26d82e6 | |||
| cd889c7a88 | |||
| 882aba4090 | |||
| 9c2f068ebd | |||
| dbab768d07 | |||
| a40f6eb80e | |||
| a8cf79e322 | |||
| 5600adf1b1 | |||
| cf25c16a54 | |||
| 8755e695c5 | |||
| f1c750e4ca | |||
| 6946a7fa50 | |||
| bc97630313 | |||
| c43d16d69a | |||
| deb6a60a84 | |||
| c4e84b7295 | |||
| 56a0480540 | |||
| dc181d41d5 | |||
| 934e796e43 | |||
| 6ffcdb1856 | |||
| 4987c5c3c2 | |||
| 21e7816e65 | |||
| 6aedd6d9fc | |||
| 7bd230cf4d | |||
| bb4d2edf10 | |||
| 109025c0a4 | |||
| 3c72072a6f | |||
| c81ab76cd9 | |||
| fd486258a0 | |||
| 940cdc94f3 | |||
| 23a16c8387 | |||
| e6e49bfef2 | |||
| 06638117bb | |||
| 22cd1eec5a | |||
| 7c6f2880aa | |||
| 70989b6f59 | |||
| ef7c692baf | |||
| da52f11250 | |||
| 7a29505e57 | |||
| d281889ac8 | |||
| a955ec806a | |||
| b5df31d15f | |||
| b9a6e42a55 | |||
| 1c30df3598 | |||
| ca447020ae | |||
| 7aa01a0ffc | |||
| 5d5d6194c9 | |||
| c97ad9b4e1 | |||
| 41155d8885 | |||
| df2d14b86e | |||
| a239ece378 | |||
| c1042cd133 | |||
| 65b1244781 | |||
| 49af3dfbf4 | |||
| a5315a6ece | |||
| f138110dfd | |||
| ad8ec9db63 | |||
| 129767958a | |||
| 90fa4c7b2c | |||
| 3196c349d1 | |||
| 0337b9a628 | |||
| 6cb12337b9 | |||
| c589d5ab06 | |||
| 28db1a68ba | |||
| 11397ce104 | |||
| 494ba30c9b | |||
| 6a18c9e06a | |||
| ccd34894e1 | |||
| c1547d1f7a | |||
| 50ca8af6f2 | |||
| 8db3fc5488 | |||
| ba5994b181 | |||
| 3341f5ce85 | |||
| d6b56d65a8 | |||
| 4f2f91bf08 | |||
| 242e064978 | |||
| 4a90f81a6d | |||
| a88d61824d | |||
| af43eddc23 | |||
| b5b4ca2a11 | |||
| c3c645ef46 | |||
| f5a9b58c8b | |||
| ce018f5bb3 | |||
| 1a7cfbebb0 | |||
| db05c66a16 | |||
| 71475478a1 | |||
| c8edf07ca7 | |||
| d9bc2bb638 | |||
| 3c39f1f5d6 | |||
| dac0d0b310 | |||
| 4a972bc1b8 | |||
| 8aa9135f2c | |||
| fcd65efcf1 | |||
| c013fa50db | |||
| 1321ea5010 | |||
| 87ebadbf4e | |||
| cc902fd25e | |||
| 306a8dcf3e | |||
| 838d2d3078 | |||
| 1d5a12e6a5 | |||
| f9eff9052d | |||
| 58b08d7702 | |||
| 27095fc4a5 | |||
| 7766c9943e | |||
| 789da35848 | |||
| 8d4edd0d67 | |||
| 5804da0218 | |||
| d55ab84451 | |||
| 889ce8ee90 | |||
| be1fdbd272 | |||
| 9904d824b8 | |||
| 5b5625f679 | |||
| 59b171172f | |||
| 4cc86b6e92 | |||
| 91937a6961 | |||
| 01972f55fe | |||
| fb7d0c3c01 | |||
| 8c58fd78c3 | |||
| 5648d717b1 | |||
| a0651b3707 | |||
| bb36d55e63 | |||
| 0766cbf511 | |||
| 692c160360 | |||
| ff176231da | |||
| 27c680cc69 | |||
| ff4ad29d05 | |||
| 781163aa9d | |||
| 6ad4e9fd2a | |||
| 1c8ed5fa43 | |||
| 1521ca4c20 | |||
| 127812e645 | |||
| 18557fdeb5 | |||
| 922cf3e2e4 | |||
| 5a18c1f9cd | |||
| aa2258e35b | |||
| f5b1c3da36 | |||
| dd9a7e4540 | |||
| 901b671455 | |||
| b204b6aa7b | |||
| 8797bd695f | |||
| 6c796631fc | |||
| 408a34925d | |||
| ef2401f669 | |||
| b651084713 | |||
| 21363ac78d | |||
| f87a54ab41 | |||
| 52d0310256 | |||
| 342984075b | |||
| 65a46af7ae | |||
| f0f240c0d8 | |||
| db79dd1f88 | |||
| b8123415b2 | |||
| 2ed4f5016a | |||
| 42f997d768 | |||
| 743188642d | |||
| 235b5d0ac5 | |||
| 41ebf965cd | |||
| 189c41221d | |||
| 7c5a829025 | |||
| 3f138e1d8f | |||
| ff7200e75b | |||
| 78f9ba9774 | |||
| 8ff952773d | |||
| a7384e0de2 | |||
| 797fcb8433 | |||
| 7dad9b4297 | |||
| 3e38aa0821 | |||
| a4a8e91022 | |||
| 10e54e365a | |||
| 29d5e43ad4 | |||
| 1286677773 | |||
| 8c411f7fce | |||
| 9b118de9d5 | |||
| 195ef6baca | |||
| 7978910f85 | |||
| ff6ab9dd2b | |||
| 5af9fd53f4 | |||
| 4a9a5cbbe9 | |||
| 6010980914 | |||
| fe4ed96006 | |||
| 4098aa5d19 | |||
| 555aaa432c | |||
| 5eba4281b0 | |||
| c5900bfd59 | |||
| c60c928717 | |||
| 8f924f0530 | |||
| 610a5bafe2 | |||
| 8bc2647e0c | |||
| 1107b4b9cf | |||
| caa7a62591 | |||
| 0c8c9a64b3 | |||
| 679f5d458e | |||
| 1193e4679d | |||
| 35c6f58dce | |||
| 6e7a0c2d86 | |||
| 5529ec74da | |||
| 6d02d2647b | |||
| 5cd01c30b4 | |||
| c76520c5b9 | |||
| 92d317f787 | |||
| eaa560f3fc | |||
| e9b919d550 | |||
| 210f6f89be | |||
| 575a7b85f5 | |||
| f2b21a5f57 | |||
| 3a799b3e4e | |||
| 133f930e61 | |||
| 5c85ddc3d7 | |||
| 0a83dac31c | |||
| 4a36d30493 | |||
| e317e7f4b4 | |||
| 8517b7d2b6 | |||
| b2d590c5f9 | |||
| dcea6335c7 | |||
| 9e88d4bb8a | |||
| eb48fddac7 | |||
| 698386ca2c | |||
| ea9a25e500 | |||
| 0c059b598d | |||
| 490661fd96 | |||
| bb99e73ef5 | |||
| 58c5a95c07 | |||
| 1bed50d424 | |||
| 24b1d7a0c8 | |||
| 91db27aff5 | |||
| f971c15b1f | |||
| a51de65758 | |||
| 5cc7f4b580 | |||
| a1291e79d3 | |||
| b030bc6980 | |||
| 5addf9f111 | |||
| d888e79bb8 | |||
| 3f91c05e5d | |||
| 47ce12d239 | |||
| 5bd8cc7dd5 | |||
| ed1b616f4a | |||
| a7382d3523 | |||
| 15fef79a29 | |||
| ff79000f28 | |||
| 8e3042b565 | |||
| ad2bcef112 | |||
| 3f80dbd51e | |||
| 096c22ad5e | |||
| d3b58968a9 | |||
| f15c80d80f | |||
| e388fc7367 | |||
| c97cdbe523 | |||
| 4400b0ac45 | |||
| 3bad3d6964 | |||
| f504cc761b | |||
| 9015b8822d | |||
| 262addd9ab | |||
| a28a784ef4 | |||
| 15669d302d | |||
| 1c2ad465c6 | |||
| 780187f090 | |||
| 3302bf4dbd | |||
| 1631c64f78 | |||
| 8174305e17 | |||
| 064b681e9b | |||
| 8f14c45197 | |||
| b4afecc626 | |||
| 07f215ad9e | |||
| 53bef48fbe | |||
| 2ec78c0e4b | |||
| b16c2980b3 | |||
| fe225745fc | |||
| 4f2144dac0 | |||
| b18d851119 | |||
| 4eabec5b18 | |||
| 171c944345 | |||
| 8bd5ff4f32 | |||
| faf10c521f | |||
| 71e2bdb2b4 | |||
| 04416ddc51 | |||
| 6326e30f86 | |||
| c855df67a4 | |||
| aecfb0a3b3 | |||
| f73912694b | |||
| d109de8f84 | |||
| 9d82438dcb | |||
| a592883d87 | |||
| 1ffd7ed54f | |||
| a0a1fb876f | |||
| 7acf972c59 | |||
| 441993200e | |||
| 7052c67c36 | |||
| 55a4bea72a | |||
| 0b9efaffd7 | |||
| 6f9b37b35b | |||
| c636e124df | |||
| db8939f481 | |||
| bdc4ffacec | |||
| 2e32cbc4a5 | |||
| be3778dee2 | |||
| 74d5faec93 | |||
| 20ff50ad10 | |||
| 9b183a234c | |||
| d3bb31b931 | |||
| 3a39c44c21 | |||
| 20728a599e |
+127
-60
@@ -83,9 +83,13 @@ TEMP_STORE = -DSQLITE_TEMP_STORE=@TEMP_STORE@
|
||||
# based on configuration. (-DSQLITE_OMIT*, -DSQLITE_ENABLE*).
|
||||
# The same set of OMIT and ENABLE flags should be passed to the
|
||||
# LEMON parser generator and the mkkeywordhash tool as well.
|
||||
OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
|
||||
#
|
||||
# Add OPTIONS=... on the command line to append additional options
|
||||
# to the OPT_FEATURE_FLAGS.
|
||||
#
|
||||
OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@ $(OPTIONS)
|
||||
|
||||
TCC += $(OPT_FEATURE_FLAGS)
|
||||
TCC += $(OPT_FEATURE_FLAGS)
|
||||
|
||||
# Add in any optional parameters specified on the make commane line
|
||||
# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
|
||||
@@ -117,14 +121,15 @@ HAVE_TCL = @HAVE_TCL@
|
||||
#
|
||||
TCLSH_CMD = @TCLSH_CMD@
|
||||
|
||||
# Additional options when running tests using testrunner.tcl
|
||||
# This is usually either blank, or else --status
|
||||
#
|
||||
TSTRNNR_OPTS = @TSTRNNR_OPTS@
|
||||
|
||||
# Where do we want to install the tcl plugin
|
||||
#
|
||||
TCLLIBDIR = @TCLLIBDIR@
|
||||
|
||||
# The suffix used on shared libraries. Ex: ".dll", ".so", ".dylib"
|
||||
#
|
||||
SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
|
||||
|
||||
# If gcov support was enabled by the configure script, add the appropriate
|
||||
# flags here. It's not always as easy as just having the user add the right
|
||||
# CFLAGS / LDFLAGS, because libtool wants to use CFLAGS when linking, which
|
||||
@@ -645,6 +650,7 @@ FUZZCHECK_SRC += $(TOP)/test/fuzzinvariants.c
|
||||
FUZZCHECK_SRC += $(TOP)/ext/recover/dbdata.c
|
||||
FUZZCHECK_SRC += $(TOP)/ext/recover/sqlite3recover.c
|
||||
FUZZCHECK_SRC += $(TOP)/test/vt02.c
|
||||
FUZZCHECK_SRC += $(TOP)/ext/misc/percentile.c
|
||||
FUZZCHECK_SRC += $(TOP)/ext/misc/randomjson.c
|
||||
DBFUZZ_OPT =
|
||||
ST_OPT = -DSQLITE_OS_KV_OPTIONAL
|
||||
@@ -655,11 +661,16 @@ SQLITE3_SHELL_TARGET_ = sqlite3$(TEXE)
|
||||
SQLITE3_SHELL_TARGET_1 =
|
||||
SQLITE3_SHELL_TARGET = $(SQLITE3_SHELL_TARGET_@HAVE_WASI_SDK@)
|
||||
|
||||
# Use $(libtclsqlite3.la_$(HAVE_TCL)) to resolve to either
|
||||
# libtclsqlite3.la or an empty value.
|
||||
libtclsqlite3.la_0 =
|
||||
libtclsqlite3.la_1 = libtclsqlite3.la
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
#
|
||||
all: sqlite3.h libsqlite3.la $(SQLITE3_SHELL_TARGET) \
|
||||
$(HAVE_TCL:1=libtclsqlite3.la)
|
||||
$(libtclsqlite3.la_$(HAVE_TCL))
|
||||
|
||||
Makefile: $(TOP)/Makefile.in
|
||||
./config.status
|
||||
@@ -683,12 +694,26 @@ sqlite3$(TEXE): shell.c sqlite3.c
|
||||
shell.c sqlite3.c \
|
||||
$(LIBREADLINE) $(TLIBS) -rpath "$(libdir)"
|
||||
|
||||
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.lo sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.lo $(TLIBS)
|
||||
sqldiff$(TEXE): $(TOP)/tool/sqldiff.c $(TOP)/ext/misc/sqlite3_stdio.h sqlite3.lo sqlite3.h
|
||||
$(LTLINK) -I$(TOP)/ext/misc -o $@ $(TOP)/tool/sqldiff.c sqlite3.lo $(TLIBS)
|
||||
|
||||
dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.lo sqlite3.h
|
||||
$(LTLINK) -o $@ $(TOP)/tool/dbhash.c sqlite3.lo $(TLIBS)
|
||||
|
||||
RSYNC_SRC = \
|
||||
$(TOP)/tool/sqlite3-rsync.c \
|
||||
sqlite3.c
|
||||
|
||||
RSYNC_OPT = \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB \
|
||||
-USQLITE_THREADSAFE \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED
|
||||
|
||||
sqlite3-rsync$(TEXE): $(RSYNC_SRC)
|
||||
$(TCC) -o $@ $(RSYNC_OPT) $(RSYNC_SRC) $(TLIBS)
|
||||
|
||||
scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.lo
|
||||
$(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \
|
||||
$(TOP)/ext/misc/scrub.c sqlite3.lo $(TLIBS)
|
||||
@@ -800,6 +825,10 @@ has_tclsh85:
|
||||
sh $(TOP)/tool/cktclsh.sh 8.5 $(TCLSH_CMD)
|
||||
touch has_tclsh85
|
||||
|
||||
has_tclconfig:
|
||||
@ if test x"$(HAVE_TCL)" != "x1"; then echo 'ERROR: Requires access to "tclConfig.sh" which "configure" was not able to locate'; exit 1; fi
|
||||
touch has_tclconfig
|
||||
|
||||
|
||||
# This target creates a directory named "tsrc" and fills it with
|
||||
# copies of all of the C source code and header files needed to
|
||||
@@ -1122,7 +1151,7 @@ tclsqlite-shell.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
tclsqlite-stubs.lo: $(TOP)/src/tclsqlite.c $(HDR)
|
||||
$(LTCOMPILE) -DUSE_TCL_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c
|
||||
|
||||
tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
|
||||
tclsqlite3$(TEXE): has_tclconfig tclsqlite-shell.lo libsqlite3.la
|
||||
$(LTLINK) -o $@ tclsqlite-shell.lo \
|
||||
libsqlite3.la $(LIBTCL)
|
||||
|
||||
@@ -1158,8 +1187,6 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/consio/console_io.c \
|
||||
$(TOP)/ext/consio/console_io.h \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/intck/sqlite3intck.c \
|
||||
@@ -1173,11 +1200,16 @@ SHELL_DEP = \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/memtrace.c \
|
||||
$(TOP)/ext/misc/pcachetrace.c \
|
||||
$(TOP)/ext/misc/percentile.c \
|
||||
$(TOP)/ext/misc/regexp.c \
|
||||
$(TOP)/ext/misc/series.c \
|
||||
$(TOP)/ext/misc/sha1.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/sqlar.c \
|
||||
$(TOP)/ext/misc/sqlite3_stdio.c \
|
||||
$(TOP)/ext/misc/sqlite3_stdio.h \
|
||||
$(TOP)/ext/misc/uint.c \
|
||||
$(TOP)/ext/misc/vfstrace.c \
|
||||
$(TOP)/ext/misc/zipfile.c \
|
||||
$(TOP)/ext/recover/dbdata.c \
|
||||
$(TOP)/ext/recover/sqlite3recover.c \
|
||||
@@ -1309,7 +1341,7 @@ TESTFIXTURE_SRC1 = sqlite3.c
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
|
||||
|
||||
testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC)
|
||||
testfixture$(TEXE): has_tclconfig has_tclsh85 $(TESTFIXTURE_SRC)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \
|
||||
-o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
|
||||
|
||||
@@ -1358,15 +1390,20 @@ tcltest: ./testfixture$(TEXE)
|
||||
testrunner: testfixture$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl
|
||||
|
||||
# Runs both fuzztest and testrunner, consecutively.
|
||||
# This is the testing target preferred by the core SQLite developers.
|
||||
# It runs tests under a standard configuration, regardless of how
|
||||
# ./configure was run. The devs run "make devtest" prior to each
|
||||
# check-in, at a minimum. Probably other tests too, but at least this
|
||||
# one.
|
||||
#
|
||||
devtest: srctree-check testfixture$(TEXE) fuzztest testrunner
|
||||
devtest: srctree-check sourcetest
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)
|
||||
|
||||
mdevtest: srctree-check has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)
|
||||
|
||||
sdevtest: has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest $(TSTRNNR_OPTS)
|
||||
|
||||
# Validate that various generated files in the source tree
|
||||
# are up-to-date.
|
||||
@@ -1376,16 +1413,19 @@ srctree-check: $(TOP)/tool/srctree-check.tcl
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: srctree-check testfixture$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl release
|
||||
releasetest: srctree-check has_tclsh85 verify-source
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl release $(TSTRNNR_OPTS)
|
||||
|
||||
# Minimal testing that runs in less than 3 minutes
|
||||
#
|
||||
quicktest: ./testfixture$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/extraquick.test $(TESTOPTS)
|
||||
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
# Try to run tests on whatever options are specified by the
|
||||
# ./configure. The developers seldom use this target. Instead
|
||||
# they use "make devtest" which runs tests on a standard set of
|
||||
# options regardless of how SQLite is configured. This "test"
|
||||
# target is provided for legacy only.
|
||||
#
|
||||
test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
|
||||
|
||||
@@ -1402,19 +1442,19 @@ valgrindtest: $(TESTPROGS) valgrindfuzz
|
||||
smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/main.test $(TESTOPTS)
|
||||
|
||||
shelltest: $(TESTPROGS)
|
||||
./testfixture$(TEXT) $(TOP)/test/permutations.test shell
|
||||
shelltest:
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl release shell
|
||||
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
|
||||
|
||||
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
|
||||
sqlite3_analyzer$(TEXE): has_tclconfig sqlite3_analyzer.c
|
||||
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh$(TEXE): sqltclsh.c
|
||||
sqltclsh$(TEXE): has_tclconfig sqltclsh.c
|
||||
$(LTLINK) sqltclsh.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
sqlite3_expert$(TEXE): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c
|
||||
@@ -1433,7 +1473,7 @@ CHECKER_DEPS =\
|
||||
sqlite3_checker.c: $(CHECKER_DEPS) has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/ext/repair/sqlite3_checker.c.in >$@
|
||||
|
||||
sqlite3_checker$(TEXE): sqlite3_checker.c
|
||||
sqlite3_checker$(TEXE): has_tclconfig sqlite3_checker.c
|
||||
$(LTLINK) sqlite3_checker.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
dbdump$(TEXE): $(TOP)/ext/misc/dbdump.c sqlite3.lo
|
||||
@@ -1547,7 +1587,12 @@ lib_install: libsqlite3.la
|
||||
$(INSTALL) -d $(DESTDIR)$(libdir)
|
||||
$(LTINSTALL) libsqlite3.la $(DESTDIR)$(libdir)
|
||||
|
||||
install: sqlite3$(TEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_install}
|
||||
# Use $(tcl_install_$(HAVE_TCL)) to resolve to either tcl_install or
|
||||
# an empty value.
|
||||
tcl_install_0 =
|
||||
tcl_install_1 = tcl_install
|
||||
|
||||
install: sqlite3$(TEXE) lib_install sqlite3.h sqlite3.pc $(tcl_install_$(HAVE_TCL))
|
||||
$(INSTALL) -d $(DESTDIR)$(bindir)
|
||||
$(LTINSTALL) sqlite3$(TEXE) $(DESTDIR)$(bindir)
|
||||
$(INSTALL) -d $(DESTDIR)$(includedir)
|
||||
@@ -1558,49 +1603,71 @@ install: sqlite3$(TEXE) lib_install sqlite3.h sqlite3.pc ${HAVE_TCL:1=tcl_instal
|
||||
|
||||
pkgIndex.tcl:
|
||||
echo 'package ifneeded sqlite3 $(RELEASE) [list load [file join $$dir libtclsqlite3[info sharedlibextension]] sqlite3]' > $@
|
||||
|
||||
tcl_install: lib_install libtclsqlite3.la pkgIndex.tcl
|
||||
$(INSTALL) -d $(DESTDIR)$(TCLLIBDIR)
|
||||
$(LTINSTALL) libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)
|
||||
rm -f $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.la $(DESTDIR)$(TCLLIBDIR)/libtclsqlite3.a
|
||||
$(INSTALL) -m 0644 pkgIndex.tcl $(DESTDIR)$(TCLLIBDIR)
|
||||
|
||||
clean:
|
||||
rm -f *.lo *.la *.o sqlite3$(TEXE) libsqlite3.la
|
||||
rm -f sqlite3.h opcodes.*
|
||||
rm -rf .libs .deps
|
||||
rm -f lemon$(BEXE) lempar.c parse.* sqlite*.tar.gz
|
||||
rm -f mkkeywordhash$(BEXE) keywordhash.h
|
||||
rm -f mksourceid$(BEXE)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc .target_source
|
||||
rm -f tclsqlite3$(TEXE)
|
||||
rm -f testfixture$(TEXE) test.db
|
||||
# Build the SQLite TCL extension in a way that make it compatible
|
||||
# with whatever version of TCL is running as $TCLSH_CMD, possibly defined
|
||||
# by --with-tclsh=
|
||||
#
|
||||
tclextension: tclsqlite3.c
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --build-only --cc $(CC) $(CFLAGS) $(OPT_FEATURE_FLAGS) $(OPTS)
|
||||
|
||||
# Install the SQLite TCL extension in a way that is appropriate for $TCLSH_CMD
|
||||
# to find it.
|
||||
#
|
||||
tclextension-install: tclsqlite3.c
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --cc $(CC) $(CFLAGS) $(OPT_FEATURE_FLAGS) $(OPTS)
|
||||
|
||||
# Install the SQLite TCL extension that is used by $TCLSH_CMD
|
||||
#
|
||||
tclextension-uninstall:
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --uninstall
|
||||
|
||||
# List all installed the SQLite TCL extension that is are accessible
|
||||
# by $TCLSH_CMD, included prior versions.
|
||||
#
|
||||
tclextension-list:
|
||||
$(TCLSH_CMD) $(TOP)/tool/buildtclext.tcl --info
|
||||
|
||||
|
||||
# Remove build products sufficient so that subsequent makes will recompile
|
||||
# everything from scratch. Do not remove:
|
||||
#
|
||||
# * test results and test logs
|
||||
# * output from ./configure
|
||||
#
|
||||
tidy:
|
||||
rm -f *.lo *.la *.o *.c *.da *.bb *.bbg gmon.* *.rws sqlite3$(TEXE)
|
||||
rm -f fts5.h keywordhash.h opcodes.h sqlite3.h sqlite3ext.h sqlite3session.h
|
||||
rm -rf .libs .deps tsrc .target_source
|
||||
rm -f lemon$(BEXE) sqlite*.tar.gz
|
||||
rm -f mkkeywordhash$(BEXE) mksourceid$(BEXE)
|
||||
rm -f parse.* fts5parse.*
|
||||
rm -f tclsqlite3$(TEXE) $(TESTPROGS)
|
||||
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
|
||||
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
|
||||
rm -f wordcount$(TEXE) changeset$(TEXE)
|
||||
rm -f version-info$(TEXT)
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
rm -f sqlite3.c
|
||||
rm -f sqlite3rc.h
|
||||
rm -f shell.c sqlite3ext.h
|
||||
rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c
|
||||
rm -f sqlite-*-output.vsix
|
||||
rm -f mptester mptester.exe
|
||||
rm -f rbu rbu.exe
|
||||
rm -f srcck1 srcck1.exe
|
||||
rm -f fuzzershell fuzzershell.exe
|
||||
rm -f fuzzcheck fuzzcheck.exe
|
||||
rm -f sqldiff sqldiff.exe
|
||||
rm -f dbhash dbhash.exe
|
||||
rm -f fts5.* fts5parse.*
|
||||
rm -f threadtest5
|
||||
rm -f src-verify
|
||||
rm -f custom.rws
|
||||
rm -f has_tclsh84 has_tclsh85
|
||||
rm -f wordcount$(TEXE) changeset$(TEXE) version-info$(TEXE)
|
||||
rm -f *.dll *.lib *.exp *.def *.pc *.vsix *.so *.dylib pkgIndex.tcl
|
||||
rm -f sqlite3_analyzer$(TEXE) sqlite3-rsync$(TEXE)
|
||||
rm -f mptester$(TEXE) rbu$(TEXE) srcck1$(TEXE)
|
||||
rm -f fuzzershell$(TEXE) fuzzcheck$(TEXE) sqldiff$(TEXE) dbhash$(TEXE)
|
||||
rm -f threadtest5$(TEXE)
|
||||
rm -f src-verify has_tclsh*
|
||||
|
||||
# Removes build products and test logs. Retains ./configure outputs.
|
||||
#
|
||||
clean: tidy
|
||||
rm -rf omittest* testrunner* testdir*
|
||||
|
||||
# Clean up everything. No exceptions.
|
||||
#
|
||||
distclean: clean
|
||||
rm -f sqlite_cfg.h config.log config.status libtool Makefile sqlite3.pc \
|
||||
$(TESTPROGS)
|
||||
rm -f sqlite_cfg.h config.log config.status Makefile $(LIBTOOL)
|
||||
|
||||
#
|
||||
# Windows section
|
||||
|
||||
+71
-12
@@ -379,6 +379,7 @@ SQLITE_TCL_DEP =
|
||||
# the Windows platform.
|
||||
#
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
OPT_FEATURE_FLAGS = $(OPT_XTRA)
|
||||
!IF $(MINIMAL_AMALGAMATION)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
|
||||
@@ -392,6 +393,14 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
# Additional feature-options above and beyond what are normally used can be
|
||||
# be added using OPTIONS=.... on the command-line. These values are
|
||||
# appended to the OPT_FEATURE_FLAGS variable.
|
||||
#
|
||||
!IFDEF OPTIONS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) $(OPTIONS)
|
||||
!ENDIF
|
||||
|
||||
# Should the session extension be enabled? If so, add compilation options
|
||||
# to enable it.
|
||||
#
|
||||
@@ -929,7 +938,7 @@ TCLSUFFIX =
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF TCLDIR
|
||||
TCLDIR = $(TOP)\compat\tcl
|
||||
TCLDIR = C:\Tcl
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF TCLINCDIR
|
||||
@@ -1758,6 +1767,7 @@ FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\fuzzinvariants.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\test\vt02.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\dbdata.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\recover\sqlite3recover.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\percentile.c
|
||||
FUZZCHECK_SRC = $(FUZZCHECK_SRC) $(TOP)\ext\misc\randomjson.c
|
||||
|
||||
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
|
||||
@@ -1820,6 +1830,19 @@ pkgIndex.tcl: $(TOP)\VERSION
|
||||
|
||||
$(SQLITE3TCLDLL): libtclsqlite3.lib $(LIBRESOBJS) tclsqlite3.def pkgIndex.tcl
|
||||
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:tclsqlite3.def /OUT:$@ libtclsqlite3.lib $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
tclextension: $(SQLITE3TCLDLL)
|
||||
|
||||
tclextension-install: $(SQLITE3TCLDLL)
|
||||
$(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --install-only
|
||||
|
||||
tclextension-uninstall:
|
||||
$(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --uninstall
|
||||
|
||||
tclextension-list:
|
||||
$(TCLSH_CMD) $(TOP)\tool\buildtclext.tcl --info
|
||||
|
||||
|
||||
# <</mark>>
|
||||
|
||||
$(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
|
||||
@@ -1838,12 +1861,25 @@ $(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\consio\console_io.h $(TOP)\ext\consio\console_io.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -I$(TOP)\ext\consio $(TOP)\tool\sqldiff.c $(TOP)\ext\consio\console_io.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)
|
||||
|
||||
RSYNC_SRC = \
|
||||
$(TOP)\tool\sqlite3-rsync.c \
|
||||
$(SQLITE3C)
|
||||
|
||||
RSYNC_OPT = \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED
|
||||
|
||||
sqlite3-rsync.exe: $(RSYNC_SRC) $(LIBRESOBJS)
|
||||
$(LTLINK) $(RSYNC_OPT) $(NO_WARN) $(RSYNC_SRC) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
|
||||
scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
@@ -1871,6 +1907,10 @@ fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
fuzzcheck-asan.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) /fsanitize=address $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
run-fuzzcheck: fuzzcheck.exe fuzzcheck-asan.exe
|
||||
fuzzcheck --spinner $(FUZZDB)
|
||||
fuzzcheck-asan --spinner $(FUZZDB)
|
||||
|
||||
ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZCHECK_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
@@ -2276,8 +2316,6 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\consio\console_io.c \
|
||||
$(TOP)\ext\consio\console_io.h \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\intck\sqlite3intck.c \
|
||||
@@ -2291,11 +2329,16 @@ SHELL_DEP = \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\memtrace.c \
|
||||
$(TOP)\ext\misc\pcachetrace.c \
|
||||
$(TOP)\ext\misc\percentile.c \
|
||||
$(TOP)\ext\misc\regexp.c \
|
||||
$(TOP)\ext\misc\series.c \
|
||||
$(TOP)\ext\misc\sha1.c \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\sqlar.c \
|
||||
$(TOP)\ext\misc\sqlite3_stdio.c \
|
||||
$(TOP)\ext\misc\sqlite3_stdio.h \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\misc\vfstrace.c \
|
||||
$(TOP)\ext\misc\zipfile.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
@@ -2522,6 +2565,13 @@ queryplantest: testfixture.exe shell
|
||||
fuzztest: fuzzcheck.exe
|
||||
.\fuzzcheck.exe $(FUZZDATA)
|
||||
|
||||
# Legacy testing target for third-party integrators. The SQLite
|
||||
# developers seldom use this target themselves. Instead
|
||||
# they use "nmake /f Makefile.msc devtest" which runs tests on
|
||||
# a standard set of options
|
||||
#
|
||||
test: $(TESTPROGS) sourcetest fuzztest tcltest
|
||||
|
||||
# Minimal testing that runs in less than 3 minutes (on a fast machine)
|
||||
#
|
||||
quicktest: testfixture.exe sourcetest
|
||||
@@ -2531,7 +2581,6 @@ quicktest: testfixture.exe sourcetest
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
#
|
||||
test: $(TESTPROGS) sourcetest fuzztest tcltest
|
||||
|
||||
# The veryquick.test TCL tests.
|
||||
#
|
||||
@@ -2545,17 +2594,27 @@ tcltest: testfixture.exe
|
||||
testrunner: testfixture.exe
|
||||
.\testfixture.exe $(TOP)\test\testrunner.tcl
|
||||
|
||||
# Runs both fuzztest and testrunner, consecutively.
|
||||
# This is the testing target preferred by the core SQLite developers.
|
||||
# It runs tests under a standard configuration. The devs run
|
||||
# "nmake /f Makefile.msc devtest" prior to each check-in, at a minimum.
|
||||
# Probably other tests too, but at least this one.
|
||||
#
|
||||
devtest: testfixture.exe fuzztest testrunner
|
||||
devtest: srctree-check sourcetest
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
|
||||
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
|
||||
|
||||
# Validate that various generated files in the source tree
|
||||
# are up-to-date.
|
||||
#
|
||||
srctree-check: $(TOP)\tool\srctree-check.tcl
|
||||
$(TCLSH_CMD) $(TOP)\tool\srctree-check.tcl
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture.exe
|
||||
testfixture.exe $(TOP)\test\testrunner.tcl release
|
||||
releasetest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
|
||||
|
||||
|
||||
smoketest: $(TESTPROGS)
|
||||
@@ -2565,7 +2624,7 @@ smoketest: $(TESTPROGS)
|
||||
shelltest: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\permutations.test shell
|
||||
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\consio\console_io.h $(TOP)\ext\consio\console_io.c $(SQLITE_TCL_DEP)
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in > $@
|
||||
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
@@ -2720,7 +2779,7 @@ clean:
|
||||
del /Q sqlite3.c sqlite3-*.c sqlite3.h 2>NUL
|
||||
del /Q sqlite3rc.h 2>NUL
|
||||
del /Q shell.c sqlite3ext.h sqlite3session.h 2>NUL
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.c 2>NUL
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.c sqlite3-rsync.exe 2>NUL
|
||||
del /Q sqlite-*-output.vsix 2>NUL
|
||||
del /Q fuzzershell.exe fuzzcheck.exe sqldiff.exe dbhash.exe 2>NUL
|
||||
del /Q sqltclsh.* 2>NUL
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
<h1 align="center">SQLite Source Repository</h1>
|
||||
|
||||
This repository contains the complete source code for the
|
||||
[SQLite database engine](https://sqlite.org/). Some test scripts
|
||||
are also included. However, many other test scripts
|
||||
[SQLite database engine](https://sqlite.org/), including
|
||||
many test scripts. However, other test scripts
|
||||
and most of the documentation are managed separately.
|
||||
|
||||
See the [on-line documentation](https://sqlite.org/) for more information
|
||||
about what SQLite is and how it works from a user's perspective. This
|
||||
README file is about the source code that goes into building SQLite,
|
||||
not about how SQLite is used.
|
||||
|
||||
## Version Control
|
||||
|
||||
SQLite sources are managed using
|
||||
[Fossil](https://www.fossil-scm.org/), a distributed version control system
|
||||
[Fossil](https://fossil-scm.org/), a distributed version control system
|
||||
that was specifically designed and written to support SQLite development.
|
||||
The [Fossil repository](https://sqlite.org/src/timeline) contains the urtext.
|
||||
|
||||
@@ -68,24 +73,26 @@ archives or [SQLite archives](https://sqlite.org/cli.html#sqlar) as follows:
|
||||
then click on the "Tarball" or "ZIP Archive" links on the information
|
||||
page.
|
||||
|
||||
If you do want to use Fossil to check out the source tree,
|
||||
To access sources directly using [Fossil](https://fossil-scm.org/home),
|
||||
first install Fossil version 2.0 or later.
|
||||
(Source tarballs and precompiled binaries available
|
||||
[here](https://www.fossil-scm.org/fossil/uv/download.html). Fossil is
|
||||
Source tarballs and precompiled binaries available at
|
||||
<https://fossil-scm.org/home/uv/download.html>. Fossil is
|
||||
a stand-alone program. To install, simply download or build the single
|
||||
executable file and put that file someplace on your $PATH.)
|
||||
executable file and put that file someplace on your $PATH.
|
||||
Then run commands like this:
|
||||
|
||||
mkdir -p ~/sqlite ~/Fossils
|
||||
mkdir -p ~/sqlite
|
||||
cd ~/sqlite
|
||||
fossil clone https://www.sqlite.org/src ~/Fossils/sqlite.fossil
|
||||
fossil open ~/Fossils/sqlite.fossil
|
||||
fossil open https://sqlite.org/src
|
||||
|
||||
After setting up a repository using the steps above, you can always
|
||||
update to the latest version using:
|
||||
The "fossil open" command will take two or three minutes. Afterwards,
|
||||
you can do fast, bandwidth-efficient updates to the whatever versions
|
||||
of SQLite you like. Some examples:
|
||||
|
||||
fossil update trunk ;# latest trunk check-in
|
||||
fossil update release ;# latest official release
|
||||
fossil update trunk ;# latest trunk check-in
|
||||
fossil update release ;# latest official release
|
||||
fossil update trunk:2024-01-01 ;# First trunk check-in after 2024-01-01
|
||||
fossil update version-3.39.0 ;# Version 3.39.0
|
||||
|
||||
Or type "fossil ui" to get a web-based user interface.
|
||||
|
||||
@@ -99,15 +106,42 @@ script found at the root of the source tree. Then run "make".
|
||||
|
||||
For example:
|
||||
|
||||
tar xzf sqlite.tar.gz ;# Unpack the source tree into "sqlite"
|
||||
mkdir bld ;# Build will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
make ;# Builds the "sqlite3" command-line tool
|
||||
make sqlite3.c ;# Build the "amalgamation" source file
|
||||
make devtest ;# Run some tests (requires Tcl)
|
||||
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 will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
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
|
||||
|
||||
See the makefile for additional targets.
|
||||
See the makefile for additional targets. For debugging builds, the
|
||||
core developers typically run "configure" with options like this:
|
||||
|
||||
../sqlite/configure --enable-all --enable-debug CFLAGS='-O0 -g'
|
||||
|
||||
For release builds, the core developers usually do:
|
||||
|
||||
../sqlite/configure --enable-all
|
||||
|
||||
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"
|
||||
target has additional requiremenst, such as "valgrind".
|
||||
|
||||
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 --enable-all
|
||||
make OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3
|
||||
|
||||
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
|
||||
@@ -117,7 +151,7 @@ show what changes are needed.
|
||||
|
||||
## Compiling for Windows Using MSVC
|
||||
|
||||
On Windows, all applicable build products can be compiled with MSVC.
|
||||
On Windows, everything can be compiled with MSVC.
|
||||
You will also need a working installation of TCL.
|
||||
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
|
||||
@@ -128,48 +162,72 @@ TCL library, using a command like this:
|
||||
|
||||
set TCLDIR=c:\Tcl
|
||||
|
||||
SQLite uses "tclsh.exe" as part of the build process, and so that utility
|
||||
program will need to be somewhere on your %PATH%. The finished SQLite library
|
||||
does not contain any TCL code, but it does use TCL to help with the build process
|
||||
and to run 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 help with the
|
||||
build process and 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.
|
||||
|
||||
Build using Makefile.msc. Example:
|
||||
|
||||
nmake /f Makefile.msc
|
||||
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.
|
||||
|
||||
## Source Code Tour
|
||||
As with the unix Makefile, the OPTIONS=... argument can be passed on the nmake
|
||||
command-line to enable new compile-time options. For example:
|
||||
|
||||
Most of the core source files are in the **src/** subdirectory. The
|
||||
**src/** folder also contains files used to build the "testfixture" test
|
||||
harness. The names of the source files used by "testfixture" all begin
|
||||
with "test".
|
||||
The **src/** also contains the "shell.c" file
|
||||
which is the main program for the "sqlite3.exe"
|
||||
[command-line shell](https://sqlite.org/cli.html) and
|
||||
the "tclsqlite.c" file which implements the
|
||||
[Tcl bindings](https://sqlite.org/tclsqlite.html) for SQLite.
|
||||
(Historical note: SQLite began as a Tcl
|
||||
extension and only later escaped to the wild as an independent library.)
|
||||
nmake /f Makefile.msc OPTIONS=-DSQLITE_OMIT_DEPRECATED sqlite3.exe
|
||||
|
||||
Test scripts and programs are found in the **test/** subdirectory.
|
||||
Additional test code is found in other source repositories.
|
||||
See [How SQLite Is Tested](https://www.sqlite.org/testing.html) for
|
||||
additional information.
|
||||
## Source Tree Map
|
||||
|
||||
The **ext/** subdirectory contains code for extensions. The
|
||||
Full-text search engine is in **ext/fts3**. The R-Tree engine is in
|
||||
**ext/rtree**. The **ext/misc** subdirectory contains a number of
|
||||
smaller, single-file extensions, such as a REGEXP operator.
|
||||
* **src/** - This directory contains the primary source code for the
|
||||
SQLite core. For historical reasons, C-code used for testing is
|
||||
also found here. Source files intended for testing begin with "`test`".
|
||||
The `tclsqlite3.c` and `tclsqlite3.h` files are the TCL interface
|
||||
for SQLite and are also not part of the core.
|
||||
|
||||
The **tool/** subdirectory contains various scripts and programs used
|
||||
for building generated source code files or for testing or for generating
|
||||
accessory programs such as "sqlite3_analyzer(.exe)".
|
||||
* **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`" (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.
|
||||
|
||||
* **tool/** - This directory contains programs and scripts used to
|
||||
build some of the machine-generated code that goes into the SQLite
|
||||
core, as well as to build and run tests and perform diagnostics.
|
||||
The source code to [the Lemon parser generator](./doc/lemon.html) is
|
||||
found here. There are also TCL scripts used to build and/or transform
|
||||
source code files. For example, the tool/mksqlite3h.tcl script reads
|
||||
the src/sqlite.h.in file and uses it as a template to construct
|
||||
the deliverable "sqlite3.h" file that defines the SQLite interface.
|
||||
|
||||
* **ext/** - Various extensions to SQLite are found under this
|
||||
directory. For example, the FTS5 subsystem is in "ext/fts5/".
|
||||
Some of these extensions (ex: FTS3/4, FTS5, RTREE) might get built
|
||||
into the SQLite amalgamation, but not all of them. The
|
||||
"ext/misc/" subdirectory contains an assortment of one-file extensions,
|
||||
many of which are omitted from the SQLite core, but which are included
|
||||
in the [SQLite CLI](https://sqlite.org/cli.html).
|
||||
|
||||
* **doc/** - Some documentation files about SQLite internals are found
|
||||
here. Note, however, that the primary documentation designed for
|
||||
application developers and users of SQLite is in a completely separate
|
||||
repository. Note also that the primary API documentation is derived
|
||||
from specially constructed comments in the src/sqlite.h.in file.
|
||||
|
||||
### Generated Source Code Files
|
||||
|
||||
@@ -252,31 +310,37 @@ individual source file exceeds 32K lines in length.
|
||||
SQLite is modular in design.
|
||||
See the [architectural description](https://www.sqlite.org/arch.html)
|
||||
for details. Other documents that are useful in
|
||||
(helping to understand how SQLite works include the
|
||||
helping to understand how SQLite works include the
|
||||
[file format](https://www.sqlite.org/fileformat2.html) description,
|
||||
the [virtual machine](https://www.sqlite.org/opcode.html) that runs
|
||||
prepared statements, the description of
|
||||
[how transactions work](https://www.sqlite.org/atomiccommit.html), and
|
||||
the [overview of the query planner](https://www.sqlite.org/optoverview.html).
|
||||
|
||||
Years of effort have gone into optimizing SQLite, both
|
||||
Decades of effort have gone into optimizing SQLite, both
|
||||
for small size and high performance. And optimizations tend to result in
|
||||
complex code. So there is a lot of complexity in the current SQLite
|
||||
implementation. It will not be the easiest library in the world to hack.
|
||||
|
||||
Key files:
|
||||
### Key source code files
|
||||
|
||||
* **sqlite.h.in** - This file defines the public interface to the SQLite
|
||||
library. Readers will need to be familiar with this interface before
|
||||
trying to understand how the library works internally.
|
||||
trying to understand how the library works internally. This file is
|
||||
really a template that is transformed into the "sqlite3.h" deliverable
|
||||
using a script invoked by the makefile.
|
||||
|
||||
* **sqliteInt.h** - this header file defines many of the data objects
|
||||
used internally by SQLite. In addition to "sqliteInt.h", some
|
||||
subsystems have their own header files.
|
||||
subsystems inside of sQLite have their own header files. These internal
|
||||
interfaces are not for use by applications. They can and do change
|
||||
from one release of SQLite to the next.
|
||||
|
||||
* **parse.y** - This file describes the LALR(1) grammar that SQLite uses
|
||||
to parse SQL statements, and the actions that are taken at each step
|
||||
in the parsing process.
|
||||
in the parsing process. The file is processed by the
|
||||
[Lemon Parser Generator](./doc/lemon.html) to produce the actual C code
|
||||
used for parsing.
|
||||
|
||||
* **vdbe.c** - This file implements the virtual machine that runs
|
||||
prepared statements. There are various helper files whose names
|
||||
@@ -319,6 +383,11 @@ Key files:
|
||||
(and some other test programs too) is built and run when you type
|
||||
"make test".
|
||||
|
||||
* **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.
|
||||
|
||||
@@ -336,7 +405,7 @@ The `manifest.uuid` file should contain the SHA3-256 hash of the
|
||||
`manifest` file. If all of the above hash comparisons are correct, then
|
||||
you can be confident that your source tree is authentic and unadulterated.
|
||||
Details on the format for the `manifest` files are available
|
||||
[on the Fossil website](https://fossil-scm.org/fossil/doc/trunk/www/fileformat.wiki#manifest).
|
||||
[on the Fossil website](https://fossil-scm.org/home/doc/trunk/www/fileformat.wiki#manifest).
|
||||
|
||||
The process of checking source code authenticity is automated by the
|
||||
makefile:
|
||||
@@ -357,3 +426,7 @@ The main SQLite website is [https://sqlite.org/](https://sqlite.org/)
|
||||
with geographically distributed backups at
|
||||
[https://www2.sqlite.org/](https://www2.sqlite.org) and
|
||||
[https://www3.sqlite.org/](https://www3.sqlite.org).
|
||||
|
||||
Contact the SQLite developers through the
|
||||
[SQLite Forum](https://sqlite.org/forum/). In an emergency, you
|
||||
can send private email to the lead developer at drh at sqlite dot org.
|
||||
|
||||
@@ -301,6 +301,7 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
|
||||
# the Windows platform.
|
||||
#
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
OPT_FEATURE_FLAGS = $(OPT_XTRA)
|
||||
!IF $(MINIMAL_AMALGAMATION)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
|
||||
@@ -314,6 +315,14 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
!ENDIF
|
||||
|
||||
# Additional feature-options above and beyond what are normally used can be
|
||||
# be added using OPTIONS=.... on the command-line. These values are
|
||||
# appended to the OPT_FEATURE_FLAGS variable.
|
||||
#
|
||||
!IFDEF OPTIONS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) $(OPTIONS)
|
||||
!ENDIF
|
||||
|
||||
# Should the session extension be enabled? If so, add compilation options
|
||||
# to enable it.
|
||||
#
|
||||
|
||||
@@ -4064,4 +4064,4 @@ AC_DEFUN([TEA_ZIPFS_SUPPORT], [
|
||||
|
||||
# Local Variables:
|
||||
# mode: autoconf
|
||||
# End:
|
||||
# End:
|
||||
|
||||
@@ -708,4 +708,3 @@ TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
|
||||
!message *** Link options '$(LINKERFLAGS)'
|
||||
|
||||
!endif
|
||||
|
||||
|
||||
@@ -781,18 +781,6 @@ TARGET_HAVE_EDITLINE
|
||||
TARGET_HAVE_READLINE
|
||||
TARGET_READLINE_INC
|
||||
TARGET_READLINE_LIBS
|
||||
HAVE_TCL
|
||||
TCL_SHLIB_SUFFIX
|
||||
TCL_STUB_LIB_SPEC
|
||||
TCL_STUB_LIB_FLAG
|
||||
TCL_STUB_LIB_FILE
|
||||
TCL_LIB_SPEC
|
||||
TCL_LIB_FLAG
|
||||
TCL_LIB_FILE
|
||||
TCL_INCLUDE_SPEC
|
||||
TCL_SRC_DIR
|
||||
TCL_BIN_DIR
|
||||
TCL_VERSION
|
||||
TARGET_EXEEXT
|
||||
SQLITE_OS_WIN
|
||||
SQLITE_OS_UNIX
|
||||
@@ -805,7 +793,12 @@ HAVE_WASI_SDK
|
||||
RELEASE
|
||||
VERSION
|
||||
program_prefix
|
||||
TSTRNNR_OPTS
|
||||
TCLLIBDIR
|
||||
HAVE_TCL
|
||||
TCL_STUB_LIB_SPEC
|
||||
TCL_LIB_SPEC
|
||||
TCL_INCLUDE_SPEC
|
||||
TCLSH_CMD
|
||||
INSTALL_DATA
|
||||
INSTALL_SCRIPT
|
||||
@@ -894,12 +887,14 @@ enable_fast_install
|
||||
with_gnu_ld
|
||||
enable_libtool_lock
|
||||
enable_largefile
|
||||
with_tclsh
|
||||
with_tcl
|
||||
enable_tcl
|
||||
enable_test_status
|
||||
with_wasi_sdk
|
||||
enable_threadsafe
|
||||
enable_releasemode
|
||||
enable_tempstore
|
||||
enable_tcl
|
||||
with_tcl
|
||||
enable_editline
|
||||
enable_readline
|
||||
with_readline_lib
|
||||
@@ -1551,11 +1546,13 @@ Optional Features:
|
||||
optimize for fast installation [default=yes]
|
||||
--disable-libtool-lock avoid locking (might break parallel builds)
|
||||
--disable-largefile omit support for large files
|
||||
--disable-tcl omit building accessory programs that require
|
||||
TCL-dev
|
||||
--enable-test-status Full-screen status of tests
|
||||
--disable-threadsafe Disable mutexing
|
||||
--enable-releasemode Support libtool link to release mode
|
||||
--enable-tempstore Use an in-ram database for temporary tables
|
||||
(never,no,yes,always)
|
||||
--disable-tcl do not build TCL extension
|
||||
--enable-editline enable BSD editline support
|
||||
--disable-readline disable readline support
|
||||
--enable-debug enable debugging & verbose explain
|
||||
@@ -1583,10 +1580,10 @@ Optional Packages:
|
||||
--with-pic try to use only PIC/non-PIC objects [default=use
|
||||
both]
|
||||
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
|
||||
--with-tclsh=PATHNAME full pathname of a tclsh to use
|
||||
--with-tcl=DIR directory containing (tclConfig.sh)
|
||||
--with-wasi-sdk=DIR directory containing the WASI SDK. Triggers
|
||||
cross-compile to WASM.
|
||||
--with-tcl=DIR directory containing tcl configuration
|
||||
(tclConfig.sh)
|
||||
--with-readline-lib specify readline library
|
||||
--with-readline-inc specify readline include paths
|
||||
--with-linenoise=DIR source directory for linenoise library
|
||||
@@ -3945,13 +3942,13 @@ if ${lt_cv_nm_interface+:} false; then :
|
||||
else
|
||||
lt_cv_nm_interface="BSD nm"
|
||||
echo "int some_variable = 0;" > conftest.$ac_ext
|
||||
(eval echo "\"\$as_me:3948: $ac_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:3945: $ac_compile\"" >&5)
|
||||
(eval "$ac_compile" 2>conftest.err)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3951: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval echo "\"\$as_me:3948: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3954: output\"" >&5)
|
||||
(eval echo "\"\$as_me:3951: output\"" >&5)
|
||||
cat conftest.out >&5
|
||||
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
|
||||
lt_cv_nm_interface="MS dumpbin"
|
||||
@@ -5157,7 +5154,7 @@ ia64-*-hpux*)
|
||||
;;
|
||||
*-*-irix6*)
|
||||
# Find out which ABI we are using.
|
||||
echo '#line 5160 "configure"' > conftest.$ac_ext
|
||||
echo '#line 5157 "configure"' > conftest.$ac_ext
|
||||
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
@@ -6682,11 +6679,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:6685: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6682: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:6689: \$? = $ac_status" >&5
|
||||
echo "$as_me:6686: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7021,11 +7018,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7024: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7021: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:7028: \$? = $ac_status" >&5
|
||||
echo "$as_me:7025: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7126,11 +7123,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7129: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7126: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7133: \$? = $ac_status" >&5
|
||||
echo "$as_me:7130: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -7181,11 +7178,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7184: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7181: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7188: \$? = $ac_status" >&5
|
||||
echo "$as_me:7185: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -9561,7 +9558,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9564 "configure"
|
||||
#line 9561 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -9657,7 +9654,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9660 "configure"
|
||||
#line 9657 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -10314,11 +10311,31 @@ done
|
||||
USE_AMALGAMATION=1
|
||||
|
||||
#########
|
||||
# See whether we can run specific tclsh versions known to work well;
|
||||
# if not, then we fall back to plain tclsh.
|
||||
# TODO: try other versions before falling back?
|
||||
# Figure out all the name of a working tclsh and parameters needed to compile against Tcl.
|
||||
# The --with-tcl= and/or --with-tclsh= configuration arguments might be useful for this.
|
||||
#
|
||||
for ac_prog in tclsh8.7 tclsh8.6 tclsh8.5 tclsh
|
||||
|
||||
# Check whether --with-tclsh was given.
|
||||
if test "${with_tclsh+set}" = set; then :
|
||||
withval=$with_tclsh;
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --with-tcl was given.
|
||||
if test "${with_tcl+set}" = set; then :
|
||||
withval=$with_tcl;
|
||||
fi
|
||||
|
||||
# Check whether --enable-tcl was given.
|
||||
if test "${enable_tcl+set}" = set; then :
|
||||
enableval=$enable_tcl; use_tcl=$enableval
|
||||
else
|
||||
use_tcl=yes
|
||||
fi
|
||||
|
||||
original_use_tcl=${use_tcl}
|
||||
if test x"${with_tclsh}" == x -a x"${with_tcl}" == x; then
|
||||
for ac_prog in tclsh8.6 tclsh tclsh9.0
|
||||
do
|
||||
# Extract the first word of "$ac_prog", so it can be a program name with args.
|
||||
set dummy $ac_prog; ac_word=$2
|
||||
@@ -10361,10 +10378,99 @@ fi
|
||||
done
|
||||
test -n "$TCLSH_CMD" || TCLSH_CMD="none"
|
||||
|
||||
with_tclsh=${TCLSH_CMD}
|
||||
fi
|
||||
if test x"${with_tclsh}" != x -a x"${with_tclsh}" != xnone; then
|
||||
TCLSH_CMD=${with_tclsh}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using tclsh at \"$TCLSH_CMD\"" >&5
|
||||
$as_echo "using tclsh at \"$TCLSH_CMD\"" >&6; }
|
||||
if test x"${use_tcl}" = "xyes"; then
|
||||
with_tcl=`${with_tclsh} <${srcdir}/tool/find_tclconfig.tcl`
|
||||
if test x"${with_tcl}" != x; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $TCLSH_CMD recommends the tclConfig.sh at ${with_tcl}" >&5
|
||||
$as_echo "$TCLSH_CMD recommends the tclConfig.sh at ${with_tcl}" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $TCLSH_CMD is unable to recommend a tclConfig.sh" >&5
|
||||
$as_echo "$as_me: WARNING: $TCLSH_CMD is unable to recommend a tclConfig.sh" >&2;}
|
||||
use_tcl=no
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test x"${use_tcl}" = "xyes"; then
|
||||
if test x"${with_tcl}" != x; then
|
||||
if test -r ${with_tcl}/tclConfig.sh; then
|
||||
tclconfig="${with_tcl}/tclConfig.sh"
|
||||
else
|
||||
for i in tcl8.6 tcl9.0 lib; do
|
||||
if test -r ${with_tcl}/$i/tclConfig.sh; then
|
||||
tclconfig=${with_tcl}/$i/tclConfig.sh
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if test ! -r "${tclconfig}"; then
|
||||
as_fn_error $? "no tclConfig.sh file found under ${with_tcl}" "$LINENO" 5
|
||||
fi
|
||||
else
|
||||
# If we have not yet found a tclConfig.sh file, look in $libdir whic is
|
||||
# set automatically by autoconf or by the --prefix command-line option.
|
||||
# See https://sqlite.org/forum/forumpost/e04e693439a22457
|
||||
libdir=${prefix}/lib
|
||||
if test -r ${libdir}/tclConfig.sh; then
|
||||
tclconfig=${libdir}/tclConfig.sh
|
||||
else
|
||||
for i in tcl8.6 tcl9.0 lib; do
|
||||
if test -r ${libdir}/$i/tclConfig.sh; then
|
||||
tclconfig=${libdir}/$i/tclConfig.sh
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if test ! -r "${tclconfig}"; then
|
||||
as_fn_error $? "cannot find a usable tclConfig.sh file.
|
||||
Use --with-tcl=DIR to specify a directory where tclConfig.sh can be found.
|
||||
SQLite does not use TCL internally, but TCL is required to build SQLite
|
||||
from canonical sources and TCL is required for testing." "$LINENO" 5
|
||||
fi
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: loading TCL configuration from ${tclconfig}" >&5
|
||||
$as_echo "loading TCL configuration from ${tclconfig}" >&6; }
|
||||
. ${tclconfig}
|
||||
|
||||
|
||||
|
||||
# There are lots of other configuration variables that are provided by the
|
||||
# tclConfig.sh file and that could be included here. But as of right now,
|
||||
# TCL_LIB_SPEC is the only what that the Makefile uses.
|
||||
HAVE_TCL=1
|
||||
elif test x"${original_use_tcl}" = "xno"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unable to run tests because of --disable-tcl" >&5
|
||||
$as_echo "unable to run tests because of --disable-tcl" >&6; }
|
||||
HAVE_TCL=0
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unable to run tests because no tclConfig.sh file could be located" >&5
|
||||
$as_echo "unable to run tests because no tclConfig.sh file could be located" >&6; }
|
||||
HAVE_TCL=0
|
||||
fi
|
||||
|
||||
if test x"$TCLSH_CMD" == x; then
|
||||
TCLSH_CMD=${TCL_EXEC_PREFIX}/bin/tclsh${TCL_VERSION}
|
||||
if test ! -x ${TCLSH_CMD}; then
|
||||
TCLSH_CMD_2=${TCL_EXEC_PREFIX}/bin/tclsh
|
||||
if test ! -x ${TCLSH_CMD_2}; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find a usable tclsh at either ${TCLSH_CMD} or ${TCLSH_CMD_2}" >&5
|
||||
$as_echo "$as_me: WARNING: cannot find a usable tclsh at either ${TCLSH_CMD} or ${TCLSH_CMD_2}" >&2;}
|
||||
TCLSH_CMD=none
|
||||
else
|
||||
TCLSH_CMD=${TCLSH_CMD_2}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test "$TCLSH_CMD" = "none"; then
|
||||
# If we can't find a local tclsh, then building the amalgamation will fail.
|
||||
# We act as though --disable-amalgamation has been used.
|
||||
echo "Warning: can't find tclsh - defaulting to non-amalgamation build."
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Warning: can't find tclsh - defaulting to non-amalgamation build." >&5
|
||||
$as_echo "$as_me: WARNING: Warning: can't find tclsh - defaulting to non-amalgamation build." >&2;}
|
||||
USE_AMALGAMATION=0
|
||||
TCLSH_CMD="tclsh"
|
||||
fi
|
||||
@@ -10372,7 +10478,6 @@ fi
|
||||
|
||||
|
||||
if test "x${TCLLIBDIR+set}" != "xset" ; then
|
||||
TCLLIBDIR='$(libdir)'
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
|
||||
if test -d $i ; then
|
||||
TCLLIBDIR=$i
|
||||
@@ -10382,6 +10487,23 @@ if test "x${TCLLIBDIR+set}" != "xset" ; then
|
||||
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
|
||||
fi
|
||||
|
||||
#########
|
||||
# Set up options for running tests.
|
||||
#
|
||||
# Check whether --enable-test-status was given.
|
||||
if test "${enable_test_status+set}" = set; then :
|
||||
enableval=$enable_test_status; use_vt100=$enableval
|
||||
else
|
||||
use_vt100=no
|
||||
fi
|
||||
|
||||
if test $use_vt100 != no; then
|
||||
TSTRNNR_OPTS=--status
|
||||
else
|
||||
TSTRNNR_OPTS=
|
||||
fi
|
||||
|
||||
|
||||
|
||||
#########
|
||||
# Set up an appropriate program prefix
|
||||
@@ -10769,213 +10891,6 @@ fi
|
||||
|
||||
|
||||
|
||||
##########
|
||||
# Figure out all the parameters needed to compile against Tcl.
|
||||
#
|
||||
# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
|
||||
# macros in the in the tcl.m4 file of the standard TCL distribution.
|
||||
# Those macros could not be used directly since we have to make some
|
||||
# minor changes to accomodate systems that do not have TCL installed.
|
||||
#
|
||||
# Check whether --enable-tcl was given.
|
||||
if test "${enable_tcl+set}" = set; then :
|
||||
enableval=$enable_tcl; use_tcl=$enableval
|
||||
else
|
||||
use_tcl=yes
|
||||
fi
|
||||
|
||||
if test "${use_tcl}" = "yes" ; then
|
||||
|
||||
# Check whether --with-tcl was given.
|
||||
if test "${with_tcl+set}" = set; then :
|
||||
withval=$with_tcl; with_tclconfig=${withval}
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl configuration" >&5
|
||||
$as_echo_n "checking for Tcl configuration... " >&6; }
|
||||
if ${ac_cv_c_tclconfig+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
# First check to see if --with-tcl was specified.
|
||||
if test x"${with_tclconfig}" != x ; then
|
||||
if test -f "${with_tclconfig}/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
|
||||
else
|
||||
as_fn_error $? "${with_tclconfig} directory doesn't contain tclConfig.sh" "$LINENO" 5
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start autosearch by asking tclsh
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# On ubuntu 14.10, $auto_path on tclsh is not quite correct.
|
||||
# So try again after applying corrections.
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD} | sed 's,/tcltk/tcl,/tcl,g'`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Recent versions of Xcode on Macs hid the tclConfig.sh file
|
||||
# in a strange place.
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# then check for a private Tcl installation
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
../tcl \
|
||||
`ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
|
||||
`ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \
|
||||
`ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \
|
||||
../../tcl \
|
||||
`ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
|
||||
`ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \
|
||||
`ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \
|
||||
../../../tcl \
|
||||
`ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
|
||||
`ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \
|
||||
`ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/unix/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# check in a few common install locations
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
`ls -d ${libdir} 2>/dev/null` \
|
||||
`ls -d /usr/local/lib 2>/dev/null` \
|
||||
`ls -d /usr/contrib/lib 2>/dev/null` \
|
||||
`ls -d /usr/lib 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# check in a few other private locations
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
${srcdir}/../tcl \
|
||||
`ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \
|
||||
`ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \
|
||||
`ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/unix/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
use_tcl=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Can't find Tcl configuration definitions" >&5
|
||||
$as_echo "$as_me: WARNING: Can't find Tcl configuration definitions" >&2;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Without Tcl the regression tests cannot be executed ***" >&5
|
||||
$as_echo "$as_me: WARNING: *** Without Tcl the regression tests cannot be executed ***" >&2;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Consider using --with-tcl=... to define location of Tcl ***" >&5
|
||||
$as_echo "$as_me: WARNING: *** Consider using --with-tcl=... to define location of Tcl ***" >&2;}
|
||||
else
|
||||
TCL_BIN_DIR=${ac_cv_c_tclconfig}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found $TCL_BIN_DIR/tclConfig.sh" >&5
|
||||
$as_echo "found $TCL_BIN_DIR/tclConfig.sh" >&6; }
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for existence of $TCL_BIN_DIR/tclConfig.sh" >&5
|
||||
$as_echo_n "checking for existence of $TCL_BIN_DIR/tclConfig.sh... " >&6; }
|
||||
if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: loading" >&5
|
||||
$as_echo "loading" >&6; }
|
||||
. $TCL_BIN_DIR/tclConfig.sh
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5
|
||||
$as_echo "file not found" >&6; }
|
||||
fi
|
||||
|
||||
#
|
||||
# If the TCL_BIN_DIR is the build directory (not the install directory),
|
||||
# then set the common variable name to the value of the build variables.
|
||||
# For example, the variable TCL_LIB_SPEC will be set to the value
|
||||
# of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
|
||||
# instead of TCL_BUILD_LIB_SPEC since it will work with both an
|
||||
# installed and uninstalled version of Tcl.
|
||||
#
|
||||
|
||||
if test -f $TCL_BIN_DIR/Makefile ; then
|
||||
TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
|
||||
TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
|
||||
TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
|
||||
fi
|
||||
|
||||
#
|
||||
# eval is required to do the TCL_DBGX substitution
|
||||
#
|
||||
|
||||
eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
|
||||
eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
|
||||
eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
|
||||
|
||||
eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
|
||||
eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
|
||||
eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
fi
|
||||
if test "${use_tcl}" = "no" ; then
|
||||
HAVE_TCL=""
|
||||
else
|
||||
HAVE_TCL=1
|
||||
fi
|
||||
|
||||
|
||||
##########
|
||||
# Figure out what C libraries are required to compile programs
|
||||
# that use "readline()" library.
|
||||
|
||||
+104
-191
@@ -116,15 +116,101 @@ AC_CHECK_FUNCS([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_s
|
||||
USE_AMALGAMATION=1
|
||||
|
||||
#########
|
||||
# See whether we can run specific tclsh versions known to work well;
|
||||
# if not, then we fall back to plain tclsh.
|
||||
# TODO: try other versions before falling back?
|
||||
#
|
||||
AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.7 tclsh8.6 tclsh8.5 tclsh], none)
|
||||
# Figure out all the name of a working tclsh and parameters needed to compile against Tcl.
|
||||
# The --with-tcl= and/or --with-tclsh= configuration arguments might be useful for this.
|
||||
#
|
||||
AC_ARG_WITH(tclsh, AS_HELP_STRING([--with-tclsh=PATHNAME],[full pathname of a tclsh to use]))
|
||||
AC_ARG_WITH(tcl, AS_HELP_STRING([--with-tcl=DIR],[directory containing (tclConfig.sh)]))
|
||||
AC_ARG_ENABLE(tcl, AS_HELP_STRING([--disable-tcl],[omit building accessory programs that require TCL-dev]),
|
||||
[use_tcl=$enableval],[use_tcl=yes])
|
||||
original_use_tcl=${use_tcl}
|
||||
if test x"${with_tclsh}" == x -a x"${with_tcl}" == x; then
|
||||
AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.6 tclsh tclsh9.0],none)
|
||||
with_tclsh=${TCLSH_CMD}
|
||||
fi
|
||||
if test x"${with_tclsh}" != x -a x"${with_tclsh}" != xnone; then
|
||||
TCLSH_CMD=${with_tclsh}
|
||||
AC_MSG_RESULT([using tclsh at "$TCLSH_CMD"])
|
||||
if test x"${use_tcl}" = "xyes"; then
|
||||
with_tcl=`${with_tclsh} <${srcdir}/tool/find_tclconfig.tcl`
|
||||
if test x"${with_tcl}" != x; then
|
||||
AC_MSG_RESULT([$TCLSH_CMD recommends the tclConfig.sh at ${with_tcl}])
|
||||
else
|
||||
AC_MSG_WARN([$TCLSH_CMD is unable to recommend a tclConfig.sh])
|
||||
use_tcl=no
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test x"${use_tcl}" = "xyes"; then
|
||||
if test x"${with_tcl}" != x; then
|
||||
if test -r ${with_tcl}/tclConfig.sh; then
|
||||
tclconfig="${with_tcl}/tclConfig.sh"
|
||||
else
|
||||
for i in tcl8.6 tcl9.0 lib; do
|
||||
if test -r ${with_tcl}/$i/tclConfig.sh; then
|
||||
tclconfig=${with_tcl}/$i/tclConfig.sh
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if test ! -r "${tclconfig}"; then
|
||||
AC_MSG_ERROR([no tclConfig.sh file found under ${with_tcl}])
|
||||
fi
|
||||
else
|
||||
# If we have not yet found a tclConfig.sh file, look in $libdir whic is
|
||||
# set automatically by autoconf or by the --prefix command-line option.
|
||||
# See https://sqlite.org/forum/forumpost/e04e693439a22457
|
||||
libdir=${prefix}/lib
|
||||
if test -r ${libdir}/tclConfig.sh; then
|
||||
tclconfig=${libdir}/tclConfig.sh
|
||||
else
|
||||
for i in tcl8.6 tcl9.0 lib; do
|
||||
if test -r ${libdir}/$i/tclConfig.sh; then
|
||||
tclconfig=${libdir}/$i/tclConfig.sh
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if test ! -r "${tclconfig}"; then
|
||||
AC_MSG_ERROR([cannot find a usable tclConfig.sh file.
|
||||
Use --with-tcl=DIR to specify a directory where tclConfig.sh can be found.
|
||||
SQLite does not use TCL internally, but TCL is required to build SQLite
|
||||
from canonical sources and TCL is required for testing.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT([loading TCL configuration from ${tclconfig}])
|
||||
. ${tclconfig}
|
||||
AC_SUBST(TCL_INCLUDE_SPEC)
|
||||
AC_SUBST(TCL_LIB_SPEC)
|
||||
AC_SUBST(TCL_STUB_LIB_SPEC)
|
||||
# There are lots of other configuration variables that are provided by the
|
||||
# tclConfig.sh file and that could be included here. But as of right now,
|
||||
# TCL_LIB_SPEC is the only what that the Makefile uses.
|
||||
HAVE_TCL=1
|
||||
elif test x"${original_use_tcl}" = "xno"; then
|
||||
AC_MSG_RESULT([unable to run tests because of --disable-tcl])
|
||||
HAVE_TCL=0
|
||||
else
|
||||
AC_MSG_RESULT([unable to run tests because no tclConfig.sh file could be located])
|
||||
HAVE_TCL=0
|
||||
fi
|
||||
AC_SUBST(HAVE_TCL)
|
||||
if test x"$TCLSH_CMD" == x; then
|
||||
TCLSH_CMD=${TCL_EXEC_PREFIX}/bin/tclsh${TCL_VERSION}
|
||||
if test ! -x ${TCLSH_CMD}; then
|
||||
TCLSH_CMD_2=${TCL_EXEC_PREFIX}/bin/tclsh
|
||||
if test ! -x ${TCLSH_CMD_2}; then
|
||||
AC_MSG_WARN([cannot find a usable tclsh at either ${TCLSH_CMD} or ${TCLSH_CMD_2}])
|
||||
TCLSH_CMD=none
|
||||
else
|
||||
TCLSH_CMD=${TCLSH_CMD_2}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test "$TCLSH_CMD" = "none"; then
|
||||
# If we can't find a local tclsh, then building the amalgamation will fail.
|
||||
# We act as though --disable-amalgamation has been used.
|
||||
echo "Warning: can't find tclsh - defaulting to non-amalgamation build."
|
||||
AC_MSG_WARN([Warning: can't find tclsh - defaulting to non-amalgamation build.])
|
||||
USE_AMALGAMATION=0
|
||||
TCLSH_CMD="tclsh"
|
||||
fi
|
||||
@@ -132,7 +218,6 @@ AC_SUBST(TCLSH_CMD)
|
||||
|
||||
AC_ARG_VAR([TCLLIBDIR], [Where to install tcl plugin])
|
||||
if test "x${TCLLIBDIR+set}" != "xset" ; then
|
||||
TCLLIBDIR='$(libdir)'
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}` ; do
|
||||
if test -d $i ; then
|
||||
TCLLIBDIR=$i
|
||||
@@ -142,6 +227,18 @@ if test "x${TCLLIBDIR+set}" != "xset" ; then
|
||||
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
|
||||
fi
|
||||
|
||||
#########
|
||||
# Set up options for running tests.
|
||||
#
|
||||
AC_ARG_ENABLE(test-status, AS_HELP_STRING([--enable-test-status],[Full-screen status of tests]),
|
||||
[use_vt100=$enableval],[use_vt100=no])
|
||||
if test $use_vt100 != no; then
|
||||
TSTRNNR_OPTS=--status
|
||||
else
|
||||
TSTRNNR_OPTS=
|
||||
fi
|
||||
AC_SUBST(TSTRNNR_OPTS)
|
||||
|
||||
|
||||
#########
|
||||
# Set up an appropriate program prefix
|
||||
@@ -339,190 +436,6 @@ AC_SUBST(SQLITE_OS_UNIX)
|
||||
AC_SUBST(SQLITE_OS_WIN)
|
||||
AC_SUBST(TARGET_EXEEXT)
|
||||
|
||||
##########
|
||||
# Figure out all the parameters needed to compile against Tcl.
|
||||
#
|
||||
# This code is derived from the SC_PATH_TCLCONFIG and SC_LOAD_TCLCONFIG
|
||||
# macros in the in the tcl.m4 file of the standard TCL distribution.
|
||||
# Those macros could not be used directly since we have to make some
|
||||
# minor changes to accomodate systems that do not have TCL installed.
|
||||
#
|
||||
AC_ARG_ENABLE(tcl, AS_HELP_STRING([--disable-tcl],[do not build TCL extension]),
|
||||
[use_tcl=$enableval],[use_tcl=yes])
|
||||
if test "${use_tcl}" = "yes" ; then
|
||||
AC_ARG_WITH(tcl, AS_HELP_STRING([--with-tcl=DIR],[directory containing tcl configuration (tclConfig.sh)]), with_tclconfig=${withval})
|
||||
AC_MSG_CHECKING([for Tcl configuration])
|
||||
AC_CACHE_VAL(ac_cv_c_tclconfig,[
|
||||
# First check to see if --with-tcl was specified.
|
||||
if test x"${with_tclconfig}" != x ; then
|
||||
if test -f "${with_tclconfig}/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
|
||||
else
|
||||
AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start autosearch by asking tclsh
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD}`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# On ubuntu 14.10, $auto_path on tclsh is not quite correct.
|
||||
# So try again after applying corrections.
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in `echo 'puts stdout $auto_path' | ${TCLSH_CMD} | sed 's,/tcltk/tcl,/tcl,g'`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Recent versions of Xcode on Macs hid the tclConfig.sh file
|
||||
# in a strange place.
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
if test x"$cross_compiling" = xno; then
|
||||
for i in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX*.sdk/usr/lib
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# then check for a private Tcl installation
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
../tcl \
|
||||
`ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
|
||||
../../tcl \
|
||||
`ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
|
||||
../../../tcl \
|
||||
`ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/unix/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# check in a few common install locations
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
`ls -d ${libdir} 2>/dev/null` \
|
||||
`ls -d /usr/local/lib 2>/dev/null` \
|
||||
`ls -d /usr/contrib/lib 2>/dev/null` \
|
||||
`ls -d /usr/lib 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# check in a few other private locations
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
for i in \
|
||||
${srcdir}/../tcl \
|
||||
`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
|
||||
`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
|
||||
`ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null`
|
||||
do
|
||||
if test -f "$i/unix/tclConfig.sh" ; then
|
||||
ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
if test x"${ac_cv_c_tclconfig}" = x ; then
|
||||
use_tcl=no
|
||||
AC_MSG_WARN(Can't find Tcl configuration definitions)
|
||||
AC_MSG_WARN(*** Without Tcl the regression tests cannot be executed ***)
|
||||
AC_MSG_WARN(*** Consider using --with-tcl=... to define location of Tcl ***)
|
||||
else
|
||||
TCL_BIN_DIR=${ac_cv_c_tclconfig}
|
||||
AC_MSG_RESULT(found $TCL_BIN_DIR/tclConfig.sh)
|
||||
|
||||
AC_MSG_CHECKING([for existence of $TCL_BIN_DIR/tclConfig.sh])
|
||||
if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
|
||||
AC_MSG_RESULT([loading])
|
||||
. $TCL_BIN_DIR/tclConfig.sh
|
||||
else
|
||||
AC_MSG_RESULT([file not found])
|
||||
fi
|
||||
|
||||
#
|
||||
# If the TCL_BIN_DIR is the build directory (not the install directory),
|
||||
# then set the common variable name to the value of the build variables.
|
||||
# For example, the variable TCL_LIB_SPEC will be set to the value
|
||||
# of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
|
||||
# instead of TCL_BUILD_LIB_SPEC since it will work with both an
|
||||
# installed and uninstalled version of Tcl.
|
||||
#
|
||||
|
||||
if test -f $TCL_BIN_DIR/Makefile ; then
|
||||
TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
|
||||
TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
|
||||
TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
|
||||
fi
|
||||
|
||||
#
|
||||
# eval is required to do the TCL_DBGX substitution
|
||||
#
|
||||
|
||||
eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
|
||||
eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
|
||||
eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
|
||||
|
||||
eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
|
||||
eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
|
||||
eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
|
||||
|
||||
AC_SUBST(TCL_VERSION)
|
||||
AC_SUBST(TCL_BIN_DIR)
|
||||
AC_SUBST(TCL_SRC_DIR)
|
||||
AC_SUBST(TCL_INCLUDE_SPEC)
|
||||
|
||||
AC_SUBST(TCL_LIB_FILE)
|
||||
AC_SUBST(TCL_LIB_FLAG)
|
||||
AC_SUBST(TCL_LIB_SPEC)
|
||||
|
||||
AC_SUBST(TCL_STUB_LIB_FILE)
|
||||
AC_SUBST(TCL_STUB_LIB_FLAG)
|
||||
AC_SUBST(TCL_STUB_LIB_SPEC)
|
||||
AC_SUBST(TCL_SHLIB_SUFFIX)
|
||||
fi
|
||||
fi
|
||||
if test "${use_tcl}" = "no" ; then
|
||||
HAVE_TCL=""
|
||||
else
|
||||
HAVE_TCL=1
|
||||
fi
|
||||
AC_SUBST(HAVE_TCL)
|
||||
|
||||
##########
|
||||
# Figure out what C libraries are required to compile programs
|
||||
# that use "readline()" library.
|
||||
|
||||
@@ -57,11 +57,22 @@ canonical source on a new Windows 11 PC, as of 2023-11-01:
|
||||
<ul>
|
||||
<li> `nmake /f makefile.msc`
|
||||
<li> `nmake /f makefile.msc sqlite3.c`
|
||||
<li> `nmake /f makefile.msc sqlite3.exe`
|
||||
<li> `nmake /f makefile.msc sqldiff.exe`
|
||||
<li> `nmake /f makefile.msc tclextension-install`
|
||||
<li> `nmake /f makefile.msc devtest`
|
||||
<li> `nmake /f makefile.msc releasetest`
|
||||
<li> `nmake /f makefile.msc sqlite3.exe`
|
||||
<li> `nmake /f makefile.msc sqlite3_analyzer.exe`
|
||||
</ul>
|
||||
|
||||
It is not required that you run the "tclextension-install" target prior to
|
||||
running tests. However, the tests will run more smoothly if you do.
|
||||
The version of SQLite used for the TCL extension does *not* need to
|
||||
correspond to the version of SQLite under test. So you can install the
|
||||
SQLite TCL extension once, and then use it to test many different versions
|
||||
of SQLite.
|
||||
|
||||
|
||||
7. For a debugging build of the CLI, where the ".treetrace" and ".wheretrace"
|
||||
commands work, add the DEBUG=3 argument to nmake. Like this:
|
||||
<ul>
|
||||
|
||||
+89
-7
@@ -53,11 +53,6 @@
|
||||
# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
|
||||
#endif
|
||||
|
||||
#if CIO_WIN_WC_XLATE
|
||||
/* Character used to represent a known-incomplete UTF-8 char group (�) */
|
||||
static WCHAR cBadGroup = 0xfffd;
|
||||
#endif
|
||||
|
||||
#if CIO_WIN_WC_XLATE
|
||||
static HANDLE handleOfFile(FILE *pf){
|
||||
int fileDesc = _fileno(pf);
|
||||
@@ -600,6 +595,93 @@ oPutbUtf8(const char *cBuf, int nAccept){
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Flush the given output stream. Return non-zero for success, else 0.
|
||||
*/
|
||||
#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
|
||||
SQLITE_INTERNAL_LINKAGE int
|
||||
fFlushBuffer(FILE *pfOut){
|
||||
# if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE)
|
||||
return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0;
|
||||
# else
|
||||
return fflush(pfOut);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CIO_WIN_WC_XLATE \
|
||||
&& !defined(SHELL_OMIT_FIO_DUPE) \
|
||||
&& defined(SQLITE_USE_ONLY_WIN32)
|
||||
static struct FileAltIds {
|
||||
int fd;
|
||||
HANDLE fh;
|
||||
} altIdsOfFile(FILE *pf){
|
||||
struct FileAltIds rv = { _fileno(pf) };
|
||||
union { intptr_t osfh; HANDLE fh; } fid = {
|
||||
(rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE
|
||||
};
|
||||
rv.fh = fid.fh;
|
||||
return rv;
|
||||
}
|
||||
|
||||
SQLITE_INTERNAL_LINKAGE size_t
|
||||
cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){
|
||||
size_t rv = 0;
|
||||
struct FileAltIds fai = altIdsOfFile(pf);
|
||||
int fmode = _setmode(fai.fd, _O_BINARY);
|
||||
_setmode(fai.fd, fmode);
|
||||
while( rv < ocnt ){
|
||||
size_t nbo = osz;
|
||||
while( nbo > 0 ){
|
||||
DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo;
|
||||
BOOL wrc = TRUE;
|
||||
BOOL genCR = (fmode & _O_TEXT)!=0;
|
||||
if( genCR ){
|
||||
const char *pnl = (const char*)memchr(buf, '\n', nbo);
|
||||
if( pnl ) nbo = pnl - (const char*)buf;
|
||||
else genCR = 0;
|
||||
}
|
||||
if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0);
|
||||
if( genCR && wrc ){
|
||||
wrc = WriteFile(fai.fh, "\r\n", 2, 0,0);
|
||||
++dwno; /* Skip over the LF */
|
||||
}
|
||||
if( !wrc ) return rv;
|
||||
buf = (const char*)buf + dwno;
|
||||
nbo += dwno;
|
||||
}
|
||||
++rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* An fgets() equivalent, using Win32 file API for actual input.
|
||||
** Input ends when given buffer is filled or a newline is read.
|
||||
** If the FILE object is in text mode, swallows 0x0D. (ASCII CR)
|
||||
*/
|
||||
SQLITE_INTERNAL_LINKAGE char *
|
||||
cfGets(char *cBuf, int n, FILE *pf){
|
||||
int nci = 0;
|
||||
struct FileAltIds fai = altIdsOfFile(pf);
|
||||
int fmode = _setmode(fai.fd, _O_BINARY);
|
||||
BOOL eatCR = (fmode & _O_TEXT)!=0;
|
||||
_setmode(fai.fd, fmode);
|
||||
while( nci < n-1 ){
|
||||
DWORD nr;
|
||||
if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break;
|
||||
if( nr>0 && (!eatCR || cBuf[nci]!='\r') ){
|
||||
nci += nr;
|
||||
if( cBuf[nci-nr]=='\n' ) break;
|
||||
}
|
||||
}
|
||||
if( nci < n ) cBuf[nci] = 0;
|
||||
return (nci>0)? cBuf : 0;
|
||||
}
|
||||
# else
|
||||
# define cfWrite(b,os,no,f) fwrite(b,os,no,f)
|
||||
# define cfGets(b,n,f) fgets(b,n,f)
|
||||
# endif
|
||||
|
||||
# ifdef CONSIO_EPUTB
|
||||
SQLITE_INTERNAL_LINKAGE int
|
||||
ePutbUtf8(const char *cBuf, int nAccept){
|
||||
@@ -611,7 +693,7 @@ ePutbUtf8(const char *cBuf, int nAccept){
|
||||
return conZstrEmit(ppst, cBuf, nAccept);
|
||||
}else {
|
||||
# endif
|
||||
return (int)fwrite(cBuf, 1, nAccept, pfErr);
|
||||
return (int)cfWrite(cBuf, 1, nAccept, pfErr);
|
||||
# if CIO_WIN_WC_XLATE
|
||||
}
|
||||
# endif
|
||||
@@ -677,7 +759,7 @@ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
|
||||
# endif
|
||||
}else{
|
||||
# endif
|
||||
return fgets(cBuf, ncMax, pfIn);
|
||||
return cfGets(cBuf, ncMax, pfIn);
|
||||
# if CIO_WIN_WC_XLATE
|
||||
}
|
||||
# endif
|
||||
|
||||
+11
-4
@@ -176,12 +176,19 @@ SQLITE_INTERNAL_LINKAGE int
|
||||
ePutbUtf8(const char *cBuf, int nAccept);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Flush the given output stream. Return non-zero for success, else 0.
|
||||
*/
|
||||
#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
|
||||
SQLITE_INTERNAL_LINKAGE int
|
||||
fFlushBuffer(FILE *pfOut);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Collect input like fgets(...) with special provisions for input
|
||||
** from the console on platforms that require same. Defers to the
|
||||
** C library fgets() when input is not from the console. Newline
|
||||
** translation may be done as set by set{Binary,Text}Mode(). As a
|
||||
** convenience, pfIn==NULL is treated as stdin.
|
||||
** from the console on such platforms as require same. Newline
|
||||
** translation may be done as set by set{Binary,Text}Mode().
|
||||
** As a convenience, pfIn==NULL is treated as stdin.
|
||||
*/
|
||||
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
|
||||
/* Like fGetsUtf8 except stream is always the designated input. */
|
||||
|
||||
@@ -428,6 +428,8 @@ do_execsql_test 5.0 {
|
||||
|
||||
WITH s(i) AS ( VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<100)
|
||||
INSERT INTO t2 SELECT (i-1)/20, (i-1)/5 FROM s;
|
||||
|
||||
CREATE INDEX i1 ON t1( lower(a) );
|
||||
}
|
||||
do_candidates_test 5.1 {
|
||||
SELECT * FROM t1,t2 WHERE (b=? OR a=?) AND (c=? OR d=?)
|
||||
@@ -457,6 +459,7 @@ do_execsql_test 5.3 {
|
||||
ANALYZE;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY 1, 2;
|
||||
} {
|
||||
t1 i1 {100 50}
|
||||
t1 t1_idx_00000061 {100 50}
|
||||
t1 t1_idx_00000062 {100 20}
|
||||
t1 t1_idx_000123a7 {100 50 17}
|
||||
@@ -465,6 +468,13 @@ do_execsql_test 5.3 {
|
||||
t2 t2_idx_0001295b {100 20 5}
|
||||
}
|
||||
|
||||
do_catchsql_test 5.4 {
|
||||
SELECT sqlite_expert_rem(123, 123);
|
||||
} {1 {no such function: sqlite_expert_rem}}
|
||||
do_catchsql_test 5.5 {
|
||||
SELECT sqlite_expert_sample();
|
||||
} {1 {no such function: sqlite_expert_sample}}
|
||||
|
||||
if 0 {
|
||||
do_test expert1-6.0 {
|
||||
catchcmd :memory: {
|
||||
@@ -484,4 +494,17 @@ USE TEMP B-TREE FOR ORDER BY
|
||||
}}
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE x1(a, b, c, d);
|
||||
CREATE INDEX x1ab ON x1(a, lower(b));
|
||||
CREATE INDEX x1dcba ON x1(d, b+c, a);
|
||||
}
|
||||
|
||||
do_candidates_test 6.1 {
|
||||
SELECT * FROM x1 WHERE b=? ORDER BY a;
|
||||
} {
|
||||
CREATE INDEX x1_idx_0001267f ON x1(b, a);
|
||||
CREATE INDEX x1_idx_00000062 ON x1(b);
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -626,7 +626,7 @@ static int expertFilter(
|
||||
pCsr->pData = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
|
||||
"SELECT * FROM main.%Q WHERE sample()", pVtab->pTab->zName
|
||||
"SELECT * FROM main.%Q WHERE sqlite_expert_sample()", pVtab->pTab->zName
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1500,7 +1500,7 @@ struct IdxRemCtx {
|
||||
};
|
||||
|
||||
/*
|
||||
** Implementation of scalar function rem().
|
||||
** Implementation of scalar function sqlite_expert_rem().
|
||||
*/
|
||||
static void idxRemFunc(
|
||||
sqlite3_context *pCtx,
|
||||
@@ -1513,7 +1513,7 @@ static void idxRemFunc(
|
||||
assert( argc==2 );
|
||||
|
||||
iSlot = sqlite3_value_int(argv[0]);
|
||||
assert( iSlot<=p->nSlot );
|
||||
assert( iSlot<p->nSlot );
|
||||
pSlot = &p->aSlot[iSlot];
|
||||
|
||||
switch( pSlot->eType ){
|
||||
@@ -1623,8 +1623,15 @@ static int idxPopulateOneStat1(
|
||||
const char *zComma = zCols==0 ? "" : ", ";
|
||||
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
|
||||
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
|
||||
if( zName==0 ){
|
||||
/* This index contains an expression. Ignore it. */
|
||||
sqlite3_free(zCols);
|
||||
sqlite3_free(zOrder);
|
||||
return sqlite3_reset(pIndexXInfo);
|
||||
}
|
||||
zCols = idxAppendText(&rc, zCols,
|
||||
"%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl
|
||||
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
|
||||
zComma, zName, nCol, zName, zColl
|
||||
);
|
||||
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
|
||||
}
|
||||
@@ -1757,13 +1764,13 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
|
||||
rc = sqlite3_create_function(
|
||||
dbrem, "rem", 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
|
||||
rc = sqlite3_create_function(dbrem, "sqlite_expert_rem",
|
||||
2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(
|
||||
p->db, "sample", 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
|
||||
rc = sqlite3_create_function(p->db, "sqlite_expert_sample",
|
||||
0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1815,6 +1822,9 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
|
||||
}
|
||||
|
||||
sqlite3_create_function(p->db, "sqlite_expert_rem", 2, SQLITE_UTF8, 0,0,0,0);
|
||||
sqlite3_create_function(p->db, "sqlite_expert_sample", 0,SQLITE_UTF8,0,0,0,0);
|
||||
|
||||
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -16,15 +16,7 @@
|
||||
#include "sqlite3expert.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
|
||||
@@ -398,6 +398,7 @@ static int fts3SnippetNextCandidate(SnippetIter *pIter){
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert( pIter->nSnippet>=0 );
|
||||
pIter->iCurrent = iStart = iEnd - pIter->nSnippet + 1;
|
||||
for(i=0; i<pIter->nPhrase; i++){
|
||||
SnippetPhrase *pPhrase = &pIter->aPhrase[i];
|
||||
|
||||
+12
-4
@@ -78,6 +78,8 @@ static int fts3termConnectMethod(
|
||||
iIndex = atoi(argv[4]);
|
||||
argc--;
|
||||
}
|
||||
|
||||
*ppVtab = 0;
|
||||
|
||||
/* The user should specify a single argument - the name of an fts3 table. */
|
||||
if( argc!=4 ){
|
||||
@@ -95,12 +97,17 @@ static int fts3termConnectMethod(
|
||||
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
nByte = sizeof(Fts3termTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
|
||||
p = (Fts3termTable *)sqlite3_malloc64(nByte);
|
||||
nByte = sizeof(Fts3termTable);
|
||||
p = (Fts3termTable *)sqlite3Fts3MallocZero(nByte);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
memset(p, 0, (size_t)nByte);
|
||||
|
||||
p->pFts3Tab = (Fts3Table *)&p[1];
|
||||
p->pFts3Tab = (Fts3Table*)sqlite3Fts3MallocZero(
|
||||
sizeof(Fts3Table) + nDb + nFts3 + 2
|
||||
);
|
||||
if( p->pFts3Tab==0 ){
|
||||
sqlite3_free(p);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
||||
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
||||
p->pFts3Tab->db = db;
|
||||
@@ -130,6 +137,7 @@ static int fts3termDisconnectMethod(sqlite3_vtab *pVtab){
|
||||
sqlite3_finalize(pFts3->aStmt[i]);
|
||||
}
|
||||
sqlite3_free(pFts3->zSegmentsTbl);
|
||||
sqlite3_free(pFts3);
|
||||
sqlite3_free(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
+13
-16
@@ -18,14 +18,7 @@
|
||||
** that the sqlite3_tokenizer_module.xLanguage() method is invoked correctly.
|
||||
*/
|
||||
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -167,7 +160,8 @@ static int SQLITE_TCLAPI fts3_near_match_cmd(
|
||||
Tcl_Obj *pPhrasecount = 0;
|
||||
|
||||
Tcl_Obj **apExprToken;
|
||||
int nExprToken;
|
||||
Tcl_Size nExprToken;
|
||||
Tcl_Size nn;
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
|
||||
@@ -201,23 +195,25 @@ static int SQLITE_TCLAPI fts3_near_match_cmd(
|
||||
}
|
||||
}
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, objv[1], &doc.nToken, &apDocToken);
|
||||
rc = Tcl_ListObjGetElements(interp, objv[1], &nn, &apDocToken);
|
||||
doc.nToken = (int)nn;
|
||||
if( rc!=TCL_OK ) goto near_match_out;
|
||||
doc.aToken = (NearToken *)ckalloc(doc.nToken*sizeof(NearToken));
|
||||
for(ii=0; ii<doc.nToken; ii++){
|
||||
doc.aToken[ii].z = Tcl_GetStringFromObj(apDocToken[ii], &doc.aToken[ii].n);
|
||||
doc.aToken[ii].z = Tcl_GetStringFromObj(apDocToken[ii], &nn);
|
||||
doc.aToken[ii].n = (int)nn;
|
||||
}
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, objv[2], &nExprToken, &apExprToken);
|
||||
if( rc!=TCL_OK ) goto near_match_out;
|
||||
|
||||
nPhrase = (nExprToken + 1) / 2;
|
||||
nPhrase = (int)(nExprToken + 1) / 2;
|
||||
aPhrase = (NearPhrase *)ckalloc(nPhrase * sizeof(NearPhrase));
|
||||
memset(aPhrase, 0, nPhrase * sizeof(NearPhrase));
|
||||
for(ii=0; ii<nPhrase; ii++){
|
||||
Tcl_Obj *pPhrase = apExprToken[ii*2];
|
||||
Tcl_Obj **apToken;
|
||||
int nToken;
|
||||
Tcl_Size nToken;
|
||||
int jj;
|
||||
|
||||
rc = Tcl_ListObjGetElements(interp, pPhrase, &nToken, &apToken);
|
||||
@@ -227,11 +223,12 @@ static int SQLITE_TCLAPI fts3_near_match_cmd(
|
||||
rc = TCL_ERROR;
|
||||
goto near_match_out;
|
||||
}
|
||||
for(jj=0; jj<nToken; jj++){
|
||||
for(jj=0; jj<(int)nToken; jj++){
|
||||
NearToken *pT = &aPhrase[ii].aToken[jj];
|
||||
pT->z = Tcl_GetStringFromObj(apToken[jj], &pT->n);
|
||||
pT->z = Tcl_GetStringFromObj(apToken[jj], &nn);
|
||||
pT->n = (int)nn;
|
||||
}
|
||||
aPhrase[ii].nToken = nToken;
|
||||
aPhrase[ii].nToken = (int)nToken;
|
||||
}
|
||||
for(ii=1; ii<nPhrase; ii++){
|
||||
Tcl_Obj *pNear = apExprToken[2*ii-1];
|
||||
|
||||
@@ -226,11 +226,7 @@ int sqlite3Fts3InitTokenizer(
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
|
||||
@@ -628,6 +628,9 @@ proc print_categories {lMap} {
|
||||
$caseP
|
||||
$caseS
|
||||
$caseZ
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ proc get_struct_docs {data names} {
|
||||
set current_doc ""
|
||||
}
|
||||
set subject n/a
|
||||
regexp {^ *([[:alpha:]]*)} $line -> subject
|
||||
regexp {^ *([[:alnum:]_]*)} $line -> subject
|
||||
if {[lsearch $names $subject]>=0} {
|
||||
set current_header $subject
|
||||
} else {
|
||||
@@ -108,8 +108,11 @@ proc get_tokenizer_docs {data} {
|
||||
append res "<dt><b>$line</b></dt><dd><p style=margin-top:0>\n"
|
||||
continue
|
||||
}
|
||||
if {[regexp {FTS5_TOKENIZER} $line]} {
|
||||
set line </dl><p>
|
||||
}
|
||||
if {[regexp {SYNONYM SUPPORT} $line]} {
|
||||
set line "</dl><h3>Synonym Support</h3>"
|
||||
set line "<h3>Synonym Support</h3>"
|
||||
}
|
||||
if {[string trim $line] == ""} {
|
||||
append res "<p>\n"
|
||||
|
||||
+115
-4
@@ -308,9 +308,32 @@ struct Fts5PhraseIter {
|
||||
**
|
||||
** This API can be quite slow if used with an FTS5 table created with the
|
||||
** "detail=none" or "detail=column" option.
|
||||
**
|
||||
** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
|
||||
** If parameter iCol is less than zero, or greater than or equal to the
|
||||
** number of columns in the table, SQLITE_RANGE is returned.
|
||||
**
|
||||
** Otherwise, this function attempts to retrieve the locale associated
|
||||
** with column iCol of the current row. Usually, there is no associated
|
||||
** locale, and output parameters (*pzLocale) and (*pnLocale) are set
|
||||
** to NULL and 0, respectively. However, if the fts5_locale() function
|
||||
** was used to associate a locale with the value when it was inserted
|
||||
** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated
|
||||
** buffer containing the name of the locale in utf-8 encoding. (*pnLocale)
|
||||
** is set to the size in bytes of the buffer, not including the
|
||||
** nul-terminator.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned. Or, if an error occurs, an
|
||||
** SQLite error code is returned. The final value of the output parameters
|
||||
** is undefined in this case.
|
||||
**
|
||||
** xTokenize_v2:
|
||||
** Tokenize text using the tokenizer belonging to the FTS5 table. This
|
||||
** API is the same as the xTokenize() API, except that it allows a tokenizer
|
||||
** locale to be specified.
|
||||
*/
|
||||
struct Fts5ExtensionApi {
|
||||
int iVersion; /* Currently always set to 3 */
|
||||
int iVersion; /* Currently always set to 4 */
|
||||
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
|
||||
@@ -352,6 +375,15 @@ struct Fts5ExtensionApi {
|
||||
const char **ppToken, int *pnToken
|
||||
);
|
||||
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
|
||||
|
||||
/* Below this point are iVersion>=4 only */
|
||||
int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn);
|
||||
int (*xTokenize_v2)(Fts5Context*,
|
||||
const char *pText, int nText, /* Text to tokenize */
|
||||
const char *pLocale, int nLocale, /* Locale to pass to tokenizer */
|
||||
void *pCtx, /* Context passed to xToken() */
|
||||
int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -364,6 +396,7 @@ struct Fts5ExtensionApi {
|
||||
** Applications may also register custom tokenizer types. A tokenizer
|
||||
** is registered by providing fts5 with a populated instance of the
|
||||
** following structure. All structure methods must be defined, setting
|
||||
**
|
||||
** any member of the fts5_tokenizer struct to NULL leads to undefined
|
||||
** behaviour. The structure methods are expected to function as follows:
|
||||
**
|
||||
@@ -372,7 +405,7 @@ struct Fts5ExtensionApi {
|
||||
** A tokenizer instance is required to actually tokenize text.
|
||||
**
|
||||
** The first argument passed to this function is a copy of the (void*)
|
||||
** pointer provided by the application when the fts5_tokenizer object
|
||||
** pointer provided by the application when the fts5_tokenizer_v2 object
|
||||
** was registered with FTS5 (the third argument to xCreateTokenizer()).
|
||||
** The second and third arguments are an array of nul-terminated strings
|
||||
** containing the tokenizer arguments, if any, specified following the
|
||||
@@ -396,7 +429,7 @@ struct Fts5ExtensionApi {
|
||||
** argument passed to this function is a pointer to an Fts5Tokenizer object
|
||||
** returned by an earlier call to xCreate().
|
||||
**
|
||||
** The second argument indicates the reason that FTS5 is requesting
|
||||
** The third argument indicates the reason that FTS5 is requesting
|
||||
** tokenization of the supplied text. This is always one of the following
|
||||
** four values:
|
||||
**
|
||||
@@ -420,6 +453,13 @@ struct Fts5ExtensionApi {
|
||||
** on a columnsize=0 database.
|
||||
** </ul>
|
||||
**
|
||||
** The sixth and seventh arguments passed to xTokenize() - pLocale and
|
||||
** nLocale - are a pointer to a buffer containing the locale to use for
|
||||
** tokenization (e.g. "en_US") and its size in bytes, respectively. The
|
||||
** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in
|
||||
** which case nLocale is always 0) to indicate that the tokenizer should
|
||||
** use its default locale.
|
||||
**
|
||||
** For each token in the input string, the supplied callback xToken() must
|
||||
** be invoked. The first argument to it should be a copy of the pointer
|
||||
** passed as the second argument to xTokenize(). The third and fourth
|
||||
@@ -443,6 +483,30 @@ struct Fts5ExtensionApi {
|
||||
** may abandon the tokenization and return any error code other than
|
||||
** SQLITE_OK or SQLITE_DONE.
|
||||
**
|
||||
** If the tokenizer is registered using an fts5_tokenizer_v2 object,
|
||||
** then the xTokenize() method has two additional arguments - pLocale
|
||||
** and nLocale. These specify the locale that the tokenizer should use
|
||||
** for the current request. If pLocale and nLocale are both 0, then the
|
||||
** tokenizer should use its default locale. Otherwise, pLocale points to
|
||||
** an nLocale byte buffer containing the name of the locale to use as utf-8
|
||||
** text. pLocale is not nul-terminated.
|
||||
**
|
||||
** FTS5_TOKENIZER
|
||||
**
|
||||
** There is also an fts5_tokenizer object. This is an older, deprecated,
|
||||
** version of fts5_tokenizer_v2. It is similar except that:
|
||||
**
|
||||
** <ul>
|
||||
** <li> There is no "iVersion" field, and
|
||||
** <li> The xTokenize() method does not take a locale argument.
|
||||
** </ul>
|
||||
**
|
||||
** Legacy fts5_tokenizer tokenizers must be registered using the
|
||||
** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2().
|
||||
**
|
||||
** Tokenizer implementations registered using either API may be retrieved
|
||||
** using both xFindTokenizer() and xFindTokenizer_v2().
|
||||
**
|
||||
** SYNONYM SUPPORT
|
||||
**
|
||||
** Custom tokenizers may also support synonyms. Consider a case in which a
|
||||
@@ -551,6 +615,33 @@ struct Fts5ExtensionApi {
|
||||
** inefficient.
|
||||
*/
|
||||
typedef struct Fts5Tokenizer Fts5Tokenizer;
|
||||
typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2;
|
||||
struct fts5_tokenizer_v2 {
|
||||
int iVersion; /* Currently always 2 */
|
||||
|
||||
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
|
||||
void (*xDelete)(Fts5Tokenizer*);
|
||||
int (*xTokenize)(Fts5Tokenizer*,
|
||||
void *pCtx,
|
||||
int flags, /* Mask of FTS5_TOKENIZE_* flags */
|
||||
const char *pText, int nText,
|
||||
const char *pLocale, int nLocale,
|
||||
int (*xToken)(
|
||||
void *pCtx, /* Copy of 2nd argument to xTokenize() */
|
||||
int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
const char *pToken, /* Pointer to buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
int iStart, /* Byte offset of token within input text */
|
||||
int iEnd /* Byte offset of end of token within input text */
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
** New code should use the fts5_tokenizer_v2 type to define tokenizer
|
||||
** implementations. The following type is included for legacy applications
|
||||
** that still use it.
|
||||
*/
|
||||
typedef struct fts5_tokenizer fts5_tokenizer;
|
||||
struct fts5_tokenizer {
|
||||
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
|
||||
@@ -570,6 +661,7 @@ struct fts5_tokenizer {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/* Flags that may be passed as the third argument to xTokenize() */
|
||||
#define FTS5_TOKENIZE_QUERY 0x0001
|
||||
#define FTS5_TOKENIZE_PREFIX 0x0002
|
||||
@@ -589,7 +681,7 @@ struct fts5_tokenizer {
|
||||
*/
|
||||
typedef struct fts5_api fts5_api;
|
||||
struct fts5_api {
|
||||
int iVersion; /* Currently always set to 2 */
|
||||
int iVersion; /* Currently always set to 3 */
|
||||
|
||||
/* Create a new tokenizer */
|
||||
int (*xCreateTokenizer)(
|
||||
@@ -616,6 +708,25 @@ struct fts5_api {
|
||||
fts5_extension_function xFunction,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
|
||||
/* APIs below this point are only available if iVersion>=3 */
|
||||
|
||||
/* Create a new tokenizer */
|
||||
int (*xCreateTokenizer_v2)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pUserData,
|
||||
fts5_tokenizer_v2 *pTokenizer,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
|
||||
/* Find an existing tokenizer */
|
||||
int (*xFindTokenizer_v2)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void **ppUserData,
|
||||
fts5_tokenizer_v2 **ppTokenizer
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+39
-3
@@ -59,6 +59,22 @@ typedef sqlite3_uint64 u64;
|
||||
# define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
# define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
|
||||
|
||||
/* The uptr type is an unsigned integer large enough to hold a pointer
|
||||
*/
|
||||
#if defined(HAVE_STDINT_H)
|
||||
typedef uintptr_t uptr;
|
||||
#elif SQLITE_PTRSIZE==4
|
||||
typedef u32 uptr;
|
||||
#else
|
||||
typedef u64 uptr;
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
|
||||
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
|
||||
#else
|
||||
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Truncate very long tokens to this many bytes. Hard limit is
|
||||
@@ -146,10 +162,13 @@ typedef struct Fts5TokenizerConfig Fts5TokenizerConfig;
|
||||
|
||||
struct Fts5TokenizerConfig {
|
||||
Fts5Tokenizer *pTok;
|
||||
fts5_tokenizer *pTokApi;
|
||||
fts5_tokenizer_v2 *pApi2;
|
||||
fts5_tokenizer *pApi1;
|
||||
const char **azArg;
|
||||
int nArg;
|
||||
int ePattern; /* FTS_PATTERN_XXX constant */
|
||||
const char *pLocale; /* Current locale to use */
|
||||
int nLocale; /* Size of pLocale in bytes */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -190,6 +209,8 @@ struct Fts5TokenizerConfig {
|
||||
**
|
||||
** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex);
|
||||
**
|
||||
** bLocale:
|
||||
** Set to true if locale=1 was specified when the table was created.
|
||||
*/
|
||||
struct Fts5Config {
|
||||
sqlite3 *db; /* Database handle */
|
||||
@@ -207,10 +228,12 @@ struct Fts5Config {
|
||||
char *zContentRowid; /* "content_rowid=" option value */
|
||||
int bColumnsize; /* "columnsize=" option value (dflt==1) */
|
||||
int bTokendata; /* "tokendata=" option value (dflt==0) */
|
||||
int bLocale; /* "locale=" option value (dflt==0) */
|
||||
int eDetail; /* FTS5_DETAIL_XXX value */
|
||||
char *zContentExprlist;
|
||||
Fts5TokenizerConfig t;
|
||||
int bLock; /* True when table is preparing statement */
|
||||
|
||||
|
||||
/* Values loaded from the %_config table */
|
||||
int iVersion; /* fts5 file format 'version' */
|
||||
@@ -276,6 +299,8 @@ int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*);
|
||||
|
||||
int sqlite3Fts5ConfigParseRank(const char*, char**, char**);
|
||||
|
||||
void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_config.c.
|
||||
**************************************************************************/
|
||||
@@ -320,7 +345,7 @@ char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
|
||||
void sqlite3Fts5Put32(u8*, int);
|
||||
int sqlite3Fts5Get32(const u8*);
|
||||
|
||||
#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32)
|
||||
#define FTS5_POS2COLUMN(iPos) (int)((iPos >> 32) & 0x7FFFFFFF)
|
||||
#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF)
|
||||
|
||||
typedef struct Fts5PoslistReader Fts5PoslistReader;
|
||||
@@ -611,6 +636,14 @@ Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64);
|
||||
|
||||
int sqlite3Fts5FlushToDisk(Fts5Table*);
|
||||
|
||||
void sqlite3Fts5ClearLocale(Fts5Config *pConfig);
|
||||
void sqlite3Fts5SetLocale(Fts5Config *pConfig, const char *pLoc, int nLoc);
|
||||
|
||||
int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal);
|
||||
int sqlite3Fts5DecodeLocaleValue(sqlite3_value *pVal,
|
||||
const char **ppText, int *pnText, const char **ppLoc, int *pnLoc
|
||||
);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5.c.
|
||||
**************************************************************************/
|
||||
@@ -690,7 +723,7 @@ int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName);
|
||||
int sqlite3Fts5DropAll(Fts5Config*);
|
||||
int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **);
|
||||
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int);
|
||||
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
|
||||
int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
|
||||
|
||||
@@ -716,6 +749,9 @@ int sqlite3Fts5StorageOptimize(Fts5Storage *p);
|
||||
int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge);
|
||||
int sqlite3Fts5StorageReset(Fts5Storage *p);
|
||||
|
||||
void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage*);
|
||||
int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_storage.c.
|
||||
**************************************************************************/
|
||||
|
||||
+75
-7
@@ -226,6 +226,7 @@ static int fts5HighlightCb(
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of highlight() function.
|
||||
*/
|
||||
@@ -256,12 +257,19 @@ static void fts5HighlightFunction(
|
||||
sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
|
||||
rc = SQLITE_OK;
|
||||
}else if( ctx.zIn ){
|
||||
const char *pLoc = 0; /* Locale of column iCol */
|
||||
int nLoc = 0; /* Size of pLoc in bytes */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
|
||||
rc = pApi->xColumnLocale(pFts, iCol, &pLoc, &nLoc);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize_v2(
|
||||
pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx, fts5HighlightCb
|
||||
);
|
||||
}
|
||||
if( ctx.bOpen ){
|
||||
fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
|
||||
@@ -458,6 +466,8 @@ static void fts5SnippetFunction(
|
||||
memset(&sFinder, 0, sizeof(Fts5SFinder));
|
||||
for(i=0; i<nCol; i++){
|
||||
if( iCol<0 || iCol==i ){
|
||||
const char *pLoc = 0; /* Locale of column iCol */
|
||||
int nLoc = 0; /* Size of pLoc in bytes */
|
||||
int nDoc;
|
||||
int nDocsize;
|
||||
int ii;
|
||||
@@ -465,8 +475,10 @@ static void fts5SnippetFunction(
|
||||
sFinder.nFirst = 0;
|
||||
rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xTokenize(pFts,
|
||||
sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
|
||||
rc = pApi->xColumnLocale(pFts, i, &pLoc, &nLoc);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xTokenize_v2(pFts,
|
||||
sFinder.zDoc, nDoc, pLoc, nLoc, (void*)&sFinder, fts5SentenceFinderCb
|
||||
);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = pApi->xColumnSize(pFts, i, &nDocsize);
|
||||
@@ -524,6 +536,9 @@ static void fts5SnippetFunction(
|
||||
rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
|
||||
}
|
||||
if( ctx.zIn ){
|
||||
const char *pLoc = 0; /* Locale of column iBestCol */
|
||||
int nLoc = 0; /* Bytes in pLoc */
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
|
||||
}
|
||||
@@ -542,7 +557,12 @@ static void fts5SnippetFunction(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
|
||||
rc = pApi->xColumnLocale(pFts, iBestCol, &pLoc, &nLoc);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize_v2(
|
||||
pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx,fts5HighlightCb
|
||||
);
|
||||
}
|
||||
if( ctx.bOpen ){
|
||||
fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
|
||||
@@ -726,6 +746,53 @@ static void fts5Bm25Function(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of fts5_get_locale() function.
|
||||
*/
|
||||
static void fts5GetLocaleFunction(
|
||||
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
|
||||
Fts5Context *pFts, /* First arg to pass to pApi functions */
|
||||
sqlite3_context *pCtx, /* Context for returning result/error */
|
||||
int nVal, /* Number of values in apVal[] array */
|
||||
sqlite3_value **apVal /* Array of trailing arguments */
|
||||
){
|
||||
int iCol = 0;
|
||||
int eType = 0;
|
||||
int rc = SQLITE_OK;
|
||||
const char *zLocale = 0;
|
||||
int nLocale = 0;
|
||||
|
||||
/* xColumnLocale() must be available */
|
||||
assert( pApi->iVersion>=4 );
|
||||
|
||||
if( nVal!=1 ){
|
||||
const char *z = "wrong number of arguments to function fts5_get_locale()";
|
||||
sqlite3_result_error(pCtx, z, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
eType = sqlite3_value_numeric_type(apVal[0]);
|
||||
if( eType!=SQLITE_INTEGER ){
|
||||
const char *z = "non-integer argument passed to function fts5_get_locale()";
|
||||
sqlite3_result_error(pCtx, z, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
iCol = sqlite3_value_int(apVal[0]);
|
||||
if( iCol<0 || iCol>=pApi->xColumnCount(pFts) ){
|
||||
sqlite3_result_error_code(pCtx, SQLITE_RANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = pApi->xColumnLocale(pFts, iCol, &zLocale, &nLocale);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_result_error_code(pCtx, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
sqlite3_result_text(pCtx, zLocale, nLocale, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
int sqlite3Fts5AuxInit(fts5_api *pApi){
|
||||
struct Builtin {
|
||||
const char *zFunc; /* Function name (nul-terminated) */
|
||||
@@ -733,9 +800,10 @@ int sqlite3Fts5AuxInit(fts5_api *pApi){
|
||||
fts5_extension_function xFunc;/* Callback function */
|
||||
void (*xDestroy)(void*); /* Destructor function */
|
||||
} aBuiltin [] = {
|
||||
{ "snippet", 0, fts5SnippetFunction, 0 },
|
||||
{ "highlight", 0, fts5HighlightFunction, 0 },
|
||||
{ "bm25", 0, fts5Bm25Function, 0 },
|
||||
{ "snippet", 0, fts5SnippetFunction, 0 },
|
||||
{ "highlight", 0, fts5HighlightFunction, 0 },
|
||||
{ "bm25", 0, fts5Bm25Function, 0 },
|
||||
{ "fts5_get_locale", 0, fts5GetLocaleFunction, 0 },
|
||||
};
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int i; /* To iterate through builtin functions */
|
||||
|
||||
+60
-11
@@ -380,6 +380,16 @@ static int fts5ConfigParseSpecial(
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("locale", zCmd, nCmd)==0 ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
|
||||
*pzErr = sqlite3_mprintf("malformed locale=... directive");
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pConfig->bLocale = (zArg[0]=='1');
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){
|
||||
const Fts5Enum aDetail[] = {
|
||||
{ "none", FTS5_DETAIL_NONE },
|
||||
@@ -506,6 +516,15 @@ static int fts5ConfigMakeExprlist(Fts5Config *p){
|
||||
}
|
||||
}
|
||||
}
|
||||
if( p->eContent==FTS5_CONTENT_NORMAL && p->bLocale ){
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( p->abUnindexed[i]==0 ){
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.l%d", i);
|
||||
}else{
|
||||
sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", NULL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert( p->zContentExprlist==0 );
|
||||
p->zContentExprlist = (char*)buf.p;
|
||||
@@ -669,7 +688,11 @@ void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
|
||||
if( pConfig ){
|
||||
int i;
|
||||
if( pConfig->t.pTok ){
|
||||
pConfig->t.pTokApi->xDelete(pConfig->t.pTok);
|
||||
if( pConfig->t.pApi1 ){
|
||||
pConfig->t.pApi1->xDelete(pConfig->t.pTok);
|
||||
}else{
|
||||
pConfig->t.pApi2->xDelete(pConfig->t.pTok);
|
||||
}
|
||||
}
|
||||
sqlite3_free((char*)pConfig->t.azArg);
|
||||
sqlite3_free(pConfig->zDb);
|
||||
@@ -752,9 +775,15 @@ int sqlite3Fts5Tokenize(
|
||||
rc = sqlite3Fts5LoadTokenizer(pConfig);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pConfig->t.pTokApi->xTokenize(
|
||||
pConfig->t.pTok, pCtx, flags, pText, nText, xToken
|
||||
);
|
||||
if( pConfig->t.pApi1 ){
|
||||
rc = pConfig->t.pApi1->xTokenize(
|
||||
pConfig->t.pTok, pCtx, flags, pText, nText, xToken
|
||||
);
|
||||
}else{
|
||||
rc = pConfig->t.pApi2->xTokenize(pConfig->t.pTok, pCtx, flags,
|
||||
pText, nText, pConfig->t.pLocale, pConfig->t.nLocale, xToken
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@@ -1011,13 +1040,10 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
|
||||
&& iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE
|
||||
){
|
||||
rc = SQLITE_ERROR;
|
||||
if( pConfig->pzErrmsg ){
|
||||
assert( 0==*pConfig->pzErrmsg );
|
||||
*pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format "
|
||||
"(found %d, expected %d or %d) - run 'rebuild'",
|
||||
iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE
|
||||
);
|
||||
}
|
||||
sqlite3Fts5ConfigErrmsg(pConfig, "invalid fts5 file format "
|
||||
"(found %d, expected %d or %d) - run 'rebuild'",
|
||||
iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE
|
||||
);
|
||||
}else{
|
||||
pConfig->iVersion = iVersion;
|
||||
}
|
||||
@@ -1027,3 +1053,26 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set (*pConfig->pzErrmsg) to point to an sqlite3_malloc()ed buffer
|
||||
** containing the error message created using printf() style formatting
|
||||
** string zFmt and its trailing arguments.
|
||||
*/
|
||||
void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){
|
||||
va_list ap; /* ... printf arguments */
|
||||
char *zMsg = 0;
|
||||
|
||||
va_start(ap, zFmt);
|
||||
zMsg = sqlite3_vmprintf(zFmt, ap);
|
||||
if( pConfig->pzErrmsg ){
|
||||
assert( *pConfig->pzErrmsg==0 );
|
||||
*pConfig->pzErrmsg = zMsg;
|
||||
}else{
|
||||
sqlite3_free(zMsg);
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+20
-17
@@ -54,7 +54,7 @@ struct Fts5Expr {
|
||||
|
||||
/*
|
||||
** eType:
|
||||
** Expression node type. Always one of:
|
||||
** Expression node type. Usually one of:
|
||||
**
|
||||
** FTS5_AND (nChild, apChild valid)
|
||||
** FTS5_OR (nChild, apChild valid)
|
||||
@@ -62,6 +62,10 @@ struct Fts5Expr {
|
||||
** FTS5_STRING (pNear valid)
|
||||
** FTS5_TERM (pNear valid)
|
||||
**
|
||||
** An expression node with eType==0 may also exist. It always matches zero
|
||||
** rows. This is created when a phrase containing no tokens is parsed.
|
||||
** e.g. "".
|
||||
**
|
||||
** iHeight:
|
||||
** Distance from this node to furthest leaf. This is always 0 for nodes
|
||||
** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one
|
||||
@@ -282,11 +286,12 @@ int sqlite3Fts5ExprNew(
|
||||
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
|
||||
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
|
||||
|
||||
assert( sParse.pExpr || sParse.rc!=SQLITE_OK );
|
||||
assert_expr_depth_ok(sParse.rc, sParse.pExpr);
|
||||
|
||||
/* If the LHS of the MATCH expression was a user column, apply the
|
||||
** implicit column-filter. */
|
||||
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
|
||||
if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
|
||||
int n = sizeof(Fts5Colset);
|
||||
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
|
||||
if( pColset ){
|
||||
@@ -303,15 +308,7 @@ int sqlite3Fts5ExprNew(
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
sqlite3Fts5ParseNodeFree(sParse.pExpr);
|
||||
}else{
|
||||
if( !sParse.pExpr ){
|
||||
const int nByte = sizeof(Fts5ExprNode);
|
||||
pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte);
|
||||
if( pNew->pRoot ){
|
||||
pNew->pRoot->bEof = 1;
|
||||
}
|
||||
}else{
|
||||
pNew->pRoot = sParse.pExpr;
|
||||
}
|
||||
pNew->pRoot = sParse.pExpr;
|
||||
pNew->pIndex = 0;
|
||||
pNew->pConfig = pConfig;
|
||||
pNew->apExprPhrase = sParse.apPhrase;
|
||||
@@ -1129,7 +1126,7 @@ static int fts5ExprNodeTest_STRING(
|
||||
}
|
||||
}else{
|
||||
Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
|
||||
if( pIter->iRowid==iLast || pIter->bEof ) continue;
|
||||
if( pIter->iRowid==iLast ) continue;
|
||||
bMatch = 0;
|
||||
if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
|
||||
return rc;
|
||||
@@ -1651,9 +1648,6 @@ Fts5ExprNearset *sqlite3Fts5ParseNearset(
|
||||
Fts5ExprNearset *pRet = 0;
|
||||
|
||||
if( pParse->rc==SQLITE_OK ){
|
||||
if( pPhrase==0 ){
|
||||
return pNear;
|
||||
}
|
||||
if( pNear==0 ){
|
||||
sqlite3_int64 nByte;
|
||||
nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
|
||||
@@ -2263,6 +2257,9 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add pSub as a child of p.
|
||||
*/
|
||||
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
|
||||
int ii = p->nChild;
|
||||
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
|
||||
@@ -2407,19 +2404,23 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
|
||||
"fts5: %s queries are not supported (detail!=full)",
|
||||
pNear->nPhrase==1 ? "phrase": "NEAR"
|
||||
);
|
||||
sqlite3_free(pRet);
|
||||
sqlite3Fts5ParseNodeFree(pRet);
|
||||
pRet = 0;
|
||||
pNear = 0;
|
||||
assert( pLeft==0 && pRight==0 );
|
||||
}
|
||||
}
|
||||
}else{
|
||||
assert( pNear==0 );
|
||||
fts5ExprAddChildren(pRet, pLeft);
|
||||
fts5ExprAddChildren(pRet, pRight);
|
||||
pLeft = pRight = 0;
|
||||
if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){
|
||||
sqlite3Fts5ParseError(pParse,
|
||||
"fts5 expression tree is too large (maximum depth %d)",
|
||||
SQLITE_FTS5_MAX_EXPR_DEPTH
|
||||
);
|
||||
sqlite3_free(pRet);
|
||||
sqlite3Fts5ParseNodeFree(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
@@ -2457,6 +2458,7 @@ Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
|
||||
assert( pRight->eType==FTS5_STRING
|
||||
|| pRight->eType==FTS5_TERM
|
||||
|| pRight->eType==FTS5_EOF
|
||||
|| (pRight->eType==FTS5_AND && pParse->bPhraseToAnd)
|
||||
);
|
||||
|
||||
if( pLeft->eType==FTS5_AND ){
|
||||
@@ -3104,6 +3106,7 @@ static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){
|
||||
pNode->iRowid = iRowid;
|
||||
pNode->bEof = 0;
|
||||
switch( pNode->eType ){
|
||||
case 0:
|
||||
case FTS5_TERM:
|
||||
case FTS5_STRING:
|
||||
return (pNode->pNear->apPhrase[0]->poslist.n>0);
|
||||
|
||||
@@ -831,11 +831,12 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *aOut = 0; /* Read blob data into this buffer */
|
||||
int nByte = sqlite3_blob_bytes(p->pReader);
|
||||
sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
|
||||
int szData = (sizeof(Fts5Data) + 7) & ~7;
|
||||
sqlite3_int64 nAlloc = szData + nByte + FTS5_DATA_PADDING;
|
||||
pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
|
||||
if( pRet ){
|
||||
pRet->nn = nByte;
|
||||
aOut = pRet->p = (u8*)&pRet[1];
|
||||
aOut = pRet->p = (u8*)pRet + szData;
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
@@ -858,6 +859,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
}
|
||||
|
||||
assert( (pRet==0)==(p->rc!=SQLITE_OK) );
|
||||
assert( pRet==0 || EIGHT_BYTE_ALIGNMENT( pRet->p ) );
|
||||
return pRet;
|
||||
}
|
||||
|
||||
@@ -2183,7 +2185,7 @@ static void fts5SegIterNext_None(
|
||||
|
||||
if( iOff<pIter->iEndofDoclist ){
|
||||
/* Next entry is on the current page */
|
||||
i64 iDelta;
|
||||
u64 iDelta;
|
||||
iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta);
|
||||
pIter->iLeafOffset = iOff;
|
||||
pIter->iRowid += iDelta;
|
||||
|
||||
+779
-148
File diff suppressed because it is too large
Load Diff
+357
-92
@@ -16,13 +16,40 @@
|
||||
|
||||
#include "fts5Int.h"
|
||||
|
||||
/*
|
||||
** pSavedRow:
|
||||
** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it
|
||||
** does a by-rowid lookup to retrieve a single row from the %_content
|
||||
** table or equivalent external-content table/view.
|
||||
**
|
||||
** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original
|
||||
** values for a row being UPDATEd. In that case, the SQL statement is
|
||||
** not reset and pSavedRow is set to point at it. This is so that the
|
||||
** insert operation that follows the delete may access the original
|
||||
** row values for any new values for which sqlite3_value_nochange() returns
|
||||
** true. i.e. if the user executes:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1);
|
||||
** ...
|
||||
** UPDATE fts SET a=?, b=? WHERE rowid=?;
|
||||
**
|
||||
** then the value passed to the xUpdate() method of this table as the
|
||||
** new.c value is an sqlite3_value_nochange() value. So in this case it
|
||||
** must be read from the saved row stored in Fts5Storage.pSavedRow.
|
||||
**
|
||||
** This is necessary - using sqlite3_value_nochange() instead of just having
|
||||
** SQLite pass the original value back via xUpdate() - so as not to discard
|
||||
** any locale information associated with such values.
|
||||
**
|
||||
*/
|
||||
struct Fts5Storage {
|
||||
Fts5Config *pConfig;
|
||||
Fts5Index *pIndex;
|
||||
int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */
|
||||
i64 nTotalRow; /* Total number of rows in FTS table */
|
||||
i64 *aTotalSize; /* Total sizes of each column */
|
||||
sqlite3_stmt *aStmt[11];
|
||||
sqlite3_stmt *pSavedRow;
|
||||
sqlite3_stmt *aStmt[12];
|
||||
};
|
||||
|
||||
|
||||
@@ -36,14 +63,39 @@ struct Fts5Storage {
|
||||
# error "FTS5_STMT_LOOKUP mismatch"
|
||||
#endif
|
||||
|
||||
#define FTS5_STMT_INSERT_CONTENT 3
|
||||
#define FTS5_STMT_REPLACE_CONTENT 4
|
||||
#define FTS5_STMT_DELETE_CONTENT 5
|
||||
#define FTS5_STMT_REPLACE_DOCSIZE 6
|
||||
#define FTS5_STMT_DELETE_DOCSIZE 7
|
||||
#define FTS5_STMT_LOOKUP_DOCSIZE 8
|
||||
#define FTS5_STMT_REPLACE_CONFIG 9
|
||||
#define FTS5_STMT_SCAN 10
|
||||
#define FTS5_STMT_LOOKUP2 3
|
||||
#define FTS5_STMT_INSERT_CONTENT 4
|
||||
#define FTS5_STMT_REPLACE_CONTENT 5
|
||||
#define FTS5_STMT_DELETE_CONTENT 6
|
||||
#define FTS5_STMT_REPLACE_DOCSIZE 7
|
||||
#define FTS5_STMT_DELETE_DOCSIZE 8
|
||||
#define FTS5_STMT_LOOKUP_DOCSIZE 9
|
||||
#define FTS5_STMT_REPLACE_CONFIG 10
|
||||
#define FTS5_STMT_SCAN 11
|
||||
|
||||
/*
|
||||
** Return a pointer to a buffer obtained from sqlite3_malloc() that contains
|
||||
** nBind comma-separated question marks. e.g. if nBind is passed 5, this
|
||||
** function returns "?,?,?,?,?".
|
||||
**
|
||||
** If *pRc is not SQLITE_OK when this function is called, it is a no-op and
|
||||
** NULL is returned immediately. Or, if the attempt to malloc a buffer
|
||||
** fails, then *pRc is set to SQLITE_NOMEM and NULL is returned. Otherwise,
|
||||
** if it is SQLITE_OK when this function is called and the malloc() succeeds,
|
||||
** *pRc is left unchanged.
|
||||
*/
|
||||
static char *fts5BindingsList(int *pRc, int nBind){
|
||||
char *zBind = sqlite3Fts5MallocZero(pRc, 1 + nBind*2);
|
||||
if( zBind ){
|
||||
int ii;
|
||||
for(ii=0; ii<nBind; ii++){
|
||||
zBind[ii*2] = '?';
|
||||
zBind[ii*2 + 1] = ',';
|
||||
}
|
||||
zBind[ii*2-1] = '\0';
|
||||
}
|
||||
return zBind;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare the two insert statements - Fts5Storage.pInsertContent and
|
||||
@@ -73,6 +125,7 @@ static int fts5StorageGetStmt(
|
||||
"SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
|
||||
"SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
|
||||
"SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */
|
||||
"SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP2 */
|
||||
|
||||
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
|
||||
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
|
||||
@@ -88,6 +141,8 @@ static int fts5StorageGetStmt(
|
||||
Fts5Config *pC = p->pConfig;
|
||||
char *zSql = 0;
|
||||
|
||||
assert( ArraySize(azStmt)==ArraySize(p->aStmt) );
|
||||
|
||||
switch( eStmt ){
|
||||
case FTS5_STMT_SCAN:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt],
|
||||
@@ -104,24 +159,26 @@ static int fts5StorageGetStmt(
|
||||
break;
|
||||
|
||||
case FTS5_STMT_LOOKUP:
|
||||
case FTS5_STMT_LOOKUP2:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt],
|
||||
pC->zContentExprlist, pC->zContent, pC->zContentRowid
|
||||
);
|
||||
break;
|
||||
|
||||
case FTS5_STMT_INSERT_CONTENT:
|
||||
case FTS5_STMT_REPLACE_CONTENT: {
|
||||
int nCol = pC->nCol + 1;
|
||||
case FTS5_STMT_INSERT_CONTENT: {
|
||||
int nCol = 0;
|
||||
char *zBind;
|
||||
int i;
|
||||
|
||||
zBind = sqlite3_malloc64(1 + nCol*2);
|
||||
if( zBind ){
|
||||
for(i=0; i<nCol; i++){
|
||||
zBind[i*2] = '?';
|
||||
zBind[i*2 + 1] = ',';
|
||||
nCol = 1 + pC->nCol;
|
||||
if( pC->bLocale ){
|
||||
for(i=0; i<pC->nCol; i++){
|
||||
if( pC->abUnindexed[i]==0 ) nCol++;
|
||||
}
|
||||
zBind[i*2-1] = '\0';
|
||||
}
|
||||
|
||||
zBind = fts5BindingsList(&rc, nCol);
|
||||
if( zBind ){
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
|
||||
sqlite3_free(zBind);
|
||||
}
|
||||
@@ -150,7 +207,7 @@ static int fts5StorageGetStmt(
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int f = SQLITE_PREPARE_PERSISTENT;
|
||||
if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB;
|
||||
if( eStmt>FTS5_STMT_LOOKUP2 ) f |= SQLITE_PREPARE_NO_VTAB;
|
||||
p->pConfig->bLock++;
|
||||
rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0);
|
||||
p->pConfig->bLock--;
|
||||
@@ -312,7 +369,7 @@ int sqlite3Fts5StorageOpen(
|
||||
if( bCreate ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
int nDefn = 32 + pConfig->nCol*10;
|
||||
char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10);
|
||||
char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20);
|
||||
if( zDefn==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -324,6 +381,14 @@ int sqlite3Fts5StorageOpen(
|
||||
sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
|
||||
iOff += (int)strlen(&zDefn[iOff]);
|
||||
}
|
||||
if( pConfig->bLocale ){
|
||||
for(i=0; i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i]==0 ){
|
||||
sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i);
|
||||
iOff += (int)strlen(&zDefn[iOff]);
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
|
||||
}
|
||||
sqlite3_free(zDefn);
|
||||
@@ -399,15 +464,49 @@ static int fts5StorageInsertCallback(
|
||||
return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used as part of an UPDATE statement that modifies the
|
||||
** rowid of a row. In that case, this function is called first to set
|
||||
** Fts5Storage.pSavedRow to point to a statement that may be used to
|
||||
** access the original values of the row being deleted - iDel.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
||||
** It is not considered an error if row iDel does not exist. In this case
|
||||
** pSavedRow is not set and SQLITE_OK returned.
|
||||
*/
|
||||
int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pSeek = 0;
|
||||
|
||||
assert( p->pSavedRow==0 );
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pSeek, 1, iDel);
|
||||
if( sqlite3_step(pSeek)!=SQLITE_ROW ){
|
||||
rc = sqlite3_reset(pSeek);
|
||||
}else{
|
||||
p->pSavedRow = pSeek;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If a row with rowid iDel is present in the %_content table, add the
|
||||
** delete-markers to the FTS index necessary to delete it. Do not actually
|
||||
** remove the %_content row at this time though.
|
||||
**
|
||||
** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left
|
||||
** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access
|
||||
** the original values of the row being deleted. This is used by UPDATE
|
||||
** statements.
|
||||
*/
|
||||
static int fts5StorageDeleteFromIndex(
|
||||
Fts5Storage *p,
|
||||
i64 iDel,
|
||||
sqlite3_value **apVal
|
||||
sqlite3_value **apVal,
|
||||
int bSaveRow /* True to set pSavedRow */
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
|
||||
@@ -416,12 +515,21 @@ static int fts5StorageDeleteFromIndex(
|
||||
int iCol;
|
||||
Fts5InsertCtx ctx;
|
||||
|
||||
assert( bSaveRow==0 || apVal==0 );
|
||||
assert( bSaveRow==0 || bSaveRow==1 );
|
||||
assert( FTS5_STMT_LOOKUP2==FTS5_STMT_LOOKUP+1 );
|
||||
|
||||
if( apVal==0 ){
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_bind_int64(pSeek, 1, iDel);
|
||||
if( sqlite3_step(pSeek)!=SQLITE_ROW ){
|
||||
return sqlite3_reset(pSeek);
|
||||
if( p->pSavedRow && bSaveRow ){
|
||||
pSeek = p->pSavedRow;
|
||||
p->pSavedRow = 0;
|
||||
}else{
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+bSaveRow, &pSeek, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_bind_int64(pSeek, 1, iDel);
|
||||
if( sqlite3_step(pSeek)!=SQLITE_ROW ){
|
||||
return sqlite3_reset(pSeek);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,26 +537,42 @@ static int fts5StorageDeleteFromIndex(
|
||||
ctx.iCol = -1;
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1]==0 ){
|
||||
const char *zText;
|
||||
int nText;
|
||||
sqlite3_value *pVal = 0;
|
||||
const char *pText = 0;
|
||||
int nText = 0;
|
||||
const char *pLoc = 0;
|
||||
int nLoc = 0;
|
||||
|
||||
assert( pSeek==0 || apVal==0 );
|
||||
assert( pSeek!=0 || apVal!=0 );
|
||||
if( pSeek ){
|
||||
zText = (const char*)sqlite3_column_text(pSeek, iCol);
|
||||
nText = sqlite3_column_bytes(pSeek, iCol);
|
||||
}else if( ALWAYS(apVal) ){
|
||||
zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
|
||||
nText = sqlite3_value_bytes(apVal[iCol-1]);
|
||||
pVal = sqlite3_column_value(pSeek, iCol);
|
||||
}else{
|
||||
continue;
|
||||
pVal = apVal[iCol-1];
|
||||
}
|
||||
ctx.szCol = 0;
|
||||
rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText, (void*)&ctx, fts5StorageInsertCallback
|
||||
);
|
||||
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
|
||||
if( p->aTotalSize[iCol-1]<0 ){
|
||||
rc = FTS5_CORRUPT;
|
||||
|
||||
if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
|
||||
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
|
||||
}else{
|
||||
pText = (const char*)sqlite3_value_text(pVal);
|
||||
nText = sqlite3_value_bytes(pVal);
|
||||
if( pConfig->bLocale && pSeek ){
|
||||
pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol);
|
||||
nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
|
||||
ctx.szCol = 0;
|
||||
rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
|
||||
pText, nText, (void*)&ctx, fts5StorageInsertCallback
|
||||
);
|
||||
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
|
||||
if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
sqlite3Fts5ClearLocale(pConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,11 +582,29 @@ static int fts5StorageDeleteFromIndex(
|
||||
p->nTotalRow--;
|
||||
}
|
||||
|
||||
rc2 = sqlite3_reset(pSeek);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
if( rc==SQLITE_OK && bSaveRow ){
|
||||
assert( p->pSavedRow==0 );
|
||||
p->pSavedRow = pSeek;
|
||||
}else{
|
||||
rc2 = sqlite3_reset(pSeek);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset any saved statement pSavedRow. Zero pSavedRow as well. This
|
||||
** should be called by the xUpdate() method of the fts5 table before
|
||||
** returning from any operation that may have set Fts5Storage.pSavedRow.
|
||||
*/
|
||||
void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){
|
||||
assert( pStorage->pSavedRow==0
|
||||
|| pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2]
|
||||
);
|
||||
sqlite3_reset(pStorage->pSavedRow);
|
||||
pStorage->pSavedRow = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to process a DELETE on a contentless_delete=1
|
||||
** table. It adds the tombstone required to delete the entry with rowid
|
||||
@@ -519,12 +661,12 @@ static int fts5StorageInsertDocsize(
|
||||
rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin);
|
||||
sqlite3_bind_int64(pReplace, 3, iOrigin);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
||||
sqlite3_step(pReplace);
|
||||
rc = sqlite3_reset(pReplace);
|
||||
sqlite3_bind_null(pReplace, 2);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
||||
sqlite3_step(pReplace);
|
||||
rc = sqlite3_reset(pReplace);
|
||||
sqlite3_bind_null(pReplace, 2);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@@ -578,7 +720,12 @@ static int fts5StorageSaveTotals(Fts5Storage *p){
|
||||
/*
|
||||
** Remove a row from the FTS table.
|
||||
*/
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
|
||||
int sqlite3Fts5StorageDelete(
|
||||
Fts5Storage *p, /* Storage object */
|
||||
i64 iDel, /* Rowid to delete from table */
|
||||
sqlite3_value **apVal, /* Optional - values to remove from index */
|
||||
int bSaveRow /* If true, set pSavedRow for deleted row */
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc;
|
||||
sqlite3_stmt *pDel = 0;
|
||||
@@ -595,7 +742,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
|
||||
if( p->pConfig->bContentlessDelete ){
|
||||
rc = fts5StorageContentlessDelete(p, iDel);
|
||||
}else{
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,14 +831,36 @@ int sqlite3Fts5StorageRebuild(Fts5Storage *p){
|
||||
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->abUnindexed[ctx.iCol]==0 ){
|
||||
const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1);
|
||||
int nText = sqlite3_column_bytes(pScan, ctx.iCol+1);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
int nText = 0; /* Size of pText in bytes */
|
||||
const char *pText = 0; /* Pointer to buffer containing text value */
|
||||
int nLoc = 0; /* Size of pLoc in bytes */
|
||||
const char *pLoc = 0; /* Pointer to buffer containing text value */
|
||||
|
||||
sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1);
|
||||
if( pConfig->eContent==FTS5_CONTENT_EXTERNAL
|
||||
&& sqlite3Fts5IsLocaleValue(pConfig, pVal)
|
||||
){
|
||||
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
|
||||
}else{
|
||||
pText = (const char*)sqlite3_value_text(pVal);
|
||||
nText = sqlite3_value_bytes(pVal);
|
||||
if( pConfig->bLocale ){
|
||||
int iCol = ctx.iCol + 1 + pConfig->nCol;
|
||||
pLoc = (const char*)sqlite3_column_text(pScan, iCol);
|
||||
nLoc = sqlite3_column_bytes(pScan, iCol);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
pText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
sqlite3Fts5ClearLocale(pConfig);
|
||||
}
|
||||
}
|
||||
sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
|
||||
p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
|
||||
@@ -773,9 +942,49 @@ int sqlite3Fts5StorageContentInsert(
|
||||
}else{
|
||||
sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */
|
||||
int i; /* Counter variable */
|
||||
int nIndexed = 0; /* Number indexed columns seen */
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
|
||||
for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
|
||||
rc = sqlite3_bind_value(pInsert, i, apVal[i]);
|
||||
if( pInsert ) sqlite3_clear_bindings(pInsert);
|
||||
|
||||
/* Bind the rowid value */
|
||||
sqlite3_bind_value(pInsert, 1, apVal[1]);
|
||||
|
||||
/* Loop through values for user-defined columns. i=2 is the leftmost
|
||||
** user-defined column. As is column 1 of pSavedRow. */
|
||||
for(i=2; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
|
||||
int bUnindexed = pConfig->abUnindexed[i-2];
|
||||
sqlite3_value *pVal = apVal[i];
|
||||
|
||||
nIndexed += !bUnindexed;
|
||||
if( sqlite3_value_nochange(pVal) && p->pSavedRow ){
|
||||
/* This is an UPDATE statement, and column (i-2) was not modified.
|
||||
** Retrieve the value from Fts5Storage.pSavedRow instead. */
|
||||
pVal = sqlite3_column_value(p->pSavedRow, i-1);
|
||||
if( pConfig->bLocale && bUnindexed==0 ){
|
||||
sqlite3_bind_value(pInsert, pConfig->nCol + 1 + nIndexed,
|
||||
sqlite3_column_value(p->pSavedRow, pConfig->nCol + i - 1)
|
||||
);
|
||||
}
|
||||
}else if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
|
||||
const char *pText = 0;
|
||||
const char *pLoc = 0;
|
||||
int nText = 0;
|
||||
int nLoc = 0;
|
||||
assert( pConfig->bLocale );
|
||||
|
||||
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT);
|
||||
if( bUnindexed==0 ){
|
||||
int iLoc = pConfig->nCol + 1 + nIndexed;
|
||||
sqlite3_bind_text(pInsert, iLoc, pLoc, nLoc, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = sqlite3_bind_value(pInsert, i, pVal);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pInsert);
|
||||
@@ -810,14 +1019,38 @@ int sqlite3Fts5StorageIndexInsert(
|
||||
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->abUnindexed[ctx.iCol]==0 ){
|
||||
const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]);
|
||||
int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
int nText = 0; /* Size of pText in bytes */
|
||||
const char *pText = 0; /* Pointer to buffer containing text value */
|
||||
int nLoc = 0; /* Size of pText in bytes */
|
||||
const char *pLoc = 0; /* Pointer to buffer containing text value */
|
||||
|
||||
sqlite3_value *pVal = apVal[ctx.iCol+2];
|
||||
if( p->pSavedRow && sqlite3_value_nochange(pVal) ){
|
||||
pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1);
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){
|
||||
int iCol = ctx.iCol + 1 + pConfig->nCol;
|
||||
pLoc = (const char*)sqlite3_column_text(p->pSavedRow, iCol);
|
||||
nLoc = sqlite3_column_bytes(p->pSavedRow, iCol);
|
||||
}
|
||||
}else{
|
||||
pVal = apVal[ctx.iCol+2];
|
||||
}
|
||||
|
||||
if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
|
||||
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
|
||||
}else{
|
||||
pText = (const char*)sqlite3_value_text(pVal);
|
||||
nText = sqlite3_value_bytes(pVal);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
sqlite3Fts5ClearLocale(pConfig);
|
||||
}
|
||||
}
|
||||
sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
|
||||
p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
|
||||
@@ -981,29 +1214,61 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i] ) continue;
|
||||
ctx.iCol = i;
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zText = (const char*)sqlite3_column_text(pScan, i+1);
|
||||
int nText = sqlite3_column_bytes(pScan, i+1);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
aTotalSize[i] += ctx.szCol;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
if( pConfig->abUnindexed[i]==0 ){
|
||||
const char *pText = 0;
|
||||
int nText = 0;
|
||||
const char *pLoc = 0;
|
||||
int nLoc = 0;
|
||||
sqlite3_value *pVal = sqlite3_column_value(pScan, i+1);
|
||||
|
||||
if( pConfig->eContent==FTS5_CONTENT_EXTERNAL
|
||||
&& sqlite3Fts5IsLocaleValue(pConfig, pVal)
|
||||
){
|
||||
rc = sqlite3Fts5DecodeLocaleValue(
|
||||
pVal, &pText, &nText, &pLoc, &nLoc
|
||||
);
|
||||
}else{
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL && pConfig->bLocale ){
|
||||
int iCol = i + 1 + pConfig->nCol;
|
||||
pLoc = (const char*)sqlite3_column_text(pScan, iCol);
|
||||
nLoc = sqlite3_column_bytes(pScan, iCol);
|
||||
}
|
||||
pText = (const char*)sqlite3_value_text(pVal);
|
||||
nText = sqlite3_value_bytes(pVal);
|
||||
}
|
||||
|
||||
ctx.iCol = i;
|
||||
ctx.szCol = 0;
|
||||
|
||||
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
pText, nText,
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
sqlite3Fts5ClearLocale(pConfig);
|
||||
}
|
||||
|
||||
/* If this is not a columnsize=0 database, check that the number
|
||||
** of tokens in the value matches the aColSize[] value read from
|
||||
** the %_docsize table. */
|
||||
if( rc==SQLITE_OK
|
||||
&& pConfig->bColumnsize
|
||||
&& ctx.szCol!=aColSize[i]
|
||||
){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
aTotalSize[i] += ctx.szCol;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
|
||||
+360
-54
@@ -14,14 +14,7 @@
|
||||
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
|
||||
@@ -103,14 +96,14 @@ static int SQLITE_TCLAPI f5tDbAndApi(
|
||||
|
||||
rc = sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3_bind_pointer(pStmt, 1, (void*)&pApi, "fts5_api_ptr", 0);
|
||||
sqlite3_step(pStmt);
|
||||
|
||||
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -247,6 +240,7 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
|
||||
{ "xQueryToken", 2, "IPHRASE ITERM" }, /* 18 */
|
||||
{ "xInstToken", 2, "IDX ITERM" }, /* 19 */
|
||||
{ "xColumnLocale", 1, "COL" }, /* 20 */
|
||||
{ 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -297,12 +291,12 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
break;
|
||||
}
|
||||
CASE(3, "xTokenize") {
|
||||
int nText;
|
||||
Tcl_Size nText;
|
||||
char *zText = Tcl_GetStringFromObj(objv[2], &nText);
|
||||
F5tFunction ctx;
|
||||
ctx.interp = interp;
|
||||
ctx.pScript = objv[3];
|
||||
rc = p->pApi->xTokenize(p->pFts, zText, nText, &ctx, xTokenizeCb);
|
||||
rc = p->pApi->xTokenize(p->pFts, zText, (int)nText, &ctx, xTokenizeCb);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_ResetResult(interp);
|
||||
}
|
||||
@@ -398,7 +392,7 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
CASE(12, "xSetAuxdata") {
|
||||
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
|
||||
if( pData==0 ){
|
||||
Tcl_AppendResult(interp, "out of memory", 0);
|
||||
Tcl_AppendResult(interp, "out of memory", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pData->pObj = objv[2];
|
||||
@@ -458,7 +452,7 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
|
||||
rc = p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for( ;iCol>=0; p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff) ){
|
||||
@@ -535,6 +529,20 @@ static int SQLITE_TCLAPI xF5tApi(
|
||||
break;
|
||||
}
|
||||
|
||||
CASE(20, "xColumnLocale") {
|
||||
const char *z = 0;
|
||||
int n = 0;
|
||||
int iCol;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = p->pApi->xColumnLocale(p->pFts, iCol, &z, &n);
|
||||
if( rc==SQLITE_OK && z ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
@@ -605,15 +613,16 @@ static void xF5tFunction(
|
||||
sqlite3_result_error(pCtx, Tcl_GetStringResult(p->interp), -1);
|
||||
}else{
|
||||
Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
|
||||
int n;
|
||||
const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
|
||||
char c = zType[0];
|
||||
if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
|
||||
/* Only return a BLOB type if the Tcl variable is a bytearray and
|
||||
** has no string representation. */
|
||||
unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &n);
|
||||
sqlite3_result_blob(pCtx, data, n, SQLITE_TRANSIENT);
|
||||
Tcl_Size nn;
|
||||
unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &nn);
|
||||
sqlite3_result_blob(pCtx, data, (int)nn, SQLITE_TRANSIENT);
|
||||
}else if( c=='b' && strcmp(zType,"boolean")==0 ){
|
||||
int n;
|
||||
Tcl_GetIntFromObj(0, pVar, &n);
|
||||
sqlite3_result_int(pCtx, n);
|
||||
}else if( c=='d' && strcmp(zType,"double")==0 ){
|
||||
@@ -626,8 +635,9 @@ static void xF5tFunction(
|
||||
Tcl_GetWideIntFromObj(0, pVar, &v);
|
||||
sqlite3_result_int64(pCtx, v);
|
||||
}else{
|
||||
unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
|
||||
sqlite3_result_text(pCtx, (char *)data, n, SQLITE_TRANSIENT);
|
||||
Tcl_Size nn;
|
||||
unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &nn);
|
||||
sqlite3_result_text(pCtx, (char*)data, (int)nn, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -673,7 +683,7 @@ static int SQLITE_TCLAPI f5tCreateFunction(
|
||||
pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -720,7 +730,7 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
char *zText;
|
||||
int nText;
|
||||
Tcl_Size nText;
|
||||
sqlite3 *db = 0;
|
||||
fts5_api *pApi = 0;
|
||||
Fts5Tokenizer *pTok = 0;
|
||||
@@ -729,7 +739,7 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
void *pUserdata;
|
||||
int rc;
|
||||
|
||||
int nArg;
|
||||
Tcl_Size nArg;
|
||||
const char **azArg;
|
||||
F5tTokenizeCtx ctx;
|
||||
|
||||
@@ -740,7 +750,7 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
if( objc==5 ){
|
||||
char *zOpt = Tcl_GetString(objv[1]);
|
||||
if( strcmp("-subst", zOpt) ){
|
||||
Tcl_AppendResult(interp, "unrecognized option: ", zOpt, 0);
|
||||
Tcl_AppendResult(interp, "unrecognized option: ", zOpt, (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -749,7 +759,7 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( nArg==0 ){
|
||||
Tcl_AppendResult(interp, "no such tokenizer: ", 0);
|
||||
Tcl_AppendResult(interp, "no such tokenizer: ", (char*)0);
|
||||
Tcl_Free((void*)azArg);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -757,13 +767,13 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
|
||||
rc = pApi->xFindTokenizer(pApi, azArg[0], &pUserdata, &tokenizer);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], 0);
|
||||
Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = tokenizer.xCreate(pUserdata, &azArg[1], nArg-1, &pTok);
|
||||
rc = tokenizer.xCreate(pUserdata, &azArg[1], (int)(nArg-1), &pTok);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error in tokenizer.xCreate()", 0);
|
||||
Tcl_AppendResult(interp, "error in tokenizer.xCreate()", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -773,11 +783,11 @@ static int SQLITE_TCLAPI f5tTokenize(
|
||||
ctx.pRet = pRet;
|
||||
ctx.zInput = zText;
|
||||
rc = tokenizer.xTokenize(
|
||||
pTok, (void*)&ctx, FTS5_TOKENIZE_DOCUMENT, zText, nText, xTokenizeCb2
|
||||
pTok, (void*)&ctx, FTS5_TOKENIZE_DOCUMENT, zText,(int)nText, xTokenizeCb2
|
||||
);
|
||||
tokenizer.xDelete(pTok);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", 0);
|
||||
Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", (char*)0);
|
||||
Tcl_DecrRefCount(pRet);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -801,18 +811,32 @@ typedef struct F5tTokenizerInstance F5tTokenizerInstance;
|
||||
struct F5tTokenizerContext {
|
||||
void *pCtx;
|
||||
int (*xToken)(void*, int, const char*, int, int, int);
|
||||
F5tTokenizerInstance *pInst;
|
||||
};
|
||||
|
||||
struct F5tTokenizerModule {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pScript;
|
||||
void *pParentCtx;
|
||||
fts5_tokenizer_v2 parent_v2;
|
||||
fts5_tokenizer parent;
|
||||
F5tTokenizerContext *pContext;
|
||||
};
|
||||
|
||||
/*
|
||||
** zLocale:
|
||||
** Within a call to xTokenize_v2(), pLocale/nLocale store the locale
|
||||
** passed to the call by fts5. This can be retrieved by a Tcl tokenize
|
||||
** script using [sqlite3_fts5_locale].
|
||||
*/
|
||||
struct F5tTokenizerInstance {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pScript;
|
||||
F5tTokenizerModule *pModule;
|
||||
Fts5Tokenizer *pParent;
|
||||
F5tTokenizerContext *pContext;
|
||||
const char *pLocale;
|
||||
int nLocale;
|
||||
};
|
||||
|
||||
static int f5tTokenizerCreate(
|
||||
@@ -821,11 +845,20 @@ static int f5tTokenizerCreate(
|
||||
int nArg,
|
||||
Fts5Tokenizer **ppOut
|
||||
){
|
||||
Fts5Tokenizer *pParent = 0;
|
||||
F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx;
|
||||
Tcl_Obj *pEval;
|
||||
int rc = TCL_OK;
|
||||
int i;
|
||||
|
||||
assert( pMod->parent_v2.xCreate==0 || pMod->parent.xCreate==0 );
|
||||
if( pMod->parent_v2.xCreate ){
|
||||
rc = pMod->parent_v2.xCreate(pMod->pParentCtx, 0, 0, &pParent);
|
||||
}
|
||||
if( pMod->parent.xCreate ){
|
||||
rc = pMod->parent.xCreate(pMod->pParentCtx, 0, 0, &pParent);
|
||||
}
|
||||
|
||||
pEval = Tcl_DuplicateObj(pMod->pScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
for(i=0; rc==TCL_OK && i<nArg; i++){
|
||||
@@ -845,6 +878,8 @@ static int f5tTokenizerCreate(
|
||||
pInst->interp = pMod->interp;
|
||||
pInst->pScript = Tcl_GetObjResult(pMod->interp);
|
||||
pInst->pContext = pMod->pContext;
|
||||
pInst->pParent = pParent;
|
||||
pInst->pModule = pMod;
|
||||
Tcl_IncrRefCount(pInst->pScript);
|
||||
*ppOut = (Fts5Tokenizer*)pInst;
|
||||
}
|
||||
@@ -855,11 +890,21 @@ static int f5tTokenizerCreate(
|
||||
|
||||
static void f5tTokenizerDelete(Fts5Tokenizer *p){
|
||||
F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
|
||||
Tcl_DecrRefCount(pInst->pScript);
|
||||
ckfree((char *)pInst);
|
||||
if( pInst ){
|
||||
if( pInst->pParent ){
|
||||
if( pInst->pModule->parent_v2.xDelete ){
|
||||
pInst->pModule->parent_v2.xDelete(pInst->pParent);
|
||||
}else{
|
||||
pInst->pModule->parent.xDelete(pInst->pParent);
|
||||
}
|
||||
}
|
||||
Tcl_DecrRefCount(pInst->pScript);
|
||||
ckfree((char *)pInst);
|
||||
}
|
||||
}
|
||||
|
||||
static int f5tTokenizerTokenize(
|
||||
|
||||
static int f5tTokenizerReallyTokenize(
|
||||
Fts5Tokenizer *p,
|
||||
void *pCtx,
|
||||
int flags,
|
||||
@@ -867,6 +912,7 @@ static int f5tTokenizerTokenize(
|
||||
int (*xToken)(void*, int, const char*, int, int, int)
|
||||
){
|
||||
F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
|
||||
F5tTokenizerInstance *pOldInst = 0;
|
||||
void *pOldCtx;
|
||||
int (*xOldToken)(void*, int, const char*, int, int, int);
|
||||
Tcl_Obj *pEval;
|
||||
@@ -875,9 +921,11 @@ static int f5tTokenizerTokenize(
|
||||
|
||||
pOldCtx = pInst->pContext->pCtx;
|
||||
xOldToken = pInst->pContext->xToken;
|
||||
pOldInst = pInst->pContext->pInst;
|
||||
|
||||
pInst->pContext->pCtx = pCtx;
|
||||
pInst->pContext->xToken = xToken;
|
||||
pInst->pContext->pInst = pInst;
|
||||
|
||||
assert(
|
||||
flags==FTS5_TOKENIZE_DOCUMENT
|
||||
@@ -913,9 +961,105 @@ static int f5tTokenizerTokenize(
|
||||
|
||||
pInst->pContext->pCtx = pOldCtx;
|
||||
pInst->pContext->xToken = xOldToken;
|
||||
pInst->pContext->pInst = pOldInst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct CallbackCtx CallbackCtx;
|
||||
struct CallbackCtx {
|
||||
Fts5Tokenizer *p;
|
||||
void *pCtx;
|
||||
int flags;
|
||||
int (*xToken)(void*, int, const char*, int, int, int);
|
||||
};
|
||||
|
||||
static int f5tTokenizeCallback(
|
||||
void *pCtx,
|
||||
int tflags,
|
||||
const char *z, int n,
|
||||
int iStart, int iEnd
|
||||
){
|
||||
CallbackCtx *p = (CallbackCtx*)pCtx;
|
||||
return f5tTokenizerReallyTokenize(p->p, p->pCtx, p->flags, z, n, p->xToken);
|
||||
}
|
||||
|
||||
static int f5tTokenizerTokenize_v2(
|
||||
Fts5Tokenizer *p,
|
||||
void *pCtx,
|
||||
int flags,
|
||||
const char *pText, int nText,
|
||||
const char *pLoc, int nLoc,
|
||||
int (*xToken)(void*, int, const char*, int, int, int)
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
|
||||
|
||||
pInst->pLocale = pLoc;
|
||||
pInst->nLocale = nLoc;
|
||||
|
||||
if( pInst->pParent ){
|
||||
CallbackCtx ctx;
|
||||
ctx.p = p;
|
||||
ctx.pCtx = pCtx;
|
||||
ctx.flags = flags;
|
||||
ctx.xToken = xToken;
|
||||
if( pInst->pModule->parent_v2.xTokenize ){
|
||||
rc = pInst->pModule->parent_v2.xTokenize(
|
||||
pInst->pParent, (void*)&ctx, flags, pText, nText,
|
||||
pLoc, nLoc, f5tTokenizeCallback
|
||||
);
|
||||
}else{
|
||||
rc = pInst->pModule->parent.xTokenize(
|
||||
pInst->pParent, (void*)&ctx, flags, pText, nText, f5tTokenizeCallback
|
||||
);
|
||||
}
|
||||
}else{
|
||||
rc = f5tTokenizerReallyTokenize(p, pCtx, flags, pText, nText, xToken);
|
||||
}
|
||||
|
||||
pInst->pLocale = 0;
|
||||
pInst->nLocale = 0;
|
||||
return rc;
|
||||
}
|
||||
static int f5tTokenizerTokenize(
|
||||
Fts5Tokenizer *p,
|
||||
void *pCtx,
|
||||
int flags,
|
||||
const char *pText, int nText,
|
||||
int (*xToken)(void*, int, const char*, int, int, int)
|
||||
){
|
||||
return f5tTokenizerTokenize_v2(p, pCtx, flags, pText, nText, 0, 0, xToken);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_fts5_locale
|
||||
*/
|
||||
static int SQLITE_TCLAPI f5tTokenizerLocale(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
F5tTokenizerContext *p = (F5tTokenizerContext*)clientData;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( p->xToken==0 ){
|
||||
Tcl_AppendResult(interp,
|
||||
"sqlite3_fts5_locale may only be used by tokenizer callback", (char*)0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp,
|
||||
Tcl_NewStringObj(p->pInst->pLocale, p->pInst->nLocale)
|
||||
);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_fts5_token ?-colocated? TEXT START END
|
||||
*/
|
||||
@@ -928,13 +1072,13 @@ static int SQLITE_TCLAPI f5tTokenizerReturn(
|
||||
F5tTokenizerContext *p = (F5tTokenizerContext*)clientData;
|
||||
int iStart;
|
||||
int iEnd;
|
||||
int nToken;
|
||||
Tcl_Size nToken;
|
||||
int tflags = 0;
|
||||
char *zToken;
|
||||
int rc;
|
||||
|
||||
if( objc==5 ){
|
||||
int nArg;
|
||||
Tcl_Size nArg;
|
||||
char *zArg = Tcl_GetStringFromObj(objv[1], &nArg);
|
||||
if( nArg<=10 && nArg>=2 && memcmp("-colocated", zArg, nArg)==0 ){
|
||||
tflags |= FTS5_TOKEN_COLOCATED;
|
||||
@@ -954,12 +1098,12 @@ static int SQLITE_TCLAPI f5tTokenizerReturn(
|
||||
|
||||
if( p->xToken==0 ){
|
||||
Tcl_AppendResult(interp,
|
||||
"sqlite3_fts5_token may only be used by tokenizer callback", 0
|
||||
"sqlite3_fts5_token may only be used by tokenizer callback", (char*)0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = p->xToken(p->pCtx, tflags, zToken, nToken, iStart, iEnd);
|
||||
rc = p->xToken(p->pCtx, tflags, zToken, (int)nToken, iStart, iEnd);
|
||||
Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return rc==SQLITE_OK ? TCL_OK : TCL_ERROR;
|
||||
|
||||
@@ -1001,32 +1145,112 @@ static int SQLITE_TCLAPI f5tCreateTokenizer(
|
||||
fts5_api *pApi;
|
||||
char *zName;
|
||||
Tcl_Obj *pScript;
|
||||
fts5_tokenizer t;
|
||||
F5tTokenizerModule *pMod;
|
||||
int rc;
|
||||
int rc = SQLITE_OK;
|
||||
int bV2 = 0; /* True to use _v2 API */
|
||||
int iVersion = 2; /* Value for _v2.iVersion */
|
||||
const char *zParent = 0; /* Name of parent tokenizer, if any */
|
||||
int ii = 0;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT");
|
||||
if( objc<4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DB NAME SCRIPT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( f5tDbAndApi(interp, objv[1], &db, &pApi) ){
|
||||
|
||||
/* Parse any options. Set stack variables bV2 and zParent. */
|
||||
for(ii=1; ii<objc-3; ii++){
|
||||
int iOpt = 0;
|
||||
const char *azOpt[] = { "-v2", "-parent", "-version", 0 };
|
||||
if( Tcl_GetIndexFromObj(interp, objv[ii], azOpt, "OPTION", 0, &iOpt) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
switch( iOpt ){
|
||||
case 0: /* -v2 */ {
|
||||
bV2 = 1;
|
||||
break;
|
||||
}
|
||||
case 1: /* -parent */ {
|
||||
ii++;
|
||||
if( ii==objc-3 ){
|
||||
Tcl_AppendResult(
|
||||
interp, "option requires an argument: -parent", (char*)0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zParent = Tcl_GetString(objv[ii]);
|
||||
break;
|
||||
}
|
||||
case 2: /* -version */ {
|
||||
ii++;
|
||||
if( ii==objc-3 ){
|
||||
Tcl_AppendResult(
|
||||
interp, "option requires an argument: -version", (char*)0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[ii], &iVersion) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( f5tDbAndApi(interp, objv[objc-3], &db, &pApi) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zName = Tcl_GetString(objv[2]);
|
||||
pScript = objv[3];
|
||||
|
||||
t.xCreate = f5tTokenizerCreate;
|
||||
t.xTokenize = f5tTokenizerTokenize;
|
||||
t.xDelete = f5tTokenizerDelete;
|
||||
zName = Tcl_GetString(objv[objc-2]);
|
||||
pScript = objv[objc-1];
|
||||
|
||||
pMod = (F5tTokenizerModule*)ckalloc(sizeof(F5tTokenizerModule));
|
||||
memset(pMod, 0, sizeof(F5tTokenizerModule));
|
||||
pMod->interp = interp;
|
||||
pMod->pScript = pScript;
|
||||
pMod->pContext = pContext;
|
||||
Tcl_IncrRefCount(pScript);
|
||||
rc = pApi->xCreateTokenizer(pApi, zName, (void*)pMod, &t, f5tDelTokenizer);
|
||||
pMod->pContext = pContext;
|
||||
if( zParent ){
|
||||
if( bV2 ){
|
||||
fts5_tokenizer_v2 *pParent = 0;
|
||||
rc = pApi->xFindTokenizer_v2(pApi, zParent, &pMod->pParentCtx, &pParent);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(&pMod->parent_v2, pParent, sizeof(fts5_tokenizer_v2));
|
||||
pMod->parent_v2.xDelete(0);
|
||||
}
|
||||
}else{
|
||||
rc = pApi->xFindTokenizer(pApi, zParent, &pMod->pParentCtx,&pMod->parent);
|
||||
if( rc==SQLITE_OK ){
|
||||
pMod->parent.xDelete(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
void *pModCtx = (void*)pMod;
|
||||
if( bV2==0 ){
|
||||
fts5_tokenizer t;
|
||||
t.xCreate = f5tTokenizerCreate;
|
||||
t.xTokenize = f5tTokenizerTokenize;
|
||||
t.xDelete = f5tTokenizerDelete;
|
||||
rc = pApi->xCreateTokenizer(pApi, zName, pModCtx, &t, f5tDelTokenizer);
|
||||
}else{
|
||||
fts5_tokenizer_v2 t2;
|
||||
memset(&t2, 0, sizeof(t2));
|
||||
t2.iVersion = iVersion;
|
||||
t2.xCreate = f5tTokenizerCreate;
|
||||
t2.xTokenize = f5tTokenizerTokenize_v2;
|
||||
t2.xDelete = f5tTokenizerDelete;
|
||||
rc = pApi->xCreateTokenizer_v2(pApi, zName, pModCtx, &t2,f5tDelTokenizer);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error in fts5_api.xCreateTokenizer()", 0);
|
||||
Tcl_AppendResult(interp, (
|
||||
bV2 ? "error in fts5_api.xCreateTokenizer_v2()"
|
||||
: "error in fts5_api.xCreateTokenizer()"
|
||||
), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
@@ -1083,7 +1307,7 @@ static int SQLITE_TCLAPI f5tTokenHash(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
char *z;
|
||||
int n;
|
||||
Tcl_Size n;
|
||||
unsigned int iVal;
|
||||
int nSlot;
|
||||
|
||||
@@ -1096,7 +1320,7 @@ static int SQLITE_TCLAPI f5tTokenHash(
|
||||
}
|
||||
z = Tcl_GetStringFromObj(objv[2], &n);
|
||||
|
||||
iVal = f5t_fts5HashKey(nSlot, z, n);
|
||||
iVal = f5t_fts5HashKey(nSlot, z, (int)n);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -1316,12 +1540,92 @@ static int SQLITE_TCLAPI f5tRegisterOriginText(
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to DROP an fts5 table. It works even if the data
|
||||
** structures fts5 stores within the database are corrupt, which sometimes
|
||||
** prevents a straight "DROP TABLE" command from succeeding.
|
||||
**
|
||||
** The first parameter is the database handle to use for the DROP TABLE
|
||||
** operation. The second is the name of the database to drop the fts5 table
|
||||
** from (i.e. "main", "temp" or the name of an attached database). The
|
||||
** third parameter is the name of the fts5 table to drop.
|
||||
**
|
||||
** SQLITE_OK is returned if the table is successfully dropped. Or, if an
|
||||
** error occurs, an SQLite error code.
|
||||
*/
|
||||
static int sqlite3_fts5_drop_corrupt_table(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zDb, /* Database name ("main", "temp" etc.) */
|
||||
const char *zTab /* Name of fts5 table to drop */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int bDef = 0;
|
||||
|
||||
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDef);
|
||||
if( rc==SQLITE_OK ){
|
||||
char *zScript = sqlite3_mprintf(
|
||||
"DELETE FROM %Q.'%q_data';"
|
||||
"DELETE FROM %Q.'%q_config';"
|
||||
"INSERT INTO %Q.'%q_data' VALUES(10, X'0000000000');"
|
||||
"INSERT INTO %Q.'%q_config' VALUES('version', 4);"
|
||||
"DROP TABLE %Q.'%q';",
|
||||
zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab
|
||||
);
|
||||
|
||||
if( zScript==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
|
||||
rc = sqlite3_exec(db, zScript, 0, 0, 0);
|
||||
if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
|
||||
sqlite3_free(zScript);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_fts5_drop_corrupt_table DB DATABASE TABLE
|
||||
**
|
||||
** Description...
|
||||
*/
|
||||
static int SQLITE_TCLAPI f5tDropCorruptTable(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db = 0;
|
||||
const char *zDb = 0;
|
||||
const char *zTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB DATABASE TABLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( f5tDbPointer(interp, objv[1], &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
zTab = Tcl_GetString(objv[3]);
|
||||
|
||||
rc = sqlite3_fts5_drop_corrupt_table(db, zDb, zTab);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Entry point.
|
||||
*/
|
||||
@@ -1333,13 +1637,15 @@ int Fts5tcl_Init(Tcl_Interp *interp){
|
||||
} aCmd[] = {
|
||||
{ "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 },
|
||||
{ "sqlite3_fts5_token", f5tTokenizerReturn, 1 },
|
||||
{ "sqlite3_fts5_locale", f5tTokenizerLocale, 1 },
|
||||
{ "sqlite3_fts5_tokenize", f5tTokenize, 0 },
|
||||
{ "sqlite3_fts5_create_function", f5tCreateFunction, 0 },
|
||||
{ "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 },
|
||||
{ "sqlite3_fts5_token_hash", f5tTokenHash, 0 },
|
||||
{ "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 },
|
||||
{ "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 },
|
||||
{ "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 }
|
||||
{ "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 },
|
||||
{ "sqlite3_fts5_drop_corrupt_table", f5tDropCorruptTable, 0 }
|
||||
};
|
||||
int i;
|
||||
F5tTokenizerContext *pContext;
|
||||
|
||||
+56
-37
@@ -386,7 +386,6 @@ static int fts5UnicodeCreate(
|
||||
zCat = azArg[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = unicodeSetCategories(p, zCat);
|
||||
}
|
||||
@@ -416,7 +415,6 @@ static int fts5UnicodeCreate(
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
@@ -555,7 +553,7 @@ static int fts5UnicodeTokenize(
|
||||
|
||||
typedef struct PorterTokenizer PorterTokenizer;
|
||||
struct PorterTokenizer {
|
||||
fts5_tokenizer tokenizer; /* Parent tokenizer module */
|
||||
fts5_tokenizer_v2 tokenizer_v2; /* Parent tokenizer module */
|
||||
Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */
|
||||
char aBuf[FTS5_PORTER_MAX_TOKEN + 64];
|
||||
};
|
||||
@@ -567,7 +565,7 @@ static void fts5PorterDelete(Fts5Tokenizer *pTok){
|
||||
if( pTok ){
|
||||
PorterTokenizer *p = (PorterTokenizer*)pTok;
|
||||
if( p->pTokenizer ){
|
||||
p->tokenizer.xDelete(p->pTokenizer);
|
||||
p->tokenizer_v2.xDelete(p->pTokenizer);
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
@@ -586,6 +584,7 @@ static int fts5PorterCreate(
|
||||
PorterTokenizer *pRet;
|
||||
void *pUserdata = 0;
|
||||
const char *zBase = "unicode61";
|
||||
fts5_tokenizer_v2 *pV2 = 0;
|
||||
|
||||
if( nArg>0 ){
|
||||
zBase = azArg[0];
|
||||
@@ -594,14 +593,15 @@ static int fts5PorterCreate(
|
||||
pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
|
||||
if( pRet ){
|
||||
memset(pRet, 0, sizeof(PorterTokenizer));
|
||||
rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer);
|
||||
rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
int nArg2 = (nArg>0 ? nArg-1 : 0);
|
||||
const char **azArg2 = (nArg2 ? &azArg[1] : 0);
|
||||
rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer);
|
||||
const char **az2 = (nArg2 ? &azArg[1] : 0);
|
||||
memcpy(&pRet->tokenizer_v2, pV2, sizeof(fts5_tokenizer_v2));
|
||||
rc = pRet->tokenizer_v2.xCreate(pUserdata, az2, nArg2, &pRet->pTokenizer);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -1252,6 +1252,7 @@ static int fts5PorterTokenize(
|
||||
void *pCtx,
|
||||
int flags,
|
||||
const char *pText, int nText,
|
||||
const char *pLoc, int nLoc,
|
||||
int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
|
||||
){
|
||||
PorterTokenizer *p = (PorterTokenizer*)pTokenizer;
|
||||
@@ -1259,8 +1260,8 @@ static int fts5PorterTokenize(
|
||||
sCtx.xToken = xToken;
|
||||
sCtx.pCtx = pCtx;
|
||||
sCtx.aBuf = p->aBuf;
|
||||
return p->tokenizer.xTokenize(
|
||||
p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb
|
||||
return p->tokenizer_v2.xTokenize(
|
||||
p->pTokenizer, (void*)&sCtx, flags, pText, nText, pLoc, nLoc, fts5PorterCb
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1290,40 +1291,46 @@ static int fts5TriCreate(
|
||||
Fts5Tokenizer **ppOut
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
|
||||
TrigramTokenizer *pNew = 0;
|
||||
UNUSED_PARAM(pUnused);
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
if( nArg%2 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
int i;
|
||||
pNew->bFold = 1;
|
||||
pNew->iFoldParam = 0;
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pNew->bFold = 1;
|
||||
pNew->iFoldParam = 0;
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNew->bFold = (zArg[0]=='0');
|
||||
}
|
||||
}else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
|
||||
}
|
||||
}else{
|
||||
pNew->bFold = (zArg[0]=='0');
|
||||
}
|
||||
}else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
|
||||
}
|
||||
}else{
|
||||
}
|
||||
|
||||
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5TriDelete((Fts5Tokenizer*)pNew);
|
||||
pNew = 0;
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5TriDelete((Fts5Tokenizer*)pNew);
|
||||
pNew = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
*ppOut = (Fts5Tokenizer*)pNew;
|
||||
@@ -1448,7 +1455,6 @@ int sqlite3Fts5TokenizerInit(fts5_api *pApi){
|
||||
} aBuiltin[] = {
|
||||
{ "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
|
||||
{ "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
|
||||
{ "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
|
||||
{ "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}},
|
||||
};
|
||||
|
||||
@@ -1463,6 +1469,19 @@ int sqlite3Fts5TokenizerInit(fts5_api *pApi){
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
fts5_tokenizer_v2 sPorter = {
|
||||
2,
|
||||
fts5PorterCreate,
|
||||
fts5PorterDelete,
|
||||
fts5PorterTokenize
|
||||
};
|
||||
rc = pApi->xCreateTokenizer_v2(pApi,
|
||||
"porter",
|
||||
(void*)pApi,
|
||||
&sPorter,
|
||||
0
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -364,6 +364,9 @@ int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
|
||||
default: return 1; }
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ proc fts5_test_poslist2 {cmd} {
|
||||
sort_poslist $res
|
||||
}
|
||||
|
||||
proc fts5_test_insttoken {cmd iInst iToken} {
|
||||
$cmd xInstToken $iInst $iToken
|
||||
}
|
||||
|
||||
proc fts5_test_collist {cmd} {
|
||||
set res [list]
|
||||
|
||||
@@ -78,6 +82,9 @@ proc fts5_test_columnsize {cmd} {
|
||||
proc fts5_columntext {cmd iCol} {
|
||||
$cmd xColumnText $iCol
|
||||
}
|
||||
proc fts5_columnlocale {cmd iCol} {
|
||||
$cmd xColumnLocale $iCol
|
||||
}
|
||||
|
||||
proc fts5_test_columntext {cmd} {
|
||||
set res [list]
|
||||
@@ -87,6 +94,14 @@ proc fts5_test_columntext {cmd} {
|
||||
set res
|
||||
}
|
||||
|
||||
proc fts5_test_columnlocale {cmd} {
|
||||
set res [list]
|
||||
for {set i 0} {$i < [$cmd xColumnCount]} {incr i} {
|
||||
lappend res [$cmd xColumnLocale $i]
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
proc fts5_test_columntotalsize {cmd} {
|
||||
set res [list]
|
||||
for {set i 0} {$i < [$cmd xColumnCount]} {incr i} {
|
||||
@@ -165,10 +180,12 @@ proc fts5_aux_test_functions {db} {
|
||||
foreach f {
|
||||
fts5_test_columnsize
|
||||
fts5_test_columntext
|
||||
fts5_test_columnlocale
|
||||
fts5_test_columntotalsize
|
||||
fts5_test_poslist
|
||||
fts5_test_poslist2
|
||||
fts5_test_collist
|
||||
fts5_test_insttoken
|
||||
fts5_test_tokenize
|
||||
fts5_test_rowcount
|
||||
fts5_test_rowid
|
||||
@@ -177,6 +194,7 @@ proc fts5_aux_test_functions {db} {
|
||||
fts5_test_queryphrase
|
||||
fts5_test_phrasecount
|
||||
fts5_columntext
|
||||
fts5_columnlocale
|
||||
fts5_queryphrase
|
||||
fts5_collist
|
||||
} {
|
||||
|
||||
@@ -440,7 +440,7 @@ do_execsql_test 16.1 {
|
||||
proc funk {} {
|
||||
db eval { UPDATE n1_config SET v=50 WHERE k='version' }
|
||||
set fd [db incrblob main n1_data block 10]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
fconfigure $fd -translation binary
|
||||
# puts -nonewline $fd "\x44\x45"
|
||||
close $fd
|
||||
}
|
||||
@@ -453,7 +453,7 @@ db func funk funk
|
||||
# statement no longer fails.
|
||||
#
|
||||
do_catchsql_test 16.2 {
|
||||
SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d'
|
||||
SELECT funk(), format('%g',bm25(n1)), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d'
|
||||
} {0 {{} -1e-06 {}}}
|
||||
# {1 {SQL logic error}}
|
||||
|
||||
|
||||
@@ -163,6 +163,17 @@ do_execsql_test 1.8.2 {
|
||||
SELECT count(*) FROM t1 WHERE t1 MATCH 'x' AND rowid < 'text';
|
||||
} {10000}
|
||||
|
||||
do_execsql_test 1.8.3 {
|
||||
SELECT count(*) FROM t1 WHERE t1 MATCH 'x' AND rowid<5000 AND rowid < 'text';
|
||||
} {4999}
|
||||
do_execsql_test 1.8.4 {
|
||||
SELECT count(*) FROM t1 WHERE t1 MATCH 'x' AND rowid>5000 AND rowid > 'text';
|
||||
} {0}
|
||||
|
||||
do_catchsql_test 1.9 {
|
||||
SELECT * FROM t1('*xy');
|
||||
} {1 {unknown special query: xy}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
||||
|
||||
@@ -293,6 +293,16 @@ do_catchsql_test 4.4.4 {
|
||||
SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH NULL
|
||||
} {1 {parse error in rank function: }}
|
||||
|
||||
# Check that the second and subsequent rank= constraints are ignored.
|
||||
#
|
||||
do_catchsql_test 4.3.3 {
|
||||
SELECT *, rank FROM t3
|
||||
WHERE t3 MATCH 'a' AND
|
||||
rank MATCH 'nosuch()' AND
|
||||
rank MATCH 'rowidmod(3)'
|
||||
ORDER BY rank ASC
|
||||
} {1 {unable to use function MATCH in the requested context}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
# 2024 July 30
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file verifies that:
|
||||
#
|
||||
# * blob values may be written to locale=0 tables.
|
||||
#
|
||||
# * blob values - other than fts5_locale() values - may not be written
|
||||
# to locale=0 tables. This is an SQLITE_MISMATCH error
|
||||
#
|
||||
# * blob values may be returned by queries on the external-content table
|
||||
# of a locale=0 table.
|
||||
#
|
||||
# * blob values not may be returned by queries on the external-content
|
||||
# table of a locale=1 table, apart from fts5_locale() blobs. This is an
|
||||
# SQLITE_MISMATCH error.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5blob
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Test that blobs may be stored in normal locale=0 tables.
|
||||
#
|
||||
foreach {tn enc} {
|
||||
1 utf8
|
||||
2 utf16
|
||||
} {
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
|
||||
execsql "PRAGMA encoding = $enc"
|
||||
|
||||
execsql "
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y);
|
||||
"
|
||||
do_execsql_test 1.$tn.0 {
|
||||
CREATE VIRTUAL TABLE tt USING fts5vocab('t1', 'instance');
|
||||
INSERT INTO t1(rowid, x, y) VALUES(1, 555, X'0000000041424320444546');
|
||||
INSERT INTO t1(rowid, x, y) VALUES(2, 666, X'41424300444546');
|
||||
INSERT INTO t1(rowid, x, y) VALUES(3, 777, 'xyz');
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.1 {
|
||||
SELECT rowid, quote(x), quote(y) FROM t1
|
||||
} {
|
||||
1 555 X'0000000041424320444546'
|
||||
2 666 X'41424300444546'
|
||||
3 777 'xyz'
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.2 {
|
||||
DELETE FROM t1 WHERE rowid=2;
|
||||
DELETE FROM t1 WHERE rowid=1;
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.3 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test that a blob may be stored and retrieved in an unindexed column of
|
||||
# a regular table with locale=1.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y UNINDEXED, locale=1);
|
||||
INSERT INTO t1(rowid, x, y) VALUES(12, 'twelve', X'0000000041424320444546');
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
select rowid, x, quote(y) FROM t1
|
||||
} {
|
||||
12 twelve X'0000000041424320444546'
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test that blobs may not be written to any type of table with locale=1
|
||||
# set. Except, they may be written to UNINDEXED columns.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a, b, locale=1);
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(a, b, locale=1, content=t2);
|
||||
CREATE VIRTUAL TABLE x3 USING fts5(a, b, locale=1, content=);
|
||||
}
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
INSERT INTO x1(rowid, a, b) VALUES(113, 'hello world', X'123456');
|
||||
} {0 {}}
|
||||
do_catchsql_test 3.2 {
|
||||
INSERT INTO x2(rowid, a, b) VALUES(113, 'hello world', X'123456');
|
||||
} {0 {}}
|
||||
do_catchsql_test 3.3 {
|
||||
INSERT INTO x3(rowid, a, b) VALUES(113, 'hello world', X'123456');
|
||||
} {0 {}}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test that fts5_locale() values may not be written to any type of table
|
||||
# without locale=1 set. Even to an UNINDEXED column.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(a, b, content=t2);
|
||||
CREATE VIRTUAL TABLE x3 USING fts5(a, b, content=);
|
||||
|
||||
CREATE VIRTUAL TABLE x4 USING fts5(a, b, c UNINDEXED);
|
||||
}
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
INSERT INTO x1(rowid, a, b)
|
||||
VALUES(113, 'hello world', fts5_locale('en_AU', 'abc'));
|
||||
} {1 {fts5_locale() requires locale=1}}
|
||||
do_catchsql_test 3.2 {
|
||||
INSERT INTO x2(rowid, a, b)
|
||||
VALUES(113, 'hello world', fts5_locale('en_AU', 'abc'));
|
||||
} {1 {fts5_locale() requires locale=1}}
|
||||
do_catchsql_test 3.3 {
|
||||
INSERT INTO x3(rowid, a, b)
|
||||
VALUES(113, 'hello world', fts5_locale('en_AU', 'abc'));
|
||||
} {1 {fts5_locale() requires locale=1}}
|
||||
do_catchsql_test 3.4 {
|
||||
INSERT INTO x4(rowid, a, b, c)
|
||||
VALUES(113, 'hello world', 'yesno', fts5_locale('en_AU', 'abc'));
|
||||
} {1 {fts5_locale() requires locale=1}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
}
|
||||
|
||||
foreach {tn sql} {
|
||||
1 { INSERT INTO x1(rowid, x) VALUES(4.5, 'abcd') }
|
||||
2 { INSERT INTO x1(rowid, x) VALUES('xyz', 'abcd') }
|
||||
3 { INSERT INTO x1(rowid, x) VALUES(X'001122', 'abcd') }
|
||||
} {
|
||||
do_catchsql_test 4.1.$tn $sql {1 {datatype mismatch}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -55,5 +55,22 @@ do_execsql_test 1.5 {
|
||||
SELECT * FROM t4t
|
||||
} {สนามกีฬา 1 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 "
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(c,
|
||||
tokenize=\"unicode61 categories ' \t'\");
|
||||
"
|
||||
|
||||
do_catchsql_test 2.1 "
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(c,
|
||||
tokenize=\"unicode61 categories 'N*\t\tMYZ'\");
|
||||
" {1 {error in tokenizer constructor}}
|
||||
|
||||
do_catchsql_test 2.2 "
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(c,
|
||||
tokenize=\"unicode61 categories 'N*\t\tXYZ'\");
|
||||
" {1 {error in tokenizer constructor}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -267,4 +267,24 @@ do_execsql_test 8.2 {
|
||||
SELECT rowid FROM ft('four');
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=0);
|
||||
INSERT INTO ft VALUES('hello world');
|
||||
INSERT INTO ft VALUES('one two three');
|
||||
}
|
||||
|
||||
do_catchsql_test 9.1 {
|
||||
INSERT INTO ft(ft, rowid, x) VALUES('delete', 1, 'hello world');
|
||||
} {0 {}}
|
||||
|
||||
do_catchsql_test 9.2 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(x, content='', contentless_delete=2);
|
||||
} {1 {malformed contentless_delete=... directive}}
|
||||
|
||||
do_catchsql_test 9.3 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(x, content='', contentless_delete=11);
|
||||
} {1 {malformed contentless_delete=... directive}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -20,6 +20,7 @@ ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
unset -nocomplain res
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, content='', contentless_delete=1);
|
||||
|
||||
@@ -101,4 +101,25 @@ do_catchsql_test 3.1 {
|
||||
SELECT * FROM t3 WHERE t3 MATCH 'o';
|
||||
} {1 {fts5: missing row 3 from content table 'main'.'t3_content'}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x);
|
||||
INSERT INTO t2 VALUES('one two three');
|
||||
INSERT INTO t2 VALUES('four five six');
|
||||
INSERT INTO t2 VALUES('seven eight nine');
|
||||
INSERT INTO t2 VALUES('ten eleven twelve');
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
SELECT hex(block) FROM t2_data WHERE id=1;
|
||||
} {040C}
|
||||
do_execsql_test 4.2 {
|
||||
UPDATE t2_data SET block = X'0402' WHERE id=1
|
||||
}
|
||||
breakpoint
|
||||
do_catchsql_test 4.3 {
|
||||
DELETE FROM t2 WHERE rowid=3
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -100,6 +100,7 @@ set lrowid [db one {SELECT max(rowid) FROM t1_data WHERE (rowid & $mask)=0}]
|
||||
set nbyte [db one {SELECT length(block) FROM t1_data WHERE rowid=$lrowid}]
|
||||
set all [db eval {SELECT rowid FROM t1}]
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
unset -nocomplain res
|
||||
for {set i [expr $nbyte-2]} {$i>=0} {incr i -1} {
|
||||
do_execsql_test 2.$i.1 {
|
||||
BEGIN;
|
||||
@@ -152,7 +153,7 @@ foreach {tn hdr} {
|
||||
execsql BEGIN
|
||||
|
||||
set fd [db incrblob main x3_data block $rowid]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
fconfigure $fd -translation binary
|
||||
set existing [read $fd [string length $hdr]]
|
||||
seek $fd 0
|
||||
puts -nonewline $fd $hdr
|
||||
@@ -238,7 +239,7 @@ foreach {tn hdr} {
|
||||
execsql BEGIN
|
||||
|
||||
set fd [db incrblob main x5_data block $rowid]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
fconfigure $fd -translation binary
|
||||
puts -nonewline $fd $hdr
|
||||
close $fd
|
||||
|
||||
|
||||
+170
-11
@@ -680,11 +680,11 @@ do_test 12.0 {
|
||||
|
||||
do_catchsql_test 11.1 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'abandon';
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_catchsql_test 11.2 {
|
||||
INSERT INTO t1(t1, rank) VALUES('merge', 500);
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -1040,7 +1040,7 @@ do_test 16.0 {
|
||||
|
||||
do_catchsql_test 16.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -1126,7 +1126,7 @@ do_test 17.0 {
|
||||
|
||||
do_catchsql_test 17.1 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'abandon';
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -1630,7 +1630,7 @@ do_test 20.0 {
|
||||
|
||||
do_catchsql_test 20.1 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'abandon';
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -2100,7 +2100,7 @@ do_test 22.0 {
|
||||
|
||||
do_catchsql_test 22.1 {
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -3700,7 +3700,7 @@ do_catchsql_test 32.1 {
|
||||
highlight(t1, 2, '[', ']')
|
||||
FROM t1('g + h')
|
||||
WHERE rank MATCH 'bm25(1.0, 1.0)' ORDER BY rank;
|
||||
} {1 {vtable constructor failed: t1}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_catchsql_test 32.2 {
|
||||
SELECT * FROM t3;
|
||||
@@ -8958,7 +8958,6 @@ do_catchsql_test 61.2 {
|
||||
SELECT * FROM t3 ORDER BY rowid;
|
||||
} {/*malformed database schema*/}
|
||||
|
||||
breakpoint
|
||||
#-------------------------------------------------------------------------
|
||||
do_test 62.0 {
|
||||
sqlite3 db {}
|
||||
@@ -10768,6 +10767,7 @@ do_catchsql_test 73.1 {
|
||||
reset_db
|
||||
do_test 74.0 {
|
||||
sqlite3 db {}
|
||||
sqlite3_fts5_register_matchinfo db
|
||||
db deserialize [decode_hexdb {
|
||||
| size 106496 pagesize 4096 filename x.db
|
||||
| page 1 offset 0
|
||||
@@ -14587,14 +14587,19 @@ do_test 74.0 {
|
||||
| end x.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 74.1 {
|
||||
SELECT rowid, quote(matchinfo(t1,'p�xyb<s')) FROM t1 WHERE t1 MATCH 'e*';
|
||||
do_catchsql_test 74.0.5 {
|
||||
SELECT matchinfo(1,2);
|
||||
} {1 {unable to use function matchinfo in the requested context}}
|
||||
|
||||
do_catchsql_test 74.1 {
|
||||
SELECT rowid, quote(matchinfo(t1,'pxyb<s')) FROM t1 WHERE t1 MATCH 'e*';
|
||||
} {1 {unrecognized matchinfo flag: <}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 75.0 {
|
||||
sqlite3 db {}
|
||||
sqlite3_fts5_register_matchinfo db
|
||||
db deserialize [decode_hexdb {
|
||||
| size 32768 pagesize 4096 filename crash-033d665d5caa8d.db
|
||||
| page 1 offset 0
|
||||
@@ -14791,7 +14796,7 @@ do_test 75.0 {
|
||||
|
||||
do_catchsql_test 75.1 {
|
||||
SELECT rowid, quote(matchinfo(t1,'pcxybs')) FROM t1 WHERE t1 MATCH 'e*';
|
||||
} {1 {unable to use function matchinfo in the requested context}}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -15519,6 +15524,160 @@ do_catchsql_test 80.1 {
|
||||
SELECT snippet(rowid, -1, '.', '..', '[', '(]'),snippet(rowid, -1, '.', '.', '', '(]'), highlight(t1, 29, 1 , '') FROM t1('g+ h') WHERE rank MATCH 'bm25(1.0, 10)' ORDER BY NOT (SELECT 1 FROM t1('g+ æ') WHERE rank MATCH 'bm25(1.0, 10)' ORDER BY rank);
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 81.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 40960 pagesize 4096 filename crash-44e8035a976422.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 0a .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 0d 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 00 00 00 0d 0b 6e 00 0f a3 0f 4c ..........n....L
|
||||
| 112: 0e e1 0e 81 0e 24 0d cc 0d 72 0d 1b 0c b0 0c 50 .....$...r.....P
|
||||
| 128: 0b f8 0b b3 0b 6e 00 00 00 00 00 00 00 00 00 00 .....n..........
|
||||
| 2912: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 0d ..............C.
|
||||
| 2928: 06 17 11 11 08 75 74 61 62 6c 65 74 34 74 34 43 .....utablet4t4C
|
||||
| 2944: 52 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 REATE VIRTUAL TA
|
||||
| 2960: 42 4c 45 20 74 34 20 55 53 49 4e 47 20 66 74 73 BLE t4 USING fts
|
||||
| 2976: 35 76 6f 63 61 62 28 27 74 32 27 2c 20 27 72 6f 5vocab('t2', 'ro
|
||||
| 2992: 77 27 29 43 0c 06 17 11 11 08 75 74 61 62 6c 65 w')C......utable
|
||||
| 3008: 74 33 74 33 43 52 45 41 54 45 20 56 49 52 54 55 t3t3CREATE VIRTU
|
||||
| 3024: 41 4c 20 54 41 42 4c 45 20 74 33 20 55 53 49 4e AL TABLE t3 USIN
|
||||
| 3040: 47 20 66 74 73 35 76 6f 63 61 62 28 27 74 31 27 G fts5vocab('t1'
|
||||
| 3056: 2c 20 27 72 6f 77 27 29 56 0b 06 17 1f 1f 01 7d , 'row')V.......
|
||||
| 3072: 74 61 62 6c 65 74 32 5f 63 6f 6e 66 69 67 74 32 tablet2_configt2
|
||||
| 3088: 5f 63 6f 6e 66 69 67 0a 43 52 45 41 54 45 20 54 _config.CREATE T
|
||||
| 3104: 41 42 4c 45 20 27 74 32 5f 63 6f 6e 66 69 67 27 ABLE 't2_config'
|
||||
| 3120: 28 6b 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 (k PRIMARY KEY,
|
||||
| 3136: 76 29 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 v) WITHOUT ROWID
|
||||
| 3152: 5e 0a 07 17 21 21 01 81 07 74 61 62 6c 65 74 32 ^...!!...tablet2
|
||||
| 3168: 5f 63 6f 6e 74 65 6e 74 74 32 5f 63 6f 6e 74 65 _contentt2_conte
|
||||
| 3184: 6e 74 09 43 52 45 41 54 45 20 54 41 42 4c 45 20 nt.CREATE TABLE
|
||||
| 3200: 27 74 32 5f 63 6f 6e 74 65 6e 74 27 28 69 64 20 't2_content'(id
|
||||
| 3216: 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 INTEGER PRIMARY
|
||||
| 3232: 4b 45 59 2c 20 63 30 2c 20 63 31 2c 20 63 32 29 KEY, c0, c1, c2)
|
||||
| 3248: 69 09 07 17 19 19 01 81 2d 74 61 62 6c 65 74 32 i.......-tablet2
|
||||
| 3264: 5f 69 64 78 74 32 5f 69 64 78 08 43 52 45 41 54 _idxt2_idx.CREAT
|
||||
| 3280: 45 20 54 41 42 4c 45 20 27 74 32 5f 69 64 78 27 E TABLE 't2_idx'
|
||||
| 3296: 28 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 67 (segid, term, pg
|
||||
| 3312: 6e 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 28 no, PRIMARY KEY(
|
||||
| 3328: 73 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 49 segid, term)) WI
|
||||
| 3344: 54 48 4f 55 54 20 52 4f 57 49 44 55 08 07 17 1b THOUT ROWIDU....
|
||||
| 3360: 1b 01 81 01 74 61 62 6c 65 74 32 5f 64 61 74 61 ....tablet2_data
|
||||
| 3376: 74 32 5f 64 61 74 61 07 43 52 45 41 54 45 20 54 t2_data.CREATE T
|
||||
| 3392: 41 42 4c 45 20 27 74 32 5f 64 61 74 61 27 28 69 ABLE 't2_data'(i
|
||||
| 3408: 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 d INTEGER PRIMAR
|
||||
| 3424: 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 4c 4f Y KEY, block BLO
|
||||
| 3440: 42 29 58 07 07 17 11 11 08 81 1d 74 61 62 6c 65 B)X........table
|
||||
| 3456: 74 32 74 32 43 52 45 41 54 45 20 56 49 52 54 55 t2t2CREATE VIRTU
|
||||
| 3472: 41 4c 20 54 41 42 4c 45 20 74 32 20 55 53 49 4e AL TABLE t2 USIN
|
||||
| 3488: 47 20 66 74 73 35 28 27 61 27 2c 5b 62 5d 2c 22 G fts5('a',[b],.
|
||||
| 3504: 63 22 2c 64 65 74 61 69 6c 3d 6e 6f 6e 65 2c 63 c.,detail=none,c
|
||||
| 3520: 6f 6c 75 6d 6e 73 69 7a 65 3d 30 29 56 06 06 17 olumnsize=0)V...
|
||||
| 3536: 1f 1f 01 7d 74 61 62 6c 65 74 31 5f 63 6f 6e 66 ....tablet1_conf
|
||||
| 3552: 69 67 74 31 5f 63 6f 6e 66 69 67 06 43 52 45 41 igt1_config.CREA
|
||||
| 3568: 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 6f 6e TE TABLE 't1_con
|
||||
| 3584: 66 69 67 27 28 6b 20 50 52 49 4d 41 52 59 20 4b fig'(k PRIMARY K
|
||||
| 3600: 45 59 2c 20 76 29 20 57 49 54 48 4f 55 54 20 52 EY, v) WITHOUT R
|
||||
| 3616: 4f 57 49 44 5b 05 07 17 21 21 01 81 01 74 61 62 OWID[...!!...tab
|
||||
| 3632: 6c 65 74 31 5f 64 6f 63 73 69 7a 65 74 31 5f 64 let1_docsizet1_d
|
||||
| 3648: 6f 63 73 69 7a 65 05 43 52 45 41 54 45 20 54 41 ocsize.CREATE TA
|
||||
| 3664: 42 4c 45 20 27 74 31 5f 64 6f 63 73 69 7a 65 27 BLE 't1_docsize'
|
||||
| 3680: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM
|
||||
| 3696: 41 52 59 20 4b 45 59 2c 20 73 7a 20 42 4c 4f 42 ARY KEY, sz BLOB
|
||||
| 3712: 29 5e 04 07 17 21 21 01 81 07 74 61 62 6c 65 74 )^...!!...tablet
|
||||
| 3728: 31 5f 63 6f 6e 74 65 6e 74 74 31 5f 63 6f 6e 74 1_contentt1_cont
|
||||
| 3744: 65 6e 74 04 43 52 45 41 54 45 20 54 41 42 4c 45 ent.CREATE TABLE
|
||||
| 3760: 20 27 74 31 5f 63 6f 6e 74 65 6e 74 27 28 69 64 't1_content'(id
|
||||
| 3776: 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 52 59 INTEGER PRIMARY
|
||||
| 3792: 20 4b 45 59 2c 20 63 30 2c 20 63 31 2c 20 63 32 KEY, c0, c1, c2
|
||||
| 3808: 29 69 03 07 17 19 19 01 81 2d 74 61 62 6c 65 74 )i.......-tablet
|
||||
| 3824: 31 5f 69 64 78 74 31 5f 69 64 78 03 43 52 45 41 1_idxt1_idx.CREA
|
||||
| 3840: 54 45 20 54 41 42 4c 45 20 27 74 31 5f 69 64 78 TE TABLE 't1_idx
|
||||
| 3856: 27 28 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 70 '(segid, term, p
|
||||
| 3872: 67 6e 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 59 gno, PRIMARY KEY
|
||||
| 3888: 28 73 65 67 69 64 2c 20 74 65 72 6d 29 29 20 57 (segid, term)) W
|
||||
| 3904: 49 54 48 4f 55 54 20 52 4f 57 49 44 55 02 07 17 ITHOUT ROWIDU...
|
||||
| 3920: 1b 1b 01 81 01 74 61 62 6c 65 74 31 5f 64 61 74 .....tablet1_dat
|
||||
| 3936: 61 74 31 5f 64 61 74 61 02 43 52 45 41 54 45 20 at1_data.CREATE
|
||||
| 3952: 54 41 42 4c 45 20 27 74 31 5f 64 61 74 61 27 28 TABLE 't1_data'(
|
||||
| 3968: 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 id INTEGER PRIMA
|
||||
| 3984: 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 4c RY KEY, block BL
|
||||
| 4000: 4f 42 29 5b 01 07 17 11 11 08 81 23 74 61 62 6c OB)[.......#tabl
|
||||
| 4016: 65 74 31 74 31 43 52 45 41 54 45 20 56 49 52 54 et1t1CREATE VIRT
|
||||
| 4032: 55 41 4c 20 54 41 42 4c 45 20 74 31 20 55 53 49 UAL TABLE t1 USI
|
||||
| 4048: 4e 47 20 66 74 73 35 28 61 2c 62 20 75 6e 69 6e NG fts5(a,b unin
|
||||
| 4064: 64 65 78 65 64 2c 63 2c 74 6f 6b 65 6e 69 7a 65 dexed,c,tokenize
|
||||
| 4080: 3d 22 70 6f 72 74 65 72 20 61 73 63 69 69 22 29 =.porter ascii.)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 0f 68 00 05 0f 13 00 0f e6 0f 13 0f a8 0f 7c ..h............|
|
||||
| 16: 0f 2a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .*..............
|
||||
| 3856: 00 00 00 15 0a 03 00 30 00 00 00 00 01 03 03 00 .......0........
|
||||
| 3872: 03 01 01 01 02 01 01 03 01 01 37 8c 80 80 80 80 ..........7.....
|
||||
| 3888: 01 03 00 74 00 00 00 2e 02 30 61 03 02 02 01 01 ...t.....0a.....
|
||||
| 3904: 62 03 02 03 01 01 63 03 02 04 01 01 67 03 06 01 b.....c.....g...
|
||||
| 3920: 02 02 01 01 68 03 06 01 02 03 01 01 69 03 06 01 ....h.......i...
|
||||
| 3936: 02 04 04 06 06 06 08 08 0f ef 00 14 2a 00 00 00 ............*...
|
||||
| 3952: 00 01 02 02 00 02 01 01 01 02 01 01 25 88 80 80 ............%...
|
||||
| 3968: 80 80 01 03 00 50 00 00 00 1f 02 30 67 02 08 02 .....P.....0g...
|
||||
| 3984: 01 02 02 01 01 68 02 08 03 8d 02 03 01 01 6a 42 .....h........jB
|
||||
| 4000: 08 04 01 02 04 04 09 09 37 84 80 80 80 80 01 03 ........7.......
|
||||
| 4016: 00 74 00 00 00 2e 02 30 61 01 12 02 01 01 62 01 .t.....0a.....b.
|
||||
| 4032: 02 03 01 01 63 01 02 04 01 01 67 01 06 01 02 02 ....c.....g.....
|
||||
| 4048: 01 01 68 01 05 01 02 03 01 01 69 01 06 01 02 04 ..h.......i.....
|
||||
| 4064: 04 06 06 06 08 08 07 01 03 00 14 03 09 00 09 00 ................
|
||||
| 4080: 00 00 11 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 03 0f ec 00 0f fa 0f f3 0f ec 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 00 00 00 00 06 04 01 0c ................
|
||||
| 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0c 01 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 03 0f be 00 0f ea 00 00 00 00 00 00 ................
|
||||
| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 03 ................
|
||||
| 4032: 05 00 17 17 17 61 20 62 20 63 67 20 68 20 69 67 .....a b cg h ig
|
||||
| 4048: 20 68 20 69 14 02 05 00 17 17 17 67 20 68 20 69 h i.......g h i
|
||||
| 4064: 61 20 62 20 63 67 20 68 20 69 14 01 05 00 17 17 a b cg h i......
|
||||
| 4080: 17 61 20 62 20 63 64 20 65 20 66 67 20 68 20 69 .a b cd e fg h i
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 03 0f e8 00 0f f8 0f f0 0f e8 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 06 03 03 00 12 03 00 03 ................
|
||||
| 4080: 06 02 03 00 12 03 00 03 06 01 03 00 12 03 00 03 ................
|
||||
| page 6 offset 20480
|
||||
| 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 7 offset 24576
|
||||
| 0: 0d 00 00 00 03 0f 9e 00 0f e6 0f ef 0f 9e 00 00 ................
|
||||
| 3984: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 84 ..............A.
|
||||
| 4000: 80 80 80 80 01 04 00 81 06 00 00 00 34 02 30 61 ............4.0a
|
||||
| 4016: 01 01 01 01 01 62 01 01 01 01 01 63 01 01 01 01 .....b.....c....
|
||||
| 4032: e6 64 01 01 01 65 01 01 01 66 01 01 01 67 01 01 .d...e...f...g..
|
||||
| 4048: 01 01 01 68 01 01 01 01 01 69 01 01 01 04 06 06 ...h.....i......
|
||||
| 4064: 06 04 04 04 06 06 07 01 03 00 14 03 09 09 09 0f ................
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
|
||||
| page 8 offset 28672
|
||||
| 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 9 offset 32768
|
||||
| 0: 0d 00 00 00 03 0f be 00 0f ea 0f d4 0f be 00 00 ................
|
||||
| 4016: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 03 ................
|
||||
| 4032: 05 00 17 17 17 61 20 62 20 63 67 20 68 20 69 67 .....a b cg h ig
|
||||
| 4048: 20 68 21 69 14 02 05 00 17 17 17 67 20 68 20 69 h!i.......g h i
|
||||
| 4064: 61 20 62 20 63 67 20 68 20 69 14 01 05 00 17 17 a b cg h i......
|
||||
| 4080: 17 61 20 62 20 63 64 20 65 20 66 67 20 68 20 69 .a b cd e fg h i
|
||||
| page 10 offset 36864
|
||||
| 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.
|
||||
| end crash-44e8035a976422.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 81.2 {
|
||||
UPDATE t1 SET b=zeroblob(299);
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# 2024 Aug 28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5corrupt8
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
UPDATE t1_data SET block='hello world' WHERE id=10
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
SELECT * FROM t1
|
||||
} {1 {database disk image is malformed}}
|
||||
do_catchsql_test 1.3 {
|
||||
DROP TABLE t1
|
||||
} {0 {}}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT * FROM sqlite_schema
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
}
|
||||
do_execsql_test 2.1 {
|
||||
UPDATE t1_config SET v=555 WHERE k='version'
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT * FROM t1
|
||||
} {1 {invalid fts5 file format (found 555, expected 4 or 5) - run 'rebuild'}}
|
||||
do_catchsql_test 2.3 {
|
||||
DROP TABLE t1
|
||||
} {1 {invalid fts5 file format (found 555, expected 4 or 5) - run 'rebuild'}}
|
||||
do_test 2.4 {
|
||||
sqlite3_fts5_drop_corrupt_table db main t1
|
||||
} {}
|
||||
do_execsql_test 2.5 {
|
||||
SELECT * FROM sqlite_schema
|
||||
}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
DELETE FROM t1_config;
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_catchsql_test 3.2 {
|
||||
SELECT * FROM t1
|
||||
} {1 {invalid fts5 file format (found 0, expected 4 or 5) - run 'rebuild'}}
|
||||
do_catchsql_test 3.3 {
|
||||
DROP TABLE t1
|
||||
} {1 {invalid fts5 file format (found 0, expected 4 or 5) - run 'rebuild'}}
|
||||
|
||||
|
||||
do_test 3.4 {
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
} {1}
|
||||
do_test 3.5 {
|
||||
sqlite3_fts5_drop_corrupt_table db main t1
|
||||
} {}
|
||||
do_test 3.6 {
|
||||
sqlite3_db_config db DEFENSIVE -1
|
||||
} {1}
|
||||
do_execsql_test 3.7 {
|
||||
SELECT * FROM sqlite_schema
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -86,13 +86,13 @@ do_execsql_test 3.0 {
|
||||
INSERT INTO e1 VALUES ('just a few words with a / inside');
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"just"' ORDER BY rank;
|
||||
SELECT rowid, format('%g',bm25(e1)) FROM e1 WHERE e1 MATCH '"just"' ORDER BY rank;
|
||||
} {1 -1e-06}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT rowid FROM e1 WHERE e1 MATCH '"/" OR "just"'
|
||||
} 1
|
||||
do_execsql_test 3.3 {
|
||||
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank;
|
||||
SELECT rowid, format('%g',bm25(e1)) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank;
|
||||
} {1 -1e-06}
|
||||
|
||||
do_execsql_test 3.4 "
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# 2024 August 8
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5expr
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a);
|
||||
INSERT INTO x1(rowid, a) VALUES (113, 'fts5 expr test');
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT rowid FROM x1('expr');
|
||||
} {113}
|
||||
|
||||
for {set ii 0} {$ii < 300} {incr ii} {
|
||||
set expr "expr "
|
||||
append expr [string repeat "NOT abcd " $ii]
|
||||
|
||||
if {$ii<257} {
|
||||
set res {0 113}
|
||||
} else {
|
||||
set res {1 {fts5 expression tree is too large (maximum depth 256)}}
|
||||
}
|
||||
do_catchsql_test 1.1.$ii {
|
||||
SELECT rowid FROM x1($expr)
|
||||
} $res
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT rowid FROM x1 WHERE a MATCH '"..."'
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -90,7 +90,7 @@ set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
|
||||
do_faultsim_test 4 -faults oom-* -body {
|
||||
db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
|
||||
} -test {
|
||||
faultsim_test_result {0 {0 {} 3}}
|
||||
faultsim_test_result {0 {0 {} 2}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
# 2010 June 15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5faultI
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set ::testprefix fts5faultI
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA encoding = utf16;
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, locale=1);
|
||||
INSERT INTO t1 VALUES('origintext unicode61 ascii porter trigram');
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
faultsim_restore_and_reopen
|
||||
|
||||
do_faultsim_test 1 -faults oom* -prep {
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT rowid FROM t1(fts5_locale('en_US', 'origintext'));
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 1}
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql {
|
||||
SELECT * FROM t1('ascii');
|
||||
}
|
||||
} -body {
|
||||
execsql {
|
||||
UPDATE t1 SET rowid=rowid+1;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_faultsim_test 3 -faults oom* -prep {
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT fts5_columnlocale(t1, 0) FROM t1('unicode*');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {{}}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE w1 USING fts5(a);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 4 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO w1 VALUES('token token token');
|
||||
}
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO w1(w1, rank) VALUES('rank', 'bm25()');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
do_faultsim_test 5 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO w1 VALUES('one');
|
||||
SAVEPOINT one;
|
||||
INSERT INTO w1 VALUES('two');
|
||||
ROLLBACK TO one;
|
||||
}
|
||||
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO w1 VALUES('string');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE w1 USING fts5(a);
|
||||
INSERT INTO w1 VALUES('one two three');
|
||||
}
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_faultsim_test 5 -faults oom* -prep {
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT fts5_test_insttoken(w1, 0, 0) FROM w1('two');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 two} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE w1 USING fts5(a);
|
||||
INSERT INTO w1 VALUES('one two three');
|
||||
}
|
||||
fts5_aux_test_functions db
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 6 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO w1 VALUES('four five six');
|
||||
SAVEPOINT abc;
|
||||
INSERT INTO w1 VALUES('seven eight nine');
|
||||
SAVEPOINT def;
|
||||
INSERT INTO w1 VALUES('ten eleven twelve');
|
||||
}
|
||||
} -body {
|
||||
execsql {
|
||||
RELEASE abc;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE w1 USING fts5(a);
|
||||
INSERT INTO w1 VALUES('one two three');
|
||||
INSERT INTO w1 VALUES('three two one');
|
||||
DELETE FROM w1_content WHERE rowid=1;
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 7 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval { SELECT * FROM w1 }
|
||||
} -body {
|
||||
execsql {
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} -test {
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
fts5_tclnum_register db
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(
|
||||
x, tokenize = "tclnum query", detail=columns
|
||||
);
|
||||
INSERT INTO ft VALUES('one two three i ii iii');
|
||||
INSERT INTO ft VALUES('four five six iv v vi');
|
||||
INSERT INTO ft VALUES('eight nine ten viii ix x');
|
||||
} {}
|
||||
|
||||
do_faultsim_test 8.1 -faults oom* -prep {
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT fts5_test_collist (ft) FROM ft('one two');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {{0.0 1.0}}} {1 {SQL logic error}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
do_faultsim_test 8.2 -faults oom* -prep {
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT rowid FROM ft('one two') ORDER BY rank;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 1} {1 {SQL logic error}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 9.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x);
|
||||
INSERT INTO ft VALUES('one two three i ii iii');
|
||||
INSERT INTO ft VALUES('four five six iv v vi');
|
||||
INSERT INTO ft VALUES('eight nine ten viii ix x');
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 9.1 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
UPDATE ft SET rowid=4 WHERE rowid=1
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
do_faultsim_test 9.2 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT rowid FROM ft WHERE x MATCH 'one AND two AND three'
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, locale=1);
|
||||
INSERT INTO ft VALUES(fts5_locale('hello', 'one two three i ii iii'));
|
||||
INSERT INTO ft VALUES('four five six iv v vi');
|
||||
INSERT INTO ft VALUES('eight nine ten viii ix x');
|
||||
} {}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
SELECT fts5_get_locale(ft, 0) FROM ft WHERE x MATCH 'one AND two AND three'
|
||||
} {hello}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 10.1 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT fts5_get_locale(ft, 0) FROM ft WHERE x MATCH 'one AND two AND three'
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 hello}
|
||||
}
|
||||
|
||||
breakpoint
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 10.2 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO ft VALUES(zeroblob(10000));
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 11.0 {
|
||||
CREATE VIRTUAL TABLE f1 USING fts5(content);
|
||||
CREATE TABLE g1(id, content);
|
||||
INSERT INTO g1 VALUES(30000, 'a b c');
|
||||
INSERT INTO g1 VALUES(40000, 'd e f');
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 11 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO f1(rowid, content) SELECT id, content FROM g1;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -22,6 +22,7 @@ do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a, b);
|
||||
}
|
||||
|
||||
unset -nocomplain res
|
||||
foreach {tn expr ok} {
|
||||
1 {^abc} 1
|
||||
2 {^abc + def} 1
|
||||
|
||||
@@ -153,6 +153,7 @@ do_execsql_test 5.3 {
|
||||
INSERT INTO gg(gg) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
unset -nocomplain res
|
||||
do_test 5.4.1 {
|
||||
set ok 0
|
||||
for {set i 0} {$i < 10000} {incr i} {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# 2024 September 3
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests focused on the integrity-check procedure.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5integrity2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, detail='none');
|
||||
BEGIN;
|
||||
INSERT INTO t2(rowid, a) VALUES(-1, 'hello world');
|
||||
INSERT INTO t2(rowid, a) VALUES(9223372036854775807, 'hello world');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT rowid FROM t2('hello AND world');
|
||||
} {-1 9223372036854775807}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail='none');
|
||||
CREATE TABLE r1(r);
|
||||
|
||||
WITH c(x) AS (VALUES(1) UNION SELECT x<<1 FROM c)
|
||||
INSERT INTO r1(r) SELECT -1-x FROM c;
|
||||
|
||||
INSERT INTO t1(rowid, a) SELECT r, 'abc' FROM r1;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT rowid FROM t1('abc') ORDER BY +rowid;
|
||||
} [db eval {SELECT r FROM r1 ORDER BY r}]
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -33,6 +33,7 @@ proc progress_handler {args} {
|
||||
return 0
|
||||
}
|
||||
|
||||
unset -nocomplain res
|
||||
foreach {tn sql} {
|
||||
1 { INSERT INTO t1(rowid, a) VALUES(0, 'z z z z') }
|
||||
2 { COMMIT }
|
||||
@@ -64,4 +65,3 @@ foreach {tn sql} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,748 @@
|
||||
# 2014 Dec 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests focusing on the built-in fts5 tokenizers.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5locale
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc transform_token {locale token} {
|
||||
switch -- $locale {
|
||||
reverse {
|
||||
set ret ""
|
||||
foreach c [split $token ""] {
|
||||
set ret "$c$ret"
|
||||
}
|
||||
set token $ret
|
||||
}
|
||||
|
||||
default {
|
||||
# no-op
|
||||
}
|
||||
}
|
||||
|
||||
set token
|
||||
}
|
||||
|
||||
proc tcl_create {args} { return "tcl_tokenize" }
|
||||
proc tcl_tokenize {tflags text} {
|
||||
set iToken 1
|
||||
set bSkip 0
|
||||
if {[sqlite3_fts5_locale]=="second"} { set bSkip 1 }
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
incr iToken
|
||||
if {(($iToken) % ($bSkip + 1))} continue
|
||||
|
||||
set w [transform_token [sqlite3_fts5_locale] $w]
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that queries can have a locale attached to them.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=tcl);
|
||||
INSERT INTO t1 VALUES('abc');
|
||||
INSERT INTO t1 VALUES('cba');
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT rowid, a FROM t1( fts5_locale('en_US', 'abc') );
|
||||
} {1 abc}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT rowid, a FROM t1( fts5_locale('reverse', 'abc') );
|
||||
} {2 cba}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the locale= option exists and seems to accept values. And
|
||||
# that fts5_locale() values may only be inserted into an internal-content
|
||||
# table if the locale=1 option was specified.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE b1 USING fts5(x, y, locale=1, tokenize=tcl);
|
||||
CREATE VIRTUAL TABLE b2 USING fts5(x, y, locale=0, tokenize=tcl);
|
||||
|
||||
CREATE VIRTUAL TABLE ttt USING fts5vocab('b1', instance);
|
||||
}
|
||||
|
||||
do_catchsql_test 2.2.1 {
|
||||
CREATE VIRTUAL TABLE b3 USING fts5(x, y, locale=2);
|
||||
} {1 {malformed locale=... directive}}
|
||||
do_catchsql_test 2.2.2 {
|
||||
CREATE VIRTUAL TABLE b3 USING fts5(x, y, locale=111);
|
||||
} {1 {malformed locale=... directive}}
|
||||
|
||||
do_catchsql_test 2.3 {
|
||||
INSERT INTO b1(b1, rank) VALUES('locale', 0);
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
do_execsql_test 2.4.1 {
|
||||
INSERT INTO b1 VALUES('abc', 'one two three');
|
||||
}
|
||||
|
||||
do_execsql_test 2.4.2 {
|
||||
INSERT INTO b1 VALUES('def', fts5_locale('reverse', 'four five six'));
|
||||
}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
INSERT INTO b2 VALUES('abc', 'one two three');
|
||||
}
|
||||
|
||||
do_catchsql_test 2.6 {
|
||||
INSERT INTO b2 VALUES('def', fts5_locale('reverse', 'four five six'));
|
||||
} {1 {fts5_locale() requires locale=1}}
|
||||
|
||||
do_execsql_test 2.7 { SELECT rowid FROM b1('one') } {1}
|
||||
do_execsql_test 2.8 { SELECT rowid FROM b1('four') } {}
|
||||
do_execsql_test 2.9 { SELECT rowid FROM b1('ruof') } 2
|
||||
do_execsql_test 2.10 { SELECT rowid FROM b1(fts5_locale('reverse', 'five'))} 2
|
||||
|
||||
do_execsql_test 2.11 {
|
||||
SELECT x, quote(y) FROM b1
|
||||
} {
|
||||
abc {'one two three'}
|
||||
def {'four five six'}
|
||||
}
|
||||
|
||||
do_execsql_test 2.12 { SELECT quote(y) FROM b1('ruof') } {
|
||||
{'four five six'}
|
||||
}
|
||||
|
||||
do_execsql_test 2.13 {
|
||||
INSERT INTO b1(b1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 2.14 {
|
||||
INSERT INTO b1(b1) VALUES('rebuild');
|
||||
}
|
||||
do_execsql_test 2.15 {
|
||||
INSERT INTO b1(b1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 2.16 {
|
||||
DELETE FROM b1 WHERE rowid=2
|
||||
}
|
||||
do_execsql_test 2.17 {
|
||||
INSERT INTO b1(b1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 2.18 {
|
||||
INSERT INTO b1(rowid, x, y) VALUES(
|
||||
test_setsubtype(45, 76), 'abc def', 'def abc'
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the 'delete' command with contentless tables.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE c1 USING fts5(x, content=, tokenize=tcl, locale=1);
|
||||
CREATE VIRTUAL TABLE c2 USING fts5vocab('c1', instance);
|
||||
|
||||
INSERT INTO c1 VALUES('hello world');
|
||||
INSERT INTO c1 VALUES( fts5_locale('reverse', 'one two three') );
|
||||
}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT DISTINCT term FROM c2 ORDER BY 1
|
||||
} {
|
||||
eerht eno hello owt world
|
||||
}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO c1(c1, rowid, x)
|
||||
VALUES('delete', 2, fts5_locale('reverse', 'one two three') );
|
||||
}
|
||||
|
||||
do_execsql_test 3.4 {
|
||||
SELECT DISTINCT term FROM c2 ORDER BY 1
|
||||
} {
|
||||
hello world
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that an UPDATE that updates a subset of the columns does not
|
||||
# magically discard the locale from those columns not updated.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
CREATE VIRTUAL TABLE d1 USING fts5(x, y, locale=1, tokenize=tcl);
|
||||
CREATE VIRTUAL TABLE d2 USING fts5vocab('d1', instance);
|
||||
|
||||
INSERT INTO d1(rowid, x, y) VALUES(1, 'abc', 'def');
|
||||
INSERT INTO d1(rowid, x, y) VALUES(2, 'ghi', fts5_locale('reverse', 'hello'));
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
SELECT DISTINCT term FROM d2 ORDER BY 1
|
||||
} {
|
||||
abc def ghi olleh
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
UPDATE d1 SET x='jkl' WHERE rowid=2;
|
||||
}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
SELECT DISTINCT term FROM d2 ORDER BY 1
|
||||
} {
|
||||
abc def jkl olleh
|
||||
}
|
||||
|
||||
do_execsql_test 4.5 {
|
||||
SELECT rowid, * FROM d1
|
||||
} {
|
||||
1 abc def
|
||||
2 jkl hello
|
||||
}
|
||||
|
||||
do_execsql_test 4.6 {
|
||||
UPDATE d1 SET rowid=4 WHERE rowid=2
|
||||
}
|
||||
|
||||
do_execsql_test 4.7 {
|
||||
SELECT rowid, * FROM d1
|
||||
} {
|
||||
1 abc def
|
||||
4 jkl hello
|
||||
}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 4.8.1 {
|
||||
SELECT fts5_test_columntext(d1) FROM d1('jkl')
|
||||
} {{jkl hello}}
|
||||
do_execsql_test 4.8.2 {
|
||||
SELECT fts5_test_columntext(d1) FROM d1(fts5_locale('reverse', 'hello'))
|
||||
} {{jkl hello}}
|
||||
|
||||
do_execsql_test 4.9 {
|
||||
SELECT fts5_test_columnlocale(d1) FROM d1(fts5_locale('reverse', 'hello'))
|
||||
} {{{} reverse}}
|
||||
|
||||
do_execsql_test 4.10 {
|
||||
SELECT fts5_test_columnlocale(d1) FROM d1
|
||||
} {
|
||||
{{} {}}
|
||||
{{} reverse}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that if an fts5_locale() value is written to an UNINDEXED
|
||||
# column it is stored as text. This is so that blobs and other values
|
||||
# can also be stored as is.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(
|
||||
x, y UNINDEXED, locale=1, tokenize=tcl
|
||||
);
|
||||
|
||||
INSERT INTO t1(rowid, x, y) VALUES(111,
|
||||
fts5_locale('reverse', 'one two three'),
|
||||
fts5_locale('reverse', 'four five six')
|
||||
);
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT rowid, x, y FROM t1
|
||||
} {
|
||||
111 {one two three} {four five six}
|
||||
}
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
SELECT typeof(c0), typeof(c1), typeof(l0) FROM t1_content
|
||||
} {
|
||||
text text text
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn opt} {
|
||||
1 {}
|
||||
2 {, columnsize=0}
|
||||
} {
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
do_execsql_test 6.$tn.1 "
|
||||
CREATE VIRTUAL TABLE y1 USING fts5(t, locale=1, tokenize=tcl $opt);
|
||||
"
|
||||
|
||||
do_execsql_test 6.$tn.2 {
|
||||
INSERT INTO y1(rowid, t) VALUES
|
||||
(1, fts5_locale('second', 'the city of London')),
|
||||
(2, fts5_locale('second', 'shall have all the old')),
|
||||
(3, fts5_locale('second', 'Liberties and Customs')),
|
||||
(4, fts5_locale('second', 'which it hath been used to have'));
|
||||
}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 6.$tn.3 {
|
||||
SELECT fts5_test_columnsize(y1) FROM y1
|
||||
} {
|
||||
2 3 2 4
|
||||
}
|
||||
|
||||
do_execsql_test 6.$tn.4 {
|
||||
SELECT rowid, fts5_test_columnsize(y1) FROM y1('shall');
|
||||
} {
|
||||
2 3
|
||||
}
|
||||
|
||||
do_execsql_test 6.$tn.5 {
|
||||
SELECT rowid, fts5_test_columnsize(y1) FROM y1('shall');
|
||||
} {
|
||||
2 3
|
||||
}
|
||||
|
||||
do_execsql_test 6.$tn.6 {
|
||||
SELECT rowid, fts5_test_columnsize(y1) FROM y1('have');
|
||||
} {
|
||||
4 4
|
||||
}
|
||||
|
||||
do_execsql_test 6.$tn.7 {
|
||||
SELECT rowid, highlight(y1, 0, '[', ']') FROM y1('have');
|
||||
} {
|
||||
4 {which it hath been used to [have]}
|
||||
}
|
||||
|
||||
do_execsql_test 6.$tn.8 {
|
||||
SELECT rowid,
|
||||
highlight(y1, 0, '[', ']'),
|
||||
snippet(y1, 0, '[', ']', '...', 10)
|
||||
FROM y1('Liberties + Customs');
|
||||
} {
|
||||
3 {[Liberties and Customs]}
|
||||
{[Liberties and Customs]}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
}
|
||||
do_catchsql_test 6.1 {
|
||||
INSERT INTO x1(rowid, x) VALUES(123, fts5_locale('en_AU', 'hello world'));
|
||||
} {1 {fts5_locale() requires locale=1}}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
SELECT typeof( fts5_locale(NULL, 'xyz') ), typeof( fts5_locale('', 'abc') );
|
||||
} {text text}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test that fts5_locale() works with external-content tables.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE t1(ii INTEGER PRIMARY KEY, bb BLOB, tt TEXT, locale TEXT);
|
||||
CREATE VIEW v1 AS
|
||||
SELECT ii AS rowid, bb, fts5_locale(locale, tt) AS tt FROM t1;
|
||||
|
||||
CREATE VIRTUAL TABLE ft USING fts5(
|
||||
bb, tt, locale=1, tokenize=tcl, content=v1
|
||||
);
|
||||
|
||||
INSERT INTO t1 VALUES(1, NULL, 'one two three', NULL);
|
||||
INSERT INTO t1 VALUES(2, '7800616263', 'four five six', 'reverse');
|
||||
INSERT INTO t1 VALUES(3, '000000007800616263', 'seven eight nine', 'second');
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
INSERT INTO ft(ft) VALUES('rebuild');
|
||||
INSERT INTO ft(ft) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
SELECT rowid, quote(bb), quote(tt) FROM ft
|
||||
} {
|
||||
1 NULL {'one two three'}
|
||||
2 '7800616263' {'four five six'}
|
||||
3 '000000007800616263' {'seven eight nine'}
|
||||
}
|
||||
|
||||
do_execsql_test 7.4 { SELECT rowid FROM ft('six'); }
|
||||
do_execsql_test 7.5 { SELECT rowid FROM ft(fts5_locale('reverse','six')); } 2
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 7.6 {
|
||||
SELECT fts5_test_columnlocale(ft) FROM ft;
|
||||
} {
|
||||
{{} {}} {{} reverse} {{} second}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the porter tokenizer works with locales.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(tt, locale=1, tokenize="porter tcl");
|
||||
CREATE VIRTUAL TABLE vocab USING fts5vocab('ft', instance);
|
||||
|
||||
INSERT INTO ft(rowid, tt) VALUES
|
||||
(111, fts5_locale('second', 'the porter tokenizer is a wrapper tokenizer')),
|
||||
(222, fts5_locale('reverse', 'This value may also be set'));
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
SELECT DISTINCT term FROM vocab ORDER BY 1
|
||||
} {
|
||||
a eb eulav osla sihT te the token yam
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that position-lists (used by xInst, xPhraseFirst etc.) work with
|
||||
# locales and modes other than detail=full.
|
||||
#
|
||||
foreach {tn detail} {
|
||||
1 detail=full
|
||||
2 detail=none
|
||||
3 detail=column
|
||||
} {
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
do_execsql_test 9.$tn.0 "
|
||||
CREATE VIRTUAL TABLE ft USING fts5(tt, locale=1, tokenize=tcl, $detail);
|
||||
"
|
||||
do_execsql_test 9.$tn.1 {
|
||||
CREATE VIRTUAL TABLE vocab USING fts5vocab('ft', instance);
|
||||
INSERT INTO ft(rowid, tt) VALUES
|
||||
(-1, fts5_locale('second', 'it is an ancient mariner'));
|
||||
}
|
||||
|
||||
do_execsql_test 9.$tn.2 {
|
||||
SELECT DISTINCT term FROM vocab
|
||||
} {an it mariner}
|
||||
|
||||
do_execsql_test 9.$tn.3 {
|
||||
SELECT highlight(ft, 0, '[', ']') FROM ft('mariner')
|
||||
} {{it is an ancient [mariner]}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check some corrupt fts5_locale() blob formats are detected.
|
||||
#
|
||||
foreach_detail_mode $::testprefix {
|
||||
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 10.1 {
|
||||
CREATE TABLE x1(ii INTEGER PRIMARY KEY, x);
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x,
|
||||
content=x1, content_rowid=ii, locale=1, detail=%DETAIL%, columnsize=0
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(
|
||||
x, locale=1, detail=%DETAIL%, columnsize=0
|
||||
);
|
||||
}
|
||||
|
||||
foreach {tn v} {
|
||||
1 X'001152'
|
||||
2 X'0011223344'
|
||||
3 X'00E0B2EB68656c6c6f'
|
||||
4 X'00E0B2EB0068656c6c6f'
|
||||
} {
|
||||
do_execsql_test 10.2.$tn.0 { INSERT INTO ft(ft) VALUES('delete-all') }
|
||||
do_execsql_test 10.2.$tn.1 { DELETE FROM x1; }
|
||||
do_execsql_test 10.2.$tn.2 " INSERT INTO x1 VALUES(NULL, $v) "
|
||||
|
||||
do_catchsql_test 10.2.$tn.3 {
|
||||
INSERT INTO ft(ft) VALUES('rebuild');
|
||||
} {0 {}}
|
||||
|
||||
do_catchsql_test 10.2.$tn.4 "
|
||||
SELECT * FROM ft( test_setsubtype($v, 76) );
|
||||
" {1 {fts5: syntax error near ""}}
|
||||
|
||||
do_execsql_test 10.2.$tn.5 {
|
||||
INSERT INTO ft(rowid, x) VALUES(1, 'hello world');
|
||||
}
|
||||
|
||||
if {"%DETAIL%"=="full"} {
|
||||
do_execsql_test 10.2.$tn.6 {
|
||||
SELECT fts5_test_poslist(ft) FROM ft('world');
|
||||
} {0.0.1}
|
||||
|
||||
do_execsql_test 10.2.$tn.7.1 {
|
||||
SELECT fts5_test_columnsize(ft) FROM ft('world');
|
||||
} {1}
|
||||
|
||||
do_execsql_test 10.2.$tn.7.2 {
|
||||
SELECT fts5_test_columnlocale(ft) FROM ft('world');
|
||||
} {{{}}}
|
||||
}
|
||||
|
||||
do_catchsql_test 10.2.$tn.8 {
|
||||
SELECT count(*) FROM ft('hello')
|
||||
} {0 1}
|
||||
|
||||
do_catchsql_test 10.2.$tn.9 {
|
||||
PRAGMA integrity_check;
|
||||
} {0 ok}
|
||||
|
||||
do_execsql_test 10.2.$tn.10 {
|
||||
DELETE FROM x1;
|
||||
INSERT INTO x1(ii, x) VALUES(1, 'hello world');
|
||||
}
|
||||
|
||||
do_catchsql_test 10.2.$tn.11 "
|
||||
INSERT INTO ft(ft, rowid, x) VALUES('delete', 1, test_setsubtype($v,76) )
|
||||
" {0 {}}
|
||||
|
||||
do_catchsql_test 10.2.$tn.12 "
|
||||
INSERT INTO ft(rowid, x) VALUES(2, test_setsubtype($v,76) )
|
||||
" {0 {}}
|
||||
|
||||
do_execsql_test 10.2.$tn.13 {
|
||||
INSERT INTO ft2(rowid, x) VALUES(1, 'hello world');
|
||||
}
|
||||
do_execsql_test 10.2.$tn.14 "UPDATE ft2_content SET c0=$v"
|
||||
|
||||
do_catchsql_test 10.2.$tn.15 {
|
||||
PRAGMA integrity_check;
|
||||
} {0 {{malformed inverted index for FTS5 table main.ft2}}}
|
||||
|
||||
do_execsql_test 10.2.$tn.16 {
|
||||
DELETE FROM ft2_content;
|
||||
INSERT INTO ft2(ft2) VALUES('rebuild');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 11.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(abc, locale=1);
|
||||
INSERT INTO x1(rowid, abc) VALUES(123, fts5_locale('en_US', 'one two three'));
|
||||
}
|
||||
|
||||
do_catchsql_test 11.1 {
|
||||
SELECT fts5_columnlocale(x1, -1) FROM x1('two');
|
||||
} {1 SQLITE_RANGE}
|
||||
do_catchsql_test 11.2 {
|
||||
SELECT fts5_columnlocale(x1, 1) FROM x1('two');
|
||||
} {1 SQLITE_RANGE}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_test 12.0 {
|
||||
list [catch {
|
||||
sqlite3_fts5_create_tokenizer -v2 -version 3 db tcl tcl_create
|
||||
} msg] $msg
|
||||
} {1 {error in fts5_api.xCreateTokenizer_v2()}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests for auxiliary function fts5_get_locale().
|
||||
#
|
||||
reset_db
|
||||
|
||||
# Check that if the table does not support locale=1, fts5_get_locale()
|
||||
# always returns NULL.
|
||||
do_execsql_test 13.1.0 {
|
||||
CREATE VIRTUAL TABLE nolocale USING fts5(a, b);
|
||||
INSERT INTO nolocale VALUES('one two three', 'four five six');
|
||||
INSERT INTO nolocale VALUES('three two one', 'seven eight nine');
|
||||
}
|
||||
do_execsql_test 13.1.1 {
|
||||
SELECT fts5_get_locale(nolocale, 0) IS NULL FROM nolocale;
|
||||
} {1 1}
|
||||
do_execsql_test 13.1.2 {
|
||||
SELECT fts5_get_locale(nolocale, 1) IS NULL FROM nolocale('one + two');
|
||||
} {1}
|
||||
do_execsql_test 13.1.3 {
|
||||
SELECT fts5_get_locale(nolocale, 0) IS NULL FROM nolocale('one AND two');
|
||||
} {1 1}
|
||||
do_execsql_test 13.1.4 {
|
||||
SELECT
|
||||
fts5_get_locale(nolocale, 1) IS NULL
|
||||
FROM nolocale('three AND two') ORDER BY rank
|
||||
} {1 1}
|
||||
do_catchsql_test 13.1.5 {
|
||||
SELECT fts5_get_locale(nolocale, 2) IS NULL FROM nolocale('three AND two');
|
||||
} {1 {column index out of range}}
|
||||
do_catchsql_test 13.1.6 {
|
||||
SELECT fts5_get_locale(nolocale, -1) IS NULL FROM nolocale('three AND two');
|
||||
} {1 {column index out of range}}
|
||||
do_catchsql_test 13.1.7 {
|
||||
SELECT fts5_get_locale(nolocale) IS NULL FROM nolocale('three AND two');
|
||||
} {1 {wrong number of arguments to function fts5_get_locale()}}
|
||||
do_catchsql_test 13.1.8 {
|
||||
SELECT fts5_get_locale(nolocale, 0, 0) IS NULL FROM nolocale('three AND two');
|
||||
} {1 {wrong number of arguments to function fts5_get_locale()}}
|
||||
do_catchsql_test 13.1.9 {
|
||||
SELECT fts5_get_locale(nolocale, 'text') FROM nolocale('three AND two');
|
||||
} {1 {non-integer argument passed to function fts5_get_locale()}}
|
||||
|
||||
|
||||
# Check that if the table does support locale=1, fts5_get_locale()
|
||||
# returns the locale of the identified row/column.
|
||||
do_execsql_test 13.2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, b, locale=1);
|
||||
INSERT INTO ft VALUES(
|
||||
fts5_locale('th_TH', 'one two three'), 'four five six seven'
|
||||
);
|
||||
INSERT INTO ft VALUES(
|
||||
'three two one', fts5_locale('en_AU', 'seven eight nine')
|
||||
);
|
||||
}
|
||||
|
||||
do_execsql_test 13.2.1 {
|
||||
SELECT quote(fts5_get_locale(ft, 0)), quote(fts5_get_locale(ft, 1)) FROM ft
|
||||
} { 'th_TH' NULL NULL 'en_AU' }
|
||||
do_execsql_test 13.2.2 {
|
||||
SELECT
|
||||
quote(fts5_get_locale(ft, 0)), quote(fts5_get_locale(ft, 1))
|
||||
FROM ft('one AND three')
|
||||
} { 'th_TH' NULL NULL 'en_AU' }
|
||||
do_execsql_test 13.2.3 {
|
||||
SELECT
|
||||
quote(fts5_get_locale(ft, 0)), quote(fts5_get_locale(ft, 1))
|
||||
FROM ft('one AND three') ORDER BY rank
|
||||
} { NULL 'en_AU' 'th_TH' NULL }
|
||||
do_execsql_test 13.2.4 {
|
||||
SELECT
|
||||
quote(fts5_get_locale(ft, 0)), quote(fts5_get_locale(ft, 1))
|
||||
FROM ft('one AND three') ORDER BY rowid
|
||||
} { 'th_TH' NULL NULL 'en_AU' }
|
||||
|
||||
do_execsql_test 13.2.5 {
|
||||
SELECT
|
||||
quote(fts5_get_locale(ft, '0')), quote(fts5_get_locale(ft, 1))
|
||||
FROM ft('one AND three') ORDER BY rowid
|
||||
} { 'th_TH' NULL NULL 'en_AU' }
|
||||
|
||||
do_catchsql_test 13.2.6 {
|
||||
SELECT
|
||||
quote(fts5_get_locale(ft, '0.0')), quote(fts5_get_locale(ft, 1))
|
||||
FROM ft('one AND three') ORDER BY rowid
|
||||
} {1 {non-integer argument passed to function fts5_get_locale()}}
|
||||
do_catchsql_test 13.2.7 {
|
||||
SELECT
|
||||
quote(fts5_get_locale(ft, 0.0)), quote(fts5_get_locale(ft, 1))
|
||||
FROM ft('one AND three') ORDER BY rowid
|
||||
} {1 {non-integer argument passed to function fts5_get_locale()}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that UPDATE statements that may affect more than one row work.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 14.1 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, b, locale=1);
|
||||
}
|
||||
|
||||
do_execsql_test 14.2 {
|
||||
INSERT INTO ft VALUES('hello', 'world');
|
||||
}
|
||||
|
||||
do_execsql_test 14.3 {
|
||||
UPDATE ft SET b = fts5_locale('en_AU', 'world');
|
||||
}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
INSERT INTO ft VALUES(X'abcd', X'1234');
|
||||
} {}
|
||||
|
||||
do_execsql_test 14.5 {
|
||||
SELECT quote(a), quote(b) FROM ft
|
||||
} {'hello' 'world' X'ABCD' X'1234'}
|
||||
|
||||
do_execsql_test 14.6 {
|
||||
DELETE FROM ft;
|
||||
INSERT INTO ft VALUES(NULL, 'null');
|
||||
INSERT INTO ft VALUES(123, 'int');
|
||||
INSERT INTO ft VALUES(345.0, 'real');
|
||||
INSERT INTO ft VALUES('abc', 'text');
|
||||
INSERT INTO ft VALUES(fts5_locale('abc', 'def'), 'text');
|
||||
|
||||
SELECT a, typeof(a), b FROM ft
|
||||
} {
|
||||
{} null null
|
||||
123 integer int
|
||||
345.0 real real
|
||||
abc text text
|
||||
def text text
|
||||
}
|
||||
|
||||
do_execsql_test 14.7 {
|
||||
SELECT quote(c0), typeof(c0) FROM ft_content
|
||||
} {
|
||||
NULL null
|
||||
123 integer
|
||||
345.0 real
|
||||
'abc' text
|
||||
'def' text
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that inserting UNINDEXED columns between indexed columns of a
|
||||
# locale=1 table does not cause a problem.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer -v2 db tcl tcl_create
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 15.1 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, b UNINDEXED, c, locale=1, tokenize=tcl);
|
||||
}
|
||||
|
||||
do_execsql_test 15.2 {
|
||||
INSERT INTO ft VALUES('one', 'two', 'three');
|
||||
INSERT INTO ft VALUES('one', 'two', fts5_locale('loc', 'three'));
|
||||
}
|
||||
|
||||
do_execsql_test 15.3 {
|
||||
SELECT c2, l2 FROM ft_content
|
||||
} {three {} three loc}
|
||||
|
||||
do_execsql_test 15.4 {
|
||||
SELECT c, fts5_columnlocale(ft, 2) FROM ft
|
||||
} {three {} three loc}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+104
-5
@@ -35,21 +35,21 @@ do_catchsql_test 1.1.2 {
|
||||
|
||||
do_catchsql_test 1.2.1 {
|
||||
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*id');
|
||||
} {0 {{}}}
|
||||
} {1 {no such cursor: 4}}
|
||||
|
||||
do_catchsql_test 1.2.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*id'));
|
||||
} {0 {}}
|
||||
} {1 {no such cursor: 6}}
|
||||
|
||||
do_catchsql_test 1.3.1 {
|
||||
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads');
|
||||
} {1 {no such cursor: 2}}
|
||||
} {1 {no such cursor: 0}}
|
||||
|
||||
do_catchsql_test 1.3.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
|
||||
} {1 {no such cursor: 2}}
|
||||
} {1 {no such cursor: 0}}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
@@ -57,7 +57,12 @@ sqlite3 db test.db
|
||||
do_catchsql_test 1.3.3 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
|
||||
} {1 {no such cursor: 1}}
|
||||
} {1 {no such cursor: 0}}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_catchsql_test 1.3.4 {
|
||||
SELECT fts5_columntext(t1) FROM t1('*reads');
|
||||
} {1 {no such cursor: 0}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -566,5 +571,99 @@ do_execsql_test 20.5 {
|
||||
SELECT rowid FROM x1 WHERE x1 MATCH 'z' OR (x1 MATCH 'a' AND x1 MATCH 'd');
|
||||
} {3 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 21.0 {
|
||||
CREATE TABLE t1(ii INTEGER, x TEXT, y TEXT);
|
||||
CREATE VIRTUAL TABLE xyz USING fts5(content_rowid=ii, content=t1, x, y);
|
||||
INSERT INTO t1 VALUES(1, 'one', 'i');
|
||||
INSERT INTO t1 VALUES(2, 'two', 'ii');
|
||||
INSERT INTO t1 VALUES(3, 'tree', 'iii');
|
||||
INSERT INTO xyz(xyz) VALUES('rebuild');
|
||||
}
|
||||
|
||||
do_execsql_test 21.1 {
|
||||
UPDATE xyz SET y='TWO' WHERE rowid=2;
|
||||
UPDATE t1 SET y='TWO' WHERE ii=2;
|
||||
}
|
||||
|
||||
do_execsql_test 21.2 {
|
||||
PRAGMA integrity_check
|
||||
} {ok}
|
||||
|
||||
breakpoint
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
do_execsql_test 21.3 {
|
||||
CREATE TABLE xyz_notashadow(x, y);
|
||||
DROP TABLE xyz_notashadow;
|
||||
}
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
SELECT fts5(NULL);
|
||||
} {{}}
|
||||
do_execsql_test 22.1 {
|
||||
SELECT count(*) FROM (
|
||||
SELECT fts5_source_id()
|
||||
)
|
||||
} {1}
|
||||
execsql_pp {
|
||||
SELECT fts5_source_id()
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 23.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
INSERT INTO x1 VALUES('one + two + three');
|
||||
INSERT INTO x1 VALUES('one + xyz + three');
|
||||
INSERT INTO x1 VALUES('xyz + two + xyz');
|
||||
}
|
||||
do_execsql_test 23.1 {
|
||||
SELECT rowid FROM x1('one + two + three');
|
||||
} {1}
|
||||
|
||||
do_execsql_test 23.2 {
|
||||
SELECT rowid FROM x1('^".." AND one');
|
||||
} {}
|
||||
|
||||
do_execsql_test 23.3 {
|
||||
SELECT rowid FROM x1('abc NEAR ".." NEAR def');
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 24.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail='none');
|
||||
INSERT INTO t1(a) VALUES('a');
|
||||
}
|
||||
|
||||
do_execsql_test 24.2 {
|
||||
SELECT rank FROM ( SELECT rank FROM t1('a NOT "" NOT def') ) ORDER BY 1;
|
||||
} {-1e-06}
|
||||
|
||||
do_execsql_test 24.3 {
|
||||
SELECT rank FROM ( SELECT rank FROM t1('a NOT � NOT def') ) ORDER BY 1;
|
||||
} {-1e-06}
|
||||
|
||||
do_execsql_test 24.4 {
|
||||
SELECT rank FROM ( SELECT rank FROM t1('a NOT "" NOT def') );
|
||||
} {-1e-06}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 25.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail='none', content='');
|
||||
INSERT INTO t1(a) VALUES('a b c');
|
||||
}
|
||||
|
||||
do_execsql_test 25.0 {
|
||||
SELECT fts5_test_poslist(t1) FROM t1('b') ORDER BY rank;
|
||||
} {{}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -261,8 +261,8 @@ do_execsql_test 5.3 {
|
||||
reset_db
|
||||
sqlite3_fts5_register_origintext db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(
|
||||
x, y, tokenize='origintext unicode61', detail=%DETAIL%
|
||||
CREATE VIRTUAL TABLE ft USING fts5(
|
||||
x, y, tokenize='origintext unicode61', detail=%DETAIL%, tokendata=0
|
||||
);
|
||||
|
||||
INSERT INTO ft VALUES('One Two', 'Three two');
|
||||
@@ -291,6 +291,22 @@ do_execsql_test 6.3 {
|
||||
SELECT rowid, tokens(ft) FROM ft('Three*');
|
||||
} {1 {{}} 2 {{}}}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_catchsql_test 6.4 {
|
||||
SELECT fts5_test_insttoken(ft, -1, 0) FROM ft('one');
|
||||
} {1 SQLITE_RANGE}
|
||||
|
||||
do_catchsql_test 6.5 {
|
||||
SELECT fts5_test_insttoken(ft, 1, 0) FROM ft('one');
|
||||
} {1 SQLITE_RANGE}
|
||||
|
||||
do_catchsql_test 6.6 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(x, tokendata=2);
|
||||
} {1 {malformed tokendata=... directive}}
|
||||
do_catchsql_test 6.7 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(x, content='', tokendata=11);
|
||||
} {1 {malformed tokendata=... directive}}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -93,15 +93,21 @@ foreach {tn cols tokens} {
|
||||
10 {b} "i e"
|
||||
11 {a} "i e"
|
||||
} {
|
||||
set fts "{$cols}:[join $tokens +]"
|
||||
set where [list]
|
||||
foreach c $cols { lappend where "pmatch($c, '$tokens')" }
|
||||
set where [join $where " OR "]
|
||||
|
||||
set res [db eval "SELECT rowid FROM t3 WHERE $where"]
|
||||
do_execsql_test "1.$tn.$fts->([llength $res] rows)" {
|
||||
SELECT rowid FROM t3($fts)
|
||||
} $res
|
||||
foreach fts [list \
|
||||
"{$cols}:[join $tokens +]" \
|
||||
"{$cols}:NEAR([join $tokens +])" \
|
||||
"{$cols}:NEAR([join $tokens +],1)" \
|
||||
"{$cols}:NEAR([join $tokens +],111)" \
|
||||
] {
|
||||
set res [db eval "SELECT rowid FROM t3 WHERE $where"]
|
||||
do_execsql_test "1.$tn.$fts->([llength $res] rows)" {
|
||||
SELECT rowid FROM t3($fts)
|
||||
} $res
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
|
||||
@@ -29,6 +29,7 @@ do_execsql_test 1.0 {
|
||||
# Run the 'optimize' command. Check that it does not disturb ongoing
|
||||
# full-text queries.
|
||||
#
|
||||
unset -nocomplain lRowid
|
||||
do_test 1.1 {
|
||||
for {set i 1} {$i < 1000} {incr i} {
|
||||
execsql { INSERT INTO f1 VALUES('a b c d e') }
|
||||
|
||||
@@ -58,6 +58,10 @@ do_execsql_test 2.1 {
|
||||
pragma quick_check;
|
||||
} {ok}
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
INSERT INTO xyz(xyz, rank) VALUES('secure-delete', 'hello world');
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ do_execsql_test 14.3 {
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'
|
||||
} {0 {} 3}
|
||||
} {0 {} 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -480,4 +480,33 @@ do_execsql_test 22.0 {
|
||||
do_catchsql_test 22.1 {SELECT * FROM x1('')} {1 {fts5: syntax error near ""}}
|
||||
do_catchsql_test 22.2 {SELECT * FROM x1(NULL)} {1 {fts5: syntax error near ""}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 23.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
SELECT count(*) FROM x1_data;
|
||||
} {2}
|
||||
|
||||
do_execsql_test 23.1 {
|
||||
BEGIN;
|
||||
INSERT INTO x1 VALUES('a b c d');
|
||||
INSERT INTO x1 VALUES('a b c d');
|
||||
INSERT INTO x1 VALUES('a b c d');
|
||||
}
|
||||
|
||||
do_execsql_test 23.2 {
|
||||
SELECT count(*) FROM x1_data;
|
||||
} {2}
|
||||
|
||||
do_execsql_test 23.3 {
|
||||
INSERT INTO x1(x1) VALUES('flush');
|
||||
SELECT count(*) FROM x1_data;
|
||||
} {3}
|
||||
|
||||
do_execsql_test 23.4 {
|
||||
ROLLBACK;
|
||||
SELECT count(*) FROM x1_data;
|
||||
} {2}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -85,5 +85,25 @@ do_execsql_test 1.7 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('BB mess');
|
||||
} {AAdont>BBmess<}
|
||||
|
||||
# 2024-08-06 https://sqlite.org/forum/forumpost/171bcc2bcd
|
||||
# Error handling of tokenize= arguments.
|
||||
#
|
||||
foreach {n tkz} {
|
||||
1 {ascii none}
|
||||
2 {unicode61 none}
|
||||
3 {porter none}
|
||||
4 {trigram none}
|
||||
5 {ascii none 0}
|
||||
6 {unicode61 none 0}
|
||||
7 {porter none 0}
|
||||
8 {trigram none 0}
|
||||
} {
|
||||
db eval {DROP TABLE IF EXISTS t2;}
|
||||
do_catchsql_test 2.$n "
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a,b,c,tokenize='$tkz');
|
||||
" {1 {error in tokenizer constructor}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# 2024 Aug 10
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests focusing on the built-in fts5 tokenizers.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5tokenizer3
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
proc get_sod {args} { return "split_on_dot" }
|
||||
proc get_lowercase {args} { return "lowercase" }
|
||||
|
||||
proc lowercase {flags txt} {
|
||||
set n [string length $txt]
|
||||
sqlite3_fts5_token [string tolower $txt] 0 $n
|
||||
return 0
|
||||
}
|
||||
|
||||
proc split_on_dot {flags txt} {
|
||||
set iOff 0
|
||||
foreach t [split $txt "."] {
|
||||
set n [string length $txt]
|
||||
sqlite3_fts5_token $t $iOff [expr $iOff+$n]
|
||||
incr iOff [expr {$n+1}]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
foreach {tn script} {
|
||||
1 {
|
||||
sqlite3_fts5_create_tokenizer db lowercase get_lowercase
|
||||
sqlite3_fts5_create_tokenizer -parent lowercase db split_on_dot get_sod
|
||||
}
|
||||
2 {
|
||||
sqlite3_fts5_create_tokenizer -v2 db lowercase get_lowercase
|
||||
sqlite3_fts5_create_tokenizer -parent lowercase db split_on_dot get_sod
|
||||
}
|
||||
3 {
|
||||
sqlite3_fts5_create_tokenizer db lowercase get_lowercase
|
||||
sqlite3_fts5_create_tokenizer -v2 -parent lowercase db split_on_dot get_sod
|
||||
}
|
||||
4 {
|
||||
sqlite3_fts5_create_tokenizer -v2 db lowercase get_lowercase
|
||||
sqlite3_fts5_create_tokenizer -v2 -parent lowercase db split_on_dot get_sod
|
||||
}
|
||||
} {
|
||||
reset_db
|
||||
eval $script
|
||||
|
||||
do_execsql_test 1.$tn.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize=split_on_dot);
|
||||
CREATE VIRTUAL TABLE t1vocab USING fts5vocab(t1, instance);
|
||||
INSERT INTO t1 VALUES('ABC.Def.ghi');
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.1 {
|
||||
SELECT term FROM t1vocab ORDER BY 1
|
||||
} {abc def ghi}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -56,6 +56,7 @@ foreach {tn like res} {
|
||||
7 {ABCDEFG%} 1
|
||||
8 {%รุงเ%} 2
|
||||
9 {%งเ%} 2
|
||||
10 {%"งเ"%} {}
|
||||
} {
|
||||
do_execsql_test 1.3.$tn {
|
||||
SELECT rowid FROM t1 WHERE y LIKE $like
|
||||
@@ -69,6 +70,9 @@ do_execsql_test 2.0 {
|
||||
INSERT INTO t1 VALUES('abcdefghijklm');
|
||||
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
|
||||
}
|
||||
do_catchsql_test 2.0.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(z, tokenize='trigram case_sensitive');
|
||||
} {1 {error in tokenizer constructor}}
|
||||
|
||||
foreach {tn s res} {
|
||||
1 abc "(abc)defghijklm"
|
||||
@@ -197,6 +201,12 @@ do_eqp_test 6.3 {
|
||||
do_eqp_test 6.4 {
|
||||
SELECT * FROM ci1 WHERE x GLOB ?
|
||||
} {VIRTUAL TABLE INDEX 0:G0}
|
||||
do_eqp_test 6.5 {
|
||||
SELECT * FROM ci1 WHERE x < ?
|
||||
} {{SCAN ci1 VIRTUAL TABLE INDEX 0:}}
|
||||
do_eqp_test 6.6 {
|
||||
SELECT * FROM ci0 WHERE x < ?
|
||||
} {{SCAN ci0 VIRTUAL TABLE INDEX 0:}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
@@ -206,7 +216,7 @@ do_execsql_test 7.0 {
|
||||
(20, "жираф.png"),
|
||||
(30, "cat.png"),
|
||||
(40, "кот.png"),
|
||||
(50, "misic-🎵-.mp3");
|
||||
(50, "misic-ðµ-.mp3");
|
||||
}
|
||||
do_execsql_test 7.1 {
|
||||
SELECT rowid FROM f WHERE +filename GLOB '*ир*';
|
||||
@@ -253,4 +263,85 @@ do_execsql_test 8.3 {
|
||||
{[abcde]}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(
|
||||
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
|
||||
tokenize=trigram
|
||||
);
|
||||
|
||||
INSERT INTO t1(rowid, a12) VALUES(111, 'thats a tricky case though');
|
||||
INSERT INTO t1(rowid, a12) VALUES(222, 'the query planner cannot do');
|
||||
}
|
||||
|
||||
do_execsql_test 9.1 {
|
||||
SELECT rowid FROM t1 WHERE a12 LIKE '%tricky%'
|
||||
} {111}
|
||||
|
||||
do_execsql_test 9.2 {
|
||||
SELECT rowid FROM t1 WHERE a12 LIKE '%tricky%' AND a12 LIKE '%case%'
|
||||
} {111}
|
||||
|
||||
do_execsql_test 9.3 {
|
||||
SELECT rowid FROM t1 WHERE a12 LIKE NULL
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=trigram);
|
||||
}
|
||||
|
||||
do_test 10.1 {
|
||||
foreach {val} {
|
||||
"abc \UFFjkl\UFF"
|
||||
"abc \UFFFjkl\UFFF"
|
||||
"abc \UFFFFjkl\UFFFF"
|
||||
"abc \UFFFFFjkl\UFFFFF"
|
||||
"\UFFjkl\UFF abc"
|
||||
"\UFFFjkl\UFFF abc"
|
||||
"\UFFFFjkl\UFFFF abc"
|
||||
"\UFFFFFjkl\UFFFFF abc"
|
||||
"\U10001jkl\U10001 abc"
|
||||
} {
|
||||
execsql { INSERT INTO t1 VALUES( $val ) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 10.2 {
|
||||
foreach {val} {
|
||||
X'E18000626320646566'
|
||||
X'61EDA0806320646566'
|
||||
X'61EDA0806320646566'
|
||||
X'61EFBFBE6320646566'
|
||||
X'76686920E18000626320646566'
|
||||
X'7668692061EDA0806320646566'
|
||||
X'7668692061EDA0806320646566'
|
||||
X'7668692061EFBFBE6320646566'
|
||||
} {
|
||||
execsql " INSERT INTO t1 VALUES( $val ) "
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 10.3 {
|
||||
set a [binary format c* {0x61 0xF7 0xBF 0xBF 0xBF 0x62}]
|
||||
set b [binary format c* {0x61 0xF7 0xBF 0xBF 0xBF 0xBF 0x62}]
|
||||
set c [binary format c* {0x61 0xF7 0xBF 0xBF 0xBF 0xBF 0xBF 0x62}]
|
||||
set d [binary format c* {0x61 0xF7 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0x62}]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES($a);
|
||||
INSERT INTO t1 VALUES($b);
|
||||
INSERT INTO t1 VALUES($c);
|
||||
INSERT INTO t1 VALUES($d);
|
||||
|
||||
INSERT INTO t1 VALUES('abcd' || $a);
|
||||
INSERT INTO t1 VALUES('abcd' || $b);
|
||||
INSERT INTO t1 VALUES('abcd' || $c);
|
||||
INSERT INTO t1 VALUES('abcd' || $d);
|
||||
}
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -21,6 +21,9 @@ do_execsql_test 1.0 "
|
||||
INSERT INTO t1 VALUES('abc\u0303defghijklm');
|
||||
INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm');
|
||||
"
|
||||
do_catchsql_test 1.0.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(z, tokenize='trigram remove_diacritics');
|
||||
} {1 {error in tokenizer constructor}}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('abc');
|
||||
@@ -117,4 +120,18 @@ do_execsql_test 4.2 {
|
||||
SELECT rowid FROM t4 WHERE z LIKE '%abc%'
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts5(
|
||||
c1, tokenize='trigram', detail='none'
|
||||
);
|
||||
INSERT INTO t5(rowid, c1) VALUES(1, 'abc_____xyx_yxz');
|
||||
INSERT INTO t5(rowid, c1) VALUES(2, 'abc_____xyxz');
|
||||
INSERT INTO t5(rowid, c1) VALUES(3, 'ac_____xyxz');
|
||||
} {}
|
||||
do_execsql_test 5.1 {
|
||||
SELECT rowid FROM t5 WHERE c1 LIKE 'abc%xyxz'
|
||||
} {2}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -60,6 +60,7 @@ do_execsql_test 2.1 "
|
||||
# require 17 or more bits to store).
|
||||
#
|
||||
|
||||
unset -nocomplain A B C D
|
||||
set A [db one {SELECT char(0x1F75E)}] ;# Type So
|
||||
set B [db one {SELECT char(0x1F5FD)}] ;# Type So
|
||||
set C [db one {SELECT char(0x2F802)}] ;# Type Lo
|
||||
|
||||
+17
-112
@@ -116,6 +116,7 @@ set docs [list {
|
||||
connected by OR.
|
||||
}]
|
||||
|
||||
unset -nocomplain map
|
||||
set map(a) [list "\u00C4" "\u00E4"] ; # LATIN LETTER A WITH DIAERESIS
|
||||
set map(e) [list "\u00CB" "\u00EB"] ; # LATIN LETTER E WITH DIAERESIS
|
||||
set map(i) [list "\u00CF" "\u00EF"] ; # LATIN LETTER I WITH DIAERESIS
|
||||
@@ -470,119 +471,23 @@ do_execsql_test 8.2.3 {
|
||||
} {2 4}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
if 0 {
|
||||
foreach {tn sql} {
|
||||
1 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 [tokenchars= .]);
|
||||
CREATE VIRTUAL TABLE t6 USING fts4(
|
||||
tokenize=unicode61 [tokenchars=="] "tokenchars=[]");
|
||||
CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 [separators=x\xC4]);
|
||||
}
|
||||
2 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 "tokenchars= .");
|
||||
CREATE VIRTUAL TABLE t6 USING fts4(tokenize=unicode61 "tokenchars=[=""]");
|
||||
CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 "separators=x\xC4");
|
||||
}
|
||||
3 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 'tokenchars= .');
|
||||
CREATE VIRTUAL TABLE t6 USING fts4(tokenize=unicode61 'tokenchars=="[]');
|
||||
CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 'separators=x\xC4');
|
||||
}
|
||||
4 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 `tokenchars= .`);
|
||||
CREATE VIRTUAL TABLE t6 USING fts4(tokenize=unicode61 `tokenchars=[="]`);
|
||||
CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 `separators=x\xC4`);
|
||||
}
|
||||
|
||||
foreach {tn val bErr} {
|
||||
1 0 0
|
||||
2 1 0
|
||||
3 2 0
|
||||
4 3 1
|
||||
5 11 1
|
||||
} {
|
||||
do_execsql_test 9.$tn.0 {
|
||||
DROP TABLE IF EXISTS t5;
|
||||
DROP TABLE IF EXISTS t5aux;
|
||||
DROP TABLE IF EXISTS t6;
|
||||
DROP TABLE IF EXISTS t6aux;
|
||||
DROP TABLE IF EXISTS t7;
|
||||
DROP TABLE IF EXISTS t7aux;
|
||||
}
|
||||
do_execsql_test 9.$tn.1 $sql
|
||||
|
||||
do_execsql_test 9.$tn.2 {
|
||||
CREATE VIRTUAL TABLE t5aux USING fts4aux(t5);
|
||||
INSERT INTO t5 VALUES('one two three/four.five.six');
|
||||
SELECT * FROM t5aux;
|
||||
} {
|
||||
four.five.six * 1 1 four.five.six 0 1 1
|
||||
{one two three} * 1 1 {one two three} 0 1 1
|
||||
}
|
||||
|
||||
do_execsql_test 9.$tn.3 {
|
||||
CREATE VIRTUAL TABLE t6aux USING fts4aux(t6);
|
||||
INSERT INTO t6 VALUES('alpha=beta"gamma/delta[epsilon]zeta');
|
||||
SELECT * FROM t6aux;
|
||||
} {
|
||||
{alpha=beta"gamma} * 1 1 {alpha=beta"gamma} 0 1 1
|
||||
{delta[epsilon]zeta} * 1 1 {delta[epsilon]zeta} 0 1 1
|
||||
}
|
||||
|
||||
do_execsql_test 9.$tn.4 {
|
||||
CREATE VIRTUAL TABLE t7aux USING fts4aux(t7);
|
||||
INSERT INTO t7 VALUES('alephxbeth\xC4gimel');
|
||||
SELECT * FROM t7aux;
|
||||
} {
|
||||
aleph * 1 1 aleph 0 1 1
|
||||
beth * 1 1 beth 0 1 1
|
||||
gimel * 1 1 gimel 0 1 1
|
||||
}
|
||||
}
|
||||
|
||||
# Check that multiple options are handled correctly.
|
||||
#
|
||||
do_execsql_test 10.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(tokenize=unicode61
|
||||
"tokenchars=xyz" "tokenchars=.=" "separators=.=" "separators=xy"
|
||||
"separators=a" "separators=a" "tokenchars=a" "tokenchars=a"
|
||||
);
|
||||
|
||||
INSERT INTO t1 VALUES('oneatwoxthreeyfour');
|
||||
INSERT INTO t1 VALUES('a.single=word');
|
||||
CREATE VIRTUAL TABLE t1aux USING fts4aux(t1);
|
||||
SELECT * FROM t1aux;
|
||||
} {
|
||||
.single=word * 1 1 .single=word 0 1 1
|
||||
four * 1 1 four 0 1 1
|
||||
one * 1 1 one 0 1 1
|
||||
three * 1 1 three 0 1 1
|
||||
two * 1 1 two 0 1 1
|
||||
}
|
||||
|
||||
# Test that case folding happens after tokenization, not before.
|
||||
#
|
||||
do_execsql_test 10.2 {
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE VIRTUAL TABLE t2 USING fts4(tokenize=unicode61 "separators=aB");
|
||||
INSERT INTO t2 VALUES('oneatwoBthree');
|
||||
INSERT INTO t2 VALUES('onebtwoAthree');
|
||||
CREATE VIRTUAL TABLE t2aux USING fts4aux(t2);
|
||||
SELECT * FROM t2aux;
|
||||
} {
|
||||
one * 1 1 one 0 1 1
|
||||
onebtwoathree * 1 1 onebtwoathree 0 1 1
|
||||
three * 1 1 three 0 1 1
|
||||
two * 1 1 two 0 1 1
|
||||
}
|
||||
|
||||
# Test that the tokenchars and separators options work with the
|
||||
# fts3tokenize table.
|
||||
#
|
||||
do_execsql_test 11.1 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts3tokenize(
|
||||
"unicode61", "tokenchars=@.", "separators=1234567890"
|
||||
);
|
||||
SELECT token FROM ft1 WHERE input = 'berlin@street123sydney.road';
|
||||
} {
|
||||
berlin@street sydney.road
|
||||
}
|
||||
|
||||
reset_db
|
||||
set aRes(0) {0 {}}
|
||||
set aRes(1) {1 {error in tokenizer constructor}}
|
||||
set res $aRes($bErr)
|
||||
do_catchsql_test 9.1.$tn "
|
||||
CREATE VIRTUAL TABLE bl USING fts5(
|
||||
s, tokenize='trigram remove_diacritics $val'
|
||||
);
|
||||
" $res
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -513,6 +513,7 @@ do_execsql_test 10.5 {
|
||||
INSERT INTO ft(a) VALUES('4 5 6');
|
||||
}
|
||||
|
||||
unset -nocomplain x res
|
||||
do_test 10.6 {
|
||||
set res [list]
|
||||
db eval { SELECT rowid FROM ft('4') } x {
|
||||
|
||||
@@ -15,13 +15,7 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqlite3intck.h"
|
||||
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
|
||||
#include "tclsqlite.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
@@ -851,11 +851,26 @@ class JsonBlockCommand extends TableResultCommand {
|
||||
//! --new command
|
||||
class NewDbCommand extends OpenDbCommand {
|
||||
public NewDbCommand(){ super(true); }
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
if(argv.length>1){
|
||||
Util.unlink(argv[1]);
|
||||
}
|
||||
super.process(t, ts, argv);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//! Placeholder dummy/no-op commands
|
||||
//! Placeholder dummy/no-op/unimplemented commands
|
||||
class NoopCommand extends Command {
|
||||
private boolean verbose = false;
|
||||
public NoopCommand(boolean verbose){
|
||||
this.verbose = verbose;
|
||||
}
|
||||
public NoopCommand(){}
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
if( this.verbose ){
|
||||
t.outln("Skipping unhandled command: "+argv[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,6 +1036,7 @@ class CommandDispatcher {
|
||||
case "db": rv = new DbCommand(); break;
|
||||
case "glob": rv = new GlobCommand(); break;
|
||||
case "json": rv = new JsonCommand(); break;
|
||||
case "jsonglob": rv = new NoopCommand(true); break;
|
||||
case "json-block": rv = new JsonBlockCommand(); break;
|
||||
case "new": rv = new NewDbCommand(); break;
|
||||
case "notglob": rv = new NotGlobCommand(); break;
|
||||
@@ -1030,6 +1046,7 @@ class CommandDispatcher {
|
||||
case "print": rv = new PrintCommand(); break;
|
||||
case "result": rv = new ResultCommand(); break;
|
||||
case "run": rv = new RunCommand(); break;
|
||||
case "stmt-cache": rv = new NoopCommand(); break;
|
||||
case "tableresult": rv = new TableResultCommand(); break;
|
||||
case "testcase": rv = new TestCaseCommand(); break;
|
||||
case "verbosity": rv = new VerbosityCommand(); break;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
The purpose of the Test Script Interpreter is to read and interpret
|
||||
script files that contain SQL commands and desired results. The
|
||||
interpreter will check results and report an discrepencies found.
|
||||
interpreter will check results and report any discrepencies found.
|
||||
|
||||
The test script files are ASCII text files. The filename always ends with
|
||||
".test". Each script is evaluated independently; context does not carry
|
||||
@@ -87,6 +87,7 @@ Each command looks like an SQL comment. The command begins at the left
|
||||
margin (no leading space) and starts with exactly 2 minus signs ("-").
|
||||
The command name consists of lowercase letters and maybe a "-" or two.
|
||||
Some commands have arguments.
|
||||
|
||||
The arguments are separated from the command name by one or more spaces.
|
||||
|
||||
Commands have access to the input buffer and might reset the input buffer.
|
||||
@@ -159,9 +160,9 @@ the result buffer. This distinction does not matter for the --result
|
||||
command itself, but it is important for related commands like --glob
|
||||
and --notglob. Sometimes test cases will contains a bunch of SQL
|
||||
followed by multiple --glob and/or --notglob statements. All of the
|
||||
globs should be evaluted agains the result buffer correct, but the SQL
|
||||
should only be run once. This is accomplished by resetting the input
|
||||
buffer but not the result buffer.
|
||||
globs should be evaluated agains the result buffer, but the SQL should
|
||||
only be run once. This is accomplished by resetting the input buffer
|
||||
but not the result buffer.
|
||||
|
||||
### The --glob command
|
||||
|
||||
@@ -187,7 +188,7 @@ The TEST-GLOB pattern is slightly different for a standard GLOB:
|
||||
### The --notglob command
|
||||
|
||||
The --notglob command works just like --glob except that it reports an
|
||||
error if the GLOB does match, rather than if the GLOB does not matches.
|
||||
error if the GLOB does match, rather than if the GLOB does not match.
|
||||
|
||||
### The --oom command
|
||||
|
||||
|
||||
+10
-3
@@ -40,7 +40,7 @@
|
||||
** modification-time of the target file is set to this value before
|
||||
** returning.
|
||||
**
|
||||
** If three or more arguments are passed to this function and an
|
||||
** If five or more arguments are passed to this function and an
|
||||
** error is encountered, an exception is raised.
|
||||
**
|
||||
** READFILE(FILE):
|
||||
@@ -110,6 +110,13 @@ SQLITE_EXTENSION_INIT1
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* When used as part of the CLI, the sqlite3_stdio.h module will have
|
||||
** been included before this one. In that case use the sqlite3_stdio.h
|
||||
** #defines. If not, create our own for fopen().
|
||||
*/
|
||||
#ifndef _SQLITE3_STDIO_H_
|
||||
# define sqlite3_fopen fopen
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Structure of the fsdir() table-valued function
|
||||
@@ -142,7 +149,7 @@ static void readFileContents(sqlite3_context *ctx, const char *zName){
|
||||
sqlite3 *db;
|
||||
int mxBlob;
|
||||
|
||||
in = fopen(zName, "rb");
|
||||
in = sqlite3_fopen(zName, "rb");
|
||||
if( in==0 ){
|
||||
/* File does not exist or is unreadable. Leave the result set to NULL. */
|
||||
return;
|
||||
@@ -397,7 +404,7 @@ static int writeFile(
|
||||
sqlite3_int64 nWrite = 0;
|
||||
const char *z;
|
||||
int rc = 0;
|
||||
FILE *out = fopen(zFile, "wb");
|
||||
FILE *out = sqlite3_fopen(zFile, "wb");
|
||||
if( out==0 ) return 1;
|
||||
z = (const char*)sqlite3_value_blob(pData);
|
||||
if( z ){
|
||||
|
||||
+335
-52
@@ -11,7 +11,7 @@
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains code to implement the percentile(Y,P) SQL function
|
||||
** as described below:
|
||||
** and similar as described below:
|
||||
**
|
||||
** (1) The percentile(Y,P) function is an aggregate function taking
|
||||
** exactly two arguments.
|
||||
@@ -57,29 +57,108 @@
|
||||
** (12) The percentile(Y,P) is implemented as a single C99 source-code
|
||||
** file that compiles into a shared-library or DLL that can be loaded
|
||||
** into SQLite using the sqlite3_load_extension() interface.
|
||||
**
|
||||
** (13) A separate median(Y) function is the equivalent percentile(Y,50).
|
||||
**
|
||||
** (14) A separate percentile_cont(Y,P) function is equivalent to
|
||||
** percentile(Y,P/100.0). In other words, the fraction value in
|
||||
** the second argument is in the range of 0 to 1 instead of 0 to 100.
|
||||
**
|
||||
** (15) A separate percentile_disc(Y,P) function is like
|
||||
** percentile_cont(Y,P) except that instead of returning the weighted
|
||||
** average of the nearest two input values, it returns the next lower
|
||||
** value. So the percentile_disc(Y,P) will always return a value
|
||||
** that was one of the inputs.
|
||||
**
|
||||
** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
|
||||
** percentile_disc(Y,P) can be used as window functions.
|
||||
**
|
||||
** Differences from standard SQL:
|
||||
**
|
||||
** * The percentile_cont(X,P) function is equivalent to the following in
|
||||
** standard SQL:
|
||||
**
|
||||
** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
|
||||
**
|
||||
** The SQLite syntax is much more compact. The standard SQL syntax
|
||||
** is also supported if SQLite is compiled with the
|
||||
** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
|
||||
**
|
||||
** * No median(X) function exists in the SQL standard. App developers
|
||||
** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
|
||||
**
|
||||
** * No percentile(Y,P) function exists in the SQL standard. Instead of
|
||||
** percential(Y,P), developers must write this:
|
||||
** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
|
||||
** the fraction parameter to percentile() goes from 0 to 100 whereas
|
||||
** the fraction parameter in SQL standard percentile_cont() goes from
|
||||
** 0 to 1.
|
||||
**
|
||||
** Implementation notes as of 2024-08-31:
|
||||
**
|
||||
** * The regular aggregate-function versions of these routines work
|
||||
** by accumulating all values in an array of doubles, then sorting
|
||||
** that array using quicksort before computing the answer. Thus
|
||||
** the runtime is O(NlogN) where N is the number of rows of input.
|
||||
**
|
||||
** * For the window-function versions of these routines, the array of
|
||||
** inputs is sorted as soon as the first value is computed. Thereafter,
|
||||
** the array is kept in sorted order using an insert-sort. This
|
||||
** results in O(N*K) performance where K is the size of the window.
|
||||
** One can imagine alternative implementations that give O(N*logN*logK)
|
||||
** performance, but they require more complex logic and data structures.
|
||||
** The developers have elected to keep the asymptotically slower
|
||||
** algorithm for now, for simplicity, under the theory that window
|
||||
** functions are seldom used and when they are, the window size K is
|
||||
** often small. The developers might revisit that decision later,
|
||||
** should the need arise.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#if defined(SQLITE3_H)
|
||||
/* no-op */
|
||||
#elif defined(SQLITE_STATIC_PERCENTILE)
|
||||
# include "sqlite3.h"
|
||||
#else
|
||||
# include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The following object is the session context for a single percentile()
|
||||
** function. We have to remember all input Y values until the very end.
|
||||
/* The following object is the group context for a single percentile()
|
||||
** aggregate. Remember all input Y values until the very end.
|
||||
** Those values are accumulated in the Percentile.a[] array.
|
||||
*/
|
||||
typedef struct Percentile Percentile;
|
||||
struct Percentile {
|
||||
unsigned nAlloc; /* Number of slots allocated for a[] */
|
||||
unsigned nUsed; /* Number of slots actually used in a[] */
|
||||
double rPct; /* 1.0 more than the value for P */
|
||||
char bSorted; /* True if a[] is already in sorted order */
|
||||
char bKeepSorted; /* True if advantageous to keep a[] sorted */
|
||||
char bPctValid; /* True if rPct is valid */
|
||||
double rPct; /* Fraction. 0.0 to 1.0 */
|
||||
double *a; /* Array of Y values */
|
||||
};
|
||||
|
||||
/* Details of each function in the percentile family */
|
||||
typedef struct PercentileFunc PercentileFunc;
|
||||
struct PercentileFunc {
|
||||
const char *zName; /* Function name */
|
||||
char nArg; /* Number of arguments */
|
||||
char mxFrac; /* Maximum value of the "fraction" input */
|
||||
char bDiscrete; /* True for percentile_disc() */
|
||||
};
|
||||
static const PercentileFunc aPercentFunc[] = {
|
||||
{ "median", 1, 1, 0 },
|
||||
{ "percentile", 2, 100, 0 },
|
||||
{ "percentile_cont", 2, 1, 0 },
|
||||
{ "percentile_disc", 2, 1, 1 },
|
||||
};
|
||||
|
||||
/*
|
||||
** Return TRUE if the input floating-point number is an infinity.
|
||||
*/
|
||||
static int isInfinity(double r){
|
||||
static int percentIsInfinity(double r){
|
||||
sqlite3_uint64 u;
|
||||
assert( sizeof(u)==sizeof(r) );
|
||||
memcpy(&u, &r, sizeof(u));
|
||||
@@ -87,13 +166,64 @@ static int isInfinity(double r){
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if two doubles differ by 0.001 or less
|
||||
** Return TRUE if two doubles differ by 0.001 or less.
|
||||
*/
|
||||
static int sameValue(double a, double b){
|
||||
static int percentSameValue(double a, double b){
|
||||
a -= b;
|
||||
return a>=-0.001 && a<=0.001;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search p (which must have p->bSorted) looking for an entry with
|
||||
** value y. Return the index of that entry.
|
||||
**
|
||||
** If bExact is true, return -1 if the entry is not found.
|
||||
**
|
||||
** If bExact is false, return the index at which a new entry with
|
||||
** value y should be insert in order to keep the values in sorted
|
||||
** order. The smallest return value in this case will be 0, and
|
||||
** the largest return value will be p->nUsed.
|
||||
*/
|
||||
static int percentBinarySearch(Percentile *p, double y, int bExact){
|
||||
int iFirst = 0; /* First element of search range */
|
||||
int iLast = p->nUsed - 1; /* Last element of search range */
|
||||
while( iLast>=iFirst ){
|
||||
int iMid = (iFirst+iLast)/2;
|
||||
double x = p->a[iMid];
|
||||
if( x<y ){
|
||||
iFirst = iMid + 1;
|
||||
}else if( x>y ){
|
||||
iLast = iMid - 1;
|
||||
}else{
|
||||
return iMid;
|
||||
}
|
||||
}
|
||||
if( bExact ) return -1;
|
||||
return iFirst;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate an error for a percentile function.
|
||||
**
|
||||
** The error format string must have exactly one occurrance of "%%s()"
|
||||
** (with two '%' characters). That substring will be replaced by the name
|
||||
** of the function.
|
||||
*/
|
||||
static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
|
||||
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
|
||||
char *zMsg1;
|
||||
char *zMsg2;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zMsg1 = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0;
|
||||
sqlite3_result_error(pCtx, zMsg2, -1);
|
||||
sqlite3_free(zMsg1);
|
||||
sqlite3_free(zMsg2);
|
||||
}
|
||||
|
||||
/*
|
||||
** The "step" function for percentile(Y,P) is called once for each
|
||||
** input row.
|
||||
@@ -103,16 +233,24 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
double rPct;
|
||||
int eType;
|
||||
double y;
|
||||
assert( argc==2 );
|
||||
assert( argc==2 || argc==1 );
|
||||
|
||||
/* Requirement 3: P must be a number between 0 and 100 */
|
||||
eType = sqlite3_value_numeric_type(argv[1]);
|
||||
rPct = sqlite3_value_double(argv[1]);
|
||||
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|
||||
|| rPct<0.0 || rPct>100.0 ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
|
||||
"a number between 0.0 and 100.0", -1);
|
||||
return;
|
||||
if( argc==1 ){
|
||||
/* Requirement 13: median(Y) is the same as percentile(Y,50). */
|
||||
rPct = 0.5;
|
||||
}else{
|
||||
/* Requirement 3: P must be a number between 0 and 100 */
|
||||
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
|
||||
eType = sqlite3_value_numeric_type(argv[1]);
|
||||
rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac;
|
||||
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|
||||
|| rPct<0.0 || rPct>1.0
|
||||
){
|
||||
percentError(pCtx, "the fraction argument to %%s()"
|
||||
" is not between 0.0 and %.1f",
|
||||
(double)pFunc->mxFrac);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the session context. */
|
||||
@@ -121,11 +259,12 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
|
||||
/* Remember the P value. Throw an error if the P value is different
|
||||
** from any prior row, per Requirement (2). */
|
||||
if( p->rPct==0.0 ){
|
||||
p->rPct = rPct+1.0;
|
||||
}else if( !sameValue(p->rPct,rPct+1.0) ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
|
||||
"same for all input rows", -1);
|
||||
if( !p->bPctValid ){
|
||||
p->rPct = rPct;
|
||||
p->bPctValid = 1;
|
||||
}else if( !percentSameValue(p->rPct,rPct) ){
|
||||
percentError(pCtx, "the fraction argument to %%s()"
|
||||
" is not the same for all input rows");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -136,15 +275,14 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
/* If not NULL, then Y must be numeric. Otherwise throw an error.
|
||||
** Requirement 4 */
|
||||
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
|
||||
sqlite3_result_error(pCtx, "1st argument to percentile() is not "
|
||||
"numeric", -1);
|
||||
percentError(pCtx, "input to %%s() is not numeric");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Throw an error if the Y value is infinity or NaN */
|
||||
y = sqlite3_value_double(argv[0]);
|
||||
if( isInfinity(y) ){
|
||||
sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
|
||||
if( percentIsInfinity(y) ){
|
||||
percentError(pCtx, "Inf input to %%s()");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,26 +299,143 @@ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
p->nAlloc = n;
|
||||
p->a = a;
|
||||
}
|
||||
p->a[p->nUsed++] = y;
|
||||
if( p->nUsed==0 ){
|
||||
p->a[p->nUsed++] = y;
|
||||
p->bSorted = 1;
|
||||
}else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
|
||||
p->a[p->nUsed++] = y;
|
||||
}else if( p->bKeepSorted ){
|
||||
int i;
|
||||
i = percentBinarySearch(p, y, 0);
|
||||
if( i<(int)p->nUsed ){
|
||||
memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
|
||||
}
|
||||
p->a[i] = y;
|
||||
p->nUsed++;
|
||||
}else{
|
||||
p->a[p->nUsed++] = y;
|
||||
p->bSorted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare to doubles for sorting using qsort()
|
||||
** Interchange two doubles.
|
||||
*/
|
||||
static int SQLITE_CDECL doubleCmp(const void *pA, const void *pB){
|
||||
double a = *(double*)pA;
|
||||
double b = *(double*)pB;
|
||||
if( a==b ) return 0;
|
||||
if( a<b ) return -1;
|
||||
return +1;
|
||||
}
|
||||
#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
|
||||
|
||||
/*
|
||||
** Called to compute the final output of percentile() and to clean
|
||||
** up all allocated memory.
|
||||
** Sort an array of doubles.
|
||||
**
|
||||
** Algorithm: quicksort
|
||||
**
|
||||
** This is implemented separately rather than using the qsort() routine
|
||||
** from the standard library because:
|
||||
**
|
||||
** (1) To avoid a dependency on qsort()
|
||||
** (2) To avoid the function call to the comparison routine for each
|
||||
** comparison.
|
||||
*/
|
||||
static void percentFinal(sqlite3_context *pCtx){
|
||||
static void percentSort(double *a, unsigned int n){
|
||||
int iLt; /* Entries before a[iLt] are less than rPivot */
|
||||
int iGt; /* Entries at or after a[iGt] are greater than rPivot */
|
||||
int i; /* Loop counter */
|
||||
double rPivot; /* The pivot value */
|
||||
|
||||
assert( n>=2 );
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
|
||||
/* Uncomment for testing */
|
||||
#if 0
|
||||
for(i=0; i<n-1; i++){
|
||||
assert( a[i]<=a[i+1] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** The "inverse" function for percentile(Y,P) is called to remove a
|
||||
** row that was previously inserted by "step".
|
||||
*/
|
||||
static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
|
||||
Percentile *p;
|
||||
int eType;
|
||||
double y;
|
||||
int i;
|
||||
assert( argc==2 || argc==1 );
|
||||
|
||||
/* Allocate the session context. */
|
||||
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
||||
assert( p!=0 );
|
||||
|
||||
/* Ignore rows for which Y is NULL */
|
||||
eType = sqlite3_value_type(argv[0]);
|
||||
if( eType==SQLITE_NULL ) return;
|
||||
|
||||
/* If not NULL, then Y must be numeric. Otherwise throw an error.
|
||||
** Requirement 4 */
|
||||
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore the Y value if it is infinity or NaN */
|
||||
y = sqlite3_value_double(argv[0]);
|
||||
if( percentIsInfinity(y) ){
|
||||
return;
|
||||
}
|
||||
if( p->bSorted==0 ){
|
||||
assert( p->nUsed>1 );
|
||||
percentSort(p->a, p->nUsed);
|
||||
p->bSorted = 1;
|
||||
}
|
||||
p->bKeepSorted = 1;
|
||||
|
||||
/* Find and remove the row */
|
||||
i = percentBinarySearch(p, y, 1);
|
||||
if( i>=0 ){
|
||||
p->nUsed--;
|
||||
if( i<(int)p->nUsed ){
|
||||
memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute the final output of percentile(). Clean up all allocated
|
||||
** memory if and only if bIsFinal is true.
|
||||
*/
|
||||
static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
|
||||
Percentile *p;
|
||||
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
|
||||
unsigned i1, i2;
|
||||
double v1, v2;
|
||||
double ix, vx;
|
||||
@@ -188,21 +443,38 @@ static void percentFinal(sqlite3_context *pCtx){
|
||||
if( p==0 ) return;
|
||||
if( p->a==0 ) return;
|
||||
if( p->nUsed ){
|
||||
qsort(p->a, p->nUsed, sizeof(double), doubleCmp);
|
||||
ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
|
||||
if( p->bSorted==0 ){
|
||||
assert( p->nUsed>1 );
|
||||
percentSort(p->a, p->nUsed);
|
||||
p->bSorted = 1;
|
||||
}
|
||||
ix = p->rPct*(p->nUsed-1);
|
||||
i1 = (unsigned)ix;
|
||||
i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
|
||||
v1 = p->a[i1];
|
||||
v2 = p->a[i2];
|
||||
vx = v1 + (v2-v1)*(ix-i1);
|
||||
if( pFunc->bDiscrete ){
|
||||
vx = p->a[i1];
|
||||
}else{
|
||||
i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
|
||||
v1 = p->a[i1];
|
||||
v2 = p->a[i2];
|
||||
vx = v1 + (v2-v1)*(ix-i1);
|
||||
}
|
||||
sqlite3_result_double(pCtx, vx);
|
||||
}
|
||||
sqlite3_free(p->a);
|
||||
memset(p, 0, sizeof(*p));
|
||||
if( bIsFinal ){
|
||||
sqlite3_free(p->a);
|
||||
memset(p, 0, sizeof(*p));
|
||||
}else{
|
||||
p->bKeepSorted = 1;
|
||||
}
|
||||
}
|
||||
static void percentFinal(sqlite3_context *pCtx){
|
||||
percentCompute(pCtx, 1);
|
||||
}
|
||||
static void percentValue(sqlite3_context *pCtx){
|
||||
percentCompute(pCtx, 0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE)
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_percentile_init(
|
||||
@@ -211,10 +483,21 @@ int sqlite3_percentile_init(
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
unsigned int i;
|
||||
#if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE)
|
||||
(void)pApi; /* Unused parameter */
|
||||
#else
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#endif
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "percentile", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
0, percentStep, percentFinal);
|
||||
for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
|
||||
rc = sqlite3_create_window_function(db,
|
||||
aPercentFunc[i].zName,
|
||||
aPercentFunc[i].nArg,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
|
||||
(void*)&aPercentFunc[i],
|
||||
percentStep, percentFinal, percentValue, percentInverse, 0);
|
||||
if( rc ) break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
+223
-31
@@ -90,6 +90,26 @@
|
||||
** and a very large cost if either start or stop are unavailable. This
|
||||
** encourages the query planner to order joins such that the bounds of the
|
||||
** series are well-defined.
|
||||
**
|
||||
** Update on 2024-08-22:
|
||||
** xBestIndex now also looks for equality and inequality constraints against
|
||||
** the value column and uses those constraints as additional bounds against
|
||||
** the sequence range. Thus, a query like this:
|
||||
**
|
||||
** SELECT value FROM generate_series($SA,$EA)
|
||||
** WHERE value BETWEEN $SB AND $EB;
|
||||
**
|
||||
** Is logically the same as:
|
||||
**
|
||||
** SELECT value FROM generate_series(max($SA,$SB),min($EA,$EB));
|
||||
**
|
||||
** Constraints on the value column can server as substitutes for constraints
|
||||
** on the hidden start and stop columns. So, the following two queries
|
||||
** are equivalent:
|
||||
**
|
||||
** SELECT value FROM generate_series($S,$E);
|
||||
** SELECT value FROM generate_series WHERE value BETWEEN $S and $E;
|
||||
**
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@@ -131,8 +151,10 @@ static sqlite3_int64 genSeqMember(
|
||||
typedef unsigned char u8;
|
||||
|
||||
typedef struct SequenceSpec {
|
||||
sqlite3_int64 iBase; /* Starting value ("start") */
|
||||
sqlite3_int64 iTerm; /* Given terminal value ("stop") */
|
||||
sqlite3_int64 iOBase; /* Original starting value ("start") */
|
||||
sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
|
||||
sqlite3_int64 iBase; /* Starting value to actually use */
|
||||
sqlite3_int64 iTerm; /* Terminal value to actually use */
|
||||
sqlite3_int64 iStep; /* Increment ("step") */
|
||||
sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
|
||||
sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
|
||||
@@ -325,9 +347,9 @@ static int seriesColumn(
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
sqlite3_int64 x = 0;
|
||||
switch( i ){
|
||||
case SERIES_COLUMN_START: x = pCur->ss.iBase; break;
|
||||
case SERIES_COLUMN_STOP: x = pCur->ss.iTerm; break;
|
||||
case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
|
||||
case SERIES_COLUMN_START: x = pCur->ss.iOBase; break;
|
||||
case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break;
|
||||
case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
|
||||
default: x = pCur->ss.iValueNow; break;
|
||||
}
|
||||
sqlite3_result_int64(ctx, x);
|
||||
@@ -335,7 +357,9 @@ static int seriesColumn(
|
||||
}
|
||||
|
||||
#ifndef LARGEST_UINT64
|
||||
#define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
|
||||
#define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -376,13 +400,18 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
|
||||
** parameter. (idxStr is not used in this implementation.) idxNum
|
||||
** is a bitmask showing which constraints are available:
|
||||
**
|
||||
** 0x01: start=VALUE
|
||||
** 0x02: stop=VALUE
|
||||
** 0x04: step=VALUE
|
||||
** 0x08: descending order
|
||||
** 0x10: ascending order
|
||||
** 0x20: LIMIT VALUE
|
||||
** 0x40: OFFSET VALUE
|
||||
** 0x0001: start=VALUE
|
||||
** 0x0002: stop=VALUE
|
||||
** 0x0004: step=VALUE
|
||||
** 0x0008: descending order
|
||||
** 0x0010: ascending order
|
||||
** 0x0020: LIMIT VALUE
|
||||
** 0x0040: OFFSET VALUE
|
||||
** 0x0080: value=VALUE
|
||||
** 0x0100: value>=VALUE
|
||||
** 0x0200: value>VALUE
|
||||
** 0x1000: value<=VALUE
|
||||
** 0x2000: value<VALUE
|
||||
**
|
||||
** This routine should initialize the cursor and position it so that it
|
||||
** is pointing at the first row, or pointing off the end of the table
|
||||
@@ -395,6 +424,12 @@ static int seriesFilter(
|
||||
){
|
||||
series_cursor *pCur = (series_cursor *)pVtabCursor;
|
||||
int i = 0;
|
||||
int returnNoRows = 0;
|
||||
sqlite3_int64 iMin = SMALLEST_INT64;
|
||||
sqlite3_int64 iMax = LARGEST_INT64;
|
||||
sqlite3_int64 iLimit = 0;
|
||||
sqlite3_int64 iOffset = 0;
|
||||
|
||||
(void)idxStrUnused;
|
||||
if( idxNum & 0x01 ){
|
||||
pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
|
||||
@@ -416,16 +451,98 @@ static int seriesFilter(
|
||||
}else{
|
||||
pCur->ss.iStep = 1;
|
||||
}
|
||||
|
||||
/* If there are constraints on the value column but there are
|
||||
** no constraints on the start, stop, and step columns, then
|
||||
** initialize the default range to be the entire range of 64-bit signed
|
||||
** integers. This range will contracted by the value column constraints
|
||||
** further below.
|
||||
*/
|
||||
if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
|
||||
pCur->ss.iBase = SMALLEST_INT64;
|
||||
}
|
||||
if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
|
||||
pCur->ss.iTerm = LARGEST_INT64;
|
||||
}
|
||||
pCur->ss.iOBase = pCur->ss.iBase;
|
||||
pCur->ss.iOTerm = pCur->ss.iTerm;
|
||||
|
||||
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
|
||||
** The range must first be constrained by the limits on value.
|
||||
*/
|
||||
if( idxNum & 0x20 ){
|
||||
sqlite3_int64 iLimit = sqlite3_value_int64(argv[i++]);
|
||||
sqlite3_int64 iTerm;
|
||||
iLimit = sqlite3_value_int64(argv[i++]);
|
||||
if( idxNum & 0x40 ){
|
||||
sqlite3_int64 iOffset = sqlite3_value_int64(argv[i++]);
|
||||
if( iOffset>0 ){
|
||||
pCur->ss.iBase += pCur->ss.iStep*iOffset;
|
||||
iOffset = sqlite3_value_int64(argv[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
if( idxNum & 0x3380 ){
|
||||
/* Extract the maximum range of output values determined by
|
||||
** constraints on the "value" column.
|
||||
*/
|
||||
if( idxNum & 0x0080 ){
|
||||
iMin = iMax = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
if( idxNum & 0x0300 ){
|
||||
iMin = sqlite3_value_int64(argv[i++]);
|
||||
if( idxNum & 0x0200 ){
|
||||
if( iMin==LARGEST_INT64 ){
|
||||
returnNoRows = 1;
|
||||
}else{
|
||||
iMin++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( idxNum & 0x3000 ){
|
||||
iMax = sqlite3_value_int64(argv[i++]);
|
||||
if( idxNum & 0x2000 ){
|
||||
if( iMax==SMALLEST_INT64 ){
|
||||
returnNoRows = 1;
|
||||
}else{
|
||||
iMax--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( iMin>iMax ){
|
||||
returnNoRows = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to reduce the range of values to be generated based on
|
||||
** constraints on the "value" column.
|
||||
*/
|
||||
if( pCur->ss.iStep>0 ){
|
||||
sqlite3_int64 szStep = pCur->ss.iStep;
|
||||
if( pCur->ss.iBase<iMin ){
|
||||
sqlite3_uint64 d = iMin - pCur->ss.iBase;
|
||||
pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
|
||||
}
|
||||
if( pCur->ss.iTerm>iMax ){
|
||||
sqlite3_uint64 d = pCur->ss.iTerm - iMax;
|
||||
pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep;
|
||||
}
|
||||
}else{
|
||||
sqlite3_int64 szStep = -pCur->ss.iStep;
|
||||
assert( szStep>0 );
|
||||
if( pCur->ss.iBase>iMax ){
|
||||
sqlite3_uint64 d = pCur->ss.iBase - iMax;
|
||||
pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
|
||||
}
|
||||
if( pCur->ss.iTerm<iMin ){
|
||||
sqlite3_uint64 d = iMin - pCur->ss.iTerm;
|
||||
pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply LIMIT and OFFSET constraints, if any */
|
||||
if( idxNum & 0x20 ){
|
||||
if( iOffset>0 ){
|
||||
pCur->ss.iBase += pCur->ss.iStep*iOffset;
|
||||
}
|
||||
if( iLimit>=0 ){
|
||||
sqlite3_int64 iTerm;
|
||||
iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
|
||||
if( pCur->ss.iStep<0 ){
|
||||
if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
|
||||
@@ -434,16 +551,21 @@ static int seriesFilter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(i=0; i<argc; i++){
|
||||
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
|
||||
/* If any of the constraints have a NULL value, then return no rows.
|
||||
** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
|
||||
pCur->ss.iBase = 1;
|
||||
pCur->ss.iTerm = 0;
|
||||
pCur->ss.iStep = 1;
|
||||
returnNoRows = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( returnNoRows ){
|
||||
pCur->ss.iBase = 1;
|
||||
pCur->ss.iTerm = 0;
|
||||
pCur->ss.iStep = 1;
|
||||
}
|
||||
if( idxNum & 0x08 ){
|
||||
pCur->ss.isReversing = pCur->ss.iStep > 0;
|
||||
}else{
|
||||
@@ -464,13 +586,35 @@ static int seriesFilter(
|
||||
**
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** 0x01 start = $value -- constraint exists
|
||||
** 0x02 stop = $value -- constraint exists
|
||||
** 0x04 step = $value -- constraint exists
|
||||
** 0x08 output is in descending order
|
||||
** 0x10 output is in ascending order
|
||||
** 0x20 LIMIT $value -- constraint exists
|
||||
** 0x40 OFFSET $value -- constraint exists
|
||||
** 0x0001 start = $num
|
||||
** 0x0002 stop = $num
|
||||
** 0x0004 step = $num
|
||||
** 0x0008 output is in descending order
|
||||
** 0x0010 output is in ascending order
|
||||
** 0x0020 LIMIT $num
|
||||
** 0x0040 OFFSET $num
|
||||
** 0x0080 value = $num
|
||||
** 0x0100 value >= $num
|
||||
** 0x0200 value > $num
|
||||
** 0x1000 value <= $num
|
||||
** 0x2000 value < $num
|
||||
**
|
||||
** Only one of 0x0100 or 0x0200 will be returned. Similarly, only
|
||||
** one of 0x1000 or 0x2000 will be returned. If the 0x0080 is set, then
|
||||
** none of the 0xff00 bits will be set.
|
||||
**
|
||||
** The order of parameters passed to xFilter is as follows:
|
||||
**
|
||||
** * The argument to start= if bit 0x0001 is in the idxNum mask
|
||||
** * The argument to stop= if bit 0x0002 is in the idxNum mask
|
||||
** * The argument to step= if bit 0x0004 is in the idxNum mask
|
||||
** * The argument to LIMIT if bit 0x0020 is in the idxNum mask
|
||||
** * The argument to OFFSET if bit 0x0040 is in the idxNum mask
|
||||
** * The argument to value=, or value>= or value> if any of
|
||||
** bits 0x0380 are in the idxNum mask
|
||||
** * The argument to value<= or value< if either of bits 0x3000
|
||||
** are in the mask
|
||||
**
|
||||
*/
|
||||
static int seriesBestIndex(
|
||||
sqlite3_vtab *pVTab,
|
||||
@@ -483,7 +627,9 @@ static int seriesBestIndex(
|
||||
#endif
|
||||
int unusableMask = 0; /* Mask of unusable constraints */
|
||||
int nArg = 0; /* Number of arguments that seriesFilter() expects */
|
||||
int aIdx[5]; /* Constraints on start, stop, step, LIMIT, OFFSET */
|
||||
int aIdx[7]; /* Constraints on start, stop, step, LIMIT, OFFSET,
|
||||
** and value. aIdx[5] covers value=, value>=, and
|
||||
** value>, aIdx[6] covers value<= and value< */
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
|
||||
/* This implementation assumes that the start, stop, and step columns
|
||||
@@ -491,7 +637,7 @@ static int seriesBestIndex(
|
||||
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
|
||||
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
|
||||
|
||||
aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = -1;
|
||||
aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = aIdx[5] = aIdx[6] = -1;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
int iCol; /* 0 for start, 1 for stop, 2 for step */
|
||||
@@ -512,7 +658,50 @@ static int seriesBestIndex(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
|
||||
if( pConstraint->iColumn==SERIES_COLUMN_VALUE ){
|
||||
switch( op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ:
|
||||
case SQLITE_INDEX_CONSTRAINT_IS: {
|
||||
idxNum |= 0x0080;
|
||||
idxNum &= ~0x3300;
|
||||
aIdx[5] = i;
|
||||
aIdx[6] = -1;
|
||||
bStartSeen = 1;
|
||||
break;
|
||||
}
|
||||
case SQLITE_INDEX_CONSTRAINT_GE: {
|
||||
if( idxNum & 0x0080 ) break;
|
||||
idxNum |= 0x0100;
|
||||
idxNum &= ~0x0200;
|
||||
aIdx[5] = i;
|
||||
bStartSeen = 1;
|
||||
break;
|
||||
}
|
||||
case SQLITE_INDEX_CONSTRAINT_GT: {
|
||||
if( idxNum & 0x0080 ) break;
|
||||
idxNum |= 0x0200;
|
||||
idxNum &= ~0x0100;
|
||||
aIdx[5] = i;
|
||||
bStartSeen = 1;
|
||||
break;
|
||||
}
|
||||
case SQLITE_INDEX_CONSTRAINT_LE: {
|
||||
if( idxNum & 0x0080 ) break;
|
||||
idxNum |= 0x1000;
|
||||
idxNum &= ~0x2000;
|
||||
aIdx[6] = i;
|
||||
break;
|
||||
}
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: {
|
||||
if( idxNum & 0x0080 ) break;
|
||||
idxNum |= 0x2000;
|
||||
idxNum &= ~0x1000;
|
||||
aIdx[6] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
|
||||
assert( iCol>=0 && iCol<=2 );
|
||||
iMask = 1 << iCol;
|
||||
@@ -534,7 +723,7 @@ static int seriesBestIndex(
|
||||
idxNum &= ~0x60;
|
||||
aIdx[4] = 0;
|
||||
}
|
||||
for(i=0; i<5; i++){
|
||||
for(i=0; i<7; i++){
|
||||
if( (j = aIdx[i])>=0 ){
|
||||
pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[j].omit =
|
||||
@@ -582,6 +771,9 @@ static int seriesBestIndex(
|
||||
pIdxInfo->estimatedRows = 2147483647;
|
||||
}
|
||||
pIdxInfo->idxNum = idxNum;
|
||||
#ifdef SQLITE_INDEX_SCAN_HEX
|
||||
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_HEX;
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+25
-9
@@ -196,7 +196,8 @@ static void hash_step_vformat(
|
||||
** zOut[]. zOut[] must be at least 41 bytes long. */
|
||||
static void hash_finish(
|
||||
SHA1Context *p, /* The SHA1 context to finish and render */
|
||||
char *zOut /* Store hexadecimal hash here */
|
||||
char *zOut, /* Store hex or binary hash here */
|
||||
int bAsBinary /* 1 for binary hash, 0 for hex hash */
|
||||
){
|
||||
unsigned int i;
|
||||
unsigned char finalcount[8];
|
||||
@@ -215,11 +216,15 @@ static void hash_finish(
|
||||
for (i = 0; i < 20; i++){
|
||||
digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
for(i=0; i<20; i++){
|
||||
zOut[i*2] = zEncode[(digest[i]>>4)&0xf];
|
||||
zOut[i*2+1] = zEncode[digest[i] & 0xf];
|
||||
if( bAsBinary ){
|
||||
memcpy(zOut, digest, 20);
|
||||
}else{
|
||||
for(i=0; i<20; i++){
|
||||
zOut[i*2] = zEncode[(digest[i]>>4)&0xf];
|
||||
zOut[i*2+1] = zEncode[digest[i] & 0xf];
|
||||
}
|
||||
zOut[i*2]= 0;
|
||||
}
|
||||
zOut[i*2]= 0;
|
||||
}
|
||||
/* End of the hashing logic
|
||||
*****************************************************************************/
|
||||
@@ -251,8 +256,13 @@ static void sha1Func(
|
||||
}else{
|
||||
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
|
||||
}
|
||||
hash_finish(&cx, zOut);
|
||||
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
if( sqlite3_user_data(context)!=0 ){
|
||||
hash_finish(&cx, zOut, 1);
|
||||
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
hash_finish(&cx, zOut, 0);
|
||||
sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -365,7 +375,7 @@ static void sha1QueryFunc(
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
hash_finish(&cx, zOut);
|
||||
hash_finish(&cx, zOut, 0);
|
||||
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
@@ -379,11 +389,17 @@ int sqlite3_sha_init(
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
static int one = 1;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sha1", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, sha1Func, 0, 0);
|
||||
0, sha1Func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha1b", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
(void*)&one, sha1Func, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha1_query", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
|
||||
+179
-50
@@ -15,10 +15,20 @@
|
||||
** Two SQL functions are implemented:
|
||||
**
|
||||
** sha3(X,SIZE)
|
||||
** sha3_query(Y,SIZE)
|
||||
** sha3_agg(Y,SIZE)
|
||||
** sha3_query(Z,SIZE)
|
||||
**
|
||||
** The sha3(X) function computes the SHA3 hash of the input X, or NULL if
|
||||
** X is NULL.
|
||||
** X is NULL. If inputs X is text, the UTF-8 rendering of that text is
|
||||
** used to compute the hash. If X is a BLOB, then the binary data of the
|
||||
** blob is used to compute the hash. If X is an integer or real number,
|
||||
** then that number if converted into UTF-8 text and the hash is computed
|
||||
** over the text.
|
||||
**
|
||||
** The sha3_agg(Y) function computes the SHA3 hash of all Y inputs. Since
|
||||
** order is important for the hash, it is recommended that the Y expression
|
||||
** by followed by an ORDER BY clause to guarantee that the inputs occur
|
||||
** in the desired order.
|
||||
**
|
||||
** The sha3_query(Y) function evaluates all queries in the SQL statements of Y
|
||||
** and returns a hash of their results.
|
||||
@@ -26,6 +36,68 @@
|
||||
** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm
|
||||
** is used. If SIZE is included it must be one of the integers 224, 256,
|
||||
** 384, or 512, to determine SHA3 hash variant that is computed.
|
||||
**
|
||||
** Because the sha3_agg() and sha3_query() functions compute a hash over
|
||||
** multiple values, the values are encode to use include type information.
|
||||
**
|
||||
** In sha3_agg(), the sequence of bytes that gets hashed for each input
|
||||
** Y depends on the datatype of Y:
|
||||
**
|
||||
** typeof(Y)='null' A single "N" is hashed. (One byte)
|
||||
**
|
||||
** typeof(Y)='integer' The data hash is the character "I" followed
|
||||
** by an 8-byte big-endian binary of the
|
||||
** 64-bit signed integer. (Nine bytes total.)
|
||||
**
|
||||
** typeof(Y)='real' The character "F" followed by an 8-byte
|
||||
** big-ending binary of the double. (Nine
|
||||
** bytes total.)
|
||||
**
|
||||
** typeof(Y)='text' The hash is over prefix "Tnnn:" followed
|
||||
** by the UTF8 encoding of the text. The "nnn"
|
||||
** in the prefix is the minimum-length decimal
|
||||
** representation of the octet_length of the text.
|
||||
** Notice the ":" at the end of the prefix, which
|
||||
** is needed to separate the prefix from the
|
||||
** content in cases where the content starts
|
||||
** with a digit.
|
||||
**
|
||||
** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed
|
||||
** by the binary content of the blob. The "nnn"
|
||||
** in the prefix is the mimimum-length decimal
|
||||
** representation of the byte-length of the blob.
|
||||
**
|
||||
** According to the rules above, all of the following SELECT statements
|
||||
** should return TRUE:
|
||||
**
|
||||
** SELECT sha3(1) = sha3('1');
|
||||
**
|
||||
** SELECT sha3('hello') = sha3(x'68656c6c6f');
|
||||
**
|
||||
** WITH a(x) AS (VALUES('xyzzy'))
|
||||
** SELECT sha3_agg(x) = sha3('T5:xyzzy') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(x'010203'))
|
||||
** SELECT sha3_agg(x) = sha3(x'42333a010203') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(0x123456))
|
||||
** SELECT sha3_agg(x) = sha3(x'490000000000123456') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(100.015625))
|
||||
** SELECT sha3_agg(x) = sha3(x'464059010000000000') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(NULL))
|
||||
** SELECT sha3_agg(x) = sha3('N') FROM a;
|
||||
**
|
||||
**
|
||||
** In sha3_query(), individual column values are encoded as with
|
||||
** sha3_agg(), but with the addition that a single "R" character is
|
||||
** inserted at the start of each row.
|
||||
**
|
||||
** Note that sha3_agg() hashes rows for which Y is NULL. Add a FILTER
|
||||
** clause if NULL rows should be excluded:
|
||||
**
|
||||
** SELECT sha3_agg(x ORDER BY rowid) FILTER(WHERE x NOT NULL) FROM t1;
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
@@ -75,6 +147,7 @@ struct SHA3Context {
|
||||
unsigned nRate; /* Bytes of input accepted per Keccak iteration */
|
||||
unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */
|
||||
unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */
|
||||
unsigned iSize; /* 224, 256, 358, or 512 */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -404,6 +477,7 @@ static void KeccakF1600Step(SHA3Context *p){
|
||||
*/
|
||||
static void SHA3Init(SHA3Context *p, int iSize){
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->iSize = iSize;
|
||||
if( iSize>=128 && iSize<=512 ){
|
||||
p->nRate = (1600 - ((iSize + 31)&~31)*2)/8;
|
||||
}else{
|
||||
@@ -547,6 +621,60 @@ static void sha3_step_vformat(
|
||||
SHA3Update(p, (unsigned char*)zBuf, n);
|
||||
}
|
||||
|
||||
/*
|
||||
** Update a SHA3Context using a single sqlite3_value.
|
||||
*/
|
||||
static void sha3UpdateFromValue(SHA3Context *p, sqlite3_value *pVal){
|
||||
switch( sqlite3_value_type(pVal) ){
|
||||
case SQLITE_NULL: {
|
||||
SHA3Update(p, (const unsigned char*)"N",1);
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
sqlite3_int64 v = sqlite3_value_int64(pVal);
|
||||
memcpy(&u, &v, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'I';
|
||||
SHA3Update(p, x, 9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
double r = sqlite3_value_double(pVal);
|
||||
memcpy(&u, &r, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'F';
|
||||
SHA3Update(p,x,9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n2 = sqlite3_value_bytes(pVal);
|
||||
const unsigned char *z2 = sqlite3_value_text(pVal);
|
||||
sha3_step_vformat(p,"T%d:",n2);
|
||||
SHA3Update(p, z2, n2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n2 = sqlite3_value_bytes(pVal);
|
||||
const unsigned char *z2 = sqlite3_value_blob(pVal);
|
||||
sha3_step_vformat(p,"B%d:",n2);
|
||||
SHA3Update(p, z2, n2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sha3_query(SQL,SIZE) function.
|
||||
**
|
||||
@@ -636,54 +764,7 @@ static void sha3QueryFunc(
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
SHA3Update(&cx,(const unsigned char*)"R",1);
|
||||
for(i=0; i<nCol; i++){
|
||||
switch( sqlite3_column_type(pStmt,i) ){
|
||||
case SQLITE_NULL: {
|
||||
SHA3Update(&cx, (const unsigned char*)"N",1);
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
sqlite3_int64 v = sqlite3_column_int64(pStmt,i);
|
||||
memcpy(&u, &v, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'I';
|
||||
SHA3Update(&cx, x, 9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
double r = sqlite3_column_double(pStmt,i);
|
||||
memcpy(&u, &r, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'F';
|
||||
SHA3Update(&cx,x,9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_text(pStmt, i);
|
||||
sha3_step_vformat(&cx,"T%d:",n2);
|
||||
SHA3Update(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
|
||||
sha3_step_vformat(&cx,"B%d:",n2);
|
||||
SHA3Update(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sha3UpdateFromValue(&cx, sqlite3_column_value(pStmt,i));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
@@ -691,6 +772,44 @@ static void sha3QueryFunc(
|
||||
sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** xStep function for sha3_agg().
|
||||
*/
|
||||
static void sha3AggStep(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
SHA3Context *p;
|
||||
p = (SHA3Context*)sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( p->nRate==0 ){
|
||||
int sz = 256;
|
||||
if( argc==2 ){
|
||||
sz = sqlite3_value_int(argv[1]);
|
||||
if( sz!=224 && sz!=384 && sz!=512 ){
|
||||
sz = 256;
|
||||
}
|
||||
}
|
||||
SHA3Init(p, sz);
|
||||
}
|
||||
sha3UpdateFromValue(p, argv[0]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xFinal function for sha3_agg().
|
||||
*/
|
||||
static void sha3AggFinal(sqlite3_context *context){
|
||||
SHA3Context *p;
|
||||
p = (SHA3Context*)sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( p->iSize ){
|
||||
sqlite3_result_blob(context, SHA3Final(p), p->iSize/8, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
@@ -711,6 +830,16 @@ int sqlite3_shathree_init(
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, sha3Func, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha3_agg", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, 0, sha3AggStep, sha3AggFinal);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha3_agg", 2,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, 0, sha3AggStep, sha3AggFinal);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha3_query", 1,
|
||||
SQLITE_UTF8 | SQLITE_DIRECTONLY,
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
** 2024-09-24
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** Implementation of standard I/O interfaces for UTF-8 that are missing
|
||||
** on Windows.
|
||||
*/
|
||||
#ifdef _WIN32 /* This file is a no-op on all platforms except Windows */
|
||||
#ifndef _SQLITE3_STDIO_H_
|
||||
#include "sqlite3_stdio.h"
|
||||
#endif
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "sqlite3.h"
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/*
|
||||
** If the SQLITE_U8TEXT_ONLY option is defined, then only use
|
||||
** _O_U8TEXT, _O_WTEXT, and similar together with the UTF-16
|
||||
** interfaces to the Windows CRT. The use of ANSI-only routines
|
||||
** like fputs() and ANSI modes like _O_TEXT and _O_BINARY is
|
||||
** avoided.
|
||||
**
|
||||
** The downside of using SQLITE_U8TEXT_ONLY is that it becomes
|
||||
** impossible to output a bare newline character (0x0a) - that is,
|
||||
** a newline that is not preceded by a carriage return (0x0d).
|
||||
** And without that capability, sometimes the output will be slightly
|
||||
** incorrect, as extra 0x0d characters will have been inserted where
|
||||
** they do not belong.
|
||||
**
|
||||
** The SQLITE_U8TEXT_STDIO compile-time option is a compromise.
|
||||
** It always enables _O_WTEXT or similar for stdin, stdout, stderr,
|
||||
** but allows other streams to be _O_TEXT and/or O_BINARY. The
|
||||
** SQLITE_U8TEXT_STDIO option has the same downside as SQLITE_U8TEXT_ONLY
|
||||
** in that stray 0x0d characters might appear where they ought not, but
|
||||
** at least with this option those characters only appear on standard
|
||||
** I/O streams, and not on new streams that might be created by the
|
||||
** application using sqlite3_fopen() or sqlite3_popen().
|
||||
*/
|
||||
#if defined(SQLITE_U8TEXT_ONLY)
|
||||
# define UseWtextForOutput(fd) 1
|
||||
# define UseWtextForInput(fd) 1
|
||||
# define IsConsole(fd) _isatty(_fileno(fd))
|
||||
#elif defined(SQLITE_U8TEXT_STDIO)
|
||||
# define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr)
|
||||
# define UseWtextForInput(fd) ((fd)==stdin)
|
||||
# define IsConsole(fd) _isatty(_fileno(fd))
|
||||
#else
|
||||
# define UseWtextForOutput(fd) _isatty(_fileno(fd))
|
||||
# define UseWtextForInput(fd) _isatty(_fileno(fd))
|
||||
# define IsConsole(fd) 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Work-alike for the fopen() routine from the standard C library.
|
||||
*/
|
||||
FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
|
||||
FILE *fp = 0;
|
||||
wchar_t *b1, *b2;
|
||||
int sz1, sz2;
|
||||
|
||||
sz1 = (int)strlen(zFilename);
|
||||
sz2 = (int)strlen(zMode);
|
||||
b1 = malloc( (sz1+1)*sizeof(b1[0]) );
|
||||
b2 = malloc( (sz2+1)*sizeof(b1[0]) );
|
||||
if( b1 && b2 ){
|
||||
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
|
||||
b1[sz1] = 0;
|
||||
sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
|
||||
b2[sz2] = 0;
|
||||
fp = _wfopen(b1, b2);
|
||||
}
|
||||
free(b1);
|
||||
free(b2);
|
||||
return fp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Work-alike for the popen() routine from the standard C library.
|
||||
*/
|
||||
FILE *sqlite3_popen(const char *zCommand, const char *zMode){
|
||||
FILE *fp = 0;
|
||||
wchar_t *b1, *b2;
|
||||
int sz1, sz2;
|
||||
|
||||
sz1 = (int)strlen(zCommand);
|
||||
sz2 = (int)strlen(zMode);
|
||||
b1 = malloc( (sz1+1)*sizeof(b1[0]) );
|
||||
b2 = malloc( (sz2+1)*sizeof(b1[0]) );
|
||||
if( b1 && b2 ){
|
||||
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
|
||||
b1[sz1] = 0;
|
||||
sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
|
||||
b2[sz2] = 0;
|
||||
fp = _wpopen(b1, b2);
|
||||
}
|
||||
free(b1);
|
||||
free(b2);
|
||||
return fp;
|
||||
}
|
||||
|
||||
/*
|
||||
** Work-alike for fgets() from the standard C library.
|
||||
*/
|
||||
char *sqlite3_fgets(char *buf, int sz, FILE *in){
|
||||
if( UseWtextForInput(in) ){
|
||||
/* When reading from the command-prompt in Windows, it is necessary
|
||||
** to use _O_WTEXT input mode to read UTF-16 characters, then translate
|
||||
** that into UTF-8. Otherwise, non-ASCII characters all get translated
|
||||
** into '?'.
|
||||
*/
|
||||
wchar_t *b1 = malloc( sz*sizeof(wchar_t) );
|
||||
if( b1==0 ) return 0;
|
||||
_setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT);
|
||||
if( fgetws(b1, sz/4, in)==0 ){
|
||||
sqlite3_free(b1);
|
||||
return 0;
|
||||
}
|
||||
WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0);
|
||||
sqlite3_free(b1);
|
||||
return buf;
|
||||
}else{
|
||||
/* Reading from a file or other input source, just read bytes without
|
||||
** any translation. */
|
||||
return fgets(buf, sz, in);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Work-alike for fputs() from the standard C library.
|
||||
*/
|
||||
int sqlite3_fputs(const char *z, FILE *out){
|
||||
if( UseWtextForOutput(out) ){
|
||||
/* When writing to the command-prompt in Windows, it is necessary
|
||||
** to use _O_WTEXT input mode and write UTF-16 characters.
|
||||
*/
|
||||
int sz = (int)strlen(z);
|
||||
wchar_t *b1 = malloc( (sz+1)*sizeof(wchar_t) );
|
||||
if( b1==0 ) return 0;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
_setmode(_fileno(out), _O_U8TEXT);
|
||||
fputws(b1, out);
|
||||
sqlite3_free(b1);
|
||||
return 0;
|
||||
}else{
|
||||
/* Writing to a file or other destination, just write bytes without
|
||||
** any translation. */
|
||||
return fputs(z, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Work-alike for fprintf() from the standard C library.
|
||||
*/
|
||||
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
int rc;
|
||||
if( UseWtextForOutput(out) ){
|
||||
/* When writing to the command-prompt in Windows, it is necessary
|
||||
** to use _O_WTEXT input mode and write UTF-16 characters.
|
||||
*/
|
||||
char *z;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
z = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
sqlite3_fputs(z, out);
|
||||
rc = (int)strlen(z);
|
||||
sqlite3_free(z);
|
||||
}else{
|
||||
/* Writing to a file or other destination, just write bytes without
|
||||
** any translation. */
|
||||
va_list ap;
|
||||
va_start(ap, zFormat);
|
||||
rc = vfprintf(out, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the mode for an output stream. mode argument is typically _O_BINARY or
|
||||
** _O_TEXT.
|
||||
*/
|
||||
void sqlite3_fsetmode(FILE *fp, int mode){
|
||||
if( !UseWtextForOutput(fp) ){
|
||||
fflush(fp);
|
||||
_setmode(_fileno(fp), mode);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(_WIN32) */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
** 2024-09-24
|
||||
**
|
||||
** 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 header file contains definitions of interfaces that provide
|
||||
** cross-platform I/O for UTF-8 content.
|
||||
**
|
||||
** On most platforms, the interfaces definitions in this file are
|
||||
** just #defines. For example sqlite3_fopen() is a macro that resolves
|
||||
** to the standard fopen() in the C-library.
|
||||
**
|
||||
** But Windows does not have a standard C-library, at least not one that
|
||||
** can handle UTF-8. So for windows build, the interfaces resolve to new
|
||||
** C-language routines contained in the separate sqlite3_stdio.c source file.
|
||||
**
|
||||
** So on all non-Windows platforms, simply #include this header file and
|
||||
** use the interfaces defined herein. Then to run your application on Windows,
|
||||
** also link in the accompanying sqlite3_stdio.c source file when compiling
|
||||
** to get compatible interfaces.
|
||||
*/
|
||||
#ifndef _SQLITE3_STDIO_H_
|
||||
#define _SQLITE3_STDIO_H_ 1
|
||||
#ifdef _WIN32
|
||||
/**** Definitions For Windows ****/
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
|
||||
FILE *sqlite3_popen(const char *zCommand, const char *type);
|
||||
char *sqlite3_fgets(char *s, int size, FILE *stream);
|
||||
int sqlite3_fputs(const char *s, FILE *stream);
|
||||
int sqlite3_fprintf(FILE *stream, const char *format, ...);
|
||||
void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
|
||||
|
||||
#else
|
||||
/**** Definitions For All Other Platforms ****/
|
||||
#include <stdio.h>
|
||||
#define sqlite3_fopen fopen
|
||||
#define sqlite3_popen popen
|
||||
#define sqlite3_fgets fgets
|
||||
#define sqlite3_fputs fputs
|
||||
#define sqlite3_fprintf fprintf
|
||||
#define sqlite3_fsetmode(F,X) /*no-op*/
|
||||
|
||||
#endif
|
||||
#endif /* _SQLITE3_STDIO_H_ */
|
||||
@@ -182,7 +182,7 @@ static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName);
|
||||
** xyzzy.txt -> xyzzy.txt
|
||||
*/
|
||||
static const char *fileTail(const char *z){
|
||||
int i;
|
||||
size_t i;
|
||||
if( z==0 ) return 0;
|
||||
i = strlen(z)-1;
|
||||
while( i>0 && z[i-1]!='/' ){ i--; }
|
||||
@@ -207,36 +207,34 @@ static void vfstrace_printf(
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert value rc into a string and print it using zFormat. zFormat
|
||||
** should have exactly one %s
|
||||
** Try to convert an error code into a symbolic name for that error code.
|
||||
*/
|
||||
static void vfstrace_print_errcode(
|
||||
vfstrace_info *pInfo,
|
||||
const char *zFormat,
|
||||
int rc
|
||||
){
|
||||
char zBuf[50];
|
||||
char *zVal;
|
||||
static const char *vfstrace_errcode_name(int rc ){
|
||||
const char *zVal = 0;
|
||||
switch( rc ){
|
||||
case SQLITE_OK: zVal = "SQLITE_OK"; break;
|
||||
case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break;
|
||||
case SQLITE_PERM: zVal = "SQLITE_PERM"; break;
|
||||
case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break;
|
||||
case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break;
|
||||
case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break;
|
||||
case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break;
|
||||
case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break;
|
||||
case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break;
|
||||
case SQLITE_FULL: zVal = "SQLITE_FULL"; break;
|
||||
case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break;
|
||||
case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break;
|
||||
case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break;
|
||||
case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break;
|
||||
case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break;
|
||||
case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break;
|
||||
case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break;
|
||||
case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break;
|
||||
case SQLITE_OK: zVal = "SQLITE_OK"; break;
|
||||
case SQLITE_INTERNAL: zVal = "SQLITE_INTERNAL"; break;
|
||||
case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break;
|
||||
case SQLITE_PERM: zVal = "SQLITE_PERM"; break;
|
||||
case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break;
|
||||
case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break;
|
||||
case SQLITE_LOCKED: zVal = "SQLITE_LOCKED"; break;
|
||||
case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break;
|
||||
case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break;
|
||||
case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break;
|
||||
case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break;
|
||||
case SQLITE_NOTFOUND: zVal = "SQLITE_NOTFOUND"; break;
|
||||
case SQLITE_FULL: zVal = "SQLITE_FULL"; break;
|
||||
case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break;
|
||||
case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break;
|
||||
case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break;
|
||||
case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break;
|
||||
case SQLITE_TOOBIG: zVal = "SQLITE_TOOBIG"; break;
|
||||
case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break;
|
||||
case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break;
|
||||
case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break;
|
||||
case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break;
|
||||
case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break;
|
||||
case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break;
|
||||
case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break;
|
||||
@@ -266,11 +264,30 @@ static void vfstrace_print_errcode(
|
||||
case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
|
||||
case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break;
|
||||
case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
|
||||
default: {
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc);
|
||||
zVal = zBuf;
|
||||
break;
|
||||
}
|
||||
return zVal;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert value rc into a string and print it using zFormat. zFormat
|
||||
** should have exactly one %s
|
||||
*/
|
||||
static void vfstrace_print_errcode(
|
||||
vfstrace_info *pInfo,
|
||||
const char *zFormat,
|
||||
int rc
|
||||
){
|
||||
const char *zVal;
|
||||
char zBuf[50];
|
||||
zVal = vfstrace_errcode_name(rc);
|
||||
if( zVal==0 ){
|
||||
zVal = vfstrace_errcode_name(rc&0xff);
|
||||
if( zVal ){
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%s | 0x%x", zVal, rc&0xffff00);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d (0x%x)", rc, rc);
|
||||
}
|
||||
zVal = zBuf;
|
||||
}
|
||||
vfstrace_printf(pInfo, zFormat, zVal);
|
||||
}
|
||||
@@ -399,7 +416,7 @@ static const char *lockName(int eLock){
|
||||
const char *azLockNames[] = {
|
||||
"NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE"
|
||||
};
|
||||
if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){
|
||||
if( eLock<0 || eLock>=(int)(sizeof(azLockNames)/sizeof(azLockNames[0])) ){
|
||||
return "???";
|
||||
}else{
|
||||
return azLockNames[eLock];
|
||||
@@ -457,12 +474,14 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
vfstrace_info *pInfo = p->pInfo;
|
||||
int rc;
|
||||
char zBuf[100];
|
||||
char zBuf2[100];
|
||||
char *zOp;
|
||||
char *zRVal = 0;
|
||||
switch( op ){
|
||||
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
|
||||
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
|
||||
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
|
||||
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
|
||||
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
|
||||
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
|
||||
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
|
||||
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
|
||||
case SQLITE_FCNTL_SIZE_HINT: {
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld",
|
||||
*(sqlite3_int64*)pArg);
|
||||
@@ -474,20 +493,62 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
zOp = zBuf;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
|
||||
case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break;
|
||||
case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break;
|
||||
case SQLITE_FCNTL_PERSIST_WAL: zOp = "PERSIST_WAL"; break;
|
||||
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
|
||||
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
|
||||
case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break;
|
||||
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
|
||||
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
|
||||
case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break;
|
||||
case SQLITE_FCNTL_PERSIST_WAL: {
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "PERSIST_WAL,%d", *(int*)pArg);
|
||||
zOp = zBuf;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
|
||||
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
|
||||
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
|
||||
case SQLITE_FCNTL_PRAGMA: {
|
||||
const char *const* a = (const char*const*)pArg;
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
|
||||
zOp = zBuf;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
|
||||
case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break;
|
||||
case SQLITE_FCNTL_MMAP_SIZE: {
|
||||
sqlite3_int64 iMMap = *(sqlite3_int64*)pArg;
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "MMAP_SIZE,%lld",iMMap);
|
||||
zOp = zBuf;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_TRACE: zOp = "TRACE"; break;
|
||||
case SQLITE_FCNTL_HAS_MOVED: zOp = "HAS_MOVED"; break;
|
||||
case SQLITE_FCNTL_SYNC: zOp = "SYNC"; break;
|
||||
case SQLITE_FCNTL_COMMIT_PHASETWO: zOp = "COMMIT_PHASETWO"; break;
|
||||
case SQLITE_FCNTL_WIN32_SET_HANDLE: zOp = "WIN32_SET_HANDLE"; break;
|
||||
case SQLITE_FCNTL_WAL_BLOCK: zOp = "WAL_BLOCK"; break;
|
||||
case SQLITE_FCNTL_ZIPVFS: zOp = "ZIPVFS"; break;
|
||||
case SQLITE_FCNTL_RBU: zOp = "RBU"; break;
|
||||
case SQLITE_FCNTL_VFS_POINTER: zOp = "VFS_POINTER"; break;
|
||||
case SQLITE_FCNTL_JOURNAL_POINTER: zOp = "JOURNAL_POINTER"; break;
|
||||
case SQLITE_FCNTL_WIN32_GET_HANDLE: zOp = "WIN32_GET_HANDLE"; break;
|
||||
case SQLITE_FCNTL_PDB: zOp = "PDB"; break;
|
||||
case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: zOp = "BEGIN_ATOMIC_WRITE"; break;
|
||||
case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: zOp = "COMMIT_ATOMIC_WRITE"; break;
|
||||
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
|
||||
zOp = "ROLLBACK_ATOMIC_WRITE";
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_LOCK_TIMEOUT: {
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "LOCK_TIMEOUT,%d", *(int*)pArg);
|
||||
zOp = zBuf;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_DATA_VERSION: zOp = "DATA_VERSION"; break;
|
||||
case SQLITE_FCNTL_SIZE_LIMIT: zOp = "SIZE_LIMIT"; break;
|
||||
case SQLITE_FCNTL_CKPT_DONE: zOp = "CKPT_DONE"; break;
|
||||
case SQLITE_FCNTL_RESERVE_BYTES: zOp = "RESERVED_BYTES"; break;
|
||||
case SQLITE_FCNTL_CKPT_START: zOp = "CKPT_START"; break;
|
||||
case SQLITE_FCNTL_EXTERNAL_READER: zOp = "EXTERNAL_READER"; break;
|
||||
case SQLITE_FCNTL_CKSM_FILE: zOp = "CKSM_FILE"; break;
|
||||
case SQLITE_FCNTL_RESET_CACHE: zOp = "RESET_CACHE"; break;
|
||||
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
|
||||
default: {
|
||||
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
|
||||
zOp = zBuf;
|
||||
@@ -497,15 +558,37 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)",
|
||||
pInfo->zVfsName, p->zFName, zOp);
|
||||
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||||
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
|
||||
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
|
||||
if( rc==SQLITE_OK ){
|
||||
switch( op ){
|
||||
case SQLITE_FCNTL_VFSNAME: {
|
||||
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
|
||||
pInfo->zVfsName, *(char**)pArg);
|
||||
zRVal = *(char**)pArg;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_MMAP_SIZE: {
|
||||
sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%lld", *(sqlite3_int64*)pArg);
|
||||
zRVal = zBuf2;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_HAS_MOVED:
|
||||
case SQLITE_FCNTL_PERSIST_WAL: {
|
||||
sqlite3_snprintf(sizeof(zBuf2), zBuf2, "%d", *(int*)pArg);
|
||||
zRVal = zBuf2;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FCNTL_PRAGMA:
|
||||
case SQLITE_FCNTL_TEMPFILENAME: {
|
||||
zRVal = *(char**)pArg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( (op==SQLITE_FCNTL_PRAGMA || op==SQLITE_FCNTL_TEMPFILENAME)
|
||||
&& rc==SQLITE_OK && *(char**)pArg ){
|
||||
vfstrace_printf(pInfo, "%s.xFileControl(%s,%s) returns %s",
|
||||
pInfo->zVfsName, p->zFName, zOp, *(char**)pArg);
|
||||
if( zRVal ){
|
||||
vfstrace_print_errcode(pInfo, " -> %s", rc);
|
||||
vfstrace_printf(pInfo, ", %s\n", zRVal);
|
||||
}else{
|
||||
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -840,14 +923,14 @@ int vfstrace_register(
|
||||
sqlite3_vfs *pNew;
|
||||
sqlite3_vfs *pRoot;
|
||||
vfstrace_info *pInfo;
|
||||
int nName;
|
||||
int nByte;
|
||||
size_t nName;
|
||||
size_t nByte;
|
||||
|
||||
pRoot = sqlite3_vfs_find(zOldVfsName);
|
||||
if( pRoot==0 ) return SQLITE_NOTFOUND;
|
||||
nName = strlen(zTraceName);
|
||||
nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1;
|
||||
pNew = sqlite3_malloc( nByte );
|
||||
pNew = sqlite3_malloc64( nByte );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, nByte);
|
||||
pInfo = (vfstrace_info*)&pNew[1];
|
||||
@@ -890,3 +973,15 @@ int vfstrace_register(
|
||||
pInfo->zVfsName, pRoot->zName);
|
||||
return sqlite3_vfs_register(pNew, makeDefault);
|
||||
}
|
||||
|
||||
/*
|
||||
** Look for the named VFS. If it is a TRACEVFS, then unregister it
|
||||
** and delete it.
|
||||
*/
|
||||
void vfstrace_unregister(const char *zTraceName){
|
||||
sqlite3_vfs *pVfs = sqlite3_vfs_find(zTraceName);
|
||||
if( pVfs==0 ) return;
|
||||
if( pVfs->xOpen!=vfstraceOpen ) return;
|
||||
sqlite3_vfs_unregister(pVfs);
|
||||
sqlite3_free(pVfs);
|
||||
}
|
||||
+10
-2
@@ -35,6 +35,14 @@ SQLITE_EXTENSION_INIT1
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
/* When used as part of the CLI, the sqlite3_stdio.h module will have
|
||||
** been included before this one. In that case use the sqlite3_stdio.h
|
||||
** #defines. If not, create our own for fopen().
|
||||
*/
|
||||
#ifndef _SQLITE3_STDIO_H_
|
||||
# define sqlite3_fopen fopen
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
@@ -1291,7 +1299,7 @@ static int zipfileFilter(
|
||||
}
|
||||
|
||||
if( 0==pTab->pWriteFd && 0==bInMemory ){
|
||||
pCsr->pFile = zFile ? fopen(zFile, "rb") : 0;
|
||||
pCsr->pFile = zFile ? sqlite3_fopen(zFile, "rb") : 0;
|
||||
if( pCsr->pFile==0 ){
|
||||
zipfileCursorErr(pCsr, "cannot open file: %s", zFile);
|
||||
rc = SQLITE_ERROR;
|
||||
@@ -1481,7 +1489,7 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
|
||||
** structure into memory. During the transaction any new file data is
|
||||
** appended to the archive file, but the central directory is accumulated
|
||||
** in main-memory until the transaction is committed. */
|
||||
pTab->pWriteFd = fopen(pTab->zFile, "ab+");
|
||||
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+");
|
||||
if( pTab->pWriteFd==0 ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf(
|
||||
"zipfile: failed to open file %s for writing", pTab->zFile
|
||||
|
||||
+35
-6
@@ -336,6 +336,27 @@ struct RbuFrame {
|
||||
u32 iWalFrame;
|
||||
};
|
||||
|
||||
#ifndef UNUSED_PARAMETER
|
||||
/*
|
||||
** The following macros are used to suppress compiler warnings and to
|
||||
** make it clear to human readers when a function parameter is deliberately
|
||||
** left unused within the body of a function. This usually happens when
|
||||
** a function is called via a function pointer. For example the
|
||||
** implementation of an SQL aggregate step callback may not use the
|
||||
** parameter indicating the number of arguments passed to the aggregate,
|
||||
** if it knows that this is enforced elsewhere.
|
||||
**
|
||||
** When a function parameter is not used at all within the body of a function,
|
||||
** it is generally named "NotUsed" or "NotUsed2" to make things even clearer.
|
||||
** However, these macros may also be used to suppress warnings related to
|
||||
** parameters that may or may not be used depending on compilation options.
|
||||
** For example those parameters only used in assert() statements. In these
|
||||
** cases the parameters are named as per the usual conventions.
|
||||
*/
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** RBU handle.
|
||||
**
|
||||
@@ -387,7 +408,7 @@ struct sqlite3rbu {
|
||||
int rc; /* Value returned by last rbu_step() call */
|
||||
char *zErrmsg; /* Error message if rc!=SQLITE_OK */
|
||||
int nStep; /* Rows processed for current object */
|
||||
int nProgress; /* Rows processed for all objects */
|
||||
sqlite3_int64 nProgress; /* Rows processed for all objects */
|
||||
RbuObjIter objiter; /* Iterator for skipping through tbl/idx */
|
||||
const char *zVfsName; /* Name of automatically created rbu vfs */
|
||||
rbu_file *pTargetFd; /* File handle open on target db */
|
||||
@@ -504,7 +525,7 @@ static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){
|
||||
v = (v<<6) + c;
|
||||
}
|
||||
z--;
|
||||
*pLen -= z - zStart;
|
||||
*pLen -= (int)(z - zStart);
|
||||
*pz = (char*)z;
|
||||
return v;
|
||||
}
|
||||
@@ -689,6 +710,7 @@ static void rbuFossilDeltaFunc(
|
||||
char *aOut;
|
||||
|
||||
assert( argc==2 );
|
||||
UNUSED_PARAMETER(argc);
|
||||
|
||||
nOrig = sqlite3_value_bytes(argv[0]);
|
||||
aOrig = (const char*)sqlite3_value_blob(argv[0]);
|
||||
@@ -2268,13 +2290,13 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
else if( c==')' ){
|
||||
nParen--;
|
||||
if( nParen==0 ){
|
||||
int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
|
||||
int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan);
|
||||
pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}else if( c==',' && nParen==1 ){
|
||||
int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
|
||||
int nSpan = (int)(&zSql[i] - pIter->aIdxCol[iIdxCol].zSpan);
|
||||
pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
|
||||
pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1];
|
||||
}else if( c=='"' || c=='\'' || c=='`' ){
|
||||
@@ -2964,6 +2986,8 @@ static void rbuFileSuffix3(const char *zBase, char *z){
|
||||
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
|
||||
if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4);
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER2(zBase,z);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3548,7 +3572,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
|
||||
"(%d, %Q), "
|
||||
"(%d, %Q), "
|
||||
"(%d, %d), "
|
||||
"(%d, %d), "
|
||||
"(%d, %lld), "
|
||||
"(%d, %lld), "
|
||||
"(%d, %lld), "
|
||||
"(%d, %lld), "
|
||||
@@ -3906,6 +3930,7 @@ static void rbuIndexCntFunc(
|
||||
sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
|
||||
|
||||
assert( nVal==1 );
|
||||
UNUSED_PARAMETER(nVal);
|
||||
|
||||
rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
|
||||
sqlite3_mprintf("SELECT count(*) FROM sqlite_schema "
|
||||
@@ -4181,7 +4206,7 @@ sqlite3rbu *sqlite3rbu_vacuum(
|
||||
){
|
||||
if( zTarget==0 ){ return rbuMisuseError(); }
|
||||
if( zState ){
|
||||
int n = strlen(zState);
|
||||
size_t n = strlen(zState);
|
||||
if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){
|
||||
return rbuMisuseError();
|
||||
}
|
||||
@@ -4398,6 +4423,7 @@ int sqlite3rbu_savestate(sqlite3rbu *p){
|
||||
*/
|
||||
static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
|
||||
int rc = SQLITE_OK;
|
||||
UNUSED_PARAMETER(pArg);
|
||||
#if defined(_WIN32_WCE)
|
||||
{
|
||||
LPWSTR zWideOld;
|
||||
@@ -5302,6 +5328,9 @@ static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
** No-op.
|
||||
*/
|
||||
static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
UNUSED_PARAMETER(a);
|
||||
UNUSED_PARAMETER(b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-13
@@ -17,14 +17,7 @@
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU)
|
||||
|
||||
#include "sqlite3rbu.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -432,11 +425,7 @@ int SqliteRbu_Init(Tcl_Interp *interp){
|
||||
}
|
||||
|
||||
#else
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
int SqliteRbu_Init(Tcl_Interp *interp){ return TCL_OK; }
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
|
||||
#endif /* defined(SQLITE_TEST) */
|
||||
|
||||
@@ -679,6 +679,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
** near the end of a corrupt record. */
|
||||
rc = dbdataBufferSize(&pCsr->rec, nPayload+DBDATA_PADDING_BYTES);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
assert( pCsr->rec.aBuf!=0 );
|
||||
assert( nPayload!=0 );
|
||||
|
||||
/* Load the nLocal bytes of payload */
|
||||
|
||||
@@ -34,11 +34,11 @@ if {[permutation]!="inmemory_journal"} {
|
||||
do_test 1.1 {
|
||||
set sz [expr [file size test.db] - 1024]
|
||||
set fd [open test.db]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
fconfigure $fd -translation binary
|
||||
|
||||
set data [read $fd $sz]
|
||||
set fd2 [open test.db2 w]
|
||||
fconfigure $fd2 -encoding binary -translation binary
|
||||
fconfigure $fd2 -translation binary
|
||||
puts -nonewline $fd2 $data
|
||||
close $fd2
|
||||
set {} {}
|
||||
@@ -61,4 +61,3 @@ if {[permutation]!="inmemory_journal"} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -39,14 +39,14 @@ foreach {pgsz bOverflow} {
|
||||
|
||||
|
||||
set fd [open test.db]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
fconfigure $fd -translation binary
|
||||
seek $fd $pgsz
|
||||
set pg1 [read $fd $pgsz]
|
||||
set pg2 [read $fd $pgsz]
|
||||
close $fd
|
||||
|
||||
set fd2 [open test.db2 w]
|
||||
fconfigure $fd2 -encoding binary -translation binary
|
||||
fconfigure $fd2 -translation binary
|
||||
seek $fd2 $pgsz
|
||||
puts -nonewline $fd2 $pg1
|
||||
close $fd2
|
||||
@@ -71,7 +71,7 @@ foreach {pgsz bOverflow} {
|
||||
forcedelete test.db2
|
||||
|
||||
set fd2 [open test.db2 w]
|
||||
fconfigure $fd2 -encoding binary -translation binary
|
||||
fconfigure $fd2 -translation binary
|
||||
seek $fd2 $pgsz
|
||||
puts -nonewline $fd2 $pg2
|
||||
close $fd2
|
||||
@@ -95,6 +95,3 @@ foreach {pgsz bOverflow} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -363,8 +363,8 @@ static int recoverError(
|
||||
va_start(ap, zFmt);
|
||||
if( zFmt ){
|
||||
z = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
va_end(ap);
|
||||
sqlite3_free(p->zErrMsg);
|
||||
p->zErrMsg = z;
|
||||
p->errCode = errCode;
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
|
||||
#include "sqlite3recover.h"
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#include <tcl.h>
|
||||
#include "tclsqlite.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
@@ -308,4 +307,3 @@ int TestRecover_Init(Tcl_Interp *interp){
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
|
||||
/* Solely for the UNUSED_PARAMETER() macro. */
|
||||
#include "sqliteInt.h"
|
||||
@@ -90,7 +86,7 @@ static int invokeTclGeomCb(
|
||||
if( rc!=TCL_OK ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
int nObj = 0;
|
||||
Tcl_Size nObj = 0;
|
||||
Tcl_Obj **aObj = 0;
|
||||
|
||||
pRes = Tcl_GetObjResult(interp);
|
||||
@@ -279,7 +275,7 @@ static int box_query(sqlite3_rtree_query_info *pInfo){
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
double rScore = 0.0;
|
||||
int nObj = 0;
|
||||
Tcl_Size nObj = 0;
|
||||
int eP = 0;
|
||||
Tcl_Obj **aObj = 0;
|
||||
Tcl_Obj *pRes = Tcl_GetObjResult(interp);
|
||||
|
||||
@@ -27,7 +27,7 @@ if {$CF==""} {
|
||||
|
||||
proc writefile {zFile data} {
|
||||
set fd [open $zFile w]
|
||||
fconfigure $fd -translation binary -encoding binary
|
||||
fconfigure $fd -translation binary
|
||||
puts -nonewline $fd $data
|
||||
close $fd
|
||||
}
|
||||
@@ -81,4 +81,3 @@ do_test 1.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
# This file implements regression tests for the session module.
|
||||
#
|
||||
|
||||
package require Tcl 8.6
|
||||
if {$tcl_version<8.6} {
|
||||
puts "This module requires Tcl 8.6 or later"
|
||||
return
|
||||
}
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
@@ -136,7 +139,7 @@ foreach {tn blob} {
|
||||
do_test 2.$tn {
|
||||
set changeset [binary decode hex $blob]
|
||||
#set fd [open x.change w+]
|
||||
#fconfigure $fd -encoding binary -translation binary
|
||||
#fconfigure $fd -translation binary
|
||||
#puts -nonewline $fd $changeset
|
||||
#close $fd
|
||||
list [catch { sqlite3changeset_apply db $changeset xConflict } msg] $msg
|
||||
|
||||
@@ -202,6 +202,7 @@ foreach {tn sql1 at sql2} {
|
||||
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
breakpoint
|
||||
grp add $C1
|
||||
grp add $C2
|
||||
set T1 [grp output]
|
||||
|
||||
@@ -47,7 +47,7 @@ proc database_cksum {db1} {
|
||||
|
||||
proc readfile {filename} {
|
||||
set fd [open $filename]
|
||||
fconfigure $fd -translation binary -encoding binary
|
||||
fconfigure $fd -translation binary
|
||||
set data [read $fd]
|
||||
close $fd
|
||||
set data
|
||||
|
||||
@@ -367,6 +367,7 @@ do_execsql_test 7.prep1 {
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
unset -nocomplain res
|
||||
set res [list]
|
||||
for {set ::i 0} {$::i < 480} {incr ::i 4} {
|
||||
lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
|
||||
|
||||
@@ -56,4 +56,28 @@ do_faultsim_test 1 -faults oom* -prep {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
ALTER TABLE t1 ADD COLUMN c DEFAULT 'abcdefghijklmnopqrstuvwxyz';
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 2 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval {SELECT * FROM sqlite_schema}
|
||||
} -body {
|
||||
sqlite3session S db main
|
||||
S attach *
|
||||
execsql {
|
||||
DELETE FROM t1 WHERE a = 1;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
catch { S delete }
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -82,6 +82,7 @@ do_execsql_test 1.5 {
|
||||
UPDATE p1 SET c=12345 WHERE a = 45;
|
||||
}
|
||||
|
||||
breakpoint
|
||||
sqlite3changeset_apply_v2 -noaction db $C conflict
|
||||
do_execsql_test 1.6 {
|
||||
SELECT * FROM c1
|
||||
|
||||
@@ -74,6 +74,10 @@ struct SessionBuffer {
|
||||
** input data. Input data may be supplied either as a single large buffer
|
||||
** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
|
||||
** sqlite3changeset_start_strm()).
|
||||
**
|
||||
** bNoDiscard:
|
||||
** If true, then the only time data is discarded is as a result of explicit
|
||||
** sessionDiscardData() calls. Not within every sessionInputBuffer() call.
|
||||
*/
|
||||
struct SessionInput {
|
||||
int bNoDiscard; /* If true, do not discard in InputBuffer() */
|
||||
@@ -1757,16 +1761,19 @@ static void sessionPreupdateOneChange(
|
||||
for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
|
||||
sqlite3_value *p = 0;
|
||||
if( op!=SQLITE_INSERT ){
|
||||
TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
|
||||
assert( trc==SQLITE_OK );
|
||||
/* This may fail if the column has a non-NULL default and was added
|
||||
** using ALTER TABLE ADD COLUMN after this record was created. */
|
||||
rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p);
|
||||
}else if( pTab->abPK[i] ){
|
||||
TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
|
||||
assert( trc==SQLITE_OK );
|
||||
}
|
||||
|
||||
/* This may fail if SQLite value p contains a utf-16 string that must
|
||||
** be converted to utf-8 and an OOM error occurs while doing so. */
|
||||
rc = sessionSerializeValue(0, p, &nByte);
|
||||
if( rc==SQLITE_OK ){
|
||||
/* This may fail if SQLite value p contains a utf-16 string that must
|
||||
** be converted to utf-8 and an OOM error occurs while doing so. */
|
||||
rc = sessionSerializeValue(0, p, &nByte);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto error_out;
|
||||
}
|
||||
if( pTab->bRowid ){
|
||||
@@ -5124,15 +5131,21 @@ static int sessionChangesetApply(
|
||||
int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
|
||||
SessionApplyCtx sApply; /* changeset_apply() context object */
|
||||
int bPatchset;
|
||||
u64 savedFlag = db->flags & SQLITE_FkNoAction;
|
||||
|
||||
assert( xConflict!=0 );
|
||||
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
|
||||
db->flags |= ((u64)SQLITE_FkNoAction);
|
||||
db->aDb[0].pSchema->schema_cookie -= 32;
|
||||
}
|
||||
|
||||
pIter->in.bNoDiscard = 1;
|
||||
memset(&sApply, 0, sizeof(sApply));
|
||||
sApply.bRebase = (ppRebase && pnRebase);
|
||||
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
|
||||
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
|
||||
}
|
||||
@@ -5294,6 +5307,12 @@ static int sessionChangesetApply(
|
||||
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
|
||||
sqlite3_free((char*)sApply.constraints.aBuf);
|
||||
sqlite3_free((char*)sApply.rebase.aBuf);
|
||||
|
||||
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
|
||||
assert( db->flags & SQLITE_FkNoAction );
|
||||
db->flags &= ~((u64)SQLITE_FkNoAction);
|
||||
db->aDb[0].pSchema->schema_cookie -= 32;
|
||||
}
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
return rc;
|
||||
}
|
||||
@@ -5322,12 +5341,6 @@ int sqlite3changeset_apply_v2(
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
|
||||
u64 savedFlag = db->flags & SQLITE_FkNoAction;
|
||||
|
||||
if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
|
||||
db->flags |= ((u64)SQLITE_FkNoAction);
|
||||
db->aDb[0].pSchema->schema_cookie -= 32;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
@@ -5335,11 +5348,6 @@ int sqlite3changeset_apply_v2(
|
||||
);
|
||||
}
|
||||
|
||||
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
|
||||
assert( db->flags & SQLITE_FkNoAction );
|
||||
db->flags &= ~((u64)SQLITE_FkNoAction);
|
||||
db->aDb[0].pSchema->schema_cookie -= 32;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -5660,6 +5668,9 @@ static int sessionChangesetExtendRecord(
|
||||
sessionAppendBlob(pOut, aRec, nRec, &rc);
|
||||
if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){
|
||||
rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt);
|
||||
if( rc==SQLITE_OK && SQLITE_ROW!=sqlite3_step(pTab->pDfltStmt) ){
|
||||
rc = sqlite3_errcode(pGrp->db);
|
||||
}
|
||||
}
|
||||
for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){
|
||||
int eType = sqlite3_column_type(pTab->pDfltStmt, ii);
|
||||
@@ -5676,6 +5687,7 @@ static int sessionChangesetExtendRecord(
|
||||
}
|
||||
if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
|
||||
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
|
||||
pOut->nBuf += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -5815,6 +5827,8 @@ static int sessionOneChangeToHash(
|
||||
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
|
||||
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
|
||||
|
||||
assert( nRec>0 );
|
||||
|
||||
/* Ensure that only changesets, or only patchsets, but not a mixture
|
||||
** of both, are being combined. It is an error to try to combine a
|
||||
** changeset and a patchset. */
|
||||
@@ -5892,6 +5906,7 @@ static int sessionChangesetToHash(
|
||||
int nRec;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
pIter->in.bNoDiscard = 1;
|
||||
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
|
||||
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
+41
-40
@@ -5,14 +5,7 @@
|
||||
#include "sqlite3session.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#if defined(INCLUDE_SQLITE_TCL_H)
|
||||
# include "sqlite_tcl.h"
|
||||
#else
|
||||
# include "tcl.h"
|
||||
# ifndef SQLITE_TCLAPI
|
||||
# define SQLITE_TCLAPI
|
||||
# endif
|
||||
#endif
|
||||
#include "tclsqlite.h"
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
typedef unsigned char u8;
|
||||
@@ -390,10 +383,10 @@ static int SQLITE_TCLAPI test_session_cmd(
|
||||
{ "rowid", SQLITE_SESSION_OBJCONFIG_ROWID },
|
||||
{ 0, 0 }
|
||||
};
|
||||
size_t sz = sizeof(aOpt[0]);
|
||||
int sz = (int)sizeof(aOpt[0]);
|
||||
|
||||
int iArg;
|
||||
int iOpt;
|
||||
Tcl_Size iOpt;
|
||||
if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -517,11 +510,11 @@ struct TestConflictHandler {
|
||||
};
|
||||
|
||||
static int test_obj_eq_string(Tcl_Obj *p, const char *z){
|
||||
int n;
|
||||
int nObj;
|
||||
Tcl_Size n;
|
||||
Tcl_Size nObj;
|
||||
char *zObj;
|
||||
|
||||
n = (int)strlen(z);
|
||||
n = (Tcl_Size)strlen(z);
|
||||
zObj = Tcl_GetStringFromObj(p, &nObj);
|
||||
|
||||
return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
|
||||
@@ -796,7 +789,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
|
||||
int rc; /* Return code from changeset_invert() */
|
||||
void *pChangeset; /* Buffer containing changeset */
|
||||
int nChangeset; /* Size of buffer aChangeset in bytes */
|
||||
Tcl_Size nChangeset; /* Size of buffer aChangeset in bytes */
|
||||
TestConflictHandler ctx;
|
||||
TestStreamInput sStr;
|
||||
void *pRebase = 0;
|
||||
@@ -810,7 +803,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
if( bV2 ){
|
||||
while( objc>1 ){
|
||||
const char *z1 = Tcl_GetString(objv[1]);
|
||||
int n = strlen(z1);
|
||||
int n = (int)strlen(z1);
|
||||
if( n>3 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
|
||||
flags |= SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
|
||||
}
|
||||
@@ -853,18 +846,18 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
|
||||
if( sStr.nStream==0 ){
|
||||
if( bV2==0 ){
|
||||
rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
|
||||
rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
}else{
|
||||
rc = sqlite3changeset_apply_v2(db, nChangeset, pChangeset,
|
||||
rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
}
|
||||
}else{
|
||||
sStr.aData = (unsigned char*)pChangeset;
|
||||
sStr.nData = nChangeset;
|
||||
sStr.nData = (int)nChangeset;
|
||||
if( bV2==0 ){
|
||||
rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
@@ -927,7 +920,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all(
|
||||
Tcl_CmdInfo info; /* Database Tcl command (objv[1]) info */
|
||||
int rc; /* Return code from changeset_invert() */
|
||||
void *pChangeset; /* Buffer containing changeset */
|
||||
int nChangeset; /* Size of buffer aChangeset in bytes */
|
||||
Tcl_Size nChangeset; /* Size of buffer aChangeset in bytes */
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB CHANGESET");
|
||||
@@ -940,7 +933,8 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply_replace_all(
|
||||
db = *(sqlite3 **)info.objClientData;
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[2], &nChangeset);
|
||||
|
||||
rc = sqlite3changeset_apply(db, nChangeset, pChangeset, 0, replace_handler,0);
|
||||
rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
|
||||
0, replace_handler,0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return test_session_error(interp, rc, 0);
|
||||
}
|
||||
@@ -959,6 +953,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_invert(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc; /* Return code from changeset_invert() */
|
||||
Tcl_Size nn;
|
||||
TestStreamInput sIn; /* Input stream */
|
||||
TestSessionsBlob sOut; /* Output blob */
|
||||
|
||||
@@ -970,7 +965,8 @@ static int SQLITE_TCLAPI test_sqlite3changeset_invert(
|
||||
memset(&sIn, 0, sizeof(sIn));
|
||||
memset(&sOut, 0, sizeof(sOut));
|
||||
sIn.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
sIn.aData = Tcl_GetByteArrayFromObj(objv[1], &sIn.nData);
|
||||
sIn.aData = Tcl_GetByteArrayFromObj(objv[1], &nn);
|
||||
sIn.nData = (int)nn;
|
||||
|
||||
if( sIn.nStream ){
|
||||
rc = sqlite3changeset_invert_strm(
|
||||
@@ -999,6 +995,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc; /* Return code from changeset_invert() */
|
||||
Tcl_Size nn;
|
||||
|
||||
TestStreamInput sLeft; /* Input stream */
|
||||
TestStreamInput sRight; /* Input stream */
|
||||
@@ -1011,8 +1008,10 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
|
||||
memset(&sLeft, 0, sizeof(sLeft));
|
||||
memset(&sRight, 0, sizeof(sRight));
|
||||
sLeft.aData = Tcl_GetByteArrayFromObj(objv[1], &sLeft.nData);
|
||||
sRight.aData = Tcl_GetByteArrayFromObj(objv[2], &sRight.nData);
|
||||
sLeft.aData = Tcl_GetByteArrayFromObj(objv[1], &nn);
|
||||
sLeft.nData = (int)nn;
|
||||
sRight.aData = Tcl_GetByteArrayFromObj(objv[2], &nn);
|
||||
sRight.nData = (int)nn;
|
||||
sLeft.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
sRight.nStream = sLeft.nStream;
|
||||
|
||||
@@ -1106,7 +1105,7 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void *pChangeset;
|
||||
int nChangeset;
|
||||
Tcl_Size nChangeset;
|
||||
sqlite3_changeset_iter *pIter;
|
||||
int rc;
|
||||
Tcl_Obj *pVarname;
|
||||
@@ -1120,7 +1119,7 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
|
||||
while( objc>1 ){
|
||||
char *zOpt = Tcl_GetString(objv[1]);
|
||||
int nOpt = strlen(zOpt);
|
||||
int nOpt = (int)strlen(zOpt);
|
||||
if( zOpt[0]!='-' ) break;
|
||||
if( nOpt<=7 && 0==sqlite3_strnicmp(zOpt, "-invert", nOpt) ){
|
||||
isInvert = 1;
|
||||
@@ -1148,19 +1147,19 @@ static int SQLITE_TCLAPI test_sqlite3session_foreach(
|
||||
if( isInvert ){
|
||||
int f = SQLITE_CHANGESETSTART_INVERT;
|
||||
if( sStr.nStream==0 ){
|
||||
rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, f);
|
||||
rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, f);
|
||||
}else{
|
||||
void *pCtx = (void*)&sStr;
|
||||
sStr.aData = (unsigned char*)pChangeset;
|
||||
sStr.nData = nChangeset;
|
||||
sStr.nData = (int)nChangeset;
|
||||
rc = sqlite3changeset_start_v2_strm(&pIter, testStreamInput, pCtx, f);
|
||||
}
|
||||
}else{
|
||||
if( sStr.nStream==0 ){
|
||||
rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
|
||||
rc = sqlite3changeset_start(&pIter, (int)nChangeset, pChangeset);
|
||||
}else{
|
||||
sStr.aData = (unsigned char*)pChangeset;
|
||||
sStr.nData = nChangeset;
|
||||
sStr.nData = (int)nChangeset;
|
||||
rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
|
||||
}
|
||||
}
|
||||
@@ -1237,9 +1236,9 @@ static int SQLITE_TCLAPI test_rebaser_cmd(
|
||||
assert( rc==SQLITE_OK );
|
||||
switch( iSub ){
|
||||
case 0: { /* configure */
|
||||
int nRebase = 0;
|
||||
Tcl_Size nRebase = 0;
|
||||
unsigned char *pRebase = Tcl_GetByteArrayFromObj(objv[2], &nRebase);
|
||||
rc = sqlite3rebaser_configure(p, nRebase, pRebase);
|
||||
rc = sqlite3rebaser_configure(p, (int)nRebase, pRebase);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1250,10 +1249,12 @@ static int SQLITE_TCLAPI test_rebaser_cmd(
|
||||
default: { /* rebase */
|
||||
TestStreamInput sStr; /* Input stream */
|
||||
TestSessionsBlob sOut; /* Output blob */
|
||||
Tcl_Size nn;
|
||||
|
||||
memset(&sStr, 0, sizeof(sStr));
|
||||
memset(&sOut, 0, sizeof(sOut));
|
||||
sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &sStr.nData);
|
||||
sStr.aData = Tcl_GetByteArrayFromObj(objv[2], &nn);
|
||||
sStr.nData = nn;
|
||||
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
|
||||
if( sStr.nStream ){
|
||||
@@ -1392,7 +1393,7 @@ static int SQLITE_TCLAPI test_changeset(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void *pChangeset = 0; /* Buffer containing changeset */
|
||||
int nChangeset = 0; /* Size of buffer aChangeset in bytes */
|
||||
Tcl_Size nChangeset = 0; /* Size of buffer aChangeset in bytes */
|
||||
int rc = SQLITE_OK;
|
||||
char *z = 0;
|
||||
|
||||
@@ -1403,7 +1404,7 @@ static int SQLITE_TCLAPI test_changeset(
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[1], &nChangeset);
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
rc = sqlite3_test_changeset(nChangeset, pChangeset, &z);
|
||||
rc = sqlite3_test_changeset((int)nChangeset, pChangeset, &z);
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zErr = sqlite3_mprintf("(%d) - \"%s\"", rc, z);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
|
||||
@@ -1528,9 +1529,9 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
|
||||
};
|
||||
|
||||
case 1: { /* add */
|
||||
int nByte = 0;
|
||||
Tcl_Size nByte = 0;
|
||||
const u8 *aByte = Tcl_GetByteArrayFromObj(objv[2], &nByte);
|
||||
rc = sqlite3changegroup_add(p->pGrp, nByte, (void*)aByte);
|
||||
rc = sqlite3changegroup_add(p->pGrp, (int)nByte, (void*)aByte);
|
||||
if( rc!=SQLITE_OK ) rc = test_session_error(interp, rc, 0);
|
||||
break;
|
||||
};
|
||||
@@ -1678,7 +1679,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_start(
|
||||
){
|
||||
int isInvert = 0;
|
||||
void *pChangeset = 0; /* Buffer containing changeset */
|
||||
int nChangeset = 0; /* Size of buffer aChangeset in bytes */
|
||||
Tcl_Size nChangeset = 0; /* Size of buffer aChangeset in bytes */
|
||||
TestChangeIter *pNew = 0;
|
||||
sqlite3_changeset_iter *pIter = 0;
|
||||
int flags = 0;
|
||||
@@ -1688,9 +1689,9 @@ static int SQLITE_TCLAPI test_sqlite3changeset_start(
|
||||
char zCmd[64];
|
||||
|
||||
if( objc==3 ){
|
||||
int n = 0;
|
||||
Tcl_Size n = 0;
|
||||
const char *z = Tcl_GetStringFromObj(objv[1], &n);
|
||||
isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", n)==0);
|
||||
isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", (int)n)==0);
|
||||
}
|
||||
|
||||
if( objc!=2 && (objc!=3 || !isInvert) ){
|
||||
@@ -1700,7 +1701,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_start(
|
||||
|
||||
flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset);
|
||||
rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, flags);
|
||||
rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, flags);
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zErr = sqlite3_mprintf(
|
||||
"error in sqlite3changeset_start_v2() - %d", rc
|
||||
|
||||
+26
-5
@@ -233,6 +233,14 @@ SQLITE_OPT = \
|
||||
# can be used to find errant uses of sqlite3_js_vfs_create_file()
|
||||
# in client code.
|
||||
|
||||
########################################################################
|
||||
# minimal=1 disables all "extraneous" stuff from sqlite3-wasm.c, the
|
||||
# goal being to create a WASM file with only the core APIs.
|
||||
minimal ?= 0
|
||||
ifeq (1,$(minimal))
|
||||
SQLITE_OPT += -DSQLITE_WASM_MINIMAL
|
||||
endif
|
||||
|
||||
########################################################################@
|
||||
# It's important that sqlite3.h be built to completion before any
|
||||
# other parts of the build run, thus we use .NOTPARALLEL to disable
|
||||
@@ -413,11 +421,22 @@ emcc_opt_full := $(emcc_opt) -g3
|
||||
########################################################################
|
||||
# EXPORTED_FUNCTIONS.* = files for use with Emscripten's
|
||||
# -sEXPORTED_FUNCTION flag.
|
||||
EXPORTED_FUNCTIONS.api.main := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api)
|
||||
EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main)
|
||||
EXPORTED_FUNCTIONS.api.core := $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-core)
|
||||
EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.core)
|
||||
ifeq (1,$(SQLITE_C_IS_SEE))
|
||||
EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see)
|
||||
endif
|
||||
ifeq (0,$(minimal))
|
||||
EXPORTED_FUNCTIONS.api.in += \
|
||||
$(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-auth) \
|
||||
$(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-preupdate) \
|
||||
$(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-session) \
|
||||
$(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-vtab)
|
||||
else
|
||||
$(info ========================================)
|
||||
$(info This is a minimal-mode build)
|
||||
$(info ========================================)
|
||||
endif
|
||||
EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
|
||||
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE)
|
||||
cat $(EXPORTED_FUNCTIONS.api.in) > $@
|
||||
@@ -1059,9 +1078,9 @@ speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1
|
||||
# -sEXIT_RUNTIME=1 but we need EXIT_RUNTIME=0 for the worker-based app
|
||||
# which runs speedtest1 multiple times.
|
||||
|
||||
$(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api.main)
|
||||
$(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api.core)
|
||||
@echo "Making $@ ..."
|
||||
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.main); } > $@
|
||||
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.core); } > $@
|
||||
speedtest1.js := $(dir.dout)/speedtest1.js
|
||||
speedtest1.wasm := $(dir.dout)/speedtest1.wasm
|
||||
emcc.flags.speedtest1-vanilla := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
@@ -1077,6 +1096,7 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
|
||||
$(emcc.speedtest1.common) \
|
||||
$(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \
|
||||
$(SQLITE_OPT) \
|
||||
-USQLITE_WASM_MINIMAL \
|
||||
-USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \
|
||||
$(speedtest1.exit-runtime0) \
|
||||
-o $@ $(speedtest1.cfiles) -lm
|
||||
@@ -1184,7 +1204,7 @@ endif
|
||||
# Push files to public wasm-testing.sqlite.org server
|
||||
wasm-testing.include = *.js *.mjs *.html \
|
||||
./tests \
|
||||
$(dir.dout) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
$(dir.dout) $(dir.common) $(dir.fiddle) $(dir.fiddle-debug) $(dir.jacc)
|
||||
wasm-testing.exclude = sql/speedtest1.sql
|
||||
wasm-testing.dir = /jail/sites/wasm-testing
|
||||
wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir)
|
||||
@@ -1233,3 +1253,4 @@ endif
|
||||
# Run local web server for the test/demo pages.
|
||||
httpd:
|
||||
althttpd -max-age 1 -enable-sab 1 -page index.html
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
_sqlite3_set_authorizer
|
||||
+7
-53
@@ -10,6 +10,7 @@ _sqlite3_bind_int64
|
||||
_sqlite3_bind_null
|
||||
_sqlite3_bind_parameter_count
|
||||
_sqlite3_bind_parameter_index
|
||||
_sqlite3_bind_parameter_name
|
||||
_sqlite3_bind_pointer
|
||||
_sqlite3_bind_text
|
||||
_sqlite3_busy_handler
|
||||
@@ -23,7 +24,7 @@ _sqlite3_collation_needed
|
||||
_sqlite3_column_blob
|
||||
_sqlite3_column_bytes
|
||||
_sqlite3_column_count
|
||||
_sqlite3_column_count
|
||||
_sqlite3_column_decltype
|
||||
_sqlite3_column_double
|
||||
_sqlite3_column_int
|
||||
_sqlite3_column_int64
|
||||
@@ -40,15 +41,13 @@ _sqlite3_create_collation
|
||||
_sqlite3_create_collation_v2
|
||||
_sqlite3_create_function
|
||||
_sqlite3_create_function_v2
|
||||
_sqlite3_create_module
|
||||
_sqlite3_create_module_v2
|
||||
_sqlite3_create_window_function
|
||||
_sqlite3_data_count
|
||||
_sqlite3_db_filename
|
||||
_sqlite3_db_handle
|
||||
_sqlite3_db_name
|
||||
_sqlite3_db_readonly
|
||||
_sqlite3_db_status
|
||||
_sqlite3_declare_vtab
|
||||
_sqlite3_deserialize
|
||||
_sqlite3_drop_modules
|
||||
_sqlite3_errcode
|
||||
@@ -65,6 +64,8 @@ _sqlite3_free
|
||||
_sqlite3_get_auxdata
|
||||
_sqlite3_get_autocommit
|
||||
_sqlite3_initialize
|
||||
_sqlite3_interrupt
|
||||
_sqlite3_is_interrupted
|
||||
_sqlite3_keyword_count
|
||||
_sqlite3_keyword_name
|
||||
_sqlite3_keyword_check
|
||||
@@ -80,12 +81,6 @@ _sqlite3_open_v2
|
||||
_sqlite3_overload_function
|
||||
_sqlite3_prepare_v2
|
||||
_sqlite3_prepare_v3
|
||||
_sqlite3_preupdate_blobwrite
|
||||
_sqlite3_preupdate_count
|
||||
_sqlite3_preupdate_depth
|
||||
_sqlite3_preupdate_hook
|
||||
_sqlite3_preupdate_new
|
||||
_sqlite3_preupdate_old
|
||||
_sqlite3_progress_handler
|
||||
_sqlite3_randomness
|
||||
_sqlite3_realloc
|
||||
@@ -108,7 +103,6 @@ _sqlite3_result_zeroblob
|
||||
_sqlite3_result_zeroblob64
|
||||
_sqlite3_rollback_hook
|
||||
_sqlite3_serialize
|
||||
_sqlite3_set_authorizer
|
||||
_sqlite3_set_auxdata
|
||||
_sqlite3_set_last_insert_rowid
|
||||
_sqlite3_shutdown
|
||||
@@ -117,6 +111,8 @@ _sqlite3_sql
|
||||
_sqlite3_status
|
||||
_sqlite3_status64
|
||||
_sqlite3_step
|
||||
_sqlite3_stmt_busy
|
||||
_sqlite3_stmt_explain
|
||||
_sqlite3_stmt_isexplain
|
||||
_sqlite3_stmt_readonly
|
||||
_sqlite3_stmt_status
|
||||
@@ -160,45 +156,3 @@ _sqlite3_vtab_in_next
|
||||
_sqlite3_vtab_nochange
|
||||
_sqlite3_vtab_on_conflict
|
||||
_sqlite3_vtab_rhs_value
|
||||
_sqlite3changegroup_add
|
||||
_sqlite3changegroup_add_strm
|
||||
_sqlite3changegroup_delete
|
||||
_sqlite3changegroup_new
|
||||
_sqlite3changegroup_output
|
||||
_sqlite3changegroup_output_strm
|
||||
_sqlite3changeset_apply
|
||||
_sqlite3changeset_apply_strm
|
||||
_sqlite3changeset_apply_v2
|
||||
_sqlite3changeset_apply_v2_strm
|
||||
_sqlite3changeset_concat
|
||||
_sqlite3changeset_concat_strm
|
||||
_sqlite3changeset_conflict
|
||||
_sqlite3changeset_finalize
|
||||
_sqlite3changeset_fk_conflicts
|
||||
_sqlite3changeset_invert
|
||||
_sqlite3changeset_invert_strm
|
||||
_sqlite3changeset_new
|
||||
_sqlite3changeset_next
|
||||
_sqlite3changeset_old
|
||||
_sqlite3changeset_op
|
||||
_sqlite3changeset_pk
|
||||
_sqlite3changeset_start
|
||||
_sqlite3changeset_start_strm
|
||||
_sqlite3changeset_start_v2
|
||||
_sqlite3changeset_start_v2_strm
|
||||
_sqlite3session_attach
|
||||
_sqlite3session_changeset
|
||||
_sqlite3session_changeset_size
|
||||
_sqlite3session_changeset_strm
|
||||
_sqlite3session_config
|
||||
_sqlite3session_create
|
||||
_sqlite3session_delete
|
||||
_sqlite3session_diff
|
||||
_sqlite3session_enable
|
||||
_sqlite3session_indirect
|
||||
_sqlite3session_isempty
|
||||
_sqlite3session_memory_used
|
||||
_sqlite3session_object_config
|
||||
_sqlite3session_patchset
|
||||
_sqlite3session_patchset_strm
|
||||
_sqlite3session_table_filter
|
||||
@@ -0,0 +1,6 @@
|
||||
_sqlite3_preupdate_blobwrite
|
||||
_sqlite3_preupdate_count
|
||||
_sqlite3_preupdate_depth
|
||||
_sqlite3_preupdate_hook
|
||||
_sqlite3_preupdate_new
|
||||
_sqlite3_preupdate_old
|
||||
@@ -0,0 +1,42 @@
|
||||
_sqlite3changegroup_add
|
||||
_sqlite3changegroup_add_strm
|
||||
_sqlite3changegroup_delete
|
||||
_sqlite3changegroup_new
|
||||
_sqlite3changegroup_output
|
||||
_sqlite3changegroup_output_strm
|
||||
_sqlite3changeset_apply
|
||||
_sqlite3changeset_apply_strm
|
||||
_sqlite3changeset_apply_v2
|
||||
_sqlite3changeset_apply_v2_strm
|
||||
_sqlite3changeset_concat
|
||||
_sqlite3changeset_concat_strm
|
||||
_sqlite3changeset_conflict
|
||||
_sqlite3changeset_finalize
|
||||
_sqlite3changeset_fk_conflicts
|
||||
_sqlite3changeset_invert
|
||||
_sqlite3changeset_invert_strm
|
||||
_sqlite3changeset_new
|
||||
_sqlite3changeset_next
|
||||
_sqlite3changeset_old
|
||||
_sqlite3changeset_op
|
||||
_sqlite3changeset_pk
|
||||
_sqlite3changeset_start
|
||||
_sqlite3changeset_start_strm
|
||||
_sqlite3changeset_start_v2
|
||||
_sqlite3changeset_start_v2_strm
|
||||
_sqlite3session_attach
|
||||
_sqlite3session_changeset
|
||||
_sqlite3session_changeset_size
|
||||
_sqlite3session_changeset_strm
|
||||
_sqlite3session_config
|
||||
_sqlite3session_create
|
||||
_sqlite3session_delete
|
||||
_sqlite3session_diff
|
||||
_sqlite3session_enable
|
||||
_sqlite3session_indirect
|
||||
_sqlite3session_isempty
|
||||
_sqlite3session_memory_used
|
||||
_sqlite3session_object_config
|
||||
_sqlite3session_patchset
|
||||
_sqlite3session_patchset_strm
|
||||
_sqlite3session_table_filter
|
||||
@@ -0,0 +1,11 @@
|
||||
_sqlite3_create_module
|
||||
_sqlite3_create_module_v2
|
||||
_sqlite3_declare_vtab
|
||||
_sqlite3_vtab_collation
|
||||
_sqlite3_vtab_distinct
|
||||
_sqlite3_vtab_in
|
||||
_sqlite3_vtab_in_first
|
||||
_sqlite3_vtab_in_next
|
||||
_sqlite3_vtab_nochange
|
||||
_sqlite3_vtab_on_conflict
|
||||
_sqlite3_vtab_rhs_value
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user