Compare commits

..

2 Commits

Author SHA1 Message Date
drh 8ed265c9e6 Merge recent fixes and enhancements from trunk.
FossilOrigin-Name: 553258c2ed85cb3a539d7562bf6fcf58bb601a7c459e408a2188794ea4bb2f2c
2019-10-09 17:38:56 +00:00
sperry 76190ef856 An alternative, experimental lookaside memory allocator that uses two
different slot sizes.

FossilOrigin-Name: 5ba8cee8f710eb2a6844d5de0d975c207e5645754e30211a4cb5a4869248b693
2019-10-09 17:06:38 +00:00
499 changed files with 10314 additions and 44646 deletions
+15 -40
View File
@@ -190,8 +190,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
update.lo userauth.lo upsert.lo util.lo vacuum.lo \
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
vdbetrace.lo vdbevtab.lo \
wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
window.lo utf.lo vtab.lo
# Object files for the amalgamation.
@@ -235,6 +234,8 @@ SRC = \
$(TOP)/src/insert.c \
$(TOP)/src/legacy.c \
$(TOP)/src/loadext.c \
$(TOP)/src/lookaside.c \
$(TOP)/src/lookaside.h \
$(TOP)/src/main.c \
$(TOP)/src/malloc.c \
$(TOP)/src/mem0.c \
@@ -297,7 +298,6 @@ SRC = \
$(TOP)/src/vdbemem.c \
$(TOP)/src/vdbesort.c \
$(TOP)/src/vdbetrace.c \
$(TOP)/src/vdbevtab.c \
$(TOP)/src/vdbeInt.h \
$(TOP)/src/vtab.c \
$(TOP)/src/vxworks.h \
@@ -440,12 +440,9 @@ TESTSRC += \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/test_expert.c \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/carray.c \
$(TOP)/ext/misc/cksumvfs.c \
$(TOP)/ext/misc/closure.c \
$(TOP)/ext/misc/csv.c \
$(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/eval.c \
$(TOP)/ext/misc/explain.c \
$(TOP)/ext/misc/fileio.c \
@@ -507,7 +504,6 @@ TESTSRC2 = \
$(TOP)/src/vdbe.c \
$(TOP)/src/vdbemem.c \
$(TOP)/src/vdbetrace.c \
$(TOP)/src/vdbevtab.c \
$(TOP)/src/where.c \
$(TOP)/src/wherecode.c \
$(TOP)/src/whereexpr.c \
@@ -531,6 +527,7 @@ HDR = \
$(TOP)/src/hash.h \
$(TOP)/src/hwtime.h \
keywordhash.h \
$(TOP)/src/lookaside.h \
$(TOP)/src/msvc.h \
$(TOP)/src/mutex.h \
opcodes.h \
@@ -613,7 +610,6 @@ SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
SHELL_OPT += -DSQLITE_ENABLE_DBPAGE_VTAB
SHELL_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
SHELL_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
SHELL_OPT += -DSQLITE_ENABLE_OFFSET_SQL_FUNC
SHELL_OPT += -DSQLITE_ENABLE_DESERIALIZE
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
@@ -622,12 +618,10 @@ FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
FUZZCHECK_OPT += -DSQLITE_PRINTF_PRECISION_LIMIT=1000
FUZZCHECK_OPT += -DSQLITE_ENABLE_DESERIALIZE
FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS4
FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS3_PARENTHESIS
#FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS5
FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE
FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c
DBFUZZ_OPT =
@@ -697,7 +691,6 @@ DBFUZZ2_OPTS = \
-DSQLITE_ENABLE_DESERIALIZE \
-DSQLITE_DEBUG \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5
@@ -879,6 +872,9 @@ legacy.lo: $(TOP)/src/legacy.c $(HDR)
loadext.lo: $(TOP)/src/loadext.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/loadext.c
lookaside.lo: $(TOP)/src/lookaside.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/lookaside.c
main.lo: $(TOP)/src/main.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/main.c
@@ -1014,9 +1010,6 @@ vdbesort.lo: $(TOP)/src/vdbesort.c $(HDR)
vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbetrace.c
vdbevtab.lo: $(TOP)/src/vdbevtab.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vdbevtab.c
vtab.lo: $(TOP)/src/vtab.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c
@@ -1065,17 +1058,11 @@ parse.h: parse.c
parse.c: $(TOP)/src/parse.y lemon$(BEXE)
cp $(TOP)/src/parse.y .
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) -S parse.y
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION
echo '#ifndef SQLITE_RESOURCE_VERSION' >$@
echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@
cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@
echo '#endif' >>sqlite3rc.h
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
$(BCC) -o mkkeywordhash$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)/tool/mkkeywordhash.c
./mkkeywordhash$(BEXE) >keywordhash.h
@@ -1084,14 +1071,10 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
SHELL_SRC = \
$(TOP)/src/shell.c.in \
$(TOP)/ext/misc/appendvfs.c \
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/misc/decimal.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/shathree.c \
$(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/completion.c \
$(TOP)/ext/misc/sqlar.c \
$(TOP)/ext/misc/uint.c \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/misc/zipfile.c \
@@ -1200,10 +1183,10 @@ FTS5_SRC = \
$(TOP)/ext/fts5/fts5_varint.c \
$(TOP)/ext/fts5/fts5_vocab.c \
fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon
cp $(TOP)/ext/fts5/fts5parse.y .
rm -f fts5parse.h
./lemon$(BEXE) $(OPTS) -S fts5parse.y
./lemon$(BEXE) $(OPTS) fts5parse.y
fts5parse.h: fts5parse.c
@@ -1233,9 +1216,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_BYTECODE_VTAB
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DESERIALIZE
TESTFIXTURE_FLAGS += -DSQLITE_CKSUMVFS_STATIC
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
TESTFIXTURE_SRC1 = sqlite3.c
@@ -1268,7 +1249,7 @@ fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuz
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M --timeout 600 $(FUZZDATA)
valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
# The veryquick.test TCL tests.
@@ -1299,9 +1280,6 @@ valgrindtest: $(TESTPROGS) valgrindfuzz
smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
./testfixture$(TEXE) $(TOP)/test/main.test $(TESTOPTS)
shelltest: $(TESTPROGS)
./testfixture$(TEXT) $(TOP)/test/permutations.test shell
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
@@ -1379,9 +1357,6 @@ wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.c
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/test/speedtest1.c sqlite3.c $(TLIBS)
startup$(TEXE): $(TOP)/test/startup.c sqlite3.c
$(CC) -Os -g -DSQLITE_THREADSAFE=0 -o $@ $(TOP)/test/startup.c sqlite3.c $(TLIBS)
KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ
kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c
@@ -1407,10 +1382,10 @@ checksymbols: sqlite3.o
# a tarball named for the version number. Ex: sqlite-autoconf-3110000.tar.gz.
# The snapshot-tarball target builds a tarball named by the SHA1 hash
#
amalgamation-tarball: sqlite3.c sqlite3rc.h
amalgamation-tarball: sqlite3.c
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal
snapshot-tarball: sqlite3.c sqlite3rc.h
snapshot-tarball: sqlite3.c
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
# The next two rules are used to support the "threadtest" target. Building
+21 -70
View File
@@ -234,15 +234,6 @@ OSTRACE = 0
DEBUG = 0
!ENDIF
# <<mark>>
# Disable use of the --linemacros argument to the mksqlite3c.tcl tool, which
# is used to build the amalgamation.
#
!IFNDEF NO_LINEMACROS
NO_LINEMACROS = 0
!ENDIF
# <</mark>>
# Enable use of available compiler optimizations? Normally, this should be
# non-zero. Setting this to zero, thus disabling all compiler optimizations,
# can be useful for testing.
@@ -257,12 +248,6 @@ OPTIMIZATIONS = 2
SESSION = 0
!ENDIF
# Set this to non-0 to enable support for the rbu extension.
#
!IFNDEF RBU
RBU = 0
!ENDIF
# Set the source code file to be used by executables and libraries when
# they need the amalgamation.
#
@@ -366,7 +351,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
!ENDIF
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
@@ -380,16 +364,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
!ENDIF
# Always enable math functions on Windows
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
# Should the rbu extension be enabled? If so, add compilation options
# to enable it.
#
!IF $(RBU)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#
@@ -788,7 +762,7 @@ MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c.tcl
!ENDIF
!IFNDEF MKSQLITE3C_ARGS
!IF $(DEBUG)>1 && $(NO_LINEMACROS)==0
!IF $(DEBUG)>1
MKSQLITE3C_ARGS = --linemacros
!ELSE
MKSQLITE3C_ARGS =
@@ -1259,8 +1233,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
update.lo upsert.lo util.lo vacuum.lo \
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
vdbetrace.lo vdbevtab.lo wal.lo walker.lo where.lo wherecode.lo \
whereexpr.lo \
vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
window.lo utf.lo vtab.lo
# <</mark>>
@@ -1317,6 +1290,7 @@ SRC00 = \
$(TOP)\src\insert.c \
$(TOP)\src\legacy.c \
$(TOP)\src\loadext.c \
$(TOP)\src\lookaside.c \
$(TOP)\src\main.c \
$(TOP)\src\malloc.c \
$(TOP)\src\mem0.c \
@@ -1367,7 +1341,6 @@ SRC01 = \
$(TOP)\src\vdbemem.c \
$(TOP)\src\vdbesort.c \
$(TOP)\src\vdbetrace.c \
$(TOP)\src\vdbevtab.c \
$(TOP)\src\vtab.c \
$(TOP)\src\wal.c \
$(TOP)\src\walker.c \
@@ -1388,6 +1361,7 @@ SRC04 = \
$(TOP)\src\btreeInt.h \
$(TOP)\src\hash.h \
$(TOP)\src\hwtime.h \
$(TOP)\src\lookaside.h \
$(TOP)\src\msvc.h \
$(TOP)\src\mutex.h \
$(TOP)\src\os.h \
@@ -1498,7 +1472,7 @@ SRC12 =
# All source code files.
#
SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11) $(SRC12)
SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11)
# Source code to the test files.
#
@@ -1560,12 +1534,9 @@ TESTEXT = \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\test_expert.c \
$(TOP)\ext\misc\amatch.c \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\carray.c \
$(TOP)\ext\misc\cksumvfs.c \
$(TOP)\ext\misc\closure.c \
$(TOP)\ext\misc\csv.c \
$(TOP)\ext\misc\decimal.c \
$(TOP)\ext\misc\eval.c \
$(TOP)\ext\misc\explain.c \
$(TOP)\ext\misc\fileio.c \
@@ -1611,6 +1582,7 @@ HDR = \
$(TOP)\src\btreeInt.h \
$(TOP)\src\hash.h \
$(TOP)\src\hwtime.h \
$(TOP)\src\lookaside.h \
keywordhash.h \
$(TOP)\src\msvc.h \
$(TOP)\src\mutex.h \
@@ -1702,7 +1674,6 @@ FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_FTS4
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_RTREE
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
@@ -1774,7 +1745,7 @@ $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
sqlite3.def: libsqlite3.lib
echo EXPORTS > sqlite3.def
dumpbin /all libsqlite3.lib \
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser|rbu)?_[^@]*)(?:@\d+)?$$" \1 \
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@]*)(?:@\d+)?$$" \1 \
| sort >> sqlite3.def
# <</block2>>
@@ -1852,16 +1823,15 @@ mptest: mptester.exe
for %i in ($(SRC11)) do copy /Y %i tsrc
for %i in ($(SRC12)) do copy /Y %i tsrc
copy /Y fts5.c tsrc
copy /B tsrc\fts5.c +,,
copy /Y fts5.h tsrc
copy /B tsrc\fts5.h +,,
del /Q tsrc\sqlite.h.in tsrc\parse.y 2>NUL
$(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new
move vdbe.new tsrc\vdbe.c
echo > .target_source
sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL)
sqlite3.c: .target_source sqlite3ext.h $(MKSQLITE3C_TOOL)
$(TCLSH_CMD) $(MKSQLITE3C_TOOL) $(MKSQLITE3C_ARGS)
copy $(TOP)\ext\session\sqlite3session.h .
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
@@ -1876,8 +1846,7 @@ sqlite3.lo: $(SQLITE3C)
# Rules to build the LEMON compiler generator
#
lempar.c: $(TOP)\tool\lempar.c
copy /Y $(TOP)\tool\lempar.c .
copy /B lempar.c +,,
copy $(TOP)\tool\lempar.c .
lemon.exe: $(TOP)\tool\lemon.c lempar.c
$(BCC) $(NO_WARN) -Daccess=_access \
@@ -1995,6 +1964,9 @@ legacy.lo: $(TOP)\src\legacy.c $(HDR)
loadext.lo: $(TOP)\src\loadext.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\loadext.c
lookaside.lo: $(TOP)\src\lookaside.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\lookaside.c
main.lo: $(TOP)\src\main.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\main.c
@@ -2130,9 +2102,6 @@ vdbesort.lo: $(TOP)\src\vdbesort.c $(HDR)
vdbetrace.lo: $(TOP)\src\vdbetrace.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vdbetrace.c
vdbevtab.lo: $(TOP)\src\vdbevtab.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vdbevtab.c
vtab.lo: $(TOP)\src\vtab.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\vtab.c
@@ -2177,9 +2146,8 @@ parse.h: parse.c
parse.c: $(TOP)\src\parse.y lemon.exe
del /Q parse.y parse.h parse.h.temp 2>NUL
copy /Y $(TOP)\src\parse.y .
copy /B parse.y +,,
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S parse.y
copy $(TOP)\src\parse.y .
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) parse.y
$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS)
@@ -2191,13 +2159,8 @@ sqlite3ext.h: .target_source
copy /Y sqlite3ext.h tsrc\sqlite3ext.h
!ELSE
copy /Y tsrc\sqlite3ext.h sqlite3ext.h
copy /B sqlite3ext.h +,,
!ENDIF
sqlite3session.h: $(TOP)\ext\session\sqlite3session.h
copy /Y $(TOP)\ext\session\sqlite3session.h .
copy /B sqlite3session.h +,,
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
$(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) \
$(TOP)\tool\mkkeywordhash.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS)
@@ -2209,13 +2172,9 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
SHELL_SRC = \
$(TOP)\src\shell.c.in \
$(TOP)\ext\misc\appendvfs.c \
$(TOP)\ext\misc\completion.c \
$(TOP)\ext\misc\decimal.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\shathree.c \
$(TOP)\ext\misc\uint.c \
$(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\completion.c \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\sqlite3expert.h \
$(TOP)\ext\misc\memtrace.c \
@@ -2346,22 +2305,19 @@ LSM1_SRC = \
$(TOP)\ext\lsm1\lsm_win32.c
fts5parse.c: $(TOP)\ext\fts5\fts5parse.y lemon.exe
copy /Y $(TOP)\ext\fts5\fts5parse.y .
copy /B fts5parse.y +,,
copy $(TOP)\ext\fts5\fts5parse.y .
del /Q fts5parse.h 2>NUL
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S fts5parse.y
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) fts5parse.y
fts5parse.h: fts5parse.c
fts5.c: $(FTS5_SRC)
$(TCLSH_CMD) $(TOP)\ext\fts5\tool\mkfts5c.tcl
copy /Y $(TOP)\ext\fts5\fts5.h .
copy /B fts5.h +,,
copy $(TOP)\ext\fts5\fts5.h .
lsm1.c: $(LSM1_SRC)
$(TCLSH_CMD) $(TOP)\ext\lsm1\tool\mklsm1c.tcl
copy /Y $(TOP)\ext\lsm1\lsm.h .
copy /B lsm.h +,,
copy $(TOP)\ext\lsm1\lsm.h .
fts5.lo: fts5.c $(HDR) $(EXTHDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c fts5.c
@@ -2389,10 +2345,8 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CKSUMVFS_STATIC=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
@@ -2473,9 +2427,6 @@ smoketest: $(TESTPROGS)
@set PATH=$(LIBTCLPATH);$(PATH)
.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)
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 $(SQLITE_TCL_DEP)
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in > $@
+1 -1
View File
@@ -1 +1 @@
3.35.4
3.30.0
+1 -1
View File
@@ -13,7 +13,7 @@ sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_D
include_HEADERS = sqlite3.h sqlite3ext.h
EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc sqlite3rc.h README.txt Replace.cs Makefile.fallback
EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs Makefile.fallback
pkgconfigdir = ${libdir}/pkgconfig
pkgconfig_DATA = sqlite3.pc
+2 -19
View File
@@ -196,7 +196,6 @@ OSTRACE = 0
DEBUG = 0
!ENDIF
# Enable use of available compiler optimizations? Normally, this should be
# non-zero. Setting this to zero, thus disabling all compiler optimizations,
# can be useful for testing.
@@ -211,12 +210,6 @@ OPTIMIZATIONS = 2
SESSION = 0
!ENDIF
# Set this to non-0 to enable support for the rbu extension.
#
!IFNDEF RBU
RBU = 0
!ENDIF
# Set the source code file to be used by executables and libraries when
# they need the amalgamation.
#
@@ -289,7 +282,7 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DBSTAT_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_BYTECODE_VTAB=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_INTROSPECTION_PRAGMAS=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
!ENDIF
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
@@ -303,16 +296,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION=1
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK=1
!ENDIF
# Always enable math functions on Windows
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
# Should the rbu extension be enabled? If so, add compilation options
# to enable it.
#
!IF $(RBU)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for
# the Windows 10 platform.
#
@@ -995,7 +978,7 @@ Replace.exe:
sqlite3.def: Replace.exe $(LIBOBJ)
echo EXPORTS > sqlite3.def
dumpbin /all $(LIBOBJ) \
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser|rbu)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
| sort >> sqlite3.def
$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
+2 -48
View File
@@ -87,9 +87,7 @@ AC_SUBST(READLINE_LIBS)
AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
[--enable-threadsafe], [build a thread-safe library [default=yes]])],
[], [enable_threadsafe=yes])
if test x"$enable_threadsafe" == "xno"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_THREADSAFE=0"
else
if test x"$enable_threadsafe" != "xno"; then
BUILD_CFLAGS="$BUILD_CFLAGS -D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
AC_SEARCH_LIBS(pthread_create, pthread)
AC_SEARCH_LIBS(pthread_mutexattr_init, pthread)
@@ -111,34 +109,14 @@ AC_MSG_CHECKING([for whether to support dynamic extensions])
AC_MSG_RESULT($enable_dynamic_extensions)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# --enable-math
#
AC_ARG_ENABLE(math, [AS_HELP_STRING(
[--enable-math], [SQL math functions [default=yes]])],
[], [enable_math=yes])
AC_MSG_CHECKING([SQL math functions])
if test x"$enable_math" = "xyes"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_MATH_FUNCTIONS"
AC_MSG_RESULT([enabled])
AC_SEARCH_LIBS(ceil, m)
else
AC_MSG_RESULT([disabled])
fi
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
# --enable-fts4
#
AC_ARG_ENABLE(fts4, [AS_HELP_STRING(
[--enable-fts4], [include fts4 support [default=yes]])],
[], [enable_fts4=yes])
AC_MSG_CHECKING([FTS4 extension])
if test x"$enable_fts4" = "xyes"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS4"
AC_MSG_RESULT([enabled])
else
AC_MSG_RESULT([disabled])
fi
#-----------------------------------------------------------------------
@@ -148,12 +126,8 @@ fi
AC_ARG_ENABLE(fts3, [AS_HELP_STRING(
[--enable-fts3], [include fts3 support [default=no]])],
[], [])
AC_MSG_CHECKING([FTS3 extension])
if test x"$enable_fts3" = "xyes" -a x"$enable_fts4" = "xno"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS3"
AC_MSG_RESULT([enabled])
else
AC_MSG_RESULT([disabled])
fi
#-----------------------------------------------------------------------
@@ -163,13 +137,9 @@ fi
AC_ARG_ENABLE(fts5, [AS_HELP_STRING(
[--enable-fts5], [include fts5 support [default=yes]])],
[], [enable_fts5=yes])
AC_MSG_CHECKING([FTS5 extension])
if test x"$enable_fts5" = "xyes"; then
AC_MSG_RESULT([enabled])
AC_SEARCH_LIBS(log, m)
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_FTS5"
else
AC_MSG_RESULT([disabled])
fi
#-----------------------------------------------------------------------
@@ -179,12 +149,8 @@ fi
AC_ARG_ENABLE(json1, [AS_HELP_STRING(
[--enable-json1], [include json1 support [default=yes]])],
[],[enable_json1=yes])
AC_MSG_CHECKING([JSON functions])
if test x"$enable_json1" = "xyes"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_JSON1"
AC_MSG_RESULT([enabled])
else
AC_MSG_RESULT([disabled])
fi
#-----------------------------------------------------------------------
@@ -194,12 +160,8 @@ fi
AC_ARG_ENABLE(rtree, [AS_HELP_STRING(
[--enable-rtree], [include rtree support [default=yes]])],
[], [enable_rtree=yes])
AC_MSG_CHECKING([RTREE extension])
if test x"$enable_rtree" = "xyes"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_GEOPOLY"
AC_MSG_RESULT([enabled])
else
AC_MSG_RESULT([disabled])
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_RTREE"
fi
#-----------------------------------------------------------------------
@@ -209,12 +171,8 @@ fi
AC_ARG_ENABLE(session, [AS_HELP_STRING(
[--enable-session], [enable the session extension [default=no]])],
[], [])
AC_MSG_CHECKING([Session extension])
if test x"$enable_session" = "xyes"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK"
AC_MSG_RESULT([enabled])
else
AC_MSG_RESULT([disabled])
fi
#-----------------------------------------------------------------------
@@ -224,13 +182,9 @@ fi
AC_ARG_ENABLE(debug, [AS_HELP_STRING(
[--enable-debug], [build with debugging features enabled [default=no]])],
[], [])
AC_MSG_CHECKING([Build type])
if test x"$enable_debug" = "xyes"; then
BUILD_CFLAGS="$BUILD_CFLAGS -DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
CFLAGS="-g -O0"
AC_MSG_RESULT([debug])
else
AC_MSG_RESULT([release])
fi
#-----------------------------------------------------------------------
+1 -1
View File
@@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
# so you can encode the package version directly into the source files.
#-----------------------------------------------------------------------
AC_INIT([sqlite], [3.32.0])
AC_INIT([sqlite], [3.7.4])
#--------------------------------------------------------------------
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
+21 -26
View File
@@ -162,15 +162,18 @@ PROJECT = sqlite3
!if [echo REM = This file is generated from Makefile.vc > versions.vc]
!endif
# get project version from row "AC_INIT([sqlite], [3.x.y])"
# get project version from row "AC_INIT([sqlite], [3.7.14])"
!if [echo DOTVERSION = \>> versions.vc] \
&& [nmakehlp -V ..\configure.ac AC_INIT >> versions.vc]
&& [nmakehlp -V ..\configure.in AC_INIT >> versions.vc]
!endif
!include "versions.vc"
VERSION = $(DOTVERSION:.=)
STUBPREFIX = $(PROJECT)stub
DLLOBJS = \
$(TMP_DIR)\tclsqlite3.obj
#-------------------------------------------------------------------------
# Target names and paths ( shouldn't need changing )
#-------------------------------------------------------------------------
@@ -179,7 +182,7 @@ BINROOT = .
ROOT = ..
PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
PRJLIBNAME = $(PROJECT).$(EXT)
PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
@@ -201,17 +204,6 @@ DOCDIR = $(ROOT)\doc
TOOLSDIR = $(ROOT)\tools
COMPATDIR = $(ROOT)\compat
### Figure out where the primary source code file(s) is/are.
!if exist("$(ROOT)\..\..\sqlite3.c") && exist("$(ROOT)\..\..\src\tclsqlite.c")
SQL_INCLUDES = -I"$(ROOT)\..\.."
SQLITE_SRCDIR = $(ROOT)\..\..
TCLSQLITE_SRCDIR = $(ROOT)\..\..\src
DLLOBJS = $(TMP_DIR)\sqlite3.obj $(TMP_DIR)\tclsqlite.obj
!else
TCLSQLITE_SRCDIR = $(ROOT)\generic
DLLOBJS = $(TMP_DIR)\tclsqlite3.obj
!endif
#---------------------------------------------------------------------
# Compile flags
#---------------------------------------------------------------------
@@ -231,7 +223,7 @@ cdebug = -Z7 -WX -Od -GZ
!endif
### Declarations common to all compiler options
cflags = -nologo -c -W3 -D_CRT_SECURE_NO_WARNINGS -YX -Fp$(TMP_DIR)^\
cflags = -nologo -c -W3 -YX -Fp$(TMP_DIR)^\
!if $(MSVCRT)
!if $(DEBUG)
@@ -247,8 +239,8 @@ crt = -MT
!endif
!endif
INCLUDES = $(SQL_INCLUDES) $(TCL_INCLUDES) -I"$(WINDIR)" \
-I"$(GENERICDIR)" -I"$(ROOT)\.."
INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" \
-I"$(ROOT)\.."
BASE_CLFAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) \
-DSQLITE_3_SUFFIX_ONLY=1 -DSQLITE_ENABLE_RTREE=1 \
-DSQLITE_ENABLE_FTS3=1 -DSQLITE_OMIT_DEPRECATED=1
@@ -349,17 +341,20 @@ $(PRJSTUBLIB): $(PRJSTUBOBJS)
# Implicit rules
#---------------------------------------------------------------------
$(TMP_DIR)\sqlite3.obj: $(SQLITE_SRCDIR)\sqlite3.c
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ \
-c $(SQLITE_SRCDIR)\sqlite3.c
{$(WINDIR)}.c{$(TMP_DIR)}.obj::
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
$<
<<
$(TMP_DIR)\tclsqlite.obj: $(TCLSQLITE_SRCDIR)\tclsqlite.c
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ \
-c $(TCLSQLITE_SRCDIR)\tclsqlite.c
{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
$<
<<
$(TMP_DIR)\tclsqlite3.obj: $(TCLSQLITE_SRCDIR)\tclsqlite3.c
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ \
-c $(TCLSQLITE_SRCDIR)\tclsqlite3.c
{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
$(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
$<
<<
{$(WINDIR)}.rc{$(TMP_DIR)}.res:
$(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \
Vendored
+39 -185
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for sqlite 3.35.4.
# Generated by GNU Autoconf 2.69 for sqlite 3.30.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.35.4'
PACKAGE_STRING='sqlite 3.35.4'
PACKAGE_VERSION='3.30.0'
PACKAGE_STRING='sqlite 3.30.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -799,6 +799,7 @@ TEMP_STORE
ALLOWRELEASE
SQLITE_THREADSAFE
BUILD_CC
VERSION_NUMBER
RELEASE
VERSION
program_prefix
@@ -903,8 +904,6 @@ with_readline_inc
enable_debug
enable_amalgamation
enable_load_extension
enable_math
enable_all
enable_memsys5
enable_memsys3
enable_fts3
@@ -1467,7 +1466,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.35.4 to adapt to many kinds of systems.
\`configure' configures sqlite 3.30.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1532,7 +1531,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.35.4:";;
short | recursive ) echo "Configuration of sqlite 3.30.0:";;
esac
cat <<\_ACEOF
@@ -1558,8 +1557,6 @@ Optional Features:
separately
--disable-load-extension
Disable loading of external extensions
--disable-math Disable math functions
--enable-all Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions
--enable-memsys5 Enable MEMSYS5
--enable-memsys3 Enable MEMSYS3
--enable-fts3 Enable the FTS3 extension
@@ -1660,7 +1657,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.35.4
sqlite configure 3.30.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2079,7 +2076,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.35.4, which was
It was created by sqlite $as_me 3.30.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3937,13 +3934,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:3940: $ac_compile\"" >&5)
(eval echo "\"\$as_me:3937: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:3943: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval echo "\"\$as_me:3940: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:3946: output\"" >&5)
(eval echo "\"\$as_me:3943: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@@ -5149,7 +5146,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 5152 "configure"' > conftest.$ac_ext
echo '#line 5149 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -6674,11 +6671,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:6677: $lt_compile\"" >&5)
(eval echo "\"\$as_me:6674: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:6681: \$? = $ac_status" >&5
echo "$as_me:6678: \$? = $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.
@@ -7013,11 +7010,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:7016: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7013: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7020: \$? = $ac_status" >&5
echo "$as_me:7017: \$? = $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.
@@ -7118,11 +7115,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:7121: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7118: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7125: \$? = $ac_status" >&5
echo "$as_me:7122: \$? = $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
@@ -7173,11 +7170,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:7176: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7173: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7180: \$? = $ac_status" >&5
echo "$as_me:7177: \$? = $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
@@ -9553,7 +9550,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 9556 "configure"
#line 9553 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9649,7 +9646,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 9652 "configure"
#line 9649 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -10366,10 +10363,8 @@ 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
break
fi
TCLLIBDIR=$i
break
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
@@ -10391,6 +10386,12 @@ RELEASE=`cat $srcdir/VERSION`
{ $as_echo "$as_me:${as_lineno-$LINENO}: Release set to $RELEASE" >&5
$as_echo "$as_me: Release set to $RELEASE" >&6;}
VERSION_NUMBER=`cat $srcdir/VERSION \
| sed 's/[^0-9]/ /g' \
| awk '{printf "%d%03d%03d",$1,$2,$3}'`
{ $as_echo "$as_me:${as_lineno-$LINENO}: Version number set to $VERSION_NUMBER" >&5
$as_echo "$as_me: Version number set to $VERSION_NUMBER" >&6;}
#########
# Locate a compiler for the build machine. This compiler should
@@ -11251,16 +11252,10 @@ if test "${enable_debug+set}" = set; then :
enableval=$enable_debug;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build type" >&5
$as_echo_n "checking build type... " >&6; }
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug" >&5
$as_echo "debug" >&6; }
else
TARGET_DEBUG="-DNDEBUG"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: release" >&5
$as_echo "release" >&6; }
fi
@@ -11271,7 +11266,7 @@ if test "${enable_amalgamation+set}" = set; then :
enableval=$enable_amalgamation;
fi
if test "${enable_amalgamation}" = "no" ; then
if test "${enable_amalgamation}" == "no" ; then
USE_AMALGAMATION=0
fi
@@ -11421,91 +11416,6 @@ else
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
##########
# Do we want to support math functions
#
# Check whether --enable-math was given.
if test "${enable_math+set}" = set; then :
enableval=$enable_math;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support math functions" >&5
$as_echo_n "checking whether to support math functions... " >&6; }
if test "$enable_math" = "no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MATH_FUNCTIONS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ceil" >&5
$as_echo_n "checking for library containing ceil... " >&6; }
if ${ac_cv_search_ceil+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char ceil ();
int
main ()
{
return ceil ();
;
return 0;
}
_ACEOF
for ac_lib in '' m; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_ceil=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_ceil+:} false; then :
break
fi
done
if ${ac_cv_search_ceil+:} false; then :
else
ac_cv_search_ceil=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ceil" >&5
$as_echo "$ac_cv_search_ceil" >&6; }
ac_res=$ac_cv_search_ceil
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
fi
########
# The --enable-all argument is short-hand to enable
# multiple extensions.
# Check whether --enable-all was given.
if test "${enable_all+set}" = set; then :
enableval=$enable_all;
fi
##########
# Do we want to support memsys3 and/or memsys5
#
@@ -11547,26 +11457,15 @@ if test "${enable_fts3+set}" = set; then :
enableval=$enable_fts3;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support FTS3" >&5
$as_echo_n "checking whether to support FTS3... " >&6; }
if test "${enable_fts3}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Check whether --enable-fts4 was given.
if test "${enable_fts4+set}" = set; then :
enableval=$enable_fts4;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support FTS4" >&5
$as_echo_n "checking whether to support FTS4... " >&6; }
if test "${enable_fts4}" = "yes" -o "${enable_all}" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
if test "${enable_fts4}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
$as_echo_n "checking for library containing log... " >&6; }
@@ -11624,20 +11523,13 @@ if test "$ac_res" != no; then :
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Check whether --enable-fts5 was given.
if test "${enable_fts5+set}" = set; then :
enableval=$enable_fts5;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support FTS5" >&5
$as_echo_n "checking whether to support FTS5... " >&6; }
if test "${enable_fts5}" = "yes" -o "${enable_all}" = "yes" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
if test "${enable_fts5}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
$as_echo_n "checking for library containing log... " >&6; }
@@ -11695,9 +11587,6 @@ if test "$ac_res" != no; then :
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#########
@@ -11707,15 +11596,8 @@ if test "${enable_json1+set}" = set; then :
enableval=$enable_json1;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support JSON" >&5
$as_echo_n "checking whether to support JSON... " >&6; }
if test "${enable_json1}" = "yes" -o "${enable_all}" = "yes" ; then
if test "${enable_json1}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#########
@@ -11726,15 +11608,8 @@ if test "${enable_update_limit+set}" = set; then :
enableval=$enable_update_limit;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support LIMIT on UPDATE and DELETE statements" >&5
$as_echo_n "checking whether to support LIMIT on UPDATE and DELETE statements... " >&6; }
if test "${enable_update_limit}" = "yes" ; then
if test "${enable_udlimit}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#########
@@ -11746,16 +11621,9 @@ else
enable_geopoly=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support GEOPOLY" >&5
$as_echo_n "checking whether to support GEOPOLY... " >&6; }
if test "${enable_geopoly}" = "yes" -o "${enable_all}" = "yes" ; then
if test "${enable_geopoly}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
enable_rtree=yes
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#########
@@ -11765,15 +11633,8 @@ if test "${enable_rtree+set}" = set; then :
enableval=$enable_rtree;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support RTREE" >&5
$as_echo_n "checking whether to support RTREE... " >&6; }
if test "${enable_rtree}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#########
@@ -11783,16 +11644,9 @@ if test "${enable_session+set}" = set; then :
enableval=$enable_session;
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support SESSION" >&5
$as_echo_n "checking whether to support SESSION... " >&6; }
if test "${enable_session}" = "yes" -o "${enable_all}" = "yes" ; then
if test "${enable_session}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
#########
@@ -12378,7 +12232,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.35.4, which was
This file was extended by sqlite $as_me 3.30.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12444,7 +12298,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
sqlite config.status 3.35.4
sqlite config.status 3.30.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
+14 -67
View File
@@ -134,10 +134,8 @@ 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
break
fi
TCLLIBDIR=$i
break
done
TCLLIBDIR="${TCLLIBDIR}/sqlite3"
fi
@@ -157,6 +155,11 @@ AC_SUBST(VERSION)
RELEASE=`cat $srcdir/VERSION`
AC_MSG_NOTICE(Release set to $RELEASE)
AC_SUBST(RELEASE)
VERSION_NUMBER=[`cat $srcdir/VERSION \
| sed 's/[^0-9]/ /g' \
| awk '{printf "%d%03d%03d",$1,$2,$3}'`]
AC_MSG_NOTICE(Version number set to $VERSION_NUMBER)
AC_SUBST(VERSION_NUMBER)
#########
# Locate a compiler for the build machine. This compiler should
@@ -555,13 +558,10 @@ AC_SEARCH_LIBS(fdatasync, [rt])
#########
# check for debug enabled
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[enable debugging & verbose explain]))
AC_MSG_CHECKING([build type])
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
AC_MSG_RESULT([debug])
else
TARGET_DEBUG="-DNDEBUG"
AC_MSG_RESULT([release])
fi
AC_SUBST(TARGET_DEBUG)
@@ -569,7 +569,7 @@ AC_SUBST(TARGET_DEBUG)
# See whether we should use the amalgamation to build
AC_ARG_ENABLE(amalgamation, AC_HELP_STRING([--disable-amalgamation],
[Disable the amalgamation and instead build all files separately]))
if test "${enable_amalgamation}" = "no" ; then
if test "${enable_amalgamation}" == "no" ; then
USE_AMALGAMATION=0
fi
AC_SUBST(USE_AMALGAMATION)
@@ -591,27 +591,6 @@ else
OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1"
fi
##########
# Do we want to support math functions
#
AC_ARG_ENABLE(math,
AC_HELP_STRING([--disable-math],[Disable math functions]))
AC_MSG_CHECKING([whether to support math functions])
if test "$enable_math" = "no"; then
AC_MSG_RESULT([no])
else
AC_MSG_RESULT([yes])
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MATH_FUNCTIONS"
AC_SEARCH_LIBS(ceil, m)
fi
########
# The --enable-all argument is short-hand to enable
# multiple extensions.
AC_ARG_ENABLE(all, AC_HELP_STRING([--enable-all],
[Enable FTS4, FTS5, Geopoly, JSON, RTree, Sessions]))
##########
# Do we want to support memsys3 and/or memsys5
#
@@ -638,43 +617,27 @@ fi
# See whether we should enable Full Text Search extensions
AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3],
[Enable the FTS3 extension]))
AC_MSG_CHECKING([whether to support FTS3])
if test "${enable_fts3}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4],
[Enable the FTS4 extension]))
AC_MSG_CHECKING([whether to support FTS4])
if test "${enable_fts4}" = "yes" -o "${enable_all}" = "yes" ; then
AC_MSG_RESULT([yes])
if test "${enable_fts4}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
AC_SEARCH_LIBS([log],[m])
else
AC_MSG_RESULT([no])
fi
AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5],
[Enable the FTS5 extension]))
AC_MSG_CHECKING([whether to support FTS5])
if test "${enable_fts5}" = "yes" -o "${enable_all}" = "yes" ; then
AC_MSG_RESULT([yes])
if test "${enable_fts5}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
AC_SEARCH_LIBS([log],[m])
else
AC_MSG_RESULT([no])
fi
#########
# See whether we should enable JSON1
AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1],[Enable the JSON1 extension]))
AC_MSG_CHECKING([whether to support JSON])
if test "${enable_json1}" = "yes" -o "${enable_all}" = "yes" ; then
if test "${enable_json1}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
#########
@@ -682,12 +645,8 @@ fi
# statements.
AC_ARG_ENABLE(update-limit, AC_HELP_STRING([--enable-update-limit],
[Enable the UPDATE/DELETE LIMIT clause]))
AC_MSG_CHECKING([whether to support LIMIT on UPDATE and DELETE statements])
if test "${enable_update_limit}" = "yes" ; then
if test "${enable_udlimit}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
#########
@@ -695,38 +654,26 @@ fi
AC_ARG_ENABLE(geopoly, AC_HELP_STRING([--enable-geopoly],
[Enable the GEOPOLY extension]),
[enable_geopoly=yes],[enable_geopoly=no])
AC_MSG_CHECKING([whether to support GEOPOLY])
if test "${enable_geopoly}" = "yes" -o "${enable_all}" = "yes" ; then
if test "${enable_geopoly}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
enable_rtree=yes
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
#########
# See whether we should enable RTREE
AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree],
[Enable the RTREE extension]))
AC_MSG_CHECKING([whether to support RTREE])
if test "${enable_rtree}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
#########
# See whether we should enable the SESSION extension
AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session],
[Enable the SESSION extension]))
AC_MSG_CHECKING([whether to support SESSION])
if test "${enable_session}" = "yes" -o "${enable_all}" = "yes" ; then
if test "${enable_session}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
#########
+184 -360
View File
File diff suppressed because it is too large Load Diff
-142
View File
@@ -1,142 +0,0 @@
# The new-security-options branch
## The problem that the [new-security-options](/timeline?r=new-security-options) branch tries to solve
An attacker might modify the schema of an SQLite database by adding
structures that cause code to run when some other application opens and
reads the database. For example, the attacker might replace a table
definition with a view. Or the attacker might add triggers to tables
or views, or add new CHECK constraints or generated columns or indexes
with expressions in the index list or in the WHERE clause. If the
added features invoke SQL functions or virtual tables with side effects,
that might cause harm to the system if run by a high-privilege victim.
Or, the added features might exfiltrate information if the database is
read by a high-privilege victim.
The changes in this branch strive to make it easier for high-privilege
applications to safely read SQLite database files that might have been
maliciously corrupted by an attacker.
## Overview of changes in [new-security-options](/timeline?r=new-security-options)
The basic idea is to tag every SQL function and virtual table with one
of three risk levels:
1. Innocuous
2. Normal
3. Direct-Only
Innocuous functions/vtabs are safe and can be used at any time.
Direct-only elements, in contrast, might have cause side-effects and
should only be used from top-level SQL, not from within triggers or views nor
in elements of the schema such as CHECK constraint, DEFAULT values,
generated columns, index expressions, or in the WHERE clause of a
partial index that are potentially under the control of an attacker.
Normal elements behave like Innocuous if TRUSTED\_SCHEMA=on
and behave like direct-only if TRUSTED\_SCHEMA=off.
Application-defined functions and virtual tables go in as Normal unless
the application takes deliberate steps to change the risk level.
For backwards compatibility, the default is TRUSTED\_SCHEMA=on. Documentation
will be updated to recommend applications turn TRUSTED\_SCHEMA to off.
An innocuous function or virtual table is one that can only read content
from the database file in which it resides, and can only alter the database
in which it resides. Most SQL functions are innocuous. For example, there
is no harm in an attacker running the abs() function.
Direct-only elements that have side-effects that go outside the database file
in which it lives, or return information from outside of the database file.
Examples of direct-only elements include:
1. The fts3\_tokenizer() function
2. The writefile() function
3. The readfile() function
4. The zipvfs virtual table
5. The csv virtual table
We do not want an attacker to be able to add these kinds of things to
the database schema and possibly trick a high-privilege application
from performing any of these actions. Therefore, functions and vtabs
with side-effects are marked as Direct-Only.
Legacy applications might add other risky functions or vtabs. Those will
go in as "Normal" by default. For optimal security, we want those risky
app-defined functions and vtabs to be direct-only, but making that the
default might break some legacy applications. Hence, all app-defined
functions and vtabs go in as Normal, but the application can switch them
over to "Direct-Only" behavior using a single pragma.
The restrictions on the use of functions and virtual tables do not apply
to TEMP. A TEMP VIEW or a TEMP TRIGGER can use any valid SQL function
or virtual table. The idea is that TEMP views and triggers must be
directly created by the application and are thus under the control of the
application. TEMP views and triggers cannot be created by an attacker who
corrupts the schema of a persistent database file. Hence TEMP views and
triggers are safe.
## Specific changes
1. New sqlite3\_db\_config() option SQLITE\_DBCONFIG\_TRUSTED\_SCHEMA for
turning TRUSTED\_SCHEMA on and off. It defaults to ON.
2. Compile-time option -DSQLITE\_TRUSTED\_SCHEMA=0 causes the default
TRUSTED\_SCHEMA setting to be off.
3. New pragma "PRAGMA trusted\_schema=(ON\|OFF);". This provides access
to the TRUSTED_SCHEMA setting for application coded using scripting
languages or other secondary languages where they are unable to make
calls to sqlite3\_db\_config().
4. New options for the "enc" parameter to sqlite3\_create\_function() and
its kin:
<ol type="a">
<li> _SQLITE\_INNOCUOUS_ &rarr; tags the new functions as Innocuous
<li> _SQLITE\_DIRECTONLY_ &rarr; tags the new functions as Direct-Only
</ol>
5. New options to sqlite3\_vtab\_config():
<ol type="a">
<li> _SQLITE\_VTAB\_INNOCUOUS_ &rarr; tags the vtab as Innocuous
<li> _SQLITE\_VTAB\_DIRECTONLY_ &rarr; tags the vtab as Direct-Only
</ol>
6. Change many of the functions and virtual tables in the SQLite source
tree to use one of the tags above.
7. Enhanced PRAGMA function\_list and virtual-table "pragma\_function\_list"
with additional columns. The columns now are:
<ul>
<li> _name_ &rarr; Name of the function
<li> _builtin_ &rarr; 1 for built-in functions. 0 otherwise.
<li> _type_ &rarr; 's'=Scalar, 'a'=Aggregate, 'w'=Window
<li> _enc_ &rarr; 'utf8', 'utf16le', or 'utf16be'
<li> _narg_ &rarr; number of argument
<li> _flags_ &rarr; Bitmask of SQLITE\_INNOCUOUS, SQLITE\_DIRECTONLY,
SQLITE\_DETERMINISTIC, SQLITE\_SUBTYPE, and
SQLITE\_FUNC\_INTERNAL flags.
</ul>
<p>The last four columns are new.
8. The function\_list PRAGMA now also shows all entries for each function.
So, for example, if a function can take either 2 or 3 arguments,
there are separate rows for the 2-argument and 3-argument versions of
the function.
## Additional Notes
The function_list enhancements allow the application to query the set
of SQL functions that meet various criteria. For example, to see all
SQL functions that are never allowed to be used in the schema or in
trigger or views:
~~~
SELECT DISTINCT name FROM pragma_function_list
WHERE (flags & 0x80000)!=0
ORDER BY name;
~~~
Doing the same is not possible for virtual tables, as a virtual table
might be Innocuous, Normal, or Direct-Only depending on the arguments
passed into the xConnect method.
-49
View File
@@ -1,49 +0,0 @@
20-11-2020
# Memory Allocation In vdbesort.c
Memory allocation is slightly different depending on:
* whether or not SQLITE_CONFIG_SMALL_MALLOC is set, and
* whether or not worker threads are enabled.
## SQLITE_CONFIG_SMALL_MALLOC=0
Assuming SQLITE_CONFIG_SMALL_MALLOC is not set, keys passed to the sorter are
added to an in-memory buffer. This buffer is grown using sqlite3Realloc() as
required it reaches the size configured for the main pager cache using "PRAGMA
cache_size". i.e. if the user has executed "PRAGMA main.cache_size = -2048",
then this buffer is allowed to grow up to 2MB in size.
Once the buffer has grown to its threshold, keys are sorted and written to
a temp file. If worker threads are not enabled, this is the only significant
allocation the sorter module makes. After keys are sorted and flushed out to
the temp file, the buffer is reused to accumulate the next batch of keys.
If worker threads are available, then the buffer is passed to a worker thread
to sort and flush once it is full, and a new buffer allocated to allow the
main thread to continue to accumulate keys. Buffers are reused once they
have been flushed, so in this case at most (nWorker+1) buffers are allocated
and used, where nWorker is the number of configured worker threads.
There are no other significant users of heap memory in the sorter module.
Once sorted buffers of keys have been flushed to disk, they are read back
either by mapping the file (via sqlite3_file.xFetch()) or else read back
in one page at a time.
All buffers are allocated by the main thread. A sorter object is associated
with a single database connection, to which it holds a pointer.
## SQLITE_CONFIG_SMALL_MALLOC=1
This case is similar to the above, except that instead of accumulating
multiple keys in a single large buffer, sqlite3VdbeSorterWrite() stores
keys in a regular heap-memory linked list (one allocation per element).
List elements are freed as they are flushed to disk, either by the main
thread or by a worker thread.
Each time a key is added the sorter (and an allocation made),
sqlite3HeapNearlyFull() is called. If it returns true, the current
list of keys is flushed to a temporary file, even if it has not yet
reached the size threshold.
-88
View File
@@ -1,88 +0,0 @@
# Wal-Mode Blocking Locks
On some Unix-like systems, SQLite may be configured to use POSIX blocking locks
by:
* building the library with SQLITE\_ENABLE\_SETLK\_TIMEOUT defined, and
* configuring a timeout in ms using the sqlite3\_busy\_timeout() API.
Blocking locks may be advantageous as (a) waiting database clients do not
need to continuously poll the database lock, and (b) using blocking locks
facilitates transfer of OS priority between processes when a high priority
process is blocked by a lower priority one.
Only read/write clients use blocking locks. Clients that have read-only access
to the \*-shm file nevery use blocking locks.
Threads or processes that access a single database at a time never deadlock as
a result of blocking database locks. But it is of course possible for threads
that lock multiple databases simultaneously to do so. In most cases the OS will
detect the deadlock and return an error.
## Wal Recovery
Wal database "recovery" is a process required when the number of connected
database clients changes from zero to one. In this case, a client is
considered to connect to the database when it first reads data from it.
Before recovery commences, an exclusive WRITER lock is taken.
Without blocking locks, if two clients attempt recovery simultaneously, one
fails to obtain the WRITER lock and either invokes the busy-handler callback or
returns SQLITE\_BUSY to the user. With blocking locks configured, the second
client blocks on the WRITER lock.
## Database Readers
Usually, read-only are not blocked by any other database clients, so they
have no need of blocking locks.
If a read-only transaction is being opened on a snapshot, the CHECKPOINTER
lock is required briefly as part of opening the transaction (to check that a
checkpointer is not currently overwriting the snapshot being opened). A
blocking lock is used to obtain the CHECKPOINTER lock in this case. A snapshot
opener may therefore block on and transfer priority to a checkpointer in some
cases.
## Database Writers
A database writer must obtain the exclusive WRITER lock. It uses a blocking
lock to do so if any of the following are true:
* the transaction is an implicit one consisting of a single DML or DDL
statement, or
* the transaction is opened using BEGIN IMMEDIATE or BEGIN EXCLUSIVE, or
* the first SQL statement executed following the BEGIN command is a DML or
DDL statement (not a read-only statement like a SELECT).
In other words, in all cases except when an open read-transaction is upgraded
to a write-transaction. In that case a non-blocking lock is used.
## Database Checkpointers
Database checkpointers takes the following locks, in order:
* The exclusive CHECKPOINTER lock.
* The exclusive WRITER lock (FULL, RESTART and TRUNCATE only).
* Exclusive lock on read-mark slots 1-N. These are immediately released after being taken.
* Exclusive lock on read-mark 0.
* Exclusive lock on read-mark slots 1-N again. These are immediately released
after being taken (RESTART and TRUNCATE only).
All of the above use blocking locks.
## Summary
With blocking locks configured, the only cases in which clients should see an
SQLITE\_BUSY error are:
* if the OS does not grant a blocking lock before the configured timeout
expires, and
* when an open read-transaction is upgraded to a write-transaction.
In all other cases the blocking locks implementation should prevent clients
from having to handle SQLITE\_BUSY errors and facilitate appropriate transfer
of priorities between competing clients.
Clients that lock multiple databases simultaneously must be wary of deadlock.
+1
View File
@@ -1704,3 +1704,4 @@ int sqlite3async_control(int op, ...){
}
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ASYNCIO) */
+1
View File
@@ -220,3 +220,4 @@ int sqlite3async_control(int op, ...);
} /* End of the 'extern "C"' block */
#endif
#endif /* ifndef __SQLITEASYNC_H_ */
+5 -77
View File
@@ -28,7 +28,6 @@ if {[info commands sqlite3_expert_new]==""} {
return
}
set CLI [test_binary_name sqlite3]
set CMD [test_binary_name sqlite3_expert]
@@ -38,9 +37,6 @@ proc squish {txt} {
proc do_setup_rec_test {tn setup sql res} {
reset_db
if {[info exists ::set_main_db_name]} {
dbconfig_maindbname_icecube db
}
db eval $setup
uplevel [list do_rec_test $tn $sql $res]
}
@@ -80,10 +76,6 @@ foreach {tn setup} {
}
}
3 {
if {[info commands sqlite3_expert_new]==""} { continue }
set ::set_main_db_name 1
}
4 {
if {![file executable $CLI]} { continue }
proc do_rec_test {tn sql res} {
@@ -327,70 +319,6 @@ do_setup_rec_test $tn.16 {
SCAN TABLE t1
}
do_setup_rec_test $tn.17.1 {
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
} {
SELECT * FROM example WHERE a=?
} {
(no new indexes)
SEARCH TABLE example USING INDEX sqlite_autoindex_example_1 (A=?)
}
do_setup_rec_test $tn.17.2 {
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
} {
SELECT * FROM example WHERE b=?
} {
CREATE INDEX example_idx_00000042 ON example(B);
SEARCH TABLE example USING INDEX example_idx_00000042 (B=?)
}
do_setup_rec_test $tn.17.3 {
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
} {
SELECT * FROM example WHERE a=? AND b=?
} {
(no new indexes)
SEARCH TABLE example USING INDEX sqlite_autoindex_example_1 (A=? AND B=?)
}
do_setup_rec_test $tn.17.4 {
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
} {
SELECT * FROM example WHERE a=? AND b>?
} {
(no new indexes)
SEARCH TABLE example USING INDEX sqlite_autoindex_example_1 (A=? AND B>?)
}
do_setup_rec_test $tn.17.5 {
CREATE TABLE example (A INTEGER, B INTEGER, C INTEGER, PRIMARY KEY (A,B));
} {
SELECT * FROM example WHERE a>? AND b=?
} {
CREATE INDEX example_idx_0000cb3f ON example(B, A);
SEARCH TABLE example USING INDEX example_idx_0000cb3f (B=? AND A>?)
}
do_setup_rec_test $tn.18.0 {
CREATE TABLE SomeObject (
a INTEGER PRIMARY KEY,
x TEXT GENERATED ALWAYS AS(HEX(a)) VIRTUAL
);
} {
SELECT x FROM SomeObject;
} {
(no new indexes)
SCAN TABLE SomeObject
}
do_setup_rec_test $tn.18.1 {
CREATE TABLE SomeObject (
a INTEGER PRIMARY KEY,
x TEXT GENERATED ALWAYS AS(HEX(a)) VIRTUAL
);
} {
SELECT * FROM SomeObject WHERE x=?;
} {
CREATE INDEX SomeObject_idx_00000078 ON SomeObject(x);
SEARCH TABLE SomeObject USING COVERING INDEX SomeObject_idx_00000078 (x=?)
}
}
proc do_candidates_test {tn sql res} {
@@ -408,7 +336,7 @@ proc do_candidates_test {tn sql res} {
reset_db
do_execsql_test 5.0 {
do_execsql_test 4.0 {
CREATE TABLE t1(a, b);
CREATE TABLE t2(c, d);
@@ -418,7 +346,7 @@ 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;
}
do_candidates_test 5.1 {
do_candidates_test 4.1 {
SELECT * FROM t1,t2 WHERE (b=? OR a=?) AND (c=? OR d=?)
} {
CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
@@ -427,14 +355,14 @@ do_candidates_test 5.1 {
CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
}
do_candidates_test 5.2 {
do_candidates_test 4.2 {
SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=?
} {
CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 17
CREATE INDEX t2_idx_0001295b ON t2(c, d); -- stat1: 100 20 5
}
do_execsql_test 5.3 {
do_execsql_test 4.3 {
CREATE INDEX t1_idx_00000061 ON t1(a); -- stat1: 100 50
CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
CREATE INDEX t1_idx_000123a7 ON t1(a, b); -- stat1: 100 50 16
@@ -454,5 +382,5 @@ do_execsql_test 5.3 {
t2 t2_idx_0001295b {100 20 5}
}
finish_test
finish_test
+17 -26
View File
@@ -685,9 +685,8 @@ static int idxGetTableInfo(
IdxTable *pNew = 0;
int rc, rc2;
char *pCsr = 0;
int nPk = 0;
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q", zTab);
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
nByte += 1 + STRLEN(zCol);
@@ -696,7 +695,6 @@ static int idxGetTableInfo(
);
nByte += 1 + STRLEN(zCol);
nCol++;
nPk += (sqlite3_column_int(p1, 5)>0);
}
rc2 = sqlite3_reset(p1);
if( rc==SQLITE_OK ) rc = rc2;
@@ -716,7 +714,7 @@ static int idxGetTableInfo(
const char *zCol = (const char*)sqlite3_column_text(p1, 1);
int nCopy = STRLEN(zCol) + 1;
pNew->aCol[nCol].zName = pCsr;
pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5)==1 && nPk==1);
pNew->aCol[nCol].iPk = sqlite3_column_int(p1, 5);
memcpy(pCsr, zCol, nCopy);
pCsr += nCopy;
@@ -1130,19 +1128,14 @@ int idxFindIndexes(
/* int iParent = sqlite3_column_int(pExplain, 1); */
/* int iNotUsed = sqlite3_column_int(pExplain, 2); */
const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
int nDetail;
int nDetail = STRLEN(zDetail);
int i;
if( !zDetail ) continue;
nDetail = STRLEN(zDetail);
for(i=0; i<nDetail; i++){
const char *zIdx = 0;
if( i+13<nDetail && memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
zIdx = &zDetail[i+13];
}else if( i+22<nDetail
&& memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0
){
}else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){
zIdx = &zDetail[i+22];
}
if( zIdx ){
@@ -1225,7 +1218,7 @@ static int idxProcessOneTrigger(
IdxTable *pTab = pWrite->pTab;
const char *zTab = pTab->zName;
const char *zSql =
"SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema "
"SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_master "
"WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
"ORDER BY type;";
sqlite3_stmt *pSelect = 0;
@@ -1325,12 +1318,12 @@ static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
** 2) Create the equivalent virtual table in dbv.
*/
rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
"SELECT type, name, sql, 1 FROM sqlite_schema "
"SELECT type, name, sql, 1 FROM sqlite_master "
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
" UNION ALL "
"SELECT type, name, sql, 2 FROM sqlite_schema "
"SELECT type, name, sql, 2 FROM sqlite_master "
"WHERE type = 'trigger'"
" AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
" AND tbl_name IN(SELECT name FROM sqlite_master WHERE type = 'view') "
"ORDER BY 4, 1"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
@@ -1500,7 +1493,7 @@ static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
int rc = SQLITE_OK;
const char *zMax =
"SELECT max(i.seqno) FROM "
" sqlite_schema AS s, "
" sqlite_master AS s, "
" pragma_index_list(s.name) AS l, "
" pragma_index_info(l.name) AS i "
"WHERE s.type = 'table'";
@@ -1653,7 +1646,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
const char *zAllIndex =
"SELECT s.rowid, s.name, l.name FROM "
" sqlite_schema AS s, "
" sqlite_master AS s, "
" pragma_index_list(s.name) AS l "
"WHERE s.type = 'table'";
const char *zIndexXInfo =
@@ -1721,15 +1714,13 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
idxFinalize(&rc, pIndexXInfo);
idxFinalize(&rc, pWrite);
if( pCtx ){
for(i=0; i<pCtx->nSlot; i++){
sqlite3_free(pCtx->aSlot[i].z);
}
sqlite3_free(pCtx);
for(i=0; i<pCtx->nSlot; i++){
sqlite3_free(pCtx->aSlot[i].z);
}
sqlite3_free(pCtx);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_master", 0, 0, 0);
}
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
@@ -1768,7 +1759,7 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
if( rc==SQLITE_OK ){
sqlite3_stmt *pSql;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
"SELECT sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%%'"
" AND sql NOT LIKE 'CREATE VIRTUAL %%'"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
@@ -1959,4 +1950,4 @@ void sqlite3_expert_destroy(sqlite3expert *p){
}
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
#endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */
+3 -3
View File
@@ -10,8 +10,8 @@
**
*************************************************************************
*/
#if !defined(SQLITEEXPERT_H)
#define SQLITEEXPERT_H 1
#include "sqlite3.h"
typedef struct sqlite3expert sqlite3expert;
@@ -165,4 +165,4 @@ const char *sqlite3_expert_report(sqlite3expert*, int iStmt, int eReport);
*/
void sqlite3_expert_destroy(sqlite3expert*);
#endif /* !defined(SQLITEEXPERT_H) */
+2
View File
@@ -174,3 +174,5 @@ EXTERNAL CONTENT FTS4 TABLES
only be useful if the full-text index has somehow become corrupt. It is an
error to attempt to rebuild the full-text index maintained by a contentless
FTS4 table.
+64 -153
View File
@@ -308,6 +308,18 @@
SQLITE_EXTENSION_INIT1
#endif
/*
** The following are copied from sqliteInt.h.
**
** Constants for the largest and smallest possible 64-bit signed integers.
** These macros are designed to work correctly on both 32-bit and 64-bit
** compilers.
*/
#ifndef SQLITE_AMALGAMATION
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
#endif
static int fts3EvalNext(Fts3Cursor *pCsr);
static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
@@ -352,7 +364,12 @@ int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){
v = (*ptr++); \
if( (v & mask2)==0 ){ var = v; return ret; }
int sqlite3Fts3GetVarintU(const char *pBuf, sqlite_uint64 *v){
/*
** Read a 64-bit variable-length integer from memory starting at p[0].
** Return the number of bytes read, or 0 on error.
** The value is stored in *v.
*/
int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
const unsigned char *p = (const unsigned char*)pBuf;
const unsigned char *pStart = p;
u32 a;
@@ -374,41 +391,6 @@ int sqlite3Fts3GetVarintU(const char *pBuf, sqlite_uint64 *v){
return (int)(p - pStart);
}
/*
** Read a 64-bit variable-length integer from memory starting at p[0].
** Return the number of bytes read, or 0 on error.
** The value is stored in *v.
*/
int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
return sqlite3Fts3GetVarintU(pBuf, (sqlite3_uint64*)v);
}
/*
** Read a 64-bit variable-length integer from memory starting at p[0] and
** not extending past pEnd[-1].
** Return the number of bytes read, or 0 on error.
** The value is stored in *v.
*/
int sqlite3Fts3GetVarintBounded(
const char *pBuf,
const char *pEnd,
sqlite_int64 *v
){
const unsigned char *p = (const unsigned char*)pBuf;
const unsigned char *pStart = p;
const unsigned char *pX = (const unsigned char*)pEnd;
u64 b = 0;
int shift;
for(shift=0; shift<=63; shift+=7){
u64 c = p<pX ? *p : 0;
p++;
b += (c&0x7F) << shift;
if( (c & 0x80)==0 ) break;
}
*v = b;
return (int)(p - pStart);
}
/*
** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to
** a non-negative 32-bit integer before it is returned.
@@ -962,22 +944,6 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
return zRet;
}
/*
** Buffer z contains a positive integer value encoded as utf-8 text.
** Decode this value and store it in *pnOut, returning the number of bytes
** consumed. If an overflow error occurs return a negative value.
*/
int sqlite3Fts3ReadInt(const char *z, int *pnOut){
u64 iVal = 0;
int i;
for(i=0; z[i]>='0' && z[i]<='9'; i++){
iVal = iVal*10 + (z[i] - '0');
if( iVal>0x7FFFFFFF ) return -1;
}
*pnOut = (int)iVal;
return i;
}
/*
** This function interprets the string at (*pp) as a non-negative integer
** value. It reads the integer and sets *pnOut to the value read, then
@@ -993,17 +959,19 @@ int sqlite3Fts3ReadInt(const char *z, int *pnOut){
*/
static int fts3GobbleInt(const char **pp, int *pnOut){
const int MAX_NPREFIX = 10000000;
const char *p; /* Iterator pointer */
int nInt = 0; /* Output value */
int nByte;
nByte = sqlite3Fts3ReadInt(*pp, &nInt);
if( nInt>MAX_NPREFIX ){
nInt = 0;
}
if( nByte==0 ){
return SQLITE_ERROR;
for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
nInt = nInt * 10 + (p[0] - '0');
if( nInt>MAX_NPREFIX ){
nInt = 0;
break;
}
}
if( p==*pp ) return SQLITE_ERROR;
*pnOut = nInt;
*pp += nByte;
*pp = p;
return SQLITE_OK;
}
@@ -1518,10 +1486,6 @@ static int fts3InitVtab(
fts3DatabasePageSize(&rc, p);
p->nNodeSize = p->nPgsz-35;
#if defined(SQLITE_DEBUG)||defined(SQLITE_TEST)
p->nMergeCount = FTS3_MERGE_COUNT;
#endif
/* Declare the table schema to SQLite. */
fts3DeclareVtab(&rc, p);
@@ -1617,10 +1581,6 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
int iDocidLe = -1; /* Index of docid<=x constraint, if present */
int iIdx;
if( p->bLock ){
return SQLITE_ERROR;
}
/* By default use a full table scan. This is an expensive option,
** so search through the constraints to see if a more efficient
** strategy is possible.
@@ -1819,11 +1779,7 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
}else{
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
if( !zSql ) return SQLITE_NOMEM;
p->bLock++;
rc = sqlite3_prepare_v3(
p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
p->bLock--;
rc = sqlite3_prepare_v3(p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
sqlite3_free(zSql);
}
if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1;
@@ -1841,15 +1797,11 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
if( pCsr->isRequireSeek ){
rc = fts3CursorSeekStmt(pCsr);
if( rc==SQLITE_OK ){
Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab;
pTab->bLock++;
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
pCsr->isRequireSeek = 0;
if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
pTab->bLock--;
return SQLITE_OK;
}else{
pTab->bLock--;
rc = sqlite3_reset(pCsr->pStmt);
if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){
/* If no row was found and no error has occurred, then the %_content
@@ -1898,7 +1850,6 @@ static int fts3ScanInteriorNode(
i64 nAlloc = 0; /* Size of allocated buffer */
int isFirstTerm = 1; /* True when processing first term on page */
sqlite3_int64 iChild; /* Block id of child node to descend to */
int nBuffer = 0; /* Total term size */
/* Skip over the 'height' varint that occurs at the start of every
** interior node. Then load the blockid of the left-child of the b-tree
@@ -1923,15 +1874,12 @@ static int fts3ScanInteriorNode(
int cmp; /* memcmp() result */
int nSuffix; /* Size of term suffix */
int nPrefix = 0; /* Size of term prefix */
int nBuffer; /* Total term size */
/* Load the next term on the node into zBuffer. Use realloc() to expand
** the size of zBuffer if required. */
if( !isFirstTerm ){
zCsr += fts3GetVarint32(zCsr, &nPrefix);
if( nPrefix>nBuffer ){
rc = FTS_CORRUPT_VTAB;
goto finish_scan;
}
}
isFirstTerm = 0;
zCsr += fts3GetVarint32(zCsr, &nSuffix);
@@ -2025,7 +1973,7 @@ static int fts3SelectLeaf(
fts3GetVarint32(zNode, &iHeight);
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
if( rc==SQLITE_OK && iHeight>1 ){
char *zBlob = 0; /* Blob read from %_segments table */
@@ -2045,13 +1993,7 @@ static int fts3SelectLeaf(
rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0);
}
if( rc==SQLITE_OK ){
int iNewHeight = 0;
fts3GetVarint32(zBlob, &iNewHeight);
if( iNewHeight>=iHeight ){
rc = FTS_CORRUPT_VTAB;
}else{
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
}
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
}
sqlite3_free(zBlob);
}
@@ -2068,7 +2010,7 @@ static void fts3PutDeltaVarint(
sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */
sqlite3_int64 iVal /* Write this value to the list */
){
assert_fts3_nc( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
*pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
*piPrev = iVal;
}
@@ -2185,9 +2127,7 @@ static void fts3ReadNextPos(
sqlite3_int64 *pi /* IN/OUT: Value read from position-list */
){
if( (**pp)&0xFE ){
int iVal;
*pp += fts3GetVarint32((*pp), &iVal);
*pi += iVal;
fts3GetDeltaVarint(pp, pi);
*pi -= 2;
}else{
*pi = POSITION_LIST_END;
@@ -2267,9 +2207,6 @@ static int fts3PoslistMerge(
*/
fts3GetDeltaVarint(&p1, &i1);
fts3GetDeltaVarint(&p2, &i2);
if( i1<2 || i2<2 ){
break;
}
do {
fts3PutDeltaVarint(&p, &iPrev, (i1<i2) ? i1 : i2);
iPrev -= 2;
@@ -2338,7 +2275,7 @@ static int fts3PoslistPhraseMerge(
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
assert( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
p1 += fts3GetVarint32(p1, &iCol1);
@@ -2511,12 +2448,12 @@ static void fts3GetDeltaVarint3(
if( *pp>=pEnd ){
*pp = 0;
}else{
u64 iVal;
*pp += sqlite3Fts3GetVarintU(*pp, &iVal);
sqlite3_int64 iVal;
*pp += sqlite3Fts3GetVarint(*pp, &iVal);
if( bDescIdx ){
*pVal = (i64)((u64)*pVal - iVal);
*pVal -= iVal;
}else{
*pVal = (i64)((u64)*pVal + iVal);
*pVal += iVal;
}
}
}
@@ -2543,16 +2480,15 @@ static void fts3PutDeltaVarint3(
int *pbFirst, /* IN/OUT: True after first int written */
sqlite3_int64 iVal /* Write this value to the list */
){
sqlite3_uint64 iWrite;
sqlite3_int64 iWrite;
if( bDescIdx==0 || *pbFirst==0 ){
assert_fts3_nc( *pbFirst==0 || iVal>=*piPrev );
iWrite = (u64)iVal - (u64)*piPrev;
iWrite = iVal - *piPrev;
}else{
assert_fts3_nc( *piPrev>=iVal );
iWrite = (u64)*piPrev - (u64)iVal;
iWrite = *piPrev - iVal;
}
assert( *pbFirst || *piPrev==0 );
assert_fts3_nc( *pbFirst==0 || iWrite>0 );
assert( *pbFirst==0 || iWrite>=0 );
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
*piPrev = iVal;
*pbFirst = 1;
@@ -2568,8 +2504,7 @@ static void fts3PutDeltaVarint3(
** Using this makes it easier to write code that can merge doclists that are
** sorted in either ascending or descending order.
*/
/* #define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i64)((u64)i1-i2)) */
#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1>i2?1:((i1==i2)?0:-1)))
#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1-i2))
/*
** This function does an "OR" merge of two doclists (output contains all
@@ -2983,7 +2918,7 @@ static int fts3SegReaderCursor(
** Fts3SegReaderPending might segfault, as the data structures used by
** fts4aux are not completely populated. So it's easiest to filter these
** calls out here. */
if( iLevel<0 && p->aIndex && p->iPrevLangid==iLangid ){
if( iLevel<0 && p->aIndex ){
Fts3SegReader *pSeg = 0;
rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg);
if( rc==SQLITE_OK && pSeg ){
@@ -3246,8 +3181,6 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
int rc;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){
Fts3Table *pTab = (Fts3Table*)pCursor->pVtab;
pTab->bLock++;
if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){
pCsr->isEof = 1;
rc = sqlite3_reset(pCsr->pStmt);
@@ -3255,7 +3188,6 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
rc = SQLITE_OK;
}
pTab->bLock--;
}else{
rc = fts3EvalNext((Fts3Cursor *)pCursor);
}
@@ -3316,10 +3248,6 @@ static int fts3FilterMethod(
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
if( p->bLock ){
return SQLITE_ERROR;
}
eSearch = (idxNum & 0x0000FFFF);
assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( p->pSegments==0 );
@@ -3391,11 +3319,7 @@ static int fts3FilterMethod(
);
}
if( zSql ){
p->bLock++;
rc = sqlite3_prepare_v3(
p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
);
p->bLock--;
rc = sqlite3_prepare_v3(p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
@@ -3479,7 +3403,7 @@ static int fts3ColumnMethod(
break;
}else{
iCol = p->nColumn;
/* no break */ deliberate_fall_through
/* fall-through */
}
default:
@@ -3722,13 +3646,9 @@ static void fts3SnippetFunc(
switch( nVal ){
case 6: nToken = sqlite3_value_int(apVal[5]);
/* no break */ deliberate_fall_through
case 5: iCol = sqlite3_value_int(apVal[4]);
/* no break */ deliberate_fall_through
case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
/* no break */ deliberate_fall_through
case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
/* no break */ deliberate_fall_through
case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
}
if( !zEllipsis || !zEnd || !zStart ){
@@ -4416,7 +4336,7 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
int bIncrOk = (bOptOk
&& pCsr->bDesc==pTab->bDescIdx
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
#ifdef SQLITE_TEST
&& pTab->bNoIncrDoclist==0
#endif
);
@@ -4527,7 +4447,7 @@ void sqlite3Fts3DoclistNext(
assert( nDoclist>0 );
assert( *pbEof==0 );
assert_fts3_nc( p || *piDocid==0 );
assert( p || *piDocid==0 );
assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) );
if( p==0 ){
@@ -4558,16 +4478,15 @@ static void fts3EvalDlPhraseNext(
u8 *pbEof
){
char *pIter; /* Used to iterate through aAll */
char *pEnd; /* 1 byte past end of aAll */
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
if( pDL->pNextDocid ){
pIter = pDL->pNextDocid;
assert( pDL->aAll!=0 || pIter==0 );
}else{
pIter = pDL->aAll;
}
if( pIter==0 || pIter>=(pEnd = pDL->aAll + pDL->nAll) ){
if( pIter>=pEnd ){
/* We have already reached the end of this doclist. EOF. */
*pbEof = 1;
}else{
@@ -4939,13 +4858,12 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
if( rc!=SQLITE_OK ) return rc;
a = sqlite3_column_blob(pStmt, 0);
testcase( a==0 ); /* If %_stat.value set to X'' */
if( a ){
pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
while( a<pEnd ){
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nByte);
}
assert( a );
pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
a += sqlite3Fts3GetVarint(a, &nDoc);
while( a<pEnd ){
a += sqlite3Fts3GetVarint(a, &nByte);
}
if( nDoc==0 || nByte==0 ){
sqlite3_reset(pStmt);
@@ -5177,7 +5095,7 @@ static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){
**
** Parameter nNear is passed the NEAR distance of the expression (5 in
** the example above). When this function is called, *paPoslist points to
** the position list, and *pnToken is the number of phrase tokens in the
** the position list, and *pnToken is the number of phrase tokens in, the
** phrase on the other side of the NEAR operator to pPhrase. For example,
** if pPhrase refers to the "def ghi" phrase, then *paPoslist points to
** the position list associated with phrase "abc".
@@ -5212,12 +5130,10 @@ static int fts3EvalNearTrim(
);
if( res ){
nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 );
if( nNew>=0 && nNew<=pPhrase->doclist.nList ){
assert( pPhrase->doclist.pList[nNew]=='\0' );
memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
pPhrase->doclist.nList = nNew;
}
assert( pPhrase->doclist.pList[nNew]=='\0' );
assert( nNew<=pPhrase->doclist.nList && nNew>0 );
memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
pPhrase->doclist.nList = nNew;
*paPoslist = pPhrase->doclist.pList;
*pnToken = pPhrase->nToken;
}
@@ -5326,7 +5242,6 @@ static void fts3EvalNextRow(
fts3EvalNextRow(pCsr, pLeft, pRc);
}
}
pRight->bEof = pLeft->bEof = 1;
}
}
break;
@@ -5569,10 +5484,7 @@ static int fts3EvalTestExpr(
}else
#endif
{
bHit = (
pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId
&& pExpr->pPhrase->doclist.nList>0
);
bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
}
break;
}
@@ -5835,8 +5747,7 @@ static int fts3EvalGatherStats(
fts3EvalRestart(pCsr, pRoot, &rc);
do {
fts3EvalNextRow(pCsr, pRoot, &rc);
assert_fts3_nc( pRoot->bEof==0 );
if( pRoot->bEof ) rc = FTS_CORRUPT_VTAB;
assert( pRoot->bEof==0 );
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
}
}
+1 -20
View File
@@ -196,11 +196,6 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
# define TESTONLY(X)
#endif
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
#define deliberate_fall_through
#endif /* SQLITE_AMALGAMATION */
#ifdef SQLITE_DEBUG
@@ -244,7 +239,6 @@ struct Fts3Table {
char *zLanguageid; /* languageid=xxx option, or NULL */
int nAutoincrmerge; /* Value configured by 'automerge' */
u32 nLeafAdd; /* Number of leaf blocks added this trans */
int bLock; /* Used to prevent recursive content= tbls */
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
@@ -303,23 +297,13 @@ struct Fts3Table {
int mxSavepoint; /* Largest valid xSavepoint integer */
#endif
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
#ifdef SQLITE_TEST
/* True to disable the incremental doclist optimization. This is controled
** by special insert command 'test-no-incr-doclist'. */
int bNoIncrDoclist;
/* Number of segments in a level */
int nMergeCount;
#endif
};
/* Macro to find the number of segments to merge */
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
# define MergeCount(P) ((P)->nMergeCount)
#else
# define MergeCount(P) FTS3_MERGE_COUNT
#endif
/*
** When the core wants to read from the virtual table, it creates a
** virtual table cursor (an instance of the following structure) using
@@ -583,8 +567,6 @@ int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
void sqlite3Fts3ErrMsg(char**,const char*,...);
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
int sqlite3Fts3GetVarintU(const char *, sqlite_uint64 *);
int sqlite3Fts3GetVarintBounded(const char*,const char*,sqlite3_int64*);
int sqlite3Fts3GetVarint32(const char *, int *);
int sqlite3Fts3VarintLen(sqlite3_uint64);
void sqlite3Fts3Dequote(char *);
@@ -593,7 +575,6 @@ int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
int sqlite3Fts3ReadInt(const char *z, int *pnOut);
/* fts3_tokenizer.c */
const char *sqlite3Fts3NextToken(const char *, int *);
+4 -6
View File
@@ -446,7 +446,10 @@ static int getNextNode(
if( pKey->eType==FTSQUERY_NEAR ){
assert( nKey==4 );
if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
nNear = 0;
for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
nNear = nNear * 10 + (zInput[nKey] - '0');
}
}
}
@@ -493,11 +496,6 @@ static int getNextNode(
if( *zInput=='(' ){
int nConsumed = 0;
pParse->nNest++;
#if !defined(SQLITE_MAX_EXPR_DEPTH)
if( pParse->nNest>1000 ) return SQLITE_ERROR;
#elif SQLITE_MAX_EXPR_DEPTH>0
if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR;
#endif
rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
*pnConsumed = (int)(zInput - z) + 1 + nConsumed;
return rc;
+10 -32
View File
@@ -560,7 +560,7 @@ static int fts3BestSnippet(
/* Set the *pmSeen output variable. */
for(i=0; i<nList; i++){
if( sIter.aPhrase[i].pHead ){
*pmSeen |= (u64)1 << (i%64);
*pmSeen |= (u64)1 << i;
}
}
@@ -876,7 +876,7 @@ static int fts3ExprLHits(
iStart = pExpr->iPhrase * ((p->nCol + 31) / 32);
}
if( pIter ) while( 1 ){
while( 1 ){
int nHit = fts3ColumnlistCount(&pIter);
if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
if( p->flag==FTS3_MATCHINFO_LHITS ){
@@ -1038,15 +1038,11 @@ static int fts3MatchinfoSelectDoctotal(
Fts3Table *pTab,
sqlite3_stmt **ppStmt,
sqlite3_int64 *pnDoc,
const char **paLen,
const char **ppEnd
const char **paLen
){
sqlite3_stmt *pStmt;
const char *a;
const char *pEnd;
sqlite3_int64 nDoc;
int n;
if( !*ppStmt ){
int rc = sqlite3Fts3SelectDoctotal(pTab, ppStmt);
@@ -1055,20 +1051,12 @@ static int fts3MatchinfoSelectDoctotal(
pStmt = *ppStmt;
assert( sqlite3_data_count(pStmt)==1 );
n = sqlite3_column_bytes(pStmt, 0);
a = sqlite3_column_blob(pStmt, 0);
if( a==0 ){
return FTS_CORRUPT_VTAB;
}
pEnd = a + n;
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
if( nDoc<=0 || a>pEnd ){
return FTS_CORRUPT_VTAB;
}
*pnDoc = nDoc;
a += sqlite3Fts3GetVarint(a, &nDoc);
if( nDoc==0 ) return FTS_CORRUPT_VTAB;
*pnDoc = (u32)nDoc;
if( paLen ) *paLen = a;
if( ppEnd ) *ppEnd = pEnd;
return SQLITE_OK;
}
@@ -1249,7 +1237,7 @@ static int fts3MatchinfoValues(
case FTS3_MATCHINFO_NDOC:
if( bGlobal ){
sqlite3_int64 nDoc = 0;
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0, 0);
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
pInfo->aMatchinfo[0] = (u32)nDoc;
}
break;
@@ -1258,19 +1246,14 @@ static int fts3MatchinfoValues(
if( bGlobal ){
sqlite3_int64 nDoc; /* Number of rows in table */
const char *a; /* Aggregate column length array */
const char *pEnd; /* First byte past end of length array */
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a, &pEnd);
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a);
if( rc==SQLITE_OK ){
int iCol;
for(iCol=0; iCol<pInfo->nCol; iCol++){
u32 iVal;
sqlite3_int64 nToken;
a += sqlite3Fts3GetVarint(a, &nToken);
if( a>pEnd ){
rc = SQLITE_CORRUPT_VTAB;
break;
}
iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
pInfo->aMatchinfo[iCol] = iVal;
}
@@ -1284,14 +1267,9 @@ static int fts3MatchinfoValues(
if( rc==SQLITE_OK ){
int iCol;
const char *a = sqlite3_column_blob(pSelectDocsize, 0);
const char *pEnd = a + sqlite3_column_bytes(pSelectDocsize, 0);
for(iCol=0; iCol<pInfo->nCol; iCol++){
sqlite3_int64 nToken;
a += sqlite3Fts3GetVarintBounded(a, pEnd, &nToken);
if( a>pEnd ){
rc = SQLITE_CORRUPT_VTAB;
break;
}
a += sqlite3Fts3GetVarint(a, &nToken);
pInfo->aMatchinfo[iCol] = (u32)nToken;
}
}
@@ -1322,7 +1300,7 @@ static int fts3MatchinfoValues(
if( rc!=SQLITE_OK ) break;
if( bGlobal ){
if( pCsr->pDeferred ){
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc, 0);
if( rc!=SQLITE_OK ) break;
}
rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
+2 -3
View File
@@ -188,8 +188,7 @@ static int fts3tokConnectMethod(
assert( (rc==SQLITE_OK)==(pMod!=0) );
if( rc==SQLITE_OK ){
const char * const *azArg = 0;
if( nDequote>1 ) azArg = (const char * const *)&azDequote[1];
const char * const *azArg = (const char * const *)&azDequote[1];
rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok);
}
@@ -351,7 +350,7 @@ static int fts3tokFilterMethod(
if( pCsr->zInput==0 ){
rc = SQLITE_NOMEM;
}else{
if( nByte>0 ) memcpy(pCsr->zInput, zByte, nByte);
memcpy(pCsr->zInput, zByte, nByte);
pCsr->zInput[nByte] = 0;
rc = pTab->pMod->xOpen(pTab->pTok, pCsr->zInput, nByte, &pCsr->pCsr);
if( rc==SQLITE_OK ){
+2 -4
View File
@@ -390,9 +390,7 @@ int queryTokenizer(
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB
&& sqlite3_column_bytes(pStmt, 0)==sizeof(*pp)
){
if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
}
}
@@ -481,7 +479,7 @@ int sqlite3Fts3InitHashTable(
){
int rc = SQLITE_OK;
void *p = (void *)pHash;
const int any = SQLITE_UTF8|SQLITE_DIRECTONLY;
const int any = SQLITE_ANY;
#ifdef SQLITE_TEST
char *zTest = 0;
-1
View File
@@ -285,7 +285,6 @@ static int unicodeOpen(
pCsr->aInput = (const unsigned char *)aInput;
if( aInput==0 ){
pCsr->nInput = 0;
pCsr->aInput = (const unsigned char*)"";
}else if( nInput<0 ){
pCsr->nInput = (int)strlen(aInput);
}else{
+69 -140
View File
@@ -23,7 +23,7 @@
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#define FTS_MAX_APPENDABLE_HEIGHT 16
@@ -67,7 +67,7 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
#endif
/*
** The values that may be meaningfully bound to the :1 parameter in
** The two values that may be meaningfully bound to the :1 parameter in
** statements SQL_REPLACE_STAT and SQL_SELECT_STAT.
*/
#define FTS_STAT_DOCTOTAL 0
@@ -335,15 +335,13 @@ static int fts3SqlStmt(
** returns zero rows. */
/* 28 */ "SELECT level, count(*) AS cnt FROM %Q.'%q_segdir' "
" GROUP BY level HAVING cnt>=?"
" ORDER BY (level %% 1024) ASC, 2 DESC LIMIT 1",
" ORDER BY (level %% 1024) ASC LIMIT 1",
/* Estimate the upper limit on the number of leaf nodes in a new segment
** created by merging the oldest :2 segments from absolute level :1. See
** function sqlite3Fts3Incrmerge() for details. */
/* 29 */ "SELECT 2 * total(1 + leaves_end_block - start_block) "
" FROM (SELECT * FROM %Q.'%q_segdir' "
" WHERE level = ? ORDER BY idx ASC LIMIT ?"
" )",
" FROM %Q.'%q_segdir' WHERE level = ? AND idx < ?",
/* SQL_DELETE_SEGDIR_ENTRY
** Delete the %_segdir entry on absolute level :1 with index :2. */
@@ -698,7 +696,7 @@ static int fts3PendingListAppend(
assert( !p || p->iLastDocid<=iDocid );
if( !p || p->iLastDocid!=iDocid ){
u64 iDelta = (u64)iDocid - (u64)(p ? p->iLastDocid : 0);
sqlite3_int64 iDelta = iDocid - (p ? p->iLastDocid : 0);
if( p ){
assert( p->nData<p->nSpace );
assert( p->aData[p->nData]==0 );
@@ -1155,7 +1153,7 @@ static int fts3AllocateSegdirIdx(
** segment and allocate (newly freed) index 0 at level iLevel. Otherwise,
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
*/
if( iNext>=MergeCount(p) ){
if( iNext>=FTS3_MERGE_COUNT ){
fts3LogMerge(16, getAbsoluteLevel(p, iLangid, iIndex, iLevel));
rc = fts3SegmentMerge(p, iLangid, iIndex, iLevel);
*piIdx = 0;
@@ -1239,8 +1237,6 @@ int sqlite3Fts3ReadBlock(
}
*paBlob = aByte;
}
}else if( rc==SQLITE_ERROR ){
rc = FTS_CORRUPT_VTAB;
}
return rc;
@@ -1383,7 +1379,7 @@ static int fts3SegReaderNext(
pNext += fts3GetVarint32(pNext, &nSuffix);
if( nSuffix<=0
|| (&pReader->aNode[pReader->nNode] - pNext)<nSuffix
|| nPrefix>pReader->nTerm
|| nPrefix>pReader->nTermAlloc
){
return FTS_CORRUPT_VTAB;
}
@@ -1417,7 +1413,6 @@ static int fts3SegReaderNext(
*/
if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode)
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
|| pReader->nDoclist==0
){
return FTS_CORRUPT_VTAB;
}
@@ -1534,18 +1529,18 @@ static int fts3SegReaderNextDocid(
}else{
rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX);
if( rc==SQLITE_OK ){
u64 iDelta;
pReader->pOffsetList = p + sqlite3Fts3GetVarintU(p, &iDelta);
sqlite3_int64 iDelta;
pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta);
if( pTab->bDescIdx ){
pReader->iDocid = (i64)((u64)pReader->iDocid - iDelta);
pReader->iDocid -= iDelta;
}else{
pReader->iDocid = (i64)((u64)pReader->iDocid + iDelta);
pReader->iDocid += iDelta;
}
}
}
}
return rc;
return SQLITE_OK;
}
@@ -2035,11 +2030,6 @@ static int fts3NodeAddTerm(
nPrefix = fts3PrefixCompress(pTree->zTerm, pTree->nTerm, zTerm, nTerm);
nSuffix = nTerm-nPrefix;
/* If nSuffix is zero or less, then zTerm/nTerm must be a prefix of
** pWriter->zTerm/pWriter->nTerm. i.e. must be equal to or less than when
** compared with BINARY collation. This indicates corruption. */
if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix;
if( nReq<=p->nNodeSize || !pTree->zTerm ){
@@ -2284,7 +2274,6 @@ static int fts3SegWriterAdd(
int rc;
/* The current leaf node is full. Write it out to the database. */
if( pWriter->iFree==LARGEST_INT64 ) return FTS_CORRUPT_VTAB;
rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData);
if( rc!=SQLITE_OK ) return rc;
p->nLeafAdd++;
@@ -2334,11 +2323,9 @@ static int fts3SegWriterAdd(
/* Append the prefix-compressed term and doclist to the buffer. */
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nPrefix);
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nSuffix);
assert( nSuffix>0 );
memcpy(&pWriter->aData[nData], &zTerm[nPrefix], nSuffix);
nData += nSuffix;
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nDoclist);
assert( nDoclist>0 );
memcpy(&pWriter->aData[nData], aDoclist, nDoclist);
pWriter->nData = nData + nDoclist;
@@ -2358,7 +2345,6 @@ static int fts3SegWriterAdd(
pWriter->zTerm = zNew;
}
assert( pWriter->zTerm==pWriter->zMalloc );
assert( nTerm>0 );
memcpy(pWriter->zTerm, zTerm, nTerm);
}else{
pWriter->zTerm = (char *)zTerm;
@@ -2505,7 +2491,7 @@ static int fts3SegmentIsMaxLevel(Fts3Table *p, i64 iAbsLevel, int *pbMax){
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_int64(pStmt, 1, iAbsLevel+1);
sqlite3_bind_int64(pStmt, 2,
(((u64)iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
);
*pbMax = 0;
@@ -2667,7 +2653,6 @@ static int fts3MsrBufferData(
pMsr->aBuffer = pNew;
}
assert( nList>0 );
memcpy(pMsr->aBuffer, pList, nList);
return SQLITE_OK;
}
@@ -2855,19 +2840,6 @@ int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
return SQLITE_OK;
}
static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){
if( nReq>pCsr->nBuffer ){
char *aNew;
pCsr->nBuffer = nReq*2;
aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
if( !aNew ){
return SQLITE_NOMEM;
}
pCsr->aBuffer = aNew;
}
return SQLITE_OK;
}
int sqlite3Fts3SegReaderStep(
Fts3Table *p, /* Virtual table handle */
@@ -2994,17 +2966,25 @@ int sqlite3Fts3SegReaderStep(
** doclist. */
sqlite3_int64 iDelta;
if( p->bDescIdx && nDoclist>0 ){
if( iPrev<=iDocid ) return FTS_CORRUPT_VTAB;
iDelta = (i64)((u64)iPrev - (u64)iDocid);
iDelta = iPrev - iDocid;
}else{
if( nDoclist>0 && iPrev>=iDocid ) return FTS_CORRUPT_VTAB;
iDelta = (i64)((u64)iDocid - (u64)iPrev);
iDelta = iDocid - iPrev;
}
if( iDelta<=0 && (nDoclist>0 || iDelta!=iDocid) ){
return FTS_CORRUPT_VTAB;
}
assert( nDoclist>0 || iDelta==iDocid );
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist);
if( rc ) return rc;
if( nDoclist+nByte>pCsr->nBuffer ){
char *aNew;
pCsr->nBuffer = (nDoclist+nByte)*2;
aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
if( !aNew ){
return SQLITE_NOMEM;
}
pCsr->aBuffer = aNew;
}
if( isFirst ){
char *a = &pCsr->aBuffer[nDoclist];
@@ -3029,9 +3009,6 @@ int sqlite3Fts3SegReaderStep(
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
}
if( nDoclist>0 ){
rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING);
if( rc ) return rc;
memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING);
pCsr->aDoclist = pCsr->aBuffer;
pCsr->nDoclist = nDoclist;
rc = SQLITE_ROW;
@@ -3081,11 +3058,11 @@ static void fts3ReadEndBlockField(
if( zText ){
int i;
int iMul = 1;
u64 iVal = 0;
i64 iVal = 0;
for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
iVal = iVal*10 + (zText[i] - '0');
}
*piEndBlock = (i64)iVal;
*piEndBlock = iVal;
while( zText[i]==' ' ) i++;
iVal = 0;
if( zText[i]=='-' ){
@@ -3095,7 +3072,7 @@ static void fts3ReadEndBlockField(
for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
iVal = iVal*10 + (zText[i] - '0');
}
*pnByte = ((i64)iVal * (i64)iMul);
*pnByte = (iVal * (i64)iMul);
}
}
@@ -3278,7 +3255,7 @@ static int fts3SegmentMerge(
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
}
if( rc!=SQLITE_OK ) goto finished;
assert_fts3_nc( pWriter || bIgnoreEmpty );
assert( pWriter || bIgnoreEmpty );
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
rc = fts3DeleteSegdir(
@@ -3505,10 +3482,7 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
int rc;
sqlite3_stmt *pAllLangid = 0;
rc = sqlite3Fts3PendingTermsFlush(p);
if( rc==SQLITE_OK ){
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
}
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
if( rc==SQLITE_OK ){
int rc2;
sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid);
@@ -3529,6 +3503,7 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
}
sqlite3Fts3SegmentsClose(p);
sqlite3Fts3PendingTermsClear(p);
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
}
@@ -3866,7 +3841,6 @@ static int fts3IncrmergePush(
** be added to. */
nPrefix = fts3PrefixCompress(pNode->key.a, pNode->key.n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
if(nSuffix<=0 ) return FTS_CORRUPT_VTAB;
nSpace = sqlite3Fts3VarintLen(nPrefix);
nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
@@ -4261,10 +4235,6 @@ static int fts3IncrmergeLoad(
pWriter->bNoLeafData = (pWriter->nLeafData==0);
nRoot = sqlite3_column_bytes(pSelect, 4);
aRoot = sqlite3_column_blob(pSelect, 4);
if( aRoot==0 ){
sqlite3_reset(pSelect);
return nRoot ? SQLITE_NOMEM : FTS_CORRUPT_VTAB;
}
}else{
return sqlite3_reset(pSelect);
}
@@ -4300,10 +4270,6 @@ static int fts3IncrmergeLoad(
int i;
int nHeight = (int)aRoot[0];
NodeWriter *pNode;
if( nHeight<1 || nHeight>=FTS_MAX_APPENDABLE_HEIGHT ){
sqlite3_reset(pSelect);
return FTS_CORRUPT_VTAB;
}
pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT;
pWriter->iStart = iStart;
@@ -4335,20 +4301,17 @@ static int fts3IncrmergeLoad(
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
if( rc==SQLITE_OK ){
assert_fts3_nc( reader.term.n>0 || reader.aNode==0 );
if( reader.term.n>0 ){
memcpy(pNode->key.a, reader.term.a, reader.term.n);
}
memcpy(pNode->key.a, reader.term.a, reader.term.n);
pNode->key.n = reader.term.n;
if( i>0 ){
char *aBlock = 0;
int nBlock = 0;
pNode = &pWriter->aNodeWriter[i-1];
pNode->iBlock = reader.iChild;
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
);
);
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
@@ -4867,17 +4830,13 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
const int nHint = pHint->n;
int i;
i = pHint->n-1;
if( (pHint->a[i] & 0x80) ) return FTS_CORRUPT_VTAB;
i = pHint->n-2;
while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
if( i==0 ) return FTS_CORRUPT_VTAB;
i--;
while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
pHint->n = i;
i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel);
i += fts3GetVarint32(&pHint->a[i], pnInput);
assert( i<=nHint );
if( i!=nHint ) return FTS_CORRUPT_VTAB;
return SQLITE_OK;
@@ -4947,14 +4906,8 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
rc = fts3IncrmergeHintPop(&hint, &iHintAbsLevel, &nHintSeg);
if( nSeg<0 || (iAbsLevel % nMod) >= (iHintAbsLevel % nMod) ){
/* Based on the scan in the block above, it is known that there
** are no levels with a relative level smaller than that of
** iAbsLevel with more than nSeg segments, or if nSeg is -1,
** no levels with more than nMin segments. Use this to limit the
** value of nHintSeg to avoid a large memory allocation in case the
** merge-hint is corrupt*/
iAbsLevel = iHintAbsLevel;
nSeg = MIN(MAX(nMin,nSeg), nHintSeg);
nSeg = nHintSeg;
bUseHint = 1;
bDirtyHint = 1;
}else{
@@ -4967,13 +4920,7 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
/* If nSeg is less that zero, then there is no level with at least
** nMin segments and no hint in the %_stat table. No work to do.
** Exit early in this case. */
if( nSeg<=0 ) break;
assert( nMod<=0x7FFFFFFF );
if( iAbsLevel<0 || iAbsLevel>(nMod<<32) ){
rc = FTS_CORRUPT_VTAB;
break;
}
if( nSeg<0 ) break;
/* Open a cursor to iterate through the contents of the oldest nSeg
** indexes of absolute level iAbsLevel. If this cursor is opened using
@@ -5001,15 +4948,8 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
}
if( SQLITE_OK==rc && pCsr->nSegment==nSeg
&& SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
&& SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
){
int bEmpty = 0;
rc = sqlite3Fts3SegReaderStep(p, pCsr);
if( rc==SQLITE_OK ){
bEmpty = 1;
}else if( rc!=SQLITE_ROW ){
sqlite3Fts3SegReaderFinish(pCsr);
break;
}
if( bUseHint && iIdx>0 ){
const char *zKey = pCsr->zTerm;
int nKey = pCsr->nTerm;
@@ -5020,13 +4960,11 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
if( rc==SQLITE_OK && pWriter->nLeafEst ){
fts3LogMerge(nSeg, iAbsLevel);
if( bEmpty==0 ){
do {
rc = fts3IncrmergeAppend(p, pWriter, pCsr);
if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
}while( rc==SQLITE_ROW );
}
do {
rc = fts3IncrmergeAppend(p, pWriter, pCsr);
if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
}while( rc==SQLITE_ROW );
/* Update or delete the input segments */
if( rc==SQLITE_OK ){
@@ -5091,7 +5029,7 @@ static int fts3DoIncrmerge(
const char *zParam /* Nul-terminated string containing "A,B" */
){
int rc;
int nMin = (MergeCount(p) / 2);
int nMin = (FTS3_MERGE_COUNT / 2);
int nMerge = 0;
const char *z = zParam;
@@ -5136,7 +5074,7 @@ static int fts3DoAutoincrmerge(
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
p->nAutoincrmerge = fts3Getint(&zParam);
if( p->nAutoincrmerge==1 || p->nAutoincrmerge>MergeCount(p) ){
if( p->nAutoincrmerge==1 || p->nAutoincrmerge>FTS3_MERGE_COUNT ){
p->nAutoincrmerge = 8;
}
if( !p->bHasStat ){
@@ -5219,12 +5157,12 @@ static u64 fts3ChecksumIndex(
i64 iDocid = 0;
i64 iCol = 0;
u64 iPos = 0;
i64 iPos = 0;
pCsr += sqlite3Fts3GetVarint(pCsr, &iDocid);
while( pCsr<pEnd ){
u64 iVal = 0;
pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal);
i64 iVal = 0;
pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
if( pCsr<pEnd ){
if( iVal==0 || iVal==1 ){
iCol = 0;
@@ -5232,12 +5170,8 @@ static u64 fts3ChecksumIndex(
if( iVal ){
pCsr += sqlite3Fts3GetVarint(pCsr, &iCol);
}else{
pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal);
if( p->bDescIdx ){
iDocid = (i64)((u64)iDocid - iVal);
}else{
iDocid = (i64)((u64)iDocid + iVal);
}
pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
iDocid += iVal;
}
}else{
iPos += (iVal - 2);
@@ -5310,9 +5244,10 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
if( p->abNotindexed[iCol]==0 ){
const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
int nText = sqlite3_column_bytes(pStmt, iCol+1);
sqlite3_tokenizer_cursor *pT = 0;
rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, -1, &pT);
rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText,&pT);
while( rc==SQLITE_OK ){
char const *zToken; /* Buffer containing token */
int nToken = 0; /* Number of bytes in token */
@@ -5397,7 +5332,7 @@ static int fts3DoIntegrityCheck(
** meaningful value to insert is the text 'optimize'.
*/
static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
int rc = SQLITE_ERROR; /* Return Code */
int rc; /* Return Code */
const char *zVal = (const char *)sqlite3_value_text(pVal);
int nVal = sqlite3_value_bytes(pVal);
@@ -5413,27 +5348,21 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
rc = fts3DoIncrmerge(p, &zVal[6]);
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
rc = fts3DoAutoincrmerge(p, &zVal[10]);
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
}else{
int v;
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
v = atoi(&zVal[9]);
if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
v = atoi(&zVal[11]);
if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
rc = SQLITE_OK;
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal,"test-no-incr-doclist=",21) ){
p->bNoIncrDoclist = atoi(&zVal[21]);
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal,"mergecount=",11) ){
v = atoi(&zVal[11]);
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
rc = SQLITE_OK;
}
#ifdef SQLITE_TEST
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
p->nNodeSize = atoi(&zVal[9]);
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
p->nMaxPendingData = atoi(&zVal[11]);
rc = SQLITE_OK;
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
p->bNoIncrDoclist = atoi(&zVal[21]);
rc = SQLITE_OK;
#endif
}else{
rc = SQLITE_ERROR;
}
return rc;
}
+2 -2
View File
@@ -93,7 +93,7 @@ static int runSql(sqlite3 *db, const char *zFormat, ...){
static void showSchema(sqlite3 *db, const char *zTab){
sqlite3_stmt *pStmt;
pStmt = prepare(db,
"SELECT sql FROM sqlite_schema"
"SELECT sql FROM sqlite_master"
" WHERE name LIKE '%q%%'"
" ORDER BY 1",
zTab);
@@ -831,7 +831,7 @@ int main(int argc, char **argv){
sqlite3_stmt *pStmt;
int cnt = 0;
pStmt = prepare(db, "SELECT b.sql"
" FROM sqlite_schema a, sqlite_schema b"
" FROM sqlite_master a, sqlite_master b"
" WHERE a.name GLOB '*_segdir'"
" AND b.name=substr(a.name,1,length(a.name)-7)"
" ORDER BY 1");
-1
View File
@@ -742,7 +742,6 @@ proc print_categories {lMap} {
}
iTbl++;
}
aAscii[0] = 0; /* 0x00 is never a token character */
}
}]
}
+3 -3
View File
@@ -159,7 +159,7 @@ struct Fts5PhraseIter {
**
** xSetAuxdata(pFts5, pAux, xDelete)
**
** Save the pointer passed as the second argument as the extension function's
** Save the pointer passed as the second argument as the extension functions
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
** the same MATCH query using the xGetAuxdata() API.
@@ -401,8 +401,8 @@ struct Fts5ExtensionApi {
**
** There are several ways to approach this in FTS5:
**
** <ol><li> By mapping all synonyms to a single token. In this case, using
** the above example, this means that the tokenizer returns the
** <ol><li> By mapping all synonyms to a single token. In this case, the
** In the above example, this means that the tokenizer returns the
** same token for inputs "first" and "1st". Say that token is in
** fact "first", so that when the user inserts the document "I won
** 1st place" entries are added to the index for tokens "i", "won",
+9 -33
View File
@@ -61,11 +61,6 @@ typedef sqlite3_uint64 u64;
*/
#define FTS5_MAX_PREFIX_INDEXES 31
/*
** Maximum segments permitted in a single index
*/
#define FTS5_MAX_SEGMENT 2000
#define FTS5_DEFAULT_NEARDIST 10
#define FTS5_DEFAULT_RANK "bm25"
@@ -184,7 +179,6 @@ struct Fts5Config {
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
int bLock; /* True when table is preparing statement */
int ePattern; /* FTS_PATTERN_XXX constant */
/* Values loaded from the %_config table */
int iCookie; /* Incremented when %_config is modified */
@@ -205,19 +199,17 @@ struct Fts5Config {
};
/* Current expected value of %_config table 'version' field */
#define FTS5_CURRENT_VERSION 4
#define FTS5_CURRENT_VERSION 4
#define FTS5_CONTENT_NORMAL 0
#define FTS5_CONTENT_NONE 1
#define FTS5_CONTENT_EXTERNAL 2
#define FTS5_DETAIL_FULL 0
#define FTS5_DETAIL_NONE 1
#define FTS5_DETAIL_COLUMNS 2
#define FTS5_DETAIL_FULL 0
#define FTS5_DETAIL_NONE 1
#define FTS5_DETAIL_COLUMNS 2
#define FTS5_PATTERN_NONE 0
#define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */
#define FTS5_PATTERN_GLOB 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */
int sqlite3Fts5ConfigParse(
Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char**
@@ -425,11 +417,6 @@ int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
*/
void sqlite3Fts5IterClose(Fts5IndexIter*);
/*
** Close the reader blob handle, if it is open.
*/
void sqlite3Fts5IndexCloseReader(Fts5Index*);
/*
** This interface is used by the fts5vocab module.
*/
@@ -487,7 +474,7 @@ int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int);
/*
** Functions called by the storage module as part of integrity-check.
*/
int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum, int bUseCksum);
int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum);
/*
** Called during virtual module initialization to register UDF
@@ -557,7 +544,8 @@ int sqlite3Fts5GetTokenizer(
Fts5Global*,
const char **azArg,
int nArg,
Fts5Config*,
Fts5Tokenizer**,
fts5_tokenizer**,
char **pzErr
);
@@ -641,7 +629,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg);
int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**);
void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
@@ -686,19 +674,11 @@ struct Fts5Token {
/* Parse a MATCH expression. */
int sqlite3Fts5ExprNew(
Fts5Config *pConfig,
int bPhraseToAnd,
int iCol, /* Column on LHS of MATCH operator */
const char *zExpr,
Fts5Expr **ppNew,
char **pzErr
);
int sqlite3Fts5ExprPattern(
Fts5Config *pConfig,
int bGlob,
int iCol,
const char *zText,
Fts5Expr **pp
);
/*
** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc);
@@ -807,10 +787,6 @@ int sqlite3Fts5AuxInit(fts5_api*);
*/
int sqlite3Fts5TokenizerInit(fts5_api*);
int sqlite3Fts5TokenizerPattern(
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
Fts5Tokenizer *pTok
);
/*
** End of interface to code in fts5_tokenizer.c.
**************************************************************************/
+12 -10
View File
@@ -566,7 +566,7 @@ static int fts5Bm25GetData(
int rc = SQLITE_OK; /* Return code */
Fts5Bm25Data *p; /* Object to return */
p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0);
p = pApi->xGetAuxdata(pFts, 0);
if( p==0 ){
int nPhrase; /* Number of phrases in query */
sqlite3_int64 nRow = 0; /* Number of rows in table */
@@ -640,7 +640,7 @@ static void fts5Bm25Function(
){
const double k1 = 1.2; /* Constant "k1" from BM25 formula */
const double b = 0.75; /* Constant "b" from BM25 formula */
int rc; /* Error code */
int rc = SQLITE_OK; /* Error code */
double score = 0.0; /* SQL function return value */
Fts5Bm25Data *pData; /* Values allocated/calculated once only */
int i; /* Iterator variable */
@@ -672,15 +672,17 @@ static void fts5Bm25Function(
D = (double)nTok;
}
/* Determine and return the BM25 score for the current row. Or, if an
** error has occurred, throw an exception. */
/* Determine the BM25 score for the current row. */
for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){
score += pData->aIDF[i] * (
( aFreq[i] * (k1 + 1.0) ) /
( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
);
}
/* If no error has occurred, return the calculated score. Otherwise,
** throw an SQL exception. */
if( rc==SQLITE_OK ){
for(i=0; i<pData->nPhrase; i++){
score += pData->aIDF[i] * (
( aFreq[i] * (k1 + 1.0) ) /
( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
);
}
sqlite3_result_double(pCtx, -1.0 * score);
}else{
sqlite3_result_error_code(pCtx, rc);
+5 -4
View File
@@ -150,7 +150,7 @@ static int fts5Dequote(char *z){
assert( q=='[' || q=='\'' || q=='"' || q=='`' );
if( q=='[' ) q = ']';
while( z[iIn] ){
while( ALWAYS(z[iIn]) ){
if( z[iIn]==q ){
if( z[iIn+1]!=q ){
/* Character iIn was the close quote. */
@@ -325,7 +325,7 @@ static int fts5ConfigParseSpecial(
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5GetTokenizer(pGlobal,
(const char**)azArg, (int)nArg, pConfig,
(const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi,
pzErr
);
}
@@ -397,7 +397,9 @@ static int fts5ConfigParseSpecial(
*/
static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0);
return sqlite3Fts5GetTokenizer(
pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
);
}
/*
@@ -879,7 +881,6 @@ int sqlite3Fts5ConfigSetValue(
*pbBadkey = 1;
}else{
if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
if( nCrisisMerge>=FTS5_MAX_SEGMENT ) nCrisisMerge = FTS5_MAX_SEGMENT-1;
pConfig->nCrisisMerge = nCrisisMerge;
}
}
+57 -222
View File
@@ -128,7 +128,6 @@ struct Fts5Parse {
int nPhrase; /* Size of apPhrase array */
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
Fts5ExprNode *pExpr; /* Result of a successful parse */
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
};
void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
@@ -217,7 +216,6 @@ static void fts5ParseFree(void *p){ sqlite3_free(p); }
int sqlite3Fts5ExprNew(
Fts5Config *pConfig, /* FTS5 Configuration */
int bPhraseToAnd,
int iCol,
const char *zExpr, /* Expression text */
Fts5Expr **ppNew,
@@ -233,7 +231,6 @@ int sqlite3Fts5ExprNew(
*ppNew = 0;
*pzErr = 0;
memset(&sParse, 0, sizeof(sParse));
sParse.bPhraseToAnd = bPhraseToAnd;
pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc);
if( pEngine==0 ){ return SQLITE_NOMEM; }
sParse.pConfig = pConfig;
@@ -276,7 +273,6 @@ int sqlite3Fts5ExprNew(
pNew->pConfig = pConfig;
pNew->apExprPhrase = sParse.apPhrase;
pNew->nPhrase = sParse.nPhrase;
pNew->bDesc = 0;
sParse.apPhrase = 0;
}
}else{
@@ -288,81 +284,6 @@ int sqlite3Fts5ExprNew(
return sParse.rc;
}
/*
** This function is only called when using the special 'trigram' tokenizer.
** Argument zText contains the text of a LIKE or GLOB pattern matched
** against column iCol. This function creates and compiles an FTS5 MATCH
** expression that will match a superset of the rows matched by the LIKE or
** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error
** code.
*/
int sqlite3Fts5ExprPattern(
Fts5Config *pConfig, int bGlob, int iCol, const char *zText, Fts5Expr **pp
){
i64 nText = strlen(zText);
char *zExpr = (char*)sqlite3_malloc64(nText*4 + 1);
int rc = SQLITE_OK;
if( zExpr==0 ){
rc = SQLITE_NOMEM;
}else{
char aSpec[3];
int iOut = 0;
int i = 0;
int iFirst = 0;
if( bGlob==0 ){
aSpec[0] = '_';
aSpec[1] = '%';
aSpec[2] = 0;
}else{
aSpec[0] = '*';
aSpec[1] = '?';
aSpec[2] = '[';
}
while( i<=nText ){
if( i==nText
|| zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2]
){
if( i-iFirst>=3 ){
int jj;
zExpr[iOut++] = '"';
for(jj=iFirst; jj<i; jj++){
zExpr[iOut++] = zText[jj];
if( zText[jj]=='"' ) zExpr[iOut++] = '"';
}
zExpr[iOut++] = '"';
zExpr[iOut++] = ' ';
}
if( zText[i]==aSpec[2] ){
i += 2;
if( zText[i-1]=='^' ) i++;
while( i<nText && zText[i]!=']' ) i++;
}
iFirst = i+1;
}
i++;
}
if( iOut>0 ){
int bAnd = 0;
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
bAnd = 1;
if( pConfig->eDetail==FTS5_DETAIL_NONE ){
iCol = pConfig->nCol;
}
}
zExpr[iOut] = '\0';
rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg);
}else{
*pp = 0;
}
sqlite3_free(zExpr);
}
return rc;
}
/*
** Free the expression node object passed as the only argument.
*/
@@ -1500,8 +1421,8 @@ int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
}
/* If the iterator is not at a real match, skip forward until it is. */
while( pRoot->bNomatch && rc==SQLITE_OK ){
assert( pRoot->bEof==0 );
while( pRoot->bNomatch ){
assert( pRoot->bEof==0 && rc==SQLITE_OK );
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
}
return rc;
@@ -1740,20 +1661,6 @@ void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){
pParse->pExpr = p;
}
static int parseGrowPhraseArray(Fts5Parse *pParse){
if( (pParse->nPhrase % 8)==0 ){
sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
Fts5ExprPhrase **apNew;
apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte);
if( apNew==0 ){
pParse->rc = SQLITE_NOMEM;
return SQLITE_NOMEM;
}
pParse->apPhrase = apNew;
}
return SQLITE_OK;
}
/*
** This function is called by the parser to process a string token. The
** string may or may not be quoted. In any case it is tokenized and a
@@ -1789,9 +1696,16 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm(
}else{
if( pAppend==0 ){
if( parseGrowPhraseArray(pParse) ){
fts5ExprPhraseFree(sCtx.pPhrase);
return 0;
if( (pParse->nPhrase % 8)==0 ){
sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
Fts5ExprPhrase **apNew;
apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte);
if( apNew==0 ){
pParse->rc = SQLITE_NOMEM;
fts5ExprPhraseFree(sCtx.pPhrase);
return 0;
}
pParse->apPhrase = apNew;
}
pParse->nPhrase++;
}
@@ -2198,67 +2112,6 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
}
}
/*
** This function is used when parsing LIKE or GLOB patterns against
** trigram indexes that specify either detail=column or detail=none.
** It converts a phrase:
**
** abc + def + ghi
**
** into an AND tree:
**
** abc AND def AND ghi
*/
static Fts5ExprNode *fts5ParsePhraseToAnd(
Fts5Parse *pParse,
Fts5ExprNearset *pNear
){
int nTerm = pNear->apPhrase[0]->nTerm;
int ii;
int nByte;
Fts5ExprNode *pRet;
assert( pNear->nPhrase==1 );
assert( pParse->bPhraseToAnd );
nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
if( pRet ){
pRet->eType = FTS5_AND;
pRet->nChild = nTerm;
fts5ExprAssignXNext(pRet);
pParse->nPhrase--;
for(ii=0; ii<nTerm; ii++){
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
&pParse->rc, sizeof(Fts5ExprPhrase)
);
if( pPhrase ){
if( parseGrowPhraseArray(pParse) ){
fts5ExprPhraseFree(pPhrase);
}else{
pParse->apPhrase[pParse->nPhrase++] = pPhrase;
pPhrase->nTerm = 1;
pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup(
&pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1
);
pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
);
}
}
}
if( pParse->rc ){
sqlite3Fts5ParseNodeFree(pRet);
pRet = 0;
}else{
sqlite3Fts5ParseNearsetFree(pNear);
}
}
return pRet;
}
/*
** Allocate and return a new expression object. If anything goes wrong (i.e.
** OOM error), leave an error code in pParse and return NULL.
@@ -2283,58 +2136,51 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
if( eType!=FTS5_STRING && pLeft==0 ) return pRight;
if( eType!=FTS5_STRING && pRight==0 ) return pLeft;
if( eType==FTS5_STRING
&& pParse->bPhraseToAnd
&& pNear->apPhrase[0]->nTerm>1
){
pRet = fts5ParsePhraseToAnd(pParse, pNear);
}else{
if( eType==FTS5_NOT ){
nChild = 2;
}else if( eType==FTS5_AND || eType==FTS5_OR ){
nChild = 2;
if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
if( pRight->eType==eType ) nChild += pRight->nChild-1;
}
if( eType==FTS5_NOT ){
nChild = 2;
}else if( eType==FTS5_AND || eType==FTS5_OR ){
nChild = 2;
if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
if( pRight->eType==eType ) nChild += pRight->nChild-1;
}
nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
if( pRet ){
pRet->eType = eType;
pRet->pNear = pNear;
fts5ExprAssignXNext(pRet);
if( eType==FTS5_STRING ){
int iPhrase;
for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
pNear->apPhrase[iPhrase]->pNode = pRet;
if( pNear->apPhrase[iPhrase]->nTerm==0 ){
pRet->xNext = 0;
pRet->eType = FTS5_EOF;
}
if( pRet ){
pRet->eType = eType;
pRet->pNear = pNear;
fts5ExprAssignXNext(pRet);
if( eType==FTS5_STRING ){
int iPhrase;
for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
pNear->apPhrase[iPhrase]->pNode = pRet;
if( pNear->apPhrase[iPhrase]->nTerm==0 ){
pRet->xNext = 0;
pRet->eType = FTS5_EOF;
}
if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
if( pNear->nPhrase!=1
|| pPhrase->nTerm>1
|| (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
){
assert( pParse->rc==SQLITE_OK );
pParse->rc = SQLITE_ERROR;
assert( pParse->zErr==0 );
pParse->zErr = sqlite3_mprintf(
"fts5: %s queries are not supported (detail!=full)",
pNear->nPhrase==1 ? "phrase": "NEAR"
);
sqlite3_free(pRet);
pRet = 0;
}
}
}else{
fts5ExprAddChildren(pRet, pLeft);
fts5ExprAddChildren(pRet, pRight);
}
if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
if( pNear->nPhrase!=1
|| pPhrase->nTerm>1
|| (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
){
assert( pParse->rc==SQLITE_OK );
pParse->rc = SQLITE_ERROR;
assert( pParse->zErr==0 );
pParse->zErr = sqlite3_mprintf(
"fts5: %s queries are not supported (detail!=full)",
pNear->nPhrase==1 ? "phrase": "NEAR"
);
sqlite3_free(pRet);
pRet = 0;
}
}
}else{
fts5ExprAddChildren(pRet, pLeft);
fts5ExprAddChildren(pRet, pRight);
}
}
}
@@ -2555,17 +2401,8 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
int iTerm;
if( pNear->pColset ){
int ii;
Fts5Colset *pColset = pNear->pColset;
if( pColset->nCol>1 ) zRet = fts5PrintfAppend(zRet, "{");
for(ii=0; ii<pColset->nCol; ii++){
zRet = fts5PrintfAppend(zRet, "%s%s",
pConfig->azCol[pColset->aiCol[ii]], ii==pColset->nCol-1 ? "" : " "
);
}
if( zRet ){
zRet = fts5PrintfAppend(zRet, "%s : ", pColset->nCol>1 ? "}" : "");
}
int iCol = pNear->pColset->aiCol[0];
zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]);
if( zRet==0 ) return 0;
}
@@ -2679,16 +2516,14 @@ static void fts5ExprFunction(
azConfig[1] = "main";
azConfig[2] = "tbl";
for(i=3; iArg<nArg; iArg++){
const char *z = (const char*)sqlite3_value_text(apVal[iArg]);
azConfig[i++] = (z ? z : "");
azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]);
}
zExpr = (const char*)sqlite3_value_text(apVal[0]);
if( zExpr==0 ) zExpr = "";
rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr);
rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr);
}
if( rc==SQLITE_OK ){
char *zText;
+2 -2
View File
@@ -306,6 +306,7 @@ int sqlite3Fts5HashWrite(
p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
}
nIncr += p->nData;
}else{
/* Appending to an existing hash-entry. Check that there is enough
@@ -338,9 +339,8 @@ int sqlite3Fts5HashWrite(
/* If this is a new rowid, append the 4-byte size field for the previous
** entry, and the new rowid for this entry. */
if( iRowid!=p->iRowid ){
u64 iDiff = (u64)iRowid - (u64)p->iRowid;
fts5HashAddPoslistSize(pHash, p, 0);
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff);
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
p->iRowid = iRowid;
bNew = 1;
p->iSzPoslist = p->nData;
+246 -362
View File
@@ -239,6 +239,11 @@
#define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno)
#define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno)
/*
** Maximum segments permitted in a single index
*/
#define FTS5_MAX_SEGMENT 2000
#ifdef SQLITE_DEBUG
int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; }
#endif
@@ -614,7 +619,7 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
/*
** Close the read-only blob handle, if it is open.
*/
void sqlite3Fts5IndexCloseReader(Fts5Index *p){
static void fts5CloseReader(Fts5Index *p){
if( p->pReader ){
sqlite3_blob *pReader = p->pReader;
p->pReader = 0;
@@ -643,7 +648,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
assert( p->pReader==0 );
p->pReader = pBlob;
if( rc!=SQLITE_OK ){
sqlite3Fts5IndexCloseReader(p);
fts5CloseReader(p);
}
if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
}
@@ -1754,7 +1759,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
ASSERT_SZLEAF_OK(pIter->pLeaf);
while( 1 ){
u64 iDelta = 0;
i64 iDelta = 0;
if( eDetail==FTS5_DETAIL_NONE ){
/* todo */
@@ -1769,7 +1774,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
i += nPos;
}
if( i>=n ) break;
i += fts5GetVarint(&a[i], &iDelta);
i += fts5GetVarint(&a[i], (u64*)&iDelta);
pIter->iRowid += iDelta;
/* If necessary, grow the pIter->aRowidOffset[] array. */
@@ -1868,7 +1873,7 @@ static void fts5SegIterNext_Reverse(
if( pIter->iRowidOffset>0 ){
u8 *a = pIter->pLeaf->p;
int iOff;
u64 iDelta;
i64 iDelta;
pIter->iRowidOffset--;
pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset];
@@ -1877,7 +1882,7 @@ static void fts5SegIterNext_Reverse(
if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){
iOff += pIter->nPos;
}
fts5GetVarint(&a[iOff], &iDelta);
fts5GetVarint(&a[iOff], (u64*)&iDelta);
pIter->iRowid -= iDelta;
}else{
fts5SegIterReverseNewPage(p, pIter);
@@ -2070,10 +2075,14 @@ static void fts5SegIterNext(
}else{
/* The following could be done by calling fts5SegIterLoadNPos(). But
** this block is particularly performance critical, so equivalent
** code is inlined. */
** code is inlined.
**
** Later: Switched back to fts5SegIterLoadNPos() because it supports
** detail=none mode. Not ideal.
*/
int nSz;
assert( p->rc==SQLITE_OK );
assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn );
assert( pIter->iLeafOffset<=pIter->pLeaf->nn );
fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
pIter->bDel = (nSz & 0x0001);
pIter->nPos = nSz>>1;
@@ -2317,11 +2326,11 @@ static void fts5LeafSeek(
}
search_success:
if( (i64)iOff+nNew>n || nNew<1 ){
pIter->iLeafOffset = iOff + nNew;
if( pIter->iLeafOffset>n || nNew<1 ){
p->rc = FTS5_CORRUPT;
return;
}
pIter->iLeafOffset = iOff + nNew;
pIter->iTermLeafOffset = pIter->iLeafOffset;
pIter->iTermLeafPgno = pIter->iLeafPgno;
@@ -3065,7 +3074,7 @@ static void fts5ChunkIterate(
int pgno = pSeg->iLeafPgno;
int pgnoSave = 0;
/* This function does not work with detail=none databases. */
/* This function does notmwork with detail=none databases. */
assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
@@ -3078,9 +3087,6 @@ static void fts5ChunkIterate(
fts5DataRelease(pData);
if( nRem<=0 ){
break;
}else if( pSeg->pSeg==0 ){
p->rc = FTS5_CORRUPT;
return;
}else{
pgno++;
pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
@@ -3132,72 +3138,66 @@ static void fts5SegiterPoslist(
}
/*
** Parameter pPos points to a buffer containing a position list, size nPos.
** This function filters it according to pColset (which must be non-NULL)
** and sets pIter->base.pData/nData to point to the new position list.
** If memory is required for the new position list, use buffer pIter->poslist.
** Or, if the new position list is a contiguous subset of the input, set
** pIter->base.pData/nData to point directly to it.
**
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM
** before returning.
** IN/OUT parameter (*pa) points to a position list n bytes in size. If
** the position list contains entries for column iCol, then (*pa) is set
** to point to the sub-position-list for that column and the number of
** bytes in it returned. Or, if the argument position list does not
** contain any entries for column iCol, return 0.
*/
static int fts5IndexExtractCol(
const u8 **pa, /* IN/OUT: Pointer to poslist */
int n, /* IN: Size of poslist in bytes */
int iCol /* Column to extract from poslist */
){
int iCurrent = 0; /* Anything before the first 0x01 is col 0 */
const u8 *p = *pa;
const u8 *pEnd = &p[n]; /* One byte past end of position list */
while( iCol>iCurrent ){
/* Advance pointer p until it points to pEnd or an 0x01 byte that is
** not part of a varint. Note that it is not possible for a negative
** or extremely large varint to occur within an uncorrupted position
** list. So the last byte of each varint may be assumed to have a clear
** 0x80 bit. */
while( *p!=0x01 ){
while( *p++ & 0x80 );
if( p>=pEnd ) return 0;
}
*pa = p++;
iCurrent = *p++;
if( iCurrent & 0x80 ){
p--;
p += fts5GetVarint32(p, iCurrent);
}
}
if( iCol!=iCurrent ) return 0;
/* Advance pointer p until it points to pEnd or an 0x01 byte that is
** not part of a varint */
while( p<pEnd && *p!=0x01 ){
while( *p++ & 0x80 );
}
return p - (*pa);
}
static void fts5IndexExtractColset(
int *pRc,
Fts5Colset *pColset, /* Colset to filter on */
const u8 *pPos, int nPos, /* Position list */
Fts5Iter *pIter
Fts5Buffer *pBuf /* Output buffer */
){
if( *pRc==SQLITE_OK ){
const u8 *p = pPos;
const u8 *aCopy = p;
const u8 *pEnd = &p[nPos]; /* One byte past end of position list */
int i = 0;
int iCurrent = 0;
if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){
return;
}
while( 1 ){
while( pColset->aiCol[i]<iCurrent ){
i++;
if( i==pColset->nCol ){
pIter->base.pData = pIter->poslist.p;
pIter->base.nData = pIter->poslist.n;
return;
}
}
/* Advance pointer p until it points to pEnd or an 0x01 byte that is
** not part of a varint */
while( p<pEnd && *p!=0x01 ){
while( *p++ & 0x80 );
}
if( pColset->aiCol[i]==iCurrent ){
if( pColset->nCol==1 ){
pIter->base.pData = aCopy;
pIter->base.nData = p-aCopy;
return;
}
fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy);
}
if( p==pEnd ){
pIter->base.pData = pIter->poslist.p;
pIter->base.nData = pIter->poslist.n;
return;
}
aCopy = p++;
iCurrent = *p++;
if( iCurrent & 0x80 ){
p--;
p += fts5GetVarint32(p, iCurrent);
int i;
fts5BufferZero(pBuf);
for(i=0; i<pColset->nCol; i++){
const u8 *pSub = pPos;
int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
if( nSub ){
fts5BufferAppendBlob(pRc, pBuf, nSub, pSub);
}
}
}
}
/*
@@ -3317,9 +3317,16 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
/* All data is stored on the current page. Populate the output
** variables to point into the body of the page object. */
const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
int *pRc = &pIter->pIndex->rc;
fts5BufferZero(&pIter->poslist);
fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter);
if( pColset->nCol==1 ){
pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
pIter->base.pData = a;
}else{
int *pRc = &pIter->pIndex->rc;
fts5BufferZero(&pIter->poslist);
fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist);
pIter->base.pData = pIter->poslist.p;
pIter->base.nData = pIter->poslist.n;
}
}else{
/* The data is distributed over two or more pages. Copy it into the
** Fts5Iter.poslist buffer and then set the output pointer to point
@@ -4802,7 +4809,7 @@ static void fts5AppendPoslist(
static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;
assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) );
assert( pIter->aPoslist );
if( p>=pIter->aEof ){
pIter->aPoslist = 0;
}else{
@@ -4822,9 +4829,6 @@ static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
}
pIter->aPoslist = p;
if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){
pIter->aPoslist = 0;
}
}
}
@@ -4833,11 +4837,9 @@ static void fts5DoclistIterInit(
Fts5DoclistIter *pIter
){
memset(pIter, 0, sizeof(*pIter));
if( pBuf->n>0 ){
pIter->aPoslist = pBuf->p;
pIter->aEof = &pBuf->p[pBuf->n];
fts5DoclistIterNext(pIter);
}
pIter->aPoslist = pBuf->p;
pIter->aEof = &pBuf->p[pBuf->n];
fts5DoclistIterNext(pIter);
}
#if 0
@@ -4891,20 +4893,16 @@ static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){
static void fts5MergeRowidLists(
Fts5Index *p, /* FTS5 backend object */
Fts5Buffer *p1, /* First list to merge */
int nBuf, /* Number of entries in apBuf[] */
Fts5Buffer *aBuf /* Array of other lists to merge into p1 */
Fts5Buffer *p2 /* Second list to merge */
){
int i1 = 0;
int i2 = 0;
i64 iRowid1 = 0;
i64 iRowid2 = 0;
i64 iOut = 0;
Fts5Buffer *p2 = &aBuf[0];
Fts5Buffer out;
(void)nBuf;
Fts5Buffer out;
memset(&out, 0, sizeof(out));
assert( nBuf==1 );
sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
if( p->rc ) return;
@@ -4931,213 +4929,177 @@ static void fts5MergeRowidLists(
fts5BufferFree(&out);
}
typedef struct PrefixMerger PrefixMerger;
struct PrefixMerger {
Fts5DoclistIter iter; /* Doclist iterator */
i64 iPos; /* For iterating through a position list */
int iOff;
u8 *aPos;
PrefixMerger *pNext; /* Next in docid/poslist order */
};
static void fts5PrefixMergerInsertByRowid(
PrefixMerger **ppHead,
PrefixMerger *p
){
if( p->iter.aPoslist ){
PrefixMerger **pp = ppHead;
while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){
pp = &(*pp)->pNext;
}
p->pNext = *pp;
*pp = p;
}
}
static void fts5PrefixMergerInsertByPosition(
PrefixMerger **ppHead,
PrefixMerger *p
){
if( p->iPos>=0 ){
PrefixMerger **pp = ppHead;
while( *pp && p->iPos>(*pp)->iPos ){
pp = &(*pp)->pNext;
}
p->pNext = *pp;
*pp = p;
}
}
/*
** Array aBuf[] contains nBuf doclists. These are all merged in with the
** doclist in buffer p1.
** Buffers p1 and p2 contain doclists. This function merges the content
** of the two doclists together and sets buffer p1 to the result before
** returning.
**
** If an error occurs, an error code is left in p->rc. If an error has
** already occurred, this function is a no-op.
*/
static void fts5MergePrefixLists(
Fts5Index *p, /* FTS5 backend object */
Fts5Buffer *p1, /* First list to merge */
int nBuf, /* Number of buffers in array aBuf[] */
Fts5Buffer *aBuf /* Other lists to merge in */
Fts5Buffer *p2 /* Second list to merge */
){
#define fts5PrefixMergerNextPosition(p) \
sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos);
#define FTS5_MERGE_NLIST 16
PrefixMerger aMerger[FTS5_MERGE_NLIST];
PrefixMerger *pHead = 0;
int i;
int nOut = 0;
Fts5Buffer out = {0, 0, 0};
Fts5Buffer tmp = {0, 0, 0};
i64 iLastRowid = 0;
if( p2->n ){
i64 iLastRowid = 0;
Fts5DoclistIter i1;
Fts5DoclistIter i2;
Fts5Buffer out = {0, 0, 0};
Fts5Buffer tmp = {0, 0, 0};
/* Initialize a doclist-iterator for each input buffer. Arrange them in
** a linked-list starting at pHead in ascending order of rowid. Avoid
** linking any iterators already at EOF into the linked list at all. */
assert( nBuf+1<=sizeof(aMerger)/sizeof(aMerger[0]) );
memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1));
pHead = &aMerger[nBuf];
fts5DoclistIterInit(p1, &pHead->iter);
for(i=0; i<nBuf; i++){
fts5DoclistIterInit(&aBuf[i], &aMerger[i].iter);
fts5PrefixMergerInsertByRowid(&pHead, &aMerger[i]);
nOut += aBuf[i].n;
}
if( nOut==0 ) return;
nOut += p1->n + 9 + 10*nBuf;
/* The maximum size of the output is equal to the sum of the two
** input sizes + 1 varint (9 bytes). The extra varint is because if the
** first rowid in one input is a large negative number, and the first in
** the other a non-negative number, the delta for the non-negative
** number will be larger on disk than the literal integer value
** was.
**
** Or, if the input position-lists are corrupt, then the output might
** include up to 2 extra 10-byte positions created by interpreting -1
** (the value PoslistNext64() uses for EOF) as a position and appending
** it to the output. This can happen at most once for each input
** position-list, hence two 10 byte paddings. */
if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return;
fts5DoclistIterInit(p1, &i1);
fts5DoclistIterInit(p2, &i2);
/* The maximum size of the output is equal to the sum of the
** input sizes + 1 varint (9 bytes). The extra varint is because if the
** first rowid in one input is a large negative number, and the first in
** the other a non-negative number, the delta for the non-negative
** number will be larger on disk than the literal integer value
** was.
**
** Or, if the input position-lists are corrupt, then the output might
** include up to (nBuf+1) extra 10-byte positions created by interpreting -1
** (the value PoslistNext64() uses for EOF) as a position and appending
** it to the output. This can happen at most once for each input
** position-list, hence (nBuf+1) 10 byte paddings. */
if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return;
while( pHead ){
fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid);
if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){
/* Merge data from two or more poslists */
i64 iPrev = 0;
int nTmp = FTS5_DATA_ZERO_PADDING;
int nMerge = 0;
PrefixMerger *pSave = pHead;
PrefixMerger *pThis = 0;
int nTail = 0;
pHead = 0;
while( pSave && pSave->iter.iRowid==iLastRowid ){
PrefixMerger *pNext = pSave->pNext;
pSave->iOff = 0;
pSave->iPos = 0;
pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize];
fts5PrefixMergerNextPosition(pSave);
nTmp += pSave->iter.nPoslist + 10;
nMerge++;
fts5PrefixMergerInsertByPosition(&pHead, pSave);
pSave = pNext;
while( 1 ){
if( i1.iRowid<i2.iRowid ){
/* Copy entry from i1 */
fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
fts5DoclistIterNext(&i1);
if( i1.aPoslist==0 ) break;
assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
}
if( pHead==0 || pHead->pNext==0 ){
p->rc = FTS5_CORRUPT;
break;
else if( i2.iRowid!=i1.iRowid ){
/* Copy entry from i2 */
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
fts5DoclistIterNext(&i2);
if( i2.aPoslist==0 ) break;
assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
}
else{
/* Merge the two position lists. */
i64 iPos1 = 0;
i64 iPos2 = 0;
int iOff1 = 0;
int iOff2 = 0;
u8 *a1 = &i1.aPoslist[i1.nSize];
u8 *a2 = &i2.aPoslist[i2.nSize];
int nCopy;
u8 *aCopy;
/* See the earlier comment in this function for an explanation of why
** corrupt input position lists might cause the output to consume
** at most nMerge*10 bytes of unexpected space. */
if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){
break;
}
fts5BufferZero(&tmp);
i64 iPrev = 0;
Fts5PoslistWriter writer;
memset(&writer, 0, sizeof(writer));
pThis = pHead;
pHead = pThis->pNext;
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
fts5PrefixMergerNextPosition(pThis);
fts5PrefixMergerInsertByPosition(&pHead, pThis);
/* See the earlier comment in this function for an explanation of why
** corrupt input position lists might cause the output to consume
** at most 20 bytes of unexpected space. */
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
fts5BufferZero(&tmp);
sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10);
if( p->rc ) break;
while( pHead->pNext ){
pThis = pHead;
if( pThis->iPos!=iPrev ){
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
assert_nc( iPos1>=0 && iPos2>=0 );
if( iPos1<iPos2 ){
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
}else{
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
}
fts5PrefixMergerNextPosition(pThis);
pHead = pThis->pNext;
fts5PrefixMergerInsertByPosition(&pHead, pThis);
}
if( pHead->iPos!=iPrev ){
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos);
}
nTail = pHead->iter.nPoslist - pHead->iOff;
/* WRITEPOSLISTSIZE */
assert( tmp.n+nTail<=nTmp );
if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
break;
}
fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
if( nTail>0 ){
fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail);
}
pHead = pSave;
for(i=0; i<nBuf+1; i++){
PrefixMerger *pX = &aMerger[i];
if( pX->iter.aPoslist && pX->iter.iRowid==iLastRowid ){
fts5DoclistIterNext(&pX->iter);
fts5PrefixMergerInsertByRowid(&pHead, pX);
if( iPos1>=0 && iPos2>=0 ){
while( 1 ){
if( iPos1<iPos2 ){
if( iPos1!=iPrev ){
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
}
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
if( iPos1<0 ) break;
}else{
assert_nc( iPos2!=iPrev );
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
if( iPos2<0 ) break;
}
}
}
}
}else{
/* Copy poslist from pHead to output */
PrefixMerger *pThis = pHead;
Fts5DoclistIter *pI = &pThis->iter;
fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize);
fts5DoclistIterNext(pI);
pHead = pThis->pNext;
fts5PrefixMergerInsertByRowid(&pHead, pThis);
if( iPos1>=0 ){
if( iPos1!=iPrev ){
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
}
aCopy = &a1[iOff1];
nCopy = i1.nPoslist - iOff1;
}else{
assert_nc( iPos2>=0 && iPos2!=iPrev );
sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
aCopy = &a2[iOff2];
nCopy = i2.nPoslist - iOff2;
}
if( nCopy>0 ){
fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy);
}
/* WRITEPOSLISTSIZE */
assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist );
assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 );
if( tmp.n>i1.nPoslist+i2.nPoslist ){
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
break;
}
fts5BufferSafeAppendVarint(&out, tmp.n * 2);
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
fts5DoclistIterNext(&i1);
fts5DoclistIterNext(&i2);
assert_nc( out.n<=(p1->n+p2->n+9) );
if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
}
}
}
fts5BufferFree(p1);
fts5BufferFree(&tmp);
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
*p1 = out;
if( i1.aPoslist ){
fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);
}
else if( i2.aPoslist ){
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
}
assert_nc( out.n<=(p1->n+p2->n+9) );
fts5BufferSet(&p->rc, p1, out.n, out.p);
fts5BufferFree(&tmp);
fts5BufferFree(&out);
}
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
int iIdx, /* Index to scan for data */
u8 *pToken, /* Buffer containing prefix to match */
const u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
int nBuf = 32;
int nMerge = 1;
const int nBuf = 32;
void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
xMerge = fts5MergeRowidLists;
xAppend = fts5AppendRowid;
}else{
nMerge = FTS5_MERGE_NLIST-1;
nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
xMerge = fts5MergePrefixLists;
xAppend = fts5AppendPoslist;
}
@@ -5157,27 +5119,6 @@ static void fts5SetupPrefixIter(
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
if( iIdx!=0 ){
int dummy = 0;
const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
pToken[0] = FTS5_MAIN_PREFIX;
fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for(;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &dummy)
){
Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
p1->xSetOutputs(p1, pSeg);
if( p1->base.nData ){
xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
iLastRowid = p1->base.iRowid;
}
}
fts5MultiIterFree(p1);
}
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for( /* no-op */ ;
@@ -5198,21 +5139,13 @@ static void fts5SetupPrefixIter(
if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
int i1 = i*nMerge;
int iStore;
assert( i1+nMerge<=nBuf );
for(iStore=i1; iStore<i1+nMerge; iStore++){
if( aBuf[iStore].n==0 ){
fts5BufferSwap(&doclist, &aBuf[iStore]);
fts5BufferZero(&doclist);
break;
}
}
if( iStore==i1+nMerge ){
xMerge(p, &doclist, nMerge, &aBuf[i1]);
for(iStore=i1; iStore<i1+nMerge; iStore++){
fts5BufferZero(&aBuf[iStore]);
}
assert( i<nBuf );
if( aBuf[i].n==0 ){
fts5BufferSwap(&doclist, &aBuf[i]);
fts5BufferZero(&doclist);
}else{
xMerge(p, &doclist, &aBuf[i]);
fts5BufferZero(&aBuf[i]);
}
}
iLastRowid = 0;
@@ -5222,15 +5155,11 @@ static void fts5SetupPrefixIter(
iLastRowid = p1->base.iRowid;
}
assert( (nBuf%nMerge)==0 );
for(i=0; i<nBuf; i+=nMerge){
int iFree;
for(i=0; i<nBuf; i++){
if( p->rc==SQLITE_OK ){
xMerge(p, &doclist, nMerge, &aBuf[i]);
}
for(iFree=i; iFree<i+nMerge; iFree++){
fts5BufferFree(&aBuf[iFree]);
xMerge(p, &doclist, &aBuf[i]);
}
fts5BufferFree(&aBuf[i]);
}
fts5MultiIterFree(p1);
@@ -5280,7 +5209,7 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
int sqlite3Fts5IndexSync(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
sqlite3Fts5IndexCloseReader(p);
fts5CloseReader(p);
return fts5IndexReturn(p);
}
@@ -5291,7 +5220,7 @@ int sqlite3Fts5IndexSync(Fts5Index *p){
** records must be invalidated.
*/
int sqlite3Fts5IndexRollback(Fts5Index *p){
sqlite3Fts5IndexCloseReader(p);
fts5CloseReader(p);
fts5IndexDiscardData(p);
fts5StructureInvalidate(p);
/* assert( p->rc==SQLITE_OK ); */
@@ -5394,13 +5323,9 @@ int sqlite3Fts5IndexCharlenToBytelen(
for(i=0; i<nChar; i++){
if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */
if( (unsigned char)p[n++]>=0xc0 ){
if( n>=nByte ) return 0;
while( (p[n] & 0xc0)==0x80 ){
n++;
if( n>=nByte ){
if( i+1==nChar ) break;
return 0;
}
if( n>=nByte ) break;
}
}
}
@@ -5485,7 +5410,6 @@ int sqlite3Fts5IndexQuery(
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
if( nToken ) memcpy(&buf.p[1], pToken, nToken);
/* Figure out which index to search and set iIdx accordingly. If this
@@ -5507,9 +5431,7 @@ int sqlite3Fts5IndexQuery(
if( flags & FTS5INDEX_QUERY_PREFIX ){
int nChar = fts5IndexCharlen(pToken, nToken);
for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
if( pConfig->aPrefix[iIdx-1]==nChar ) break;
}
}
@@ -5526,7 +5448,8 @@ int sqlite3Fts5IndexQuery(
}else{
/* Scan multiple terms in the main index */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
buf.p[0] = FTS5_MAIN_PREFIX;
fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
fts5IterSetOutputCb(&p->rc, pRet);
if( p->rc==SQLITE_OK ){
@@ -5538,7 +5461,7 @@ int sqlite3Fts5IndexQuery(
if( p->rc ){
sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
pRet = 0;
sqlite3Fts5IndexCloseReader(p);
fts5CloseReader(p);
}
*ppIter = (Fts5IndexIter*)pRet;
@@ -5599,9 +5522,8 @@ int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
int n;
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
assert_nc( z || n<=1 );
*pn = n-1;
return (z ? &z[1] : 0);
return &z[1];
}
/*
@@ -5612,7 +5534,7 @@ void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *pIndex = pIter->pIndex;
fts5MultiIterFree(pIter);
sqlite3Fts5IndexCloseReader(pIndex);
fts5CloseReader(pIndex);
}
}
@@ -5805,37 +5727,6 @@ static int fts5QueryCksum(
return rc;
}
/*
** Check if buffer z[], size n bytes, contains as series of valid utf-8
** encoded codepoints. If so, return 0. Otherwise, if the buffer does not
** contain valid utf-8, return non-zero.
*/
static int fts5TestUtf8(const char *z, int n){
int i = 0;
assert_nc( n>0 );
while( i<n ){
if( (z[i] & 0x80)==0x00 ){
i++;
}else
if( (z[i] & 0xE0)==0xC0 ){
if( i+1>=n || (z[i+1] & 0xC0)!=0x80 ) return 1;
i += 2;
}else
if( (z[i] & 0xF0)==0xE0 ){
if( i+2>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1;
i += 3;
}else
if( (z[i] & 0xF8)==0xF0 ){
if( i+3>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1;
if( (z[i+2] & 0xC0)!=0x80 ) return 1;
i += 3;
}else{
return 1;
}
}
return 0;
}
/*
** This function is also purely an internal test. It does not contribute to
@@ -5876,14 +5767,8 @@ static void fts5TestTerm(
** This check may only be performed if the hash table is empty. This
** is because the hash table only supports a single scan query at
** a time, and the multi-iter loop from which this function is called
** is already performing such a scan.
**
** Also only do this if buffer zTerm contains nTerm bytes of valid
** utf-8. Otherwise, the last part of the buffer contents might contain
** a non-utf-8 sequence that happens to be a prefix of a valid utf-8
** character stored in the main fts index, which will cause the
** test to fail. */
if( p->nPendingData==0 && 0==fts5TestUtf8(zTerm, nTerm) ){
** is already performing such a scan. */
if( p->nPendingData==0 ){
if( iIdx>0 && rc==SQLITE_OK ){
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
ck2 = 0;
@@ -6016,8 +5901,8 @@ static void fts5IndexIntegrityCheckSegment(
i64 iRow; /* Rowid for this leaf */
Fts5Data *pLeaf; /* Data for this leaf */
const char *zIdxTerm = (const char*)sqlite3_column_blob(pStmt, 1);
int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
int iIdxLeaf = sqlite3_column_int(pStmt, 2);
int bIdxDlidx = sqlite3_column_int(pStmt, 3);
@@ -6138,7 +6023,7 @@ static void fts5IndexIntegrityCheckSegment(
** error, or some other SQLite error code if another error (e.g. OOM)
** occurs.
*/
int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
int eDetail = p->pConfig->eDetail;
u64 cksum2 = 0; /* Checksum based on contents of indexes */
Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
@@ -6199,7 +6084,6 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
}else{
poslist.n = 0;
fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0");
while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
int iCol = FTS5_POS2COLUMN(iPos);
int iTokOff = FTS5_POS2OFFSET(iPos);
@@ -6210,7 +6094,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
fts5MultiIterFree(pIter);
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
fts5StructureRelease(pStruct);
#ifdef SQLITE_DEBUG
+42 -117
View File
@@ -289,10 +289,7 @@ static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
case FTS5_ROLLBACKTO:
assert( p->ts.eState==1 );
assert( iSavepoint>=-1 );
/* The following assert() can fail if another vtab strikes an error
** within an xSavepoint() call then SQLite calls xRollbackTo() - without
** having called xSavepoint() on this vtab. */
/* assert( iSavepoint<=p->ts.iSavepoint ); */
assert( iSavepoint<=p->ts.iSavepoint );
p->ts.iSavepoint = iSavepoint;
break;
}
@@ -464,23 +461,6 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
#endif
}
static int fts5UsePatternMatch(
Fts5Config *pConfig,
struct sqlite3_index_constraint *p
){
assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
if( pConfig->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){
return 1;
}
if( pConfig->ePattern==FTS5_PATTERN_LIKE
&& (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB)
){
return 1;
}
return 0;
}
/*
** Implementation of the xBestIndex method for FTS5 tables. Within the
** WHERE constraint, it searches for the following:
@@ -510,9 +490,7 @@ static int fts5UsePatternMatch(
**
** Match against table column: "m"
** Match against rank column: "r"
** Match against other column: "M<column-number>"
** LIKE against other column: "L<column-number>"
** GLOB against other column: "G<column-number>"
** Match against other column: "<column-number>"
** Equality constraint against the rowid: "="
** A < or <= against the rowid: "<"
** A > or >= against the rowid: ">"
@@ -573,7 +551,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
return SQLITE_ERROR;
}
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1);
if( idxStr==0 ) return SQLITE_NOMEM;
pInfo->idxStr = idxStr;
pInfo->needToFreeIdxStr = 1;
@@ -597,29 +575,25 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else if( iCol>=0 ){
}else{
bSeenMatch = 1;
idxStr[iIdxStr++] = 'M';
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
idxStr += strlen(&idxStr[iIdxStr]);
assert( idxStr[iIdxStr]=='\0' );
idxStr[iIdxStr++] = 'm';
if( iCol<nCol ){
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
idxStr += strlen(&idxStr[iIdxStr]);
assert( idxStr[iIdxStr]=='\0' );
}
}
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
pInfo->aConstraintUsage[i].omit = 1;
}
}else if( p->usable ){
if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){
assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB );
idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G';
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
idxStr += strlen(&idxStr[iIdxStr]);
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
assert( idxStr[iIdxStr]=='\0' );
}else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
idxStr[iIdxStr++] = '=';
bSeenEq = 1;
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
}
}
else if( p->usable && bSeenEq==0
&& p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0
){
idxStr[iIdxStr++] = '=';
bSeenEq = 1;
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
}
}
@@ -770,7 +744,6 @@ static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
sqlite3_free(pCsr->zRankArgs);
}
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr));
}
@@ -921,24 +894,15 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
break;
}
default: {
Fts5Config *pConfig = ((Fts5Table*)pCursor->pVtab)->pConfig;
pConfig->bLock++;
default:
rc = sqlite3_step(pCsr->pStmt);
pConfig->bLock--;
if( rc!=SQLITE_ROW ){
CsrFlagSet(pCsr, FTS5CSR_EOF);
rc = sqlite3_reset(pCsr->pStmt);
if( rc!=SQLITE_OK ){
pCursor->pVtab->zErrMsg = sqlite3_mprintf(
"%s", sqlite3_errmsg(pConfig->db)
);
}
}else{
rc = SQLITE_OK;
}
break;
}
}
}
@@ -1223,13 +1187,6 @@ static int fts5FilterMethod(
int iIdxStr = 0;
Fts5Expr *pExpr = 0;
if( pConfig->bLock ){
pTab->p.base.zErrMsg = sqlite3_mprintf(
"recursively defined fts5 content table"
);
return SQLITE_ERROR;
}
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr));
@@ -1252,14 +1209,19 @@ static int fts5FilterMethod(
case 'r':
pRank = apVal[i];
break;
case 'M': {
case 'm': {
const char *zText = (const char*)sqlite3_value_text(apVal[i]);
if( zText==0 ) zText = "";
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
}else{
iCol = pConfig->nCol;
}
if( zText[0]=='*' ){
/* The user has issued a query of the form "MATCH '*...'". This
@@ -1269,7 +1231,7 @@ static int fts5FilterMethod(
goto filter_out;
}else{
char **pzErr = &pTab->p.base.zErrMsg;
rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr);
rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr);
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
pExpr = 0;
@@ -1279,25 +1241,6 @@ static int fts5FilterMethod(
break;
}
case 'L':
case 'G': {
int bGlob = (idxStr[iIdxStr-1]=='G');
const char *zText = (const char*)sqlite3_value_text(apVal[i]);
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
if( zText ){
rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
pExpr = 0;
}
if( rc!=SQLITE_OK ) goto filter_out;
break;
}
case '=':
pRowidEq = apVal[i];
break;
@@ -1464,13 +1407,10 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
}
if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
assert( pCsr->pExpr );
sqlite3_reset(pCsr->pStmt);
sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
pTab->pConfig->bLock++;
rc = sqlite3_step(pCsr->pStmt);
pTab->pConfig->bLock--;
if( rc==SQLITE_ROW ){
rc = SQLITE_OK;
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
@@ -1478,10 +1418,6 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
rc = sqlite3_reset(pCsr->pStmt);
if( rc==SQLITE_OK ){
rc = FTS5_CORRUPT;
}else if( pTab->pConfig->pzErrmsg ){
*pTab->pConfig->pzErrmsg = sqlite3_mprintf(
"%s", sqlite3_errmsg(pTab->pConfig->db)
);
}
}
}
@@ -1545,8 +1481,7 @@ static int fts5SpecialInsert(
int nMerge = sqlite3_value_int(pVal);
rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
}else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
int iArg = sqlite3_value_int(pVal);
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg);
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
#ifdef SQLITE_DEBUG
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
@@ -2177,8 +2112,7 @@ static int fts5ApiPhraseFirst(
int n;
int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
if( rc==SQLITE_OK ){
assert( pIter->a || n==0 );
pIter->b = (pIter->a ? &pIter->a[n] : 0);
pIter->b = &pIter->a[n];
*piCol = 0;
*piOff = 0;
fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
@@ -2237,8 +2171,7 @@ static int fts5ApiPhraseFirstColumn(
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
}
if( rc==SQLITE_OK ){
assert( pIter->a || n==0 );
pIter->b = (pIter->a ? &pIter->a[n] : 0);
pIter->b = &pIter->a[n];
*piCol = 0;
fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
}
@@ -2246,8 +2179,7 @@ static int fts5ApiPhraseFirstColumn(
int n;
rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
if( rc==SQLITE_OK ){
assert( pIter->a || n==0 );
pIter->b = (pIter->a ? &pIter->a[n] : 0);
pIter->b = &pIter->a[n];
if( n<=0 ){
*piCol = -1;
}else if( pIter->a[0]==0x01 ){
@@ -2501,12 +2433,10 @@ static int fts5ColumnMethod(
}
}
}else if( !fts5IsContentless(pTab) ){
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
rc = fts5SeekCursor(pCsr, 1);
if( rc==SQLITE_OK ){
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
pConfig->pzErrmsg = 0;
}
return rc;
}
@@ -2712,7 +2642,8 @@ int sqlite3Fts5GetTokenizer(
Fts5Global *pGlobal,
const char **azArg,
int nArg,
Fts5Config *pConfig,
Fts5Tokenizer **ppTok,
fts5_tokenizer **ppTokApi,
char **pzErr
){
Fts5TokenizerModule *pMod;
@@ -2724,22 +2655,16 @@ int sqlite3Fts5GetTokenizer(
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
}else{
rc = pMod->x.xCreate(
pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
);
pConfig->pTokApi = &pMod->x;
if( rc!=SQLITE_OK ){
if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
}else{
pConfig->ePattern = sqlite3Fts5TokenizerPattern(
pMod->x.xCreate, pConfig->pTok
);
rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok);
*ppTokApi = &pMod->x;
if( rc!=SQLITE_OK && pzErr ){
*pzErr = sqlite3_mprintf("error in tokenizer constructor");
}
}
if( rc!=SQLITE_OK ){
pConfig->pTokApi = 0;
pConfig->pTok = 0;
*ppTokApi = 0;
*ppTok = 0;
}
return rc;
+68 -84
View File
@@ -429,16 +429,9 @@ static int fts5StorageDeleteFromIndex(
zText, nText, (void*)&ctx, fts5StorageInsertCallback
);
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
if( p->aTotalSize[iCol-1]<0 ){
rc = FTS5_CORRUPT;
}
}
}
if( rc==SQLITE_OK && p->nTotalRow<1 ){
rc = FTS5_CORRUPT;
}else{
p->nTotalRow--;
}
p->nTotalRow--;
rc2 = sqlite3_reset(pSeek);
if( rc==SQLITE_OK ) rc = rc2;
@@ -620,11 +613,10 @@ 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,
(const char*)sqlite3_column_text(pScan, ctx.iCol+1),
sqlite3_column_bytes(pScan, ctx.iCol+1),
(void*)&ctx,
fts5StorageInsertCallback
);
@@ -746,11 +738,10 @@ 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,
(const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
sqlite3_value_bytes(apVal[ctx.iCol+2]),
(void*)&ctx,
fts5StorageInsertCallback
);
@@ -881,14 +872,13 @@ static int fts5StorageIntegrityCallback(
** some other SQLite error code if an error occurs while attempting to
** determine this.
*/
int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){
int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
Fts5Config *pConfig = p->pConfig;
int rc = SQLITE_OK; /* Return code */
int rc; /* Return code */
int *aColSize; /* Array of size pConfig->nCol */
i64 *aTotalSize; /* Array of size pConfig->nCol */
Fts5IntegrityCtx ctx;
sqlite3_stmt *pScan;
int bUseCksum;
memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
ctx.pConfig = p->pConfig;
@@ -897,88 +887,82 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){
aColSize = (int*)&aTotalSize[pConfig->nCol];
memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL
|| (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg)
);
if( bUseCksum ){
/* Generate the expected index checksum based on the contents of the
** %_content table. This block stores the checksum in ctx.cksum. */
rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
if( rc==SQLITE_OK ){
int rc2;
while( SQLITE_ROW==sqlite3_step(pScan) ){
int i;
ctx.iRowid = sqlite3_column_int64(pScan, 0);
/* Generate the expected index checksum based on the contents of the
** %_content table. This block stores the checksum in ctx.cksum. */
rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
if( rc==SQLITE_OK ){
int rc2;
while( SQLITE_ROW==sqlite3_step(pScan) ){
int i;
ctx.iRowid = sqlite3_column_int64(pScan, 0);
ctx.szCol = 0;
if( pConfig->bColumnsize ){
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
}
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
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->bColumnsize ){
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
}
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
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( rc==SQLITE_OK ){
rc = sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT,
(const char*)sqlite3_column_text(pScan, i+1),
sqlite3_column_bytes(pScan, i+1),
(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;
}
sqlite3Fts5TermsetFree(ctx.pTermset);
ctx.pTermset = 0;
if( rc!=SQLITE_OK ) break;
}
rc2 = sqlite3_reset(pScan);
if( rc==SQLITE_OK ) rc = rc2;
}
sqlite3Fts5TermsetFree(ctx.pTermset);
ctx.pTermset = 0;
/* Test that the "totals" (sometimes called "averages") record looks Ok */
if( rc==SQLITE_OK ){
int i;
rc = fts5StorageLoadTotals(p, 0);
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
}
if( rc!=SQLITE_OK ) break;
}
rc2 = sqlite3_reset(pScan);
if( rc==SQLITE_OK ) rc = rc2;
}
/* Check that the %_docsize and %_content tables contain the expected
** number of rows. */
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
i64 nRow = 0;
rc = fts5StorageCount(p, "content", &nRow);
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
}
if( rc==SQLITE_OK && pConfig->bColumnsize ){
i64 nRow = 0;
rc = fts5StorageCount(p, "docsize", &nRow);
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
/* Test that the "totals" (sometimes called "averages") record looks Ok */
if( rc==SQLITE_OK ){
int i;
rc = fts5StorageLoadTotals(p, 0);
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
}
}
/* Check that the %_docsize and %_content tables contain the expected
** number of rows. */
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
i64 nRow = 0;
rc = fts5StorageCount(p, "content", &nRow);
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
}
if( rc==SQLITE_OK && pConfig->bColumnsize ){
i64 nRow = 0;
rc = fts5StorageCount(p, "docsize", &nRow);
if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
}
/* Pass the expected checksum down to the FTS index module. It will
** verify, amongst other things, that it matches the checksum generated by
** inspecting the index itself. */
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum);
rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
}
sqlite3_free(aTotalSize);
+1 -1
View File
@@ -211,7 +211,7 @@ static int fts5tokConnectMethod(
rc = pApi->xFindTokenizer(pApi, zModule, &pTokCtx, &pTab->tok);
if( rc==SQLITE_OK ){
const char **azArg = (nDequote>1 ? (const char **)&azDequote[1] : 0);
const char **azArg = (const char **)&azDequote[1];
int nArg = nDequote>0 ? nDequote-1 : 0;
rc = pTab->tok.xCreate(pTokCtx, azArg, nArg, &pTab->pTok);
}
-128
View File
@@ -1258,133 +1258,6 @@ static int fts5PorterTokenize(
);
}
/**************************************************************************
** Start of trigram implementation.
*/
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
int bFold; /* True to fold to lower-case */
};
/*
** Free a trigram tokenizer.
*/
static void fts5TriDelete(Fts5Tokenizer *p){
sqlite3_free(p);
}
/*
** Allocate a trigram tokenizer.
*/
static int fts5TriCreate(
void *pUnused,
const char **azArg,
int nArg,
Fts5Tokenizer **ppOut
){
int rc = SQLITE_OK;
TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
UNUSED_PARAM(pUnused);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
int i;
pNew->bFold = 1;
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{
rc = SQLITE_ERROR;
}
}
if( rc!=SQLITE_OK ){
fts5TriDelete((Fts5Tokenizer*)pNew);
pNew = 0;
}
}
*ppOut = (Fts5Tokenizer*)pNew;
return rc;
}
/*
** Trigram tokenizer tokenize routine.
*/
static int fts5TriTokenize(
Fts5Tokenizer *pTok,
void *pCtx,
int unusedFlags,
const char *pText, int nText,
int (*xToken)(void*, int, const char*, int, int, int)
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = &zIn[nText];
u32 iCode;
UNUSED_PARAM(unusedFlags);
while( 1 ){
char *zOut = aBuf;
int iStart = zIn - (const unsigned char*)pText;
const unsigned char *zNext;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
zNext = zIn;
if( zIn<zEof ){
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
WRITE_UTF8(zOut, iCode);
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
}else{
break;
}
if( zIn<zEof ){
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
WRITE_UTF8(zOut, iCode);
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
WRITE_UTF8(zOut, iCode);
}else{
break;
}
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
if( rc!=SQLITE_OK ) break;
zIn = zNext;
}
return rc;
}
/*
** Argument xCreate is a pointer to a constructor function for a tokenizer.
** pTok is a tokenizer previously created using the same method. This function
** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB
** indicating the style of pattern matching that the tokenizer can support.
** In practice, this is:
**
** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB
** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE
** all other tokenizers - FTS5_PATTERN_NONE
*/
int sqlite3Fts5TokenizerPattern(
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
Fts5Tokenizer *pTok
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
}
return FTS5_PATTERN_NONE;
}
/*
** Register all built-in tokenizers with FTS5.
*/
@@ -1396,7 +1269,6 @@ int sqlite3Fts5TokenizerInit(fts5_api *pApi){
{ "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
{ "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
{ "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
{ "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}},
};
int rc = SQLITE_OK; /* Return code */
-2
View File
@@ -773,6 +773,4 @@ void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
}
iTbl++;
}
aAscii[0] = 0; /* 0x00 is never a token character */
}
-9
View File
@@ -50,7 +50,6 @@ struct Fts5VocabTable {
sqlite3 *db; /* Database handle */
Fts5Global *pGlobal; /* FTS5 global object for this database */
int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */
unsigned bBusy; /* True if busy */
};
struct Fts5VocabCursor {
@@ -333,12 +332,6 @@ static int fts5VocabOpenMethod(
sqlite3_stmt *pStmt = 0;
char *zSql = 0;
if( pTab->bBusy ){
pVTab->zErrMsg = sqlite3_mprintf(
"recursive definition for %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
);
return SQLITE_ERROR;
}
zSql = sqlite3Fts5Mprintf(&rc,
"SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
@@ -350,12 +343,10 @@ static int fts5VocabOpenMethod(
assert( rc==SQLITE_OK || pStmt==0 );
if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
pTab->bBusy = 1;
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
i64 iId = sqlite3_column_int64(pStmt, 0);
pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId);
}
pTab->bBusy = 0;
if( rc==SQLITE_OK ){
if( pFts5==0 ){
-19
View File
@@ -82,24 +82,5 @@ foreach_detail_mode $::testprefix {
} {1 {unable to use function MATCH in the requested context}}
}
#-------------------------------------------------------------------------
# Confirm that the expression parser creates the same expression tree
# for:
#
# {a b} : (abc AND def)
# -{c d} : (abc AND def)
#
# Assuming that the table columns are (a, b, c, d).
#
do_execsql_test 5.1 {
SELECT fts5_expr('abcd AND cdef');
} {{"abcd" AND "cdef"}}
do_execsql_test 5.2 {
SELECT fts5_expr('{a b} : (abcd AND cdef)', 'a', 'b', 'c', 'd');
} {{{a b} : "abcd" AND {a b} : "cdef"}}
do_execsql_test 5.3 {
SELECT fts5_expr('-{c d} : (abcd AND cdef)', 'a', 'b', 'c', 'd');
} {{{a b} : "abcd" AND {a b} : "cdef"}}
finish_test
+3 -28
View File
@@ -257,41 +257,16 @@ do_execsql_test 6.2 {
# Check that an fts5 table cannot be its own content table.
#
reset_db
do_execsql_test 7.1.1 {
do_execsql_test 7.1 {
CREATE VIRTUAL TABLE t1 USING fts5(a, c=t1 );
INSERT INTO t1( a ) VALUES('abc');
}
do_catchsql_test 7.1.2 {
do_catchsql_test 7.2 {
SELECT * FROM t1;
} {1 {recursively defined fts5 content table}}
do_catchsql_test 7.1.3 {
do_catchsql_test 7.3 {
SELECT * FROM t1('abc');
} {1 {recursively defined fts5 content table}}
do_catchsql_test 7.1.4 {
SELECT count(*) FROM t1;
} {1 {recursively defined fts5 content table}}
do_catchsql_test 7.1.5 {
SELECT * FROM t1('abc') ORDER BY rank;
} {1 {recursively defined fts5 content table}}
reset_db
do_execsql_test 7.2.1 {
CREATE VIRTUAL TABLE t1 USING fts5(a, c=t2 );
CREATE VIRTUAL TABLE t2 USING fts5(a, c=t1 );
INSERT INTO t1( a ) VALUES('abc');
}
do_catchsql_test 7.2.2 {
SELECT * FROM t1;
} {1 {recursively defined fts5 content table}}
do_catchsql_test 7.2.3 {
SELECT * FROM t1('abc');
} {1 {recursively defined fts5 content table}}
do_catchsql_test 7.2.4 {
SELECT count(*) FROM t1;
} {1 {recursively defined fts5 content table}}
do_catchsql_test 7.2.5 {
SELECT * FROM t1('abc') ORDER BY rank;
} {1 {recursively defined fts5 content table}}
finish_test
File diff suppressed because it is too large Load Diff
-2
View File
@@ -29,8 +29,6 @@ do_execsql_test 1.0 {
INSERT INTO ttt SELECT a||a, b||b FROM ttt;
}
expr srand(1)
proc mutate {blob i} {
set o [expr {$i % [string length $blob]}]
set a [string range $blob 0 $o-1]
-64
View File
@@ -50,68 +50,4 @@ do_test 1.2 {
}
} {}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE TABLE test (
id INTEGER PRIMARY KEY,
name TEXT,
value TEXT
);
CREATE VIRTUAL TABLE test_idx USING fts5(
name, content=test, content_rowid=id
);
}
do_catchsql_test 2.1 {
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 1, 'quick');
} {1 {database disk image is malformed}}
do_catchsql_test 2.2 {
INSERT INTO test_idx(rowid, name) VALUES(123, 'one one one');
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
} {1 {database disk image is malformed}}
do_execsql_test 2.3 {
DROP TABLE test_idx;
CREATE VIRTUAL TABLE test_idx USING fts5(
name, content=test, content_rowid=id
);
INSERT INTO test_idx(rowid, name) VALUES(123, 'one one one');
INSERT INTO test_idx(rowid, name) VALUES(124, 'two two two');
INSERT INTO test_idx(rowid, name) VALUES(125, 'two two two');
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
INSERT INTO test_idx (test_idx, rowid, name) VALUES('delete', 123, 'one');
}
do_catchsql_test 2.4 {
SELECT rowid FROM test_idx WHERE test_idx MATCH 'two' ORDER BY rank;
} {1 {database disk image is malformed}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE tx USING fts5(a, b, c, d, content=);
INSERT INTO tx(rowid, a, c) VALUES(1, 'abc def', 'a b c');
INSERT INTO tx(rowid, a, c) VALUES(5, 'a b c', 'a b d def');
}
do_execsql_test 3.1 {
INSERT INTO tx(tx, rowid, a, b, c, d)
VALUES('delete', 5, 'a b c', NULL, 'a b d def', NULL);
}
do_execsql_test 3.2 {
INSERT INTO tx(tx) VALUES('integrity-check');
}
do_execsql_test 3.3 {
INSERT INTO tx(tx, rowid, a, b, c, d)
VALUES('delete', 1, 'abc def', NULL, 'a b c', NULL);
}
do_execsql_test 3.4 {
INSERT INTO tx(tx) VALUES('integrity-check');
}
finish_test
+1 -18
View File
@@ -59,27 +59,10 @@ do_catchsql_test 2.1 {
SELECT fts5_expr()
} {1 {wrong number of arguments to function fts5_expr}}
do_catchsql_test 2.2 {
do_catchsql_test 2.1 {
SELECT fts5_expr_tcl()
} {1 {wrong number of arguments to function fts5_expr_tcl}}
do_catchsql_test 2.3 {
SELECT fts5_expr('')
} {1 {fts5: syntax error near ""}}
do_catchsql_test 2.4 {
SELECT fts5_expr(NULL)
} {1 {fts5: syntax error near ""}}
do_catchsql_test 2.5 {
SELECT fts5_expr(NULL, NULL)
} {1 {parse error in ""}}
for {set i 0} {$i < 255} {incr i} {
do_test 2.6.$i {
lindex [catchsql {sELECT fts5_expr(NULL, char($i));}] 0
} 1
}
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE e1 USING fts5(text, tokenize = 'porter unicode61');
+1 -1
View File
@@ -14,7 +14,7 @@
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultD
set testprefix fts5faultA
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
ifcapable !fts5 {
-71
View File
@@ -1,71 +0,0 @@
# 2016 February 2
#
# 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 is focused on OOM errors.
#
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultE
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
faultsim_save_and_close
do_faultsim_test 1 -prep {
faultsim_restore_and_reopen
} -body {
execsql { CREATE VIRTUAL TABLE t1 USING fts5(x, y, tokenize=trigram) }
} -test {
faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}}
}
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x, y, tokenize=trigram);
}
faultsim_save_and_close
do_faultsim_test 2 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
INSERT INTO t1 VALUES('abcdefghijklmnopqrstuvwxyz', NULL);
SELECT count(*) FROM t1 WHERE x LIKE '%mnop%' AND t1 MATCH 'jkl';
}
} -test {
faultsim_test_result {0 1} {1 {vtable constructor failed: t1}}
}
reset_db
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x, y, tokenize=trigram, detail=none);
INSERT INTO t1 VALUES('abcdefghijklmnopqrstuvwxyz', NULL);
}
faultsim_save_and_close
do_faultsim_test 3 -prep {
faultsim_restore_and_reopen
} -body {
execsql {
SELECT count(*) FROM t1 WHERE x LIKE '%mnopqrs%' AND t1 MATCH 'abc'
}
} -test {
faultsim_test_result {0 1} {1 {vtable constructor failed: t1}}
}
finish_test
+1 -1
View File
@@ -36,7 +36,7 @@ do_test 1.1 {
execsql { INSERT INTO x8 VALUES( rnddoc(5) ); }
}
} msg] $msg
} {0 {}}
} {1 {database or disk is full}}
finish_test
+1 -38
View File
@@ -112,7 +112,7 @@ foreach_detail_mode $testprefix {
# Add a small and very large token with the same hash value to an
# empty table. At one point this would provoke an asan error.
#
do_test 1.5 {
do_test 2.0 {
set big [string repeat 12345 40]
set hash [sqlite3_fts5_token_hash 1024 $big]
while {1} {
@@ -128,41 +128,4 @@ foreach_detail_mode $testprefix {
} ;# foreach_detail_mode
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.1 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(t1, rank) VALUES('hashsize', 1024);
INSERT INTO t1(t1, rank) VALUES('automerge', 0);
INSERT INTO t1(t1, rank) VALUES('crisismerge', 1000);
}
do_execsql_test 2.2 {
BEGIN;
INSERT INTO t1 VALUES('abc def ghi');
SELECT count(*) FROM t1_data;
} {2}
do_execsql_test 2.3 {
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1024
)
INSERT INTO t1 SELECT 'abc def ghi' FROM s;
SELECT (SELECT count(*) FROM t1_data) > 10;
} {1}
do_execsql_test 2.4 {
COMMIT;
DROP TABLE t1;
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(t1, rank) VALUES('hashsize', 1024);
INSERT INTO t1(t1, rank) VALUES('automerge', 0);
INSERT INTO t1(t1, rank) VALUES('crisismerge', 1000);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1024
)
INSERT INTO t1 SELECT 'abc' || i || ' def' || i || ' ghi' || i FROM s;
SELECT (SELECT count(*) FROM t1_data) > 100;
} {1}
finish_test
-107
View File
@@ -210,111 +210,4 @@ foreach {tn pgsz} {
} {1000}
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 7.0 {
PRAGMA encoding = 'UTF-16';
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
INSERT INTO vt0 VALUES (x'46f0');
SELECT quote(c0) FROM vt0;
} {X'46F0'}
do_execsql_test 7.1 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
do_execsql_test 7.2 {
INSERT INTO vt0(vt0) VALUES('rebuild');
}
do_execsql_test 7.3 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
do_execsql_test 7.4 {
UPDATE vt0 SET c0='';
}
do_execsql_test 7.5 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
# Ticket 7a458c2a5f4
#
reset_db
do_execsql_test 8.0 {
PRAGMA locking_mode = EXCLUSIVE;
PRAGMA journal_mode = PERSIST;
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
} {exclusive persist}
do_execsql_test 8.1 {
PRAGMA data_version
} {1}
do_execsql_test 8.2 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
PRAGMA data_version;
} {1}
do_execsql_test 8.1 {
INSERT INTO vt0(vt0, rank) VALUES('usermerge', 2);
}
#-------------------------------------------------------------------------
# Ticket [771fe617]
#
reset_db
do_execsql_test 9.0 {
PRAGMA encoding = 'UTF16';
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
}
#explain_i { SELECT quote(SUBSTR(x'37', 0)); }
#execsql { PRAGMA vdbe_trace = 1 }
do_execsql_test 9.1.1 {
SELECT quote(SUBSTR(x'37', 0));
} {X'37'}
do_execsql_test 9.1.2 {
SELECT quote(x'37');
} {X'37'}
do_execsql_test 9.2 {
INSERT INTO vt0 VALUES (SUBSTR(x'37', 0));
-- INSERT INTO vt0 VALUES (x'37');
}
do_execsql_test 9.3 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 10.0 {
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
CREATE VIRTUAL TABLE vt0 USING fts5(a, b, content=t1);
INSERT INTO vt0(rowid, a, b) VALUES(1, 'abc', 'def');
}
do_catchsql_test 10.1 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
} {0 {}}
do_catchsql_test 10.2 {
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
} {0 {}}
do_catchsql_test 10.3 {
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
} {1 {database disk image is malformed}}
do_catchsql_test 10.3 {
INSERT INTO t1 VALUES(1, 'abc', 'def');
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
} {0 {}}
do_execsql_test 10.4 {
CREATE VIRTUAL TABLE vt1 USING fts5(a, b, content=);
INSERT INTO vt1(rowid, a, b) VALUES(1, 'abc', 'def');
}
do_catchsql_test 10.5.1 {
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
} {0 {}}
do_catchsql_test 10.5.2 {
INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
} {0 {}}
do_catchsql_test 10.5.3 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
} {0 {}}
finish_test
-29
View File
@@ -491,33 +491,4 @@ do_catchsql_test 14.2 {
SELECT matchinfo(x1, 'd') FROM x1('a b c');
} {1 {unrecognized matchinfo flag: d}}
#-------------------------------------------------------------------------
# Test using matchinfo() and similar on a non-full-text query
#
do_execsql_test 15.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x, y);
INSERT INTO t1 VALUES('a', 'b');
INSERT INTO t1 VALUES('c', 'd');
}
if {$tcl_platform(byteOrder)=="littleEndian"} {
set res {X'02000000'}
} else {
set res {X'00000002'}
}
do_execsql_test 15.1 {
SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
} $res
do_execsql_test 15.2 {
DELETE FROM t1_content WHERE rowid=1;
SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
} $res
fts5_aux_test_functions db
do_execsql_test 15.3 {
SELECT fts5_test_all(t1) FROM t1 LIMIT 1;
} {
{columnsize {0 0} columntext {c d} columntotalsize {2 2} poslist {} tokenize {c d} rowcount 2}
}
finish_test
-148
View File
@@ -175,153 +175,5 @@ do_catchsql_test 5.3 {
INSERT INTO vt0(vt0, rank) VALUES('pgsz', '65537');
} {1 {SQL logic error}}
#-------------------------------------------------------------------------
# Ticket [d392017c].
#
reset_db
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
)
INSERT INTO vt0(c0) SELECT '0' FROM s;
INSERT INTO vt0(vt0, rank) VALUES('crisismerge', 2000);
INSERT INTO vt0(vt0, rank) VALUES('automerge', 0);
} {}
do_execsql_test 6.1 {
INSERT INTO vt0(vt0) VALUES('rebuild');
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 7.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(rowid, x) VALUES(1, 'hello world');
INSERT INTO t1(rowid, x) VALUES(2, 'well said');
INSERT INTO t1(rowid, x) VALUES(3, 'hello said');
INSERT INTO t1(rowid, x) VALUES(4, 'well world');
CREATE TABLE t2 (a, b);
INSERT INTO t2 VALUES(1, 'hello');
INSERT INTO t2 VALUES(2, 'world');
INSERT INTO t2 VALUES(3, 'said');
INSERT INTO t2 VALUES(4, 'hello');
}
do_execsql_test 7.1 {
SELECT rowid FROM t1 WHERE (rowid, x) IN (SELECT a, b FROM t2);
}
do_execsql_test 7.2 {
SELECT rowid FROM t1 WHERE rowid=2 AND t1 = 'hello';
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 8.0 {
CREATE VIRTUAL TABLE vt0 USING fts5(c0, tokenize = "ascii", prefix = 1);
INSERT INTO vt0(c0) VALUES (x'd1');
}
do_execsql_test 8.1 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 9.0 {
CREATE VIRTUAL TABLE t1 using FTS5(mailcontent);
insert into t1(rowid, mailcontent) values
(-4764623217061966105, 'we are going to upgrade'),
(8324454597464624651, 'we are going to upgrade');
}
do_execsql_test 9.1 {
INSERT INTO t1(t1) VALUES('integrity-check');
}
do_execsql_test 9.2 {
SELECT rowid FROM t1('upgrade');
} {
-4764623217061966105 8324454597464624651
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 10.0 {
CREATE VIRTUAL TABLE vt1 USING fts5(c1, c2, prefix = 1, tokenize = "ascii");
INSERT INTO vt1 VALUES (x'e4', '䔬');
}
do_execsql_test 10.1 {
SELECT quote(CAST(c1 AS blob)), quote(CAST(c2 AS blob)) FROM vt1
} {X'E4' X'E494AC'}
do_execsql_test 10.2 {
INSERT INTO vt1(vt1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 11.0 {
CREATE VIRTUAL TABLE vt0 USING fts5(
c0, prefix = 71, tokenize = "porter ascii", prefix = 9
);
} {}
do_execsql_test 11.1 {
BEGIN;
INSERT INTO vt0(c0) VALUES (x'e8');
}
do_execsql_test 11.2 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
# Ticket [752fdbf6]
#
reset_db
do_execsql_test 11.0 {
PRAGMA encoding = 'UTF-16';
CREATE VIRTUAL TABLE vt0 USING fts5(c0, c1);
INSERT INTO vt0(vt0, rank) VALUES('pgsz', '37');
INSERT INTO vt0(c0, c1) VALUES (0.66077, 1957391816);
}
do_execsql_test 11.1 {
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
# Ticket [7c0e06b16]
#
do_execsql_test 12.0 {
CREATE TABLE t1(a, b, rank);
INSERT INTO t1 VALUES('a', 'hello', '');
INSERT INTO t1 VALUES('b', 'world', '');
CREATE VIRTUAL TABLE ft USING fts5(a);
INSERT INTO ft VALUES('b');
INSERT INTO ft VALUES('y');
CREATE TABLE t2(x, y, ft);
INSERT INTO t2 VALUES(1, 2, 'x');
INSERT INTO t2 VALUES(3, 4, 'b');
}
do_execsql_test 12.1 {
SELECT * FROM t1 NATURAL JOIN ft WHERE ft MATCH('b')
} {b world {}}
do_execsql_test 12.2 {
SELECT * FROM ft NATURAL JOIN t1 WHERE ft MATCH('b')
} {b world {}}
do_execsql_test 12.3 {
SELECT * FROM t2 JOIN ft USING (ft)
} {3 4 b b}
finish_test
+2 -2
View File
@@ -31,7 +31,7 @@ do_eqp_test 1.1 {
} {
QUERY PLAN
|--SCAN TABLE t1
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:M1
`--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
}
do_eqp_test 1.2 {
@@ -46,7 +46,7 @@ do_eqp_test 1.3 {
SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff
} {
QUERY PLAN
|--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:M1
|--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:m
`--USE TEMP B-TREE FOR ORDER BY
}
-57
View File
@@ -1,57 +0,0 @@
# 2020 Dec 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 prefix indexes.
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5prefix2
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
foreach p {3 2 1} {
reset_db
do_execsql_test 1.$p.0 "
CREATE VIRTUAL TABLE t1 USING fts5(xyz, prefix=$p);
"
do_execsql_test 1.$p.1 {
INSERT INTO t1 VALUES
('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 f.');
}
do_execsql_test 1.$p.2 {
SELECT highlight(t1, 0, '[', ']') FROM t1('f*');
} {
{May you [find] [forgiveness] [for] yourself and [forgive] others.}
{May you share [freely], never taking more than you give [f].}
}
}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(one, prefix=3);
INSERT INTO t2 VALUES('top');
INSERT INTO t2 VALUES('to');
INSERT INTO t2 VALUES('tommy');
}
do_execsql_test 2.1 {
SELECT * FROM t2('to*');
} {top to tommy}
finish_test
-85
View File
@@ -1,85 +0,0 @@
# 2019 Dec 26
#
# 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 fts5savepoint
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft USING fts5(c);
BEGIN;
SAVEPOINT one;
INSERT INTO ft VALUES('a');
SAVEPOINT two;
INSERT INTO ft VALUES('b');
RELEASE two;
SAVEPOINT four;
INSERT INTO ft VALUES('c');
RELEASE four;
SAVEPOINT three;
INSERT INTO ft VALUES('d');
ROLLBACK TO three;
COMMIT;
SELECT * FROM ft
} {a b c}
reset_db
do_catchsql_test 2.0 {
CREATE VIRTUAL TABLE ft1 USING fts5(c);
CREATE VIRTUAL TABLE ft2 USING fts5(c);
DROP TABLE ft2_idx;
BEGIN;
INSERT INTO ft2 VALUES('a');
INSERT INTO ft1 VALUES('a');
SAVEPOINT two;
INSERT INTO ft1 VALUES('b');
COMMIT;
} {1 {SQL logic error}}
reset_db
ifcapable fts3 {
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE vt0 USING fts5(c0);
CREATE VIRTUAL TABLE vt1 USING fts4(c0);
INSERT INTO vt1(c0) VALUES(0);
}
do_execsql_test 3.1 {
BEGIN;
UPDATE vt1 SET c0 = 0;
INSERT INTO vt1(c0) VALUES (0), (0);
UPDATE vt0 SET c0 = 0;
INSERT INTO vt1(c0) VALUES (0);
UPDATE vt1 SET c0 = 0;
INSERT INTO vt1(vt1) VALUES('automerge=1');
UPDATE vt1 SET c0 = 0;
}
do_catchsql_test 3.2 {
DROP TABLE vt1;
} {1 {SQL logic error}}
do_execsql_test 3.3 {
SAVEPOINT x;
INSERT INTO vt0 VALUES('x');
COMMIT;
INSERT INTO vt0(vt0) VALUES('integrity-check');
}
}
finish_test
-35
View File
@@ -111,40 +111,5 @@ do_catchsql_test 2.1 {
SELECT * FROM t4;
} {1 {SQL logic error}}
#-------------------------------------------------------------------------
# Embedded 0x00 characters.
#
reset_db
do_execsql_test 3.1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(z);
CREATE VIRTUAL TABLE tt USING fts5vocab(t1, 'instance');
INSERT INTO t1 VALUES('abc' || char(0) || 'def');
SELECT * FROM tt;
} { abc 1 z 0 def 1 z 1 }
do_execsql_test 3.1.1 {
SELECT hex(z) FROM t1;
} {61626300646566}
do_execsql_test 3.1.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
} {}
do_execsql_test 3.2.0 {
CREATE VIRTUAL TABLE t2 USING fts5(z,
tokenize="unicode61 categories 'L* N* Co Cc'"
);
CREATE VIRTUAL TABLE tu USING fts5vocab(t2, 'instance');
INSERT INTO t2 VALUES('abc' || char(0) || 'def');
SELECT * FROM tu;
} { abc 1 z 0 def 1 z 1 }
do_execsql_test 3.2.1 {
SELECT hex(z) FROM t1;
} {61626300646566}
do_execsql_test 3.2.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
} {}
finish_test
-201
View File
@@ -1,201 +0,0 @@
# 2020 September 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.
#
#*************************************************************************
#
# Tests for the fts5 "trigram" tokenizer.
#
source [file join [file dirname [info script]] fts5_common.tcl]
ifcapable !fts5 { finish_test ; return }
set ::testprefix fts5trigram
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize=trigram);
INSERT INTO t1 VALUES('abcdefghijklm');
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
}
foreach {tn s res} {
1 abc "(abc)defghijklm"
2 defgh "abc(defgh)ijklm"
3 abcdefghijklm "(abcdefghijklm)"
4 กรุ "(กรุ)งเทพมหานคร"
5 งเทพมห "กรุ(งเทพมห)านคร"
6 กรุงเทพมหานคร "(กรุงเทพมหานคร)"
7 Abc "(abc)defghijklm"
8 deFgh "abc(defgh)ijklm"
9 aBcdefGhijKlm "(abcdefghijklm)"
} {
do_execsql_test 1.1.$tn {
SELECT highlight(t1, 0, '(', ')') FROM t1($s)
} $res
}
do_execsql_test 1.2.0 {
SELECT fts5_expr('ABCD', 'tokenize=trigram')
} {{"abc" + "bcd"}}
do_execsql_test 1.2.1 {
SELECT * FROM t1 WHERE y LIKE ? ESCAPE 'a'
}
foreach {tn like res} {
1 {%cDef%} 1
2 {cDef%} {}
3 {%f%} 1
4 {%f_h%} 1
5 {%f_g%} {}
6 {abc%klm} 1
7 {ABCDEFG%} 1
8 {%รุงเ%} 2
} {
do_execsql_test 1.3.$tn {
SELECT rowid FROM t1 WHERE y LIKE $like
} $res
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize="trigram case_sensitive 1");
INSERT INTO t1 VALUES('abcdefghijklm');
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
}
foreach {tn s res} {
1 abc "(abc)defghijklm"
2 defgh "abc(defgh)ijklm"
3 abcdefghijklm "(abcdefghijklm)"
4 กรุ "(กรุ)งเทพมหานคร"
5 งเทพมห "กรุ(งเทพมห)านคร"
6 กรุงเทพมหานคร "(กรุงเทพมหานคร)"
7 Abc ""
8 deFgh ""
9 aBcdefGhijKlm ""
} {
do_execsql_test 2.1.$tn {
SELECT highlight(t1, 0, '(', ')') FROM t1($s)
} $res
}
foreach {tn like res} {
1 {%cDef%} 1
2 {cDef%} {}
3 {%f%} 1
4 {%f_h%} 1
5 {%f_g%} {}
6 {abc%klm} 1
7 {ABCDEFG%} 1
8 {%รุงเ%} 2
} {
do_execsql_test 2.2.$tn {
SELECT rowid FROM t1 WHERE y LIKE $like
} $res
}
foreach {tn like res} {
1 {*cdef*} 1
2 {cdef*} {}
3 {*f*} 1
4 {*f?h*} 1
5 {*f?g*} {}
6 {abc*klm} 1
7 {abcdefg*} 1
8 {*รุงเ*} 2
9 {abc[d]efg*} 1
10 {abc[]d]efg*} 1
11 {abc[^]d]efg*} {}
12 {abc[^]XYZ]efg*} 1
} {
do_execsql_test 2.3.$tn {
SELECT rowid FROM t1 WHERE y GLOB $like
} $res
}
do_execsql_test 2.3.null.1 {
SELECT rowid FROM t1 WHERE y LIKE NULL
}
#-------------------------------------------------------------------------
reset_db
do_catchsql_test 3.1 {
CREATE VIRTUAL TABLE ttt USING fts5(c, tokenize="trigram case_sensitive 2");
} {1 {error in tokenizer constructor}}
do_catchsql_test 3.2 {
CREATE VIRTUAL TABLE ttt USING fts5(c, tokenize="trigram case_sensitive 11");
} {1 {error in tokenizer constructor}}
do_catchsql_test 3.3 {
CREATE VIRTUAL TABLE ttt USING fts5(c, "tokenize=trigram case_sensitive 1");
} {0 {}}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t0 USING fts5(b, tokenize = "trigram");
}
do_execsql_test 4.1 {
INSERT INTO t0 VALUES (x'000b01');
}
do_execsql_test 4.2 {
INSERT INTO t0(t0) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
foreach_detail_mode $::testprefix {
foreach {ci} {0 1} {
reset_db
do_execsql_test 5.cs=$ci.0.1 "
CREATE VIRTUAL TABLE t1 USING fts5(
y, tokenize=\"trigram case_sensitive $ci\", detail=%DETAIL%
);
"
do_execsql_test 5.cs=$ci.0.2 {
INSERT INTO t1 VALUES('abcdefghijklm');
INSERT INTO t1 VALUES('กรุงเทพมหานคร');
}
foreach {tn like res} {
1 {%cDef%} 1
2 {cDef%} {}
3 {%f%} 1
4 {%f_h%} 1
5 {%f_g%} {}
6 {abc%klm} 1
7 {ABCDEFG%} 1
8 {%รุงเ%} 2
} {
do_execsql_test 5.cs=$ci.1.$tn {
SELECT rowid FROM t1 WHERE y LIKE $like
} $res
}
}
}
do_execsql_test 6.0 {
CREATE VIRTUAL TABLE ci0 USING fts5(x, tokenize="trigram");
CREATE VIRTUAL TABLE ci1 USING fts5(x, tokenize="trigram case_sensitive 1");
}
# LIKE and GLOB both work with case-insensitive tokenizers. Only GLOB works
# with case-sensitive.
do_eqp_test 6.1 {
SELECT * FROM ci0 WHERE x LIKE ?
} {VIRTUAL TABLE INDEX 0:L0}
do_eqp_test 6.2 {
SELECT * FROM ci0 WHERE x GLOB ?
} {VIRTUAL TABLE INDEX 0:G0}
do_eqp_test 6.3 {
SELECT * FROM ci1 WHERE x LIKE ?
} {{SCAN TABLE ci1 VIRTUAL TABLE INDEX 0:}}
do_eqp_test 6.4 {
SELECT * FROM ci1 WHERE x GLOB ?
} {VIRTUAL TABLE INDEX 0:G0}
finish_test
+1 -12
View File
@@ -542,16 +542,5 @@ do_execsql_test 10.7.3 {
SELECT * FROM t2 WHERE term=?;
}
# 2020-02-16 Detect recursively define fts5vocab() tables.
# Error found by dbsqlfuzz.
#
reset_db
do_execsql_test 11.100 {
CREATE VIRTUAL TABLE t3 USING fts5vocab(rowid , 'col');
CREATE VIRTUAL TABLE rowid USING fts5vocab(rowid , 'instance');
} {}
do_catchsql_test 11.110 {
SELECT rowid+1,rowid, * FROM t3 WHERE null>rowid ;
} {1 {SQL logic error}}
finish_test
-2
View File
@@ -12,7 +12,6 @@ proc process_cmdline {} {
cmdline::process ::A $::argv {
{fts5 "use fts5 (this is the default)"}
{fts4 "use fts4"}
{trigram "Use tokenize=trigram"}
{colsize "10 10 10" "list of column sizes"}
{tblname "t1" "table name to create"}
{detail "full" "Fts5 detail mode to use"}
@@ -176,7 +175,6 @@ proc create_table {} {
set sql "CREATE VIRTUAL TABLE IF NOT EXISTS $A(tblname) USING $A(fts) ("
append sql [join $cols ,]
if {$A(fts)=="fts5"} { append sql ",detail=$A(detail)" }
if {$A(trigram)} { append sql ",tokenize=trigram" }
append sql ", prefix='$A(prefix)');"
db eval $sql
+1 -2
View File
@@ -116,8 +116,7 @@ SQLite. Documentation follows.
and use it as a dynamically loadable SQLite extension. To do this
using gcc on *nix:
gcc -fPIC -shared icu.c `pkg-config --libs --cflags icu-uc icu-io` \
-o libSqliteIcu.so
gcc -shared icu.c `icu-config --ldflags` -o libSqliteIcu.so
You may need to add "-I" flags so that gcc can find sqlite3ext.h
and sqlite3.h. The resulting shared lib, libSqliteIcu.so, may be
+16 -17
View File
@@ -143,7 +143,7 @@ static int icuLikeCompare(
** 3. uPattern is an unescaped escape character, or
** 4. uPattern is to be handled as an ordinary character
*/
if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
if( !prevEscape && uPattern==MATCH_ALL ){
/* Case 1. */
uint8_t c;
@@ -169,12 +169,12 @@ static int icuLikeCompare(
}
return 0;
}else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
}else if( !prevEscape && uPattern==MATCH_ONE ){
/* Case 2. */
if( *zString==0 ) return 0;
SQLITE_ICU_SKIP_UTF8(zString);
}else if( uPattern==(uint32_t)uEsc && !prevEscape ){
}else if( !prevEscape && uPattern==(uint32_t)uEsc){
/* Case 3. */
prevEscape = 1;
@@ -499,27 +499,26 @@ static void icuLoadCollation(
** Register the ICU extension functions with database db.
*/
int sqlite3IcuInit(sqlite3 *db){
# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
static const struct IcuScalar {
const char *zName; /* Function name */
unsigned char nArg; /* Number of arguments */
unsigned int enc; /* Optimal text encoding */
unsigned short enc; /* Optimal text encoding */
unsigned char iContext; /* sqlite3_user_data() context */
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} scalars[] = {
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
{"icu_load_collation", 2, SQLITE_UTF8, 1, icuLoadCollation},
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
{"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
{"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
{"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
{"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
{"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
{"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
{"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
{"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
{"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
{"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
{"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
{"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
};
int rc = SQLITE_OK;
+1
View File
@@ -24,3 +24,4 @@ int sqlite3IcuInit(sqlite3 *db);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
+2
View File
@@ -654,3 +654,5 @@ void test_data_3(
testFree(zName);
}
}
+2
View File
@@ -322,3 +322,5 @@ void do_writer_crash_test(const char *zPattern, int *pRc){
}
}
+3
View File
@@ -138,3 +138,6 @@ void test_data_4(
testFree(zName);
}
}
+4
View File
@@ -69,3 +69,7 @@ int do_bt(int nArg, char **azArg){
sqlite4_buffer_clear(&buf.output);
return 0;
}
+1 -1
View File
@@ -553,7 +553,7 @@ static int sql_begin(TestDb *pTestDb, int iLevel){
/* If there are no transactions at all open, open a read transaction. */
if( pDb->nOpenTrans==0 ){
int rc = sqlite3_exec(pDb->db,
"BEGIN; SELECT * FROM sqlite_schema LIMIT 1;" , 0, 0, 0
"BEGIN; SELECT * FROM sqlite_master LIMIT 1;" , 0, 0, 0
);
if( rc!=0 ) return rc;
pDb->nOpenTrans = 1;
+1
View File
@@ -367,3 +367,4 @@ int test_mdb_scan(
}
#endif /* HAVE_MDB */
+2
View File
@@ -978,3 +978,5 @@ static int bgc_detach(BtDb *pDb){
/*
** End of background checkpointer.
*************************************************************************/
+1 -8
View File
@@ -228,10 +228,6 @@ static int lsmPosixOsRemap(
}
p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
if( p->pMap==MAP_FAILED ){
p->pMap = 0;
return LSM_IOERR_BKPT;
}
p->nMap = iSz;
}
@@ -417,10 +413,7 @@ static int lsmPosixOsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
p->apShm[iChunk] = mmap(0, LSM_SHM_CHUNK_SIZE,
PROT_READ|PROT_WRITE, MAP_SHARED, p->shmfd, iChunk*LSM_SHM_CHUNK_SIZE
);
if( p->apShm[iChunk]==MAP_FAILED ){
p->apShm[iChunk] = 0;
return LSM_IOERR_BKPT;
}
if( p->apShm[iChunk]==0 ) return LSM_IOERR_BKPT;
}
*ppShm = p->apShm[iChunk];
-1
View File
@@ -900,7 +900,6 @@ static int amatchConnect(
rc = amatchLoadRules(db, pNew, pzErr);
}
if( rc==SQLITE_OK ){
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(word,distance,language,"
"command HIDDEN,nword HIDDEN)"
+116 -220
View File
@@ -14,23 +14,24 @@
** appended onto the end of some other file, such as an executable.
**
** A special record must appear at the end of the file that identifies the
** file as an appended database and provides the offset to the first page
** of the exposed content. (Or, it is the length of the content prefix.)
** For best performance page 1 should be located at a disk page boundary,
** though that is not required.
** file as an appended database and provides an offset to page 1. For
** best performance page 1 should be located at a disk page boundary, though
** that is not required.
**
** When opening a database using this VFS, the connection might treat
** the file as an ordinary SQLite database, or it might treat it as a
** database appended onto some other file. The decision is made by
** applying the following rules in order:
** the file as an ordinary SQLite database, or it might treat is as a
** database appended onto some other file. Here are the rules:
**
** (1) An empty file is an ordinary database.
** (1) When opening a new empty file, that file is treated as an ordinary
** database.
**
** (2) If the file ends with the appendvfs trailer string
** "Start-Of-SQLite3-NNNNNNNN" that file is an appended database.
** (2) When opening a file that begins with the standard SQLite prefix
** string "SQLite format 3", that file is treated as an ordinary
** database.
**
** (3) If the file begins with the standard SQLite prefix string
** "SQLite format 3", that file is an ordinary database.
** (3) When opening a file that ends with the appendvfs trailer string
** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended
** database.
**
** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
** set, then a new database is appended to the already existing file.
@@ -38,13 +39,13 @@
** (5) Otherwise, SQLITE_CANTOPEN is returned.
**
** To avoid unnecessary complications with the PENDING_BYTE, the size of
** the file containing the database is limited to 1GiB. (1073741824 bytes)
** This VFS will not read or write past the 1GiB mark. This restriction
** might be lifted in future versions. For now, if you need a larger
** database, then keep it in a separate file.
** the file containing the database is limited to 1GB. This VFS will refuse
** to read or write past the 1GB mark. This restriction might be lifted in
** future versions. For now, if you need a large database, then keep the
** database in a separate file.
**
** If the file being opened is a plain database (not an appended one), then
** this shim is a pass-through into the default underlying VFS. (rule 3)
** If the file being opened is not an appended database, then this shim is
** a pass-through into the default underlying VFS.
**/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -57,27 +58,17 @@ SQLITE_EXTENSION_INIT1
** 123456789 123456789 12345
**
** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
** the offset to page 1, and also the length of the prefix content.
** the offset to page 1.
*/
#define APND_MARK_PREFIX "Start-Of-SQLite3-"
#define APND_MARK_PREFIX_SZ 17
#define APND_MARK_FOS_SZ 8
#define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
#define APND_MARK_SIZE 25
/*
** Maximum size of the combined prefix + database + append-mark. This
** must be less than 0x40000000 to avoid locking issues on Windows.
*/
#define APND_MAX_SIZE (0x40000000)
/*
** Try to align the database to an even multiple of APND_ROUNDUP bytes.
*/
#ifndef APND_ROUNDUP
#define APND_ROUNDUP 4096
#endif
#define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1))
#define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
#define APND_MAX_SIZE (65536*15259)
/*
** Forward declaration of objects used by this utility
@@ -91,45 +82,11 @@ typedef struct ApndFile ApndFile;
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
/* An open appendvfs file
**
** An instance of this structure describes the appended database file.
** A separate sqlite3_file object is always appended. The appended
** sqlite3_file object (which can be accessed using ORIGFILE()) describes
** the entire file, including the prefix, the database, and the
** append-mark.
**
** The structure of an AppendVFS database is like this:
**
** +-------------+---------+----------+-------------+
** | prefix-file | padding | database | append-mark |
** +-------------+---------+----------+-------------+
** ^ ^
** | |
** iPgOne iMark
**
**
** "prefix file" - file onto which the database has been appended.
** "padding" - zero or more bytes inserted so that "database"
** starts on an APND_ROUNDUP boundary
** "database" - The SQLite database file
** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates
** the offset from the start of prefix-file to the start
** of "database".
**
** The size of the database is iMark - iPgOne.
**
** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value
** of iPgOne stored as a big-ending 64-bit integer.
**
** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE).
** Or, iMark is -1 to indicate that it has not yet been written.
*/
/* An open file */
struct ApndFile {
sqlite3_file base; /* Subclass. MUST BE FIRST! */
sqlite3_int64 iPgOne; /* Offset to the start of the database */
sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */
/* Always followed by another sqlite3_file that describes the whole file */
sqlite3_file base; /* IO methods */
sqlite3_int64 iPgOne; /* File offset to page 1 */
sqlite3_int64 iMark; /* Start of the append-mark */
};
/*
@@ -221,6 +178,8 @@ static const sqlite3_io_methods apnd_io_methods = {
apndUnfetch /* xUnfetch */
};
/*
** Close an apnd-file.
*/
@@ -238,37 +197,22 @@ static int apndRead(
int iAmt,
sqlite_int64 iOfst
){
ApndFile *paf = (ApndFile *)pFile;
ApndFile *p = (ApndFile *)pFile;
pFile = ORIGFILE(pFile);
return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne);
}
/*
** Add the append-mark onto what should become the end of the file.
* If and only if this succeeds, internal ApndFile.iMark is updated.
* Parameter iWriteEnd is the appendvfs-relative offset of the new mark.
** Add the append-mark onto the end of the file.
*/
static int apndWriteMark(
ApndFile *paf,
sqlite3_file *pFile,
sqlite_int64 iWriteEnd
){
sqlite_int64 iPgOne = paf->iPgOne;
static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){
int i;
unsigned char a[APND_MARK_SIZE];
int i = APND_MARK_FOS_SZ;
int rc;
assert(pFile == ORIGFILE(paf));
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
while( --i >= 0 ){
a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
iPgOne >>= 8;
for(i=0; i<8; i++){
a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff;
}
iWriteEnd += paf->iPgOne;
if( SQLITE_OK==(rc = pFile->pMethods->xWrite
(pFile, a, APND_MARK_SIZE, iWriteEnd)) ){
paf->iMark = iWriteEnd;
}
return rc;
return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark);
}
/*
@@ -280,28 +224,38 @@ static int apndWrite(
int iAmt,
sqlite_int64 iOfst
){
ApndFile *paf = (ApndFile *)pFile;
sqlite_int64 iWriteEnd = iOfst + iAmt;
if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
int rc;
ApndFile *p = (ApndFile *)pFile;
pFile = ORIGFILE(pFile);
/* If append-mark is absent or will be overwritten, write it. */
if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
int rc = apndWriteMark(paf, pFile, iWriteEnd);
if( SQLITE_OK!=rc ) return rc;
if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL;
rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne);
if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){
sqlite3_int64 sz = 0;
rc = pFile->pMethods->xFileSize(pFile, &sz);
if( rc==SQLITE_OK ){
p->iMark = sz - APND_MARK_SIZE;
if( iOfst + iAmt + p->iPgOne > p->iMark ){
p->iMark = p->iPgOne + iOfst + iAmt;
rc = apndWriteMark(p, pFile);
}
}
}
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
return rc;
}
/*
** Truncate an apnd-file.
*/
static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
ApndFile *paf = (ApndFile *)pFile;
int rc;
ApndFile *p = (ApndFile *)pFile;
pFile = ORIGFILE(pFile);
/* The append mark goes out first so truncate failure does not lose it. */
if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR;
/* Truncate underlying file just past append mark */
return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE);
if( rc==SQLITE_OK ){
p->iMark = p->iPgOne+size;
rc = apndWriteMark(p, pFile);
}
return rc;
}
/*
@@ -314,12 +268,16 @@ static int apndSync(sqlite3_file *pFile, int flags){
/*
** Return the current file-size of an apnd-file.
** If the append mark is not yet there, the file-size is 0.
*/
static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
ApndFile *paf = (ApndFile *)pFile;
*pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0;
return SQLITE_OK;
ApndFile *p = (ApndFile *)pFile;
int rc;
pFile = ORIGFILE(p);
rc = pFile->pMethods->xFileSize(pFile, pSize);
if( rc==SQLITE_OK && p->iPgOne ){
*pSize -= p->iPgOne + APND_MARK_SIZE;
}
return rc;
}
/*
@@ -350,13 +308,12 @@ static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){
** File control method. For custom operations on an apnd-file.
*/
static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
ApndFile *paf = (ApndFile *)pFile;
ApndFile *p = (ApndFile *)pFile;
int rc;
pFile = ORIGFILE(pFile);
if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne;
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
*(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", paf->iPgOne,*(char**)pArg);
*(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->iPgOne, *(char**)pArg);
}
return rc;
}
@@ -415,9 +372,6 @@ static int apndFetch(
void **pp
){
ApndFile *p = (ApndFile *)pFile;
if( p->iMark < 0 || iOfst+iAmt > p->iMark ){
return SQLITE_IOERR; /* Cannot read what is not yet there. */
}
pFile = ORIGFILE(pFile);
return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
}
@@ -429,153 +383,95 @@ static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
}
/*
** Check to see if the file is an ordinary SQLite database file.
*/
static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
int rc;
char zHdr[16];
static const char aSqliteHdr[] = "SQLite format 3";
if( sz<512 ) return 0;
rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0);
if( rc ) return 0;
return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0;
}
/*
** Try to read the append-mark off the end of a file. Return the
** start of the appended database if the append-mark is present.
** If there is no valid append-mark, return -1;
**
** An append-mark is only valid if the NNNNNNNN start-of-database offset
** indicates that the appended database contains at least one page. The
** start-of-database value must be a multiple of 512.
** start of the appended database if the append-mark is present. If
** there is no append-mark, return -1;
*/
static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
int rc, i;
sqlite3_int64 iMark;
int msbs = 8 * (APND_MARK_FOS_SZ-1);
unsigned char a[APND_MARK_SIZE];
if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1;
if( sz<=APND_MARK_SIZE ) return -1;
rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
if( rc ) return -1;
if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0x7f)) << msbs;
for(i=1; i<8; i++){
msbs -= 8;
iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs;
iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ]&0x7f))<<56;
for(i=1; i<8; i++){
iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i);
}
if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1;
if( iMark & 0x1ff ) return -1;
return iMark;
}
static const char apvfsSqliteHdr[] = "SQLite format 3";
/*
** Check to see if the file is an appendvfs SQLite database file.
** Return true iff it is such. Parameter sz is the file's size.
*/
static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
int rc;
char zHdr[16];
sqlite3_int64 iMark = apndReadMark(sz, pFile);
if( iMark>=0 ){
/* If file has the correct end-marker, the expected odd size, and the
** SQLite DB type marker where the end-marker puts it, then it
** is an appendvfs database.
*/
rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
if( SQLITE_OK==rc
&& memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
&& (sz & 0x1ff) == APND_MARK_SIZE
&& sz>=512+APND_MARK_SIZE
){
return 1; /* It's an appendvfs database */
}
}
return 0;
}
/*
** Check to see if the file is an ordinary SQLite database file.
** Return true iff so. Parameter sz is the file's size.
*/
static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
char zHdr[16];
if( apndIsAppendvfsDatabase(sz, pFile) /* rule 2 */
|| (sz & 0x1ff) != 0
|| SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0)
|| memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))!=0
){
return 0;
}else{
return 1;
}
}
/*
** Open an apnd file handle.
*/
static int apndOpen(
sqlite3_vfs *pApndVfs,
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
ApndFile *pApndFile = (ApndFile*)pFile;
sqlite3_file *pBaseFile = ORIGFILE(pFile);
sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
ApndFile *p;
sqlite3_file *pSubFile;
sqlite3_vfs *pSubVfs;
int rc;
sqlite3_int64 sz = 0;
sqlite3_int64 sz;
pSubVfs = ORIGVFS(pVfs);
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
/* The appendvfs is not to be used for transient or temporary databases.
** Just use the base VFS open to initialize the given file object and
** open the underlying file. (Appendvfs is then unused for this file.)
*/
return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
}
memset(pApndFile, 0, sizeof(ApndFile));
pFile->pMethods = &apnd_io_methods;
pApndFile->iMark = -1; /* Append mark not yet written */
rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
if( rc==SQLITE_OK ){
rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
}
p = (ApndFile*)pFile;
memset(p, 0, sizeof(*p));
pSubFile = ORIGFILE(pFile);
p->base.pMethods = &apnd_io_methods;
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
if( rc ) goto apnd_open_done;
rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
if( rc ){
pBaseFile->pMethods->xClose(pBaseFile);
pFile->pMethods = 0;
return rc;
pSubFile->pMethods->xClose(pSubFile);
goto apnd_open_done;
}
if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
/* The file being opened appears to be just an ordinary DB. Copy
** the base dispatch-table so this instance mimics the base VFS.
*/
memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
memmove(pFile, pSubFile, pSubVfs->szOsFile);
return SQLITE_OK;
}
pApndFile->iPgOne = apndReadMark(sz, pFile);
if( pApndFile->iPgOne>=0 ){
pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */
p->iMark = 0;
p->iPgOne = apndReadMark(sz, pFile);
if( p->iPgOne>0 ){
return SQLITE_OK;
}
if( (flags & SQLITE_OPEN_CREATE)==0 ){
pBaseFile->pMethods->xClose(pBaseFile);
pSubFile->pMethods->xClose(pSubFile);
rc = SQLITE_CANTOPEN;
pFile->pMethods = 0;
}else{
/* Round newly added appendvfs location to #define'd page boundary.
** Note that nothing has yet been written to the underlying file.
** The append mark will be written along with first content write.
** Until then, paf->iMark value indicates it is not yet written.
*/
pApndFile->iPgOne = APND_START_ROUNDUP(sz);
}
p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff;
apnd_open_done:
if( rc ) pFile->pMethods = 0;
return rc;
}
/*
** Delete an apnd file.
** For an appendvfs, this could mean delete the appendvfs portion,
** leaving the appendee as it was before it gained an appendvfs.
** For now, this code deletes the underlying file too.
*/
static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
}
/*
** All other VFS methods are pass-thrus.
*/
static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
}
static int apndAccess(
sqlite3_vfs *pVfs,
const char *zPath,
+7 -7
View File
@@ -21,18 +21,18 @@
** name TEXT, -- Name of table or index for this btree.
** tbl_name TEXT, -- Associated table
** rootpage INT, -- The root page of the btree
** sql TEXT, -- SQL for this btree - from sqlite_schema
** sql TEXT, -- SQL for this btree - from sqlite_master
** hasRowid BOOLEAN, -- True if the btree has a rowid
** nEntry INT, -- Estimated number of entries
** nEntry INT, -- Estimated number of enteries
** nPage INT, -- Estimated number of pages
** depth INT, -- Depth of the btree
** szPage INT, -- Size of each page in bytes
** zSchema TEXT HIDDEN -- The schema to which this btree belongs
** );
**
** The first 5 fields are taken directly from the sqlite_schema table.
** The first 5 fields are taken directly from the sqlite_master table.
** Considering only the first 5 fields, the only difference between
** this virtual table and the sqlite_schema table is that this virtual
** this virtual table and the sqlite_master table is that this virtual
** table omits all entries that have a 0 or NULL rowid - in other words
** it omits triggers and views.
**
@@ -88,7 +88,7 @@ typedef struct BinfoCursor BinfoCursor;
/* A cursor for the sqlite_btreeinfo table */
struct BinfoCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
sqlite3_stmt *pStmt; /* Query against sqlite_schema */
sqlite3_stmt *pStmt; /* Query against sqlite_master */
int rc; /* Result of previous sqlite_step() call */
int hasRowid; /* hasRowid value. Negative if unknown. */
sqlite3_int64 nEntry; /* nEntry value */
@@ -242,10 +242,10 @@ static int binfoFilter(
pCsr->zSchema = sqlite3_mprintf("main");
}
zSql = sqlite3_mprintf(
"SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
"SELECT 0, 'table','sqlite_master','sqlite_master',1,NULL "
"UNION ALL "
"SELECT rowid, type, name, tbl_name, rootpage, sql"
" FROM \"%w\".sqlite_schema WHERE rootpage>=1",
" FROM \"%w\".sqlite_master WHERE rootpage>=1",
pCsr->zSchema);
sqlite3_finalize(pCsr->pStmt);
pCsr->pStmt = 0;
+38 -153
View File
@@ -57,31 +57,20 @@ SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
** Must exactly match the definitions in carray.h.
*/
#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
#define CARRAY_DOUBLE 2 /* Data is doubles */
#define CARRAY_TEXT 3 /* Data is char* */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Names of allowed datatypes
** Allowed datatypes
*/
static const char *azType[] = { "int32", "int64", "double", "char*" };
#define CARRAY_INT32 0
#define CARRAY_INT64 1
#define CARRAY_DOUBLE 2
#define CARRAY_TEXT 3
/*
** Structure used to hold the sqlite3_carray_bind() information
** Names of types
*/
typedef struct carray_bind carray_bind;
struct carray_bind {
void *aData; /* The data */
int nData; /* Number of elements */
int mFlags; /* Control flags */
void (*xDel)(void*); /* Destructor for aData */
};
static const char *azType[] = { "int32", "int64", "double", "char*" };
/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
@@ -250,39 +239,28 @@ static int carrayFilter(
int argc, sqlite3_value **argv
){
carray_cursor *pCur = (carray_cursor *)pVtabCursor;
pCur->pPtr = 0;
pCur->iCnt = 0;
switch( idxNum ){
case 1: {
carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
if( pBind==0 ) break;
pCur->pPtr = pBind->aData;
pCur->iCnt = pBind->nData;
pCur->eType = pBind->mFlags & 0x03;
break;
}
case 2:
case 3: {
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
if( idxNum<3 ){
pCur->eType = CARRAY_INT32;
}else{
unsigned char i;
const char *zType = (const char*)sqlite3_value_text(argv[2]);
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
}
if( i>=sizeof(azType)/sizeof(azType[0]) ){
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
"unknown datatype: %Q", zType);
return SQLITE_ERROR;
}else{
pCur->eType = i;
}
if( idxNum ){
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
if( idxNum<3 ){
pCur->eType = CARRAY_INT32;
}else{
unsigned char i;
const char *zType = (const char*)sqlite3_value_text(argv[2]);
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
}
if( i>=sizeof(azType)/sizeof(azType[0]) ){
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
"unknown datatype: %Q", zType);
return SQLITE_ERROR;
}else{
pCur->eType = i;
}
break;
}
}else{
pCur->pPtr = 0;
pCur->iCnt = 0;
}
pCur->iRowid = 1;
return SQLITE_OK;
@@ -297,16 +275,9 @@ static int carrayFilter(
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** idxNum is:
**
** 1 If only the pointer= constraint exists. In this case, the
** parameter must be bound using sqlite3_carray_bind().
**
** 2 if the pointer= and count= constraints exist.
**
** 3 if the ctype= constraint also exists.
**
** idxNum is 0 otherwise and carray becomes an empty table.
** idxNum is 2 if the pointer= and count= constraints exist,
** 3 if the ctype= constraint also exists, and is 0 otherwise.
** If idxNum is 0, then carray becomes an empty table.
*/
static int carrayBestIndex(
sqlite3_vtab *tab,
@@ -334,21 +305,18 @@ static int carrayBestIndex(
break;
}
}
if( ptrIdx>=0 ){
if( ptrIdx>=0 && cntIdx>=0 ){
pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 100;
pIdxInfo->idxNum = 1;
if( cntIdx>=0 ){
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
pIdxInfo->idxNum = 2;
if( ctypeIdx>=0 ){
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
pIdxInfo->idxNum = 3;
}
pIdxInfo->idxNum = 2;
if( ctypeIdx>=0 ){
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
pIdxInfo->idxNum = 3;
}
}else{
pIdxInfo->estimatedCost = (double)2147483647;
@@ -385,89 +353,6 @@ static sqlite3_module carrayModule = {
0, /* xRename */
};
/*
** Destructor for the carray_bind object
*/
static void carrayBindDel(void *pPtr){
carray_bind *p = (carray_bind*)pPtr;
if( p->xDel!=SQLITE_STATIC ){
p->xDel(p->aData);
}
sqlite3_free(p);
}
/*
** Invoke this interface in order to bind to the single-argument
** version of CARRAY().
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_carray_bind(
sqlite3_stmt *pStmt,
int idx,
void *aData,
int nData,
int mFlags,
void (*xDestroy)(void*)
){
carray_bind *pNew;
int i;
pNew = sqlite3_malloc64(sizeof(*pNew));
if( pNew==0 ){
if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
xDestroy(aData);
}
return SQLITE_NOMEM;
}
pNew->nData = nData;
pNew->mFlags = mFlags;
if( xDestroy==SQLITE_TRANSIENT ){
sqlite3_int64 sz = nData;
switch( mFlags & 0x03 ){
case CARRAY_INT32: sz *= 4; break;
case CARRAY_INT64: sz *= 8; break;
case CARRAY_DOUBLE: sz *= 8; break;
case CARRAY_TEXT: sz *= sizeof(char*); break;
}
if( (mFlags & 0x03)==CARRAY_TEXT ){
for(i=0; i<nData; i++){
const char *z = ((char**)aData)[i];
if( z ) sz += strlen(z) + 1;
}
}
pNew->aData = sqlite3_malloc64( sz );
if( pNew->aData==0 ){
sqlite3_free(pNew);
return SQLITE_NOMEM;
}
if( (mFlags & 0x03)==CARRAY_TEXT ){
char **az = (char**)pNew->aData;
char *z = (char*)&az[nData];
for(i=0; i<nData; i++){
const char *zData = ((char**)aData)[i];
sqlite3_int64 n;
if( zData==0 ){
az[i] = 0;
continue;
}
az[i] = z;
n = strlen(zData);
memcpy(z, zData, n+1);
z += n+1;
}
}else{
memcpy(pNew->aData, aData, sz*nData);
}
pNew->xDel = sqlite3_free;
}else{
pNew->aData = aData;
pNew->xDel = xDestroy;
}
return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
}
/*
** For testing purpose in the TCL test harness, we need a method for
** setting the pointer value. The inttoptr(X) SQL function accomplishes
-23
View File
@@ -1,23 +0,0 @@
/*
** Interface definitions for the CARRAY table-valued function
** extension.
*/
/* Use this interface to bind an array to the single-argument version
** of CARRAY().
*/
int sqlite3_carray_bind(
sqlite3_stmt *pStmt, /* Statement to be bound */
int i, /* Parameter index */
void *aData, /* Pointer to array data */
int nData, /* Number of data elements */
int mFlags, /* CARRAY flags */
void (*xDel)(void*) /* Destructgor for aData*/
);
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
*/
#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
#define CARRAY_DOUBLE 2 /* Data is doubles */
#define CARRAY_TEXT 3 /* Data is char* */
-868
View File
@@ -1,868 +0,0 @@
/*
** 2020-04-20
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file implements a VFS shim that writes a checksum on each page
** of an SQLite database file. When reading pages, the checksum is verified
** and an error is raised if the checksum is incorrect.
**
** COMPILING
**
** This extension requires SQLite 3.32.0 or later. It uses the
** sqlite3_database_file_object() interface which was added in
** version 3.32.0, so it will not link with an earlier version of
** SQLite.
**
** To build this extension as a separately loaded shared library or
** DLL, use compiler command-lines similar to the following:
**
** (linux) gcc -fPIC -shared cksumvfs.c -o cksumvfs.so
** (mac) clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib
** (windows) cl cksumvfs.c -link -dll -out:cksumvfs.dll
**
** You may want to add additional compiler options, of course,
** according to the needs of your project.
**
** If you want to statically link this extension with your product,
** then compile it like any other C-language module but add the
** "-DSQLITE_CKSUMVFS_STATIC" option so that this module knows that
** it is being statically linked rather than dynamically linked
**
** LOADING
**
** To load this extension as a shared library, you first have to
** bring up a dummy SQLite database connection to use as the argument
** to the sqlite3_load_extension() API call. Then you invoke the
** sqlite3_load_extension() API and shutdown the dummy database
** connection. All subsequent database connections that are opened
** will include this extension. For example:
**
** sqlite3 *db;
** sqlite3_open(":memory:", &db);
** sqlite3_load_extention(db, "./cksumvfs");
** sqlite3_close(db);
**
** If this extension is compiled with -DSQLITE_CKSUMVFS_STATIC and
** statically linked against the application, initialize it using
** a single API call as follows:
**
** sqlite3_register_cksumvfs();
**
** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new
** default VFS and it uses the prior default VFS as the next VFS
** down in the stack. This is normally what you want. However, in
** complex situations where multiple VFS shims are being loaded,
** it might be important to ensure that cksumvfs is loaded in the
** correct order so that it sequences itself into the default VFS
** Shim stack in the right order.
**
** USING
**
** Open database connections using the sqlite3_open() or
** sqlite3_open_v2() interfaces, as normal. Ordinary database files
** (without a checksum) will operate normally. Databases with
** checksums will return an SQLITE_IOERR_DATA error if a page is
** encountered that contains an invalid checksum.
**
** Checksumming only works on databases that have a reserve-bytes
** value of exactly 8. The default value for reserve-bytes is 0.
** Hence, newly created database files will omit the checksum by
** default. To create a database that includes a checksum, change
** the reserve-bytes value to 8 by runing:
**
** int n = 8;
** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
**
** If you do this immediately after creating a new database file,
** before anything else has been written into the file, then that
** might be all that you need to do. Otherwise, the API call
** above should be followed by:
**
** sqlite3_exec(db, "VACUUM", 0, 0, 0);
**
** It never hurts to run the VACUUM, even if you don't need it.
** If the database is in WAL mode, you should shutdown and
** reopen all database connections before continuing.
**
** From the CLI, use the ".filectrl reserve_bytes 8" command,
** followed by "VACUUM;".
**
** Note that SQLite allows the number of reserve-bytes to be
** increased but not decreased. So if a database file already
** has a reserve-bytes value greater than 8, there is no way to
** activate checksumming on that database, other than to dump
** and restore the database file. Note also that other extensions
** might also make use of the reserve-bytes. Checksumming will
** be incompatible with those other extensions.
**
** VERIFICATION OF CHECKSUMS
**
** If any checksum is incorrect, the "PRAGMA quick_check" command
** will find it. To verify that checksums are actually enabled
** and running, use the following query:
**
** SELECT count(*), verify_checksum(data)
** FROM sqlite_dbpage
** GROUP BY 2;
**
** There are three possible outputs form the verify_checksum()
** function: 1, 0, and NULL. 1 is returned if the checksum is
** correct. 0 is returned if the checksum is incorrect. NULL
** is returned if the page is unreadable. If checksumming is
** enabled, the read will fail if the checksum is wrong, so the
** usual result from verify_checksum() on a bad checksum is NULL.
**
** If everything is OK, the query above should return a single
** row where the second column is 1. Any other result indicates
** either that there is a checksum error, or checksum validation
** is disabled.
**
** CONTROLLING CHECKSUM VERIFICATION
**
** The cksumvfs extension implements a new PRAGMA statement that can
** be used to disable, re-enable, or query the status of checksum
** verification:
**
** PRAGMA checksum_verification; -- query status
** PRAGMA checksum_verification=OFF; -- disable verification
** PRAGMA checksum_verification=ON; -- re-enable verification
**
** The "checksum_verification" pragma will return "1" (true) or "0"
** (false) if checksum verification is enabled or disabled, respectively.
** "Verification" in this context means the feature that causes
** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while
** reading. Checksums are always kept up-to-date as long as the
** reserve-bytes value of the database is 8, regardless of the setting
** of this pragma. Checksum verification can be disabled (for example)
** to do forensic analysis of a database that has previously reported
** a checksum error.
**
** The "checksum_verification" pragma will always respond with "0" if
** the database file does not have a reserve-bytes value of 8. The
** pragma will return no rows at all if the cksumvfs extension is
** not loaded.
**
** IMPLEMENTATION NOTES
**
** The checksum is stored in the last 8 bytes of each page. This
** module only operates if the "bytes of reserved space on each page"
** value at offset 20 the SQLite database header is exactly 8. If
** the reserved-space value is not 8, this module is a no-op.
*/
#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC)
# define SQLITE_CKSUMVFS_STATIC
#endif
#ifdef SQLITE_CKSUMVFS_STATIC
# include "sqlite3.h"
#else
# include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#endif
#include <string.h>
#include <assert.h>
/*
** Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs CksmVfs;
typedef struct CksmFile CksmFile;
/*
** Useful datatype abbreviations
*/
#if !defined(SQLITE_AMALGAMATION)
typedef unsigned char u8;
typedef unsigned int u32;
#endif
/* Access to a lower-level VFS that (might) implement dynamic loading,
** access to randomness, etc.
*/
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
#define ORIGFILE(p) ((sqlite3_file*)(((CksmFile*)(p))+1))
/* An open file */
struct CksmFile {
sqlite3_file base; /* IO methods */
const char *zFName; /* Original name of the file */
char computeCksm; /* True to compute checksums.
** Always true if reserve size is 8. */
char verifyCksm; /* True to verify checksums */
char isWal; /* True if processing a WAL file */
char inCkpt; /* Currently doing a checkpoint */
CksmFile *pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
};
/*
** Methods for CksmFile
*/
static int cksmClose(sqlite3_file*);
static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
static int cksmTruncate(sqlite3_file*, sqlite3_int64 size);
static int cksmSync(sqlite3_file*, int flags);
static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int cksmLock(sqlite3_file*, int);
static int cksmUnlock(sqlite3_file*, int);
static int cksmCheckReservedLock(sqlite3_file*, int *pResOut);
static int cksmFileControl(sqlite3_file*, int op, void *pArg);
static int cksmSectorSize(sqlite3_file*);
static int cksmDeviceCharacteristics(sqlite3_file*);
static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
static int cksmShmLock(sqlite3_file*, int offset, int n, int flags);
static void cksmShmBarrier(sqlite3_file*);
static int cksmShmUnmap(sqlite3_file*, int deleteFlag);
static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
/*
** Methods for CksmVfs
*/
static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename);
static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
static void cksmDlClose(sqlite3_vfs*, void*);
static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int cksmSleep(sqlite3_vfs*, int microseconds);
static int cksmCurrentTime(sqlite3_vfs*, double*);
static int cksmGetLastError(sqlite3_vfs*, int, char *);
static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z);
static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName);
static sqlite3_vfs cksm_vfs = {
3, /* iVersion (set when registered) */
0, /* szOsFile (set when registered) */
1024, /* mxPathname */
0, /* pNext */
"cksmvfs", /* zName */
0, /* pAppData (set when registered) */
cksmOpen, /* xOpen */
cksmDelete, /* xDelete */
cksmAccess, /* xAccess */
cksmFullPathname, /* xFullPathname */
cksmDlOpen, /* xDlOpen */
cksmDlError, /* xDlError */
cksmDlSym, /* xDlSym */
cksmDlClose, /* xDlClose */
cksmRandomness, /* xRandomness */
cksmSleep, /* xSleep */
cksmCurrentTime, /* xCurrentTime */
cksmGetLastError, /* xGetLastError */
cksmCurrentTimeInt64, /* xCurrentTimeInt64 */
cksmSetSystemCall, /* xSetSystemCall */
cksmGetSystemCall, /* xGetSystemCall */
cksmNextSystemCall /* xNextSystemCall */
};
static const sqlite3_io_methods cksm_io_methods = {
3, /* iVersion */
cksmClose, /* xClose */
cksmRead, /* xRead */
cksmWrite, /* xWrite */
cksmTruncate, /* xTruncate */
cksmSync, /* xSync */
cksmFileSize, /* xFileSize */
cksmLock, /* xLock */
cksmUnlock, /* xUnlock */
cksmCheckReservedLock, /* xCheckReservedLock */
cksmFileControl, /* xFileControl */
cksmSectorSize, /* xSectorSize */
cksmDeviceCharacteristics, /* xDeviceCharacteristics */
cksmShmMap, /* xShmMap */
cksmShmLock, /* xShmLock */
cksmShmBarrier, /* xShmBarrier */
cksmShmUnmap, /* xShmUnmap */
cksmFetch, /* xFetch */
cksmUnfetch /* xUnfetch */
};
/* Do byte swapping on a unsigned 32-bit integer */
#define BYTESWAP32(x) ( \
(((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \
+ (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \
)
/* Compute a checksum on a buffer */
static void cksmCompute(
u8 *a, /* Content to be checksummed */
int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */
u8 *aOut /* OUT: Final 8-byte checksum value output */
){
u32 s1 = 0, s2 = 0;
u32 *aData = (u32*)a;
u32 *aEnd = (u32*)&a[nByte];
u32 x = 1;
assert( nByte>=8 );
assert( (nByte&0x00000007)==0 );
assert( nByte<=65536 );
if( 1 == *(u8*)&x ){
/* Little-endian */
do {
s1 += *aData++ + s2;
s2 += *aData++ + s1;
}while( aData<aEnd );
}else{
/* Big-endian */
do {
s1 += BYTESWAP32(aData[0]) + s2;
s2 += BYTESWAP32(aData[1]) + s1;
aData += 2;
}while( aData<aEnd );
s1 = BYTESWAP32(s1);
s2 = BYTESWAP32(s2);
}
memcpy(aOut, &s1, 4);
memcpy(aOut+4, &s2, 4);
}
/*
** SQL function: verify_checksum(BLOB)
**
** Return 0 or 1 if the checksum is invalid or valid. Or return
** NULL if the input is not a BLOB that is the right size for a
** database page.
*/
static void cksmVerifyFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int nByte;
u8 *data;
u8 cksum[8];
data = (u8*)sqlite3_value_blob(argv[0]);
if( data==0 ) return;
if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ) return;
nByte = sqlite3_value_bytes(argv[0]);
if( nByte<512 || nByte>65536 || (nByte & (nByte-1))!=0 ) return;
cksmCompute(data, nByte-8, cksum);
sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
}
#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
/*
** SQL function: initialize_cksumvfs(SCHEMANAME)
**
** This SQL functions (whose name is actually determined at compile-time
** by the value of the SQLITE_CKSUMVFS_INIT_FUNCNAME macro) invokes:
**
** sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n);
**
** In order to set the reserve bytes value to 8, so that cksumvfs will
** operation. This feature is provided (if and only if the
** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string
** which is the name of the SQL function) so as to provide the ability
** to invoke the file-control in programming languages that lack
** direct access to the sqlite3_file_control() interface (ex: Java).
**
** This interface is undocumented, apart from this comment. Usage
** example:
**
** 1. Compile with -DSQLITE_CKSUMVFS_INIT_FUNCNAME="ckvfs_init"
** 2. Run: "SELECT cksum_init('main'); VACUUM;"
*/
static void cksmInitFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int nByte = 8;
const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]);
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte);
/* Return NULL */
}
#endif /* SQLITE_CKSUMBFS_INIT_FUNCNAME */
/*
** Close a cksm-file.
*/
static int cksmClose(sqlite3_file *pFile){
CksmFile *p = (CksmFile *)pFile;
if( p->pPartner ){
assert( p->pPartner->pPartner==p );
p->pPartner->pPartner = 0;
p->pPartner = 0;
}
pFile = ORIGFILE(pFile);
return pFile->pMethods->xClose(pFile);
}
/*
** Set the computeCkSm and verifyCksm flags, if they need to be
** changed.
*/
static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){
if( hasCorrectReserveSize!=p->computeCksm ){
p->computeCksm = p->verifyCksm = hasCorrectReserveSize;
if( p->pPartner ){
p->pPartner->verifyCksm = hasCorrectReserveSize;
p->pPartner->computeCksm = hasCorrectReserveSize;
}
}
}
/*
** Read data from a cksm-file.
*/
static int cksmRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
int rc;
CksmFile *p = (CksmFile *)pFile;
pFile = ORIGFILE(pFile);
rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
if( rc==SQLITE_OK ){
if( iOfst==0 && iAmt>=100 && (
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
)){
u8 *d = (u8*)zBuf;
char hasCorrectReserveSize = (d[20]==8);
cksmSetFlags(p, hasCorrectReserveSize);
}
/* Verify the checksum if
** (1) the size indicates that we are dealing with a complete
** database page
** (2) checksum verification is enabled
** (3) we are not in the middle of checkpoint
*/
if( iAmt>=512 /* (1) */
&& p->verifyCksm /* (2) */
&& !p->inCkpt /* (3) */
){
u8 cksum[8];
cksmCompute((u8*)zBuf, iAmt-8, cksum);
if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
sqlite3_log(SQLITE_IOERR_DATA,
"checksum fault offset %lld of \"%s\"",
iOfst, p->zFName);
rc = SQLITE_IOERR_DATA;
}
}
}
return rc;
}
/*
** Write data to a cksm-file.
*/
static int cksmWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
CksmFile *p = (CksmFile *)pFile;
pFile = ORIGFILE(pFile);
if( iOfst==0 && iAmt>=100 && (
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
)){
u8 *d = (u8*)zBuf;
char hasCorrectReserveSize = (d[20]==8);
cksmSetFlags(p, hasCorrectReserveSize);
}
/* If the write size is appropriate for a database page and if
** checksums where ever enabled, then it will be safe to compute
** the checksums. The reserve byte size might have increased, but
** it will never decrease. And because it cannot decrease, the
** checksum will not overwrite anything.
*/
if( iAmt>=512
&& p->computeCksm
&& !p->inCkpt
){
cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8);
}
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
}
/*
** Truncate a cksm-file.
*/
static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xTruncate(pFile, size);
}
/*
** Sync a cksm-file.
*/
static int cksmSync(sqlite3_file *pFile, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSync(pFile, flags);
}
/*
** Return the current file-size of a cksm-file.
*/
static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
CksmFile *p = (CksmFile *)pFile;
pFile = ORIGFILE(p);
return pFile->pMethods->xFileSize(pFile, pSize);
}
/*
** Lock a cksm-file.
*/
static int cksmLock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xLock(pFile, eLock);
}
/*
** Unlock a cksm-file.
*/
static int cksmUnlock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xUnlock(pFile, eLock);
}
/*
** Check if another file-handle holds a RESERVED lock on a cksm-file.
*/
static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
}
/*
** File control method. For custom operations on a cksm-file.
*/
static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){
int rc;
CksmFile *p = (CksmFile*)pFile;
pFile = ORIGFILE(pFile);
if( op==SQLITE_FCNTL_PRAGMA ){
char **azArg = (char**)pArg;
assert( azArg[1]!=0 );
if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){
char *zArg = azArg[2];
if( zArg!=0 ){
if( (zArg[0]>='1' && zArg[0]<='9')
|| sqlite3_strlike("enable%",zArg,0)==0
|| sqlite3_stricmp("yes",zArg)==0
|| sqlite3_stricmp("on",zArg)==0
){
p->verifyCksm = p->computeCksm;
}else{
p->verifyCksm = 0;
}
if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm;
}
azArg[0] = sqlite3_mprintf("%d",p->verifyCksm);
return SQLITE_OK;
}else if( p->computeCksm && azArg[2]!=0
&& sqlite3_stricmp(azArg[1], "page_size")==0 ){
/* Do not allow page size changes on a checksum database */
return SQLITE_OK;
}
}else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){
p->inCkpt = op==SQLITE_FCNTL_CKPT_START;
if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt;
}
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
*(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg);
}
return rc;
}
/*
** Return the sector-size in bytes for a cksm-file.
*/
static int cksmSectorSize(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSectorSize(pFile);
}
/*
** Return the device characteristic flags supported by a cksm-file.
*/
static int cksmDeviceCharacteristics(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xDeviceCharacteristics(pFile);
}
/* Create a shared memory file mapping */
static int cksmShmMap(
sqlite3_file *pFile,
int iPg,
int pgsz,
int bExtend,
void volatile **pp
){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
}
/* Perform locking on a shared-memory segment */
static int cksmShmLock(sqlite3_file *pFile, int offset, int n, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmLock(pFile,offset,n,flags);
}
/* Memory barrier operation on shared memory */
static void cksmShmBarrier(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
pFile->pMethods->xShmBarrier(pFile);
}
/* Unmap a shared memory segment */
static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
}
/* Fetch a page of a memory-mapped file */
static int cksmFetch(
sqlite3_file *pFile,
sqlite3_int64 iOfst,
int iAmt,
void **pp
){
CksmFile *p = (CksmFile *)pFile;
if( p->computeCksm ){
*pp = 0;
return SQLITE_OK;
}
pFile = ORIGFILE(pFile);
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
}
*pp = 0;
return SQLITE_OK;
}
/* Release a memory-mapped page */
static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
pFile = ORIGFILE(pFile);
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
}
return SQLITE_OK;
}
/*
** Open a cksm file handle.
*/
static int cksmOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
CksmFile *p;
sqlite3_file *pSubFile;
sqlite3_vfs *pSubVfs;
int rc;
pSubVfs = ORIGVFS(pVfs);
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
}
p = (CksmFile*)pFile;
memset(p, 0, sizeof(*p));
pSubFile = ORIGFILE(pFile);
pFile->pMethods = &cksm_io_methods;
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
if( rc ) goto cksm_open_done;
if( flags & SQLITE_OPEN_WAL ){
sqlite3_file *pDb = sqlite3_database_file_object(zName);
p->pPartner = (CksmFile*)pDb;
assert( p->pPartner->pPartner==0 );
p->pPartner->pPartner = p;
p->isWal = 1;
p->computeCksm = p->pPartner->computeCksm;
}else{
p->isWal = 0;
p->computeCksm = 0;
}
p->zFName = zName;
cksm_open_done:
if( rc ) pFile->pMethods = 0;
return rc;
}
/*
** All other VFS methods are pass-thrus.
*/
static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
}
static int cksmAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
}
static int cksmFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
}
static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
}
static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
}
static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
}
static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
}
static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
}
static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
}
static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
}
static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
}
static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
sqlite3_vfs *pOrig = ORIGVFS(pVfs);
int rc;
assert( pOrig->iVersion>=2 );
if( pOrig->xCurrentTimeInt64 ){
rc = pOrig->xCurrentTimeInt64(pOrig, p);
}else{
double r;
rc = pOrig->xCurrentTime(pOrig, &r);
*p = (sqlite3_int64)(r*86400000.0);
}
return rc;
}
static int cksmSetSystemCall(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_syscall_ptr pCall
){
return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
}
static sqlite3_syscall_ptr cksmGetSystemCall(
sqlite3_vfs *pVfs,
const char *zName
){
return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
}
static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
}
/* Register the verify_checksum() SQL function.
*/
static int cksmRegisterFunc(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
if( db==0 ) return SQLITE_OK;
rc = sqlite3_create_function(db, "verify_checksum", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0, cksmVerifyFunc, 0, 0);
#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
(void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1,
SQLITE_UTF8|SQLITE_DIRECTONLY,
0, cksmInitFunc, 0, 0);
#endif
return rc;
}
/*
** Register the cksum VFS as the default VFS for the system.
** Also make arrangements to automatically register the "verify_checksum()"
** SQL function on each new database connection.
*/
static int cksmRegisterVfs(void){
int rc = SQLITE_OK;
sqlite3_vfs *pOrig;
if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK;
pOrig = sqlite3_vfs_find(0);
cksm_vfs.iVersion = pOrig->iVersion;
cksm_vfs.pAppData = pOrig;
cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile);
rc = sqlite3_vfs_register(&cksm_vfs, 1);
if( rc==SQLITE_OK ){
rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc);
}
return rc;
}
#if defined(SQLITE_CKSUMVFS_STATIC)
/* This variant of the initializer runs when the extension is
** statically linked.
*/
int sqlite3_register_cksumvfs(const char *NotUsed){
(void)NotUsed;
return cksmRegisterVfs();
}
int sqlite3_unregister_cksumvfs(void){
if( sqlite3_vfs_find("cksmvfs") ){
sqlite3_vfs_unregister(&cksm_vfs);
sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc);
}
return SQLITE_OK;
}
#endif /* defined(SQLITE_CKSUMVFS_STATIC */
#if !defined(SQLITE_CKSUMVFS_STATIC)
/* This variant of the initializer function is used when the
** extension is shared library to be loaded at run-time.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
/*
** This routine is called by sqlite3_load_extension() when the
** extension is first loaded.
***/
int sqlite3_cksumvfs_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* not used */
rc = cksmRegisterFunc(db, 0, 0);
if( rc==SQLITE_OK ){
}
if( rc==SQLITE_OK ){
rc = cksmRegisterVfs();
}
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
return rc;
}
#endif /* !defined(SQLITE_CKSUMVFS_STATIC) */
+2 -3
View File
@@ -118,7 +118,6 @@ static int completionConnect(
#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */
#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x("
" candidate TEXT,"
@@ -226,7 +225,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT name FROM \"%w\".sqlite_schema",
"SELECT name FROM \"%w\".sqlite_master",
zSql, zSep, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
@@ -250,7 +249,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
"SELECT pti.name FROM \"%w\".sqlite_master AS sm"
" JOIN pragma_table_info(sm.name,%Q) AS pti"
" WHERE sm.type='table'",
zSql, zSep, zDb, zDb
+4 -6
View File
@@ -119,13 +119,11 @@ int sqlite3_compress_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "compress", 1,
SQLITE_UTF8 | SQLITE_INNOCUOUS,
0, compressFunc, 0, 0);
rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
compressFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "uncompress", 1,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, uncompressFunc, 0, 0);
rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
uncompressFunc, 0, 0);
}
return rc;
}
+1 -10
View File
@@ -632,15 +632,6 @@ static int csvtabConnect(
for(i=0; i<sizeof(azPValue)/sizeof(azPValue[0]); i++){
sqlite3_free(azPValue[i]);
}
/* Rationale for DIRECTONLY:
** An attacker who controls a database schema could use this vtab
** to exfiltrate sensitive data from other files in the filesystem.
** And, recommended practice is to put all CSV virtual tables in the
** TEMP namespace, so they should still be usable from within TEMP
** views, so there shouldn't be a serious loss of functionality by
** prohibiting the use of this vtab from persistent triggers and views.
*/
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
return SQLITE_OK;
csvtab_connect_oom:
@@ -776,7 +767,7 @@ static int csvtabColumn(
CsvCursor *pCur = (CsvCursor*)cur;
CsvTable *pTab = (CsvTable*)cur->pVtab;
if( i>=0 && i<pTab->nCol && pCur->azVal[i]!=0 ){
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_STATIC);
}
return SQLITE_OK;
}
+7 -7
View File
@@ -395,7 +395,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
if( strcmp(zTable, "sqlite_sequence")==0 ){
p->xCallback("DELETE FROM sqlite_sequence;\n", p->pArg);
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
p->xCallback("ANALYZE sqlite_schema;\n", p->pArg);
p->xCallback("ANALYZE sqlite_master;\n", p->pArg);
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
@@ -404,7 +404,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
p->writableSchema = 1;
}
output_formatted(p,
"INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)"
"INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
return 0;
@@ -646,27 +646,27 @@ int sqlite3_db_dump(
xCallback("PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\n", pArg);
if( zTable==0 ){
run_schema_dump_query(&x,
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
"SELECT name, type, sql FROM \"%w\".sqlite_master "
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'",
zSchema
);
run_schema_dump_query(&x,
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
"SELECT name, type, sql FROM \"%w\".sqlite_master "
"WHERE name=='sqlite_sequence'", zSchema
);
output_sql_from_query(&x,
"SELECT sql FROM sqlite_schema "
"SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
);
}else{
run_schema_dump_query(&x,
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
"SELECT name, type, sql FROM \"%w\".sqlite_master "
"WHERE tbl_name=%Q COLLATE nocase AND type=='table'"
" AND sql NOT NULL",
zSchema, zTable
);
output_sql_from_query(&x,
"SELECT sql FROM \"%w\".sqlite_schema "
"SELECT sql FROM \"%w\".sqlite_master "
"WHERE sql NOT NULL"
" AND type IN ('index','trigger','view')"
" AND tbl_name=%Q COLLATE nocase",
-634
View File
@@ -1,634 +0,0 @@
/*
** 2020-06-22
**
** 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.
**
******************************************************************************
**
** Routines to implement arbitrary-precision decimal math.
**
** The focus here is on simplicity and correctness, not performance.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void)(X)
#endif
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
char sign; /* 0 for positive, 1 for negative */
char oom; /* True if an OOM is encountered */
char isNull; /* True if holds a NULL rather than a number */
char isInit; /* True upon initialization */
int nDigit; /* Total number of digits */
int nFrac; /* Number of digits to the right of the decimal point */
signed char *a; /* Array of digits. Most significant first. */
};
/*
** Release memory held by a Decimal, but do not free the object itself.
*/
static void decimal_clear(Decimal *p){
sqlite3_free(p->a);
}
/*
** Destroy a Decimal object
*/
static void decimal_free(Decimal *p){
if( p ){
decimal_clear(p);
sqlite3_free(p);
}
}
/*
** Allocate a new Decimal object. Initialize it to the number given
** by the input string.
*/
static Decimal *decimal_new(
sqlite3_context *pCtx,
sqlite3_value *pIn,
int nAlt,
const unsigned char *zAlt
){
Decimal *p;
int n, i;
const unsigned char *zIn;
int iExp = 0;
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ) goto new_no_mem;
p->sign = 0;
p->oom = 0;
p->isInit = 1;
p->isNull = 0;
p->nDigit = 0;
p->nFrac = 0;
if( zAlt ){
n = nAlt,
zIn = zAlt;
}else{
if( sqlite3_value_type(pIn)==SQLITE_NULL ){
p->a = 0;
p->isNull = 1;
return p;
}
n = sqlite3_value_bytes(pIn);
zIn = sqlite3_value_text(pIn);
}
p->a = sqlite3_malloc64( n+1 );
if( p->a==0 ) goto new_no_mem;
for(i=0; isspace(zIn[i]); i++){}
if( zIn[i]=='-' ){
p->sign = 1;
i++;
}else if( zIn[i]=='+' ){
i++;
}
while( i<n && zIn[i]=='0' ) i++;
while( i<n ){
char c = zIn[i];
if( c>='0' && c<='9' ){
p->a[p->nDigit++] = c - '0';
}else if( c=='.' ){
p->nFrac = p->nDigit + 1;
}else if( c=='e' || c=='E' ){
int j = i+1;
int neg = 0;
if( j>=n ) break;
if( zIn[j]=='-' ){
neg = 1;
j++;
}else if( zIn[j]=='+' ){
j++;
}
while( j<n && iExp<1000000 ){
if( zIn[j]>='0' && zIn[j]<='9' ){
iExp = iExp*10 + zIn[j] - '0';
}
j++;
}
if( neg ) iExp = -iExp;
break;
}
i++;
}
if( p->nFrac ){
p->nFrac = p->nDigit - (p->nFrac - 1);
}
if( iExp>0 ){
if( p->nFrac>0 ){
if( iExp<=p->nFrac ){
p->nFrac -= iExp;
iExp = 0;
}else{
iExp -= p->nFrac;
p->nFrac = 0;
}
}
if( iExp>0 ){
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
if( p->a==0 ) goto new_no_mem;
memset(p->a+p->nDigit, 0, iExp);
p->nDigit += iExp;
}
}else if( iExp<0 ){
int nExtra;
iExp = -iExp;
nExtra = p->nDigit - p->nFrac - 1;
if( nExtra ){
if( nExtra>=iExp ){
p->nFrac += iExp;
iExp = 0;
}else{
iExp -= nExtra;
p->nFrac = p->nDigit - 1;
}
}
if( iExp>0 ){
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
if( p->a==0 ) goto new_no_mem;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0, iExp);
p->nDigit += iExp;
p->nFrac += iExp;
}
}
return p;
new_no_mem:
if( pCtx ) sqlite3_result_error_nomem(pCtx);
sqlite3_free(p);
return 0;
}
/*
** Make the given Decimal the result.
*/
static void decimal_result(sqlite3_context *pCtx, Decimal *p){
char *z;
int i, j;
int n;
if( p==0 || p->oom ){
sqlite3_result_error_nomem(pCtx);
return;
}
if( p->isNull ){
sqlite3_result_null(pCtx);
return;
}
z = sqlite3_malloc( p->nDigit+4 );
if( z==0 ){
sqlite3_result_error_nomem(pCtx);
return;
}
i = 0;
if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){
p->sign = 0;
}
if( p->sign ){
z[0] = '-';
i = 1;
}
n = p->nDigit - p->nFrac;
if( n<=0 ){
z[i++] = '0';
}
j = 0;
while( n>1 && p->a[j]==0 ){
j++;
n--;
}
while( n>0 ){
z[i++] = p->a[j] + '0';
j++;
n--;
}
if( p->nFrac ){
z[i++] = '.';
do{
z[i++] = p->a[j] + '0';
j++;
}while( j<p->nDigit );
}
z[i] = 0;
sqlite3_result_text(pCtx, z, i, sqlite3_free);
}
/*
** SQL Function: decimal(X)
**
** Convert input X into decimal and then back into text
*/
static void decimalFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *p = decimal_new(context, argv[0], 0, 0);
UNUSED_PARAMETER(argc);
decimal_result(context, p);
decimal_free(p);
}
/*
** Compare to Decimal objects. Return negative, 0, or positive if the
** first object is less than, equal to, or greater than the second.
**
** Preconditions for this routine:
**
** pA!=0
** pA->isNull==0
** pB!=0
** pB->isNull==0
*/
static int decimal_cmp(const Decimal *pA, const Decimal *pB){
int nASig, nBSig, rc, n;
if( pA->sign!=pB->sign ){
return pA->sign ? -1 : +1;
}
if( pA->sign ){
const Decimal *pTemp = pA;
pA = pB;
pB = pTemp;
}
nASig = pA->nDigit - pA->nFrac;
nBSig = pB->nDigit - pB->nFrac;
if( nASig!=nBSig ){
return nASig - nBSig;
}
n = pA->nDigit;
if( n>pB->nDigit ) n = pB->nDigit;
rc = memcmp(pA->a, pB->a, n);
if( rc==0 ){
rc = pA->nDigit - pB->nDigit;
}
return rc;
}
/*
** SQL Function: decimal_cmp(X, Y)
**
** Return negative, zero, or positive if X is less then, equal to, or
** greater than Y.
*/
static void decimalCmpFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = 0, *pB = 0;
int rc;
UNUSED_PARAMETER(argc);
pA = decimal_new(context, argv[0], 0, 0);
if( pA==0 || pA->isNull ) goto cmp_done;
pB = decimal_new(context, argv[1], 0, 0);
if( pB==0 || pB->isNull ) goto cmp_done;
rc = decimal_cmp(pA, pB);
if( rc<0 ) rc = -1;
else if( rc>0 ) rc = +1;
sqlite3_result_int(context, rc);
cmp_done:
decimal_free(pA);
decimal_free(pB);
}
/*
** Expand the Decimal so that it has a least nDigit digits and nFrac
** digits to the right of the decimal point.
*/
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
int nAddSig;
int nAddFrac;
if( p==0 ) return;
nAddFrac = nFrac - p->nFrac;
nAddSig = (nDigit - p->nDigit) - nAddFrac;
if( nAddFrac==0 && nAddSig==0 ) return;
p->a = sqlite3_realloc64(p->a, nDigit+1);
if( p->a==0 ){
p->oom = 1;
return;
}
if( nAddSig ){
memmove(p->a+nAddSig, p->a, p->nDigit);
memset(p->a, 0, nAddSig);
p->nDigit += nAddSig;
}
if( nAddFrac ){
memset(p->a+p->nDigit, 0, nAddFrac);
p->nDigit += nAddFrac;
p->nFrac += nAddFrac;
}
}
/*
** Add the value pB into pA.
**
** Both pA and pB might become denormalized by this routine.
*/
static void decimal_add(Decimal *pA, Decimal *pB){
int nSig, nFrac, nDigit;
int i, rc;
if( pA==0 ){
return;
}
if( pA->oom || pB==0 || pB->oom ){
pA->oom = 1;
return;
}
if( pA->isNull || pB->isNull ){
pA->isNull = 1;
return;
}
nSig = pA->nDigit - pA->nFrac;
if( nSig && pA->a[0]==0 ) nSig--;
if( nSig<pB->nDigit-pB->nFrac ){
nSig = pB->nDigit - pB->nFrac;
}
nFrac = pA->nFrac;
if( nFrac<pB->nFrac ) nFrac = pB->nFrac;
nDigit = nSig + nFrac + 1;
decimal_expand(pA, nDigit, nFrac);
decimal_expand(pB, nDigit, nFrac);
if( pA->oom || pB->oom ){
pA->oom = 1;
}else{
if( pA->sign==pB->sign ){
int carry = 0;
for(i=nDigit-1; i>=0; i--){
int x = pA->a[i] + pB->a[i] + carry;
if( x>=10 ){
carry = 1;
pA->a[i] = x - 10;
}else{
carry = 0;
pA->a[i] = x;
}
}
}else{
signed char *aA, *aB;
int borrow = 0;
rc = memcmp(pA->a, pB->a, nDigit);
if( rc<0 ){
aA = pB->a;
aB = pA->a;
pA->sign = !pA->sign;
}else{
aA = pA->a;
aB = pB->a;
}
for(i=nDigit-1; i>=0; i--){
int x = aA[i] - aB[i] - borrow;
if( x<0 ){
pA->a[i] = x+10;
borrow = 1;
}else{
pA->a[i] = x;
borrow = 0;
}
}
}
}
}
/*
** Compare text in decimal order.
*/
static int decimalCollFunc(
void *notUsed,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
const unsigned char *zA = (const unsigned char*)pKey1;
const unsigned char *zB = (const unsigned char*)pKey2;
Decimal *pA = decimal_new(0, 0, nKey1, zA);
Decimal *pB = decimal_new(0, 0, nKey2, zB);
int rc;
UNUSED_PARAMETER(notUsed);
if( pA==0 || pB==0 ){
rc = 0;
}else{
rc = decimal_cmp(pA, pB);
}
decimal_free(pA);
decimal_free(pB);
return rc;
}
/*
** SQL Function: decimal_add(X, Y)
** decimal_sub(X, Y)
**
** Return the sum or difference of X and Y.
*/
static void decimalAddFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = decimal_new(context, argv[0], 0, 0);
Decimal *pB = decimal_new(context, argv[1], 0, 0);
UNUSED_PARAMETER(argc);
decimal_add(pA, pB);
decimal_result(context, pA);
decimal_free(pA);
decimal_free(pB);
}
static void decimalSubFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = decimal_new(context, argv[0], 0, 0);
Decimal *pB = decimal_new(context, argv[1], 0, 0);
UNUSED_PARAMETER(argc);
if( pB==0 ) return;
pB->sign = !pB->sign;
decimal_add(pA, pB);
decimal_result(context, pA);
decimal_free(pA);
decimal_free(pB);
}
/* Aggregate funcion: decimal_sum(X)
**
** Works like sum() except that it uses decimal arithmetic for unlimited
** precision.
*/
static void decimalSumStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *p;
Decimal *pArg;
UNUSED_PARAMETER(argc);
p = sqlite3_aggregate_context(context, sizeof(*p));
if( p==0 ) return;
if( !p->isInit ){
p->isInit = 1;
p->a = sqlite3_malloc(2);
if( p->a==0 ){
p->oom = 1;
}else{
p->a[0] = 0;
}
p->nDigit = 1;
p->nFrac = 0;
}
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
pArg = decimal_new(context, argv[0], 0, 0);
decimal_add(p, pArg);
decimal_free(pArg);
}
static void decimalSumInverse(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *p;
Decimal *pArg;
UNUSED_PARAMETER(argc);
p = sqlite3_aggregate_context(context, sizeof(*p));
if( p==0 ) return;
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
pArg = decimal_new(context, argv[0], 0, 0);
if( pArg ) pArg->sign = !pArg->sign;
decimal_add(p, pArg);
decimal_free(pArg);
}
static void decimalSumValue(sqlite3_context *context){
Decimal *p = sqlite3_aggregate_context(context, 0);
if( p==0 ) return;
decimal_result(context, p);
}
static void decimalSumFinalize(sqlite3_context *context){
Decimal *p = sqlite3_aggregate_context(context, 0);
if( p==0 ) return;
decimal_result(context, p);
decimal_clear(p);
}
/*
** SQL Function: decimal_mul(X, Y)
**
** Return the product of X and Y.
**
** All significant digits after the decimal point are retained.
** Trailing zeros after the decimal point are omitted as long as
** the number of digits after the decimal point is no less than
** either the number of digits in either input.
*/
static void decimalMulFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = decimal_new(context, argv[0], 0, 0);
Decimal *pB = decimal_new(context, argv[1], 0, 0);
signed char *acc = 0;
int i, j, k;
int minFrac;
UNUSED_PARAMETER(argc);
if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
if( acc==0 ){
sqlite3_result_error_nomem(context);
goto mul_end;
}
memset(acc, 0, pA->nDigit + pB->nDigit + 2);
minFrac = pA->nFrac;
if( pB->nFrac<minFrac ) minFrac = pB->nFrac;
for(i=pA->nDigit-1; i>=0; i--){
signed char f = pA->a[i];
int carry = 0, x;
for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){
x = acc[k] + f*pB->a[j] + carry;
acc[k] = x%10;
carry = x/10;
}
x = acc[k] + carry;
acc[k] = x%10;
acc[k-1] += x/10;
}
sqlite3_free(pA->a);
pA->a = acc;
acc = 0;
pA->nDigit += pB->nDigit + 2;
pA->nFrac += pB->nFrac;
pA->sign ^= pB->sign;
while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){
pA->nFrac--;
pA->nDigit--;
}
decimal_result(context, pA);
mul_end:
sqlite3_free(acc);
decimal_free(pA);
decimal_free(pB);
}
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_decimal_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
static const struct {
const char *zFuncName;
int nArg;
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "decimal", 1, decimalFunc },
{ "decimal_cmp", 2, decimalCmpFunc },
{ "decimal_add", 2, decimalAddFunc },
{ "decimal_sub", 2, decimalSubFunc },
{ "decimal_mul", 2, decimalMulFunc },
};
unsigned int i;
(void)pzErrMsg; /* Unused parameter */
SQLITE_EXTENSION_INIT2(pApi);
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0, aFunc[i].xFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_window_function(db, "decimal_sum", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, 0,
decimalSumStep, decimalSumFinalize,
decimalSumValue, decimalSumInverse, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
0, decimalCollFunc);
}
return rc;
}
+2 -4
View File
@@ -113,12 +113,10 @@ int sqlite3_eval_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "eval", 1,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
sqlEvalFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "eval", 2,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
sqlEvalFunc, 0, 0);
}
return rc;
+1 -1
View File
@@ -16,7 +16,7 @@
** Usage example:
**
** .load ./explain
** SELECT p2 FROM explain('SELECT * FROM sqlite_schema')
** SELECT p2 FROM explain('SELECT * FROM sqlite_master')
** WHERE opcode='OpenRead';
**
** This module was originally written to help simplify SQLite testing,
+2 -7
View File
@@ -394,7 +394,6 @@ static int writeFile(
if( mtime>=0 ){
#if defined(_WIN32)
#if !SQLITE_OS_WINRT
/* Windows */
FILETIME lastAccess;
FILETIME lastWrite;
@@ -425,7 +424,6 @@ static int writeFile(
}else{
return 1;
}
#endif
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
/* Recent unix */
struct timespec times[2];
@@ -587,7 +585,6 @@ static int fsdirConnect(
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
}
*ppVtab = (sqlite3_vtab*)pNew;
return rc;
@@ -981,12 +978,10 @@ int sqlite3_fileio_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "readfile", 1,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
readfileFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "writefile", -1,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
writefileFunc, 0, 0);
}
if( rc==SQLITE_OK ){
+3 -5
View File
@@ -822,7 +822,6 @@ static int deltaparsevtabConnect(
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
@@ -1071,18 +1070,17 @@ int sqlite3_fossildelta_init(
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
static const int enc = SQLITE_UTF8|SQLITE_INNOCUOUS;
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "delta_create", 2, enc, 0,
rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0,
deltaCreateFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_apply", 2, enc, 0,
rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
deltaApplyFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_output_size", 1, enc, 0,
rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
deltaOutputSizeFunc, 0, 0);
}
if( rc==SQLITE_OK ){
-2
View File
@@ -540,8 +540,6 @@ static int fuzzerConnect(
if( rc!=SQLITE_OK ){
fuzzerDisconnect((sqlite3_vtab *)pNew);
pNew = 0;
}else{
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
}
}
+19 -177
View File
@@ -26,76 +26,16 @@
**
** Examples:
**
** ieee754(2.0) -> 'ieee754(2,0)'
** ieee754(45.25) -> 'ieee754(181,-2)'
** ieee754(2, 0) -> 2.0
** ieee754(181, -2) -> 45.25
**
** Two additional functions break apart the one-argument ieee754()
** result into separate integer values:
**
** ieee754_mantissa(45.25) -> 181
** ieee754_exponent(45.25) -> -2
**
** These functions convert binary64 numbers into blobs and back again.
**
** ieee754_from_blob(x'3ff0000000000000') -> 1.0
** ieee754_to_blob(1.0) -> x'3ff0000000000000'
**
** In all single-argument functions, if the argument is an 8-byte blob
** then that blob is interpreted as a big-endian binary64 value.
**
**
** EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES
** -----------------------------------------------
**
** This extension in combination with the separate 'decimal' extension
** can be used to compute the exact decimal representation of binary64
** values. To begin, first compute a table of exponent values:
**
** CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
** WITH RECURSIVE c(x,v) AS (
** VALUES(0,'1')
** UNION ALL
** SELECT x+1, decimal_mul(v,'2') FROM c WHERE x+1<=971
** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
** WITH RECURSIVE c(x,v) AS (
** VALUES(-1,'0.5')
** UNION ALL
** SELECT x-1, decimal_mul(v,'0.5') FROM c WHERE x-1>=-1075
** ) INSERT INTO pow2(x,v) SELECT x, v FROM c;
**
** Then, to compute the exact decimal representation of a floating
** point value (the value 47.49 is used in the example) do:
**
** WITH c(n) AS (VALUES(47.49))
** ---------------^^^^^---- Replace with whatever you want
** SELECT decimal_mul(ieee754_mantissa(c.n),pow2.v)
** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.n);
**
** Here is a query to show various boundry values for the binary64
** number format:
**
** WITH c(name,bin) AS (VALUES
** ('minimum positive value', x'0000000000000001'),
** ('maximum subnormal value', x'000fffffffffffff'),
** ('mininum positive nornal value', x'0010000000000000'),
** ('maximum value', x'7fefffffffffffff'))
** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
**
** ieee754(2.0) -> 'ieee754(2,0)'
** ieee754(45.25) -> 'ieee754(181,-2)'
** ieee754(2, 0) -> 2.0
** ieee754(181, -2) -> 45.25
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void)(X)
#endif
/*
** Implementation of the ieee754() function
*/
@@ -111,19 +51,8 @@ static void ieee754func(
int isNeg;
char zResult[100];
assert( sizeof(m)==sizeof(r) );
if( sqlite3_value_type(argv[0])==SQLITE_BLOB
&& sqlite3_value_bytes(argv[0])==sizeof(r)
){
const unsigned char *x = sqlite3_value_blob(argv[0]);
unsigned int i;
sqlite3_uint64 v = 0;
for(i=0; i<sizeof(r); i++){
v = (v<<8) | x[i];
}
memcpy(&r, &v, sizeof(r));
}else{
r = sqlite3_value_double(argv[0]);
}
if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
r = sqlite3_value_double(argv[0]);
if( r<0.0 ){
isNeg = 1;
r = -r;
@@ -137,49 +66,27 @@ static void ieee754func(
}else{
e = a>>52;
m = a & ((((sqlite3_int64)1)<<52)-1);
if( e==0 ){
m <<= 1;
}else{
m |= ((sqlite3_int64)1)<<52;
}
m |= ((sqlite3_int64)1)<<52;
while( e<1075 && m>0 && (m&1)==0 ){
m >>= 1;
e++;
}
if( isNeg ) m = -m;
}
switch( *(int*)sqlite3_user_data(context) ){
case 0:
sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
m, e-1075);
sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
break;
case 1:
sqlite3_result_int64(context, m);
break;
case 2:
sqlite3_result_int(context, e-1075);
break;
}
}else{
sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
m, e-1075);
sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
}else if( argc==2 ){
sqlite3_int64 m, e, a;
double r;
int isNeg = 0;
m = sqlite3_value_int64(argv[0]);
e = sqlite3_value_int64(argv[1]);
/* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
if( e>10000 ){
e = 10000;
}else if( e<-10000 ){
e = -10000;
}
if( m<0 ){
isNeg = 1;
m = -m;
if( m<0 ) return;
}else if( m==0 && e>-1000 && e<1000 ){
}else if( m==0 && e>1000 && e<1000 ){
sqlite3_result_double(context, 0.0);
return;
}
@@ -192,13 +99,8 @@ static void ieee754func(
e--;
}
e += 1075;
if( e<=0 ){
/* Subnormal */
m >>= 1-e;
e = 0;
}else if( e>0x7ff ){
e = 0x7ff;
}
if( e<0 ) e = m = 0;
if( e>0x7ff ) e = 0x7ff;
a = m & ((((sqlite3_int64)1)<<52)-1);
a |= e<<52;
if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
@@ -207,51 +109,6 @@ static void ieee754func(
}
}
/*
** Functions to convert between blobs and floats.
*/
static void ieee754func_from_blob(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_BLOB
&& sqlite3_value_bytes(argv[0])==sizeof(double)
){
double r;
const unsigned char *x = sqlite3_value_blob(argv[0]);
unsigned int i;
sqlite3_uint64 v = 0;
for(i=0; i<sizeof(r); i++){
v = (v<<8) | x[i];
}
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(context, r);
}
}
static void ieee754func_to_blob(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT
|| sqlite3_value_type(argv[0])==SQLITE_INTEGER
){
double r = sqlite3_value_double(argv[0]);
sqlite3_uint64 v;
unsigned char a[sizeof(r)];
unsigned int i;
memcpy(&v, &r, sizeof(r));
for(i=1; i<=sizeof(r); i++){
a[sizeof(r)-i] = v&0xff;
v >>= 8;
}
sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT);
}
}
#ifdef _WIN32
__declspec(dllexport)
@@ -261,29 +118,14 @@ int sqlite3_ieee_init(
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
static const struct {
char *zFName;
int nArg;
int iAux;
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "ieee754", 1, 0, ieee754func },
{ "ieee754", 2, 0, ieee754func },
{ "ieee754_mantissa", 1, 1, ieee754func },
{ "ieee754_exponent", 1, 2, ieee754func },
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
};
unsigned int i;
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zFName, aFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS,
(void*)&aFunc[i].iAux,
aFunc[i].xFunc, 0, 0);
rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
ieee754func, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
ieee754func, 0, 0);
}
return rc;
}
+22 -103
View File
@@ -42,10 +42,6 @@ SQLITE_EXTENSION_INIT1
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
#endif
#ifndef deliberate_fall_through
# define deliberate_fall_through
#endif
/*
** Versions of isspace(), isalnum() and isdigit() to which it is safe
** to pass signed char values.
@@ -258,7 +254,6 @@ static int jsonGrow(JsonString *p, u32 N){
/* Append N bytes from zIn onto the end of the JsonString string.
*/
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
if( N==0 ) return;
if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
@@ -464,7 +459,7 @@ static void jsonRenderNode(
jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
break;
}
/* no break */ deliberate_fall_through
/* Fall through into the next case */
}
case JSON_REAL:
case JSON_INT: {
@@ -527,37 +522,6 @@ static void jsonReturnJson(
sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
}
/*
** Translate a single byte of Hex into an integer.
** This routine only works if h really is a valid hexadecimal
** character: 0..9a..fA..F
*/
static u8 jsonHexToInt(int h){
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
#ifdef SQLITE_EBCDIC
h += 9*(1&~(h>>4));
#else
h += 9*(1&(h>>6));
#endif
return (u8)(h & 0xf);
}
/*
** Convert a 4-byte hex string into an integer
*/
static u32 jsonHexToInt4(const char *z){
u32 v;
assert( safe_isxdigit(z[0]) );
assert( safe_isxdigit(z[1]) );
assert( safe_isxdigit(z[2]) );
assert( safe_isxdigit(z[3]) );
v = (jsonHexToInt(z[0])<<12)
+ (jsonHexToInt(z[1])<<8)
+ (jsonHexToInt(z[2])<<4)
+ jsonHexToInt(z[3]);
return v;
}
/*
** Make the JsonNode the return value of the function.
*/
@@ -605,7 +569,7 @@ static void jsonReturn(
sqlite3_result_int64(pCtx, i);
int_done:
break;
int_as_real: i=0; /* no break */ deliberate_fall_through
int_as_real: /* fall through to real */;
}
case JSON_REAL: {
double r;
@@ -651,8 +615,15 @@ static void jsonReturn(
}else{
c = z[++i];
if( c=='u' ){
u32 v = jsonHexToInt4(z+i+1);
i += 4;
u32 v = 0, k;
for(k=0; k<4; i++, k++){
assert( i<n-2 );
c = z[i+1];
assert( safe_isxdigit(c) );
if( c<='9' ) v = v*16 + c - '0';
else if( c<='F' ) v = v*16 + c - 'A' + 10;
else v = v*16 + c - 'a' + 10;
}
if( v==0 ) break;
if( v<=0x7f ){
zOut[j++] = (char)v;
@@ -660,25 +631,9 @@ static void jsonReturn(
zOut[j++] = (char)(0xc0 | (v>>6));
zOut[j++] = 0x80 | (v&0x3f);
}else{
u32 vlo;
if( (v&0xfc00)==0xd800
&& i<n-6
&& z[i+1]=='\\'
&& z[i+2]=='u'
&& ((vlo = jsonHexToInt4(z+i+3))&0xfc00)==0xdc00
){
/* We have a surrogate pair */
v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
i += 6;
zOut[j++] = 0xf0 | (v>>18);
zOut[j++] = 0x80 | ((v>>12)&0x3f);
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}else{
zOut[j++] = 0xe0 | (v>>12);
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}
zOut[j++] = (char)(0xe0 | (v>>12));
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}
}else{
if( c=='b' ){
@@ -1181,49 +1136,18 @@ static JsonNode *jsonLookupStep(
}
return pNode;
}
}else if( zPath[0]=='[' ){
}else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
if( pRoot->eType!=JSON_ARRAY ) return 0;
i = 0;
j = 1;
while( safe_isdigit(zPath[j]) ){
i = i*10 + zPath[j] - '0';
j++;
}
if( j<2 || zPath[j]!=']' ){
if( zPath[1]=='#' ){
JsonNode *pBase = pRoot;
int iBase = iRoot;
if( pRoot->eType!=JSON_ARRAY ) return 0;
for(;;){
while( j<=pBase->n ){
if( (pBase[j].jnFlags & JNODE_REMOVE)==0 ) i++;
j += jsonNodeSize(&pBase[j]);
}
if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
iBase += pBase->u.iAppend;
pBase = &pParse->aNode[iBase];
j = 1;
}
j = 2;
if( zPath[2]=='-' && safe_isdigit(zPath[3]) ){
unsigned int x = 0;
j = 3;
do{
x = x*10 + zPath[j] - '0';
j++;
}while( safe_isdigit(zPath[j]) );
if( x>i ) return 0;
i -= x;
}
if( zPath[j]!=']' ){
*pzErr = zPath;
return 0;
}
}else{
*pzErr = zPath;
return 0;
}
if( zPath[j]!=']' ){
*pzErr = zPath;
return 0;
}
if( pRoot->eType!=JSON_ARRAY ) return 0;
zPath += j + 1;
j = 1;
for(;;){
@@ -2096,7 +2020,6 @@ static int jsonEachConnect(
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
@@ -2308,7 +2231,6 @@ static int jsonEachColumn(
}
/* For json_each() path and root are the same so fall through
** into the root case */
/* no break */ deliberate_fall_through
}
default: {
const char *zRoot = p->zRoot;
@@ -2588,19 +2510,16 @@ int sqlite3Json1Init(sqlite3 *db){
{ "json_tree", &jsonTreeModule },
};
#endif
static const int enc =
SQLITE_UTF8 |
SQLITE_DETERMINISTIC |
SQLITE_INNOCUOUS;
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg, enc,
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
(void*)&aFunc[i].flag,
aFunc[i].xFunc, 0, 0);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
SQLITE_SUBTYPE | enc, 0,
SQLITE_SUBTYPE | SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
aAgg[i].xStep, aAgg[i].xFinal,
aAgg[i].xValue, jsonGroupInverse, 0);
}
+1 -1
View File
@@ -43,7 +43,7 @@ int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){
if( 0==sqlite3_get_autocommit(db) ) return SQLITE_MISUSE;
/* Open a read-only transaction on the file in question */
zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_schema",
zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_master",
(zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "")
);
if( zSql==0 ) return SQLITE_NOMEM;
+3 -6
View File
@@ -297,17 +297,14 @@ int sqlite3_nextchar_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "next_char", 3,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
nextCharFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "next_char", 4,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
nextCharFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "next_char", 5,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
nextCharFunc, 0, 0);
}
return rc;
-68
View File
@@ -1,68 +0,0 @@
/*
** 2020-01-08
**
** 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 SQLite extension implements a noop() function used for testing.
**
** Variants:
**
** noop(X) The default. Deterministic.
** noop_i(X) Deterministic and innocuous.
** noop_do(X) Deterministic and direct-only.
** noop_nd(X) Non-deterministic.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
/*
** Implementation of the noop() function.
**
** The function returns its argument, unchanged.
*/
static void noopfunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( argc==1 );
sqlite3_result_value(context, argv[0]);
}
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_noop_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "noop", 1,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
0, noopfunc, 0, 0);
if( rc ) return rc;
rc = sqlite3_create_function(db, "noop_i", 1,
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS,
0, noopfunc, 0, 0);
if( rc ) return rc;
rc = sqlite3_create_function(db, "noop_do", 1,
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY,
0, noopfunc, 0, 0);
if( rc ) return rc;
rc = sqlite3_create_function(db, "noop_nd", 1,
SQLITE_UTF8,
0, noopfunc, 0, 0);
return rc;
}
-9
View File
@@ -286,13 +286,6 @@ static const unsigned char sqlite3CtypeMap[256] = {
#define TK_VARIABLE TK_LITERAL
#define TK_BLOB TK_LITERAL
/* Disable nuisence warnings about case fall-through */
#if !defined(deliberate_fall_through) && defined(__GCC__) && __GCC__>=7
# define deliberate_fall_through __attribute__((fallthrough));
#else
# define deliberate_fall_through
#endif
/*
** Return the length (in bytes) of the token that begins at z[0].
** Store the token type in *tokenType before returning.
@@ -443,7 +436,6 @@ static int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
/* If the next character is a digit, this is a floating point
** number that begins with ".". Fall thru into the next case */
/* no break */ deliberate_fall_through
}
case CC_DIGIT: {
*tokenType = TK_INTEGER;
@@ -536,7 +528,6 @@ static int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
/* If it is not a BLOB literal, then it must be an ID, since no
** SQL keywords start with the letter 'x'. Fall through */
/* no break */ deliberate_fall_through
}
case CC_ID: {
i = 1;
+1 -2
View File
@@ -213,8 +213,7 @@ int sqlite3_percentile_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "percentile", 2,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
0, percentStep, percentFinal);
return rc;
}
-1
View File
@@ -79,7 +79,6 @@ static int prefixesConnect(
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
+3 -3
View File
@@ -156,7 +156,7 @@ static unsigned re_next_char(ReInput *p){
&& (p->z[p->i+1]&0xc0)==0x80 ){
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
p->i += 2;
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
}else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
@@ -754,7 +754,7 @@ int sqlite3_regexp_init(
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS,
0, re_sql_func, 0, 0);
rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
re_sql_func, 0, 0);
return rc;
}
+2 -3
View File
@@ -105,9 +105,8 @@ int sqlite3_rot_init(
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "rot13", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0, rot13func, 0, 0);
rc = sqlite3_create_function(db, "rot13", 1, SQLITE_UTF8, 0,
rot13func, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
}

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