mirror of
https://github.com/status-im/sqlcipher.git
synced 2025-02-23 01:08:09 +00:00
Merge branch 'prerelease'
This commit is contained in:
commit
87b4a1ea57
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,6 +1,13 @@
|
||||
# SQLCipher Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [4.4.1] - (October 2020 - [4.4.1 changes])
|
||||
- Updates baseline to upstream SQLite 3.33.0
|
||||
- Fixes double-free bug in cipher_default_plaintext_header_size
|
||||
- Changes SQLCipher tests to use suite runner
|
||||
- Improvement to cipher_integrity_check tests to minimize false negatives
|
||||
- Deprecates PRAGMA cipher_store_pass
|
||||
|
||||
## [4.4.0] - (May 2020 - [4.4.0 changes])
|
||||
- Updates baseline to upstream SQLite 3.31.0
|
||||
- Adjusts shell to report SQLCipher version alongside SQLite version
|
||||
@ -164,7 +171,9 @@ All notable changes to this project will be documented in this file.
|
||||
### Security
|
||||
- Change KDF iteration length from 4,000 to 64,000
|
||||
|
||||
[unreleased]: https://github.com/sqlcipher/sqlcipher/compare/v4.4.0...prerelease
|
||||
[unreleased]: https://github.com/sqlcipher/sqlcipher/compare/v4.4.1...prerelease
|
||||
[4.4.1]: https://github.com/sqlcipher/sqlcipher/tree/v4.4.1
|
||||
[4.4.1 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.4.0...v4.4.1
|
||||
[4.4.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.4.0
|
||||
[4.4.0 changes]: https://github.com/sqlcipher/sqlcipher/compare/v4.3.0...v4.4.0
|
||||
[4.3.0]: https://github.com/sqlcipher/sqlcipher/tree/v4.3.0
|
||||
|
36
Makefile.in
36
Makefile.in
@ -211,8 +211,9 @@ 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 wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
|
||||
window.lo utf.lo vtab.lo $(CRYPTOLIBOBJ)
|
||||
vdbetrace.lo vdbevtab.lo \
|
||||
wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
|
||||
window.lo utf.lo vtab.lo $(CRYPTOLIBOBJ)
|
||||
|
||||
# Object files for the amalgamation.
|
||||
#
|
||||
@ -318,6 +319,7 @@ 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 \
|
||||
@ -463,6 +465,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/carray.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 \
|
||||
@ -524,6 +527,7 @@ 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 \
|
||||
@ -629,6 +633,7 @@ 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
|
||||
@ -637,10 +642,12 @@ 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 =
|
||||
|
||||
@ -710,6 +717,7 @@ DBFUZZ2_OPTS = \
|
||||
-DSQLITE_ENABLE_DESERIALIZE \
|
||||
-DSQLITE_DEBUG \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_FTS5
|
||||
@ -1041,6 +1049,9 @@ 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
|
||||
|
||||
@ -1094,6 +1105,12 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE)
|
||||
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
|
||||
@ -1102,10 +1119,13 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
SHELL_SRC = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/shathree.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 \
|
||||
@ -1247,6 +1267,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_SRC0 = $(TESTSRC2) libsqlcipher.la
|
||||
@ -1311,6 +1332,9 @@ 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
|
||||
|
||||
@ -1413,10 +1437,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
|
||||
amalgamation-tarball: sqlite3.c sqlite3rc.h
|
||||
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal
|
||||
|
||||
snapshot-tarball: sqlite3.c
|
||||
snapshot-tarball: sqlite3.c sqlite3rc.h
|
||||
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
|
||||
|
||||
# The next two rules are used to support the "threadtest" target. Building
|
||||
|
59
Makefile.msc
59
Makefile.msc
@ -234,6 +234,15 @@ 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.
|
||||
@ -357,6 +366,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_ENABLE_DESERIALIZE=1
|
||||
!ENDIF
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
@ -775,7 +785,7 @@ MKSQLITE3C_TOOL = $(TOP)\tool\mksqlite3c.tcl
|
||||
!ENDIF
|
||||
|
||||
!IFNDEF MKSQLITE3C_ARGS
|
||||
!IF $(DEBUG)>1
|
||||
!IF $(DEBUG)>1 && $(NO_LINEMACROS)==0
|
||||
MKSQLITE3C_ARGS = --linemacros
|
||||
!ELSE
|
||||
MKSQLITE3C_ARGS =
|
||||
@ -1246,7 +1256,8 @@ 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 wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
|
||||
vdbetrace.lo vdbevtab.lo wal.lo walker.lo where.lo wherecode.lo \
|
||||
whereexpr.lo \
|
||||
window.lo utf.lo vtab.lo
|
||||
# <</mark>>
|
||||
|
||||
@ -1361,6 +1372,7 @@ 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 \
|
||||
@ -1491,7 +1503,7 @@ SRC12 =
|
||||
|
||||
# All source code files.
|
||||
#
|
||||
SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11)
|
||||
SRC = $(SRC00) $(SRC01) $(SRC03) $(SRC04) $(SRC05) $(SRC06) $(SRC07) $(SRC08) $(SRC09) $(SRC10) $(SRC11) $(SRC12)
|
||||
|
||||
# Source code to the test files.
|
||||
#
|
||||
@ -1556,6 +1568,7 @@ TESTEXT = \
|
||||
$(TOP)\ext\misc\carray.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 \
|
||||
@ -1692,6 +1705,7 @@ 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
|
||||
@ -1841,15 +1855,16 @@ 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 $(MKSQLITE3C_TOOL)
|
||||
sqlite3.c: .target_source sqlite3ext.h sqlite3session.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
|
||||
@ -1864,7 +1879,8 @@ sqlite3.lo: $(SQLITE3C)
|
||||
# Rules to build the LEMON compiler generator
|
||||
#
|
||||
lempar.c: $(TOP)\tool\lempar.c
|
||||
copy $(TOP)\tool\lempar.c .
|
||||
copy /Y $(TOP)\tool\lempar.c .
|
||||
copy /B lempar.c +,,
|
||||
|
||||
lemon.exe: $(TOP)\tool\lemon.c lempar.c
|
||||
$(BCC) $(NO_WARN) -Daccess=_access \
|
||||
@ -2117,6 +2133,9 @@ 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
|
||||
|
||||
@ -2161,7 +2180,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 $(TOP)\src\parse.y .
|
||||
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
|
||||
|
||||
$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION
|
||||
@ -2174,8 +2194,13 @@ 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)
|
||||
@ -2187,9 +2212,12 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
SHELL_SRC = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\misc\appendvfs.c \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\completion.c \
|
||||
$(TOP)\ext\misc\decimal.c \
|
||||
$(TOP)\ext\misc\fileio.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\shathree.c \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\misc\memtrace.c \
|
||||
@ -2320,7 +2348,8 @@ LSM1_SRC = \
|
||||
$(TOP)\ext\lsm1\lsm_win32.c
|
||||
|
||||
fts5parse.c: $(TOP)\ext\fts5\fts5parse.y lemon.exe
|
||||
copy $(TOP)\ext\fts5\fts5parse.y .
|
||||
copy /Y $(TOP)\ext\fts5\fts5parse.y .
|
||||
copy /B fts5parse.y +,,
|
||||
del /Q fts5parse.h 2>NUL
|
||||
.\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) -S fts5parse.y
|
||||
|
||||
@ -2328,11 +2357,13 @@ fts5parse.h: fts5parse.c
|
||||
|
||||
fts5.c: $(FTS5_SRC)
|
||||
$(TCLSH_CMD) $(TOP)\ext\fts5\tool\mkfts5c.tcl
|
||||
copy $(TOP)\ext\fts5\fts5.h .
|
||||
copy /Y $(TOP)\ext\fts5\fts5.h .
|
||||
copy /B fts5.h +,,
|
||||
|
||||
lsm1.c: $(LSM1_SRC)
|
||||
$(TCLSH_CMD) $(TOP)\ext\lsm1\tool\mklsm1c.tcl
|
||||
copy $(TOP)\ext\lsm1\lsm.h .
|
||||
copy /Y $(TOP)\ext\lsm1\lsm.h .
|
||||
copy /B lsm.h +,,
|
||||
|
||||
fts5.lo: fts5.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) $(CORE_COMPILE_OPTS) $(NO_WARN) -DSQLITE_CORE -c fts5.c
|
||||
@ -2360,6 +2391,7 @@ 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) $(TEST_CCONV_OPTS)
|
||||
@ -2442,6 +2474,9 @@ 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 > $@
|
||||
|
||||
|
@ -15,10 +15,10 @@
|
||||
"requires_arc": false,
|
||||
"source": {
|
||||
"git": "https://github.com/sqlcipher/sqlcipher.git",
|
||||
"tag": "v4.4.0"
|
||||
"tag": "v4.4.1"
|
||||
},
|
||||
"summary": "Full Database Encryption for SQLite.",
|
||||
"version": "4.4.0",
|
||||
"version": "4.4.1",
|
||||
"subspecs": [
|
||||
{
|
||||
"compiler_flags": [
|
||||
|
55
aclocal.m4
vendored
55
aclocal.m4
vendored
@ -736,7 +736,6 @@ _LT_CONFIG_SAVE_COMMANDS([
|
||||
cat <<_LT_EOF >> "$cfgfile"
|
||||
#! $SHELL
|
||||
# Generated automatically by $as_me ($PACKAGE) $VERSION
|
||||
# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
|
||||
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
|
||||
|
||||
# Provide generalized library-building support services.
|
||||
@ -1048,8 +1047,8 @@ int forced_loaded() { return 2;}
|
||||
_LT_EOF
|
||||
echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
|
||||
$LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
|
||||
echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
|
||||
$AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
|
||||
echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
|
||||
$AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
|
||||
echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
|
||||
$RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
|
||||
cat > conftest.c << _LT_EOF
|
||||
@ -1499,7 +1498,7 @@ need_locks=$enable_libtool_lock
|
||||
m4_defun([_LT_PROG_AR],
|
||||
[AC_CHECK_TOOLS(AR, [ar], false)
|
||||
: ${AR=ar}
|
||||
: ${AR_FLAGS=cru}
|
||||
: ${AR_FLAGS=cr}
|
||||
_LT_DECL([], [AR], [1], [The archiver])
|
||||
_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
|
||||
|
||||
@ -2893,6 +2892,18 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
|
||||
dynamic_linker='GNU/Linux ld.so'
|
||||
;;
|
||||
|
||||
netbsdelf*-gnu)
|
||||
version_type=linux
|
||||
need_lib_prefix=no
|
||||
need_version=no
|
||||
library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
|
||||
soname_spec='${libname}${release}${shared_ext}$major'
|
||||
shlibpath_var=LD_LIBRARY_PATH
|
||||
shlibpath_overrides_runpath=no
|
||||
hardcode_into_libs=yes
|
||||
dynamic_linker='NetBSD ld.elf_so'
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
version_type=sunos
|
||||
need_lib_prefix=no
|
||||
@ -3552,7 +3563,7 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
|
||||
lt_cv_deplibs_check_method=pass_all
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
|
||||
lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
|
||||
else
|
||||
@ -4058,7 +4069,8 @@ _LT_EOF
|
||||
if AC_TRY_EVAL(ac_compile); then
|
||||
# Now try to grab the symbols.
|
||||
nlist=conftest.nm
|
||||
if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
|
||||
$ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD
|
||||
if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then
|
||||
# Try sorting and uniquifying the output.
|
||||
if sort "$nlist" | uniq > "$nlist"T; then
|
||||
mv -f "$nlist"T "$nlist"
|
||||
@ -4430,7 +4442,7 @@ m4_if([$1], [CXX], [
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
;;
|
||||
*qnx* | *nto*)
|
||||
# QNX uses GNU C++, but need to define -shared option too, otherwise
|
||||
@ -4698,6 +4710,12 @@ m4_if([$1], [CXX], [
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
|
||||
;;
|
||||
# flang / f18. f95 an alias for gfortran or flang on Debian
|
||||
flang* | f18* | f95*)
|
||||
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
|
||||
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
|
||||
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
|
||||
;;
|
||||
# icc used to be incompatible with GCC.
|
||||
# ICC 10 doesn't accept -KPIC any more.
|
||||
icc* | ifort*)
|
||||
@ -4942,6 +4960,9 @@ m4_if([$1], [CXX], [
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
linux* | k*bsd*-gnu | gnu*)
|
||||
_LT_TAGVAR(link_all_deplibs, $1)=no
|
||||
;;
|
||||
*)
|
||||
_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
|
||||
;;
|
||||
@ -5004,6 +5025,9 @@ dnl Note also adjust exclude_expsyms for C++ above.
|
||||
openbsd* | bitrig*)
|
||||
with_gnu_ld=no
|
||||
;;
|
||||
linux* | k*bsd*-gnu | gnu*)
|
||||
_LT_TAGVAR(link_all_deplibs, $1)=no
|
||||
;;
|
||||
esac
|
||||
|
||||
_LT_TAGVAR(ld_shlibs, $1)=yes
|
||||
@ -5258,7 +5282,7 @@ _LT_EOF
|
||||
fi
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
|
||||
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
|
||||
wlarc=
|
||||
@ -5779,6 +5803,7 @@ _LT_EOF
|
||||
if test yes = "$lt_cv_irix_exported_symbol"; then
|
||||
_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
|
||||
fi
|
||||
_LT_TAGVAR(link_all_deplibs, $1)=no
|
||||
else
|
||||
_LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
|
||||
_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
|
||||
@ -5800,7 +5825,7 @@ _LT_EOF
|
||||
esac
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
|
||||
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
|
||||
else
|
||||
@ -6422,7 +6447,7 @@ if test yes != "$_lt_caught_CXX_error"; then
|
||||
# Commands to make compiler produce verbose output that lists
|
||||
# what "hidden" libraries, object files and flags are used when
|
||||
# linking a shared library.
|
||||
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
|
||||
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
|
||||
|
||||
else
|
||||
GXX=no
|
||||
@ -6797,7 +6822,7 @@ if test yes != "$_lt_caught_CXX_error"; then
|
||||
# explicitly linking system object files so we need to strip them
|
||||
# from the output so that they don't get included in the library
|
||||
# dependencies.
|
||||
output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
|
||||
output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
|
||||
;;
|
||||
*)
|
||||
if test yes = "$GXX"; then
|
||||
@ -6862,7 +6887,7 @@ if test yes != "$_lt_caught_CXX_error"; then
|
||||
# explicitly linking system object files so we need to strip them
|
||||
# from the output so that they don't get included in the library
|
||||
# dependencies.
|
||||
output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
|
||||
output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
|
||||
;;
|
||||
*)
|
||||
if test yes = "$GXX"; then
|
||||
@ -7201,7 +7226,7 @@ if test yes != "$_lt_caught_CXX_error"; then
|
||||
# Commands to make compiler produce verbose output that lists
|
||||
# what "hidden" libraries, object files and flags are used when
|
||||
# linking a shared library.
|
||||
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
|
||||
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
|
||||
|
||||
else
|
||||
# FIXME: insert proper C++ library support
|
||||
@ -7285,7 +7310,7 @@ if test yes != "$_lt_caught_CXX_error"; then
|
||||
# Commands to make compiler produce verbose output that lists
|
||||
# what "hidden" libraries, object files and flags are used when
|
||||
# linking a shared library.
|
||||
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
|
||||
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
|
||||
else
|
||||
# g++ 2.7 appears to require '-G' NOT '-shared' on this
|
||||
# platform.
|
||||
@ -7296,7 +7321,7 @@ if test yes != "$_lt_caught_CXX_error"; then
|
||||
# Commands to make compiler produce verbose output that lists
|
||||
# what "hidden" libraries, object files and flags are used when
|
||||
# linking a shared library.
|
||||
output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
|
||||
output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
|
||||
fi
|
||||
|
||||
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
|
||||
|
@ -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 README.txt Replace.cs Makefile.fallback
|
||||
EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc sqlite3rc.h README.txt Replace.cs Makefile.fallback
|
||||
pkgconfigdir = ${libdir}/pkgconfig
|
||||
pkgconfig_DATA = sqlite3.pc
|
||||
|
||||
|
@ -196,6 +196,7 @@ 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.
|
||||
@ -288,6 +289,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_ENABLE_DESERIALIZE=1
|
||||
!ENDIF
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
|
@ -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.31.0])
|
||||
AC_INIT([sqlite], [3.32.0])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
88
configure
vendored
88
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.31.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlcipher 3.33.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@ -587,8 +587,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlcipher'
|
||||
PACKAGE_TARNAME='sqlcipher'
|
||||
PACKAGE_VERSION='3.31.0'
|
||||
PACKAGE_STRING='sqlcipher 3.31.0'
|
||||
PACKAGE_VERSION='3.33.0'
|
||||
PACKAGE_STRING='sqlcipher 3.33.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -729,6 +729,7 @@ infodir
|
||||
docdir
|
||||
oldincludedir
|
||||
includedir
|
||||
runstatedir
|
||||
localstatedir
|
||||
sharedstatedir
|
||||
sysconfdir
|
||||
@ -836,6 +837,7 @@ datadir='${datarootdir}'
|
||||
sysconfdir='${prefix}/etc'
|
||||
sharedstatedir='${prefix}/com'
|
||||
localstatedir='${prefix}/var'
|
||||
runstatedir='${localstatedir}/run'
|
||||
includedir='${prefix}/include'
|
||||
oldincludedir='/usr/include'
|
||||
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
|
||||
@ -1088,6 +1090,15 @@ do
|
||||
| -silent | --silent | --silen | --sile | --sil)
|
||||
silent=yes ;;
|
||||
|
||||
-runstatedir | --runstatedir | --runstatedi | --runstated \
|
||||
| --runstate | --runstat | --runsta | --runst | --runs \
|
||||
| --run | --ru | --r)
|
||||
ac_prev=runstatedir ;;
|
||||
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
|
||||
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
|
||||
| --run=* | --ru=* | --r=*)
|
||||
runstatedir=$ac_optarg ;;
|
||||
|
||||
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||
ac_prev=sbindir ;;
|
||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||
@ -1225,7 +1236,7 @@ fi
|
||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||
libdir localedir mandir
|
||||
libdir localedir mandir runstatedir
|
||||
do
|
||||
eval ac_val=\$$ac_var
|
||||
# Remove trailing slashes.
|
||||
@ -1338,7 +1349,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 sqlcipher 3.31.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlcipher 3.33.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1378,6 +1389,7 @@ Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||
--includedir=DIR C header files [PREFIX/include]
|
||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||
@ -1403,7 +1415,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.31.0:";;
|
||||
short | recursive ) echo "Configuration of sqlcipher 3.33.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1540,7 +1552,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlcipher configure 3.31.0
|
||||
sqlcipher configure 3.33.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@ -1959,7 +1971,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 sqlcipher $as_me 3.31.0, which was
|
||||
It was created by sqlcipher $as_me 3.33.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -4446,7 +4458,7 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
|
||||
lt_cv_deplibs_check_method=pass_all
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
|
||||
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
|
||||
else
|
||||
@ -4809,7 +4821,7 @@ esac
|
||||
fi
|
||||
|
||||
: ${AR=ar}
|
||||
: ${AR_FLAGS=cru}
|
||||
: ${AR_FLAGS=cr}
|
||||
|
||||
|
||||
|
||||
@ -5352,11 +5364,8 @@ _LT_EOF
|
||||
test $ac_status = 0; }; then
|
||||
# Now try to grab the symbols.
|
||||
nlist=conftest.nm
|
||||
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
|
||||
(eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; } && test -s "$nlist"; then
|
||||
$ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5
|
||||
if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then
|
||||
# Try sorting and uniquifying the output.
|
||||
if sort "$nlist" | uniq > "$nlist"T; then
|
||||
mv -f "$nlist"T "$nlist"
|
||||
@ -6575,8 +6584,8 @@ int forced_loaded() { return 2;}
|
||||
_LT_EOF
|
||||
echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
|
||||
$LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
|
||||
echo "$AR cru libconftest.a conftest.o" >&5
|
||||
$AR cru libconftest.a conftest.o 2>&5
|
||||
echo "$AR cr libconftest.a conftest.o" >&5
|
||||
$AR cr libconftest.a conftest.o 2>&5
|
||||
echo "$RANLIB libconftest.a" >&5
|
||||
$RANLIB libconftest.a 2>&5
|
||||
cat > conftest.c << _LT_EOF
|
||||
@ -7701,6 +7710,12 @@ lt_prog_compiler_static=
|
||||
lt_prog_compiler_pic='-KPIC'
|
||||
lt_prog_compiler_static='-static'
|
||||
;;
|
||||
# flang / f18. f95 an alias for gfortran or flang on Debian
|
||||
flang* | f18* | f95*)
|
||||
lt_prog_compiler_wl='-Wl,'
|
||||
lt_prog_compiler_pic='-fPIC'
|
||||
lt_prog_compiler_static='-static'
|
||||
;;
|
||||
# icc used to be incompatible with GCC.
|
||||
# ICC 10 doesn't accept -KPIC any more.
|
||||
icc* | ifort*)
|
||||
@ -8177,6 +8192,9 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
|
||||
openbsd* | bitrig*)
|
||||
with_gnu_ld=no
|
||||
;;
|
||||
linux* | k*bsd*-gnu | gnu*)
|
||||
link_all_deplibs=no
|
||||
;;
|
||||
esac
|
||||
|
||||
ld_shlibs=yes
|
||||
@ -8431,7 +8449,7 @@ _LT_EOF
|
||||
fi
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
|
||||
archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
|
||||
wlarc=
|
||||
@ -9101,6 +9119,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
|
||||
if test yes = "$lt_cv_irix_exported_symbol"; then
|
||||
archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
|
||||
fi
|
||||
link_all_deplibs=no
|
||||
else
|
||||
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
|
||||
archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
|
||||
@ -9122,7 +9141,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
|
||||
esac
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
netbsd* | netbsdelf*-gnu)
|
||||
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
|
||||
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
|
||||
else
|
||||
@ -10237,6 +10256,18 @@ fi
|
||||
dynamic_linker='GNU/Linux ld.so'
|
||||
;;
|
||||
|
||||
netbsdelf*-gnu)
|
||||
version_type=linux
|
||||
need_lib_prefix=no
|
||||
need_version=no
|
||||
library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
|
||||
soname_spec='${libname}${release}${shared_ext}$major'
|
||||
shlibpath_var=LD_LIBRARY_PATH
|
||||
shlibpath_overrides_runpath=no
|
||||
hardcode_into_libs=yes
|
||||
dynamic_linker='NetBSD ld.elf_so'
|
||||
;;
|
||||
|
||||
netbsd*)
|
||||
version_type=sunos
|
||||
need_lib_prefix=no
|
||||
@ -11366,7 +11397,7 @@ else
|
||||
We can't simply define LARGE_OFF_T to be 9223372036854775807,
|
||||
since some C++ compilers masquerading as C compilers
|
||||
incorrectly reject 9223372036854775807. */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
|
||||
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
|
||||
&& LARGE_OFF_T % 2147483647 == 1)
|
||||
? 1 : -1];
|
||||
@ -11412,7 +11443,7 @@ else
|
||||
We can't simply define LARGE_OFF_T to be 9223372036854775807,
|
||||
since some C++ compilers masquerading as C compilers
|
||||
incorrectly reject 9223372036854775807. */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
|
||||
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
|
||||
&& LARGE_OFF_T % 2147483647 == 1)
|
||||
? 1 : -1];
|
||||
@ -11436,7 +11467,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
We can't simply define LARGE_OFF_T to be 9223372036854775807,
|
||||
since some C++ compilers masquerading as C compilers
|
||||
incorrectly reject 9223372036854775807. */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
|
||||
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
|
||||
&& LARGE_OFF_T % 2147483647 == 1)
|
||||
? 1 : -1];
|
||||
@ -11481,7 +11512,7 @@ else
|
||||
We can't simply define LARGE_OFF_T to be 9223372036854775807,
|
||||
since some C++ compilers masquerading as C compilers
|
||||
incorrectly reject 9223372036854775807. */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
|
||||
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
|
||||
&& LARGE_OFF_T % 2147483647 == 1)
|
||||
? 1 : -1];
|
||||
@ -11505,7 +11536,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
We can't simply define LARGE_OFF_T to be 9223372036854775807,
|
||||
since some C++ compilers masquerading as C compilers
|
||||
incorrectly reject 9223372036854775807. */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
|
||||
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
|
||||
&& LARGE_OFF_T % 2147483647 == 1)
|
||||
? 1 : -1];
|
||||
@ -12844,7 +12875,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
|
||||
|
||||
@ -13195,7 +13226,7 @@ if test "${enable_update_limit+set}" = set; then :
|
||||
enableval=$enable_update_limit;
|
||||
fi
|
||||
|
||||
if test "${enable_udlimit}" = "yes" ; then
|
||||
if test "${enable_update_limit}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
|
||||
fi
|
||||
|
||||
@ -13819,7 +13850,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 sqlcipher $as_me 3.31.0, which was
|
||||
This file was extended by sqlcipher $as_me 3.33.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -13885,7 +13916,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="\\
|
||||
sqlcipher config.status 3.31.0
|
||||
sqlcipher config.status 3.33.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
@ -14869,7 +14900,6 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
|
||||
cat <<_LT_EOF >> "$cfgfile"
|
||||
#! $SHELL
|
||||
# Generated automatically by $as_me ($PACKAGE) $VERSION
|
||||
# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
|
||||
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
|
||||
|
||||
# Provide generalized library-building support services.
|
||||
|
@ -624,7 +624,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)
|
||||
@ -706,7 +706,7 @@ fi
|
||||
# statements.
|
||||
AC_ARG_ENABLE(update-limit, AC_HELP_STRING([--enable-update-limit],
|
||||
[Enable the UPDATE/DELETE LIMIT clause]))
|
||||
if test "${enable_udlimit}" = "yes" ; then
|
||||
if test "${enable_update_limit}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
|
||||
fi
|
||||
|
||||
|
@ -104,9 +104,13 @@ Write all output files into <i>directory</i>. Normally, output files
|
||||
are written into the directory that contains the input grammar file.
|
||||
<li><b>-D<i>name</i></b>
|
||||
Define C preprocessor macro <i>name</i>. This macro is usable by
|
||||
"<tt><a href='#pifdef'>%ifdef</a></tt>" and
|
||||
"<tt><a href='#pifdef'>%ifndef</a></tt>" lines
|
||||
"<tt><a href='#pifdef'>%ifdef</a></tt>",
|
||||
"<tt><a href='#pifdef'>%ifndef</a></tt>", and
|
||||
"<tt><a href="#pifdef">%if</a></tt> lines
|
||||
in the grammar file.
|
||||
<li><b>-E</b>
|
||||
Run the "%if" preprocessor step only and print the revised grammar
|
||||
file.
|
||||
<li><b>-g</b>
|
||||
Do not generate a parser. Instead write the input grammar to standard
|
||||
output with all comments, actions, and other extraneous text removed.
|
||||
@ -123,7 +127,7 @@ Suppress generation of the report file.
|
||||
<li><b>-r</b>
|
||||
Do not sort or renumber the parser states as part of optimization.
|
||||
<li><b>-s</b>
|
||||
Show parser statistics before existing.
|
||||
Show parser statistics before exiting.
|
||||
<li><b>-T<i>file</i></b>
|
||||
Use <i>file</i> as the template for the generated C-code parser implementation.
|
||||
<li><b>-x</b>
|
||||
@ -488,7 +492,7 @@ is an error.</p>
|
||||
The precedence of a grammar rule is equal to the precedence of the
|
||||
left-most terminal symbol in the rule for which a precedence is
|
||||
defined. This is normally what you want, but in those cases where
|
||||
you want to precedence of a grammar rule to be something different,
|
||||
you want the precedence of a grammar rule to be something different,
|
||||
you can specify an alternative precedence symbol by putting the
|
||||
symbol in square braces after the period at the end of the rule and
|
||||
before any C-code. For example:</p>
|
||||
@ -555,9 +559,11 @@ other than that, the order of directives in Lemon is arbitrary.</p>
|
||||
<li><tt><a href='#default_destructor'>%default_destructor</a></tt>
|
||||
<li><tt><a href='#default_type'>%default_type</a></tt>
|
||||
<li><tt><a href='#destructor'>%destructor</a></tt>
|
||||
<li><tt><a href='#pifdef'>%else</a></tt>
|
||||
<li><tt><a href='#pifdef'>%endif</a></tt>
|
||||
<li><tt><a href='#extraarg'>%extra_argument</a></tt>
|
||||
<li><tt><a href='#pfallback'>%fallback</a></tt>
|
||||
<li><tt><a href='#pifdef'>%if</a></tt>
|
||||
<li><tt><a href='#pifdef'>%ifdef</a></tt>
|
||||
<li><tt><a href='#pifdef'>%ifndef</a></tt>
|
||||
<li><tt><a href='#pinclude'>%include</a></tt>
|
||||
@ -689,7 +695,7 @@ on Parse().
|
||||
<a name='extractx'></a>
|
||||
<h4>The <tt>%extra_context</tt> directive</h4>
|
||||
|
||||
The <tt>%extra_context</tt> directive instructs Lemon to add a 2th parameter
|
||||
The <tt>%extra_context</tt> directive instructs Lemon to add a 2nd parameter
|
||||
to the parameter list of the ParseAlloc() and ParseInif() functions. Lemon
|
||||
doesn't do anything itself with these extra argument, but it does
|
||||
store the value make it available to C-code action routines, destructors,
|
||||
@ -699,9 +705,9 @@ and so forth. For example, if the grammar file contains:</p>
|
||||
%extra_context { MyStruct *pAbc }
|
||||
</pre></p>
|
||||
|
||||
<p>Then the ParseAlloc() and ParseInit() functions will have an 2th parameter
|
||||
<p>Then the ParseAlloc() and ParseInit() functions will have an 2nd parameter
|
||||
of type "MyStruct*" and all action routines will have access to
|
||||
a variable named "pAbc" that is the value of that 2th parameter.</p>
|
||||
a variable named "pAbc" that is the value of that 2nd parameter.</p>
|
||||
|
||||
<p>The <tt>%extra_argument</tt> directive works the same except that it
|
||||
is passed in on the Parse() routine instead of on ParseAlloc()/ParseInit().
|
||||
@ -737,10 +743,11 @@ arguments are tokens which fall back to the token identified by the first
|
||||
argument.</p>
|
||||
|
||||
<a name='pifdef'></a>
|
||||
<h4>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives</h4>
|
||||
<h4>The <tt>%if</tt> directive and its friends</h4>
|
||||
|
||||
<p>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives
|
||||
are similar to #ifdef, #ifndef, and #endif in the C-preprocessor,
|
||||
<p>The <tt>%if</tt>, <tt>%ifdef</tt>, <tt>%ifndef</tt>, <tt>%else</tt>,
|
||||
and <tt>%endif</tt> directives
|
||||
are similar to #if, #ifdef, #ifndef, #else, and #endif in the C-preprocessor,
|
||||
just not as general.
|
||||
Each of these directives must begin at the left margin. No whitespace
|
||||
is allowed between the "%" and the directive name.</p>
|
||||
@ -749,12 +756,22 @@ is allowed between the "%" and the directive name.</p>
|
||||
"<tt>%endif</tt>" is
|
||||
ignored unless the "-DMACRO" command-line option is used. Grammar text
|
||||
betwen "<tt>%ifndef MACRO</tt>" and the next nested "<tt>%endif</tt>" is
|
||||
included except when the "-DMACRO" command-line option is used.</p>
|
||||
included except when the "-DMACRO" command-line option is used.<p>
|
||||
|
||||
<p>Note that the argument to <tt>%ifdef</tt> and <tt>%ifndef</tt> must
|
||||
be a single preprocessor symbol name, not a general expression.
|
||||
There is no "<tt>%else</tt>" directive.</p>
|
||||
<p>The text in between "<tt>%if</tt> <i>CONDITIONAL</i>" and its
|
||||
corresponding <tt>%endif</tt> is included only if <i>CONDITIONAL</i>
|
||||
is true. The CONDITION is one or more macro names, optionally connected
|
||||
using the "||" and "&&" binary operators, the "!" unary operator,
|
||||
and grouped using balanced parentheses. Each term is true if the
|
||||
corresponding macro exists, and false if it does not exist.</p>
|
||||
|
||||
<p>An optional "<tt>%else</tt>" directive can occur anywhere in between a
|
||||
<tt>%ifdef</tt>, <tt>%ifndef</tt>, or <tt>%if</tt> directive and
|
||||
its corresponding <tt>%endif</tt>.</p>
|
||||
|
||||
<p>Note that the argument to <tt>%ifdef</tt> and <tt>%ifndef</tt> is
|
||||
intended to be a single preprocessor symbol name, not a general expression.
|
||||
Use the "<tt>%if</tt>" directive for general expressions.</p>
|
||||
|
||||
<a name='pinclude'></a>
|
||||
<h4>The <tt>%include</tt> directive</h4>
|
||||
@ -996,7 +1013,7 @@ on the parser's stack associated with terminal and non-terminal
|
||||
symbols. The values of all terminal symbols must be of the same
|
||||
type. This turns out to be the same data type as the 3rd parameter
|
||||
to the Parse() function generated by Lemon. Typically, you will
|
||||
make the value of a terminal symbol by a pointer to some kind of
|
||||
make the value of a terminal symbol be a pointer to some kind of
|
||||
token structure. Like this:</p>
|
||||
|
||||
<p><pre>
|
||||
|
88
doc/wal-lock.md
Normal file
88
doc/wal-lock.md
Normal file
@ -0,0 +1,88 @@
|
||||
# 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.
|
||||
|
||||
|
@ -1704,4 +1704,3 @@ int sqlite3async_control(int op, ...){
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ASYNCIO) */
|
||||
|
||||
|
@ -220,4 +220,3 @@ int sqlite3async_control(int op, ...);
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* ifndef __SQLITEASYNC_H_ */
|
||||
|
||||
|
@ -37,6 +37,9 @@ 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]
|
||||
}
|
||||
@ -76,6 +79,10 @@ 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} {
|
||||
@ -336,7 +343,7 @@ proc do_candidates_test {tn sql res} {
|
||||
|
||||
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(c, d);
|
||||
|
||||
@ -346,7 +353,7 @@ do_execsql_test 4.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 4.1 {
|
||||
do_candidates_test 5.1 {
|
||||
SELECT * FROM t1,t2 WHERE (b=? OR a=?) AND (c=? OR d=?)
|
||||
} {
|
||||
CREATE INDEX t1_idx_00000062 ON t1(b); -- stat1: 100 20
|
||||
@ -355,14 +362,14 @@ do_candidates_test 4.1 {
|
||||
CREATE INDEX t2_idx_00000064 ON t2(d); -- stat1: 100 5
|
||||
}
|
||||
|
||||
do_candidates_test 4.2 {
|
||||
do_candidates_test 5.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 4.3 {
|
||||
do_execsql_test 5.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
|
||||
|
@ -1128,14 +1128,19 @@ 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 = STRLEN(zDetail);
|
||||
int nDetail;
|
||||
int i;
|
||||
|
||||
if( !zDetail ) continue;
|
||||
nDetail = STRLEN(zDetail);
|
||||
|
||||
for(i=0; i<nDetail; i++){
|
||||
const char *zIdx = 0;
|
||||
if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
|
||||
if( i+13<nDetail && memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){
|
||||
zIdx = &zDetail[i+13];
|
||||
}else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){
|
||||
}else if( i+22<nDetail
|
||||
&& memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0
|
||||
){
|
||||
zIdx = &zDetail[i+22];
|
||||
}
|
||||
if( zIdx ){
|
||||
@ -1218,7 +1223,7 @@ static int idxProcessOneTrigger(
|
||||
IdxTable *pTab = pWrite->pTab;
|
||||
const char *zTab = pTab->zName;
|
||||
const char *zSql =
|
||||
"SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_master "
|
||||
"SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema "
|
||||
"WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
|
||||
"ORDER BY type;";
|
||||
sqlite3_stmt *pSelect = 0;
|
||||
@ -1318,12 +1323,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_master "
|
||||
"SELECT type, name, sql, 1 FROM sqlite_schema "
|
||||
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%%' "
|
||||
" UNION ALL "
|
||||
"SELECT type, name, sql, 2 FROM sqlite_master "
|
||||
"SELECT type, name, sql, 2 FROM sqlite_schema "
|
||||
"WHERE type = 'trigger'"
|
||||
" AND tbl_name IN(SELECT name FROM sqlite_master WHERE type = 'view') "
|
||||
" AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
|
||||
"ORDER BY 4, 1"
|
||||
);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
|
||||
@ -1493,7 +1498,7 @@ static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
|
||||
int rc = SQLITE_OK;
|
||||
const char *zMax =
|
||||
"SELECT max(i.seqno) FROM "
|
||||
" sqlite_master AS s, "
|
||||
" sqlite_schema AS s, "
|
||||
" pragma_index_list(s.name) AS l, "
|
||||
" pragma_index_info(l.name) AS i "
|
||||
"WHERE s.type = 'table'";
|
||||
@ -1646,7 +1651,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
|
||||
const char *zAllIndex =
|
||||
"SELECT s.rowid, s.name, l.name FROM "
|
||||
" sqlite_master AS s, "
|
||||
" sqlite_schema AS s, "
|
||||
" pragma_index_list(s.name) AS l "
|
||||
"WHERE s.type = 'table'";
|
||||
const char *zIndexXInfo =
|
||||
@ -1720,7 +1725,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
sqlite3_free(pCtx);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_master", 0, 0, 0);
|
||||
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
|
||||
}
|
||||
|
||||
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
|
||||
@ -1759,7 +1764,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_master WHERE name NOT LIKE 'sqlite_%%'"
|
||||
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
|
||||
" AND sql NOT LIKE 'CREATE VIRTUAL %%'"
|
||||
);
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
|
||||
@ -1950,4 +1955,4 @@ void sqlite3_expert_destroy(sqlite3expert *p){
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -174,5 +174,3 @@ 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.
|
||||
|
||||
|
||||
|
@ -962,6 +962,22 @@ 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
|
||||
@ -977,19 +993,17 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
|
||||
*/
|
||||
static int fts3GobbleInt(const char **pp, int *pnOut){
|
||||
const int MAX_NPREFIX = 10000000;
|
||||
const char *p; /* Iterator pointer */
|
||||
int nInt = 0; /* Output value */
|
||||
|
||||
for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
|
||||
nInt = nInt * 10 + (p[0] - '0');
|
||||
if( nInt>MAX_NPREFIX ){
|
||||
nInt = 0;
|
||||
break;
|
||||
}
|
||||
int nByte;
|
||||
nByte = sqlite3Fts3ReadInt(*pp, &nInt);
|
||||
if( nInt>MAX_NPREFIX ){
|
||||
nInt = 0;
|
||||
}
|
||||
if( nByte==0 ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( p==*pp ) return SQLITE_ERROR;
|
||||
*pnOut = nInt;
|
||||
*pp = p;
|
||||
*pp += nByte;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -1884,6 +1898,7 @@ 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
|
||||
@ -1908,12 +1923,15 @@ 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);
|
||||
@ -2050,7 +2068,7 @@ static void fts3PutDeltaVarint(
|
||||
sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */
|
||||
sqlite3_int64 iVal /* Write this value to the list */
|
||||
){
|
||||
assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
|
||||
assert_fts3_nc( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
|
||||
*pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
|
||||
*piPrev = iVal;
|
||||
}
|
||||
@ -2167,7 +2185,9 @@ static void fts3ReadNextPos(
|
||||
sqlite3_int64 *pi /* IN/OUT: Value read from position-list */
|
||||
){
|
||||
if( (**pp)&0xFE ){
|
||||
fts3GetDeltaVarint(pp, pi);
|
||||
int iVal;
|
||||
*pp += fts3GetVarint32((*pp), &iVal);
|
||||
*pi += iVal;
|
||||
*pi -= 2;
|
||||
}else{
|
||||
*pi = POSITION_LIST_END;
|
||||
@ -2247,6 +2267,9 @@ 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;
|
||||
@ -2315,7 +2338,7 @@ static int fts3PoslistPhraseMerge(
|
||||
/* Never set both isSaveLeft and isExact for the same invocation. */
|
||||
assert( isSaveLeft==0 || isExact==0 );
|
||||
|
||||
assert( p!=0 && *p1!=0 && *p2!=0 );
|
||||
assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
|
||||
if( *p1==POS_COLUMN ){
|
||||
p1++;
|
||||
p1 += fts3GetVarint32(p1, &iCol1);
|
||||
@ -3456,7 +3479,7 @@ static int fts3ColumnMethod(
|
||||
break;
|
||||
}else{
|
||||
iCol = p->nColumn;
|
||||
/* fall-through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
|
||||
default:
|
||||
@ -3699,9 +3722,13 @@ 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 ){
|
||||
@ -4500,7 +4527,7 @@ void sqlite3Fts3DoclistNext(
|
||||
|
||||
assert( nDoclist>0 );
|
||||
assert( *pbEof==0 );
|
||||
assert( p || *piDocid==0 );
|
||||
assert_fts3_nc( p || *piDocid==0 );
|
||||
assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) );
|
||||
|
||||
if( p==0 ){
|
||||
@ -5150,7 +5177,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".
|
||||
@ -5185,10 +5212,12 @@ static int fts3EvalNearTrim(
|
||||
);
|
||||
if( res ){
|
||||
nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
|
||||
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;
|
||||
if( nNew>=0 ){
|
||||
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;
|
||||
}
|
||||
@ -5297,6 +5326,7 @@ static void fts3EvalNextRow(
|
||||
fts3EvalNextRow(pCsr, pLeft, pRc);
|
||||
}
|
||||
}
|
||||
pRight->bEof = pLeft->bEof = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -5539,7 +5569,10 @@ static int fts3EvalTestExpr(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
|
||||
bHit = (
|
||||
pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId
|
||||
&& pExpr->pPhrase->doclist.nList>0
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -5802,7 +5835,8 @@ static int fts3EvalGatherStats(
|
||||
fts3EvalRestart(pCsr, pRoot, &rc);
|
||||
do {
|
||||
fts3EvalNextRow(pCsr, pRoot, &rc);
|
||||
assert( pRoot->bEof==0 );
|
||||
assert_fts3_nc( pRoot->bEof==0 );
|
||||
if( pRoot->bEof ) rc = FTS_CORRUPT_VTAB;
|
||||
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +199,8 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
|
||||
|
||||
#define deliberate_fall_through
|
||||
|
||||
#endif /* SQLITE_AMALGAMATION */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
@ -591,6 +593,7 @@ 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 *);
|
||||
|
@ -446,10 +446,7 @@ static int getNextNode(
|
||||
if( pKey->eType==FTSQUERY_NEAR ){
|
||||
assert( nKey==4 );
|
||||
if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
|
||||
nNear = 0;
|
||||
for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
|
||||
nNear = nNear * 10 + (zInput[nKey] - '0');
|
||||
}
|
||||
nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -876,7 +876,7 @@ static int fts3ExprLHits(
|
||||
iStart = pExpr->iPhrase * ((p->nCol + 31) / 32);
|
||||
}
|
||||
|
||||
while( 1 ){
|
||||
if( pIter ) while( 1 ){
|
||||
int nHit = fts3ColumnlistCount(&pIter);
|
||||
if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
|
||||
if( p->flag==FTS3_MATCHINFO_LHITS ){
|
||||
|
@ -188,7 +188,8 @@ static int fts3tokConnectMethod(
|
||||
|
||||
assert( (rc==SQLITE_OK)==(pMod!=0) );
|
||||
if( rc==SQLITE_OK ){
|
||||
const char * const *azArg = (const char * const *)&azDequote[1];
|
||||
const char * const *azArg = 0;
|
||||
if( nDequote>1 ) azArg = (const char * const *)&azDequote[1];
|
||||
rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok);
|
||||
}
|
||||
|
||||
@ -350,7 +351,7 @@ static int fts3tokFilterMethod(
|
||||
if( pCsr->zInput==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memcpy(pCsr->zInput, zByte, nByte);
|
||||
if( nByte>0 ) memcpy(pCsr->zInput, zByte, nByte);
|
||||
pCsr->zInput[nByte] = 0;
|
||||
rc = pTab->pMod->xOpen(pTab->pTok, pCsr->zInput, nByte, &pCsr->pCsr);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
@ -341,7 +341,9 @@ static int fts3SqlStmt(
|
||||
** 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 %Q.'%q_segdir' WHERE level = ? AND idx < ?",
|
||||
" FROM (SELECT * FROM %Q.'%q_segdir' "
|
||||
" WHERE level = ? ORDER BY idx ASC LIMIT ?"
|
||||
" )",
|
||||
|
||||
/* SQL_DELETE_SEGDIR_ENTRY
|
||||
** Delete the %_segdir entry on absolute level :1 with index :2. */
|
||||
@ -1415,6 +1417,7 @@ 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;
|
||||
}
|
||||
@ -2502,7 +2505,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,
|
||||
((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
|
||||
(((u64)iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
|
||||
);
|
||||
|
||||
*pbMax = 0;
|
||||
@ -2852,6 +2855,19 @@ 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 */
|
||||
@ -2986,15 +3002,9 @@ int sqlite3Fts3SegReaderStep(
|
||||
}
|
||||
|
||||
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
|
||||
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;
|
||||
}
|
||||
|
||||
rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist);
|
||||
if( rc ) return rc;
|
||||
|
||||
if( isFirst ){
|
||||
char *a = &pCsr->aBuffer[nDoclist];
|
||||
@ -3019,6 +3029,9 @@ 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;
|
||||
@ -3068,11 +3081,11 @@ static void fts3ReadEndBlockField(
|
||||
if( zText ){
|
||||
int i;
|
||||
int iMul = 1;
|
||||
i64 iVal = 0;
|
||||
u64 iVal = 0;
|
||||
for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
|
||||
iVal = iVal*10 + (zText[i] - '0');
|
||||
}
|
||||
*piEndBlock = iVal;
|
||||
*piEndBlock = (i64)iVal;
|
||||
while( zText[i]==' ' ) i++;
|
||||
iVal = 0;
|
||||
if( zText[i]=='-' ){
|
||||
@ -3082,7 +3095,7 @@ static void fts3ReadEndBlockField(
|
||||
for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
|
||||
iVal = iVal*10 + (zText[i] - '0');
|
||||
}
|
||||
*pnByte = (iVal * (i64)iMul);
|
||||
*pnByte = ((i64)iVal * (i64)iMul);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4287,7 +4300,7 @@ static int fts3IncrmergeLoad(
|
||||
int i;
|
||||
int nHeight = (int)aRoot[0];
|
||||
NodeWriter *pNode;
|
||||
if( nHeight<1 || nHeight>FTS_MAX_APPENDABLE_HEIGHT ){
|
||||
if( nHeight<1 || nHeight>=FTS_MAX_APPENDABLE_HEIGHT ){
|
||||
sqlite3_reset(pSelect);
|
||||
return FTS_CORRUPT_VTAB;
|
||||
}
|
||||
@ -4953,6 +4966,12 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
||||
** Exit early in this case. */
|
||||
if( nSeg<=0 ) break;
|
||||
|
||||
assert( nMod<=0x7FFFFFFF );
|
||||
if( iAbsLevel<0 || iAbsLevel>(nMod<<32) ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Open a cursor to iterate through the contents of the oldest nSeg
|
||||
** indexes of absolute level iAbsLevel. If this cursor is opened using
|
||||
** the 'hint' parameters, it is possible that there are less than nSeg
|
||||
|
@ -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_master"
|
||||
"SELECT sql FROM sqlite_schema"
|
||||
" 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_master a, sqlite_master b"
|
||||
" FROM sqlite_schema a, sqlite_schema b"
|
||||
" WHERE a.name GLOB '*_segdir'"
|
||||
" AND b.name=substr(a.name,1,length(a.name)-7)"
|
||||
" ORDER BY 1");
|
||||
|
@ -2321,11 +2321,11 @@ static void fts5LeafSeek(
|
||||
}
|
||||
|
||||
search_success:
|
||||
pIter->iLeafOffset = iOff + nNew;
|
||||
if( pIter->iLeafOffset>n || nNew<1 ){
|
||||
if( (i64)iOff+nNew>n || nNew<1 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
}
|
||||
pIter->iLeafOffset = iOff + nNew;
|
||||
pIter->iTermLeafOffset = pIter->iLeafOffset;
|
||||
pIter->iTermLeafPgno = pIter->iLeafPgno;
|
||||
|
||||
@ -5732,8 +5732,8 @@ static int fts5QueryCksum(
|
||||
** contain valid utf-8, return non-zero.
|
||||
*/
|
||||
static int fts5TestUtf8(const char *z, int n){
|
||||
assert_nc( n>0 );
|
||||
int i = 0;
|
||||
assert_nc( n>0 );
|
||||
while( i<n ){
|
||||
if( (z[i] & 0x80)==0x00 ){
|
||||
i++;
|
||||
|
@ -50,6 +50,7 @@ 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 {
|
||||
@ -332,6 +333,12 @@ 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
|
||||
@ -343,10 +350,12 @@ 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 ){
|
||||
|
@ -10108,6 +10108,221 @@ do_catchsql_test 68.1 {
|
||||
INSERT INTO t1(t1) SELECT x FROM t2;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 69.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 32768 pagesize 4096 filename crash-31c462b8b665d0.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 08 .....@ ........
|
||||
| 32: 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 96: 00 00 00 00 0d 0f c7 00 07 0d 92 00 0f 8d 0f 36 ...............6
|
||||
| 112: 0e cb 0e 6b 0e 0e 0d b6 0d 92 00 00 00 00 00 00 ...k............
|
||||
| 3472: 00 00 22 08 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet
|
||||
| 3488: 32 74 32 08 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE
|
||||
| 3504: 20 74 32 28 78 29 56 07 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta
|
||||
| 3520: 62 6c 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 blet1_configt1_c
|
||||
| 3536: 6f 6e 66 69 67 07 43 52 45 41 54 45 20 54 41 42 onfig.CREATE TAB
|
||||
| 3552: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k
|
||||
| 3568: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3584: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 06 WITHOUT ROWID[.
|
||||
| 3600: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d
|
||||
| 3616: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize
|
||||
| 3632: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 3648: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN
|
||||
| 3664: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE
|
||||
| 3680: 59 2c 20 73 7a 20 42 4c 4f 42 29 5e 05 07 17 21 Y, sz BLOB)^...!
|
||||
| 3696: 21 01 81 07 74 61 62 6c 65 74 31 5f 63 6f 6e 74 !...tablet1_cont
|
||||
| 3712: 65 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 05 43 52 entt1_content.CR
|
||||
| 3728: 45 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 EATE TABLE 't1_c
|
||||
| 3744: 6f 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 ontent'(id INTEG
|
||||
| 3760: 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 ER PRIMARY KEY,
|
||||
| 3776: 63 39 2c 20 63 31 2c 20 63 32 29 69 04 07 17 19 c9, c1, c2)i....
|
||||
| 3792: 19 01 81 2d 74 61 62 6c 65 74 31 5f 69 64 78 74 ...-tablet1_idxt
|
||||
| 3808: 31 5f 69 64 78 04 43 52 45 41 54 45 20 54 41 42 1_idx.CREATE TAB
|
||||
| 3824: 4c 45 20 27 74 31 5f 69 64 78 27 28 73 65 67 69 LE 't1_idx'(segi
|
||||
| 3840: 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 50 d, term, pgno, P
|
||||
| 3856: 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 64 RIMARY KEY(segid
|
||||
| 3872: 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 54 , term)) WITHOUT
|
||||
| 3888: 20 52 4f 57 49 44 55 03 07 17 1b 1b 01 81 01 74 ROWIDU........t
|
||||
| 3904: 61 62 6c 65 74 31 5f 64 61 74 61 74 31 5f 64 61 ablet1_datat1_da
|
||||
| 3920: 74 61 03 43 52 45 41 54 45 20 54 41 42 4c 45 20 ta.CREATE TABLE
|
||||
| 3936: 27 74 31 5f 64 61 74 61 27 28 69 64 20 49 4e 54 't1_data'(id INT
|
||||
| 3952: 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 59 EGER PRIMARY KEY
|
||||
| 3968: 2c 20 62 6c 6f 63 6b 20 42 4c 4f 42 29 38 02 06 , block BLOB)8..
|
||||
| 3984: 17 11 11 08 5f 74 61 62 6c 65 74 31 74 31 43 52 ...._tablet1t1CR
|
||||
| 4000: 45 41 54 45 20 56 49 52 54 55 41 4c 20 54 41 42 EATE VIRTUAL TAB
|
||||
| 4016: 4c 45 20 74 31 20 55 53 49 4e 47 20 66 74 73 35 LE t1 USING fts5
|
||||
| 4032: 28 61 2c 62 2c 63 29 00 00 00 00 00 00 00 00 00 (a,b,c).........
|
||||
| page 3 offset 8192
|
||||
| 0: 0d 00 00 00 03 0c 94 00 0f e6 0f ef 0c 94 00 00 ................
|
||||
| 3216: 00 00 00 00 86 4a 84 80 80 80 80 01 04 00 8d 18 .....J..........
|
||||
| 3232: 00 00 03 2b 02 30 30 01 02 06 01 02 06 01 02 06 ...+.00.........
|
||||
| 3248: 1f 02 03 01 02 03 01 02 03 01 08 32 30 31 36 30 ...........20160
|
||||
| 3264: 36 30 39 01 02 07 01 02 07 01 02 07 01 01 34 01 609...........4.
|
||||
| 3280: 02 05 01 02 05 01 02 05 01 01 35 01 02 04 01 02 ..........5.....
|
||||
| 3296: 04 01 02 04 02 07 30 30 30 30 30 30 30 1c 02 04 ......0000000...
|
||||
| 3312: 01 02 04 01 02 04 01 06 62 69 6e 61 72 79 03 06 ........binary..
|
||||
| 3328: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 3344: 02 02 03 06 00 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 3360: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 3376: 03 06 01 02 02 03 06 01 02 02 01 08 63 6f 6d 70 ............comp
|
||||
| 3392: 69 6c 65 72 01 02 02 01 02 02 01 02 02 01 06 64 iler...........d
|
||||
| 3408: 62 73 74 61 74 07 02 03 01 02 03 01 02 03 02 04 bstat...........
|
||||
| 3424: 65 62 75 67 04 02 02 01 02 02 01 02 02 01 06 65 ebug...........e
|
||||
| 3440: 6e 61 62 6c 65 07 02 02 01 02 02 01 02 02 01 02 nable...........
|
||||
| 3456: 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 ................
|
||||
| 3472: 01 02 02 01 02 02 01 02 02 01 02 02 01 02 02 01 ................
|
||||
| 3488: 01 02 01 02 02 01 02 02 01 02 02 01 02 02 01 02 ................
|
||||
| 3504: 02 01 02 02 02 08 78 74 65 6e 73 69 6f 6e 1f 02 ......xtension..
|
||||
| 3520: 04 01 02 04 01 02 04 01 04 66 74 73 34 1a 02 03 .........fts4...
|
||||
| 3536: 01 02 03 01 02 03 04 01 35 0d 02 03 01 02 03 01 ........5.......
|
||||
| 3552: 02 03 01 03 67 63 63 01 aa 03 01 02 03 01 02 03 ....gcc.........
|
||||
| 3568: 02 06 65 6f 70 6f 6c 79 10 02 03 02 02 03 01 02 ..eopoly........
|
||||
| 3584: 03 01 05 6a 73 6f 6e 31 13 02 03 01 02 03 01 02 ...json1........
|
||||
| 3600: 03 01 04 6c 6f 61 64 1f 02 03 01 02 03 01 02 03 ...load.........
|
||||
| 3616: 01 03 6d 61 78 1c 02 02 01 02 02 01 02 02 02 05 ..max...........
|
||||
| 3632: 65 6d 6f 72 79 1c 02 03 01 02 03 01 02 03 04 04 emory...........
|
||||
| 3648: 73 79 73 35 16 02 03 01 02 03 01 02 03 01 06 6e sys5...........n
|
||||
| 3664: 6f 63 61 73 65 02 06 01 02 02 03 06 01 02 02 03 ocase...........
|
||||
| 3680: 06 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 ................
|
||||
| 3696: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 3712: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 3728: 02 01 04 6f 6d 69 74 1f 02 02 01 02 02 01 02 01 ...omit.........
|
||||
| 3744: ff ff ff ff ff ff ff ff f0 00 00 00 00 00 01 02 ................
|
||||
| 3760: 58 81 96 4d 01 06 01 02 02 03 06 01 02 02 03 06 X..M............
|
||||
| 3776: 01 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 ................
|
||||
| 3792: 02 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 ................
|
||||
| 3808: 02 03 06 01 02 02 03 06 01 02 02 03 06 01 02 02 ................
|
||||
| 3824: 01 0a 74 68 72 65 61 64 73 61 66 65 22 02 02 01 ..threadsafe....
|
||||
| 3840: 02 02 01 02 02 01 04 76 74 61 62 07 02 04 01 02 .......vtab.....
|
||||
| 3856: 04 01 02 04 01 01 78 01 06 01 01 02 01 06 01 01 ......x.........
|
||||
| 3872: 02 01 06 01 1e 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3888: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3904: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 3920: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 3936: 00 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 3952: 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 ................
|
||||
| 3968: 01 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 ................
|
||||
| 3984: 06 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 ................
|
||||
| 4000: 01 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 ................
|
||||
| 4016: 01 02 01 06 01 01 02 01 06 01 01 02 01 06 01 01 ................
|
||||
| 4032: 02 01 06 01 01 02 01 06 01 01 02 04 15 13 0c 0c ................
|
||||
| 4048: 12 44 13 11 0f 47 13 0f 0b 0e 11 10 0f 0e 10 0f .D...G..........
|
||||
| 4064: 44 0f 10 40 15 0f 07 01 03 00 14 24 5a 24 24 0f D..@.......$Z$$.
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 24 0c 0a 00 0f d8 0f af 0f 86 0f 74 ....$..........t
|
||||
| 16: 0f 61 0f 4e 0f 2f 0f 0f 0e ef 0e d7 0e be 0e a5 .a.N./..........
|
||||
| 32: 0e 8d 0e 74 0e 5b 0e 40 0e 24 0e 08 0d ef 0d d5 ...t.[.@.$......
|
||||
| 48: 0d bb 0d a0 0d 84 0d 68 0d 4f 0d 00 00 00 00 00 .......h.O......
|
||||
| 3072: 00 00 00 00 00 00 00 00 00 00 18 24 05 00 25 0f ...........$..%.
|
||||
| 3088: 19 54 48 52 45 41 44 53 41 46 45 3d 30 58 42 49 .THREADSAFE=0XBI
|
||||
| 3104: 4e 41 52 59 18 23 05 00 25 0f 19 54 48 52 45 41 NARY.#..%..THREA
|
||||
| 3120: 44 53 41 46 45 3d 30 58 4e 4f 43 41 53 45 17 22 DSAFE=0XNOCASE..
|
||||
| 3136: 05 00 25 0f 17 54 48 52 45 41 44 53 41 46 45 3d ..%..THREADSAFE=
|
||||
| 3152: 30 58 52 54 52 49 4d 1f 21 05 00 33 0f 19 4f 4d 0XRTRIM.!..3..OM
|
||||
| 3168: 49 54 20 4c 4f 41 44 20 45 58 54 45 4e 53 49 4f IT LOAD EXTENSIO
|
||||
| 3184: 4e 58 42 49 4e 41 52 59 1f 20 05 00 33 d3 19 4f NXBINARY. ..3..O
|
||||
| 3200: 4d 49 54 28 2c 4f 41 44 b2 04 55 85 44 54 e5 34 MIT(,OAD..U.DT.4
|
||||
| 3216: 94 f4 e5 84 e4 f4 34 15 34 51 e1 f0 50 03 30 f1 ......4.4Q..P.0.
|
||||
| 3232: 74 f4 d4 95 42 04 c4 f4 14 42 04 55 85 44 54 e5 t...B....B.U.DT.
|
||||
| 3248: 34 94 f4 e5 85 25 45 24 94 d1 f1 e0 50 03 30 f1 4....%E$....P.0.
|
||||
| 3264: 94 d4 15 82 04 d4 54 d4 f5 25 93 d3 53 03 03 03 ......T..%..S...
|
||||
| 3280: 03 03 03 05 84 24 94 e4 15 25 91 f1 d0 50 03 30 .....$...%...P.0
|
||||
| 3296: f1 94 d4 15 82 04 d4 54 d4 f5 25 93 d3 53 03 03 .......T..%..S..
|
||||
| 3312: 03 03 03 03 05 84 e4 f4 34 15 34 51 e1 c0 50 03 ........4.4Q..P.
|
||||
| 3328: 30 f1 74 d4 15 82 04 d4 54 d4 f5 25 93 d3 53 03 0.t.....T..%..S.
|
||||
| 3344: 03 03 03 03 03 05 85 25 45 24 94 d1 81 b0 50 02 .......%E$....P.
|
||||
| 3360: 50 f1 94 54 e4 14 24 c4 52 05 25 45 24 54 55 84 P..T..$.R.%E$TU.
|
||||
| 3376: 24 94 e4 15 25 91 81 a0 50 02 50 f1 94 54 e4 14 $...%...P.P..T..
|
||||
| 3392: 24 c4 52 05 25 45 24 54 55 84 e4 f4 34 15 34 51 $.R.%E$TU...4.4Q
|
||||
| 3408: 71 90 50 02 50 f1 74 54 e4 14 24 c4 52 05 25 45 q.P.P.tT..$.R.%E
|
||||
| 3424: 24 54 55 85 25 45 24 94 d1 a1 80 50 02 90 f1 94 $TU.%E$....P....
|
||||
| 3440: 54 e4 14 24 c4 52 04 d4 54 d5 35 95 33 55 84 24 T..$.R..T.5.3U.$
|
||||
| 3456: 94 e4 15 25 91 a1 70 50 02 90 f1 94 54 e4 14 24 ...%..pP....T..$
|
||||
| 3472: c4 52 04 d4 54 d5 35 95 33 55 84 e4 f4 34 15 34 .R..T.5.3U...4.4
|
||||
| 3488: 51 91 60 50 02 90 f1 74 54 e4 14 24 c4 52 04 d4 Q.`P...tT..$.R..
|
||||
| 3504: 54 d5 35 95 33 55 85 25 45 24 94 d1 81 50 50 02 T.5.3U.%E$...PP.
|
||||
| 3520: 50 f1 94 54 e4 14 24 c4 52 04 a5 34 f4 e3 15 84 P..T..$.R..4....
|
||||
| 3536: 24 94 e4 15 25 91 81 40 50 02 50 f1 94 54 e4 14 $...%..@P.P..T..
|
||||
| 3552: 24 c4 52 04 a5 34 f4 e3 15 84 e4 f4 34 15 34 51 $.R..4......4.4Q
|
||||
| 3568: 71 30 50 02 4f f1 74 54 e4 14 24 c4 52 04 a5 34 q0P.O.tT..$.R..4
|
||||
| 3584: f4 e3 15 85 25 45 24 94 d1 a1 20 50 02 90 f1 94 ....%E$... P....
|
||||
| 3600: 54 e4 14 24 c4 52 04 74 54 f5 04 f4 c5 95 84 24 T..$.R.tT......$
|
||||
| 3616: 94 e4 15 25 91 a1 10 50 02 90 f1 94 54 e4 14 24 ...%...P....T..$
|
||||
| 3632: c4 52 04 74 54 f5 04 f4 c5 95 84 e4 f4 34 15 34 .R.tT........4.4
|
||||
| 3648: 51 91 00 50 02 90 f1 74 54 e4 14 24 c4 51 f4 74 Q..P...tT..$.Q.t
|
||||
| 3664: 54 f5 04 f4 c5 95 85 25 45 24 94 d1 70 f0 50 02 T......%E$..p.P.
|
||||
| 3680: 30 f1 94 54 e4 14 24 c5 20 46 54 53 35 58 42 49 0..T..$. FTS5XBI
|
||||
| 3696: 4e 41 52 59 17 0e 05 00 23 0f 19 45 4e 41 42 4c NARY....#..ENABL
|
||||
| 3712: 45 20 46 54 53 35 58 4f 4f 43 41 53 45 16 0d 05 E FTS5XOOCASE...
|
||||
| 3728: 00 23 0f 17 45 4e 41 42 4c 45 20 46 54 53 35 58 .#..ENABLE FTS5X
|
||||
| 3744: 52 54 52 49 4d 17 0c 05 00 23 0f 19 45 4e 41 42 RTRIM....#..ENAB
|
||||
| 3760: 4c 45 20 46 54 53 34 58 42 49 4e 41 52 59 97 0b LE FTS4XBINARY..
|
||||
| 3776: 05 00 23 0f 19 45 4e 41 42 4c 45 20 46 54 53 34 ..#..ENABLE FTS4
|
||||
| 3792: 58 4e 4f 43 41 53 45 16 0a 05 00 23 0f 17 45 4e XNOCASE....#..EN
|
||||
| 3808: 41 42 4c 45 20 46 54 53 34 58 52 54 52 49 4d 1e ABLE FTS4XRTRIM.
|
||||
| 3824: 09 05 00 3e 5f 19 45 4e 41 42 4c 45 20 44 42 53 ...>_.ENABLE DBS
|
||||
| 3840: 44 41 54 20 56 54 41 42 58 42 49 4e 41 52 59 1e DAT VTABXBINARY.
|
||||
| 3856: 08 05 00 31 0f 19 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS
|
||||
| 3872: 54 41 54 20 56 54 41 42 58 4e 4f 43 4d e3 45 1d TAT VTABXNOCM.E.
|
||||
| 3888: 07 05 00 31 0f 17 45 4e 41 42 4c 45 20 44 42 53 ...1..ENABLE DBS
|
||||
| 3904: 54 41 54 20 56 54 41 42 58 52 54 52 49 4d 11 06 TAT VTABXRTRIM..
|
||||
| 3920: 05 00 17 0f 19 44 45 42 55 47 58 42 49 4e 41 52 .....DEBUGXBINAR
|
||||
| 3936: 59 11 05 05 00 17 0f 19 44 45 42 55 47 58 4e 4f Y.......DEBUGXNO
|
||||
| 3952: 43 41 53 45 10 02 02 50 08 5f 17 44 45 42 55 47 CASE...P._.DEBUG
|
||||
| 3968: 58 52 54 52 49 4d 27 03 05 00 44 0f 19 43 4f 4d XRTRIM'...D..COM
|
||||
| 3984: 50 49 4c 45 52 3d 67 63 63 2d 35 2e 34 2e 30 20 PILER=gcc-5.4.0
|
||||
| 4000: 32 30 31 36 30 36 30 39 58 42 49 4e 41 52 59 27 20160609XBINARY'
|
||||
| 4016: 02 05 00 43 0f 19 43 4f 4d 50 49 4c 45 52 3d 67 ...C..COMPILER=g
|
||||
| 4032: 63 63 2d 35 2e 34 2e 30 20 32 30 31 36 30 36 30 cc-5.4.0 2016060
|
||||
| 4048: 39 58 4e 4f 43 41 53 45 26 01 05 00 43 c9 17 43 9XNOCASE&...C..C
|
||||
| 4064: 4f 4d 50 49 4c 47 02 3d 67 63 63 2d 35 2e 34 2e OMPILG.=gcc-5.4.
|
||||
| 4080: 30 20 32 30 31 36 30 36 30 39 58 52 54 52 49 4d 0 20160609XRTRIM
|
||||
| page 6 offset 20480
|
||||
| 0: 0d 00 00 00 24 0e e0 00 0f f8 0f f0 0f e8 0f e0 ....$...........
|
||||
| 16: 0f d8 0f d0 0f c8 0f c0 0f b8 0f b0 0f a8 0f a0 ................
|
||||
| 32: 0f 98 0f 90 0f 88 0f 80 0f 78 0f 70 0f 68 0f 60 .........x.p.h.`
|
||||
| 48: 0f 58 0f 50 0f 48 0f 40 0f 38 00 00 00 00 00 00 .X.P.H.@.8......
|
||||
| 3808: 06 24 03 00 12 02 01 01 06 23 03 00 12 02 01 01 .$.......#......
|
||||
| 3824: 06 22 03 00 12 02 01 01 06 21 03 00 12 03 01 01 .........!......
|
||||
| 3840: 06 20 03 00 12 03 01 01 06 1f 03 00 12 03 01 01 . ..............
|
||||
| 3856: 06 1e 03 00 12 03 01 01 06 1d 03 00 12 03 01 01 ................
|
||||
| 3872: 06 1c 03 00 12 03 01 01 06 1b 03 00 12 02 01 01 ................
|
||||
| 3888: 06 1a 03 00 12 02 01 01 06 19 03 00 12 02 01 01 ................
|
||||
| 3904: 06 18 03 00 12 02 01 01 06 17 03 00 12 02 01 01 ................
|
||||
| 3920: 06 16 03 00 12 02 01 01 06 15 03 00 12 02 01 01 ................
|
||||
| 3936: 06 14 03 00 12 02 01 01 06 13 03 00 12 02 01 01 ................
|
||||
| 3952: 06 12 03 00 12 02 01 01 06 11 03 00 12 02 01 01 ................
|
||||
| 3968: 06 10 03 00 12 02 01 01 06 1f 03 00 12 02 01 01 ................
|
||||
| 3984: 06 0e 03 00 12 02 01 01 06 0d 03 00 12 02 01 01 ................
|
||||
| 4000: 06 0c 03 00 12 02 01 01 06 0b 03 00 12 02 01 01 ................
|
||||
| 4016: 06 0a 03 00 12 02 01 01 06 09 03 00 12 03 01 01 ................
|
||||
| 4032: 06 08 03 00 12 03 01 01 06 07 03 00 12 03 01 01 ................
|
||||
| 4048: 06 06 03 00 12 01 01 01 06 05 03 00 12 01 01 01 ................
|
||||
| 4064: 06 04 03 00 12 01 01 01 06 03 03 00 12 06 01 01 ................
|
||||
| 4080: 06 02 03 00 12 06 01 01 06 01 03 00 12 06 01 01 ................
|
||||
| page 7 offset 24576
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| page 8 offset 28672
|
||||
| 0: 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4048: 00 00 00 00 00 00 11 03 02 2b 69 6e 74 65 67 72 .........+integr
|
||||
| 4064: 69 74 79 2d 63 68 65 63 6b 09 00 00 00 00 00 00 ity-check.......
|
||||
| end crash-31c462b8b665d0.db
|
||||
}]} {}
|
||||
|
||||
|
||||
do_catchsql_test 69.2 {
|
||||
SELECT * FROM t1 WHERE a MATCH 'fx*'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
@ -500,14 +500,18 @@ do_execsql_test 15.0 {
|
||||
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;
|
||||
} {X'02000000'}
|
||||
|
||||
} $res
|
||||
do_execsql_test 15.2 {
|
||||
DELETE FROM t1_content WHERE rowid=1;
|
||||
SELECT quote(matchinfo(t1, 'n')) FROM t1 LIMIT 1;
|
||||
} {X'02000000'}
|
||||
} $res
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 15.3 {
|
||||
@ -517,4 +521,3 @@ do_execsql_test 15.3 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -542,5 +542,16 @@ do_execsql_test 10.7.3 {
|
||||
SELECT * FROM t2 WHERE term=?;
|
||||
}
|
||||
|
||||
finish_test
|
||||
# 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
|
||||
|
@ -116,7 +116,8 @@ SQLite. Documentation follows.
|
||||
and use it as a dynamically loadable SQLite extension. To do this
|
||||
using gcc on *nix:
|
||||
|
||||
gcc -shared icu.c `icu-config --ldflags` -o libSqliteIcu.so
|
||||
gcc -fPIC -shared icu.c `pkg-config --libs --cflags icu-uc icu-io` \
|
||||
-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
|
||||
|
@ -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( !prevEscape && uPattern==MATCH_ALL ){
|
||||
if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
|
||||
/* Case 1. */
|
||||
uint8_t c;
|
||||
|
||||
@ -169,12 +169,12 @@ static int icuLikeCompare(
|
||||
}
|
||||
return 0;
|
||||
|
||||
}else if( !prevEscape && uPattern==MATCH_ONE ){
|
||||
}else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
|
||||
/* Case 2. */
|
||||
if( *zString==0 ) return 0;
|
||||
SQLITE_ICU_SKIP_UTF8(zString);
|
||||
|
||||
}else if( !prevEscape && uPattern==(uint32_t)uEsc){
|
||||
}else if( uPattern==(uint32_t)uEsc && !prevEscape ){
|
||||
/* Case 3. */
|
||||
prevEscape = 1;
|
||||
|
||||
|
@ -24,4 +24,3 @@ int sqlite3IcuInit(sqlite3 *db);
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
@ -654,5 +654,3 @@ void test_data_3(
|
||||
testFree(zName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -322,5 +322,3 @@ void do_writer_crash_test(const char *zPattern, int *pRc){
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -138,6 +138,3 @@ void test_data_4(
|
||||
testFree(zName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -69,7 +69,3 @@ int do_bt(int nArg, char **azArg){
|
||||
sqlite4_buffer_clear(&buf.output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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_master LIMIT 1;" , 0, 0, 0
|
||||
"BEGIN; SELECT * FROM sqlite_schema LIMIT 1;" , 0, 0, 0
|
||||
);
|
||||
if( rc!=0 ) return rc;
|
||||
pDb->nOpenTrans = 1;
|
||||
|
@ -367,4 +367,3 @@ int test_mdb_scan(
|
||||
}
|
||||
|
||||
#endif /* HAVE_MDB */
|
||||
|
||||
|
@ -978,5 +978,3 @@ static int bgc_detach(BtDb *pDb){
|
||||
/*
|
||||
** End of background checkpointer.
|
||||
*************************************************************************/
|
||||
|
||||
|
||||
|
@ -228,6 +228,10 @@ 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;
|
||||
}
|
||||
|
||||
@ -413,7 +417,10 @@ 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]==0 ) return LSM_IOERR_BKPT;
|
||||
if( p->apShm[iChunk]==MAP_FAILED ){
|
||||
p->apShm[iChunk] = 0;
|
||||
return LSM_IOERR_BKPT;
|
||||
}
|
||||
}
|
||||
|
||||
*ppShm = p->apShm[iChunk];
|
||||
|
@ -439,7 +439,7 @@ static int apndOpen(
|
||||
p = (ApndFile*)pFile;
|
||||
memset(p, 0, sizeof(*p));
|
||||
pSubFile = ORIGFILE(pFile);
|
||||
p->base.pMethods = &apnd_io_methods;
|
||||
pFile->pMethods = &apnd_io_methods;
|
||||
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
|
||||
if( rc ) goto apnd_open_done;
|
||||
rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
|
||||
|
@ -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_master
|
||||
** sql TEXT, -- SQL for this btree - from sqlite_schema
|
||||
** hasRowid BOOLEAN, -- True if the btree has a rowid
|
||||
** nEntry INT, -- Estimated number of enteries
|
||||
** nEntry INT, -- Estimated number of entries
|
||||
** 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_master table.
|
||||
** The first 5 fields are taken directly from the sqlite_schema table.
|
||||
** Considering only the first 5 fields, the only difference between
|
||||
** this virtual table and the sqlite_master table is that this virtual
|
||||
** this virtual table and the sqlite_schema 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_master */
|
||||
sqlite3_stmt *pStmt; /* Query against sqlite_schema */
|
||||
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_master','sqlite_master',1,NULL "
|
||||
"SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
|
||||
"UNION ALL "
|
||||
"SELECT rowid, type, name, tbl_name, rootpage, sql"
|
||||
" FROM \"%w\".sqlite_master WHERE rootpage>=1",
|
||||
" FROM \"%w\".sqlite_schema WHERE rootpage>=1",
|
||||
pCsr->zSchema);
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
|
797
ext/misc/cksumvfs.c
Normal file
797
ext/misc/cksumvfs.c
Normal file
@ -0,0 +1,797 @@
|
||||
/*
|
||||
** 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_cksumvfs_init();
|
||||
**
|
||||
** 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, it
|
||||
** 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_RESERVED_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.
|
||||
*/
|
||||
#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_CORE)
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 ){
|
||||
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 ){
|
||||
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);
|
||||
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
|
||||
}
|
||||
|
||||
/* Release a memory-mapped page */
|
||||
static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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){
|
||||
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
||||
}
|
||||
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);
|
||||
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();
|
||||
}
|
||||
#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) */
|
@ -226,7 +226,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_master",
|
||||
"SELECT name FROM \"%w\".sqlite_schema",
|
||||
zSql, zSep, zDb
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
@ -250,7 +250,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_master AS sm"
|
||||
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
|
||||
" JOIN pragma_table_info(sm.name,%Q) AS pti"
|
||||
" WHERE sm.type='table'",
|
||||
zSql, zSep, zDb, zDb
|
||||
|
@ -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_master;\n", p->pArg);
|
||||
p->xCallback("ANALYZE sqlite_schema;\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_master(type,name,tbl_name,rootpage,sql)"
|
||||
"INSERT INTO sqlite_schema(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_master "
|
||||
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
|
||||
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'",
|
||||
zSchema
|
||||
);
|
||||
run_schema_dump_query(&x,
|
||||
"SELECT name, type, sql FROM \"%w\".sqlite_master "
|
||||
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
|
||||
"WHERE name=='sqlite_sequence'", zSchema
|
||||
);
|
||||
output_sql_from_query(&x,
|
||||
"SELECT sql FROM sqlite_master "
|
||||
"SELECT sql FROM sqlite_schema "
|
||||
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
|
||||
);
|
||||
}else{
|
||||
run_schema_dump_query(&x,
|
||||
"SELECT name, type, sql FROM \"%w\".sqlite_master "
|
||||
"SELECT name, type, sql FROM \"%w\".sqlite_schema "
|
||||
"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_master "
|
||||
"SELECT sql FROM \"%w\".sqlite_schema "
|
||||
"WHERE sql NOT NULL"
|
||||
" AND type IN ('index','trigger','view')"
|
||||
" AND tbl_name=%Q COLLATE nocase",
|
||||
|
634
ext/misc/decimal.c
Normal file
634
ext/misc/decimal.c
Normal file
@ -0,0 +1,634 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./explain
|
||||
** SELECT p2 FROM explain('SELECT * FROM sqlite_master')
|
||||
** SELECT p2 FROM explain('SELECT * FROM sqlite_schema')
|
||||
** WHERE opcode='OpenRead';
|
||||
**
|
||||
** This module was originally written to help simplify SQLite testing,
|
||||
|
@ -394,6 +394,7 @@ static int writeFile(
|
||||
|
||||
if( mtime>=0 ){
|
||||
#if defined(_WIN32)
|
||||
#if !SQLITE_OS_WINRT
|
||||
/* Windows */
|
||||
FILETIME lastAccess;
|
||||
FILETIME lastWrite;
|
||||
@ -424,6 +425,7 @@ static int writeFile(
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
|
||||
/* Recent unix */
|
||||
struct timespec times[2];
|
||||
|
@ -26,16 +26,76 @@
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** ieee754(2.0) -> 'ieee754(2,0)'
|
||||
** ieee754(45.25) -> 'ieee754(181,-2)'
|
||||
** ieee754(2, 0) -> 2.0
|
||||
** ieee754(181, -2) -> 45.25
|
||||
** 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);
|
||||
**
|
||||
*/
|
||||
#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
|
||||
*/
|
||||
@ -51,8 +111,19 @@ static void ieee754func(
|
||||
int isNeg;
|
||||
char zResult[100];
|
||||
assert( sizeof(m)==sizeof(r) );
|
||||
if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
|
||||
r = sqlite3_value_double(argv[0]);
|
||||
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( r<0.0 ){
|
||||
isNeg = 1;
|
||||
r = -r;
|
||||
@ -66,17 +137,31 @@ static void ieee754func(
|
||||
}else{
|
||||
e = a>>52;
|
||||
m = a & ((((sqlite3_int64)1)<<52)-1);
|
||||
m |= ((sqlite3_int64)1)<<52;
|
||||
if( e==0 ){
|
||||
m <<= 1;
|
||||
}else{
|
||||
m |= ((sqlite3_int64)1)<<52;
|
||||
}
|
||||
while( e<1075 && m>0 && (m&1)==0 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
if( isNeg ) m = -m;
|
||||
}
|
||||
sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
|
||||
m, e-1075);
|
||||
sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
|
||||
}else if( argc==2 ){
|
||||
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_int64 m, e, a;
|
||||
double r;
|
||||
int isNeg = 0;
|
||||
@ -86,7 +171,7 @@ static void ieee754func(
|
||||
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;
|
||||
}
|
||||
@ -99,8 +184,13 @@ static void ieee754func(
|
||||
e--;
|
||||
}
|
||||
e += 1075;
|
||||
if( e<0 ) e = m = 0;
|
||||
if( e>0x7ff ) e = 0x7ff;
|
||||
if( e<=0 ){
|
||||
/* Subnormal */
|
||||
m >>= 1-e;
|
||||
e = 0;
|
||||
}else if( e>0x7ff ){
|
||||
e = 0x7ff;
|
||||
}
|
||||
a = m & ((((sqlite3_int64)1)<<52)-1);
|
||||
a |= e<<52;
|
||||
if( isNeg ) a |= ((sqlite3_uint64)1)<<63;
|
||||
@ -109,6 +199,51 @@ 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)
|
||||
@ -118,16 +253,29 @@ 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 */
|
||||
rc = sqlite3_create_function(db, "ieee754", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
ieee754func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "ieee754", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
ieee754func, 0, 0);
|
||||
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);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -42,6 +42,10 @@ 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.
|
||||
@ -254,6 +258,7 @@ 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;
|
||||
@ -459,7 +464,7 @@ static void jsonRenderNode(
|
||||
jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
|
||||
break;
|
||||
}
|
||||
/* Fall through into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case JSON_REAL:
|
||||
case JSON_INT: {
|
||||
@ -600,7 +605,7 @@ static void jsonReturn(
|
||||
sqlite3_result_int64(pCtx, i);
|
||||
int_done:
|
||||
break;
|
||||
int_as_real: /* fall through to real */;
|
||||
int_as_real: i=0; /* no break */ deliberate_fall_through
|
||||
}
|
||||
case JSON_REAL: {
|
||||
double r;
|
||||
@ -2303,6 +2308,7 @@ 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;
|
||||
|
@ -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_master",
|
||||
zSql = sqlite3_mprintf("BEGIN; SELECT * FROM %s%q%ssqlite_schema",
|
||||
(zDb ? "'" : ""), (zDb ? zDb : ""), (zDb ? "'." : "")
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
|
@ -286,6 +286,13 @@ 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.
|
||||
@ -436,6 +443,7 @@ 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;
|
||||
@ -528,6 +536,7 @@ 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;
|
||||
|
@ -166,7 +166,7 @@ static void scrubBackupOpenSrc(ScrubState *p){
|
||||
sqlite3_errmsg(p->dbSrc));
|
||||
return;
|
||||
}
|
||||
p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_master; BEGIN;",
|
||||
p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_schema; BEGIN;",
|
||||
0, 0, 0);
|
||||
if( p->rcErr ){
|
||||
scrubBackupErr(p,
|
||||
@ -535,7 +535,7 @@ int sqlite3_scrub_backup(
|
||||
/* Copy all of the btrees */
|
||||
scrubBackupBtree(&s, 1, 0);
|
||||
pStmt = scrubBackupPrepare(&s, s.dbSrc,
|
||||
"SELECT rootpage FROM sqlite_master WHERE coalesce(rootpage,0)>0");
|
||||
"SELECT rootpage FROM sqlite_schema WHERE coalesce(rootpage,0)>0");
|
||||
if( pStmt==0 ) goto scrub_abort;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
i = (u32)sqlite3_column_int(pStmt, 0);
|
||||
|
@ -381,8 +381,9 @@ int sqlite3_sha_init(
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
sha1Func, 0, 0);
|
||||
rc = sqlite3_create_function(db, "sha1", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, sha1Func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha1_query", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <zlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** Implementation of the "sqlar_compress(X)" SQL function.
|
||||
|
@ -168,7 +168,8 @@ static int stmtColumn(
|
||||
sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
|
||||
break;
|
||||
}
|
||||
case STMT_COLUMN_MEM: {
|
||||
default: {
|
||||
assert( i==STMT_COLUMN_MEM );
|
||||
i = SQLITE_STMTSTATUS_MEMUSED +
|
||||
STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
|
||||
/* Fall thru */
|
||||
|
92
ext/misc/uint.c
Normal file
92
ext/misc/uint.c
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
** 2020-04-14
|
||||
**
|
||||
** 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 the UINT collating sequence.
|
||||
**
|
||||
** UINT works like BINARY for text, except that embedded strings
|
||||
** of digits compare in numeric order.
|
||||
**
|
||||
** * Leading zeros are handled properly, in the sense that
|
||||
** they do not mess of the maginitude comparison of embedded
|
||||
** strings of digits. "x00123y" is equal to "x123y".
|
||||
**
|
||||
** * Only unsigned integers are recognized. Plus and minus
|
||||
** signs are ignored. Decimal points and exponential notation
|
||||
** are ignored.
|
||||
**
|
||||
** * Embedded integers can be of arbitrary length. Comparison
|
||||
** is *not* limited integers that can be expressed as a
|
||||
** 64-bit machine integer.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
** Compare text in lexicographic order, except strings of digits
|
||||
** compare in numeric order.
|
||||
*/
|
||||
static int uintCollFunc(
|
||||
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;
|
||||
int i=0, j=0, x;
|
||||
(void)notUsed;
|
||||
while( i<nKey1 && j<nKey2 ){
|
||||
x = zA[i] - zB[j];
|
||||
if( isdigit(zA[i]) ){
|
||||
int k;
|
||||
if( !isdigit(zB[j]) ) return x;
|
||||
while( i<nKey1 && zA[i]=='0' ){ i++; }
|
||||
while( j<nKey2 && zB[j]=='0' ){ j++; }
|
||||
k = 0;
|
||||
while( i+k<nKey1 && isdigit(zA[i+k])
|
||||
&& j+k<nKey2 && isdigit(zB[j+k]) ){
|
||||
k++;
|
||||
}
|
||||
if( i+k<nKey1 && isdigit(zA[i+k]) ){
|
||||
return +1;
|
||||
}else if( j+k<nKey2 && isdigit(zB[j+k]) ){
|
||||
return -1;
|
||||
}else{
|
||||
x = memcmp(zA+i, zB+j, k);
|
||||
if( x ) return x;
|
||||
i += k;
|
||||
j += k;
|
||||
}
|
||||
}else if( x ){
|
||||
return x;
|
||||
}else{
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return (nKey1 - i) - (nKey2 - j);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_uint_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
|
||||
}
|
@ -811,7 +811,7 @@ int sqlite3_vfsstat_init(
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = vstatRegister(db, pzErrMsg, pApi);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_auto_extension(vstatRegister);
|
||||
rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
||||
|
@ -975,7 +975,7 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg,
|
||||
sqlite3_mprintf(
|
||||
"SELECT rbu_target_name(name, type='view') AS target, name "
|
||||
"FROM sqlite_master "
|
||||
"FROM sqlite_schema "
|
||||
"WHERE type IN ('table', 'view') AND target IS NOT NULL "
|
||||
" %s "
|
||||
"ORDER BY name"
|
||||
@ -984,7 +984,7 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
|
||||
"SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
|
||||
" FROM main.sqlite_master "
|
||||
" FROM main.sqlite_schema "
|
||||
" WHERE type='index' AND tbl_name = ?"
|
||||
);
|
||||
}
|
||||
@ -1156,12 +1156,12 @@ static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
|
||||
**
|
||||
** ALGORITHM:
|
||||
**
|
||||
** if( no entry exists in sqlite_master ){
|
||||
** if( no entry exists in sqlite_schema ){
|
||||
** return RBU_PK_NOTABLE
|
||||
** }else if( sql for the entry starts with "CREATE VIRTUAL" ){
|
||||
** return RBU_PK_VTAB
|
||||
** }else if( "PRAGMA index_list()" for the table contains a "pk" index ){
|
||||
** if( the index that is the pk exists in sqlite_master ){
|
||||
** if( the index that is the pk exists in sqlite_schema ){
|
||||
** *piPK = rootpage of that index.
|
||||
** return RBU_PK_EXTERNAL
|
||||
** }else{
|
||||
@ -1181,9 +1181,9 @@ static void rbuTableType(
|
||||
int *piPk
|
||||
){
|
||||
/*
|
||||
** 0) SELECT count(*) FROM sqlite_master where name=%Q AND IsVirtual(%Q)
|
||||
** 0) SELECT count(*) FROM sqlite_schema where name=%Q AND IsVirtual(%Q)
|
||||
** 1) PRAGMA index_list = ?
|
||||
** 2) SELECT count(*) FROM sqlite_master where name=%Q
|
||||
** 2) SELECT count(*) FROM sqlite_schema where name=%Q
|
||||
** 3) PRAGMA table_info = ?
|
||||
*/
|
||||
sqlite3_stmt *aStmt[4] = {0, 0, 0, 0};
|
||||
@ -1195,7 +1195,7 @@ static void rbuTableType(
|
||||
p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg,
|
||||
sqlite3_mprintf(
|
||||
"SELECT (sql LIKE 'create virtual%%'), rootpage"
|
||||
" FROM sqlite_master"
|
||||
" FROM sqlite_schema"
|
||||
" WHERE name=%Q", zTab
|
||||
));
|
||||
if( p->rc!=SQLITE_OK || sqlite3_step(aStmt[0])!=SQLITE_ROW ){
|
||||
@ -1218,7 +1218,7 @@ static void rbuTableType(
|
||||
if( zOrig && zIdx && zOrig[0]=='p' ){
|
||||
p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg,
|
||||
sqlite3_mprintf(
|
||||
"SELECT rootpage FROM sqlite_master WHERE name = %Q", zIdx
|
||||
"SELECT rootpage FROM sqlite_schema WHERE name = %Q", zIdx
|
||||
));
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( sqlite3_step(aStmt[2])==SQLITE_ROW ){
|
||||
@ -2038,7 +2038,7 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
** This is needed for the argument to "PRAGMA index_xinfo". Set
|
||||
** zIdx to point to a nul-terminated string containing this name. */
|
||||
p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg,
|
||||
"SELECT name FROM sqlite_master WHERE rootpage = ?"
|
||||
"SELECT name FROM sqlite_schema WHERE rootpage = ?"
|
||||
);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pQuery, 1, tnum);
|
||||
@ -2211,7 +2211,7 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
|
||||
"SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?"
|
||||
"SELECT trim(sql) FROM sqlite_schema WHERE type='index' AND name=?"
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -2793,7 +2793,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
|
||||
int bOk = 0;
|
||||
sqlite3_stmt *pCnt = 0;
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pCnt, &p->zErrmsg,
|
||||
"SELECT count(*) FROM stat.sqlite_master"
|
||||
"SELECT count(*) FROM stat.sqlite_schema"
|
||||
);
|
||||
if( p->rc==SQLITE_OK
|
||||
&& sqlite3_step(pCnt)==SQLITE_ROW
|
||||
@ -2897,7 +2897,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
|
||||
}
|
||||
rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
|
||||
rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_schema");
|
||||
|
||||
/* Mark the database file just opened as an RBU target database. If
|
||||
** this call returns SQLITE_NOTFOUND, then the RBU vfs is not in use.
|
||||
@ -2990,7 +2990,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
|
||||
if( pState==0 ){
|
||||
p->eStage = 0;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_master", 0, 0, 0);
|
||||
p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_schema", 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3581,7 +3581,7 @@ static void rbuCreateTargetSchema(sqlite3rbu *p){
|
||||
p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
||||
"SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
|
||||
"SELECT sql FROM sqlite_schema WHERE sql!='' AND rootpage!=0"
|
||||
" AND name!='sqlite_sequence' "
|
||||
" ORDER BY type DESC"
|
||||
);
|
||||
@ -3596,13 +3596,13 @@ static void rbuCreateTargetSchema(sqlite3rbu *p){
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
||||
"SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
|
||||
"SELECT * FROM sqlite_schema WHERE rootpage=0 OR rootpage IS NULL"
|
||||
);
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
|
||||
"INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
|
||||
"INSERT INTO sqlite_schema VALUES(?,?,?,?,?)"
|
||||
);
|
||||
}
|
||||
|
||||
@ -3865,7 +3865,7 @@ static void rbuIndexCntFunc(
|
||||
assert( nVal==1 );
|
||||
|
||||
rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
|
||||
sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
|
||||
sqlite3_mprintf("SELECT count(*) FROM sqlite_schema "
|
||||
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -3916,7 +3916,7 @@ static void rbuInitPhaseOneSteps(sqlite3rbu *p){
|
||||
** occurs, nPhaseOneStep will be left set to -1. */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
|
||||
"SELECT 1 FROM sqlite_master WHERE tbl_name = 'rbu_count'"
|
||||
"SELECT 1 FROM sqlite_schema WHERE tbl_name = 'rbu_count'"
|
||||
);
|
||||
}
|
||||
if( p->rc==SQLITE_OK ){
|
||||
|
@ -473,7 +473,7 @@ static int cidxLookupIndex(
|
||||
|
||||
/* Find the table for this index. */
|
||||
pFindTab = cidxPrepare(&rc, pCsr,
|
||||
"SELECT tbl_name, sql FROM sqlite_master WHERE name=%Q AND type='index'",
|
||||
"SELECT tbl_name, sql FROM sqlite_schema WHERE name=%Q AND type='index'",
|
||||
zIdx
|
||||
);
|
||||
if( rc==SQLITE_OK && sqlite3_step(pFindTab)==SQLITE_ROW ){
|
||||
|
@ -683,6 +683,8 @@ static GeoPoly *geopolyBBox(
|
||||
aCoord[2].f = mnY;
|
||||
aCoord[3].f = mxY;
|
||||
}
|
||||
}else{
|
||||
memset(aCoord, 0, sizeof(RtreeCoord)*4);
|
||||
}
|
||||
return pOut;
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ typedef unsigned int u32;
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The following macro is used to suppress compiler warnings.
|
||||
*/
|
||||
@ -419,6 +420,23 @@ struct RtreeMatchArg {
|
||||
# define testcase(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Make sure that the compiler intrinsics we desire are enabled when
|
||||
** compiling with an appropriate version of MSVC unless prevented by
|
||||
** the SQLITE_DISABLE_INTRINSIC define.
|
||||
*/
|
||||
#if !defined(SQLITE_DISABLE_INTRINSIC)
|
||||
# if defined(_MSC_VER) && _MSC_VER>=1400
|
||||
# if !defined(_WIN32_WCE)
|
||||
# include <intrin.h>
|
||||
# pragma intrinsic(_byteswap_ulong)
|
||||
# pragma intrinsic(_byteswap_uint64)
|
||||
# else
|
||||
# include <cmnintrin.h>
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macros to determine whether the machine is big or little endian,
|
||||
** and whether or not that determination is run-time or compile-time.
|
||||
@ -3746,8 +3764,10 @@ static int rtreeInit(
|
||||
}else if( pRtree->nAux>0 ){
|
||||
break;
|
||||
}else{
|
||||
static const char *azFormat[] = {",%.*s REAL", ",%.*s INT"};
|
||||
pRtree->nDim2++;
|
||||
sqlite3_str_appendf(pSql, ",%.*s NUM", rtreeTokenLength(zArg), zArg);
|
||||
sqlite3_str_appendf(pSql, azFormat[eCoordType],
|
||||
rtreeTokenLength(zArg), zArg);
|
||||
}
|
||||
}
|
||||
sqlite3_str_appendf(pSql, ");");
|
||||
|
@ -716,6 +716,18 @@ do_execsql_test 18.0 {
|
||||
SELECT rt0.c1 > '-1' FROM rt0;
|
||||
} {9 1}
|
||||
|
||||
|
||||
expand_all_sql db
|
||||
|
||||
# 2020-02-28 ticket e63b4d1a65546532
|
||||
reset_db
|
||||
do_execsql_test 19.0 {
|
||||
CREATE VIRTUAL TABLE rt0 USING rtree(a,b,c);
|
||||
INSERT INTO rt0(a,b,c) VALUES(0,0.0,0.0);
|
||||
CREATE VIEW v0(x) AS SELECT DISTINCT rt0.b FROM rt0;
|
||||
SELECT v0.x FROM v0, rt0;
|
||||
} {0.0}
|
||||
do_execsql_test 19.1 {
|
||||
SELECT v0.x FROM v0, rt0 WHERE v0.x = rt0.b;
|
||||
} {0.0}
|
||||
|
||||
finish_test
|
||||
|
@ -34,5 +34,51 @@ do_test 1.0 {
|
||||
compare_db db db2
|
||||
} {}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
db2 close
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO main.t1 VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);
|
||||
}
|
||||
|
||||
do_test 2.1 {
|
||||
sqlite3session S db main
|
||||
S attach *
|
||||
db eval {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(10, 11, 12);
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
UPDATE t1 SET b='five', c='six' WHERE a=4;
|
||||
}
|
||||
|
||||
set C [S changeset]
|
||||
db eval ROLLBACK
|
||||
S delete
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
CREATE TEMP TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO temp.t1 VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);
|
||||
}
|
||||
|
||||
set ::conflict [list]
|
||||
proc xConflict {args} { lappend ::conflict $args ; return "" }
|
||||
do_test 2.3 {
|
||||
sqlite3changeset_apply db $C xConflict
|
||||
set ::conflict
|
||||
} {}
|
||||
do_execsql_test 2.4 {
|
||||
SELECT * FROM main.t1;
|
||||
SELECT '****';
|
||||
SELECT * FROM temp.t1;
|
||||
} {
|
||||
4 five six 7 8 9 10 11 12
|
||||
****
|
||||
1 2 3 4 5 6 7 8 9
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -172,8 +172,8 @@ proc compare_db {db1 db2} {
|
||||
set data1 [$db1 eval $sql]
|
||||
set data2 [$db2 eval $sql]
|
||||
if {$data1 != $data2} {
|
||||
puts "$data1"
|
||||
puts "$data2"
|
||||
puts "$db1: $data1"
|
||||
puts "$db2: $data2"
|
||||
error "table $tbl data mismatch"
|
||||
}
|
||||
}
|
||||
|
@ -155,5 +155,29 @@ do_test 3.2 {
|
||||
compare_db db db2
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
}
|
||||
|
||||
do_invert_test 4.1 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1, 'two');
|
||||
INSERT INTO t1 VALUES(2, 'five');
|
||||
INSERT INTO t1 VALUES(3, 'one');
|
||||
INSERT INTO t1 VALUES(4, 'three');
|
||||
} {
|
||||
{UPDATE t1 0 X. {i 1 t two} {{} {} t one}}
|
||||
{UPDATE t1 0 X. {i 2 t five} {{} {} t two}}
|
||||
{UPDATE t1 0 X. {i 3 t one} {{} {} t three}}
|
||||
{UPDATE t1 0 X. {i 4 t three} {{} {} t four}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -2956,8 +2956,13 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
|
||||
}
|
||||
|
||||
p->apValue = (sqlite3_value**)p->tblhdr.aBuf;
|
||||
p->abPK = (u8*)&p->apValue[p->nCol*2];
|
||||
p->zTab = (char*)&p->abPK[p->nCol];
|
||||
if( p->apValue==0 ){
|
||||
p->abPK = 0;
|
||||
p->zTab = 0;
|
||||
}else{
|
||||
p->abPK = (u8*)&p->apValue[p->nCol*2];
|
||||
p->zTab = p->abPK ? (char*)&p->abPK[p->nCol] : 0;
|
||||
}
|
||||
return (p->rc = rc);
|
||||
}
|
||||
|
||||
@ -3479,6 +3484,7 @@ struct SessionApplyCtx {
|
||||
u8 *abPK; /* Boolean array - true if column is in PK */
|
||||
int bStat1; /* True if table is sqlite_stat1 */
|
||||
int bDeferConstraints; /* True to defer constraints */
|
||||
int bInvertConstraints; /* Invert when iterating constraints buffer */
|
||||
SessionBuffer constraints; /* Deferred constraints are stored here */
|
||||
SessionBuffer rebase; /* Rebase information (if any) here */
|
||||
u8 bRebaseStarted; /* If table header is already in rebase */
|
||||
@ -3513,7 +3519,7 @@ static int sessionDeleteRow(
|
||||
SessionBuffer buf = {0, 0, 0};
|
||||
int nPk = 0;
|
||||
|
||||
sessionAppendStr(&buf, "DELETE FROM ", &rc);
|
||||
sessionAppendStr(&buf, "DELETE FROM main.", &rc);
|
||||
sessionAppendIdent(&buf, zTab, &rc);
|
||||
sessionAppendStr(&buf, " WHERE ", &rc);
|
||||
|
||||
@ -3596,7 +3602,7 @@ static int sessionUpdateRow(
|
||||
SessionBuffer buf = {0, 0, 0};
|
||||
|
||||
/* Append "UPDATE tbl SET " */
|
||||
sessionAppendStr(&buf, "UPDATE ", &rc);
|
||||
sessionAppendStr(&buf, "UPDATE main.", &rc);
|
||||
sessionAppendIdent(&buf, zTab, &rc);
|
||||
sessionAppendStr(&buf, " SET ", &rc);
|
||||
|
||||
@ -4251,7 +4257,9 @@ static int sessionRetryConstraints(
|
||||
SessionBuffer cons = pApply->constraints;
|
||||
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
|
||||
|
||||
rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0);
|
||||
rc = sessionChangesetStart(
|
||||
&pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
|
||||
int rc2;
|
||||
@ -4318,6 +4326,7 @@ static int sessionChangesetApply(
|
||||
pIter->in.bNoDiscard = 1;
|
||||
memset(&sApply, 0, sizeof(sApply));
|
||||
sApply.bRebase = (ppRebase && pnRebase);
|
||||
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
|
||||
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
|
||||
|
30
main.mk
30
main.mk
@ -74,7 +74,8 @@ LIBOBJ+= vdbe.o parse.o \
|
||||
table.o threads.o tokenize.o treeview.o trigger.o \
|
||||
update.o upsert.o userauth.o util.o vacuum.o \
|
||||
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
|
||||
vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \
|
||||
vdbetrace.o vdbevtab.o \
|
||||
wal.o walker.o where.o wherecode.o whereexpr.o \
|
||||
utf.o vtab.o window.o
|
||||
|
||||
LIBOBJ += sqlite3session.o
|
||||
@ -173,6 +174,7 @@ 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 \
|
||||
@ -361,6 +363,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/carray.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 \
|
||||
@ -421,6 +424,7 @@ TESTSRC2 = \
|
||||
$(TOP)/src/vdbeaux.c \
|
||||
$(TOP)/src/vdbe.c \
|
||||
$(TOP)/src/vdbemem.c \
|
||||
$(TOP)/src/vdbevtab.c \
|
||||
$(TOP)/src/where.c \
|
||||
$(TOP)/src/wherecode.c \
|
||||
$(TOP)/src/whereexpr.c \
|
||||
@ -526,6 +530,7 @@ 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
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
|
||||
@ -536,6 +541,7 @@ FUZZCHECK_OPT += -DSQLITE_ENABLE_FTS4
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
DBFUZZ_OPT =
|
||||
KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
|
||||
ST_OPT = -DSQLITE_THREADSAFE=0
|
||||
@ -586,6 +592,7 @@ DBFUZZ2_OPTS = \
|
||||
-DSQLITE_ENABLE_DESERIALIZE \
|
||||
-DSQLITE_DEBUG \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_FTS5
|
||||
@ -721,6 +728,12 @@ parse.c: $(TOP)/src/parse.y lemon
|
||||
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h
|
||||
tclsh $(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 $(TOP)/tool/replace.tcl exact . , >>$@
|
||||
echo '#endif' >>sqlite3rc.h
|
||||
|
||||
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
$(BCC) -o mkkeywordhash $(OPTS) $(TOP)/tool/mkkeywordhash.c
|
||||
./mkkeywordhash >keywordhash.h
|
||||
@ -729,10 +742,13 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
|
||||
SHELL_SRC = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/misc/appendvfs.c \
|
||||
$(TOP)/ext/misc/shathree.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/completion.c \
|
||||
$(TOP)/ext/misc/decimal.c \
|
||||
$(TOP)/ext/misc/fileio.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/shathree.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 \
|
||||
@ -894,6 +910,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 += -DTCLSH_INIT_PROC=sqlite3TestInit
|
||||
|
||||
testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
@ -967,6 +984,9 @@ valgrindtest: $(TESTPROGS) valgrindfuzz
|
||||
smoketest: $(TESTPROGS) fuzzcheck$(EXE)
|
||||
./testfixture$(EXE) $(TOP)/test/main.test $(TESTOPTS)
|
||||
|
||||
shelltest: $(TESTPROGS)
|
||||
./testfixture$(EXT) $(TOP)/test/permutations.test shell
|
||||
|
||||
# The next two rules are used to support the "threadtest" target. Building
|
||||
# threadtest runs a few thread-safety tests that are implemented in C. This
|
||||
# target is invoked by the releasetest.tcl script.
|
||||
@ -1068,10 +1088,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
|
||||
amalgamation-tarball: sqlite3.c sqlite3rc.h
|
||||
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --normal
|
||||
|
||||
snapshot-tarball: sqlite3.c
|
||||
snapshot-tarball: sqlite3.c sqlite3rc.h
|
||||
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
|
||||
|
||||
|
||||
|
@ -1 +1 @@
|
||||
f6affdd41608946fcfcea914ece149038a8b25a62bbe719ed2561c649b86d824
|
||||
fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f
|
||||
|
131
src/alter.c
131
src/alter.c
@ -52,22 +52,22 @@ static int isAlterableTable(Parse *pParse, Table *pTab){
|
||||
static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT 1 "
|
||||
"FROM \"%w\".%s "
|
||||
"FROM \"%w\"." DFLT_SCHEMA_TABLE " "
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
" AND sql NOT LIKE 'create virtual%%'"
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
|
||||
zDb, MASTER_NAME,
|
||||
zDb,
|
||||
zDb, bTemp
|
||||
);
|
||||
|
||||
if( bTemp==0 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT 1 "
|
||||
"FROM temp.%s "
|
||||
"FROM temp." DFLT_SCHEMA_TABLE " "
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
" AND sql NOT LIKE 'create virtual%%'"
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
|
||||
MASTER_NAME, zDb
|
||||
zDb
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,10 @@ void sqlite3AlterRenameTable(
|
||||
/* Check that a table or index named 'zName' does not already exist
|
||||
** in database iDb. If so, this is an error.
|
||||
*/
|
||||
if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){
|
||||
if( sqlite3FindTable(db, zName, zDb)
|
||||
|| sqlite3FindIndex(db, zName, zDb)
|
||||
|| sqlite3IsShadowTableOf(db, pTab, zName)
|
||||
){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"there is already another table or index with this name: %s", zName);
|
||||
goto exit_rename_table;
|
||||
@ -182,17 +185,17 @@ void sqlite3AlterRenameTable(
|
||||
/* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
|
||||
** the schema to use the new table name. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
|
||||
"WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
|
||||
"AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
, zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
|
||||
, zDb, zDb, zTabName, zName, (iDb==1), zTabName
|
||||
);
|
||||
|
||||
/* Update the tbl_name and name columns of the sqlite_master table
|
||||
/* Update the tbl_name and name columns of the sqlite_schema table
|
||||
** as required. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET "
|
||||
"UPDATE %Q." DFLT_SCHEMA_TABLE " SET "
|
||||
"tbl_name = %Q, "
|
||||
"name = CASE "
|
||||
"WHEN type='table' THEN %Q "
|
||||
@ -202,7 +205,7 @@ void sqlite3AlterRenameTable(
|
||||
"ELSE name END "
|
||||
"WHERE tbl_name=%Q COLLATE nocase AND "
|
||||
"(type='table' OR type='index' OR type='trigger');",
|
||||
zDb, MASTER_NAME,
|
||||
zDb,
|
||||
zName, zName, zName,
|
||||
nTabName, zTabName
|
||||
);
|
||||
@ -223,7 +226,7 @@ void sqlite3AlterRenameTable(
|
||||
** as required. */
|
||||
if( iDb!=1 ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE sqlite_temp_master SET "
|
||||
"UPDATE sqlite_temp_schema SET "
|
||||
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
|
||||
"tbl_name = "
|
||||
"CASE WHEN tbl_name=%Q COLLATE nocase AND "
|
||||
@ -255,6 +258,22 @@ exit_rename_table:
|
||||
db->mDbFlags = savedDbFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write code that will raise an error if the table described by
|
||||
** zDb and zTab is not empty.
|
||||
*/
|
||||
static void sqlite3ErrorIfNotEmpty(
|
||||
Parse *pParse, /* Parsing context */
|
||||
const char *zDb, /* Schema holding the table */
|
||||
const char *zTab, /* Table to check for empty */
|
||||
const char *zErr /* Error message text */
|
||||
){
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
|
||||
zErr, zDb, zTab
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called after an "ALTER TABLE ... ADD" statement
|
||||
** has been parsed. Argument pColDef contains the text of the new
|
||||
@ -307,7 +326,8 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
return;
|
||||
}
|
||||
if( pNew->pIndex ){
|
||||
sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"Cannot add a UNIQUE column");
|
||||
return;
|
||||
}
|
||||
if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
|
||||
@ -320,16 +340,15 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
pDflt = 0;
|
||||
}
|
||||
if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
|
||||
"Cannot add a REFERENCES column with non-NULL default value");
|
||||
return;
|
||||
}
|
||||
if( pCol->notNull && !pDflt ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
|
||||
"Cannot add a NOT NULL column with default value NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure the default expression is something that sqlite3ValueFromExpr()
|
||||
** can handle (i.e. not CURRENT_TIME etc.)
|
||||
*/
|
||||
@ -343,14 +362,13 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
return;
|
||||
}
|
||||
if( !pVal ){
|
||||
sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default");
|
||||
return;
|
||||
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
|
||||
"Cannot add a column with non-constant default");
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
}
|
||||
}else if( pCol->colFlags & COLFLAG_STORED ){
|
||||
sqlite3ErrorMsg(pParse, "cannot add a STORED column");
|
||||
return;
|
||||
sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
|
||||
}
|
||||
|
||||
|
||||
@ -364,10 +382,10 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
}
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin;
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
|
||||
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
|
||||
"WHERE type = 'table' AND name = %Q",
|
||||
zDb, MASTER_NAME, pNew->addColOffset, zCol, pNew->addColOffset+1,
|
||||
zDb, pNew->addColOffset, zCol, pNew->addColOffset+1,
|
||||
zTab
|
||||
);
|
||||
sqlite3DbFree(db, zCol);
|
||||
@ -469,6 +487,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
for(i=0; i<pNew->nCol; i++){
|
||||
Column *pCol = &pNew->aCol[i];
|
||||
pCol->zName = sqlite3DbStrDup(db, pCol->zName);
|
||||
pCol->hName = sqlite3StrIHash(pCol->zName);
|
||||
pCol->zColl = 0;
|
||||
pCol->pDflt = 0;
|
||||
}
|
||||
@ -568,7 +587,7 @@ void sqlite3AlterRenameColumn(
|
||||
|
||||
/* Do the rename operation using a recursive UPDATE statement that
|
||||
** uses the sqlite_rename_column() SQL function to compute the new
|
||||
** CREATE statement text for the sqlite_master table.
|
||||
** CREATE statement text for the sqlite_schema table.
|
||||
*/
|
||||
sqlite3MayAbort(pParse);
|
||||
zNew = sqlite3NameFromToken(db, pNew);
|
||||
@ -576,21 +595,20 @@ void sqlite3AlterRenameColumn(
|
||||
assert( pNew->n>0 );
|
||||
bQuote = sqlite3Isquote(pNew->z[0]);
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\".%s SET "
|
||||
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
|
||||
" AND (type != 'index' OR tbl_name = %Q)"
|
||||
" AND sql NOT LIKE 'create virtual%%'",
|
||||
zDb, MASTER_NAME,
|
||||
zDb,
|
||||
zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
|
||||
pTab->zName
|
||||
);
|
||||
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE temp.%s SET "
|
||||
"UPDATE temp." DFLT_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) "
|
||||
"WHERE type IN ('trigger', 'view')",
|
||||
MASTER_NAME,
|
||||
zDb, pTab->zName, iCol, zNew, bQuote
|
||||
);
|
||||
|
||||
@ -697,7 +715,7 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
|
||||
RenameToken *pNew;
|
||||
assert( pPtr || pParse->db->mallocFailed );
|
||||
renameTokenCheckAll(pParse, pPtr);
|
||||
if( pParse->eParseMode!=PARSE_MODE_UNMAP ){
|
||||
if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
|
||||
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
|
||||
if( pNew ){
|
||||
pNew->p = pPtr;
|
||||
@ -755,6 +773,21 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Unmap all tokens in the IdList object passed as the second argument.
|
||||
*/
|
||||
static void unmapColumnIdlistNames(
|
||||
Parse *pParse,
|
||||
IdList *pIdList
|
||||
){
|
||||
if( pIdList ){
|
||||
int ii;
|
||||
for(ii=0; ii<pIdList->nId; ii++){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Walker callback used by sqlite3RenameExprUnmap().
|
||||
*/
|
||||
@ -776,6 +809,7 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
|
||||
if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
|
||||
unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
|
||||
}
|
||||
}
|
||||
|
||||
@ -984,6 +1018,7 @@ static void renameColumnIdlistNames(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Parse the SQL statement zSql using Parse object (*p). The Parse object
|
||||
** is initialized by this function before it is used.
|
||||
@ -1125,7 +1160,7 @@ static int renameEditSql(
|
||||
** successful. Otherwise, return an SQLite error code and leave an error
|
||||
** message in the Parse object.
|
||||
*/
|
||||
static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
||||
static int renameResolveTrigger(Parse *pParse){
|
||||
sqlite3 *db = pParse->db;
|
||||
Trigger *pNew = pParse->pNewTrigger;
|
||||
TriggerStep *pStep;
|
||||
@ -1156,17 +1191,22 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
||||
if( pParse->nErr ) rc = pParse->rc;
|
||||
}
|
||||
if( rc==SQLITE_OK && pStep->zTarget ){
|
||||
Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
|
||||
if( pTarget==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){
|
||||
SrcList sSrc;
|
||||
memset(&sSrc, 0, sizeof(sSrc));
|
||||
sSrc.nSrc = 1;
|
||||
sSrc.a[0].zName = pStep->zTarget;
|
||||
sSrc.a[0].pTab = pTarget;
|
||||
sNC.pSrcList = &sSrc;
|
||||
if( pStep->pWhere ){
|
||||
SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
|
||||
if( pSrc ){
|
||||
int i;
|
||||
for(i=0; i<pSrc->nSrc && rc==SQLITE_OK; i++){
|
||||
struct SrcList_item *p = &pSrc->a[i];
|
||||
p->pTab = sqlite3LocateTableItem(pParse, 0, p);
|
||||
p->iCursor = pParse->nTab++;
|
||||
if( p->pTab==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
p->pTab->nTabRef++;
|
||||
rc = sqlite3ViewGetColumnNames(pParse, p->pTab);
|
||||
}
|
||||
}
|
||||
sNC.pSrcList = pSrc;
|
||||
if( rc==SQLITE_OK && pStep->pWhere ){
|
||||
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -1176,7 +1216,7 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUpsert = pStep->pUpsert;
|
||||
assert( rc==SQLITE_OK );
|
||||
pUpsert->pUpsertSrc = &sSrc;
|
||||
pUpsert->pUpsertSrc = pSrc;
|
||||
sNC.uNC.pUpsert = pUpsert;
|
||||
sNC.ncFlags = NC_UUpsert;
|
||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
||||
@ -1193,6 +1233,9 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
|
||||
sNC.ncFlags = 0;
|
||||
}
|
||||
sNC.pSrcList = 0;
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1379,7 +1422,7 @@ static void renameColumnFunc(
|
||||
}else{
|
||||
/* A trigger */
|
||||
TriggerStep *pStep;
|
||||
rc = renameResolveTrigger(&sParse, (bTemp ? 0 : zDb));
|
||||
rc = renameResolveTrigger(&sParse);
|
||||
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
|
||||
|
||||
for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
|
||||
@ -1582,7 +1625,7 @@ static void renameTableFunc(
|
||||
}
|
||||
|
||||
if( isLegacy==0 ){
|
||||
rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
|
||||
rc = renameResolveTrigger(&sParse);
|
||||
if( rc==SQLITE_OK ){
|
||||
renameWalkTrigger(&sWalker, pTrigger);
|
||||
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
|
||||
@ -1669,7 +1712,7 @@ static void renameTableTest(
|
||||
|
||||
else if( sParse.pNewTrigger ){
|
||||
if( isLegacy==0 ){
|
||||
rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
|
||||
rc = renameResolveTrigger(&sParse);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema);
|
||||
|
360
src/analyze.c
360
src/analyze.c
@ -186,8 +186,13 @@ static void openStatTable(
|
||||
sqlite3 *db = pParse->db;
|
||||
Db *pDb;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int aRoot[ArraySize(aTable)];
|
||||
u32 aRoot[ArraySize(aTable)];
|
||||
u8 aCreateTbl[ArraySize(aTable)];
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1;
|
||||
#else
|
||||
const int nToOpen = 1;
|
||||
#endif
|
||||
|
||||
if( v==0 ) return;
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
@ -200,8 +205,9 @@ static void openStatTable(
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
const char *zTab = aTable[i].zName;
|
||||
Table *pStat;
|
||||
aCreateTbl[i] = 0;
|
||||
if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
|
||||
if( aTable[i].zCols ){
|
||||
if( i<nToOpen ){
|
||||
/* The sqlite_statN table does not exist. Create it. Note that a
|
||||
** side-effect of the CREATE TABLE statement is to leave the rootpage
|
||||
** of the new table in register pParse->regRoot. This is important
|
||||
@ -209,7 +215,7 @@ static void openStatTable(
|
||||
sqlite3NestedParse(pParse,
|
||||
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
|
||||
);
|
||||
aRoot[i] = pParse->regRoot;
|
||||
aRoot[i] = (u32)pParse->regRoot;
|
||||
aCreateTbl[i] = OPFLAG_P2ISREG;
|
||||
}
|
||||
}else{
|
||||
@ -217,7 +223,6 @@ static void openStatTable(
|
||||
** associated with the table zWhere. If zWhere is NULL, delete the
|
||||
** entire contents of the table. */
|
||||
aRoot[i] = pStat->tnum;
|
||||
aCreateTbl[i] = 0;
|
||||
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
|
||||
if( zWhere ){
|
||||
sqlite3NestedParse(pParse,
|
||||
@ -230,15 +235,15 @@ static void openStatTable(
|
||||
#endif
|
||||
}else{
|
||||
/* The sqlite_stat[134] table already exists. Delete all rows. */
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the sqlite_stat[134] tables for writing. */
|
||||
for(i=0; aTable[i].zCols; i++){
|
||||
for(i=0; i<nToOpen; i++){
|
||||
assert( i<ArraySize(aTable) );
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, (int)aRoot[i], iDb, 3);
|
||||
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
|
||||
VdbeComment((v, aTable[i].zName));
|
||||
}
|
||||
@ -256,9 +261,9 @@ static void openStatTable(
|
||||
** share an instance of the following structure to hold their state
|
||||
** information.
|
||||
*/
|
||||
typedef struct Stat4Accum Stat4Accum;
|
||||
typedef struct Stat4Sample Stat4Sample;
|
||||
struct Stat4Sample {
|
||||
typedef struct StatAccum StatAccum;
|
||||
typedef struct StatSample StatSample;
|
||||
struct StatSample {
|
||||
tRowcnt *anEq; /* sqlite_stat4.nEq */
|
||||
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
@ -273,27 +278,32 @@ struct Stat4Sample {
|
||||
u32 iHash; /* Tiebreaker hash */
|
||||
#endif
|
||||
};
|
||||
struct Stat4Accum {
|
||||
tRowcnt nRow; /* Number of rows in the entire table */
|
||||
tRowcnt nPSample; /* How often to do a periodic sample */
|
||||
struct StatAccum {
|
||||
sqlite3 *db; /* Database connection, for malloc() */
|
||||
tRowcnt nEst; /* Estimated number of rows */
|
||||
tRowcnt nRow; /* Number of rows visited so far */
|
||||
int nLimit; /* Analysis row-scan limit */
|
||||
int nCol; /* Number of columns in index + pk/rowid */
|
||||
int nKeyCol; /* Number of index columns w/o the pk/rowid */
|
||||
u8 nSkipAhead; /* Number of times of skip-ahead */
|
||||
StatSample current; /* Current row as a StatSample */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
tRowcnt nPSample; /* How often to do a periodic sample */
|
||||
int mxSample; /* Maximum number of samples to accumulate */
|
||||
Stat4Sample current; /* Current row as a Stat4Sample */
|
||||
u32 iPrn; /* Pseudo-random number used for sampling */
|
||||
Stat4Sample *aBest; /* Array of nCol best samples */
|
||||
StatSample *aBest; /* Array of nCol best samples */
|
||||
int iMin; /* Index in a[] of entry with minimum score */
|
||||
int nSample; /* Current number of samples */
|
||||
int nMaxEqZero; /* Max leading 0 in anEq[] for any a[] entry */
|
||||
int iGet; /* Index of current sample accessed by stat_get() */
|
||||
Stat4Sample *a; /* Array of mxSample Stat4Sample objects */
|
||||
sqlite3 *db; /* Database connection, for malloc() */
|
||||
StatSample *a; /* Array of mxSample StatSample objects */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Reclaim memory used by a Stat4Sample
|
||||
/* Reclaim memory used by a StatSample
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleClear(sqlite3 *db, Stat4Sample *p){
|
||||
static void sampleClear(sqlite3 *db, StatSample *p){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ){
|
||||
sqlite3DbFree(db, p->u.aRowid);
|
||||
@ -305,7 +315,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
|
||||
/* Initialize the BLOB value of a ROWID
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
|
||||
static void sampleSetRowid(sqlite3 *db, StatSample *p, int n, const u8 *pData){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
||||
p->u.aRowid = sqlite3DbMallocRawNN(db, n);
|
||||
@ -321,7 +331,7 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
|
||||
/* Initialize the INTEGER value of a ROWID.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
|
||||
static void sampleSetRowidInt64(sqlite3 *db, StatSample *p, i64 iRowid){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
||||
p->nRowid = 0;
|
||||
@ -334,7 +344,7 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
|
||||
** Copy the contents of object (*pFrom) into (*pTo).
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
|
||||
static void sampleCopy(StatAccum *p, StatSample *pTo, StatSample *pFrom){
|
||||
pTo->isPSample = pFrom->isPSample;
|
||||
pTo->iCol = pFrom->iCol;
|
||||
pTo->iHash = pFrom->iHash;
|
||||
@ -350,40 +360,41 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Reclaim all memory of a Stat4Accum structure.
|
||||
** Reclaim all memory of a StatAccum structure.
|
||||
*/
|
||||
static void stat4Destructor(void *pOld){
|
||||
Stat4Accum *p = (Stat4Accum*)pOld;
|
||||
static void statAccumDestructor(void *pOld){
|
||||
StatAccum *p = (StatAccum*)pOld;
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
|
||||
for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
|
||||
sampleClear(p->db, &p->current);
|
||||
if( p->mxSample ){
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
|
||||
for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
|
||||
sampleClear(p->db, &p->current);
|
||||
}
|
||||
#endif
|
||||
sqlite3DbFree(p->db, p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the stat_init(N,K,C) SQL function. The three parameters
|
||||
** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters
|
||||
** are:
|
||||
** N: The number of columns in the index including the rowid/pk (note 1)
|
||||
** K: The number of columns in the index excluding the rowid/pk.
|
||||
** C: The number of rows in the index (note 2)
|
||||
** C: Estimated number of rows in the index
|
||||
** L: A limit on the number of rows to scan, or 0 for no-limit
|
||||
**
|
||||
** Note 1: In the special case of the covering index that implements a
|
||||
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
|
||||
** total number of columns in the table.
|
||||
**
|
||||
** Note 2: C is only used for STAT4.
|
||||
**
|
||||
** For indexes on ordinary rowid tables, N==K+1. But for indexes on
|
||||
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
|
||||
** PRIMARY KEY of the table. The covering index that implements the
|
||||
** original WITHOUT ROWID table as N==K as a special case.
|
||||
**
|
||||
** This routine allocates the Stat4Accum object in heap memory. The return
|
||||
** value is a pointer to the Stat4Accum object. The datatype of the
|
||||
** return value is BLOB, but it is really just a pointer to the Stat4Accum
|
||||
** This routine allocates the StatAccum object in heap memory. The return
|
||||
** value is a pointer to the StatAccum object. The datatype of the
|
||||
** return value is BLOB, but it is really just a pointer to the StatAccum
|
||||
** object.
|
||||
*/
|
||||
static void statInit(
|
||||
@ -391,14 +402,15 @@ static void statInit(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat4Accum *p;
|
||||
StatAccum *p;
|
||||
int nCol; /* Number of columns in index being sampled */
|
||||
int nKeyCol; /* Number of key columns */
|
||||
int nColUp; /* nCol rounded up for alignment */
|
||||
int n; /* Bytes of space to allocate */
|
||||
sqlite3 *db; /* Database connection */
|
||||
sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
int mxSample = SQLITE_STAT4_SAMPLES;
|
||||
/* Maximum number of samples. 0 if STAT4 data is not collected */
|
||||
int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0;
|
||||
#endif
|
||||
|
||||
/* Decode the three function arguments */
|
||||
@ -410,16 +422,17 @@ static void statInit(
|
||||
assert( nKeyCol<=nCol );
|
||||
assert( nKeyCol>0 );
|
||||
|
||||
/* Allocate the space required for the Stat4Accum object */
|
||||
/* Allocate the space required for the StatAccum object */
|
||||
n = sizeof(*p)
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
|
||||
+ sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
|
||||
+ sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
|
||||
+ sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
|
||||
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
|
||||
if( mxSample ){
|
||||
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
|
||||
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
|
||||
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
|
||||
}
|
||||
#endif
|
||||
;
|
||||
db = sqlite3_context_db_handle(context);
|
||||
p = sqlite3DbMallocZero(db, n);
|
||||
if( p==0 ){
|
||||
@ -428,25 +441,28 @@ static void statInit(
|
||||
}
|
||||
|
||||
p->db = db;
|
||||
p->nEst = sqlite3_value_int64(argv[2]);
|
||||
p->nRow = 0;
|
||||
p->nLimit = sqlite3_value_int64(argv[3]);
|
||||
p->nCol = nCol;
|
||||
p->nKeyCol = nKeyCol;
|
||||
p->nSkipAhead = 0;
|
||||
p->current.anDLt = (tRowcnt*)&p[1];
|
||||
p->current.anEq = &p->current.anDLt[nColUp];
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
{
|
||||
p->mxSample = p->nLimit==0 ? mxSample : 0;
|
||||
if( mxSample ){
|
||||
u8 *pSpace; /* Allocated space not yet assigned */
|
||||
int i; /* Used to iterate through p->aSample[] */
|
||||
|
||||
p->iGet = -1;
|
||||
p->mxSample = mxSample;
|
||||
p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
|
||||
p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
|
||||
p->current.anLt = &p->current.anEq[nColUp];
|
||||
p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
|
||||
|
||||
/* Set up the Stat4Accum.a[] and aBest[] arrays */
|
||||
p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
|
||||
/* Set up the StatAccum.a[] and aBest[] arrays */
|
||||
p->a = (struct StatSample*)&p->current.anLt[nColUp];
|
||||
p->aBest = &p->a[mxSample];
|
||||
pSpace = (u8*)(&p->a[mxSample+nCol]);
|
||||
for(i=0; i<(mxSample+nCol); i++){
|
||||
@ -466,10 +482,10 @@ static void statInit(
|
||||
** only the pointer (the 2nd parameter) matters. The size of the object
|
||||
** (given by the 3rd parameter) is never used and can be any positive
|
||||
** value. */
|
||||
sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
|
||||
sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
|
||||
}
|
||||
static const FuncDef statInitFuncdef = {
|
||||
2+IsStat4, /* nArg */
|
||||
4, /* nArg */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
@ -493,9 +509,9 @@ static const FuncDef statInitFuncdef = {
|
||||
** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid.
|
||||
*/
|
||||
static int sampleIsBetterPost(
|
||||
Stat4Accum *pAccum,
|
||||
Stat4Sample *pNew,
|
||||
Stat4Sample *pOld
|
||||
StatAccum *pAccum,
|
||||
StatSample *pNew,
|
||||
StatSample *pOld
|
||||
){
|
||||
int nCol = pAccum->nCol;
|
||||
int i;
|
||||
@ -517,9 +533,9 @@ static int sampleIsBetterPost(
|
||||
** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid.
|
||||
*/
|
||||
static int sampleIsBetter(
|
||||
Stat4Accum *pAccum,
|
||||
Stat4Sample *pNew,
|
||||
Stat4Sample *pOld
|
||||
StatAccum *pAccum,
|
||||
StatSample *pNew,
|
||||
StatSample *pOld
|
||||
){
|
||||
tRowcnt nEqNew = pNew->anEq[pNew->iCol];
|
||||
tRowcnt nEqOld = pOld->anEq[pOld->iCol];
|
||||
@ -539,21 +555,21 @@ static int sampleIsBetter(
|
||||
** Copy the contents of sample *pNew into the p->a[] array. If necessary,
|
||||
** remove the least desirable sample from p->a[] to make room.
|
||||
*/
|
||||
static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
Stat4Sample *pSample = 0;
|
||||
static void sampleInsert(StatAccum *p, StatSample *pNew, int nEqZero){
|
||||
StatSample *pSample = 0;
|
||||
int i;
|
||||
|
||||
assert( IsStat4 || nEqZero==0 );
|
||||
|
||||
/* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0
|
||||
** values in the anEq[] array of any sample in Stat4Accum.a[]. In
|
||||
/* StatAccum.nMaxEqZero is set to the maximum number of leading 0
|
||||
** values in the anEq[] array of any sample in StatAccum.a[]. In
|
||||
** other words, if nMaxEqZero is n, then it is guaranteed that there
|
||||
** are no samples with Stat4Sample.anEq[m]==0 for (m>=n). */
|
||||
** are no samples with StatSample.anEq[m]==0 for (m>=n). */
|
||||
if( nEqZero>p->nMaxEqZero ){
|
||||
p->nMaxEqZero = nEqZero;
|
||||
}
|
||||
if( pNew->isPSample==0 ){
|
||||
Stat4Sample *pUpgrade = 0;
|
||||
StatSample *pUpgrade = 0;
|
||||
assert( pNew->anEq[pNew->iCol]>0 );
|
||||
|
||||
/* This sample is being added because the prefix that ends in column
|
||||
@ -562,7 +578,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
** this one. Instead, upgrade the priority of the highest priority
|
||||
** existing sample that shares this prefix. */
|
||||
for(i=p->nSample-1; i>=0; i--){
|
||||
Stat4Sample *pOld = &p->a[i];
|
||||
StatSample *pOld = &p->a[i];
|
||||
if( pOld->anEq[pNew->iCol]==0 ){
|
||||
if( pOld->isPSample ) return;
|
||||
assert( pOld->iCol>pNew->iCol );
|
||||
@ -581,7 +597,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
|
||||
/* If necessary, remove sample iMin to make room for the new sample. */
|
||||
if( p->nSample>=p->mxSample ){
|
||||
Stat4Sample *pMin = &p->a[p->iMin];
|
||||
StatSample *pMin = &p->a[p->iMin];
|
||||
tRowcnt *anEq = pMin->anEq;
|
||||
tRowcnt *anLt = pMin->anLt;
|
||||
tRowcnt *anDLt = pMin->anDLt;
|
||||
@ -624,20 +640,20 @@ find_new_min:
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/*
|
||||
** Field iChng of the index being scanned has changed. So at this point
|
||||
** p->current contains a sample that reflects the previous row of the
|
||||
** index. The value of anEq[iChng] and subsequent anEq[] elements are
|
||||
** correct at this point.
|
||||
*/
|
||||
static void samplePushPrevious(Stat4Accum *p, int iChng){
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
static void samplePushPrevious(StatAccum *p, int iChng){
|
||||
int i;
|
||||
|
||||
/* Check if any samples from the aBest[] array should be pushed
|
||||
** into IndexSample.a[] at this point. */
|
||||
for(i=(p->nCol-2); i>=iChng; i--){
|
||||
Stat4Sample *pBest = &p->aBest[i];
|
||||
StatSample *pBest = &p->aBest[i];
|
||||
pBest->anEq[i] = p->current.anEq[i];
|
||||
if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){
|
||||
sampleInsert(p, pBest, i);
|
||||
@ -661,27 +677,25 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
|
||||
}
|
||||
p->nMaxEqZero = iChng;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_ENABLE_STAT4
|
||||
UNUSED_PARAMETER( p );
|
||||
UNUSED_PARAMETER( iChng );
|
||||
#endif
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/*
|
||||
** Implementation of the stat_push SQL function: stat_push(P,C,R)
|
||||
** Arguments:
|
||||
**
|
||||
** P Pointer to the Stat4Accum object created by stat_init()
|
||||
** P Pointer to the StatAccum object created by stat_init()
|
||||
** C Index of left-most column to differ from previous row
|
||||
** R Rowid for the current row. Might be a key record for
|
||||
** WITHOUT ROWID tables.
|
||||
**
|
||||
** This SQL function always returns NULL. It's purpose it to accumulate
|
||||
** statistical data and/or samples in the Stat4Accum object about the
|
||||
** index being analyzed. The stat_get() SQL function will later be used to
|
||||
** extract relevant information for constructing the sqlite_statN tables.
|
||||
** The purpose of this routine is to collect statistical data and/or
|
||||
** samples from the index being analyzed into the StatAccum object.
|
||||
** The stat_get() SQL function will be used afterwards to
|
||||
** retrieve the information gathered.
|
||||
**
|
||||
** This SQL function usually returns NULL, but might return an integer
|
||||
** if it wants the byte-code to do special processing.
|
||||
**
|
||||
** The R parameter is only used for STAT4
|
||||
*/
|
||||
@ -693,7 +707,7 @@ static void statPush(
|
||||
int i;
|
||||
|
||||
/* The three function arguments */
|
||||
Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
|
||||
StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
|
||||
int iChng = sqlite3_value_int(argv[1]);
|
||||
|
||||
UNUSED_PARAMETER( argc );
|
||||
@ -706,7 +720,9 @@ static void statPush(
|
||||
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
|
||||
}else{
|
||||
/* Second and subsequent calls get processed here */
|
||||
samplePushPrevious(p, iChng);
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( p->mxSample ) samplePushPrevious(p, iChng);
|
||||
#endif
|
||||
|
||||
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
|
||||
** to the current row of the index. */
|
||||
@ -716,26 +732,25 @@ static void statPush(
|
||||
for(i=iChng; i<p->nCol; i++){
|
||||
p->current.anDLt[i]++;
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
p->current.anLt[i] += p->current.anEq[i];
|
||||
if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
|
||||
#endif
|
||||
p->current.anEq[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
p->nRow++;
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
|
||||
sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
|
||||
}else{
|
||||
sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
|
||||
sqlite3_value_blob(argv[2]));
|
||||
}
|
||||
p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
{
|
||||
tRowcnt nLt = p->current.anLt[p->nCol-1];
|
||||
if( p->mxSample ){
|
||||
tRowcnt nLt;
|
||||
if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
|
||||
sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
|
||||
}else{
|
||||
sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
|
||||
sqlite3_value_blob(argv[2]));
|
||||
}
|
||||
p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
|
||||
|
||||
nLt = p->current.anLt[p->nCol-1];
|
||||
/* Check if this is to be a periodic sample. If so, add it. */
|
||||
if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
|
||||
p->current.isPSample = 1;
|
||||
@ -751,9 +766,14 @@ static void statPush(
|
||||
sampleCopy(p, &p->aBest[i], &p->current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){
|
||||
p->nSkipAhead++;
|
||||
sqlite3_result_int(context, p->current.anDLt[0]>0);
|
||||
}
|
||||
}
|
||||
|
||||
static const FuncDef statPushFuncdef = {
|
||||
2+IsStat4, /* nArg */
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
@ -775,15 +795,15 @@ static const FuncDef statPushFuncdef = {
|
||||
/*
|
||||
** Implementation of the stat_get(P,J) SQL function. This routine is
|
||||
** used to query statistical information that has been gathered into
|
||||
** the Stat4Accum object by prior calls to stat_push(). The P parameter
|
||||
** has type BLOB but it is really just a pointer to the Stat4Accum object.
|
||||
** the StatAccum object by prior calls to stat_push(). The P parameter
|
||||
** has type BLOB but it is really just a pointer to the StatAccum object.
|
||||
** The content to returned is determined by the parameter J
|
||||
** which is one of the STAT_GET_xxxx values defined above.
|
||||
**
|
||||
** The stat_get(P,J) function is not available to generic SQL. It is
|
||||
** inserted as part of a manually constructed bytecode program. (See
|
||||
** the callStatGet() routine below.) It is guaranteed that the P
|
||||
** parameter will always be a poiner to a Stat4Accum object, never a
|
||||
** parameter will always be a pointer to a StatAccum object, never a
|
||||
** NULL.
|
||||
**
|
||||
** If STAT4 is not enabled, then J is always
|
||||
@ -796,7 +816,7 @@ static void statGet(
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
|
||||
StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/* STAT4 has a parameter on this routine. */
|
||||
int eCall = sqlite3_value_int(argv[1]);
|
||||
@ -805,6 +825,7 @@ static void statGet(
|
||||
|| eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
|
||||
|| eCall==STAT_GET_NDLT
|
||||
);
|
||||
assert( eCall==STAT_GET_STAT1 || p->mxSample );
|
||||
if( eCall==STAT_GET_STAT1 )
|
||||
#else
|
||||
assert( argc==1 );
|
||||
@ -817,7 +838,7 @@ static void statGet(
|
||||
** the index. The first integer in the list is the total number of
|
||||
** entries in the index. There is one additional integer in the list
|
||||
** for each indexed column. This additional integer is an estimate of
|
||||
** the number of rows matched by a stabbing query on the index using
|
||||
** the number of rows matched by a equality query on the index using
|
||||
** a key with the corresponding number of fields. In other words,
|
||||
** if the index is on columns (a,b) and the sqlite_stat1 value is
|
||||
** "100 10 2", then SQLite estimates that:
|
||||
@ -840,7 +861,8 @@ static void statGet(
|
||||
return;
|
||||
}
|
||||
|
||||
sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
|
||||
sqlite3_snprintf(24, zRet, "%llu",
|
||||
p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
|
||||
z = zRet + sqlite3Strlen30(zRet);
|
||||
for(i=0; i<p->nKeyCol; i++){
|
||||
u64 nDistinct = p->current.anDLt[i] + 1;
|
||||
@ -860,7 +882,7 @@ static void statGet(
|
||||
p->iGet = 0;
|
||||
}
|
||||
if( p->iGet<p->nSample ){
|
||||
Stat4Sample *pS = p->a + p->iGet;
|
||||
StatSample *pS = p->a + p->iGet;
|
||||
if( pS->nRowid==0 ){
|
||||
sqlite3_result_int64(context, pS->u.iRowid);
|
||||
}else{
|
||||
@ -916,19 +938,43 @@ static const FuncDef statGetFuncdef = {
|
||||
{0}
|
||||
};
|
||||
|
||||
static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){
|
||||
static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1);
|
||||
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1);
|
||||
#elif SQLITE_DEBUG
|
||||
assert( iParam==STAT_GET_STAT1 );
|
||||
#else
|
||||
UNUSED_PARAMETER( iParam );
|
||||
#endif
|
||||
assert( regOut!=regStat4 && regOut!=regStat4+1 );
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4,
|
||||
assert( regOut!=regStat && regOut!=regStat+1 );
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4,
|
||||
&statGetFuncdef, 0);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
/* Add a comment to the most recent VDBE opcode that is the name
|
||||
** of the k-th column of the pIdx index.
|
||||
*/
|
||||
static void analyzeVdbeCommentIndexWithColumnName(
|
||||
Vdbe *v, /* Prepared statement under construction */
|
||||
Index *pIdx, /* Index whose column is being loaded */
|
||||
int k /* Which column index */
|
||||
){
|
||||
int i; /* Index of column in the table */
|
||||
assert( k>=0 && k<pIdx->nColumn );
|
||||
i = pIdx->aiColumn[k];
|
||||
if( NEVER(i==XN_ROWID) ){
|
||||
VdbeComment((v,"%s.rowid",pIdx->zName));
|
||||
}else if( i==XN_EXPR ){
|
||||
VdbeComment((v,"%s.expr(%d)",pIdx->zName, k));
|
||||
}else{
|
||||
VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zName));
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define analyzeVdbeCommentIndexWithColumnName(a,b,c)
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Generate code to do an analysis of all indices associated with
|
||||
** a single table.
|
||||
@ -951,12 +997,11 @@ static void analyzeOneTable(
|
||||
int iDb; /* Index of database containing pTab */
|
||||
u8 needTableCnt = 1; /* True to count the table */
|
||||
int regNewRowid = iMem++; /* Rowid for the inserted record */
|
||||
int regStat4 = iMem++; /* Register to hold Stat4Accum object */
|
||||
int regStat = iMem++; /* Register to hold StatAccum object */
|
||||
int regChng = iMem++; /* Index of changed index field */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
|
||||
#endif
|
||||
int regTemp = iMem++; /* Temporary use register */
|
||||
int regTemp2 = iMem++; /* Second temporary use register */
|
||||
int regTabname = iMem++; /* Register containing table name */
|
||||
int regIdxname = iMem++; /* Register containing index name */
|
||||
int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
|
||||
@ -1084,17 +1129,26 @@ static void analyzeOneTable(
|
||||
** (1) the number of columns in the index including the rowid
|
||||
** (or for a WITHOUT ROWID table, the number of PK columns),
|
||||
** (2) the number of columns in the key without the rowid/pk
|
||||
** (3) the number of rows in the index,
|
||||
**
|
||||
**
|
||||
** The third argument is only used for STAT4
|
||||
** (3) estimated number of rows in the index,
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
|
||||
assert( regRowid==regStat+2 );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
|
||||
if( OptimizationEnabled(db, SQLITE_Stat4) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
|
||||
VdbeCoverage(v);
|
||||
}else
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4,
|
||||
{
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
|
||||
}
|
||||
assert( regTemp2==regStat+4 );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
|
||||
sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
|
||||
&statInitFuncdef, 0);
|
||||
|
||||
/* Implementation of the following:
|
||||
@ -1105,8 +1159,6 @@ static void analyzeOneTable(
|
||||
** goto next_push_0;
|
||||
**
|
||||
*/
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
|
||||
addrNextRow = sqlite3VdbeCurrentAddr(v);
|
||||
|
||||
@ -1139,6 +1191,7 @@ static void analyzeOneTable(
|
||||
char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
|
||||
analyzeVdbeCommentIndexWithColumnName(v,pIdx,i);
|
||||
aGotoChng[i] =
|
||||
sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
@ -1159,6 +1212,7 @@ static void analyzeOneTable(
|
||||
for(i=0; i<nColTest; i++){
|
||||
sqlite3VdbeJumpHere(v, aGotoChng[i]);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
|
||||
analyzeVdbeCommentIndexWithColumnName(v,pIdx,i);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, endDistinctTest);
|
||||
sqlite3DbFree(db, aGotoChng);
|
||||
@ -1172,30 +1226,46 @@ static void analyzeOneTable(
|
||||
** if !eof(csr) goto next_row;
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
assert( regRowid==(regStat4+2) );
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
int j, k, regKey;
|
||||
regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
|
||||
assert( k>=0 && k<pIdx->nColumn );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
|
||||
VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
|
||||
if( OptimizationEnabled(db, SQLITE_Stat4) ){
|
||||
assert( regRowid==(regStat+2) );
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
int j, k, regKey;
|
||||
regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
|
||||
assert( k>=0 && k<pIdx->nColumn );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
|
||||
analyzeVdbeCommentIndexWithColumnName(v,pIdx,k);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
|
||||
sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
|
||||
sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
|
||||
}
|
||||
#endif
|
||||
assert( regChng==(regStat4+1) );
|
||||
sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4,
|
||||
&statPushFuncdef, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
|
||||
assert( regChng==(regStat+1) );
|
||||
{
|
||||
sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4,
|
||||
&statPushFuncdef, 0);
|
||||
if( db->nAnalysisLimit ){
|
||||
int j1, j2, j3;
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v);
|
||||
j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v);
|
||||
j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, j2);
|
||||
sqlite3VdbeJumpHere(v, j3);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the entry to the stat1 table. */
|
||||
callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1);
|
||||
callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
|
||||
assert( "BBB"[0]==SQLITE_AFF_TEXT );
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
@ -1207,7 +1277,7 @@ static void analyzeOneTable(
|
||||
|
||||
/* Add the entries to the stat4 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
{
|
||||
if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){
|
||||
int regEq = regStat1;
|
||||
int regLt = regStat1+1;
|
||||
int regDLt = regStat1+2;
|
||||
@ -1221,12 +1291,12 @@ static void analyzeOneTable(
|
||||
pParse->nMem = MAX(pParse->nMem, regCol+nCol);
|
||||
|
||||
addrNext = sqlite3VdbeCurrentAddr(v);
|
||||
callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid);
|
||||
callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
|
||||
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
|
||||
VdbeCoverage(v);
|
||||
callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq);
|
||||
callStatGet(pParse, regStat4, STAT_GET_NLT, regLt);
|
||||
callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt);
|
||||
callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
|
||||
callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
|
||||
callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt);
|
||||
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
|
||||
VdbeCoverage(v);
|
||||
for(i=0; i<nCol; i++){
|
||||
|
37
src/attach.c
37
src/attach.c
@ -45,6 +45,17 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if zName points to a name that may be used to refer to
|
||||
** database iDb attached to handle db.
|
||||
*/
|
||||
int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName){
|
||||
return (
|
||||
sqlite3StrICmp(db->aDb[iDb].zDbSName, zName)==0
|
||||
|| (iDb==0 && sqlite3StrICmp("main", zName)==0)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** An SQL user-function registered to do the work of an ATTACH statement. The
|
||||
** three arguments to the function come directly from an attach statement:
|
||||
@ -117,9 +128,8 @@ static void attachFunc(
|
||||
goto attach_error;
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
char *z = db->aDb[i].zDbSName;
|
||||
assert( z && zName );
|
||||
if( sqlite3StrICmp(z, zName)==0 ){
|
||||
assert( zName );
|
||||
if( sqlite3DbIsNamed(db, i, zName) ){
|
||||
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
|
||||
goto attach_error;
|
||||
}
|
||||
@ -188,7 +198,7 @@ static void attachFunc(
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if( rc==SQLITE_OK ){
|
||||
extern int sqlite3CodecAttach(sqlite3*, int, const void*, int);
|
||||
@ -215,7 +225,7 @@ static void attachFunc(
|
||||
** use the key from the main database. */
|
||||
if( sqlite3CodecQueryParameters(db, zName, zPath)==0 ){
|
||||
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
|
||||
if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
|
||||
if( nKey || sqlite3BtreeGetRequestedReserve(db->aDb[0].pBt)>0 ){
|
||||
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
|
||||
}
|
||||
}
|
||||
@ -223,7 +233,8 @@ static void attachFunc(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sqlite3_free( zPath );
|
||||
/* END SQLCIPHER */
|
||||
sqlite3_free_filename( zPath );
|
||||
|
||||
/* If the file was opened successfully, read the schema for the new database.
|
||||
** If this fails, or if opening the file failed, then close the file and
|
||||
@ -308,7 +319,7 @@ static void detachFunc(
|
||||
for(i=0; i<db->nDb; i++){
|
||||
pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ) continue;
|
||||
if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
|
||||
if( sqlite3DbIsNamed(db, i, zName) ) break;
|
||||
}
|
||||
|
||||
if( i>=db->nDb ){
|
||||
@ -499,20 +510,21 @@ int sqlite3FixSrcList(
|
||||
SrcList *pList /* The Source list to check and modify */
|
||||
){
|
||||
int i;
|
||||
const char *zDb;
|
||||
struct SrcList_item *pItem;
|
||||
sqlite3 *db = pFix->pParse->db;
|
||||
int iDb = sqlite3FindDbName(db, pFix->zDb);
|
||||
|
||||
if( NEVER(pList==0) ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pFix->bTemp==0 ){
|
||||
if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
|
||||
if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
return 1;
|
||||
}
|
||||
sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
|
||||
sqlite3DbFree(db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
pItem->fg.fromDDL = 1;
|
||||
@ -624,6 +636,9 @@ int sqlite3FixTriggerStep(
|
||||
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
||||
return 1;
|
||||
}
|
||||
if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){
|
||||
return 1;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUp = pStep->pUpsert;
|
||||
|
10
src/backup.c
10
src/backup.c
@ -112,7 +112,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
|
||||
*/
|
||||
static int setDestPgsz(sqlite3_backup *p){
|
||||
int rc;
|
||||
rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
|
||||
rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -256,13 +256,15 @@ static int backupOnePage(
|
||||
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
|
||||
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
|
||||
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
/* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
|
||||
** guaranteed that the shared-mutex is held by this thread, handle
|
||||
** p->pSrc may not actually be the owner. */
|
||||
int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
|
||||
int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest);
|
||||
int nDestReserve = sqlite3BtreeGetRequestedReserve(p->pDest);
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
int rc = SQLITE_OK;
|
||||
i64 iOff;
|
||||
|
||||
@ -279,6 +281,7 @@ static int backupOnePage(
|
||||
rc = SQLITE_READONLY;
|
||||
}
|
||||
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
/* Backup is not possible if the page size of the destination is changing
|
||||
** and a codec is in use.
|
||||
@ -298,6 +301,7 @@ static int backupOnePage(
|
||||
if( rc==SQLITE_OK && newPgsz!=(u32)nSrcPgsz ) rc = SQLITE_READONLY;
|
||||
}
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
/* This loop runs once for each destination page spanned by the source
|
||||
** page. For each iteration, variable iOff is set to the byte offset
|
||||
@ -794,9 +798,11 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
b.pDest = pTo;
|
||||
b.iNext = 1;
|
||||
|
||||
/* BEGIN SQLCIPHER */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom));
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
|
||||
** file. By passing this as the number of pages to copy to
|
||||
|
283
src/btree.c
283
src/btree.c
@ -69,7 +69,7 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */
|
||||
** but the test harness needs to access it so we make it global for
|
||||
** test builds.
|
||||
**
|
||||
** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER.
|
||||
** Access to this variable is protected by SQLITE_MUTEX_STATIC_MAIN.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
|
||||
@ -200,16 +200,18 @@ static int hasSharedCacheTableLock(
|
||||
** table. */
|
||||
if( isIndex ){
|
||||
HashElem *p;
|
||||
int bSeen = 0;
|
||||
for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){
|
||||
Index *pIdx = (Index *)sqliteHashData(p);
|
||||
if( pIdx->tnum==(int)iRoot ){
|
||||
if( iTab ){
|
||||
if( bSeen ){
|
||||
/* Two or more indexes share the same root page. There must
|
||||
** be imposter tables. So just return true. The assert is not
|
||||
** useful in that case. */
|
||||
return 1;
|
||||
}
|
||||
iTab = pIdx->pTable->tnum;
|
||||
bSeen = 1;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@ -355,7 +357,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
|
||||
|
||||
/* A connection with the read-uncommitted flag set will never try to
|
||||
** obtain a read-lock using this function. The only read-lock obtained
|
||||
** by a connection in read-uncommitted mode is on the sqlite_master
|
||||
** by a connection in read-uncommitted mode is on the sqlite_schema
|
||||
** table, and that lock is obtained in BtreeBeginTrans(). */
|
||||
assert( 0==(p->db->flags&SQLITE_ReadUncommit) || eLock==WRITE_LOCK );
|
||||
|
||||
@ -611,7 +613,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
|
||||
*/
|
||||
static int btreeGetHasContent(BtShared *pBt, Pgno pgno){
|
||||
Bitvec *p = pBt->pHasContent;
|
||||
return (p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno)));
|
||||
return p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTestNotNull(p, pgno));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -991,7 +993,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
|
||||
if( *pRC ) return;
|
||||
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
/* The master-journal page number must never be used as a pointer map page */
|
||||
/* The super-journal page number must never be used as a pointer map page */
|
||||
assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
|
||||
|
||||
assert( pBt->autoVacuum );
|
||||
@ -1449,7 +1451,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
|
||||
int sz2 = 0;
|
||||
int sz = get2byte(&data[iFree+2]);
|
||||
int top = get2byte(&data[hdr+5]);
|
||||
if( NEVER(top>=iFree) ){
|
||||
if( top>=iFree ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
if( iFree2 ){
|
||||
@ -1751,7 +1753,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
nFrag = iFreeBlk - iEnd;
|
||||
if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
|
||||
if( NEVER(iEnd > pPage->pBt->usableSize) ){
|
||||
if( iEnd > pPage->pBt->usableSize ){
|
||||
return SQLITE_CORRUPT_PAGE(pPage);
|
||||
}
|
||||
iSize = iEnd - iStart;
|
||||
@ -1780,7 +1782,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
|
||||
** so just extend the cell content area rather than create another
|
||||
** freelist entry */
|
||||
if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
if( NEVER(iPtr!=hdr+1) ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||
put2byte(&data[hdr+1], iFreeBlk);
|
||||
put2byte(&data[hdr+5], iEnd);
|
||||
}else{
|
||||
@ -2137,12 +2139,11 @@ static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){
|
||||
** error, return ((unsigned int)-1).
|
||||
*/
|
||||
static Pgno btreePagecount(BtShared *pBt){
|
||||
assert( (pBt->nPage & 0x80000000)==0 || CORRUPT_DB );
|
||||
return pBt->nPage;
|
||||
}
|
||||
u32 sqlite3BtreeLastPage(Btree *p){
|
||||
Pgno sqlite3BtreeLastPage(Btree *p){
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
return btreePagecount(p->pBt) & 0x7fffffff;
|
||||
return btreePagecount(p->pBt);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2304,8 +2305,7 @@ static int btreeInvokeBusyHandler(void *pArg){
|
||||
BtShared *pBt = (BtShared*)pArg;
|
||||
assert( pBt->db );
|
||||
assert( sqlite3_mutex_held(pBt->db->mutex) );
|
||||
return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
|
||||
sqlite3PagerFile(pBt->pPager));
|
||||
return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2421,7 +2421,7 @@ int sqlite3BtreeOpen(
|
||||
#if SQLITE_THREADSAFE
|
||||
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
|
||||
sqlite3_mutex_enter(mutexOpen);
|
||||
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
|
||||
sqlite3_mutex_enter(mutexShared);
|
||||
#endif
|
||||
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
|
||||
@ -2540,7 +2540,7 @@ int sqlite3BtreeOpen(
|
||||
pBt->nRef = 1;
|
||||
if( p->sharable ){
|
||||
MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
|
||||
MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
|
||||
MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);)
|
||||
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
|
||||
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
|
||||
if( pBt->mutex==0 ){
|
||||
@ -2629,13 +2629,13 @@ btree_open_out:
|
||||
*/
|
||||
static int removeFromSharingList(BtShared *pBt){
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMaster; )
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMainMtx; )
|
||||
BtShared *pList;
|
||||
int removed = 0;
|
||||
|
||||
assert( sqlite3_mutex_notheld(pBt->mutex) );
|
||||
MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
|
||||
sqlite3_mutex_enter(pMainMtx);
|
||||
pBt->nRef--;
|
||||
if( pBt->nRef<=0 ){
|
||||
if( GLOBAL(BtShared*,sqlite3SharedCacheList)==pBt ){
|
||||
@ -2654,7 +2654,7 @@ static int removeFromSharingList(BtShared *pBt){
|
||||
}
|
||||
removed = 1;
|
||||
}
|
||||
sqlite3_mutex_leave(pMaster);
|
||||
sqlite3_mutex_leave(pMainMtx);
|
||||
return removed;
|
||||
#else
|
||||
return 1;
|
||||
@ -2856,19 +2856,17 @@ int sqlite3BtreeSetPagerFlags(
|
||||
*/
|
||||
int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
||||
int rc = SQLITE_OK;
|
||||
int x;
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( nReserve>=-1 && nReserve<=255 );
|
||||
assert( nReserve>=0 && nReserve<=255 );
|
||||
sqlite3BtreeEnter(p);
|
||||
#if SQLITE_HAS_CODEC
|
||||
if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve;
|
||||
#endif
|
||||
pBt->nReserveWanted = nReserve;
|
||||
x = pBt->pageSize - pBt->usableSize;
|
||||
if( nReserve<x ) nReserve = x;
|
||||
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
|
||||
sqlite3BtreeLeave(p);
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
if( nReserve<0 ){
|
||||
nReserve = pBt->pageSize - pBt->usableSize;
|
||||
}
|
||||
assert( nReserve>=0 && nReserve<=255 );
|
||||
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
|
||||
((pageSize-1)&pageSize)==0 ){
|
||||
@ -2914,19 +2912,17 @@ int sqlite3BtreeGetReserveNoMutex(Btree *p){
|
||||
** are intentually left unused. This is the "reserved" space that is
|
||||
** sometimes used by extensions.
|
||||
**
|
||||
** If SQLITE_HAS_MUTEX is defined then the number returned is the
|
||||
** greater of the current reserved space and the maximum requested
|
||||
** reserve space.
|
||||
** The value returned is the larger of the current reserve size and
|
||||
** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES.
|
||||
** The amount of reserve can only grow - never shrink.
|
||||
*/
|
||||
int sqlite3BtreeGetOptimalReserve(Btree *p){
|
||||
int n;
|
||||
int sqlite3BtreeGetRequestedReserve(Btree *p){
|
||||
int n1, n2;
|
||||
sqlite3BtreeEnter(p);
|
||||
n = sqlite3BtreeGetReserveNoMutex(p);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if( n<p->pBt->optimalReserve ) n = p->pBt->optimalReserve;
|
||||
#endif
|
||||
n1 = (int)p->pBt->nReserveWanted;
|
||||
n2 = sqlite3BtreeGetReserveNoMutex(p);
|
||||
sqlite3BtreeLeave(p);
|
||||
return n;
|
||||
return n1>n2 ? n1 : n2;
|
||||
}
|
||||
|
||||
|
||||
@ -2935,8 +2931,8 @@ int sqlite3BtreeGetOptimalReserve(Btree *p){
|
||||
** No changes are made if mxPage is 0 or negative.
|
||||
** Regardless of the value of mxPage, return the maximum page count.
|
||||
*/
|
||||
int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
|
||||
int n;
|
||||
Pgno sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){
|
||||
Pgno n;
|
||||
sqlite3BtreeEnter(p);
|
||||
n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
|
||||
sqlite3BtreeLeave(p);
|
||||
@ -3376,6 +3372,7 @@ int sqlite3BtreeNewDb(Btree *p){
|
||||
*/
|
||||
int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
|
||||
BtShared *pBt = p->pBt;
|
||||
Pager *pPager = pBt->pPager;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
@ -3391,7 +3388,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
|
||||
assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
|
||||
|
||||
if( (p->db->flags & SQLITE_ResetDatabase)
|
||||
&& sqlite3PagerIsreadonly(pBt->pPager)==0
|
||||
&& sqlite3PagerIsreadonly(pPager)==0
|
||||
){
|
||||
pBt->btsFlags &= ~BTS_READ_ONLY;
|
||||
}
|
||||
@ -3433,12 +3430,24 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
|
||||
/* Any read-only or read-write transaction implies a read-lock on
|
||||
** page 1. So if some other shared-cache client already has a write-lock
|
||||
** on page 1, the transaction cannot be opened. */
|
||||
rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
|
||||
rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
|
||||
if( SQLITE_OK!=rc ) goto trans_begun;
|
||||
|
||||
pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
|
||||
if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
|
||||
do {
|
||||
sqlite3PagerWalDb(pPager, p->db);
|
||||
|
||||
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
||||
/* If transitioning from no transaction directly to a write transaction,
|
||||
** block for the WRITER lock first if possible. */
|
||||
if( pBt->pPage1==0 && wrflag ){
|
||||
assert( pBt->inTransaction==TRANS_NONE );
|
||||
rc = sqlite3PagerWalWriteLock(pPager, 1);
|
||||
if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Call lockBtree() until either pBt->pPage1 is populated or
|
||||
** lockBtree() returns something other than SQLITE_OK. lockBtree()
|
||||
** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
|
||||
@ -3452,7 +3461,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
|
||||
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
|
||||
rc = SQLITE_READONLY;
|
||||
}else{
|
||||
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
|
||||
rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = newDatabase(pBt);
|
||||
}else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
|
||||
@ -3465,11 +3474,15 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
(void)sqlite3PagerWalWriteLock(pPager, 0);
|
||||
unlockBtreeIfUnused(pBt);
|
||||
}
|
||||
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
|
||||
btreeInvokeBusyHandler(pBt) );
|
||||
sqlite3PagerResetLockTimeout(pBt->pPager);
|
||||
sqlite3PagerWalDb(pPager, 0);
|
||||
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
||||
if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
|
||||
#endif
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->inTrans==TRANS_NONE ){
|
||||
@ -3521,7 +3534,7 @@ trans_begun:
|
||||
** open savepoints. If the second parameter is greater than 0 and
|
||||
** the sub-journal is not already open, then it will be opened here.
|
||||
*/
|
||||
rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
|
||||
rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3872,7 +3885,7 @@ int sqlite3BtreeIncrVacuum(Btree *p){
|
||||
Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
|
||||
Pgno nFin = finalDbSize(pBt, nOrig, nFree);
|
||||
|
||||
if( nOrig<nFin ){
|
||||
if( nOrig<nFin || nFree>=nOrig ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else if( nFree>0 ){
|
||||
rc = saveAllCursors(pBt, 0, 0);
|
||||
@ -3969,18 +3982,18 @@ static int autoVacuumCommit(BtShared *pBt){
|
||||
**
|
||||
** This call is a no-op if no write-transaction is currently active on pBt.
|
||||
**
|
||||
** Otherwise, sync the database file for the btree pBt. zMaster points to
|
||||
** the name of a master journal file that should be written into the
|
||||
** individual journal file, or is NULL, indicating no master journal file
|
||||
** Otherwise, sync the database file for the btree pBt. zSuperJrnl points to
|
||||
** the name of a super-journal file that should be written into the
|
||||
** individual journal file, or is NULL, indicating no super-journal file
|
||||
** (single database transaction).
|
||||
**
|
||||
** When this is called, the master journal should already have been
|
||||
** When this is called, the super-journal should already have been
|
||||
** created, populated with this journal pointer and synced to disk.
|
||||
**
|
||||
** Once this is routine has returned, the only thing required to commit
|
||||
** the write-transaction for this database file is to delete the journal.
|
||||
*/
|
||||
int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
|
||||
int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->inTrans==TRANS_WRITE ){
|
||||
BtShared *pBt = p->pBt;
|
||||
@ -3997,7 +4010,7 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
|
||||
sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
|
||||
}
|
||||
#endif
|
||||
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
|
||||
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0);
|
||||
sqlite3BtreeLeave(p);
|
||||
}
|
||||
return rc;
|
||||
@ -4060,7 +4073,7 @@ static void btreeEndTransaction(Btree *p){
|
||||
** the upper layer will attempt a rollback. However, if the second argument
|
||||
** is non-zero then this b-tree transaction is part of a multi-file
|
||||
** transaction. In this case, the transaction has already been committed
|
||||
** (by deleting a master journal file) and the caller will ignore this
|
||||
** (by deleting a super-journal file) and the caller will ignore this
|
||||
** functions return code. So, even if an error occurs in the pager layer,
|
||||
** reset the b-tree objects internal state to indicate that the write
|
||||
** transaction has been closed. This is quite safe, as the pager will have
|
||||
@ -4358,7 +4371,7 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
||||
*/
|
||||
static int btreeCursor(
|
||||
Btree *p, /* The btree */
|
||||
int iTable, /* Root page of table to open */
|
||||
Pgno iTable, /* Root page of table to open */
|
||||
int wrFlag, /* 1 to write. 0 read-only */
|
||||
struct KeyInfo *pKeyInfo, /* First arg to comparison function */
|
||||
BtCursor *pCur /* Space for new cursor */
|
||||
@ -4401,7 +4414,7 @@ static int btreeCursor(
|
||||
|
||||
/* Now that no other errors can occur, finish filling in the BtCursor
|
||||
** variables and link the cursor into the BtShared list. */
|
||||
pCur->pgnoRoot = (Pgno)iTable;
|
||||
pCur->pgnoRoot = iTable;
|
||||
pCur->iPage = -1;
|
||||
pCur->pKeyInfo = pKeyInfo;
|
||||
pCur->pBtree = p;
|
||||
@ -4411,7 +4424,7 @@ static int btreeCursor(
|
||||
/* If there are two or more cursors on the same btree, then all such
|
||||
** cursors *must* have the BTCF_Multiple flag set. */
|
||||
for(pX=pBt->pCursor; pX; pX=pX->pNext){
|
||||
if( pX->pgnoRoot==(Pgno)iTable ){
|
||||
if( pX->pgnoRoot==iTable ){
|
||||
pX->curFlags |= BTCF_Multiple;
|
||||
pCur->curFlags |= BTCF_Multiple;
|
||||
}
|
||||
@ -4423,7 +4436,7 @@ static int btreeCursor(
|
||||
}
|
||||
static int btreeCursorWithLock(
|
||||
Btree *p, /* The btree */
|
||||
int iTable, /* Root page of table to open */
|
||||
Pgno iTable, /* Root page of table to open */
|
||||
int wrFlag, /* 1 to write. 0 read-only */
|
||||
struct KeyInfo *pKeyInfo, /* First arg to comparison function */
|
||||
BtCursor *pCur /* Space for new cursor */
|
||||
@ -4436,7 +4449,7 @@ static int btreeCursorWithLock(
|
||||
}
|
||||
int sqlite3BtreeCursor(
|
||||
Btree *p, /* The btree */
|
||||
int iTable, /* Root page of table to open */
|
||||
Pgno iTable, /* Root page of table to open */
|
||||
int wrFlag, /* 1 to write. 0 read-only */
|
||||
struct KeyInfo *pKeyInfo, /* First arg to xCompare() */
|
||||
BtCursor *pCur /* Write new cursor here */
|
||||
@ -4822,7 +4835,7 @@ static int accessPayload(
|
||||
Pgno nextPage;
|
||||
|
||||
nextPage = get4byte(&aPayload[pCur->info.nLocal]);
|
||||
|
||||
|
||||
/* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
|
||||
**
|
||||
** The aOverflow[] array is sized at one entry for each overflow page
|
||||
@ -4861,6 +4874,7 @@ static int accessPayload(
|
||||
assert( rc==SQLITE_OK && amt>0 );
|
||||
while( nextPage ){
|
||||
/* If required, populate the overflow page-list cache. */
|
||||
if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT;
|
||||
assert( pCur->aOverflow[iIdx]==0
|
||||
|| pCur->aOverflow[iIdx]==nextPage
|
||||
|| CORRUPT_DB );
|
||||
@ -6276,6 +6290,10 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
||||
u32 nLeaf; /* Initial number of leaf cells on trunk page */
|
||||
|
||||
iTrunk = get4byte(&pPage1->aData[32]);
|
||||
if( iTrunk>btreePagecount(pBt) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto freepage_out;
|
||||
}
|
||||
rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto freepage_out;
|
||||
@ -7157,7 +7175,7 @@ static int editPage(
|
||||
assert( nCell>=0 );
|
||||
if( iOld<iNew ){
|
||||
int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
|
||||
if( nShift>nCell ) return SQLITE_CORRUPT_BKPT;
|
||||
if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT;
|
||||
memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
|
||||
nCell -= nShift;
|
||||
}
|
||||
@ -8758,7 +8776,11 @@ int sqlite3BtreeInsert(
|
||||
assert( pPage->intKey || pX->nKey>=0 );
|
||||
assert( pPage->leaf || !pPage->intKey );
|
||||
if( pPage->nFree<0 ){
|
||||
rc = btreeComputeFreeSpace(pPage);
|
||||
if( pCur->eState>CURSOR_INVALID ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
rc = btreeComputeFreeSpace(pPage);
|
||||
}
|
||||
if( rc ) return rc;
|
||||
}
|
||||
|
||||
@ -9076,7 +9098,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
|
||||
** BTREE_ZERODATA Used for SQL indices
|
||||
*/
|
||||
static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
|
||||
BtShared *pBt = p->pBt;
|
||||
MemPage *pRoot;
|
||||
Pgno pgnoRoot;
|
||||
@ -9109,6 +9131,9 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
** created so far, so the new root-page is (meta[3]+1).
|
||||
*/
|
||||
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
|
||||
if( pgnoRoot>btreePagecount(pBt) ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pgnoRoot++;
|
||||
|
||||
/* The new root-page may not be allocated on a pointer-map page, or the
|
||||
@ -9118,8 +9143,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
|
||||
pgnoRoot++;
|
||||
}
|
||||
assert( pgnoRoot>=3 || CORRUPT_DB );
|
||||
testcase( pgnoRoot<3 );
|
||||
assert( pgnoRoot>=3 );
|
||||
|
||||
/* Allocate a page. The page that currently resides at pgnoRoot will
|
||||
** be moved to the allocated page (unless the allocated page happens
|
||||
@ -9216,10 +9240,10 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
||||
zeroPage(pRoot, ptfFlags);
|
||||
sqlite3PagerUnref(pRoot->pDbPage);
|
||||
assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 );
|
||||
*piTable = (int)pgnoRoot;
|
||||
*piTable = pgnoRoot;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
|
||||
int sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){
|
||||
int rc;
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = btreeCreateTable(p, piTable, flags);
|
||||
@ -9465,7 +9489,7 @@ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( p->inTrans>TRANS_NONE );
|
||||
assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) );
|
||||
assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) );
|
||||
assert( pBt->pPage1 );
|
||||
assert( idx>=0 && idx<=15 );
|
||||
|
||||
@ -9514,7 +9538,6 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
/*
|
||||
** The first argument, pCur, is a cursor opened on some b-tree. Count the
|
||||
** number of entries in the b-tree and write the result to *pnEntry.
|
||||
@ -9536,7 +9559,7 @@ int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){
|
||||
/* Unless an error occurs, the following loop runs one iteration for each
|
||||
** page in the B-Tree structure (not including overflow pages).
|
||||
*/
|
||||
while( rc==SQLITE_OK && !db->u1.isInterrupted ){
|
||||
while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){
|
||||
int iIdx; /* Index of child node in parent */
|
||||
MemPage *pPage; /* Current page of the b-tree */
|
||||
|
||||
@ -9587,7 +9610,6 @@ int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){
|
||||
/* An error has occurred. Return an error code. */
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the pager associated with a BTree. This routine is used for
|
||||
@ -9620,7 +9642,7 @@ static void checkAppendMsg(
|
||||
sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
|
||||
va_end(ap);
|
||||
if( pCheck->errMsg.accError==SQLITE_NOMEM ){
|
||||
pCheck->mallocFailed = 1;
|
||||
pCheck->bOomFault = 1;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
@ -9662,7 +9684,7 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage){
|
||||
checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
|
||||
return 1;
|
||||
}
|
||||
if( pCheck->db->u1.isInterrupted ) return 1;
|
||||
if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1;
|
||||
setPageReferenced(pCheck, iPage);
|
||||
return 0;
|
||||
}
|
||||
@ -9685,7 +9707,7 @@ static void checkPtrmap(
|
||||
|
||||
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
|
||||
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->bOomFault = 1;
|
||||
checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
|
||||
return;
|
||||
}
|
||||
@ -9705,7 +9727,7 @@ static void checkPtrmap(
|
||||
static void checkList(
|
||||
IntegrityCk *pCheck, /* Integrity checking context */
|
||||
int isFreeList, /* True for a freelist. False for overflow page list */
|
||||
int iPage, /* Page number for first page in the list */
|
||||
Pgno iPage, /* Page number for first page in the list */
|
||||
u32 N /* Expected number of pages in the list */
|
||||
){
|
||||
int i;
|
||||
@ -9837,7 +9859,7 @@ static int btreeHeapPull(u32 *aHeap, u32 *pOut){
|
||||
*/
|
||||
static int checkTreePage(
|
||||
IntegrityCk *pCheck, /* Context for the sanity check */
|
||||
int iPage, /* Page number of the page to check */
|
||||
Pgno iPage, /* Page number of the page to check */
|
||||
i64 *piMinKey, /* Write minimum integer primary key here */
|
||||
i64 maxKey /* Error if integer primary key greater than this */
|
||||
){
|
||||
@ -9873,9 +9895,9 @@ static int checkTreePage(
|
||||
usableSize = pBt->usableSize;
|
||||
if( iPage==0 ) return 0;
|
||||
if( checkRef(pCheck, iPage) ) return 0;
|
||||
pCheck->zPfx = "Page %d: ";
|
||||
pCheck->zPfx = "Page %u: ";
|
||||
pCheck->v1 = iPage;
|
||||
if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
|
||||
if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
|
||||
checkAppendMsg(pCheck,
|
||||
"unable to get the page. error code=%d", rc);
|
||||
goto end_of_check;
|
||||
@ -9900,7 +9922,7 @@ static int checkTreePage(
|
||||
hdr = pPage->hdrOffset;
|
||||
|
||||
/* Set up for cell analysis */
|
||||
pCheck->zPfx = "On tree page %d cell %d: ";
|
||||
pCheck->zPfx = "On tree page %u cell %d: ";
|
||||
contentOffset = get2byteNotZero(&data[hdr+5]);
|
||||
assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
|
||||
|
||||
@ -9920,7 +9942,7 @@ static int checkTreePage(
|
||||
pgno = get4byte(&data[hdr+8]);
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum ){
|
||||
pCheck->zPfx = "On page %d at right child: ";
|
||||
pCheck->zPfx = "On page %u at right child: ";
|
||||
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
|
||||
}
|
||||
#endif
|
||||
@ -10061,7 +10083,7 @@ static int checkTreePage(
|
||||
while( btreeHeapPull(heap,&x) ){
|
||||
if( (prev&0xffff)>=(x>>16) ){
|
||||
checkAppendMsg(pCheck,
|
||||
"Multiple uses for byte %u of page %d", x>>16, iPage);
|
||||
"Multiple uses for byte %u of page %u", x>>16, iPage);
|
||||
break;
|
||||
}else{
|
||||
nFrag += (x>>16) - (prev&0xffff) - 1;
|
||||
@ -10076,7 +10098,7 @@ static int checkTreePage(
|
||||
*/
|
||||
if( heap[0]==0 && nFrag!=data[hdr+7] ){
|
||||
checkAppendMsg(pCheck,
|
||||
"Fragmentation of %d bytes reported as %d on page %d",
|
||||
"Fragmentation of %d bytes reported as %d on page %u",
|
||||
nFrag, data[hdr+7], iPage);
|
||||
}
|
||||
}
|
||||
@ -10104,11 +10126,20 @@ end_of_check:
|
||||
** allocation errors, an error message held in memory obtained from
|
||||
** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is
|
||||
** returned. If a memory allocation error occurs, NULL is returned.
|
||||
**
|
||||
** If the first entry in aRoot[] is 0, that indicates that the list of
|
||||
** root pages is incomplete. This is a "partial integrity-check". This
|
||||
** happens when performing an integrity check on a single table. The
|
||||
** zero is skipped, of course. But in addition, the freelist checks
|
||||
** and the checks to make sure every page is referenced are also skipped,
|
||||
** since obviously it is not possible to know which pages are covered by
|
||||
** the unverified btrees. Except, if aRoot[1] is 1, then the freelist
|
||||
** checks are still performed.
|
||||
*/
|
||||
char *sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
Btree *p, /* The btree to be checked */
|
||||
int *aRoot, /* An array of root pages numbers for individual trees */
|
||||
Pgno *aRoot, /* An array of root pages numbers for individual trees */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
int mxErr, /* Stop reporting errors after this many */
|
||||
int *pnErr /* Write number of errors seen to this variable */
|
||||
@ -10118,7 +10149,17 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
BtShared *pBt = p->pBt;
|
||||
u64 savedDbFlags = pBt->db->flags;
|
||||
char zErr[100];
|
||||
int bPartial = 0; /* True if not checking all btrees */
|
||||
int bCkFreelist = 1; /* True to scan the freelist */
|
||||
VVA_ONLY( int nRef );
|
||||
assert( nRoot>0 );
|
||||
|
||||
/* aRoot[0]==0 means this is a partial check */
|
||||
if( aRoot[0]==0 ){
|
||||
assert( nRoot>1 );
|
||||
bPartial = 1;
|
||||
if( aRoot[1]!=1 ) bCkFreelist = 0;
|
||||
}
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
|
||||
@ -10130,7 +10171,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
sCheck.nPage = btreePagecount(sCheck.pBt);
|
||||
sCheck.mxErr = mxErr;
|
||||
sCheck.nErr = 0;
|
||||
sCheck.mallocFailed = 0;
|
||||
sCheck.bOomFault = 0;
|
||||
sCheck.zPfx = 0;
|
||||
sCheck.v1 = 0;
|
||||
sCheck.v2 = 0;
|
||||
@ -10144,12 +10185,12 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
|
||||
sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
|
||||
if( !sCheck.aPgRef ){
|
||||
sCheck.mallocFailed = 1;
|
||||
sCheck.bOomFault = 1;
|
||||
goto integrity_ck_cleanup;
|
||||
}
|
||||
sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
|
||||
if( sCheck.heap==0 ){
|
||||
sCheck.mallocFailed = 1;
|
||||
sCheck.bOomFault = 1;
|
||||
goto integrity_ck_cleanup;
|
||||
}
|
||||
|
||||
@ -10158,29 +10199,33 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
|
||||
/* Check the integrity of the freelist
|
||||
*/
|
||||
sCheck.zPfx = "Main freelist: ";
|
||||
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
|
||||
get4byte(&pBt->pPage1->aData[36]));
|
||||
sCheck.zPfx = 0;
|
||||
if( bCkFreelist ){
|
||||
sCheck.zPfx = "Main freelist: ";
|
||||
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
|
||||
get4byte(&pBt->pPage1->aData[36]));
|
||||
sCheck.zPfx = 0;
|
||||
}
|
||||
|
||||
/* Check all the tables.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum ){
|
||||
int mx = 0;
|
||||
int mxInHdr;
|
||||
for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
|
||||
mxInHdr = get4byte(&pBt->pPage1->aData[52]);
|
||||
if( mx!=mxInHdr ){
|
||||
if( !bPartial ){
|
||||
if( pBt->autoVacuum ){
|
||||
Pgno mx = 0;
|
||||
Pgno mxInHdr;
|
||||
for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
|
||||
mxInHdr = get4byte(&pBt->pPage1->aData[52]);
|
||||
if( mx!=mxInHdr ){
|
||||
checkAppendMsg(&sCheck,
|
||||
"max rootpage (%d) disagrees with header (%d)",
|
||||
mx, mxInHdr
|
||||
);
|
||||
}
|
||||
}else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
|
||||
checkAppendMsg(&sCheck,
|
||||
"max rootpage (%d) disagrees with header (%d)",
|
||||
mx, mxInHdr
|
||||
"incremental_vacuum enabled with a max rootpage of zero"
|
||||
);
|
||||
}
|
||||
}else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
|
||||
checkAppendMsg(&sCheck,
|
||||
"incremental_vacuum enabled with a max rootpage of zero"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
testcase( pBt->db->flags & SQLITE_CellSizeCk );
|
||||
@ -10189,7 +10234,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
i64 notUsed;
|
||||
if( aRoot[i]==0 ) continue;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum && aRoot[i]>1 ){
|
||||
if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
|
||||
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
|
||||
}
|
||||
#endif
|
||||
@ -10199,24 +10244,26 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
|
||||
/* Make sure every page in the file is referenced
|
||||
*/
|
||||
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
|
||||
if( !bPartial ){
|
||||
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
if( getPageReferenced(&sCheck, i)==0 ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
if( getPageReferenced(&sCheck, i)==0 ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
#else
|
||||
/* If the database supports auto-vacuum, make sure no tables contain
|
||||
** references to pointer-map pages.
|
||||
*/
|
||||
if( getPageReferenced(&sCheck, i)==0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
if( getPageReferenced(&sCheck, i)!=0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
|
||||
}
|
||||
/* If the database supports auto-vacuum, make sure no tables contain
|
||||
** references to pointer-map pages.
|
||||
*/
|
||||
if( getPageReferenced(&sCheck, i)==0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
if( getPageReferenced(&sCheck, i)!=0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up and report errors.
|
||||
@ -10224,7 +10271,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
integrity_ck_cleanup:
|
||||
sqlite3PageFree(sCheck.heap);
|
||||
sqlite3_free(sCheck.aPgRef);
|
||||
if( sCheck.mallocFailed ){
|
||||
if( sCheck.bOomFault ){
|
||||
sqlite3_str_reset(&sCheck.errMsg);
|
||||
sCheck.nErr++;
|
||||
}
|
||||
@ -10344,13 +10391,13 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
|
||||
/*
|
||||
** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
|
||||
** btree as the argument handle holds an exclusive lock on the
|
||||
** sqlite_master table. Otherwise SQLITE_OK.
|
||||
** sqlite_schema table. Otherwise SQLITE_OK.
|
||||
*/
|
||||
int sqlite3BtreeSchemaLocked(Btree *p){
|
||||
int rc;
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
|
||||
rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE );
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
|
16
src/btree.h
16
src/btree.h
@ -71,20 +71,20 @@ int sqlite3BtreeSetSpillSize(Btree*,int);
|
||||
int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
|
||||
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
|
||||
int sqlite3BtreeGetPageSize(Btree*);
|
||||
int sqlite3BtreeMaxPageCount(Btree*,int);
|
||||
u32 sqlite3BtreeLastPage(Btree*);
|
||||
Pgno sqlite3BtreeMaxPageCount(Btree*,Pgno);
|
||||
Pgno sqlite3BtreeLastPage(Btree*);
|
||||
int sqlite3BtreeSecureDelete(Btree*,int);
|
||||
int sqlite3BtreeGetOptimalReserve(Btree*);
|
||||
int sqlite3BtreeGetRequestedReserve(Btree*);
|
||||
int sqlite3BtreeGetReserveNoMutex(Btree *p);
|
||||
int sqlite3BtreeSetAutoVacuum(Btree *, int);
|
||||
int sqlite3BtreeGetAutoVacuum(Btree *);
|
||||
int sqlite3BtreeBeginTrans(Btree*,int,int*);
|
||||
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
||||
int sqlite3BtreeCommitPhaseOne(Btree*, const char*);
|
||||
int sqlite3BtreeCommitPhaseTwo(Btree*, int);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*,int,int);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
@ -225,7 +225,7 @@ int sqlite3BtreeNewDb(Btree *p);
|
||||
|
||||
int sqlite3BtreeCursor(
|
||||
Btree*, /* BTree containing table to open */
|
||||
int iTable, /* Index of root page */
|
||||
Pgno iTable, /* Index of root page */
|
||||
int wrFlag, /* 1 for writing. 0 for read-only */
|
||||
struct KeyInfo*, /* First argument to compare function */
|
||||
BtCursor *pCursor /* Space to write cursor structure */
|
||||
@ -316,7 +316,7 @@ const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
|
||||
u32 sqlite3BtreePayloadSize(BtCursor*);
|
||||
sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*);
|
||||
|
||||
char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,int*aRoot,int nRoot,int,int*);
|
||||
char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*);
|
||||
struct Pager *sqlite3BtreePager(Btree*);
|
||||
i64 sqlite3BtreeRowCountEst(BtCursor*);
|
||||
|
||||
@ -336,9 +336,7 @@ int sqlite3BtreeCursorIsValid(BtCursor*);
|
||||
#endif
|
||||
int sqlite3BtreeCursorIsValidNN(BtCursor*);
|
||||
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
|
||||
|
@ -381,7 +381,7 @@ struct Btree {
|
||||
**
|
||||
** Fields in this structure are accessed under the BtShared.mutex
|
||||
** mutex, except for nRef and pNext which are accessed under the
|
||||
** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
|
||||
** global SQLITE_MUTEX_STATIC_MAIN mutex. The pPager field
|
||||
** may not be modified once it is initially set as long as nRef>0.
|
||||
** The pSchema field may be set once under BtShared.mutex and
|
||||
** thereafter is unchanged as long as nRef>0.
|
||||
@ -417,9 +417,7 @@ struct BtShared {
|
||||
#endif
|
||||
u8 inTransaction; /* Transaction state */
|
||||
u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
u8 optimalReserve; /* Desired amount of reserved space per page */
|
||||
#endif
|
||||
u8 nReserveWanted; /* Desired number of extra bytes per page */
|
||||
u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
|
||||
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
|
||||
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
|
||||
@ -681,9 +679,10 @@ struct IntegrityCk {
|
||||
Pgno nPage; /* Number of pages in the database */
|
||||
int mxErr; /* Stop accumulating errors when this reaches zero */
|
||||
int nErr; /* Number of messages written to zErrMsg so far */
|
||||
int mallocFailed; /* A memory allocation error has occurred */
|
||||
int bOomFault; /* A memory allocation error has occurred */
|
||||
const char *zPfx; /* Error message prefix */
|
||||
int v1, v2; /* Values for up to two %d fields in zPfx */
|
||||
Pgno v1; /* Value for first %u substitution in zPfx */
|
||||
int v2; /* Value for second %d substitution in zPfx */
|
||||
StrAccum errMsg; /* Accumulate the error message text here */
|
||||
u32 *heap; /* Min-heap used for analyzing cell coverage */
|
||||
sqlite3 *db; /* Database connection running the check */
|
||||
|
329
src/build.c
329
src/build.c
@ -31,7 +31,7 @@
|
||||
*/
|
||||
struct TableLock {
|
||||
int iDb; /* The database containing the table to be locked */
|
||||
int iTab; /* The root page of the table to be locked */
|
||||
Pgno iTab; /* The root page of the table to be locked */
|
||||
u8 isWriteLock; /* True for write lock. False for a read lock */
|
||||
const char *zLockName; /* Name of the table */
|
||||
};
|
||||
@ -49,7 +49,7 @@ struct TableLock {
|
||||
void sqlite3TableLock(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int iDb, /* Index of the database containing the table to lock */
|
||||
int iTab, /* Root page number of the table to be locked */
|
||||
Pgno iTab, /* Root page number of the table to be locked */
|
||||
u8 isWriteLock, /* True for a write lock */
|
||||
const char *zName /* Name of the table to be locked */
|
||||
){
|
||||
@ -207,12 +207,21 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
*/
|
||||
sqlite3AutoincrementBegin(pParse);
|
||||
|
||||
/* Code constant expressions that where factored out of inner loops */
|
||||
/* Code constant expressions that where factored out of inner loops.
|
||||
**
|
||||
** The pConstExpr list might also contain expressions that we simply
|
||||
** want to keep around until the Parse object is deleted. Such
|
||||
** expressions have iConstExprReg==0. Do not generate code for
|
||||
** those expressions, of course.
|
||||
*/
|
||||
if( pParse->pConstExpr ){
|
||||
ExprList *pEL = pParse->pConstExpr;
|
||||
pParse->okConstFactor = 0;
|
||||
for(i=0; i<pEL->nExpr; i++){
|
||||
sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
|
||||
int iReg = pEL->a[i].u.iConstExprReg;
|
||||
if( iReg>0 ){
|
||||
sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +253,7 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
** outermost parser.
|
||||
**
|
||||
** Not everything is nestable. This facility is designed to permit
|
||||
** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER. Use
|
||||
** INSERT, UPDATE, and DELETE operations against the schema table. Use
|
||||
** care if you decide to try to use this routine for some other purposes.
|
||||
*/
|
||||
void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
|
||||
@ -312,22 +321,59 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
while(1){
|
||||
for(i=OMIT_TEMPDB; i<db->nDb; i++){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
|
||||
if( p ) return p;
|
||||
if( zDatabase ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break;
|
||||
}
|
||||
if( i>=db->nDb ){
|
||||
/* No match against the official names. But always match "main"
|
||||
** to schema 0 as a legacy fallback. */
|
||||
if( sqlite3StrICmp(zDatabase,"main")==0 ){
|
||||
i = 0;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName);
|
||||
if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
|
||||
if( i==1 ){
|
||||
if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0
|
||||
|| sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0
|
||||
|| sqlite3StrICmp(zName+7, &DFLT_SCHEMA_TABLE[7])==0
|
||||
){
|
||||
p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash,
|
||||
DFLT_TEMP_SCHEMA_TABLE);
|
||||
}
|
||||
}else{
|
||||
if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){
|
||||
p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash,
|
||||
DFLT_SCHEMA_TABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* Match against TEMP first */
|
||||
p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, zName);
|
||||
if( p ) return p;
|
||||
/* The main database is second */
|
||||
p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, zName);
|
||||
if( p ) return p;
|
||||
/* Attached databases are in order of attachment */
|
||||
for(i=2; i<db->nDb; i++){
|
||||
assert( sqlite3SchemaMutexHeld(db, i, 0) );
|
||||
p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName);
|
||||
if( p ) break;
|
||||
}
|
||||
if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
|
||||
if( sqlite3StrICmp(zName+7, &ALT_SCHEMA_TABLE[7])==0 ){
|
||||
p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, DFLT_SCHEMA_TABLE);
|
||||
}else if( sqlite3StrICmp(zName+7, &ALT_TEMP_SCHEMA_TABLE[7])==0 ){
|
||||
p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash,
|
||||
DFLT_TEMP_SCHEMA_TABLE);
|
||||
}
|
||||
}
|
||||
/* Not found. If the name we were looking for was temp.sqlite_master
|
||||
** then change the name to sqlite_temp_master and try again. */
|
||||
if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break;
|
||||
if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break;
|
||||
zName = TEMP_MASTER_NAME;
|
||||
}
|
||||
return 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -437,7 +483,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
||||
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
|
||||
Schema *pSchema = db->aDb[j].pSchema;
|
||||
assert( pSchema );
|
||||
if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
|
||||
if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue;
|
||||
assert( sqlite3SchemaMutexHeld(db, j, 0) );
|
||||
p = sqlite3HashFind(&pSchema->idxHash, zName);
|
||||
if( p ) break;
|
||||
@ -590,6 +636,7 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
|
||||
assert( pTable!=0 );
|
||||
if( (pCol = pTable->aCol)!=0 ){
|
||||
for(i=0; i<pTable->nCol; i++, pCol++){
|
||||
assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) );
|
||||
sqlite3DbFree(db, pCol->zName);
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
sqlite3DbFree(db, pCol->zColl);
|
||||
@ -716,13 +763,13 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
|
||||
}
|
||||
|
||||
/*
|
||||
** Open the sqlite_master table stored in database number iDb for
|
||||
** Open the sqlite_schema table stored in database number iDb for
|
||||
** writing. The table is opened using cursor 0.
|
||||
*/
|
||||
void sqlite3OpenMasterTable(Parse *p, int iDb){
|
||||
void sqlite3OpenSchemaTable(Parse *p, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(p);
|
||||
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, MASTER_NAME);
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5);
|
||||
sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, DFLT_SCHEMA_TABLE);
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5);
|
||||
if( p->nTab==0 ){
|
||||
p->nTab = 1;
|
||||
}
|
||||
@ -830,7 +877,7 @@ int sqlite3WritableSchema(sqlite3 *db){
|
||||
** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
|
||||
** is reserved for internal use.
|
||||
**
|
||||
** When parsing the sqlite_master table, this routine also checks to
|
||||
** When parsing the sqlite_schema table, this routine also checks to
|
||||
** make sure the "type", "name", and "tbl_name" columns are consistent
|
||||
** with the SQL.
|
||||
*/
|
||||
@ -841,7 +888,10 @@ int sqlite3CheckObjectName(
|
||||
const char *zTblName /* Parent table name for triggers and indexes */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
if( sqlite3WritableSchema(db) || db->init.imposterTable ){
|
||||
if( sqlite3WritableSchema(db)
|
||||
|| db->init.imposterTable
|
||||
|| !sqlite3Config.bExtraSchemaChecks
|
||||
){
|
||||
/* Skip these error checks for writable_schema=ON */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -850,10 +900,8 @@ int sqlite3CheckObjectName(
|
||||
|| sqlite3_stricmp(zName, db->init.azInit[1])
|
||||
|| sqlite3_stricmp(zTblName, db->init.azInit[2])
|
||||
){
|
||||
if( sqlite3Config.bExtraSchemaChecks ){
|
||||
sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}else{
|
||||
if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7))
|
||||
@ -1002,7 +1050,7 @@ void sqlite3StartTable(
|
||||
Token *pName; /* Unqualified name of the table to create */
|
||||
|
||||
if( db->init.busy && db->init.newTnum==1 ){
|
||||
/* Special case: Parsing the sqlite_master or sqlite_temp_master schema */
|
||||
/* Special case: Parsing the sqlite_schema or sqlite_temp_schema schema */
|
||||
iDb = db->init.iDb;
|
||||
zName = sqlite3DbStrDup(db, SCHEMA_TABLE(iDb));
|
||||
pName = pName1;
|
||||
@ -1108,7 +1156,7 @@ void sqlite3StartTable(
|
||||
#endif
|
||||
|
||||
/* Begin generating the code that will insert the table record into
|
||||
** the SQLITE_MASTER table. Note in particular that we must go ahead
|
||||
** the schema table. Note in particular that we must go ahead
|
||||
** and allocate the record number for the table entry now. Before any
|
||||
** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause
|
||||
** indices to be created and the table record must come before the
|
||||
@ -1144,7 +1192,7 @@ void sqlite3StartTable(
|
||||
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, ENC(db));
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
|
||||
/* This just creates a place-holder record in the sqlite_master table.
|
||||
/* This just creates a place-holder record in the sqlite_schema table.
|
||||
** The record created does not contain anything yet. It will be replaced
|
||||
** by the real entry in code generated at sqlite3EndTable().
|
||||
**
|
||||
@ -1162,7 +1210,7 @@ void sqlite3StartTable(
|
||||
pParse->addrCrTab =
|
||||
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
|
||||
}
|
||||
sqlite3OpenMasterTable(pParse, iDb);
|
||||
sqlite3OpenSchemaTable(pParse, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
|
||||
sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
|
||||
@ -1238,6 +1286,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
|
||||
pCol = &p->aCol[p->nCol];
|
||||
memset(pCol, 0, sizeof(p->aCol[0]));
|
||||
pCol->zName = z;
|
||||
pCol->hName = sqlite3StrIHash(z);
|
||||
sqlite3ColumnPropertiesFromName(p, pCol);
|
||||
|
||||
if( pType->n==0 ){
|
||||
@ -1971,9 +2020,9 @@ static void recomputeColumnsNotIndexed(Index *pIdx){
|
||||
** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
|
||||
** (2) Convert P3 parameter of the OP_CreateBtree from BTREE_INTKEY
|
||||
** into BTREE_BLOBKEY.
|
||||
** (3) Bypass the creation of the sqlite_master table entry
|
||||
** (3) Bypass the creation of the sqlite_schema table entry
|
||||
** for the PRIMARY KEY as the primary key index is now
|
||||
** identified by the sqlite_master table entry of the table itself.
|
||||
** identified by the sqlite_schema table entry of the table itself.
|
||||
** (4) Set the Index.tnum of the PRIMARY KEY Index object in the
|
||||
** schema to the rootpage from the main table.
|
||||
** (5) Add all table columns to the PRIMARY KEY Index object
|
||||
@ -2060,13 +2109,13 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
|
||||
nPk = pPk->nColumn = pPk->nKeyCol;
|
||||
|
||||
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
|
||||
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_schema
|
||||
** table entry. This is only required if currently generating VDBE
|
||||
** code for a CREATE TABLE (not when parsing one as part of reading
|
||||
** a database schema). */
|
||||
if( v && pPk->tnum>0 ){
|
||||
assert( db->init.busy==0 );
|
||||
sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto);
|
||||
sqlite3VdbeChangeOpcode(v, (int)pPk->tnum, OP_Goto);
|
||||
}
|
||||
|
||||
/* The root page of the PRIMARY KEY is the table root page */
|
||||
@ -2129,6 +2178,28 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
recomputeColumnsNotIndexed(pPk);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Return true if pTab is a virtual table and zName is a shadow table name
|
||||
** for that virtual table.
|
||||
*/
|
||||
int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char *zName){
|
||||
int nName; /* Length of zName */
|
||||
Module *pMod; /* Module for the virtual table */
|
||||
|
||||
if( !IsVirtual(pTab) ) return 0;
|
||||
nName = sqlite3Strlen30(pTab->zName);
|
||||
if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0;
|
||||
if( zName[nName]!='_' ) return 0;
|
||||
pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
|
||||
if( pMod==0 ) return 0;
|
||||
if( pMod->pModule->iVersion<3 ) return 0;
|
||||
if( pMod->pModule->xShadowName==0 ) return 0;
|
||||
return pMod->pModule->xShadowName(zName+nName+1);
|
||||
}
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Return true if zName is a shadow table name in the current database
|
||||
@ -2140,8 +2211,6 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
|
||||
char *zTail; /* Pointer to the last "_" in zName */
|
||||
Table *pTab; /* Table that zName is a shadow of */
|
||||
Module *pMod; /* Module for the virtual table */
|
||||
|
||||
zTail = strrchr(zName, '_');
|
||||
if( zTail==0 ) return 0;
|
||||
*zTail = 0;
|
||||
@ -2149,14 +2218,37 @@ int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
|
||||
*zTail = '_';
|
||||
if( pTab==0 ) return 0;
|
||||
if( !IsVirtual(pTab) ) return 0;
|
||||
pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
|
||||
if( pMod==0 ) return 0;
|
||||
if( pMod->pModule->iVersion<3 ) return 0;
|
||||
if( pMod->pModule->xShadowName==0 ) return 0;
|
||||
return pMod->pModule->xShadowName(zTail+1);
|
||||
return sqlite3IsShadowTableOf(db, pTab, zName);
|
||||
}
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Mark all nodes of an expression as EP_Immutable, indicating that
|
||||
** they should not be changed. Expressions attached to a table or
|
||||
** index definition are tagged this way to help ensure that we do
|
||||
** not pass them into code generator routines by mistake.
|
||||
*/
|
||||
static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){
|
||||
ExprSetVVAProperty(pExpr, EP_Immutable);
|
||||
return WRC_Continue;
|
||||
}
|
||||
static void markExprListImmutable(ExprList *pList){
|
||||
if( pList ){
|
||||
Walker w;
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = markImmutableExprStep;
|
||||
w.xSelectCallback = sqlite3SelectWalkNoop;
|
||||
w.xSelectCallback2 = 0;
|
||||
sqlite3WalkExprList(&w, pList);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define markExprListImmutable(X) /* no-op */
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
|
||||
/*
|
||||
** This routine is called to report the final ")" that terminates
|
||||
** a CREATE TABLE statement.
|
||||
@ -2165,12 +2257,12 @@ int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
|
||||
** is added to the internal hash tables, assuming no errors have
|
||||
** occurred.
|
||||
**
|
||||
** An entry for the table is made in the master table on disk, unless
|
||||
** An entry for the table is made in the schema table on disk, unless
|
||||
** this is a temporary table or db->init.busy==1. When db->init.busy==1
|
||||
** it means we are reading the sqlite_master table because we just
|
||||
** connected to the database or because the sqlite_master table has
|
||||
** it means we are reading the sqlite_schema table because we just
|
||||
** connected to the database or because the sqlite_schema table has
|
||||
** recently changed, so the entry for this table already exists in
|
||||
** the sqlite_master table. We do not want to create it again.
|
||||
** the sqlite_schema table. We do not want to create it again.
|
||||
**
|
||||
** If the pSelect argument is not NULL, it means that this routine
|
||||
** was called to create a table generated from a
|
||||
@ -2201,12 +2293,12 @@ void sqlite3EndTable(
|
||||
}
|
||||
|
||||
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
||||
** "sqlite_master" or "sqlite_temp_master" table on the disk.
|
||||
** "sqlite_schema" or "sqlite_temp_schema" table on the disk.
|
||||
** So do not write to the disk again. Extract the root page number
|
||||
** for the table from the db->init.newTnum field. (The page number
|
||||
** should have been put there by the sqliteOpenCb routine.)
|
||||
**
|
||||
** If the root page number is 1, that means this is the sqlite_master
|
||||
** If the root page number is 1, that means this is the sqlite_schema
|
||||
** table itself. So mark it read-only.
|
||||
*/
|
||||
if( db->init.busy ){
|
||||
@ -2249,6 +2341,8 @@ void sqlite3EndTable(
|
||||
** actually be used if PRAGMA writable_schema=ON is set. */
|
||||
sqlite3ExprListDelete(db, p->pCheck);
|
||||
p->pCheck = 0;
|
||||
}else{
|
||||
markExprListImmutable(p->pCheck);
|
||||
}
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
@ -2291,7 +2385,7 @@ void sqlite3EndTable(
|
||||
}
|
||||
|
||||
/* If not initializing, then create a record for the new table
|
||||
** in the SQLITE_MASTER table of the database.
|
||||
** in the schema table of the database.
|
||||
**
|
||||
** If this is a TEMPORARY table, write the entry into the auxiliary
|
||||
** file instead of into the main database file.
|
||||
@ -2393,14 +2487,14 @@ void sqlite3EndTable(
|
||||
}
|
||||
|
||||
/* A slot for the record has already been allocated in the
|
||||
** SQLITE_MASTER table. We just need to update that slot with all
|
||||
** schema table. We just need to update that slot with all
|
||||
** the information we've collected.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s "
|
||||
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
|
||||
"WHERE rowid=#%d",
|
||||
db->aDb[iDb].zDbSName, MASTER_NAME,
|
||||
"UPDATE %Q." DFLT_SCHEMA_TABLE
|
||||
" SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q"
|
||||
" WHERE rowid=#%d",
|
||||
db->aDb[iDb].zDbSName,
|
||||
zType,
|
||||
p->zName,
|
||||
p->zName,
|
||||
@ -2528,7 +2622,7 @@ void sqlite3CreateView(
|
||||
sEnd.z = &z[n-1];
|
||||
sEnd.n = 1;
|
||||
|
||||
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
|
||||
/* Use sqlite3EndTable() to add the view to the schema table */
|
||||
sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
|
||||
|
||||
create_view_fail:
|
||||
@ -2609,10 +2703,8 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
assert( pTable->pSelect );
|
||||
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
|
||||
if( pSel ){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
u8 eParseMode = pParse->eParseMode;
|
||||
pParse->eParseMode = PARSE_MODE_NORMAL;
|
||||
#endif
|
||||
n = pParse->nTab;
|
||||
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
|
||||
pTable->nCol = -1;
|
||||
@ -2660,9 +2752,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
EnableLookaside;
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
pParse->eParseMode = eParseMode;
|
||||
#endif
|
||||
} else {
|
||||
nErr++;
|
||||
}
|
||||
@ -2717,7 +2807,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
|
||||
** in order to be certain that we got the right one.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
|
||||
void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){
|
||||
HashElem *pElem;
|
||||
Hash *pHash;
|
||||
Db *pDb;
|
||||
@ -2743,7 +2833,7 @@ void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
|
||||
|
||||
/*
|
||||
** Write code to erase the table with root-page iTable from database iDb.
|
||||
** Also write code to modify the sqlite_master table and internal schema
|
||||
** Also write code to modify the sqlite_schema table and internal schema
|
||||
** if a root-page of another table is moved by the btree-layer whilst
|
||||
** erasing iTable (this can happen with an auto-vacuum database).
|
||||
*/
|
||||
@ -2756,7 +2846,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
/* OP_Destroy stores an in integer r1. If this integer
|
||||
** is non-zero, then it is the root page number of a table moved to
|
||||
** location iTable. The following code modifies the sqlite_master table to
|
||||
** location iTable. The following code modifies the sqlite_schema table to
|
||||
** reflect this.
|
||||
**
|
||||
** The "#NNN" in the SQL is a special constant that means whatever value
|
||||
@ -2764,15 +2854,16 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
||||
** token for additional information.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
|
||||
pParse->db->aDb[iDb].zDbSName, MASTER_NAME, iTable, r1, r1);
|
||||
"UPDATE %Q." DFLT_SCHEMA_TABLE
|
||||
" SET rootpage=%d WHERE #%d AND rootpage=#%d",
|
||||
pParse->db->aDb[iDb].zDbSName, iTable, r1, r1);
|
||||
#endif
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write VDBE code to erase table pTab and all associated indices on disk.
|
||||
** Code to update the sqlite_master tables and internal schema definitions
|
||||
** Code to update the sqlite_schema tables and internal schema definitions
|
||||
** in case a root-page belonging to another table is moved by the btree layer
|
||||
** is also added (this can happen with an auto-vacuum database).
|
||||
*/
|
||||
@ -2793,18 +2884,18 @@ static void destroyTable(Parse *pParse, Table *pTab){
|
||||
** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
|
||||
** a free-list page.
|
||||
*/
|
||||
int iTab = pTab->tnum;
|
||||
int iDestroyed = 0;
|
||||
Pgno iTab = pTab->tnum;
|
||||
Pgno iDestroyed = 0;
|
||||
|
||||
while( 1 ){
|
||||
Index *pIdx;
|
||||
int iLargest = 0;
|
||||
Pgno iLargest = 0;
|
||||
|
||||
if( iDestroyed==0 || iTab<iDestroyed ){
|
||||
iLargest = iTab;
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int iIdx = pIdx->tnum;
|
||||
Pgno iIdx = pIdx->tnum;
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
|
||||
iLargest = iIdx;
|
||||
@ -2865,8 +2956,8 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
#endif
|
||||
|
||||
/* Drop all triggers associated with the table being dropped. Code
|
||||
** is generated to remove entries from sqlite_master and/or
|
||||
** sqlite_temp_master if required.
|
||||
** is generated to remove entries from sqlite_schema and/or
|
||||
** sqlite_temp_schema if required.
|
||||
*/
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
@ -2890,16 +2981,17 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop all SQLITE_MASTER table and index entries that refer to the
|
||||
** table. The program name loops through the master table and deletes
|
||||
/* Drop all entries in the schema table that refer to the
|
||||
** table. The program name loops through the schema table and deletes
|
||||
** every row that refers to a table of the same name as the one being
|
||||
** dropped. Triggers are handled separately because a trigger can be
|
||||
** created in the temp database that refers to a table in another
|
||||
** database.
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zDbSName, MASTER_NAME, pTab->zName);
|
||||
"DELETE FROM %Q." DFLT_SCHEMA_TABLE
|
||||
" WHERE tbl_name=%Q and type!='trigger'",
|
||||
pDb->zDbSName, pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
@ -3035,7 +3127,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code to remove the table from the master table
|
||||
/* Generate code to remove the table from the schema table
|
||||
** on disk.
|
||||
*/
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
@ -3226,7 +3318,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
int iSorter; /* Cursor opened by OpenSorter (if in use) */
|
||||
int addr1; /* Address of top of loop */
|
||||
int addr2; /* Address to jump to for next iteration */
|
||||
int tnum; /* Root page of index */
|
||||
Pgno tnum; /* Root page of index */
|
||||
int iPartIdxLabel; /* Jump to this label to skip a row */
|
||||
Vdbe *v; /* Generate code into this virtual machine */
|
||||
KeyInfo *pKey; /* KeyInfo for index */
|
||||
@ -3247,7 +3339,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) return;
|
||||
if( memRootPage>=0 ){
|
||||
tnum = memRootPage;
|
||||
tnum = (Pgno)memRootPage;
|
||||
}else{
|
||||
tnum = pIndex->tnum;
|
||||
}
|
||||
@ -3272,7 +3364,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, (int)tnum, iDb,
|
||||
(char *)pKey, P4_KEYINFO);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
|
||||
|
||||
@ -3489,10 +3581,7 @@ void sqlite3CreateIndex(
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
&& sqlite3UserAuthTable(pTab->zName)==0
|
||||
#endif
|
||||
#ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX
|
||||
&& sqlite3StrICmp(&pTab->zName[7],"master")!=0
|
||||
#endif
|
||||
){
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
@ -3514,7 +3603,7 @@ void sqlite3CreateIndex(
|
||||
** index or table with the same name.
|
||||
**
|
||||
** Exception: If we are reading the names of permanent indices from the
|
||||
** sqlite_master table (because some other process changed the schema) and
|
||||
** sqlite_schema table (because some other process changed the schema) and
|
||||
** one of the index names collides with the name of a temporary table or
|
||||
** index, then we will continue to process this index.
|
||||
**
|
||||
@ -3858,8 +3947,8 @@ void sqlite3CreateIndex(
|
||||
/* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
|
||||
** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
|
||||
** emit code to allocate the index rootpage on disk and make an entry for
|
||||
** the index in the sqlite_master table and populate the index with
|
||||
** content. But, do not do this if we are simply reading the sqlite_master
|
||||
** the index in the sqlite_schema table and populate the index with
|
||||
** content. But, do not do this if we are simply reading the sqlite_schema
|
||||
** table to parse the schema, or if this index is the PRIMARY KEY index
|
||||
** of a WITHOUT ROWID table.
|
||||
**
|
||||
@ -3884,7 +3973,7 @@ void sqlite3CreateIndex(
|
||||
** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
|
||||
** that case the convertToWithoutRowidTable() routine will replace
|
||||
** the Noop with a Goto to jump over the VDBE code generated below. */
|
||||
pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
pIndex->tnum = (Pgno)sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY);
|
||||
|
||||
/* Gather the complete text of the CREATE INDEX statement into
|
||||
@ -3903,11 +3992,11 @@ void sqlite3CreateIndex(
|
||||
zStmt = 0;
|
||||
}
|
||||
|
||||
/* Add an entry in sqlite_master for this index
|
||||
/* Add an entry in sqlite_schema for this index
|
||||
*/
|
||||
sqlite3NestedParse(pParse,
|
||||
"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
|
||||
db->aDb[iDb].zDbSName, MASTER_NAME,
|
||||
"INSERT INTO %Q." DFLT_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);",
|
||||
db->aDb[iDb].zDbSName,
|
||||
pIndex->zName,
|
||||
pTab->zName,
|
||||
iMem,
|
||||
@ -3926,7 +4015,7 @@ void sqlite3CreateIndex(
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
|
||||
}
|
||||
|
||||
sqlite3VdbeJumpHere(v, pIndex->tnum);
|
||||
sqlite3VdbeJumpHere(v, (int)pIndex->tnum);
|
||||
}
|
||||
}
|
||||
if( db->init.busy || pTblName==0 ){
|
||||
@ -3983,9 +4072,10 @@ exit_create_index:
|
||||
** are based on typical values found in actual indices.
|
||||
*/
|
||||
void sqlite3DefaultRowEst(Index *pIdx){
|
||||
/* 10, 9, 8, 7, 6 */
|
||||
LogEst aVal[] = { 33, 32, 30, 28, 26 };
|
||||
/* 10, 9, 8, 7, 6 */
|
||||
static const LogEst aVal[] = { 33, 32, 30, 28, 26 };
|
||||
LogEst *a = pIdx->aiRowLogEst;
|
||||
LogEst x;
|
||||
int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
|
||||
int i;
|
||||
|
||||
@ -3994,10 +4084,21 @@ void sqlite3DefaultRowEst(Index *pIdx){
|
||||
|
||||
/* Set the first entry (number of rows in the index) to the estimated
|
||||
** number of rows in the table, or half the number of rows in the table
|
||||
** for a partial index. But do not let the estimate drop below 10. */
|
||||
a[0] = pIdx->pTable->nRowLogEst;
|
||||
if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) );
|
||||
if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
|
||||
** for a partial index.
|
||||
**
|
||||
** 2020-05-27: If some of the stat data is coming from the sqlite_stat1
|
||||
** table but other parts we are having to guess at, then do not let the
|
||||
** estimated number of rows in the table be less than 1000 (LogEst 99).
|
||||
** Failure to do this can cause the indexes for which we do not have
|
||||
** stat1 data to be ignored by the query planner.
|
||||
*/
|
||||
x = pIdx->pTable->nRowLogEst;
|
||||
assert( 99==sqlite3LogEst(1000) );
|
||||
if( x<99 ){
|
||||
pIdx->pTable->nRowLogEst = x = 99;
|
||||
}
|
||||
if( pIdx->pPartIdxWhere!=0 ) x -= 10; assert( 10==sqlite3LogEst(2) );
|
||||
a[0] = x;
|
||||
|
||||
/* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
|
||||
** 6 and each subsequent value (if any) is 5. */
|
||||
@ -4060,13 +4161,13 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Generate code to remove the index and from the master table */
|
||||
/* Generate code to remove the index and from the schema table */
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v ){
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zDbSName, MASTER_NAME, pIndex->zName
|
||||
"DELETE FROM %Q." DFLT_SCHEMA_TABLE " WHERE name=%Q AND type='index'",
|
||||
db->aDb[iDb].zDbSName, pIndex->zName
|
||||
);
|
||||
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
@ -4465,6 +4566,26 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Append the contents of SrcList p2 to SrcList p1 and return the resulting
|
||||
** SrcList. Or, if an error occurs, return NULL. In all cases, p1 and p2
|
||||
** are deleted by this function.
|
||||
*/
|
||||
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
|
||||
assert( p1 && p1->nSrc==1 );
|
||||
if( p2 ){
|
||||
SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1);
|
||||
if( pNew==0 ){
|
||||
sqlite3SrcListDelete(pParse->db, p2);
|
||||
}else{
|
||||
p1 = pNew;
|
||||
memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item));
|
||||
sqlite3DbFree(pParse->db, p2);
|
||||
}
|
||||
}
|
||||
return p1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the list of function arguments to the SrcList entry for a
|
||||
** table-valued-function.
|
||||
@ -4600,7 +4721,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){
|
||||
}
|
||||
db->aDb[1].pBt = pBt;
|
||||
assert( db->aDb[1].pSchema );
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){
|
||||
sqlite3OomFault(db);
|
||||
return 1;
|
||||
}
|
||||
@ -4711,7 +4832,7 @@ void sqlite3HaltConstraint(
|
||||
u8 p5Errmsg /* P5_ErrMsg type */
|
||||
){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
assert( (errCode&0xff)==SQLITE_CONSTRAINT );
|
||||
assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
|
||||
if( onError==OE_Abort ){
|
||||
sqlite3MayAbort(pParse);
|
||||
}
|
||||
|
@ -163,17 +163,30 @@ CollSeq *sqlite3FindCollSeq(
|
||||
int create /* True to create CollSeq if doesn't already exist */
|
||||
){
|
||||
CollSeq *pColl;
|
||||
assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
||||
assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
|
||||
if( zName ){
|
||||
pColl = findCollSeqEntry(db, zName, create);
|
||||
if( pColl ) pColl += enc-1;
|
||||
}else{
|
||||
pColl = db->pDfltColl;
|
||||
}
|
||||
assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
||||
assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
|
||||
if( pColl ) pColl += enc-1;
|
||||
return pColl;
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the text encoding for a database connection. This means that
|
||||
** the pDfltColl must change as well.
|
||||
*/
|
||||
void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
db->enc = enc;
|
||||
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
|
||||
** strings is BINARY.
|
||||
*/
|
||||
db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is responsible for invoking the collation factory callback
|
||||
** or substituting a collation sequence of a different encoding when the
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
** SQLCipher
|
||||
** http://sqlcipher.net
|
||||
**
|
||||
@ -119,7 +119,10 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && zRight ) {
|
||||
if(ctx) {
|
||||
char *deprecation = "PRAGMA cipher_store_pass is deprecated, please remove from use";
|
||||
sqlcipher_codec_set_store_pass(ctx, sqlite3GetBoolean(zRight, 1));
|
||||
codec_vdbe_return_string(pParse, "cipher_store_pass", deprecation, P4_TRANSIENT);
|
||||
sqlite3_log(SQLITE_WARNING, deprecation);
|
||||
}
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) {
|
||||
@ -318,7 +321,6 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
} else {
|
||||
char *size = sqlite3_mprintf("%d", sqlcipher_get_default_plaintext_header_size());
|
||||
codec_vdbe_return_string(pParse, "cipher_default_plaintext_header_size", size, P4_DYNAMIC);
|
||||
sqlite3_free(size);
|
||||
}
|
||||
}else
|
||||
if( sqlite3StrICmp(zLeft,"cipher_salt")==0 ){
|
||||
@ -1026,7 +1028,6 @@ void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **ar
|
||||
int saved_nChange = db->nChange; /* Saved value of db->nChange */
|
||||
int saved_nTotalChange = db->nTotalChange; /* Saved value of db->nTotalChange */
|
||||
u8 saved_mTrace = db->mTrace; /* Saved value of db->mTrace */
|
||||
int (*saved_xTrace)(u32,void*,void*,void*) = db->xTrace; /* Saved db->xTrace */
|
||||
int rc = SQLITE_OK; /* Return code from service routines */
|
||||
char *zSql = NULL; /* SQL statements */
|
||||
char *pzErrMsg = NULL;
|
||||
@ -1053,7 +1054,6 @@ void sqlcipher_exportFunc(sqlite3_context *context, int argc, sqlite3_value **ar
|
||||
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
|
||||
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
|
||||
db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows);
|
||||
db->xTrace = 0;
|
||||
db->mTrace = 0;
|
||||
|
||||
/* Query the schema of the main database. Create a mirror schema
|
||||
@ -1133,7 +1133,6 @@ end_of_export:
|
||||
db->mDbFlags = saved_mDbFlags;
|
||||
db->nChange = saved_nChange;
|
||||
db->nTotalChange = saved_nTotalChange;
|
||||
db->xTrace = saved_xTrace;
|
||||
db->mTrace = saved_mTrace;
|
||||
|
||||
if(zSql) sqlite3_free(zSql);
|
||||
|
@ -59,7 +59,7 @@ void sqlite3pager_reset(Pager *pPager);
|
||||
#define CIPHER_STR(s) #s
|
||||
|
||||
#ifndef CIPHER_VERSION_NUMBER
|
||||
#define CIPHER_VERSION_NUMBER 4.4.0
|
||||
#define CIPHER_VERSION_NUMBER 4.4.1
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER_VERSION_BUILD
|
||||
|
@ -1419,7 +1419,7 @@ migrate:
|
||||
pDb = &(db->aDb[db->nDb-1]);
|
||||
pSrc = pDb->pBt;
|
||||
|
||||
nRes = sqlite3BtreeGetOptimalReserve(pSrc);
|
||||
nRes = sqlite3BtreeGetRequestedReserve(pSrc);
|
||||
/* unset the BTS_PAGESIZE_FIXED flag to avoid SQLITE_READONLY */
|
||||
pDest->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
|
||||
rc = sqlite3BtreeSetPageSize(pDest, default_page_size, nRes, 0);
|
||||
|
@ -193,6 +193,9 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|
||||
"ENABLE_BATCH_ATOMIC_WRITE",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_BYTECODE_VTAB
|
||||
"ENABLE_BYTECODE_VTAB",
|
||||
#endif
|
||||
#if SQLITE_ENABLE_CEROD
|
||||
"ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
|
||||
#endif
|
||||
@ -355,9 +358,11 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_FTS5_NO_WITHOUT_ROWID
|
||||
"FTS5_NO_WITHOUT_ROWID",
|
||||
#endif
|
||||
/* BEGIN SQLCIPHER */
|
||||
#if SQLITE_HAS_CODEC
|
||||
"HAS_CODEC",
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
|
||||
"HAVE_ISNAN",
|
||||
#endif
|
||||
@ -514,9 +519,6 @@ static const char * const sqlite3azCompileOpt[] = {
|
||||
#if SQLITE_OMIT_BLOB_LITERAL
|
||||
"OMIT_BLOB_LITERAL",
|
||||
#endif
|
||||
#if SQLITE_OMIT_BTREECOUNT
|
||||
"OMIT_BTREECOUNT",
|
||||
#endif
|
||||
#if SQLITE_OMIT_CAST
|
||||
"OMIT_CAST",
|
||||
#endif
|
||||
|
22
src/date.c
22
src/date.c
@ -515,7 +515,7 @@ static int osLocaltime(time_t *t, struct tm *pTm){
|
||||
#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S
|
||||
struct tm *pX;
|
||||
#if SQLITE_THREADSAFE>0
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
|
||||
#endif
|
||||
sqlite3_mutex_enter(mutex);
|
||||
pX = localtime(t);
|
||||
@ -621,12 +621,12 @@ static const struct {
|
||||
double rLimit; /* Maximum NNN value for this transform */
|
||||
double rXform; /* Constant used for this transform */
|
||||
} aXformType[] = {
|
||||
{ 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
|
||||
{ 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
|
||||
{ 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
|
||||
{ 0, 3, "day", 5373485.0, 86400000.0 },
|
||||
{ 1, 5, "month", 176546.0, 30.0*86400000.0 },
|
||||
{ 2, 4, "year", 14713.0, 365.0*86400000.0 },
|
||||
{ 0, 6, "second", 464269060800.0, 1000.0 },
|
||||
{ 0, 6, "minute", 7737817680.0, 60000.0 },
|
||||
{ 0, 4, "hour", 128963628.0, 3600000.0 },
|
||||
{ 0, 3, "day", 5373485.0, 86400000.0 },
|
||||
{ 1, 5, "month", 176546.0, 2592000000.0 },
|
||||
{ 2, 4, "year", 14713.0, 31536000000.0 },
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1112,8 +1112,8 @@ static void strftimeFunc(
|
||||
case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break;
|
||||
case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break;
|
||||
case 's': {
|
||||
sqlite3_snprintf(30,&z[j],"%lld",
|
||||
(i64)(x.iJD/1000 - 21086676*(i64)10000));
|
||||
i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000);
|
||||
sqlite3Int64ToText(iS, &z[j]);
|
||||
j += sqlite3Strlen30(&z[j]);
|
||||
break;
|
||||
}
|
||||
@ -1211,10 +1211,10 @@ static void currentTimeFunc(
|
||||
#if HAVE_GMTIME_R
|
||||
pTm = gmtime_r(&t, &sNow);
|
||||
#else
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
|
||||
pTm = gmtime(&t);
|
||||
if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
||||
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
|
||||
#endif
|
||||
if( pTm ){
|
||||
strftime(zBuf, 20, zFormat, &sNow);
|
||||
|
@ -238,6 +238,7 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
i = 0;
|
||||
if( iSchema>=0 ){
|
||||
pIdxInfo->aConstraintUsage[iSchema].argvIndex = ++i;
|
||||
pIdxInfo->aConstraintUsage[iSchema].omit = 1;
|
||||
pIdxInfo->idxNum |= 0x01;
|
||||
}
|
||||
if( iName>=0 ){
|
||||
@ -452,7 +453,9 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
if( nPayload>(u32)nLocal ){
|
||||
int j;
|
||||
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
|
||||
if( iOff+nLocal>nUsable ) goto statPageIsCorrupt;
|
||||
if( iOff+nLocal>nUsable || nPayload>0x7fffffff ){
|
||||
goto statPageIsCorrupt;
|
||||
}
|
||||
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
|
||||
pCell->nOvfl = nOvfl;
|
||||
pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
|
||||
@ -721,10 +724,10 @@ static int statFilter(
|
||||
pSql = sqlite3_str_new(pTab->db);
|
||||
sqlite3_str_appendf(pSql,
|
||||
"SELECT * FROM ("
|
||||
"SELECT 'sqlite_master' AS name,1 AS rootpage,'table' AS type"
|
||||
"SELECT 'sqlite_schema' AS name,1 AS rootpage,'table' AS type"
|
||||
" UNION ALL "
|
||||
"SELECT name,rootpage,type"
|
||||
" FROM \"%w\".sqlite_master WHERE rootpage!=0)",
|
||||
" FROM \"%w\".sqlite_schema WHERE rootpage!=0)",
|
||||
pTab->db->aDb[pCsr->iDb].zDbSName);
|
||||
if( zName ){
|
||||
sqlite3_str_appendf(pSql, "WHERE name=%Q", zName);
|
||||
|
@ -31,7 +31,7 @@
|
||||
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
struct SrcList_item *pItem = pSrc->a;
|
||||
Table *pTab;
|
||||
assert( pItem && pSrc->nSrc==1 );
|
||||
assert( pItem && pSrc->nSrc>=1 );
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
|
||||
sqlite3DeleteTable(pParse->db, pItem->pTab);
|
||||
pItem->pTab = pTab;
|
||||
@ -51,7 +51,7 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
** 1) It is a virtual table and no implementation of the xUpdate method
|
||||
** has been provided
|
||||
**
|
||||
** 2) It is a system table (i.e. sqlite_master), this call is not
|
||||
** 2) It is a system table (i.e. sqlite_schema), this call is not
|
||||
** part of a nested parse and writable_schema pragma has not
|
||||
** been specified
|
||||
**
|
||||
@ -533,7 +533,9 @@ void sqlite3DeleteFrom(
|
||||
iTabCur, aToOpen, &iDataCur, &iIdxCur);
|
||||
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
|
||||
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
|
||||
if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
sqlite3VdbeJumpHereOrPopInst(v, iAddrOnce);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up a loop over the rowids/primary-keys that were found in the
|
||||
@ -856,6 +858,7 @@ void sqlite3GenerateRowIndexDelete(
|
||||
&iPartIdxLabel, pPrior, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */
|
||||
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
|
||||
pPrior = pIdx;
|
||||
}
|
||||
|
388
src/expr.c
388
src/expr.c
@ -42,16 +42,19 @@ char sqlite3TableColumnAffinity(Table *pTab, int iCol){
|
||||
** SELECT a AS b FROM t1 WHERE b;
|
||||
** SELECT * FROM t1 WHERE (select a from t1);
|
||||
*/
|
||||
char sqlite3ExprAffinity(Expr *pExpr){
|
||||
char sqlite3ExprAffinity(const Expr *pExpr){
|
||||
int op;
|
||||
while( ExprHasProperty(pExpr, EP_Skip) ){
|
||||
assert( pExpr->op==TK_COLLATE );
|
||||
assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
|
||||
pExpr = pExpr->pLeft;
|
||||
assert( pExpr!=0 );
|
||||
}
|
||||
op = pExpr->op;
|
||||
if( op==TK_SELECT ){
|
||||
assert( pExpr->flags&EP_xIsSelect );
|
||||
assert( pExpr->x.pSelect!=0 );
|
||||
assert( pExpr->x.pSelect->pEList!=0 );
|
||||
assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
|
||||
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
|
||||
}
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
@ -112,7 +115,7 @@ Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
|
||||
*/
|
||||
Expr *sqlite3ExprSkipCollate(Expr *pExpr){
|
||||
while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
|
||||
assert( pExpr->op==TK_COLLATE );
|
||||
assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
return pExpr;
|
||||
@ -131,7 +134,7 @@ Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
|
||||
assert( pExpr->op==TK_FUNCTION );
|
||||
pExpr = pExpr->x.pList->a[0].pExpr;
|
||||
}else{
|
||||
assert( pExpr->op==TK_COLLATE );
|
||||
assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
}
|
||||
@ -152,10 +155,10 @@ Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
|
||||
** COLLATE operators take first precedence. Left operands take
|
||||
** precedence over right operands.
|
||||
*/
|
||||
CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
|
||||
CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
|
||||
sqlite3 *db = pParse->db;
|
||||
CollSeq *pColl = 0;
|
||||
Expr *p = pExpr;
|
||||
const Expr *p = pExpr;
|
||||
while( p ){
|
||||
int op = p->op;
|
||||
if( op==TK_REGISTER ) op = p->op2;
|
||||
@ -195,7 +198,7 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
|
||||
&& ALWAYS(!ExprHasProperty(p, EP_xIsSelect))
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<p->x.pList->nExpr; i++){
|
||||
for(i=0; ALWAYS(i<p->x.pList->nExpr); i++){
|
||||
if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){
|
||||
pNext = p->x.pList->a[i].pExpr;
|
||||
break;
|
||||
@ -224,7 +227,7 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
|
||||
** The sqlite3ExprCollSeq() routine works the same except that it
|
||||
** returns NULL if there is no defined collation.
|
||||
*/
|
||||
CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
|
||||
CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr){
|
||||
CollSeq *p = sqlite3ExprCollSeq(pParse, pExpr);
|
||||
if( p==0 ) p = pParse->db->pDfltColl;
|
||||
assert( p!=0 );
|
||||
@ -234,7 +237,7 @@ CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
|
||||
/*
|
||||
** Return TRUE if the two expressions have equivalent collating sequences.
|
||||
*/
|
||||
int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
|
||||
int sqlite3ExprCollSeqMatch(Parse *pParse, const Expr *pE1, const Expr *pE2){
|
||||
CollSeq *pColl1 = sqlite3ExprNNCollSeq(pParse, pE1);
|
||||
CollSeq *pColl2 = sqlite3ExprNNCollSeq(pParse, pE2);
|
||||
return sqlite3StrICmp(pColl1->zName, pColl2->zName)==0;
|
||||
@ -245,7 +248,7 @@ int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
|
||||
** type affinity of the other operand. This routine returns the
|
||||
** type affinity that should be used for the comparison operator.
|
||||
*/
|
||||
char sqlite3CompareAffinity(Expr *pExpr, char aff2){
|
||||
char sqlite3CompareAffinity(const Expr *pExpr, char aff2){
|
||||
char aff1 = sqlite3ExprAffinity(pExpr);
|
||||
if( aff1>SQLITE_AFF_NONE && aff2>SQLITE_AFF_NONE ){
|
||||
/* Both sides of the comparison are columns. If one has numeric
|
||||
@ -267,7 +270,7 @@ char sqlite3CompareAffinity(Expr *pExpr, char aff2){
|
||||
** pExpr is a comparison operator. Return the type affinity that should
|
||||
** be applied to both operands prior to doing the comparison.
|
||||
*/
|
||||
static char comparisonAffinity(Expr *pExpr){
|
||||
static char comparisonAffinity(const Expr *pExpr){
|
||||
char aff;
|
||||
assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT ||
|
||||
pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE ||
|
||||
@ -290,7 +293,7 @@ static char comparisonAffinity(Expr *pExpr){
|
||||
** if the index with affinity idx_affinity may be used to implement
|
||||
** the comparison in pExpr.
|
||||
*/
|
||||
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
|
||||
int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity){
|
||||
char aff = comparisonAffinity(pExpr);
|
||||
if( aff<SQLITE_AFF_TEXT ){
|
||||
return 1;
|
||||
@ -305,7 +308,11 @@ int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
|
||||
** Return the P5 value that should be used for a binary comparison
|
||||
** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2.
|
||||
*/
|
||||
static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
|
||||
static u8 binaryCompareP5(
|
||||
const Expr *pExpr1, /* Left operand */
|
||||
const Expr *pExpr2, /* Right operand */
|
||||
int jumpIfNull /* Extra flags added to P5 */
|
||||
){
|
||||
u8 aff = (char)sqlite3ExprAffinity(pExpr2);
|
||||
aff = (u8)sqlite3CompareAffinity(pExpr1, aff) | (u8)jumpIfNull;
|
||||
return aff;
|
||||
@ -325,8 +332,8 @@ static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
|
||||
*/
|
||||
CollSeq *sqlite3BinaryCompareCollSeq(
|
||||
Parse *pParse,
|
||||
Expr *pLeft,
|
||||
Expr *pRight
|
||||
const Expr *pLeft,
|
||||
const Expr *pRight
|
||||
){
|
||||
CollSeq *pColl;
|
||||
assert( pLeft );
|
||||
@ -351,7 +358,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(
|
||||
** is reversed in the sqlite3BinaryCompareCollSeq() call so that the
|
||||
** correct collating sequence is found.
|
||||
*/
|
||||
CollSeq *sqlite3ExprCompareCollSeq(Parse *pParse, Expr *p){
|
||||
CollSeq *sqlite3ExprCompareCollSeq(Parse *pParse, const Expr *p){
|
||||
if( ExprHasProperty(p, EP_Commuted) ){
|
||||
return sqlite3BinaryCompareCollSeq(pParse, p->pRight, p->pLeft);
|
||||
}else{
|
||||
@ -594,6 +601,7 @@ static void codeVectorCompare(
|
||||
int addrDone = sqlite3VdbeMakeLabel(pParse);
|
||||
int isCommuted = ExprHasProperty(pExpr,EP_Commuted);
|
||||
|
||||
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
|
||||
if( pParse->nErr ) return;
|
||||
if( nLeft!=sqlite3ExprVectorSize(pRight) ){
|
||||
sqlite3ErrorMsg(pParse, "row value misused");
|
||||
@ -1206,7 +1214,7 @@ static int dupedExprStructSize(Expr *p, int flags){
|
||||
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
|
||||
assert( !ExprHasProperty(p, EP_FromJoin) );
|
||||
assert( !ExprHasProperty(p, EP_MemToken) );
|
||||
assert( !ExprHasProperty(p, EP_NoReduce) );
|
||||
assert( !ExprHasVVAProperty(p, EP_NoReduce) );
|
||||
if( p->pLeft || p->x.pList ){
|
||||
nSize = EXPR_REDUCEDSIZE | EP_Reduced;
|
||||
}else{
|
||||
@ -1311,6 +1319,10 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
|
||||
pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
|
||||
pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
|
||||
pNew->flags |= staticFlag;
|
||||
ExprClearVVAProperties(pNew);
|
||||
if( dupFlags ){
|
||||
ExprSetVVAProperty(pNew, EP_Immutable);
|
||||
}
|
||||
|
||||
/* Copy the p->u.zToken string, if any. */
|
||||
if( nToken ){
|
||||
@ -1778,6 +1790,7 @@ void sqlite3ExprListSetName(
|
||||
int dequote /* True to cause the name to be dequoted */
|
||||
){
|
||||
assert( pList!=0 || pParse->db->mallocFailed!=0 );
|
||||
assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 );
|
||||
if( pList ){
|
||||
struct ExprList_item *pItem;
|
||||
assert( pList->nExpr>0 );
|
||||
@ -1785,9 +1798,14 @@ void sqlite3ExprListSetName(
|
||||
assert( pItem->zEName==0 );
|
||||
assert( pItem->eEName==ENAME_NAME );
|
||||
pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
|
||||
if( dequote ) sqlite3Dequote(pItem->zEName);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
|
||||
if( dequote ){
|
||||
/* If dequote==0, then pName->z does not point to part of a DDL
|
||||
** statement handled by the parser. And so no token need be added
|
||||
** to the token-map. */
|
||||
sqlite3Dequote(pItem->zEName);
|
||||
if( IN_RENAME_OBJECT ){
|
||||
sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1973,10 +1991,10 @@ Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
|
||||
**
|
||||
** The sqlite3ExprIsConstantOrFunction() is used for evaluating DEFAULT
|
||||
** expressions in a CREATE TABLE statement. The Walker.eCode value is 5
|
||||
** when parsing an existing schema out of the sqlite_master table and 4
|
||||
** when parsing an existing schema out of the sqlite_schema table and 4
|
||||
** when processing a new CREATE TABLE statement. A bound parameter raises
|
||||
** an error for new statements, but is silently converted
|
||||
** to NULL for existing schemas. This allows sqlite_master tables that
|
||||
** to NULL for existing schemas. This allows sqlite_schema tables that
|
||||
** contain a bound parameter because they were generated by older versions
|
||||
** of SQLite to be parsed by newer versions of SQLite without raising a
|
||||
** malformed schema error.
|
||||
@ -2011,7 +2029,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
if( sqlite3ExprIdToTrueFalse(pExpr) ){
|
||||
return WRC_Prune;
|
||||
}
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_COLUMN:
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_AGG_COLUMN:
|
||||
@ -2025,18 +2043,20 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
|
||||
return WRC_Continue;
|
||||
}
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_IF_NULL_ROW:
|
||||
case TK_REGISTER:
|
||||
case TK_DOT:
|
||||
testcase( pExpr->op==TK_REGISTER );
|
||||
testcase( pExpr->op==TK_IF_NULL_ROW );
|
||||
testcase( pExpr->op==TK_DOT );
|
||||
pWalker->eCode = 0;
|
||||
return WRC_Abort;
|
||||
case TK_VARIABLE:
|
||||
if( pWalker->eCode==5 ){
|
||||
/* Silently convert bound parameters that appear inside of CREATE
|
||||
** statements into a NULL when parsing the CREATE statement text out
|
||||
** of the sqlite_master table */
|
||||
** of the sqlite_schema table */
|
||||
pExpr->op = TK_NULL;
|
||||
}else if( pWalker->eCode==4 ){
|
||||
/* A bound parameter in a CREATE statement that originates from
|
||||
@ -2044,7 +2064,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
pWalker->eCode = 0;
|
||||
return WRC_Abort;
|
||||
}
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
default:
|
||||
testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
|
||||
testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
|
||||
@ -2087,7 +2107,7 @@ int sqlite3ExprIsConstant(Expr *p){
|
||||
**
|
||||
** When this routine returns true, it indicates that the expression
|
||||
** can be added to the pParse->pConstExpr list and evaluated once when
|
||||
** the prepared statement starts up. See sqlite3ExprCodeAtInit().
|
||||
** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce().
|
||||
*/
|
||||
int sqlite3ExprIsConstantNotJoin(Expr *p){
|
||||
return exprIsConst(p, 2, 0);
|
||||
@ -2169,12 +2189,12 @@ int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprList *pGroupBy){
|
||||
** the expression is constant or a function call with constant arguments.
|
||||
** Return and 0 if there are any variables.
|
||||
**
|
||||
** isInit is true when parsing from sqlite_master. isInit is false when
|
||||
** isInit is true when parsing from sqlite_schema. isInit is false when
|
||||
** processing a new CREATE TABLE statement. When isInit is true, parameters
|
||||
** (such as ? or $abc) in the expression are converted into NULL. When
|
||||
** isInit is false, parameters raise an error. Parameters should not be
|
||||
** allowed in a CREATE TABLE statement, but some legacy versions of SQLite
|
||||
** allowed it, so we need to support it when reading sqlite_master for
|
||||
** allowed it, so we need to support it when reading sqlite_schema for
|
||||
** backwards compatibility.
|
||||
**
|
||||
** If isInit is true, set EP_FromDDL on every TK_FUNCTION node.
|
||||
@ -2537,7 +2557,7 @@ int sqlite3FindInIndex(
|
||||
if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Table *pTab; /* Table <table>. */
|
||||
i16 iDb; /* Database idx for pTab */
|
||||
int iDb; /* Database idx for pTab */
|
||||
ExprList *pEList = p->pEList;
|
||||
int nExpr = pEList->nExpr;
|
||||
|
||||
@ -2548,6 +2568,7 @@ int sqlite3FindInIndex(
|
||||
|
||||
/* Code an OP_Transaction and OP_TableLock for <table>. */
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb>=0 && iDb<SQLITE_MAX_ATTACHED );
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
|
||||
|
||||
@ -2850,6 +2871,7 @@ void sqlite3CodeRhsOfIN(
|
||||
|
||||
/* Begin coding the subroutine */
|
||||
ExprSetProperty(pExpr, EP_Subrtn);
|
||||
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
|
||||
pExpr->y.sub.regReturn = ++pParse->nMem;
|
||||
pExpr->y.sub.iAddr =
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1;
|
||||
@ -2931,6 +2953,8 @@ void sqlite3CodeRhsOfIN(
|
||||
affinity = sqlite3ExprAffinity(pLeft);
|
||||
if( affinity<=SQLITE_AFF_NONE ){
|
||||
affinity = SQLITE_AFF_BLOB;
|
||||
}else if( affinity==SQLITE_AFF_REAL ){
|
||||
affinity = SQLITE_AFF_NUMERIC;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
@ -3169,7 +3193,9 @@ static void sqlite3ExprCodeIN(
|
||||
int destNotNull; /* Jump here if a comparison is not true in step 6 */
|
||||
int addrTop; /* Top of the step-6 loop */
|
||||
int iTab = 0; /* Index to use */
|
||||
u8 okConstFactor = pParse->okConstFactor;
|
||||
|
||||
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
|
||||
pLeft = pExpr->pLeft;
|
||||
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
|
||||
zAff = exprINAffinity(pParse, pExpr);
|
||||
@ -3212,8 +3238,14 @@ static void sqlite3ExprCodeIN(
|
||||
** so that the fields are in the same order as an existing index. The
|
||||
** aiMap[] array contains a mapping from the original LHS field order to
|
||||
** the field order that matches the RHS index.
|
||||
*/
|
||||
**
|
||||
** Avoid factoring the LHS of the IN(...) expression out of the loop,
|
||||
** even if it is constant, as OP_Affinity may be used on the register
|
||||
** by code generated below. */
|
||||
assert( pParse->okConstFactor==okConstFactor );
|
||||
pParse->okConstFactor = 0;
|
||||
rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
|
||||
pParse->okConstFactor = okConstFactor;
|
||||
for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
|
||||
if( i==nVector ){
|
||||
/* LHS fields are not reordered */
|
||||
@ -3239,21 +3271,13 @@ static void sqlite3ExprCodeIN(
|
||||
int r2, regToFree;
|
||||
int regCkNull = 0;
|
||||
int ii;
|
||||
int bLhsReal; /* True if the LHS of the IN has REAL affinity */
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
if( destIfNull!=destIfFalse ){
|
||||
regCkNull = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
|
||||
}
|
||||
bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL;
|
||||
for(ii=0; ii<pList->nExpr; ii++){
|
||||
if( bLhsReal ){
|
||||
r2 = regToFree = sqlite3GetTempReg(pParse);
|
||||
sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2);
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC);
|
||||
}else{
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree);
|
||||
}
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, ®ToFree);
|
||||
if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
|
||||
sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
|
||||
}
|
||||
@ -3496,7 +3520,7 @@ void sqlite3ExprCodeGeneratedColumn(
|
||||
}else{
|
||||
iAddr = 0;
|
||||
}
|
||||
sqlite3ExprCode(pParse, pCol->pDflt, regOut);
|
||||
sqlite3ExprCodeCopy(pParse, pCol->pDflt, regOut);
|
||||
if( pCol->affinity>=SQLITE_AFF_TEXT ){
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1);
|
||||
}
|
||||
@ -3637,6 +3661,16 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
|
||||
return iResult;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the last opcode is a OP_Copy, then set the do-not-merge flag (p5)
|
||||
** so that a subsequent copy will not be merged into this one.
|
||||
*/
|
||||
static void setDoNotMergeFlagOnCopy(Vdbe *v){
|
||||
if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){
|
||||
sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to implement special SQL functions that are implemented
|
||||
** in-line rather than by using the usual callbacks.
|
||||
@ -3668,12 +3702,17 @@ static int exprCodeInlineFunction(
|
||||
VdbeCoverage(v);
|
||||
sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target);
|
||||
}
|
||||
if( sqlite3VdbeGetOp(v, -1)->opcode==OP_Copy ){
|
||||
sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergable */
|
||||
}
|
||||
setDoNotMergeFlagOnCopy(v);
|
||||
sqlite3VdbeResolveLabel(v, endCoalesce);
|
||||
break;
|
||||
}
|
||||
case INLINEFUNC_iif: {
|
||||
Expr caseExpr;
|
||||
memset(&caseExpr, 0, sizeof(caseExpr));
|
||||
caseExpr.op = TK_CASE;
|
||||
caseExpr.x.pList = pFarg;
|
||||
return sqlite3ExprCodeTarget(pParse, &caseExpr, target);
|
||||
}
|
||||
|
||||
default: {
|
||||
/* The UNLIKELY() function is a no-op. The result is the value
|
||||
@ -3763,30 +3802,41 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
int p5 = 0;
|
||||
|
||||
assert( target>0 && target<=pParse->nMem );
|
||||
if( v==0 ){
|
||||
assert( pParse->db->mallocFailed );
|
||||
return 0;
|
||||
}
|
||||
assert( v!=0 );
|
||||
|
||||
expr_code_doover:
|
||||
if( pExpr==0 ){
|
||||
op = TK_NULL;
|
||||
}else{
|
||||
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
|
||||
op = pExpr->op;
|
||||
}
|
||||
switch( op ){
|
||||
case TK_AGG_COLUMN: {
|
||||
AggInfo *pAggInfo = pExpr->pAggInfo;
|
||||
struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
|
||||
struct AggInfo_col *pCol;
|
||||
assert( pAggInfo!=0 );
|
||||
assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
|
||||
pCol = &pAggInfo->aCol[pExpr->iAgg];
|
||||
if( !pAggInfo->directMode ){
|
||||
assert( pCol->iMem>0 );
|
||||
return pCol->iMem;
|
||||
}else if( pAggInfo->useSortingIdx ){
|
||||
Table *pTab = pCol->pTab;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
|
||||
pCol->iSorterColumn, target);
|
||||
if( pCol->iColumn<0 ){
|
||||
VdbeComment((v,"%s.rowid",pTab->zName));
|
||||
}else{
|
||||
VdbeComment((v,"%s.%s",pTab->zName,pTab->aCol[pCol->iColumn].zName));
|
||||
if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){
|
||||
sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
/* Otherwise, fall thru into the TK_COLUMN case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case TK_COLUMN: {
|
||||
int iTab = pExpr->iTable;
|
||||
@ -3809,10 +3859,6 @@ expr_code_doover:
|
||||
static const char zAff[] = "B\000C\000D\000E";
|
||||
assert( SQLITE_AFF_BLOB=='A' );
|
||||
assert( SQLITE_AFF_TEXT=='B' );
|
||||
if( iReg!=target ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target);
|
||||
iReg = target;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
|
||||
&zAff[(aff-'B')*2], P4_STATIC);
|
||||
}
|
||||
@ -4026,6 +4072,7 @@ expr_code_doover:
|
||||
tempX.op = TK_INTEGER;
|
||||
tempX.flags = EP_IntValue|EP_TokenOnly;
|
||||
tempX.u.iValue = 0;
|
||||
ExprClearVVAProperties(&tempX);
|
||||
r1 = sqlite3ExprCodeTemp(pParse, &tempX, ®Free1);
|
||||
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free2);
|
||||
sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target);
|
||||
@ -4071,7 +4118,10 @@ expr_code_doover:
|
||||
}
|
||||
case TK_AGG_FUNCTION: {
|
||||
AggInfo *pInfo = pExpr->pAggInfo;
|
||||
if( pInfo==0 ){
|
||||
if( pInfo==0
|
||||
|| NEVER(pExpr->iAgg<0)
|
||||
|| NEVER(pExpr->iAgg>=pInfo->nFunc)
|
||||
){
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken);
|
||||
}else{
|
||||
@ -4097,16 +4147,13 @@ expr_code_doover:
|
||||
#endif
|
||||
|
||||
if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
|
||||
/* SQL functions can be expensive. So try to move constant functions
|
||||
** out of the inner loop, even if that means an extra OP_Copy. */
|
||||
return sqlite3ExprCodeAtInit(pParse, pExpr, -1);
|
||||
/* SQL functions can be expensive. So try to avoid running them
|
||||
** multiple times if we know they always give the same result */
|
||||
return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly) ){
|
||||
pFarg = 0;
|
||||
}else{
|
||||
pFarg = pExpr->x.pList;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_TokenOnly) );
|
||||
pFarg = pExpr->x.pList;
|
||||
nFarg = pFarg ? pFarg->nExpr : 0;
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
zId = pExpr->u.zToken;
|
||||
@ -4222,7 +4269,9 @@ expr_code_doover:
|
||||
int nCol;
|
||||
testcase( op==TK_EXISTS );
|
||||
testcase( op==TK_SELECT );
|
||||
if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
|
||||
if( pParse->db->mallocFailed ){
|
||||
return 0;
|
||||
}else if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
|
||||
sqlite3SubselectError(pParse, nCol, 1);
|
||||
}else{
|
||||
return sqlite3CodeSubselect(pParse, pExpr);
|
||||
@ -4441,6 +4490,7 @@ expr_code_doover:
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
||||
}
|
||||
sqlite3ExprDelete(db, pDel);
|
||||
setDoNotMergeFlagOnCopy(v);
|
||||
sqlite3VdbeResolveLabel(v, endLabel);
|
||||
break;
|
||||
}
|
||||
@ -4451,7 +4501,7 @@ expr_code_doover:
|
||||
|| pExpr->affExpr==OE_Fail
|
||||
|| pExpr->affExpr==OE_Ignore
|
||||
);
|
||||
if( !pParse->pTriggerTab ){
|
||||
if( !pParse->pTriggerTab && !pParse->nested ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"RAISE() may only be used within a trigger-program");
|
||||
return 0;
|
||||
@ -4465,8 +4515,9 @@ expr_code_doover:
|
||||
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
|
||||
pExpr->affExpr, pExpr->u.zToken, 0, 0);
|
||||
sqlite3HaltConstraint(pParse,
|
||||
pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR,
|
||||
pExpr->affExpr, pExpr->u.zToken, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -4479,15 +4530,23 @@ expr_code_doover:
|
||||
}
|
||||
|
||||
/*
|
||||
** Factor out the code of the given expression to initialization time.
|
||||
** Generate code that will evaluate expression pExpr just one time
|
||||
** per prepared statement execution.
|
||||
**
|
||||
** If the expression uses functions (that might throw an exception) then
|
||||
** guard them with an OP_Once opcode to ensure that the code is only executed
|
||||
** once. If no functions are involved, then factor the code out and put it at
|
||||
** the end of the prepared statement in the initialization section.
|
||||
**
|
||||
** If regDest>=0 then the result is always stored in that register and the
|
||||
** result is not reusable. If regDest<0 then this routine is free to
|
||||
** store the value whereever it wants. The register where the expression
|
||||
** is stored is returned. When regDest<0, two identical expressions will
|
||||
** code to the same register.
|
||||
** is stored is returned. When regDest<0, two identical expressions might
|
||||
** code to the same register, if they do not contain function calls and hence
|
||||
** are factored out into the initialization section at the end of the
|
||||
** prepared statement.
|
||||
*/
|
||||
int sqlite3ExprCodeAtInit(
|
||||
int sqlite3ExprCodeRunJustOnce(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The expression to code when the VDBE initializes */
|
||||
int regDest /* Store the value in this register */
|
||||
@ -4505,14 +4564,29 @@ int sqlite3ExprCodeAtInit(
|
||||
}
|
||||
}
|
||||
pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
|
||||
p = sqlite3ExprListAppend(pParse, p, pExpr);
|
||||
if( p ){
|
||||
struct ExprList_item *pItem = &p->a[p->nExpr-1];
|
||||
pItem->reusable = regDest<0;
|
||||
if( regDest<0 ) regDest = ++pParse->nMem;
|
||||
pItem->u.iConstExprReg = regDest;
|
||||
if( pExpr!=0 && ExprHasProperty(pExpr, EP_HasFunc) ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int addr;
|
||||
assert( v );
|
||||
addr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
pParse->okConstFactor = 0;
|
||||
if( !pParse->db->mallocFailed ){
|
||||
if( regDest<0 ) regDest = ++pParse->nMem;
|
||||
sqlite3ExprCode(pParse, pExpr, regDest);
|
||||
}
|
||||
pParse->okConstFactor = 1;
|
||||
sqlite3ExprDelete(pParse->db, pExpr);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}else{
|
||||
p = sqlite3ExprListAppend(pParse, p, pExpr);
|
||||
if( p ){
|
||||
struct ExprList_item *pItem = &p->a[p->nExpr-1];
|
||||
pItem->reusable = regDest<0;
|
||||
if( regDest<0 ) regDest = ++pParse->nMem;
|
||||
pItem->u.iConstExprReg = regDest;
|
||||
}
|
||||
pParse->pConstExpr = p;
|
||||
}
|
||||
pParse->pConstExpr = p;
|
||||
return regDest;
|
||||
}
|
||||
|
||||
@ -4537,7 +4611,7 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
|
||||
&& sqlite3ExprIsConstantNotJoin(pExpr)
|
||||
){
|
||||
*pReg = 0;
|
||||
r2 = sqlite3ExprCodeAtInit(pParse, pExpr, -1);
|
||||
r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
|
||||
}else{
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
|
||||
@ -4559,10 +4633,12 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
|
||||
void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
|
||||
int inReg;
|
||||
|
||||
assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
|
||||
assert( target>0 && target<=pParse->nMem );
|
||||
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
|
||||
if( inReg!=target && pParse->pVdbe ){
|
||||
if( pParse->pVdbe==0 ) return;
|
||||
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
|
||||
if( inReg!=target ){
|
||||
u8 op;
|
||||
if( ExprHasProperty(pExpr,EP_Subquery) ){
|
||||
op = OP_Copy;
|
||||
@ -4593,9 +4669,9 @@ void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){
|
||||
*/
|
||||
void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
|
||||
if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){
|
||||
sqlite3ExprCodeAtInit(pParse, pExpr, target);
|
||||
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target);
|
||||
}else{
|
||||
sqlite3ExprCode(pParse, pExpr, target);
|
||||
sqlite3ExprCodeCopy(pParse, pExpr, target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4653,7 +4729,7 @@ int sqlite3ExprCodeExprList(
|
||||
}else if( (flags & SQLITE_ECEL_FACTOR)!=0
|
||||
&& sqlite3ExprIsConstantNotJoin(pExpr)
|
||||
){
|
||||
sqlite3ExprCodeAtInit(pParse, pExpr, target+i);
|
||||
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i);
|
||||
}else{
|
||||
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
|
||||
if( inReg!=target+i ){
|
||||
@ -4776,6 +4852,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
|
||||
if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
|
||||
if( NEVER(pExpr==0) ) return; /* No way this can happen */
|
||||
assert( !ExprHasVVAProperty(pExpr, EP_Immutable) );
|
||||
op = pExpr->op;
|
||||
switch( op ){
|
||||
case TK_AND:
|
||||
@ -4825,7 +4902,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
testcase( op==TK_ISNOT );
|
||||
op = (op==TK_IS) ? TK_EQ : TK_NE;
|
||||
jumpIfNull = SQLITE_NULLEQ;
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
@ -4917,6 +4994,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
|
||||
if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
|
||||
if( pExpr==0 ) return;
|
||||
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
|
||||
|
||||
/* The value of pExpr->op and op are related as follows:
|
||||
**
|
||||
@ -5000,7 +5078,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
|
||||
testcase( pExpr->op==TK_ISNOT );
|
||||
op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
|
||||
jumpIfNull = SQLITE_NULLEQ;
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
@ -5200,7 +5278,7 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
|
||||
}
|
||||
if( (pA->flags & (EP_Distinct|EP_Commuted))
|
||||
!= (pB->flags & (EP_Distinct|EP_Commuted)) ) return 2;
|
||||
if( (combinedFlags & EP_TokenOnly)==0 ){
|
||||
if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
|
||||
if( combinedFlags & EP_xIsSelect ) return 2;
|
||||
if( (combinedFlags & EP_FixedCol)==0
|
||||
&& sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
|
||||
@ -5208,24 +5286,10 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
|
||||
if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
|
||||
if( pA->op!=TK_STRING
|
||||
&& pA->op!=TK_TRUEFALSE
|
||||
&& (combinedFlags & EP_Reduced)==0
|
||||
&& ALWAYS((combinedFlags & EP_Reduced)==0)
|
||||
){
|
||||
if( pA->iColumn!=pB->iColumn ) return 2;
|
||||
if( pA->op2!=pB->op2 ){
|
||||
if( pA->op==TK_TRUTH ) return 2;
|
||||
if( pA->op==TK_FUNCTION && iTab<0 ){
|
||||
/* Ex: CREATE TABLE t1(a CHECK( a<julianday('now') ));
|
||||
** INSERT INTO t1(a) VALUES(julianday('now')+10);
|
||||
** Without this test, sqlite3ExprCodeAtInit() will run on the
|
||||
** the julianday() of INSERT first, and remember that expression.
|
||||
** Then sqlite3ExprCodeInit() will see the julianday() in the CHECK
|
||||
** constraint as redundant, reusing the one from the INSERT, even
|
||||
** though the julianday() in INSERT lacks the critical NC_IsCheck
|
||||
** flag. See ticket [830277d9db6c3ba1] (2019-10-30)
|
||||
*/
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
if( pA->op2!=pB->op2 && pA->op==TK_TRUTH ) return 2;
|
||||
if( pA->op!=TK_IN && pA->iTable!=pB->iTable && pA->iTable!=iTab ){
|
||||
return 2;
|
||||
}
|
||||
@ -5326,13 +5390,13 @@ static int exprImpliesNotNull(
|
||||
case TK_RSHIFT:
|
||||
case TK_CONCAT:
|
||||
seenNot = 1;
|
||||
/* Fall thru */
|
||||
/* no break */ deliberate_fall_through
|
||||
case TK_STAR:
|
||||
case TK_REM:
|
||||
case TK_BITAND:
|
||||
case TK_SLASH: {
|
||||
if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1;
|
||||
/* Fall thru into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case TK_SPAN:
|
||||
case TK_COLLATE:
|
||||
@ -5463,19 +5527,26 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
|
||||
case TK_LT:
|
||||
case TK_LE:
|
||||
case TK_GT:
|
||||
case TK_GE:
|
||||
case TK_GE: {
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
Expr *pRight = pExpr->pRight;
|
||||
testcase( pExpr->op==TK_EQ );
|
||||
testcase( pExpr->op==TK_NE );
|
||||
testcase( pExpr->op==TK_LT );
|
||||
testcase( pExpr->op==TK_LE );
|
||||
testcase( pExpr->op==TK_GT );
|
||||
testcase( pExpr->op==TK_GE );
|
||||
if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab))
|
||||
|| (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab))
|
||||
/* The y.pTab=0 assignment in wherecode.c always happens after the
|
||||
** impliesNotNullRow() test */
|
||||
if( (pLeft->op==TK_COLUMN && ALWAYS(pLeft->y.pTab!=0)
|
||||
&& IsVirtual(pLeft->y.pTab))
|
||||
|| (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0)
|
||||
&& IsVirtual(pRight->y.pTab))
|
||||
){
|
||||
return WRC_Prune;
|
||||
return WRC_Prune;
|
||||
}
|
||||
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
default:
|
||||
return WRC_Continue;
|
||||
}
|
||||
@ -5587,10 +5658,25 @@ int sqlite3ExprCoveredByIndex(
|
||||
*/
|
||||
struct SrcCount {
|
||||
SrcList *pSrc; /* One particular FROM clause in a nested query */
|
||||
int iSrcInner; /* Smallest cursor number in this context */
|
||||
int nThis; /* Number of references to columns in pSrcList */
|
||||
int nOther; /* Number of references to columns in other FROM clauses */
|
||||
};
|
||||
|
||||
/*
|
||||
** xSelect callback for sqlite3FunctionUsesThisSrc(). If this is the first
|
||||
** SELECT with a FROM clause encountered during this iteration, set
|
||||
** SrcCount.iSrcInner to the cursor number of the leftmost object in
|
||||
** the FROM cause.
|
||||
*/
|
||||
static int selectSrcCount(Walker *pWalker, Select *pSel){
|
||||
struct SrcCount *p = pWalker->u.pSrcCount;
|
||||
if( p->iSrcInner==0x7FFFFFFF && ALWAYS(pSel->pSrc) && pSel->pSrc->nSrc ){
|
||||
pWalker->u.pSrcCount->iSrcInner = pSel->pSrc->a[0].iCursor;
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Count the number of references to columns.
|
||||
*/
|
||||
@ -5611,7 +5697,7 @@ static int exprSrcCount(Walker *pWalker, Expr *pExpr){
|
||||
}
|
||||
if( i<nSrc ){
|
||||
p->nThis++;
|
||||
}else if( nSrc==0 || pExpr->iTable<pSrc->a[0].iCursor ){
|
||||
}else if( pExpr->iTable<p->iSrcInner ){
|
||||
/* In a well-formed parse tree (no name resolution errors),
|
||||
** TK_COLUMN nodes with smaller Expr.iTable values are in an
|
||||
** outer context. Those are the only ones to count as "other" */
|
||||
@ -5633,9 +5719,10 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
|
||||
assert( pExpr->op==TK_AGG_FUNCTION );
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = exprSrcCount;
|
||||
w.xSelectCallback = sqlite3SelectWalkNoop;
|
||||
w.xSelectCallback = selectSrcCount;
|
||||
w.u.pSrcCount = &cnt;
|
||||
cnt.pSrc = pSrcList;
|
||||
cnt.iSrcInner = (pSrcList&&pSrcList->nSrc)?pSrcList->a[0].iCursor:0x7FFFFFFF;
|
||||
cnt.nThis = 0;
|
||||
cnt.nOther = 0;
|
||||
sqlite3WalkExprList(&w, pExpr->x.pList);
|
||||
@ -5647,6 +5734,64 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
|
||||
return cnt.nThis>0 || cnt.nOther==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a Walker expression node callback.
|
||||
**
|
||||
** For Expr nodes that contain pAggInfo pointers, make sure the AggInfo
|
||||
** object that is referenced does not refer directly to the Expr. If
|
||||
** it does, make a copy. This is done because the pExpr argument is
|
||||
** subject to change.
|
||||
**
|
||||
** The copy is stored on pParse->pConstExpr with a register number of 0.
|
||||
** This will cause the expression to be deleted automatically when the
|
||||
** Parse object is destroyed, but the zero register number means that it
|
||||
** will not generate any code in the preamble.
|
||||
*/
|
||||
static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
|
||||
if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced))
|
||||
&& pExpr->pAggInfo!=0
|
||||
){
|
||||
AggInfo *pAggInfo = pExpr->pAggInfo;
|
||||
int iAgg = pExpr->iAgg;
|
||||
Parse *pParse = pWalker->pParse;
|
||||
sqlite3 *db = pParse->db;
|
||||
assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION );
|
||||
if( pExpr->op==TK_AGG_COLUMN ){
|
||||
assert( iAgg>=0 && iAgg<pAggInfo->nColumn );
|
||||
if( pAggInfo->aCol[iAgg].pCExpr==pExpr ){
|
||||
pExpr = sqlite3ExprDup(db, pExpr, 0);
|
||||
if( pExpr ){
|
||||
pAggInfo->aCol[iAgg].pCExpr = pExpr;
|
||||
pParse->pConstExpr =
|
||||
sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
assert( iAgg>=0 && iAgg<pAggInfo->nFunc );
|
||||
if( pAggInfo->aFunc[iAgg].pFExpr==pExpr ){
|
||||
pExpr = sqlite3ExprDup(db, pExpr, 0);
|
||||
if( pExpr ){
|
||||
pAggInfo->aFunc[iAgg].pFExpr = pExpr;
|
||||
pParse->pConstExpr =
|
||||
sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a Walker object so that will persist AggInfo entries referenced
|
||||
** by the tree that is walked.
|
||||
*/
|
||||
void sqlite3AggInfoPersistWalkerInit(Walker *pWalker, Parse *pParse){
|
||||
memset(pWalker, 0, sizeof(*pWalker));
|
||||
pWalker->pParse = pParse;
|
||||
pWalker->xExprCallback = agginfoPersistExprCb;
|
||||
pWalker->xSelectCallback = sqlite3SelectWalkNoop;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new element to the pAggInfo->aCol[] array. Return the index of
|
||||
** the new element. Return a negative number if malloc fails.
|
||||
@ -5677,7 +5822,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
|
||||
&i
|
||||
);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the xExprCallback for a tree walker. It is used to
|
||||
@ -5728,7 +5873,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
pCol->iColumn = pExpr->iColumn;
|
||||
pCol->iMem = ++pParse->nMem;
|
||||
pCol->iSorterColumn = -1;
|
||||
pCol->pExpr = pExpr;
|
||||
pCol->pCExpr = pExpr;
|
||||
if( pAggInfo->pGroupBy ){
|
||||
int j, n;
|
||||
ExprList *pGB = pAggInfo->pGroupBy;
|
||||
@ -5771,7 +5916,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
*/
|
||||
struct AggInfo_func *pItem = pAggInfo->aFunc;
|
||||
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
|
||||
if( sqlite3ExprCompare(0, pItem->pExpr, pExpr, -1)==0 ){
|
||||
if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -5783,7 +5928,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
if( i>=0 ){
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
pItem = &pAggInfo->aFunc[i];
|
||||
pItem->pExpr = pExpr;
|
||||
pItem->pFExpr = pExpr;
|
||||
pItem->iMem = ++pParse->nMem;
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
pItem->pFunc = sqlite3FindFunction(pParse->db,
|
||||
@ -5810,15 +5955,6 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
|
||||
UNUSED_PARAMETER(pSelect);
|
||||
pWalker->walkerDepth++;
|
||||
return WRC_Continue;
|
||||
}
|
||||
static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){
|
||||
UNUSED_PARAMETER(pSelect);
|
||||
pWalker->walkerDepth--;
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyze the pExpr expression looking for aggregate functions and
|
||||
@ -5832,8 +5968,8 @@ static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){
|
||||
void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
|
||||
Walker w;
|
||||
w.xExprCallback = analyzeAggregate;
|
||||
w.xSelectCallback = analyzeAggregatesInSelect;
|
||||
w.xSelectCallback2 = analyzeAggregatesInSelectEnd;
|
||||
w.xSelectCallback = sqlite3WalkerDepthIncrease;
|
||||
w.xSelectCallback2 = sqlite3WalkerDepthDecrease;
|
||||
w.walkerDepth = 0;
|
||||
w.u.pNC = pNC;
|
||||
w.pParse = 0;
|
||||
|
@ -658,7 +658,7 @@ static void fkScanChildren(
|
||||
/* Clean up the WHERE clause constructed above. */
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
if( iFkIfZero ){
|
||||
sqlite3VdbeJumpHere(v, iFkIfZero);
|
||||
sqlite3VdbeJumpHereOrPopInst(v, iFkIfZero);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1359,6 +1359,7 @@ static Trigger *fkActionTrigger(
|
||||
pStep->op = TK_DELETE;
|
||||
break;
|
||||
}
|
||||
/* no break */ deliberate_fall_through
|
||||
default:
|
||||
pStep->op = TK_UPDATE;
|
||||
}
|
||||
|
41
src/func.c
41
src/func.c
@ -853,6 +853,7 @@ static void likeFunc(
|
||||
int nPat;
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
struct compareInfo *pInfo = sqlite3_user_data(context);
|
||||
struct compareInfo backupInfo;
|
||||
|
||||
#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_BLOB
|
||||
@ -888,6 +889,12 @@ static void likeFunc(
|
||||
return;
|
||||
}
|
||||
escape = sqlite3Utf8Read(&zEsc);
|
||||
if( escape==pInfo->matchAll || escape==pInfo->matchOne ){
|
||||
memcpy(&backupInfo, pInfo, sizeof(backupInfo));
|
||||
pInfo = &backupInfo;
|
||||
if( escape==pInfo->matchAll ) pInfo->matchAll = 0;
|
||||
if( escape==pInfo->matchOne ) pInfo->matchOne = 0;
|
||||
}
|
||||
}else{
|
||||
escape = pInfo->matchSet;
|
||||
}
|
||||
@ -1276,7 +1283,7 @@ static void replaceFunc(
|
||||
** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */
|
||||
u8 *zOld;
|
||||
zOld = zOut;
|
||||
zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1));
|
||||
zOut = sqlite3Realloc(zOut, (int)nOut + (nOut - nStr - 1));
|
||||
if( zOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
sqlite3_free(zOld);
|
||||
@ -1877,19 +1884,12 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
nExpr = pExpr->x.pList->nExpr;
|
||||
pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0);
|
||||
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
if( pDef==0 ) return 0;
|
||||
#endif
|
||||
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
|
||||
return 0;
|
||||
}
|
||||
if( nExpr<3 ){
|
||||
aWc[3] = 0;
|
||||
}else{
|
||||
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
|
||||
char *zEscape;
|
||||
if( pEscape->op!=TK_STRING ) return 0;
|
||||
zEscape = pEscape->u.zToken;
|
||||
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
|
||||
aWc[3] = zEscape[0];
|
||||
}
|
||||
|
||||
/* The memcpy() statement assumes that the wildcard characters are
|
||||
** the first three statements in the compareInfo structure. The
|
||||
@ -1899,6 +1899,20 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
|
||||
assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
|
||||
assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
|
||||
|
||||
if( nExpr<3 ){
|
||||
aWc[3] = 0;
|
||||
}else{
|
||||
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
|
||||
char *zEscape;
|
||||
if( pEscape->op!=TK_STRING ) return 0;
|
||||
zEscape = pEscape->u.zToken;
|
||||
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
|
||||
if( zEscape[0]==aWc[0] ) return 0;
|
||||
if( zEscape[0]==aWc[1] ) return 0;
|
||||
aWc[3] = zEscape[0];
|
||||
}
|
||||
|
||||
*pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
|
||||
return 1;
|
||||
}
|
||||
@ -1979,7 +1993,7 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
FUNCTION(upper, 1, 0, 0, upperFunc ),
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE),
|
||||
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
|
||||
VFUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
@ -2019,7 +2033,8 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
#endif
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, SQLITE_FUNC_COALESCE),
|
||||
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
|
||||
INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
|
||||
};
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
|
@ -140,11 +140,13 @@ const unsigned char sqlite3CtypeMap[256] = {
|
||||
** enabled.
|
||||
*/
|
||||
#ifndef SQLITE_USE_URI
|
||||
/* BEGIN SQLCIPHER */
|
||||
# ifdef SQLITE_HAS_CODEC
|
||||
# define SQLITE_USE_URI 1
|
||||
# else
|
||||
# define SQLITE_USE_URI 0
|
||||
# endif
|
||||
/* END SQLCIPHER */
|
||||
#endif
|
||||
|
||||
/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the
|
||||
@ -307,6 +309,11 @@ sqlite3_uint64 sqlite3NProfileCnt = 0;
|
||||
int sqlite3PendingByte = 0x40000000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Flags for select tracing and the ".selecttrace" macro of the CLI
|
||||
*/
|
||||
u32 sqlite3_unsupported_selecttrace = 0;
|
||||
|
||||
#include "opcodes.h"
|
||||
/*
|
||||
** Properties of opcodes. The OPFLG_INITIALIZER macro is
|
||||
|
41
src/insert.c
41
src/insert.c
@ -180,7 +180,7 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
|
||||
assert( pOp!=0 );
|
||||
if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){
|
||||
Index *pIndex;
|
||||
int tnum = pOp->p2;
|
||||
Pgno tnum = pOp->p2;
|
||||
if( tnum==pTab->tnum ){
|
||||
return 1;
|
||||
}
|
||||
@ -1606,13 +1606,13 @@ void sqlite3GenerateConstraintChecks(
|
||||
VdbeCoverage(v);
|
||||
assert( (pCol->colFlags & COLFLAG_GENERATED)==0 );
|
||||
nSeenReplace++;
|
||||
sqlite3ExprCode(pParse, pCol->pDflt, iReg);
|
||||
sqlite3ExprCodeCopy(pParse, pCol->pDflt, iReg);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
break;
|
||||
}
|
||||
case OE_Abort:
|
||||
sqlite3MayAbort(pParse);
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
case OE_Rollback:
|
||||
case OE_Fail: {
|
||||
char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
|
||||
@ -1661,6 +1661,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
|
||||
for(i=0; i<pCheck->nExpr; i++){
|
||||
int allOk;
|
||||
Expr *pCopy;
|
||||
Expr *pExpr = pCheck->a[i].pExpr;
|
||||
if( aiChng
|
||||
&& !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng)
|
||||
@ -1669,9 +1670,17 @@ void sqlite3GenerateConstraintChecks(
|
||||
** updated so there is no point it verifying the check constraint */
|
||||
continue;
|
||||
}
|
||||
if( bAffinityDone==0 ){
|
||||
sqlite3TableAffinity(v, pTab, regNewData+1);
|
||||
bAffinityDone = 1;
|
||||
}
|
||||
allOk = sqlite3VdbeMakeLabel(pParse);
|
||||
sqlite3VdbeVerifyAbortable(v, onError);
|
||||
sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
|
||||
pCopy = sqlite3ExprDup(db, pExpr, 0);
|
||||
if( !db->mallocFailed ){
|
||||
sqlite3ExprIfTrue(pParse, pCopy, allOk, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
sqlite3ExprDelete(db, pCopy);
|
||||
if( onError==OE_Ignore ){
|
||||
sqlite3VdbeGoto(v, ignoreDest);
|
||||
}else{
|
||||
@ -1831,7 +1840,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
switch( onError ){
|
||||
default: {
|
||||
onError = OE_Abort;
|
||||
/* Fall thru into the next case */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
@ -1892,7 +1901,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
case OE_Update: {
|
||||
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur);
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
#endif
|
||||
case OE_Ignore: {
|
||||
@ -1935,7 +1944,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
sqlite3TableAffinity(v, pTab, regNewData+1);
|
||||
bAffinityDone = 1;
|
||||
}
|
||||
VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
|
||||
VdbeNoopComment((v, "prep index %s", pIdx->zName));
|
||||
iThisCur = iIdxCur+ix;
|
||||
|
||||
|
||||
@ -2113,7 +2122,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
case OE_Update: {
|
||||
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix);
|
||||
/* Fall through */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
#endif
|
||||
case OE_Ignore: {
|
||||
@ -2170,12 +2179,14 @@ void sqlite3GenerateConstraintChecks(
|
||||
x = *sqlite3VdbeGetOp(v, addrConflictCk);
|
||||
if( x.opcode!=OP_IdxRowid ){
|
||||
int p2; /* New P2 value for copied conflict check opcode */
|
||||
const char *zP4;
|
||||
if( sqlite3OpcodeProperty[x.opcode]&OPFLG_JUMP ){
|
||||
p2 = lblRecheckOk;
|
||||
}else{
|
||||
p2 = x.p2;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, x.opcode, x.p1, p2, x.p3, x.p4.z, x.p4type);
|
||||
zP4 = x.p4type==P4_INT32 ? SQLITE_INT_TO_PTR(x.p4.i) : x.p4.z;
|
||||
sqlite3VdbeAddOp4(v, x.opcode, x.p1, p2, x.p3, zP4, x.p4type);
|
||||
sqlite3VdbeChangeP5(v, x.p5);
|
||||
VdbeCoverageIf(v, p2!=x.p2);
|
||||
}
|
||||
@ -2599,7 +2610,7 @@ static int xferOptimization(
|
||||
return 0; /* FROM clause does not contain a real table */
|
||||
}
|
||||
if( pSrc->tnum==pDest->tnum && pSrc->pSchema==pDest->pSchema ){
|
||||
testcase( pSrc!=pDest ); /* Possible due to bad sqlite_master.rootpage */
|
||||
testcase( pSrc!=pDest ); /* Possible due to bad sqlite_schema.rootpage */
|
||||
return 0; /* tab1 and tab2 may not be the same table */
|
||||
}
|
||||
if( HasRowid(pDest)!=HasRowid(pSrc) ){
|
||||
@ -2783,14 +2794,13 @@ static int xferOptimization(
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
|
||||
OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
||||
insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
|
||||
}else{
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
|
||||
(char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeChangeP5(v, insFlags);
|
||||
@ -2815,7 +2825,6 @@ static int xferOptimization(
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
|
||||
VdbeComment((v, "%s", pDestIdx->zName));
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( db->mDbFlags & DBFLAG_Vacuum ){
|
||||
/* This INSERT command is part of a VACUUM operation, which guarantees
|
||||
** that the destination table is empty. If all indexed columns use
|
||||
@ -2839,10 +2848,10 @@ static int xferOptimization(
|
||||
idxInsFlags = OPFLAG_USESEEKRESULT;
|
||||
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
|
||||
}
|
||||
}
|
||||
if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
|
||||
}else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
|
||||
idxInsFlags |= OPFLAG_NCHANGE;
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
|
||||
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
|
||||
|
@ -474,8 +474,20 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
sqlite3_filename_database,
|
||||
sqlite3_filename_journal,
|
||||
sqlite3_filename_wal,
|
||||
/* Version 3.32.0 and later */
|
||||
sqlite3_create_filename,
|
||||
sqlite3_free_filename,
|
||||
sqlite3_database_file_object,
|
||||
};
|
||||
|
||||
/* True if x is the directory separator character
|
||||
*/
|
||||
#if SQLITE_OS_WIN
|
||||
# define DirSep(X) ((X)=='/'||(X)=='\\')
|
||||
#else
|
||||
# define DirSep(X) ((X)=='/')
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Attempt to load an SQLite extension library contained in the file
|
||||
** zFile. The entry point is zProc. zProc may be 0 in which case a
|
||||
@ -577,7 +589,7 @@ static int sqlite3LoadExtension(
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memcpy(zAltEntry, "sqlite3_", 8);
|
||||
for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
|
||||
for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
|
||||
iFile++;
|
||||
if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
|
||||
for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
|
||||
@ -677,7 +689,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
|
||||
** The following object holds the list of automatically loaded
|
||||
** extensions.
|
||||
**
|
||||
** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER
|
||||
** This list is shared across threads. The SQLITE_MUTEX_STATIC_MAIN
|
||||
** mutex must be held while accessing this list.
|
||||
*/
|
||||
typedef struct sqlite3AutoExtList sqlite3AutoExtList;
|
||||
@ -719,7 +731,7 @@ int sqlite3_auto_extension(
|
||||
{
|
||||
u32 i;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
|
||||
#endif
|
||||
wsdAutoextInit;
|
||||
sqlite3_mutex_enter(mutex);
|
||||
@ -757,7 +769,7 @@ int sqlite3_cancel_auto_extension(
|
||||
void (*xInit)(void)
|
||||
){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
|
||||
#endif
|
||||
int i;
|
||||
int n = 0;
|
||||
@ -784,7 +796,7 @@ void sqlite3_reset_auto_extension(void){
|
||||
#endif
|
||||
{
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
|
||||
#endif
|
||||
wsdAutoextInit;
|
||||
sqlite3_mutex_enter(mutex);
|
||||
@ -814,7 +826,7 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
for(i=0; go; i++){
|
||||
char *zErrmsg;
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
||||
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||||
const sqlite3_api_routines *pThunk = 0;
|
||||
|
428
src/main.c
428
src/main.c
@ -25,19 +25,82 @@
|
||||
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
||||
# include "sqliteicu.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This is an extension initializer that is a no-op and always
|
||||
** succeeds, except that it fails if the fault-simulation is set
|
||||
** to 500.
|
||||
*/
|
||||
static int sqlite3TestExtInit(sqlite3 *db){
|
||||
(void)db;
|
||||
return sqlite3FaultSim(500);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Forward declarations of external module initializer functions
|
||||
** for modules that need them.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_FTS1
|
||||
int sqlite3Fts1Init(sqlite3*);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_FTS2
|
||||
int sqlite3Fts2Init(sqlite3*);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
int sqlite3Fts5Init(sqlite3*);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_JSON1
|
||||
int sqlite3Json1Init(sqlite3*);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STMTVTAB
|
||||
int sqlite3StmtVtabInit(sqlite3*);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
int sqlite3Fts5Init(sqlite3*);
|
||||
|
||||
/*
|
||||
** An array of pointers to extension initializer functions for
|
||||
** built-in extensions.
|
||||
*/
|
||||
static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
|
||||
#ifdef SQLITE_ENABLE_FTS1
|
||||
sqlite3Fts1Init,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_FTS2
|
||||
sqlite3Fts2Init,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_FTS3
|
||||
sqlite3Fts3Init,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
sqlite3Fts5Init,
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
||||
sqlite3IcuInit,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
sqlite3RtreeInit,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_DBPAGE_VTAB
|
||||
sqlite3DbpageRegister,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
|
||||
sqlite3DbstatRegister,
|
||||
#endif
|
||||
sqlite3TestExtInit,
|
||||
#ifdef SQLITE_ENABLE_JSON1
|
||||
sqlite3Json1Init,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STMTVTAB
|
||||
sqlite3StmtVtabInit,
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
|
||||
sqlite3VdbeBytecodeVtabInit,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
|
||||
** contains the text of SQLITE_VERSION macro.
|
||||
** contains the text of SQLITE_VERSION macro.
|
||||
*/
|
||||
const char sqlite3_version[] = SQLITE_VERSION;
|
||||
#endif
|
||||
@ -138,7 +201,7 @@ char *sqlite3_data_directory = 0;
|
||||
** without blocking.
|
||||
*/
|
||||
int sqlite3_initialize(void){
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
|
||||
MUTEX_LOGIC( sqlite3_mutex *pMainMtx; ) /* The main static mutex */
|
||||
int rc; /* Result code */
|
||||
#ifdef SQLITE_EXTRA_INIT
|
||||
int bRunExtraInit = 0; /* Extra initialization needed */
|
||||
@ -161,7 +224,10 @@ int sqlite3_initialize(void){
|
||||
** must be complete. So isInit must not be set until the very end
|
||||
** of this routine.
|
||||
*/
|
||||
if( sqlite3GlobalConfig.isInit ) return SQLITE_OK;
|
||||
if( sqlite3GlobalConfig.isInit ){
|
||||
sqlite3MemoryBarrier();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Make sure the mutex subsystem is initialized. If unable to
|
||||
** initialize the mutex subsystem, return early with the error.
|
||||
@ -175,13 +241,13 @@ int sqlite3_initialize(void){
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Initialize the malloc() system and the recursive pInitMutex mutex.
|
||||
** This operation is protected by the STATIC_MASTER mutex. Note that
|
||||
** This operation is protected by the STATIC_MAIN mutex. Note that
|
||||
** MutexAlloc() is called for a static mutex prior to initializing the
|
||||
** malloc subsystem - this implies that the allocation of a static
|
||||
** mutex must not require support from the malloc subsystem.
|
||||
*/
|
||||
MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
|
||||
sqlite3_mutex_enter(pMainMtx);
|
||||
sqlite3GlobalConfig.isMutexInit = 1;
|
||||
if( !sqlite3GlobalConfig.isMallocInit ){
|
||||
rc = sqlite3MallocInit();
|
||||
@ -199,7 +265,7 @@ int sqlite3_initialize(void){
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3GlobalConfig.nRefInitMutex++;
|
||||
}
|
||||
sqlite3_mutex_leave(pMaster);
|
||||
sqlite3_mutex_leave(pMainMtx);
|
||||
|
||||
/* If rc is not SQLITE_OK at this point, then either the malloc
|
||||
** subsystem could not be initialized or the system failed to allocate
|
||||
@ -247,6 +313,7 @@ int sqlite3_initialize(void){
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
|
||||
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
|
||||
sqlite3MemoryBarrier();
|
||||
sqlite3GlobalConfig.isInit = 1;
|
||||
#ifdef SQLITE_EXTRA_INIT
|
||||
bRunExtraInit = 1;
|
||||
@ -259,14 +326,14 @@ int sqlite3_initialize(void){
|
||||
/* Go back under the static mutex and clean up the recursive
|
||||
** mutex to prevent a resource leak.
|
||||
*/
|
||||
sqlite3_mutex_enter(pMaster);
|
||||
sqlite3_mutex_enter(pMainMtx);
|
||||
sqlite3GlobalConfig.nRefInitMutex--;
|
||||
if( sqlite3GlobalConfig.nRefInitMutex<=0 ){
|
||||
assert( sqlite3GlobalConfig.nRefInitMutex==0 );
|
||||
sqlite3_mutex_free(sqlite3GlobalConfig.pInitMutex);
|
||||
sqlite3GlobalConfig.pInitMutex = 0;
|
||||
}
|
||||
sqlite3_mutex_leave(pMaster);
|
||||
sqlite3_mutex_leave(pMainMtx);
|
||||
|
||||
/* The following is just a sanity check to make sure SQLite has
|
||||
** been compiled correctly. It is important to run this code, but
|
||||
@ -1135,7 +1202,7 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( db->mTrace & SQLITE_TRACE_CLOSE ){
|
||||
db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
|
||||
}
|
||||
|
||||
/* Force xDisconnect calls on all virtual tables */
|
||||
@ -1548,8 +1615,7 @@ const char *sqlite3ErrStr(int rc){
|
||||
*/
|
||||
static int sqliteDefaultBusyCallback(
|
||||
void *ptr, /* Database connection */
|
||||
int count, /* Number of times table has been busy */
|
||||
sqlite3_file *pFile /* The file on which the lock occurred */
|
||||
int count /* Number of times table has been busy */
|
||||
){
|
||||
#if SQLITE_OS_WIN || HAVE_USLEEP
|
||||
/* This case is for systems that have support for sleeping for fractions of
|
||||
@ -1563,19 +1629,6 @@ static int sqliteDefaultBusyCallback(
|
||||
int tmout = db->busyTimeout;
|
||||
int delay, prior;
|
||||
|
||||
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
|
||||
if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
|
||||
if( count ){
|
||||
tmout = 0;
|
||||
sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
|
||||
return 0;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(pFile);
|
||||
#endif
|
||||
assert( count>=0 );
|
||||
if( count < NDELAY ){
|
||||
delay = delays[count];
|
||||
@ -1595,7 +1648,6 @@ static int sqliteDefaultBusyCallback(
|
||||
** must be done in increments of whole seconds */
|
||||
sqlite3 *db = (sqlite3 *)ptr;
|
||||
int tmout = ((sqlite3 *)ptr)->busyTimeout;
|
||||
UNUSED_PARAMETER(pFile);
|
||||
if( (count+1)*1000 > tmout ){
|
||||
return 0;
|
||||
}
|
||||
@ -1613,19 +1665,10 @@ static int sqliteDefaultBusyCallback(
|
||||
** If this routine returns non-zero, the lock is retried. If it
|
||||
** returns 0, the operation aborts with an SQLITE_BUSY error.
|
||||
*/
|
||||
int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
|
||||
int sqlite3InvokeBusyHandler(BusyHandler *p){
|
||||
int rc;
|
||||
if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
|
||||
if( p->bExtraFileArg ){
|
||||
/* Add an extra parameter with the pFile pointer to the end of the
|
||||
** callback argument list */
|
||||
int (*xTra)(void*,int,sqlite3_file*);
|
||||
xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
|
||||
rc = xTra(p->pBusyArg, p->nBusy, pFile);
|
||||
}else{
|
||||
/* Legacy style busy handler callback */
|
||||
rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
|
||||
}
|
||||
rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
|
||||
if( rc==0 ){
|
||||
p->nBusy = -1;
|
||||
}else{
|
||||
@ -1650,7 +1693,6 @@ int sqlite3_busy_handler(
|
||||
db->busyHandler.xBusyHandler = xBusy;
|
||||
db->busyHandler.pBusyArg = pArg;
|
||||
db->busyHandler.nBusy = 0;
|
||||
db->busyHandler.bExtraFileArg = 0;
|
||||
db->busyTimeout = 0;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
@ -1701,7 +1743,6 @@ int sqlite3_busy_timeout(sqlite3 *db, int ms){
|
||||
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
|
||||
(void*)db);
|
||||
db->busyTimeout = ms;
|
||||
db->busyHandler.bExtraFileArg = 1;
|
||||
}else{
|
||||
sqlite3_busy_handler(db, 0, 0);
|
||||
}
|
||||
@ -1718,7 +1759,7 @@ void sqlite3_interrupt(sqlite3 *db){
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
db->u1.isInterrupted = 1;
|
||||
AtomicStore(&db->u1.isInterrupted, 1);
|
||||
}
|
||||
|
||||
|
||||
@ -2050,7 +2091,7 @@ void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pOld = db->pTraceArg;
|
||||
db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
|
||||
db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
|
||||
db->trace.xLegacy = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return pOld;
|
||||
@ -2074,7 +2115,7 @@ int sqlite3_trace_v2(
|
||||
if( mTrace==0 ) xTrace = 0;
|
||||
if( xTrace==0 ) mTrace = 0;
|
||||
db->mTrace = mTrace;
|
||||
db->xTrace = xTrace;
|
||||
db->trace.xV2 = xTrace;
|
||||
db->pTraceArg = pArg;
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
@ -2340,7 +2381,7 @@ int sqlite3_wal_checkpoint_v2(
|
||||
/* If there are no active statements, clear the interrupt flag at this
|
||||
** point. */
|
||||
if( db->nVdbeActive==0 ){
|
||||
db->u1.isInterrupted = 0;
|
||||
AtomicStore(&db->u1.isInterrupted, 0);
|
||||
}
|
||||
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@ -2750,9 +2791,11 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
**
|
||||
** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to
|
||||
** the VFS that should be used to open the database file. *pzFile is set to
|
||||
** point to a buffer containing the name of the file to open. It is the
|
||||
** responsibility of the caller to eventually call sqlite3_free() to release
|
||||
** this buffer.
|
||||
** point to a buffer containing the name of the file to open. The value
|
||||
** stored in *pzFile is a database name acceptable to sqlite3_uri_parameter()
|
||||
** and is in the same format as names created using sqlite3_create_filename().
|
||||
** The caller must invoke sqlite3_free_filename() (not sqlite3_free()!) on
|
||||
** the value returned in *pzFile to avoid a memory leak.
|
||||
**
|
||||
** If an error occurs, then an SQLite error code is returned and *pzErrMsg
|
||||
** may be set to point to a buffer containing an English language error
|
||||
@ -2784,7 +2827,7 @@ int sqlite3ParseUri(
|
||||
int eState; /* Parser state when parsing URI */
|
||||
int iIn; /* Input character index */
|
||||
int iOut = 0; /* Output character index */
|
||||
u64 nByte = nUri+2; /* Bytes of space to allocate */
|
||||
u64 nByte = nUri+8; /* Bytes of space to allocate */
|
||||
|
||||
/* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
|
||||
** method that there may be extra parameters following the file-name. */
|
||||
@ -2794,6 +2837,9 @@ int sqlite3ParseUri(
|
||||
zFile = sqlite3_malloc64(nByte);
|
||||
if( !zFile ) return SQLITE_NOMEM_BKPT;
|
||||
|
||||
memset(zFile, 0, 4); /* 4-byte of 0x00 is the start of DB name marker */
|
||||
zFile += 4;
|
||||
|
||||
iIn = 5;
|
||||
#ifdef SQLITE_ALLOW_URI_AUTHORITY
|
||||
if( strncmp(zUri+5, "///", 3)==0 ){
|
||||
@ -2883,8 +2929,7 @@ int sqlite3ParseUri(
|
||||
zFile[iOut++] = c;
|
||||
}
|
||||
if( eState==1 ) zFile[iOut++] = '\0';
|
||||
zFile[iOut++] = '\0';
|
||||
zFile[iOut++] = '\0';
|
||||
memset(zFile+iOut, 0, 4); /* end-of-options + empty journal filenames */
|
||||
|
||||
/* Check if there were any options specified that should be interpreted
|
||||
** here. Options that are interpreted here include "vfs" and those that
|
||||
@ -2964,13 +3009,14 @@ int sqlite3ParseUri(
|
||||
}
|
||||
|
||||
}else{
|
||||
zFile = sqlite3_malloc64(nUri+2);
|
||||
zFile = sqlite3_malloc64(nUri+8);
|
||||
if( !zFile ) return SQLITE_NOMEM_BKPT;
|
||||
memset(zFile, 0, 4);
|
||||
zFile += 4;
|
||||
if( nUri ){
|
||||
memcpy(zFile, zUri, nUri);
|
||||
}
|
||||
zFile[nUri] = '\0';
|
||||
zFile[nUri+1] = '\0';
|
||||
memset(zFile+nUri, 0, 4);
|
||||
flags &= ~SQLITE_OPEN_URI;
|
||||
}
|
||||
|
||||
@ -2981,7 +3027,7 @@ int sqlite3ParseUri(
|
||||
}
|
||||
parse_uri_out:
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(zFile);
|
||||
sqlite3_free_filename(zFile);
|
||||
zFile = 0;
|
||||
}
|
||||
*pFlags = flags;
|
||||
@ -2989,6 +3035,22 @@ int sqlite3ParseUri(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine does the core work of extracting URI parameters from a
|
||||
** database filename for the sqlite3_uri_parameter() interface.
|
||||
*/
|
||||
static const char *uriParameter(const char *zFilename, const char *zParam){
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] ){
|
||||
int x = strcmp(zFilename, zParam);
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
if( x==0 ) return zFilename;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* BEGIN SQLCIPHER */
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
/*
|
||||
** Process URI filename query parameters relevant to the SQLite Encryption
|
||||
@ -3001,7 +3063,9 @@ int sqlite3CodecQueryParameters(
|
||||
const char *zUri /* URI filename */
|
||||
){
|
||||
const char *zKey;
|
||||
if( (zKey = sqlite3_uri_parameter(zUri, "hexkey"))!=0 && zKey[0] ){
|
||||
if( zUri==0 ){
|
||||
return 0;
|
||||
}else if( (zKey = uriParameter(zUri, "hexkey"))!=0 && zKey[0] ){
|
||||
u8 iByte;
|
||||
int i;
|
||||
char zDecoded[40];
|
||||
@ -3011,10 +3075,10 @@ int sqlite3CodecQueryParameters(
|
||||
}
|
||||
sqlite3_key_v2(db, zDb, zDecoded, i/2);
|
||||
return 1;
|
||||
}else if( (zKey = sqlite3_uri_parameter(zUri, "key"))!=0 ){
|
||||
}else if( (zKey = uriParameter(zUri, "key"))!=0 ){
|
||||
sqlite3_key_v2(db, zDb, zKey, sqlite3Strlen30(zKey));
|
||||
return 1;
|
||||
}else if( (zKey = sqlite3_uri_parameter(zUri, "textkey"))!=0 ){
|
||||
}else if( (zKey = uriParameter(zUri, "textkey"))!=0 ){
|
||||
sqlite3_key_v2(db, zDb, zKey, -1);
|
||||
return 1;
|
||||
}else{
|
||||
@ -3022,6 +3086,7 @@ int sqlite3CodecQueryParameters(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
|
||||
/*
|
||||
@ -3040,6 +3105,7 @@ static int openDatabase(
|
||||
int isThreadsafe; /* True for threadsafe connections */
|
||||
char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
|
||||
char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
|
||||
int i; /* Loop counter */
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
|
||||
@ -3083,7 +3149,7 @@ static int openDatabase(
|
||||
SQLITE_OPEN_MAIN_JOURNAL |
|
||||
SQLITE_OPEN_TEMP_JOURNAL |
|
||||
SQLITE_OPEN_SUBJOURNAL |
|
||||
SQLITE_OPEN_MASTER_JOURNAL |
|
||||
SQLITE_OPEN_SUPER_JOURNAL |
|
||||
SQLITE_OPEN_NOMUTEX |
|
||||
SQLITE_OPEN_FULLMUTEX |
|
||||
SQLITE_OPEN_WAL
|
||||
@ -3187,6 +3253,9 @@ static int openDatabase(
|
||||
#endif
|
||||
#if defined(SQLITE_DEFAULT_DEFENSIVE)
|
||||
| SQLITE_Defensive
|
||||
#endif
|
||||
#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
|
||||
| SQLITE_LegacyAlter
|
||||
#endif
|
||||
;
|
||||
sqlite3HashInit(&db->aCollSeq);
|
||||
@ -3209,11 +3278,6 @@ static int openDatabase(
|
||||
if( db->mallocFailed ){
|
||||
goto opendb_out;
|
||||
}
|
||||
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
|
||||
** strings is BINARY.
|
||||
*/
|
||||
db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0);
|
||||
assert( db->pDfltColl!=0 );
|
||||
|
||||
/* Parse the filename/URI argument
|
||||
**
|
||||
@ -3235,7 +3299,7 @@ static int openDatabase(
|
||||
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
|
||||
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
|
||||
if( ((1<<(flags&7)) & 0x46)==0 ){
|
||||
rc = SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */
|
||||
rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */
|
||||
}else{
|
||||
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
|
||||
}
|
||||
@ -3258,7 +3322,9 @@ static int openDatabase(
|
||||
}
|
||||
sqlite3BtreeEnter(db->aDb[0].pBt);
|
||||
db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
|
||||
if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db);
|
||||
if( !db->mallocFailed ){
|
||||
sqlite3SetTextEncoding(db, SCHEMA_ENC(db));
|
||||
}
|
||||
sqlite3BtreeLeave(db->aDb[0].pBt);
|
||||
db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
|
||||
|
||||
@ -3283,14 +3349,11 @@ static int openDatabase(
|
||||
sqlite3RegisterPerConnectionBuiltinFunctions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
/* Register any built-in FTS5 module before loading the automatic
|
||||
** extensions. This allows automatic extensions to register FTS5
|
||||
** tokenizers and auxiliary functions. */
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Init(db);
|
||||
|
||||
/* Load compiled-in extensions */
|
||||
for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){
|
||||
rc = sqlite3BuiltinExtensions[i](db);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load automatic extensions - extensions that have been registered
|
||||
** using the sqlite3_automatic_extension() API.
|
||||
@ -3303,62 +3366,6 @@ static int openDatabase(
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS1
|
||||
if( !db->mallocFailed ){
|
||||
extern int sqlite3Fts1Init(sqlite3*);
|
||||
rc = sqlite3Fts1Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS2
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
extern int sqlite3Fts2Init(sqlite3*);
|
||||
rc = sqlite3Fts2Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts3Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
rc = sqlite3IcuInit(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3RtreeInit(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_DBPAGE_VTAB
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3DbpageRegister(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3DbstatRegister(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_JSON1
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3Json1Init(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMTVTAB
|
||||
if( !db->mallocFailed && rc==SQLITE_OK){
|
||||
rc = sqlite3StmtVtabInit(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLCIPHER_EXT
|
||||
if( !db->mallocFailed && rc==SQLITE_OK ){
|
||||
extern int sqlcipherVtabInit(sqlite3 *);
|
||||
@ -3413,10 +3420,12 @@ opendb_out:
|
||||
sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0);
|
||||
}
|
||||
#endif
|
||||
/* BEGIN SQLCIPHER */
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
if( rc==SQLITE_OK ) sqlite3CodecQueryParameters(db, 0, zOpen);
|
||||
#endif
|
||||
sqlite3_free(zOpen);
|
||||
/* END SQLCIPHER */
|
||||
sqlite3_free_filename(zOpen);
|
||||
return rc & 0xff;
|
||||
}
|
||||
|
||||
@ -3643,13 +3652,15 @@ int sqlite3CantopenError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return sqlite3ReportError(SQLITE_CANTOPEN, lineno, "cannot open file");
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO)
|
||||
int sqlite3CorruptPgnoError(int lineno, Pgno pgno){
|
||||
char zMsg[100];
|
||||
sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno);
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
|
||||
}
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3NomemError(int lineno){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
return sqlite3ReportError(SQLITE_NOMEM, lineno, "OOM");
|
||||
@ -3852,6 +3863,13 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
|
||||
}else if( op==SQLITE_FCNTL_DATA_VERSION ){
|
||||
*(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
|
||||
rc = SQLITE_OK;
|
||||
}else if( op==SQLITE_FCNTL_RESERVE_BYTES ){
|
||||
int iNew = *(int*)pArg;
|
||||
*(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree);
|
||||
if( iNew>=0 && iNew<=255 ){
|
||||
sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = sqlite3OsFileControl(fd, op, pArg);
|
||||
}
|
||||
@ -4068,20 +4086,6 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
|
||||
**
|
||||
** Set the nReserve size to N for the main database on the database
|
||||
** connection db.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_RESERVE: {
|
||||
sqlite3 *db = va_arg(ap, sqlite3*);
|
||||
int x = va_arg(ap,int);
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N)
|
||||
**
|
||||
** Enable or disable various optimizations for testing purposes. The
|
||||
@ -4134,8 +4138,14 @@ int sqlite3_test_control(int op, ...){
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int);
|
||||
**
|
||||
** Set or clear a flag that causes SQLite to verify that type, name,
|
||||
** and tbl_name fields of the sqlite_master table. This is normally
|
||||
** and tbl_name fields of the sqlite_schema table. This is normally
|
||||
** on, but it is sometimes useful to turn it off for testing.
|
||||
**
|
||||
** 2020-07-22: Disabling EXTRA_SCHEMA_CHECKS also disables the
|
||||
** verification of rootpage numbers when parsing the schema. This
|
||||
** is useful to make it easier to reach strange internal error states
|
||||
** during testing. The EXTRA_SCHEMA_CHECKS setting is always enabled
|
||||
** in production.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: {
|
||||
sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int);
|
||||
@ -4250,6 +4260,83 @@ int sqlite3_test_control(int op, ...){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The Pager stores the Database filename, Journal filename, and WAL filename
|
||||
** consecutively in memory, in that order. The database filename is prefixed
|
||||
** by four zero bytes. Locate the start of the database filename by searching
|
||||
** backwards for the first byte following four consecutive zero bytes.
|
||||
**
|
||||
** This only works if the filename passed in was obtained from the Pager.
|
||||
*/
|
||||
static const char *databaseName(const char *zName){
|
||||
while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
|
||||
zName--;
|
||||
}
|
||||
return zName;
|
||||
}
|
||||
|
||||
/*
|
||||
** Append text z[] to the end of p[]. Return a pointer to the first
|
||||
** character after then zero terminator on the new text in p[].
|
||||
*/
|
||||
static char *appendText(char *p, const char *z){
|
||||
size_t n = strlen(z);
|
||||
memcpy(p, z, n+1);
|
||||
return p+n+1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate memory to hold names for a database, journal file, WAL file,
|
||||
** and query parameters. The pointer returned is valid for use by
|
||||
** sqlite3_filename_database() and sqlite3_uri_parameter() and related
|
||||
** functions.
|
||||
**
|
||||
** Memory layout must be compatible with that generated by the pager
|
||||
** and expected by sqlite3_uri_parameter() and databaseName().
|
||||
*/
|
||||
char *sqlite3_create_filename(
|
||||
const char *zDatabase,
|
||||
const char *zJournal,
|
||||
const char *zWal,
|
||||
int nParam,
|
||||
const char **azParam
|
||||
){
|
||||
sqlite3_int64 nByte;
|
||||
int i;
|
||||
char *pResult, *p;
|
||||
nByte = strlen(zDatabase) + strlen(zJournal) + strlen(zWal) + 10;
|
||||
for(i=0; i<nParam*2; i++){
|
||||
nByte += strlen(azParam[i])+1;
|
||||
}
|
||||
pResult = p = sqlite3_malloc64( nByte );
|
||||
if( p==0 ) return 0;
|
||||
memset(p, 0, 4);
|
||||
p += 4;
|
||||
p = appendText(p, zDatabase);
|
||||
for(i=0; i<nParam*2; i++){
|
||||
p = appendText(p, azParam[i]);
|
||||
}
|
||||
*(p++) = 0;
|
||||
p = appendText(p, zJournal);
|
||||
p = appendText(p, zWal);
|
||||
*(p++) = 0;
|
||||
*(p++) = 0;
|
||||
assert( (sqlite3_int64)(p - pResult)==nByte );
|
||||
return pResult + 4;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free memory obtained from sqlite3_create_filename(). It is a severe
|
||||
** error to call this routine with any parameter other than a pointer
|
||||
** previously obtained from sqlite3_create_filename() or a NULL pointer.
|
||||
*/
|
||||
void sqlite3_free_filename(char *p){
|
||||
if( p==0 ) return;
|
||||
p = (char*)databaseName(p);
|
||||
sqlite3_free(p - 4);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This is a utility routine, useful to VFS implementations, that checks
|
||||
** to see if a database file was a URI that contained a specific query
|
||||
@ -4263,14 +4350,8 @@ int sqlite3_test_control(int op, ...){
|
||||
*/
|
||||
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
||||
if( zFilename==0 || zParam==0 ) return 0;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] ){
|
||||
int x = strcmp(zFilename, zParam);
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
if( x==0 ) return zFilename;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
}
|
||||
return 0;
|
||||
zFilename = databaseName(zFilename);
|
||||
return uriParameter(zFilename, zParam);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4278,6 +4359,7 @@ const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
||||
*/
|
||||
const char *sqlite3_uri_key(const char *zFilename, int N){
|
||||
if( zFilename==0 || N<0 ) return 0;
|
||||
zFilename = databaseName(zFilename);
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] && (N--)>0 ){
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
@ -4311,25 +4393,6 @@ sqlite3_int64 sqlite3_uri_int64(
|
||||
return bDflt;
|
||||
}
|
||||
|
||||
/*
|
||||
** The Pager stores the Journal filename, WAL filename, and Database filename
|
||||
** consecutively in memory, in that order, with prefixes \000\001\000,
|
||||
** \002\000, and \003\000, in that order. Thus the three names look like query
|
||||
** parameters if you start at the first prefix.
|
||||
**
|
||||
** This routine backs up a filename to the start of the first prefix.
|
||||
**
|
||||
** This only works if the filenamed passed in was obtained from the Pager.
|
||||
*/
|
||||
static const char *startOfNameList(const char *zName){
|
||||
while( zName[0]!='\001' || zName[1]!=0 ){
|
||||
zName -= 3;
|
||||
while( zName[0]!='\000' ){ zName--; }
|
||||
zName++;
|
||||
}
|
||||
return zName-1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Translate a filename that was handed to a VFS routine into the corresponding
|
||||
** database, journal, or WAL file.
|
||||
@ -4341,14 +4404,25 @@ static const char *startOfNameList(const char *zName){
|
||||
** corruption.
|
||||
*/
|
||||
const char *sqlite3_filename_database(const char *zFilename){
|
||||
return sqlite3_uri_parameter(zFilename - 3, "\003");
|
||||
return databaseName(zFilename);
|
||||
}
|
||||
const char *sqlite3_filename_journal(const char *zFilename){
|
||||
const char *z = sqlite3_uri_parameter(startOfNameList(zFilename), "\001");
|
||||
return ALWAYS(z) && z[0] ? z : 0;
|
||||
zFilename = databaseName(zFilename);
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] ){
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
}
|
||||
return zFilename + 1;
|
||||
}
|
||||
const char *sqlite3_filename_wal(const char *zFilename){
|
||||
return sqlite3_uri_parameter(startOfNameList(zFilename), "\002");
|
||||
#ifdef SQLITE_OMIT_WAL
|
||||
return 0;
|
||||
#else
|
||||
zFilename = sqlite3_filename_journal(zFilename);
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
return zFilename;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
22
src/malloc.c
22
src/malloc.c
@ -111,7 +111,7 @@ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
||||
}
|
||||
mem0.alarmThreshold = n;
|
||||
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
mem0.nearlyFull = (n>0 && n<=nUsed);
|
||||
AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed);
|
||||
sqlite3_mutex_leave(mem0.mutex);
|
||||
excess = sqlite3_memory_used() - n;
|
||||
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
|
||||
@ -190,7 +190,7 @@ int sqlite3MallocInit(void){
|
||||
** sqlite3_soft_heap_limit().
|
||||
*/
|
||||
int sqlite3HeapNearlyFull(void){
|
||||
return mem0.nearlyFull;
|
||||
return AtomicLoad(&mem0.nearlyFull);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -254,7 +254,7 @@ static void mallocWithAlarm(int n, void **pp){
|
||||
if( mem0.alarmThreshold>0 ){
|
||||
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
if( nUsed >= mem0.alarmThreshold - nFull ){
|
||||
mem0.nearlyFull = 1;
|
||||
AtomicStore(&mem0.nearlyFull, 1);
|
||||
sqlite3MallocAlarm(nFull);
|
||||
if( mem0.hardLimit ){
|
||||
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
@ -264,7 +264,7 @@ static void mallocWithAlarm(int n, void **pp){
|
||||
}
|
||||
}
|
||||
}else{
|
||||
mem0.nearlyFull = 0;
|
||||
AtomicStore(&mem0.nearlyFull, 0);
|
||||
}
|
||||
}
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
@ -493,10 +493,12 @@ void *sqlite3Realloc(void *pOld, u64 nBytes){
|
||||
sqlite3MallocAlarm(nDiff);
|
||||
}
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
if( pNew==0 && mem0.alarmThreshold>0 ){
|
||||
sqlite3MallocAlarm((int)nBytes);
|
||||
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
||||
}
|
||||
#endif
|
||||
if( pNew ){
|
||||
nNew = sqlite3MallocSize(pNew);
|
||||
sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
|
||||
@ -681,7 +683,7 @@ static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
|
||||
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
||||
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
pNew = sqlite3_realloc64(p, n);
|
||||
pNew = sqlite3Realloc(p, n);
|
||||
if( !pNew ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
@ -728,11 +730,9 @@ char *sqlite3DbStrDup(sqlite3 *db, const char *z){
|
||||
char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
|
||||
char *zNew;
|
||||
assert( db!=0 );
|
||||
if( z==0 ){
|
||||
return 0;
|
||||
}
|
||||
assert( z!=0 || n==0 );
|
||||
assert( (n&0x7fffffff)==n );
|
||||
zNew = sqlite3DbMallocRawNN(db, n+1);
|
||||
zNew = z ? sqlite3DbMallocRawNN(db, n+1) : 0;
|
||||
if( zNew ){
|
||||
memcpy(zNew, z, (size_t)n);
|
||||
zNew[n] = 0;
|
||||
@ -771,7 +771,7 @@ void sqlite3OomFault(sqlite3 *db){
|
||||
if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
|
||||
db->mallocFailed = 1;
|
||||
if( db->nVdbeExec>0 ){
|
||||
db->u1.isInterrupted = 1;
|
||||
AtomicStore(&db->u1.isInterrupted, 1);
|
||||
}
|
||||
DisableLookaside;
|
||||
if( db->pParse ){
|
||||
@ -790,7 +790,7 @@ void sqlite3OomFault(sqlite3 *db){
|
||||
void sqlite3OomClear(sqlite3 *db){
|
||||
if( db->mallocFailed && db->nVdbeExec==0 ){
|
||||
db->mallocFailed = 0;
|
||||
db->u1.isInterrupted = 0;
|
||||
AtomicStore(&db->u1.isInterrupted, 0);
|
||||
assert( db->lookaside.bDisable>0 );
|
||||
EnableLookaside;
|
||||
}
|
||||
|
@ -379,7 +379,7 @@ void sqlite3MemSetDefault(void){
|
||||
** Set the "type" of an allocation.
|
||||
*/
|
||||
void sqlite3MemdebugSetType(void *p, u8 eType){
|
||||
if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
||||
if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){
|
||||
struct MemBlockHdr *pHdr;
|
||||
pHdr = sqlite3MemsysGetHeader(p);
|
||||
assert( pHdr->iForeGuard==FOREGUARD );
|
||||
@ -398,7 +398,7 @@ void sqlite3MemdebugSetType(void *p, u8 eType){
|
||||
*/
|
||||
int sqlite3MemdebugHasType(void *p, u8 eType){
|
||||
int rc = 1;
|
||||
if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
||||
if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){
|
||||
struct MemBlockHdr *pHdr;
|
||||
pHdr = sqlite3MemsysGetHeader(p);
|
||||
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
|
||||
@ -420,7 +420,7 @@ int sqlite3MemdebugHasType(void *p, u8 eType){
|
||||
*/
|
||||
int sqlite3MemdebugNoType(void *p, u8 eType){
|
||||
int rc = 1;
|
||||
if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
||||
if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){
|
||||
struct MemBlockHdr *pHdr;
|
||||
pHdr = sqlite3MemsysGetHeader(p);
|
||||
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
|
||||
|
148
src/mem3.c
148
src/mem3.c
@ -118,16 +118,16 @@ static SQLITE_WSD struct Mem3Global {
|
||||
/*
|
||||
** The minimum amount of free space that we have seen.
|
||||
*/
|
||||
u32 mnMaster;
|
||||
u32 mnKeyBlk;
|
||||
|
||||
/*
|
||||
** iMaster is the index of the master chunk. Most new allocations
|
||||
** occur off of this chunk. szMaster is the size (in Mem3Blocks)
|
||||
** of the current master. iMaster is 0 if there is not master chunk.
|
||||
** The master chunk is not in either the aiHash[] or aiSmall[].
|
||||
** iKeyBlk is the index of the key chunk. Most new allocations
|
||||
** occur off of this chunk. szKeyBlk is the size (in Mem3Blocks)
|
||||
** of the current key chunk. iKeyBlk is 0 if there is no key chunk.
|
||||
** The key chunk is not in either the aiHash[] or aiSmall[].
|
||||
*/
|
||||
u32 iMaster;
|
||||
u32 szMaster;
|
||||
u32 iKeyBlk;
|
||||
u32 szKeyBlk;
|
||||
|
||||
/*
|
||||
** Array of lists of free blocks according to the block size
|
||||
@ -263,34 +263,34 @@ static void *memsys3Checkout(u32 i, u32 nBlock){
|
||||
}
|
||||
|
||||
/*
|
||||
** Carve a piece off of the end of the mem3.iMaster free chunk.
|
||||
** Return a pointer to the new allocation. Or, if the master chunk
|
||||
** Carve a piece off of the end of the mem3.iKeyBlk free chunk.
|
||||
** Return a pointer to the new allocation. Or, if the key chunk
|
||||
** is not large enough, return 0.
|
||||
*/
|
||||
static void *memsys3FromMaster(u32 nBlock){
|
||||
static void *memsys3FromKeyBlk(u32 nBlock){
|
||||
assert( sqlite3_mutex_held(mem3.mutex) );
|
||||
assert( mem3.szMaster>=nBlock );
|
||||
if( nBlock>=mem3.szMaster-1 ){
|
||||
/* Use the entire master */
|
||||
void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster);
|
||||
mem3.iMaster = 0;
|
||||
mem3.szMaster = 0;
|
||||
mem3.mnMaster = 0;
|
||||
assert( mem3.szKeyBlk>=nBlock );
|
||||
if( nBlock>=mem3.szKeyBlk-1 ){
|
||||
/* Use the entire key chunk */
|
||||
void *p = memsys3Checkout(mem3.iKeyBlk, mem3.szKeyBlk);
|
||||
mem3.iKeyBlk = 0;
|
||||
mem3.szKeyBlk = 0;
|
||||
mem3.mnKeyBlk = 0;
|
||||
return p;
|
||||
}else{
|
||||
/* Split the master block. Return the tail. */
|
||||
/* Split the key block. Return the tail. */
|
||||
u32 newi, x;
|
||||
newi = mem3.iMaster + mem3.szMaster - nBlock;
|
||||
assert( newi > mem3.iMaster+1 );
|
||||
mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock;
|
||||
mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2;
|
||||
newi = mem3.iKeyBlk + mem3.szKeyBlk - nBlock;
|
||||
assert( newi > mem3.iKeyBlk+1 );
|
||||
mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = nBlock;
|
||||
mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x |= 2;
|
||||
mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1;
|
||||
mem3.szMaster -= nBlock;
|
||||
mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster;
|
||||
x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
|
||||
mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
|
||||
if( mem3.szMaster < mem3.mnMaster ){
|
||||
mem3.mnMaster = mem3.szMaster;
|
||||
mem3.szKeyBlk -= nBlock;
|
||||
mem3.aPool[newi-1].u.hdr.prevSize = mem3.szKeyBlk;
|
||||
x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2;
|
||||
mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x;
|
||||
if( mem3.szKeyBlk < mem3.mnKeyBlk ){
|
||||
mem3.mnKeyBlk = mem3.szKeyBlk;
|
||||
}
|
||||
return (void*)&mem3.aPool[newi];
|
||||
}
|
||||
@ -304,13 +304,13 @@ static void *memsys3FromMaster(u32 nBlock){
|
||||
** This routine examines all entries on the given list and tries
|
||||
** to coalesce each entries with adjacent free chunks.
|
||||
**
|
||||
** If it sees a chunk that is larger than mem3.iMaster, it replaces
|
||||
** the current mem3.iMaster with the new larger chunk. In order for
|
||||
** this mem3.iMaster replacement to work, the master chunk must be
|
||||
** If it sees a chunk that is larger than mem3.iKeyBlk, it replaces
|
||||
** the current mem3.iKeyBlk with the new larger chunk. In order for
|
||||
** this mem3.iKeyBlk replacement to work, the key chunk must be
|
||||
** linked into the hash tables. That is not the normal state of
|
||||
** affairs, of course. The calling routine must link the master
|
||||
** affairs, of course. The calling routine must link the key
|
||||
** chunk before invoking this routine, then must unlink the (possibly
|
||||
** changed) master chunk once this routine has finished.
|
||||
** changed) key chunk once this routine has finished.
|
||||
*/
|
||||
static void memsys3Merge(u32 *pRoot){
|
||||
u32 iNext, prev, size, i, x;
|
||||
@ -337,9 +337,9 @@ static void memsys3Merge(u32 *pRoot){
|
||||
}else{
|
||||
size /= 4;
|
||||
}
|
||||
if( size>mem3.szMaster ){
|
||||
mem3.iMaster = i;
|
||||
mem3.szMaster = size;
|
||||
if( size>mem3.szKeyBlk ){
|
||||
mem3.iKeyBlk = i;
|
||||
mem3.szKeyBlk = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -388,26 +388,26 @@ static void *memsys3MallocUnsafe(int nByte){
|
||||
|
||||
/* STEP 2:
|
||||
** Try to satisfy the allocation by carving a piece off of the end
|
||||
** of the master chunk. This step usually works if step 1 fails.
|
||||
** of the key chunk. This step usually works if step 1 fails.
|
||||
*/
|
||||
if( mem3.szMaster>=nBlock ){
|
||||
return memsys3FromMaster(nBlock);
|
||||
if( mem3.szKeyBlk>=nBlock ){
|
||||
return memsys3FromKeyBlk(nBlock);
|
||||
}
|
||||
|
||||
|
||||
/* STEP 3:
|
||||
** Loop through the entire memory pool. Coalesce adjacent free
|
||||
** chunks. Recompute the master chunk as the largest free chunk.
|
||||
** chunks. Recompute the key chunk as the largest free chunk.
|
||||
** Then try again to satisfy the allocation by carving a piece off
|
||||
** of the end of the master chunk. This step happens very
|
||||
** of the end of the key chunk. This step happens very
|
||||
** rarely (we hope!)
|
||||
*/
|
||||
for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){
|
||||
memsys3OutOfMemory(toFree);
|
||||
if( mem3.iMaster ){
|
||||
memsys3Link(mem3.iMaster);
|
||||
mem3.iMaster = 0;
|
||||
mem3.szMaster = 0;
|
||||
if( mem3.iKeyBlk ){
|
||||
memsys3Link(mem3.iKeyBlk);
|
||||
mem3.iKeyBlk = 0;
|
||||
mem3.szKeyBlk = 0;
|
||||
}
|
||||
for(i=0; i<N_HASH; i++){
|
||||
memsys3Merge(&mem3.aiHash[i]);
|
||||
@ -415,10 +415,10 @@ static void *memsys3MallocUnsafe(int nByte){
|
||||
for(i=0; i<MX_SMALL-1; i++){
|
||||
memsys3Merge(&mem3.aiSmall[i]);
|
||||
}
|
||||
if( mem3.szMaster ){
|
||||
memsys3Unlink(mem3.iMaster);
|
||||
if( mem3.szMaster>=nBlock ){
|
||||
return memsys3FromMaster(nBlock);
|
||||
if( mem3.szKeyBlk ){
|
||||
memsys3Unlink(mem3.iKeyBlk);
|
||||
if( mem3.szKeyBlk>=nBlock ){
|
||||
return memsys3FromKeyBlk(nBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -448,23 +448,23 @@ static void memsys3FreeUnsafe(void *pOld){
|
||||
mem3.aPool[i+size-1].u.hdr.size4x &= ~2;
|
||||
memsys3Link(i);
|
||||
|
||||
/* Try to expand the master using the newly freed chunk */
|
||||
if( mem3.iMaster ){
|
||||
while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){
|
||||
size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize;
|
||||
mem3.iMaster -= size;
|
||||
mem3.szMaster += size;
|
||||
memsys3Unlink(mem3.iMaster);
|
||||
x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
|
||||
mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
|
||||
mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
|
||||
/* Try to expand the key using the newly freed chunk */
|
||||
if( mem3.iKeyBlk ){
|
||||
while( (mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x&2)==0 ){
|
||||
size = mem3.aPool[mem3.iKeyBlk-1].u.hdr.prevSize;
|
||||
mem3.iKeyBlk -= size;
|
||||
mem3.szKeyBlk += size;
|
||||
memsys3Unlink(mem3.iKeyBlk);
|
||||
x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2;
|
||||
mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x;
|
||||
mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk;
|
||||
}
|
||||
x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
|
||||
while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){
|
||||
memsys3Unlink(mem3.iMaster+mem3.szMaster);
|
||||
mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4;
|
||||
mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
|
||||
mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
|
||||
x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2;
|
||||
while( (mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x&1)==0 ){
|
||||
memsys3Unlink(mem3.iKeyBlk+mem3.szKeyBlk);
|
||||
mem3.szKeyBlk += mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x/4;
|
||||
mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x;
|
||||
mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -560,11 +560,11 @@ static int memsys3Init(void *NotUsed){
|
||||
mem3.aPool = (Mem3Block *)sqlite3GlobalConfig.pHeap;
|
||||
mem3.nPool = (sqlite3GlobalConfig.nHeap / sizeof(Mem3Block)) - 2;
|
||||
|
||||
/* Initialize the master block. */
|
||||
mem3.szMaster = mem3.nPool;
|
||||
mem3.mnMaster = mem3.szMaster;
|
||||
mem3.iMaster = 1;
|
||||
mem3.aPool[0].u.hdr.size4x = (mem3.szMaster<<2) + 2;
|
||||
/* Initialize the key block. */
|
||||
mem3.szKeyBlk = mem3.nPool;
|
||||
mem3.mnKeyBlk = mem3.szKeyBlk;
|
||||
mem3.iKeyBlk = 1;
|
||||
mem3.aPool[0].u.hdr.size4x = (mem3.szKeyBlk<<2) + 2;
|
||||
mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool;
|
||||
mem3.aPool[mem3.nPool].u.hdr.size4x = 1;
|
||||
|
||||
@ -624,7 +624,7 @@ void sqlite3Memsys3Dump(const char *zFilename){
|
||||
fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8);
|
||||
}else{
|
||||
fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8,
|
||||
i==mem3.iMaster ? " **master**" : "");
|
||||
i==mem3.iKeyBlk ? " **key**" : "");
|
||||
}
|
||||
}
|
||||
for(i=0; i<MX_SMALL-1; i++){
|
||||
@ -645,9 +645,9 @@ void sqlite3Memsys3Dump(const char *zFilename){
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
fprintf(out, "master=%d\n", mem3.iMaster);
|
||||
fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szMaster*8);
|
||||
fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnMaster*8);
|
||||
fprintf(out, "key=%d\n", mem3.iKeyBlk);
|
||||
fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szKeyBlk*8);
|
||||
fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnKeyBlk*8);
|
||||
sqlite3_mutex_leave(mem3.mutex);
|
||||
if( out==stdout ){
|
||||
fflush(stdout);
|
||||
|
15
src/memdb.c
15
src/memdb.c
@ -166,7 +166,7 @@ static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){
|
||||
}
|
||||
newSz *= 2;
|
||||
if( newSz>p->szMax ) newSz = p->szMax;
|
||||
pNew = sqlite3_realloc64(p->aData, newSz);
|
||||
pNew = sqlite3Realloc(p->aData, newSz);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
p->aData = pNew;
|
||||
p->szAlloc = newSz;
|
||||
@ -339,12 +339,12 @@ static int memdbOpen(
|
||||
p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE;
|
||||
assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */
|
||||
*pOutFlags = flags | SQLITE_OPEN_MEMORY;
|
||||
p->base.pMethods = &memdb_io_methods;
|
||||
pFile->pMethods = &memdb_io_methods;
|
||||
p->szMax = sqlite3GlobalConfig.mxMemdbSize;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#if 0 /* Only used to delete rollback journals, master journals, and WAL
|
||||
#if 0 /* Only used to delete rollback journals, super-journals, and WAL
|
||||
** files, none of which exist in memdb. So this routine is never used */
|
||||
/*
|
||||
** Delete the file located at zPath. If the dirSync argument is true,
|
||||
@ -613,10 +613,11 @@ int sqlite3MemdbInit(void){
|
||||
sqlite3_vfs *pLower = sqlite3_vfs_find(0);
|
||||
int sz = pLower->szOsFile;
|
||||
memdb_vfs.pAppData = pLower;
|
||||
/* In all known configurations of SQLite, the size of a default
|
||||
** sqlite3_file is greater than the size of a memdb sqlite3_file.
|
||||
** Should that ever change, remove the following NEVER() */
|
||||
if( NEVER(sz<sizeof(MemFile)) ) sz = sizeof(MemFile);
|
||||
/* The following conditional can only be true when compiled for
|
||||
** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave
|
||||
** it in, to be safe, but it is marked as NO_TEST since there
|
||||
** is no way to reach it under most builds. */
|
||||
if( sz<sizeof(MemFile) ) sz = sizeof(MemFile); /*NO_TEST*/
|
||||
memdb_vfs.szOsFile = sz;
|
||||
return sqlite3_vfs_register(&memdb_vfs, 0);
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ int sqlite3JournalOpen(
|
||||
assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
|
||||
}
|
||||
|
||||
p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
|
||||
pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods;
|
||||
p->nSpill = nSpill;
|
||||
p->flags = flags;
|
||||
p->zJournal = zName;
|
||||
@ -392,7 +392,7 @@ void sqlite3MemJournalOpen(sqlite3_file *pJfd){
|
||||
int sqlite3JournalCreate(sqlite3_file *pJfd){
|
||||
int rc = SQLITE_OK;
|
||||
MemJournal *p = (MemJournal*)pJfd;
|
||||
if( p->pMethod==&MemJournalMethods && (
|
||||
if( pJfd->pMethods==&MemJournalMethods && (
|
||||
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
|
||||
p->nSpill>0
|
||||
#else
|
||||
|
@ -254,6 +254,7 @@ int sqlite3MutexInit(void){
|
||||
GLOBAL(int, mutexIsInit) = 1;
|
||||
#endif
|
||||
|
||||
sqlite3MemoryBarrier();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user