mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Merge all the latest trunk changes into the sessions branch - especially
the SQLITE_ENABLE_STAT3 enhancements. FossilOrigin-Name: 403431cac6b039b0693915c5422f08dc60dae230
This commit is contained in:
25
Makefile.in
25
Makefile.in
@@ -866,6 +866,7 @@ rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
|
||||
#
|
||||
TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
|
||||
TESTFIXTURE_FLAGS += -DBUILD_sqlite
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
|
||||
TESTFIXTURE_SRC1 = sqlite3.c
|
||||
@@ -885,18 +886,16 @@ soaktest: testfixture$(TEXE) sqlite3$(TEXE)
|
||||
test: testfixture$(TEXE) sqlite3$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/veryquick.test
|
||||
|
||||
sqlite3_analyzer$(TEXE): $(TESTFIXTURE_SRC) $(TOP)/tool/spaceanal.tcl
|
||||
sed \
|
||||
-e '/^#/d' \
|
||||
-e 's,\\,\\\\,g' \
|
||||
-e 's,",\\",g' \
|
||||
-e 's,^,",' \
|
||||
-e 's,$$,\\n",' \
|
||||
$(TOP)/tool/spaceanal.tcl >spaceanal_tcl.h
|
||||
$(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
|
||||
-DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE \
|
||||
$(TEMP_STORE) -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
|
||||
echo "#define TCLSH 2" > $@
|
||||
cat sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c >> $@
|
||||
echo "static const char *tclsh_main_loop(void){" >> $@
|
||||
echo "static const char *zMainloop = " >> $@
|
||||
$(NAWK) -f $(TOP)/tool/tostr.awk $(TOP)/tool/spaceanal.tcl >> $@
|
||||
echo "; return zMainloop; }" >> $@
|
||||
|
||||
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
|
||||
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
# Standard install and cleanup targets
|
||||
#
|
||||
@@ -930,9 +929,11 @@ clean:
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc .target_source
|
||||
rm -f tclsqlite3$(TEXE)
|
||||
rm -f testfixture$(TEXE) test.db
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.def
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
rm -f sqlite3.c
|
||||
rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c
|
||||
|
||||
distclean: clean
|
||||
rm -f config.log config.status libtool Makefile sqlite3.pc
|
||||
|
75
Makefile.msc
75
Makefile.msc
@@ -11,6 +11,10 @@ TOP = .
|
||||
#
|
||||
USE_AMALGAMATION = 1
|
||||
|
||||
# Set this non-0 to use the International Components for Unicode (ICU).
|
||||
#
|
||||
USE_ICU = 0
|
||||
|
||||
# Set this to non-0 to create and use PDBs.
|
||||
#
|
||||
SYMBOLS = 1
|
||||
@@ -31,8 +35,8 @@ DEBUG = 0
|
||||
# Version numbers and release number for the SQLite being compiled.
|
||||
#
|
||||
VERSION = 3.7
|
||||
VERSION_NUMBER = 3007008
|
||||
RELEASE = 3.7.8
|
||||
VERSION_NUMBER = 3007009
|
||||
RELEASE = 3.7.9
|
||||
|
||||
# C Compiler and options for use in building executables that
|
||||
# will run on the platform that is doing the build.
|
||||
@@ -109,6 +113,23 @@ TCLLIBDIR = c:\tcl\lib
|
||||
LIBTCL = tcl85.lib
|
||||
!endif
|
||||
|
||||
# The locations of the ICU header and library files. These variables
|
||||
# (ICUINCDIR, ICULIBDIR, and LIBICU) may be overridden via the environment
|
||||
# prior to running nmake in order to match the actual installed location on
|
||||
# this machine.
|
||||
#
|
||||
!if "$(ICUINCDIR)" == ""
|
||||
ICUINCDIR = c:\icu\include
|
||||
!endif
|
||||
|
||||
!if "$(ICULIBDIR)" == ""
|
||||
ICULIBDIR = c:\icu\lib
|
||||
!endif
|
||||
|
||||
!if "$(LIBICU)" == ""
|
||||
LIBICU = icuuc.lib icuin.lib
|
||||
!endif
|
||||
|
||||
# This is the command to use for tclsh - normally just "tclsh", but we may
|
||||
# know the specific version we want to use. This variable (TCLSH_CMD) may be
|
||||
# overridden via the environment prior to running nmake in order to select a
|
||||
@@ -136,7 +157,9 @@ TCC = $(TCC) -DSQLITE_THREAD_OVERRIDE_LOCK=-1
|
||||
|
||||
# Any target libraries which libsqlite must be linked against
|
||||
#
|
||||
!if "$(TLIBS)" == ""
|
||||
TLIBS =
|
||||
!endif
|
||||
|
||||
# Flags controlling use of the in memory btree implementation
|
||||
#
|
||||
@@ -179,6 +202,13 @@ TCC = $(TCC) -O2
|
||||
TCC = $(TCC) -Zi
|
||||
!ENDIF
|
||||
|
||||
# If ICU support is enabled, add the compiler options for it.
|
||||
!IF $(USE_ICU)!=0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_ICU=1
|
||||
TCC = $(TCC) -I$(TOP)\ext\icu
|
||||
TCC = $(TCC) -I$(ICUINCDIR)
|
||||
!ENDIF
|
||||
|
||||
# libtool compile/link
|
||||
LTCOMPILE = $(TCC) -Fo$@
|
||||
LTLIB = lib.exe
|
||||
@@ -198,6 +228,16 @@ LTLIBOPTS = /MACHINE:$(PLATFORM)
|
||||
LTLINKOPTS = $(LTLINKOPTS) /DEBUG
|
||||
!ENDIF
|
||||
|
||||
# Start with the Tcl related linker options.
|
||||
LTLIBPATHS = /LIBPATH:$(TCLLIBDIR)
|
||||
LTLIBS = $(LIBTCL)
|
||||
|
||||
# If ICU support is enabled, add the linker options for it.
|
||||
!IF $(USE_ICU)!=0
|
||||
LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ICULIBDIR)
|
||||
LTLIBS = $(LTLIBS) $(LIBICU)
|
||||
!ENDIF
|
||||
|
||||
# nawk compatible awk.
|
||||
NAWK = gawk.exe
|
||||
|
||||
@@ -528,12 +568,12 @@ libsqlite3.lib: $(LIBOBJ)
|
||||
$(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS)
|
||||
|
||||
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
|
||||
$(LTLIB) $(LTLIBOPTS) /LIBPATH:$(TCLLIBDIR) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
|
||||
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
|
||||
|
||||
sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib sqlite3.h
|
||||
$(LTLINK) $(READLINE_FLAGS) \
|
||||
$(TOP)\src\shell.c \
|
||||
/link $(LTLINKOPTS) libsqlite3.lib $(LIBREADLINE) $(TLIBS)
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
# This target creates a directory named "tsrc" and fills it with
|
||||
# copies of all of the C source code and header files needed to
|
||||
@@ -804,7 +844,7 @@ tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR)
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo libsqlite3.lib
|
||||
$(LTLINK) tclsqlite-shell.lo \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) libsqlite3.lib $(LIBTCL)
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LTLIBS) $(TLIBS)
|
||||
|
||||
# Rules to build opcodes.c and opcodes.h
|
||||
#
|
||||
@@ -915,7 +955,7 @@ testfixture.exe: $(TESTFIXTURE_SRC) $(HDR)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) $(LIBTCL) $(TLIBS)
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
fulltest: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\all.test
|
||||
@@ -926,16 +966,16 @@ soaktest: testfixture.exe sqlite3.exe
|
||||
test: testfixture.exe sqlite3.exe
|
||||
.\testfixture.exe $(TOP)\test\veryquick.test
|
||||
|
||||
spaceanal_tcl.h: $(TOP)\tool\spaceanal.tcl
|
||||
$(NAWK) -f $(TOP)/tool/tostr.awk \
|
||||
$(TOP)\tool\spaceanal.tcl > spaceanal_tcl.h
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
|
||||
copy sqlite3.c + $(TOP)\src\test_stat.c + $(TOP)\src\tclsqlite.c $@
|
||||
echo static const char *tclsh_main_loop(void){ >> $@
|
||||
echo static const char *zMainloop = >> $@
|
||||
$(NAWK) -f $(TOP)\tool\tostr.awk $(TOP)\tool\spaceanal.tcl >> $@
|
||||
echo ; return zMainloop; } >> $@
|
||||
|
||||
sqlite3_analyzer.exe: $(TESTFIXTURE_SRC) spaceanal_tcl.h
|
||||
$(LTLINK) -DTCLSH=2 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 \
|
||||
-DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LTLINKOPTS) /LIBPATH:$(TCLLIBDIR) $(LIBTCL) $(TLIBS)
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c
|
||||
$(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
clean:
|
||||
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
|
||||
@@ -944,10 +984,11 @@ clean:
|
||||
del /Q mkkeywordhash.exe keywordhash.h
|
||||
-rmdir /Q/S tsrc
|
||||
del /Q .target_source
|
||||
del /Q tclsqlite3.exe
|
||||
del /Q testfixture.exe testfixture.exp test.db
|
||||
del /Q sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
del /Q sqlite3.c
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp spaceanal_tcl.h
|
||||
del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp sqlite3_analyzer.c
|
||||
|
||||
#
|
||||
# Windows section
|
||||
@@ -961,4 +1002,4 @@ sqlite3.def: libsqlite3.lib
|
||||
| sort >> sqlite3.def
|
||||
|
||||
sqlite3.dll: $(LIBOBJ) sqlite3.def
|
||||
link $(LTLINKOPTS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ)
|
||||
link $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ) $(LTLIBS) $(TLIBS)
|
||||
|
27
main.mk
27
main.mk
@@ -521,6 +521,16 @@ tclsqlite3: $(TOP)/src/tclsqlite.c libsqlite3.a
|
||||
$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
|
||||
$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
|
||||
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl
|
||||
echo "#define TCLSH 2" > $@
|
||||
cat sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c >> $@
|
||||
echo "static const char *tclsh_main_loop(void){" >> $@
|
||||
echo "static const char *zMainloop = " >> $@
|
||||
$(NAWK) -f $(TOP)/tool/tostr.awk $(TOP)/tool/spaceanal.tcl >> $@
|
||||
echo "; return zMainloop; }" >> $@
|
||||
|
||||
sqlite3_analyzer$(EXE): sqlite3_analyzer.c
|
||||
$(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB)
|
||||
|
||||
# Rules to build the 'testfixture' application.
|
||||
#
|
||||
@@ -563,16 +573,6 @@ threadtest3$(EXE): sqlite3.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoin
|
||||
threadtest: threadtest3$(EXE)
|
||||
./threadtest3$(EXE)
|
||||
|
||||
sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \
|
||||
$(TOP)/tool/spaceanal.tcl
|
||||
$(NAWK) -f $(TOP)/tool/tostr.awk $(TOP)/tool/spaceanal.tcl \
|
||||
>spaceanal_tcl.h
|
||||
$(TCCX) $(TCL_FLAGS) -DTCLSH=2 $(TESTFIXTURE_FLAGS) \
|
||||
-DSQLITE_TEST=1 -DSQLITE_PRIVATE="" \
|
||||
$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c \
|
||||
-o sqlite3_analyzer$(EXE) \
|
||||
$(LIBTCL) $(THREADLIB)
|
||||
|
||||
TEST_EXTENSION = $(SHPREFIX)testloadext.$(SO)
|
||||
$(TEST_EXTENSION): $(TOP)/src/test_loadext.c
|
||||
$(MKSHLIB) $(TOP)/src/test_loadext.c -o $(TEST_EXTENSION)
|
||||
@@ -596,10 +596,15 @@ install: sqlite3 libsqlite3.a sqlite3.h
|
||||
mv sqlite3.h /usr/include
|
||||
|
||||
clean:
|
||||
rm -f *.o sqlite3 libsqlite3.a sqlite3.h opcodes.*
|
||||
rm -f *.o sqlite3 sqlite3.exe libsqlite3.a sqlite3.h opcodes.*
|
||||
rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf tsrc target_source
|
||||
rm -f testloadext.dll libtestloadext.so
|
||||
rm -f amalgamation-testfixture amalgamation-testfixture.exe
|
||||
rm -f fts3-testfixture fts3-testfixture.exe
|
||||
rm -f testfixture testfixture.exe
|
||||
rm -f threadtest3 threadtest3.exe
|
||||
rm -f sqlite3.c fts?amal.c tclsqlite3.c
|
||||
rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c
|
||||
|
141
manifest
141
manifest
@@ -1,12 +1,12 @@
|
||||
C Merge\sin\sall\strunk\schanges\sthrough\sthe\sversion\s3.7.8\srelease.
|
||||
D 2011-09-19T20:28:07.777
|
||||
C Merge\sall\sthe\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch\s-\sespecially\nthe\sSQLITE_ENABLE_STAT3\senhancements.
|
||||
D 2011-10-11T12:58:38.127
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in d314143fa6be24828021d3f583ad37d9afdce505
|
||||
F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc b5e917439d5ed42364173d1648aae1d418e323ea
|
||||
F Makefile.msc dcad80fa69f17d46fe6778ba873fc108ca16298d
|
||||
F Makefile.vxworks 1deb39c8bb047296c30161ffa10c1b5423e632f9
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION f724de7326e87b7f3b0a55f16ef4b4d993680d54
|
||||
F VERSION bb37c274b503bbe73f00ea4f374eb817cba4b171
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
|
||||
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
|
||||
@@ -23,7 +23,7 @@ F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 405a958bdb3af382a809dccb08a44694923ddd61
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 4010a2e577b7d43c429672858d442ac5a570fdcd x
|
||||
F configure 806c06aef5895860da49600ce098dbe4d5a8435c x
|
||||
F configure.ac 298a759c086e72c013da459c2aec02a104f4224f
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html 3091574143dd3415669b6745843ff8d011d33549
|
||||
@@ -116,7 +116,7 @@ F ext/session/sqlite3session.h f374c9c4c96e08f67ac418871c29d423245c7673
|
||||
F ext/session/test_session.c ea4dc9b4a1895c8e6bddcbfe3838d7eb57df2d99
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk ba82d040a497dc525b96103d007da17ce5baad6d
|
||||
F main.mk 57f16530780b158e99c4983debabeb50b2331a57
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@@ -130,19 +130,19 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
|
||||
F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a
|
||||
F src/analyze.c 682fd999a01c897a682365a459190758b83de836
|
||||
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 28a4fe55327ff708bfaf9d4326d02686f7a553c3
|
||||
F src/backup.c 4fd4440c8f81339d8eb8e5d2df54b68d79e94f2f
|
||||
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 77b09c69d4849a90361e6fe5db36d167f20600c0
|
||||
F src/btree.c b53e009bccb4cfcbcde074f586f0c1c6712a0e12
|
||||
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce
|
||||
F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3
|
||||
F src/build.c 851e81f26a75abbb98bd99a7c5f10e8670d867bb
|
||||
F src/build.c 119937b0ae1ff4dcec8fdea53771acc95bafca51
|
||||
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c e3132ec65240b2e2f3d50831021eac387f27584d
|
||||
F src/ctime.c 829f3261d3db48e3d87891bc887208734734c2e4
|
||||
F src/date.c a3c6842bad7ae632281811de112a8ba63ff08ab3
|
||||
F src/delete.c 614d6e012aa5b624e78f3b556243497825de196b
|
||||
F src/expr.c f4dcaeb8252c4b16fcdc245660f70ed366bc6cdd
|
||||
@@ -153,7 +153,7 @@ F src/global.c e230227de13601714b29f9363028514aada5ae2f
|
||||
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c 1f1688a9da8b8e27114ba1909a6e74100791139b
|
||||
F src/insert.c 25b1bdb27db651ccc9cef37aa691be65d6be9cda
|
||||
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
|
||||
@@ -177,29 +177,29 @@ F src/os.c 3b3f69c34be7f998f5ea6bd46a2fe8a2b7fa8f70
|
||||
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
|
||||
F src/os_unix.c 10e0c4dcdbec8d4189890fdf3e71b32efae194e3
|
||||
F src/os_win.c 0fc0f46c94b0385a940b0ee32992a833019a5985
|
||||
F src/pager.c 15d10371e2d560b68870a9ccec022ad8f01e70a2
|
||||
F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1
|
||||
F src/os_unix.c 9da63854b702e0855ce13711a80d8bdcc5b69549
|
||||
F src/os_win.c fbe47c7fdc9a846a772bbf98719c328becad5f8a
|
||||
F src/pager.c 8a6ac3e0d9694412076e2273e3c81e9c4e08758f
|
||||
F src/pager.h dbcaa791e8b6c3a6b77c168c5c27deec289fb176
|
||||
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
|
||||
F src/pcache.c 49e718c095810c6b3334e3a6d89970aceaddefce
|
||||
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
|
||||
F src/pcache1.c c8982f7048a70b7fd37975a8f6c84d6bc294175a
|
||||
F src/pragma.c ebcd20f1e654f5cb3aeef864ed69c4697719fbaa
|
||||
F src/pcache1.c 24f5e85a78514584b46190260ba7ab0a66312197
|
||||
F src/pragma.c 68d7db4fc9de8bcfae94c1d43120531ec252b9c0
|
||||
F src/prepare.c e64261559a3187698a3e7e6c8b001a4f4f98dab4
|
||||
F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 36368f44569208fa074e61f4dd0b6c4fb60ca2b4
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c d9b7d20b0365f80761846f00ef3638d4b33eeaf2
|
||||
F src/shell.c 13fe2aeddc3cc90d6a273831d1f63736d1596f81
|
||||
F src/sqlite.h.in 355493ac9492746a0bbd17a4fd40911aa89b5a3a
|
||||
F src/shell.c e8fe1251aee84baa2fb232ce83d938de25aa650f
|
||||
F src/sqlite.h.in 1d5116f51910cc59187b100557feacd8341b633d
|
||||
F src/sqlite3ext.h 1a1a4f784aa9c3b00edd287940197de52487cd93
|
||||
F src/sqliteInt.h 2c05edf4b8155bf4ef0775a9c30e4b57d7527af3
|
||||
F src/sqliteInt.h 0d9d61edf5be116d34f6f980cb0db70375e152ad
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
|
||||
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c be0e691e223a907aa032c7b3ed310dc0ff048109
|
||||
F src/tclsqlite.c 8adc2f2bc8a155f2a340242df6d82112a629ed7c
|
||||
F src/test1.c 0f41b7c67719207a5de24b009e172c4dcf189827
|
||||
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
|
||||
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
|
||||
@@ -213,7 +213,7 @@ F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
|
||||
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
|
||||
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
|
||||
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
|
||||
F src/test_config.c fc48c199ad4b88c99bf7c727cae407ca53145c3d
|
||||
F src/test_config.c b36ab29f3d9ed4453c989717ef99b1f3c52f443e
|
||||
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
|
||||
@@ -224,7 +224,7 @@ F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
|
||||
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
|
||||
F src/test_journal.c 03313c693cca72959dcaaf79f8d76f21c01e19ff
|
||||
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
|
||||
F src/test_malloc.c 91d5cf1751d3e563754fd183da1c020727b5480e
|
||||
F src/test_malloc.c 8d416f29ad8573f32601f6056c9d2b17472e9ad5
|
||||
F src/test_multiplex.c 3fc368022c46fe44ec22c5e1ed727223a54a6a1d
|
||||
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
|
||||
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
|
||||
@@ -235,7 +235,7 @@ F src/test_quota.c a391c866217e92986c6f523f05b08aa6956c8419
|
||||
F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
|
||||
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
|
||||
F src/test_stat.c 69de4361c7a69fc1136d31ab7144408cd00805c7
|
||||
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
|
||||
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
|
||||
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
|
||||
@@ -247,39 +247,40 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
|
||||
F src/trigger.c 1cfb80e2290ef66ea89cb4e821caae65a02c0d56
|
||||
F src/update.c 2d67e24d5a44d8b1c0839bf2ee0c391593e852bf
|
||||
F src/utf.c c53eb7404b3eb5c1cbb5655c6a7a0e0ce6bd50f0
|
||||
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
|
||||
F src/util.c 06302ffd2b80408d4f6c7af71f7090e0cf8d8ff7
|
||||
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
|
||||
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
|
||||
F src/vdbe.c a4d43779fd31b00cc7c6e1717e4b6cfacf499e66
|
||||
F src/vdbe.h 226d4bc726b3597c35be155a4342db290601d67c
|
||||
F src/vdbeInt.h 1400515b37a4863cdda4601abc0f76eca846c9f5
|
||||
F src/vdbeapi.c c969d467817ca90f99f3d3b46d115fbec08aeb4c
|
||||
F src/vdbeaux.c 61701754a1528451ddb6e55e9d353e35ea940492
|
||||
F src/vdbeaux.c 2339c8c4d88aba3709f810e8aab7731eecae7aed
|
||||
F src/vdbeblob.c 11248c6362389569764682eb0f59ce910f3cc381
|
||||
F src/vdbemem.c 5e6effb96dd53d233361cbfaa3f0a43b9af689e9
|
||||
F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
|
||||
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
|
||||
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
|
||||
F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582
|
||||
F src/wal.c 3154756177d6219e233d84291d5b05f4e06ff5e9
|
||||
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c b641d399cfd8588d0e20d9790d1582b663a732a8
|
||||
F src/where.c 12939ac49f5122eb11b5ca4c35b2fdd8eaae9833
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 52fc8dee494092031a556911d404ca30a749a30b
|
||||
F test/alter.test 5314fc01ef51ab8af0b8890725b710ed48d4806b
|
||||
F test/alter.test 54912d932309df2e4f62aeb47169c2ff740e53ed
|
||||
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
|
||||
F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
|
||||
F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5
|
||||
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
|
||||
F test/analyze.test 68b43c1f9cd6ffc3bbb30d27a23712b38c413eca
|
||||
F test/analyze.test f8ab7d15858b4093b06caf5e57e2a5ff7104bdae
|
||||
F test/analyze2.test 8f2b1534d43f5547ce9a6b736c021d4192c75be3
|
||||
F test/analyze3.test d61f55d8b472fc6e713160b1e577f7a68e63f38b
|
||||
F test/analyze3.test 9be0af5e23b711559e8f78c42a6c04de956cba9b
|
||||
F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
|
||||
F test/analyze5.test 1de8d66b11aae5a1453aa042d62e834a476bac9c
|
||||
F test/analyze6.test c125622a813325bba1b4999040ddc213773c2290
|
||||
F test/analyze7.test 5508e7828164ea0b518ed219bed7320a481863d4
|
||||
F test/analyze5.test 713354664c5ff1853ab2cbcb740f0cf5cb7c802e
|
||||
F test/analyze6.test bd3625806a5ee6f7bef72d06295bd319f0290af2
|
||||
F test/analyze7.test d3587aa5af75c9048d031b94fceca2534fa75d1d
|
||||
F test/analyze8.test 4ca170de2ba30ccb1af2c0406803db72262f9691
|
||||
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
|
||||
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
|
||||
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
|
||||
@@ -288,9 +289,9 @@ F test/async5.test 0dd8701bd588bf6e70c2557a22ae3f22b2567b4c
|
||||
F test/attach.test 0e6f8de2589f11a5f474ef57fe5af2877e61c0e8
|
||||
F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966
|
||||
F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e
|
||||
F test/attach4.test 31f9eb0ca7bdbc393cc4657b877903a226a83d4b
|
||||
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
|
||||
F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0
|
||||
F test/auth.test b047105c32da7db70b842fd24056723125ecc2ff
|
||||
F test/auth.test ac996c81ad910148606f5c7e3b3f85d47c29960f
|
||||
F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882
|
||||
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
|
||||
F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf
|
||||
@@ -346,7 +347,7 @@ F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
|
||||
F test/conflict.test cabc41f7616675df71b4fddabca3bd5d9221915a
|
||||
F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e
|
||||
F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0
|
||||
F test/corrupt3.test e3006aaf579d2ed7f1b94bf4cc695d3c784fa5af
|
||||
F test/corrupt3.test 889d7cdb811800303aa722d7813fe8a4299cf726
|
||||
F test/corrupt4.test b963f9e01e0f92d15c76fb0747876fd4b96dc30a
|
||||
F test/corrupt5.test c23da7bfb20917cc7fdbb13ee25c7cc4e9fffeff
|
||||
F test/corrupt6.test 4e4161aef1f30b9f34582bb4142334b7f47eacae
|
||||
@@ -357,7 +358,7 @@ F test/corruptA.test fafa652aa585753be4f6b62ff0bb250266eaf7ce
|
||||
F test/corruptB.test 20d4a20cbed23958888c3e8995b424a47223d647
|
||||
F test/corruptC.test 62a767fe64acb1975f58cc6171192839c783edbb
|
||||
F test/corruptD.test 99b1999dbfa7cc04aaeac9d695a2445d4e7c7458
|
||||
F test/corruptE.test 78f7e1b9fd4a92e5951c7a5e414f2c4492733870
|
||||
F test/corruptE.test 1b9eb20a8711251ce57b44a257e241085b39b52d
|
||||
F test/count.test 454e1ce985c94d13efeac405ce54439f49336163
|
||||
F test/crash.test 519dc29f6fea151f015a23236e555239353946eb
|
||||
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
|
||||
@@ -370,9 +371,10 @@ F test/crash8.test 38767cb504bbe491de6be4a7006b154973a2309f
|
||||
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
|
||||
F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
|
||||
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/ctime.test 7f0bd5084d9dd7da9ad46901810896edd2ebb463
|
||||
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
|
||||
F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff
|
||||
F test/dbstatus.test a719af0f226bd280748a4bb9054c0a5a9fc1b16c
|
||||
F test/dbstatus.test 9eb484ba837c6f3f9bbcaecc29e6060a8c3ba6d2
|
||||
F test/dbstatus2.test dc57b0d9610851c0ff58a8e1b5b191678398b72a
|
||||
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
@@ -575,7 +577,7 @@ F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
|
||||
F test/malloc.test 8c727fe29fccd280cbf8f6acf08bd10b76beaf34
|
||||
F test/malloc3.test de8eca0c3e748878845fdca3663ec4b642073caf
|
||||
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
|
||||
F test/malloc5.test 338e0f7df5fde4a0129c8002a915410e1080bfb4
|
||||
F test/malloc5.test 30dc30b57fa22552eba0d8c95210d96c3d958a39
|
||||
F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
|
||||
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
|
||||
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
|
||||
@@ -595,16 +597,16 @@ F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
|
||||
F test/malloc_common.tcl 2930895b0962823ec679853e67e58dd6d8198b3c
|
||||
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
|
||||
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
|
||||
F test/memdb.test 4b5d2671588ed59cb08642adc67fd78c666dc9c2
|
||||
F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6
|
||||
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
||||
F test/memsubsys1.test 39f1ddddf76ce51a3232aab0279668e23cf00f83
|
||||
F test/memsubsys1.test 16ce163ac1ace3d71bf0eaa6a821ed153addd91f
|
||||
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||
F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
|
||||
F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/misc1.test 55cb2bfbf4a8cd61f4be1effc30426ad41696bff
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test 8e42d54b772a23b3c573672d3e0894d15b05221d
|
||||
F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738
|
||||
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
|
||||
F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
@@ -620,17 +622,17 @@ F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a
|
||||
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
|
||||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
F test/oserror.test 3fe52e0bd2891a9bf7cdeb639554992453d46301
|
||||
F test/pager1.test 2d3a7c6facd899d8879d23f31454cc53f49358b9
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 1b630b3248c7d28862fe9e190cfe52234b502504
|
||||
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
|
||||
F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401
|
||||
F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7
|
||||
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
|
||||
F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
|
||||
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
|
||||
F test/pcache2.test bc67c6802989dba05cdf3a4574fd882e238c7ecf
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/permutations.test 97d576e6384f1779e4428516a48458ce8cf7dfd1
|
||||
F test/pragma.test c8108e01da04f16e67e5754e610bc62c1b993f6c
|
||||
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
|
||||
@@ -697,17 +699,17 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
|
||||
F test/stat.test 0997f6a57a35866b14111ed361ed8851ce7978ae
|
||||
F test/stat.test 36bc951bdc2beac4224cc54396fd6a7dc65336f4
|
||||
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
|
||||
F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
|
||||
F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd
|
||||
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
|
||||
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
|
||||
F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082
|
||||
F test/superlock.test 7b1167925e9d30a5d1f0701d24812fdda42c3a86
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test 966addf703faee6a5d509abe6d8885e393e552fd
|
||||
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
|
||||
F test/table.test 50c47f5fe9c112e92723af27cd735e6c92de6f85
|
||||
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tclsqlite.test d5298750115768bca173da8043ffac2924be21a0
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
@@ -751,7 +753,8 @@ F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78
|
||||
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
|
||||
F test/tkt-b72787b1.test e6b62b2b2785c04d0d698d6a603507e384165049
|
||||
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
|
||||
F test/tkt-cbd054fa6b.test f14f97ea43662e6f70c9e63287081e8be5d9d589
|
||||
F test/tkt-c48d99d690.test bed446e3513ae10eec1b86fdd186ef750226c408
|
||||
F test/tkt-cbd054fa6b.test bd9fb546f63bc0c79d1776978d059fa51c5b1c63
|
||||
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
|
||||
F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
|
||||
F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6
|
||||
@@ -868,11 +871,11 @@ F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
|
||||
F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e
|
||||
F test/unordered.test e81169ce2a8f31b2c6b66af691887e1376ab3ced
|
||||
F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb
|
||||
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
|
||||
F test/uri.test 53de9a2549cbda9c343223236918ef502f6a9051
|
||||
F test/uri.test 0d289d32396bdbc491e9dc845f1a52e13f861e0b
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
F test/vacuum.test 9516f3a8e49be666f2dde28561e4be5ae5612de0
|
||||
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324
|
||||
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
|
||||
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
@@ -906,7 +909,7 @@ F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
|
||||
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
|
||||
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
|
||||
F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877
|
||||
F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0
|
||||
F test/walbig.test 0ab8a430ef420a3114f7092e0f30fc9585ffa155
|
||||
F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde
|
||||
F test/walcrash.test 4fcb661faf71db91214156d52d43ee327f52bde1
|
||||
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
|
||||
@@ -914,8 +917,8 @@ F test/walfault.test efb0d5724893133e71b8d9d90abdb781845a6bb0
|
||||
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
|
||||
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
|
||||
F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
|
||||
F test/walpersist.test 45fb0c94fb63908e2d66b1d99ce4645bfce0fa1e
|
||||
F test/walro.test 2d5d69e2e99da19ce6faab340330234fc4ca0720
|
||||
F test/walpersist.test fd40d33765b2693f721c90c66d97f99757559006
|
||||
F test/walro.test 412d0809300b94ba142440e94d6a30eabf2220b7
|
||||
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
|
||||
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
|
||||
F test/walthread.test a2ed5270eb695284d4ad27d252517bdc3317ee2a
|
||||
@@ -925,10 +928,10 @@ F test/where3.test 8e1175c7ef710c70502858fc4fb08d784b3620b9
|
||||
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
|
||||
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
|
||||
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
F test/where7.test aa4cfcd6f66e2a4ef87b6717327325bf4d547502
|
||||
F test/where7.test 814d7373413398e074f134cff5f8872e2c08bd3b
|
||||
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
|
||||
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
|
||||
F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848
|
||||
F test/where9.test bed66dcfc69a54a99661c0c9906189cb5e58f4e2
|
||||
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
@@ -965,7 +968,7 @@ F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
|
||||
F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
|
||||
F tool/spaceanal.tcl fe02dede3d29ef0fefcf80a685644fdf4d9a768e
|
||||
F tool/spaceanal.tcl 15f6cd939b4ecc14d061de7e8ace89e26c30c40b
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -975,8 +978,8 @@ F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
|
||||
F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5
|
||||
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
F tool/warnings.sh b7fdb2cc525f5ef4fa43c80e771636dd3690f9d2
|
||||
P 5efb02949dbeabccfe1d848d275529f03f3dfc84 3e0da808d2f5b4d12046e05980ca04578f581177
|
||||
R 29a8a873fd8274eaac6f4c279a0f93a3
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
P 98619a23fd11a5eb319687adfdacc91ff2db896c 9325c1a8c413dfbf0381190d8347f0a446ae5f5b
|
||||
R d253d1168c1ea36796b51c0ccbb3736e
|
||||
U drh
|
||||
Z cf555a868961f047f28c626087f81d1a
|
||||
Z eba0cc6baac730dcbeed3fe014c48887
|
||||
|
@@ -1 +1 @@
|
||||
98619a23fd11a5eb319687adfdacc91ff2db896c
|
||||
403431cac6b039b0693915c5422f08dc60dae230
|
753
src/analyze.c
753
src/analyze.c
@@ -10,6 +10,108 @@
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains code associated with the ANALYZE command.
|
||||
**
|
||||
** The ANALYZE command gather statistics about the content of tables
|
||||
** and indices. These statistics are made available to the query planner
|
||||
** to help it make better decisions about how to perform queries.
|
||||
**
|
||||
** The following system tables are or have been supported:
|
||||
**
|
||||
** CREATE TABLE sqlite_stat1(tbl, idx, stat);
|
||||
** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample);
|
||||
** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample);
|
||||
**
|
||||
** Additional tables might be added in future releases of SQLite.
|
||||
** The sqlite_stat2 table is not created or used unless the SQLite version
|
||||
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
|
||||
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
|
||||
** The sqlite_stat2 table is superceded by sqlite_stat3, which is only
|
||||
** created and used by SQLite versions 3.7.9 and later and with
|
||||
** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3
|
||||
** is a superset of sqlite_stat2.
|
||||
**
|
||||
** Format of sqlite_stat1:
|
||||
**
|
||||
** There is normally one row per index, with the index identified by the
|
||||
** name in the idx column. The tbl column is the name of the table to
|
||||
** which the index belongs. In each such row, the stat column will be
|
||||
** a string consisting of a list of integers. The first integer in this
|
||||
** list is the number of rows in the index and in the table. The second
|
||||
** integer is the average number of rows in the index that have the same
|
||||
** value in the first column of the index. The third integer is the average
|
||||
** number of rows in the index that have the same value for the first two
|
||||
** columns. The N-th integer (for N>1) is the average number of rows in
|
||||
** the index which have the same value for the first N-1 columns. For
|
||||
** a K-column index, there will be K+1 integers in the stat column. If
|
||||
** the index is unique, then the last integer will be 1.
|
||||
**
|
||||
** The list of integers in the stat column can optionally be followed
|
||||
** by the keyword "unordered". The "unordered" keyword, if it is present,
|
||||
** must be separated from the last integer by a single space. If the
|
||||
** "unordered" keyword is present, then the query planner assumes that
|
||||
** the index is unordered and will not use the index for a range query.
|
||||
**
|
||||
** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat
|
||||
** column contains a single integer which is the (estimated) number of
|
||||
** rows in the table identified by sqlite_stat1.tbl.
|
||||
**
|
||||
** Format of sqlite_stat2:
|
||||
**
|
||||
** The sqlite_stat2 is only created and is only used if SQLite is compiled
|
||||
** with SQLITE_ENABLE_STAT2 and if the SQLite version number is between
|
||||
** 3.6.18 and 3.7.8. The "stat2" table contains additional information
|
||||
** about the distribution of keys within an index. The index is identified by
|
||||
** the "idx" column and the "tbl" column is the name of the table to which
|
||||
** the index belongs. There are usually 10 rows in the sqlite_stat2
|
||||
** table for each index.
|
||||
**
|
||||
** The sqlite_stat2 entries for an index that have sampleno between 0 and 9
|
||||
** inclusive are samples of the left-most key value in the index taken at
|
||||
** evenly spaced points along the index. Let the number of samples be S
|
||||
** (10 in the standard build) and let C be the number of rows in the index.
|
||||
** Then the sampled rows are given by:
|
||||
**
|
||||
** rownumber = (i*C*2 + C)/(S*2)
|
||||
**
|
||||
** For i between 0 and S-1. Conceptually, the index space is divided into
|
||||
** S uniform buckets and the samples are the middle row from each bucket.
|
||||
**
|
||||
** The format for sqlite_stat2 is recorded here for legacy reference. This
|
||||
** version of SQLite does not support sqlite_stat2. It neither reads nor
|
||||
** writes the sqlite_stat2 table. This version of SQLite only supports
|
||||
** sqlite_stat3.
|
||||
**
|
||||
** Format for sqlite_stat3:
|
||||
**
|
||||
** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is
|
||||
** used to avoid compatibility problems.
|
||||
**
|
||||
** The format of the sqlite_stat3 table is similar to the format of
|
||||
** the sqlite_stat2 table. There are multiple entries for each index.
|
||||
** The idx column names the index and the tbl column is the table of the
|
||||
** index. If the idx and tbl columns are the same, then the sample is
|
||||
** of the INTEGER PRIMARY KEY. The sample column is a value taken from
|
||||
** the left-most column of the index. The nEq column is the approximate
|
||||
** number of entires in the index whose left-most column exactly matches
|
||||
** the sample. nLt is the approximate number of entires whose left-most
|
||||
** column is less than the sample. The nDLt column is the approximate
|
||||
** number of distinct left-most entries in the index that are less than
|
||||
** the sample.
|
||||
**
|
||||
** Future versions of SQLite might change to store a string containing
|
||||
** multiple integers values in the nDLt column of sqlite_stat3. The first
|
||||
** integer will be the number of prior index entires that are distinct in
|
||||
** the left-most column. The second integer will be the number of prior index
|
||||
** entries that are distinct in the first two columns. The third integer
|
||||
** will be the number of prior index entries that are distinct in the first
|
||||
** three columns. And so forth. With that extension, the nDLt field is
|
||||
** similar in function to the sqlite_stat1.stat field.
|
||||
**
|
||||
** There can be an arbitrary number of sqlite_stat3 entries per index.
|
||||
** The ANALYZE command will typically generate sqlite_stat3 tables
|
||||
** that contain between 10 and 40 samples which are distributed across
|
||||
** the key space, though not uniformly, and which include samples with
|
||||
** largest possible nEq values.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
#include "sqliteInt.h"
|
||||
@@ -42,8 +144,8 @@ static void openStatTable(
|
||||
const char *zCols;
|
||||
} aTable[] = {
|
||||
{ "sqlite_stat1", "tbl,idx,stat" },
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
{ "sqlite_stat2", "tbl,idx,sampleno,sample" },
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
{ "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -59,6 +161,9 @@ static void openStatTable(
|
||||
assert( sqlite3VdbeDb(v)==db );
|
||||
pDb = &db->aDb[iDb];
|
||||
|
||||
/* Create new statistic tables if they do not exist, or clear them
|
||||
** if they do already exist.
|
||||
*/
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
const char *zTab = aTable[i].zName;
|
||||
Table *pStat;
|
||||
@@ -89,7 +194,7 @@ static void openStatTable(
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the sqlite_stat[12] tables for writing. */
|
||||
/* Open the sqlite_stat[13] tables for writing. */
|
||||
for(i=0; i<ArraySize(aTable); i++){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
|
||||
@@ -97,6 +202,226 @@ static void openStatTable(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Recommended number of samples for sqlite_stat3
|
||||
*/
|
||||
#ifndef SQLITE_STAT3_SAMPLES
|
||||
# define SQLITE_STAT3_SAMPLES 24
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Three SQL functions - stat3_init(), stat3_push(), and stat3_pop() -
|
||||
** share an instance of the following structure to hold their state
|
||||
** information.
|
||||
*/
|
||||
typedef struct Stat3Accum Stat3Accum;
|
||||
struct Stat3Accum {
|
||||
tRowcnt nRow; /* Number of rows in the entire table */
|
||||
tRowcnt nPSample; /* How often to do a periodic sample */
|
||||
int iMin; /* Index of entry with minimum nEq and hash */
|
||||
int mxSample; /* Maximum number of samples to accumulate */
|
||||
int nSample; /* Current number of samples */
|
||||
u32 iPrn; /* Pseudo-random number used for sampling */
|
||||
struct Stat3Sample {
|
||||
i64 iRowid; /* Rowid in main table of the key */
|
||||
tRowcnt nEq; /* sqlite_stat3.nEq */
|
||||
tRowcnt nLt; /* sqlite_stat3.nLt */
|
||||
tRowcnt nDLt; /* sqlite_stat3.nDLt */
|
||||
u8 isPSample; /* True if a periodic sample */
|
||||
u32 iHash; /* Tiebreaker hash */
|
||||
} *a; /* An array of samples */
|
||||
};
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Implementation of the stat3_init(C,S) SQL function. The two parameters
|
||||
** are the number of rows in the table or index (C) and the number of samples
|
||||
** to accumulate (S).
|
||||
**
|
||||
** This routine allocates the Stat3Accum object.
|
||||
**
|
||||
** The return value is the Stat3Accum object (P).
|
||||
*/
|
||||
static void stat3Init(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat3Accum *p;
|
||||
tRowcnt nRow;
|
||||
int mxSample;
|
||||
int n;
|
||||
|
||||
UNUSED_PARAMETER(argc);
|
||||
nRow = (tRowcnt)sqlite3_value_int64(argv[0]);
|
||||
mxSample = sqlite3_value_int(argv[1]);
|
||||
n = sizeof(*p) + sizeof(p->a[0])*mxSample;
|
||||
p = sqlite3_malloc( n );
|
||||
if( p==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
memset(p, 0, n);
|
||||
p->a = (struct Stat3Sample*)&p[1];
|
||||
p->nRow = nRow;
|
||||
p->mxSample = mxSample;
|
||||
p->nPSample = p->nRow/(mxSample/3+1) + 1;
|
||||
sqlite3_randomness(sizeof(p->iPrn), &p->iPrn);
|
||||
sqlite3_result_blob(context, p, sizeof(p), sqlite3_free);
|
||||
}
|
||||
static const FuncDef stat3InitFuncdef = {
|
||||
2, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
stat3Init, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"stat3_init", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The
|
||||
** arguments describe a single key instance. This routine makes the
|
||||
** decision about whether or not to retain this key for the sqlite_stat3
|
||||
** table.
|
||||
**
|
||||
** The return value is NULL.
|
||||
*/
|
||||
static void stat3Push(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]);
|
||||
tRowcnt nEq = sqlite3_value_int64(argv[0]);
|
||||
tRowcnt nLt = sqlite3_value_int64(argv[1]);
|
||||
tRowcnt nDLt = sqlite3_value_int64(argv[2]);
|
||||
i64 rowid = sqlite3_value_int64(argv[3]);
|
||||
u8 isPSample = 0;
|
||||
u8 doInsert = 0;
|
||||
int iMin = p->iMin;
|
||||
struct Stat3Sample *pSample;
|
||||
int i;
|
||||
u32 h;
|
||||
|
||||
UNUSED_PARAMETER(context);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( nEq==0 ) return;
|
||||
h = p->iPrn = p->iPrn*1103515245 + 12345;
|
||||
if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){
|
||||
doInsert = isPSample = 1;
|
||||
}else if( p->nSample<p->mxSample ){
|
||||
doInsert = 1;
|
||||
}else{
|
||||
if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){
|
||||
doInsert = 1;
|
||||
}
|
||||
}
|
||||
if( !doInsert ) return;
|
||||
if( p->nSample==p->mxSample ){
|
||||
assert( p->nSample - iMin - 1 >= 0 );
|
||||
memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1));
|
||||
pSample = &p->a[p->nSample-1];
|
||||
}else{
|
||||
pSample = &p->a[p->nSample++];
|
||||
}
|
||||
pSample->iRowid = rowid;
|
||||
pSample->nEq = nEq;
|
||||
pSample->nLt = nLt;
|
||||
pSample->nDLt = nDLt;
|
||||
pSample->iHash = h;
|
||||
pSample->isPSample = isPSample;
|
||||
|
||||
/* Find the new minimum */
|
||||
if( p->nSample==p->mxSample ){
|
||||
pSample = p->a;
|
||||
i = 0;
|
||||
while( pSample->isPSample ){
|
||||
i++;
|
||||
pSample++;
|
||||
assert( i<p->nSample );
|
||||
}
|
||||
nEq = pSample->nEq;
|
||||
h = pSample->iHash;
|
||||
iMin = i;
|
||||
for(i++, pSample++; i<p->nSample; i++, pSample++){
|
||||
if( pSample->isPSample ) continue;
|
||||
if( pSample->nEq<nEq
|
||||
|| (pSample->nEq==nEq && pSample->iHash<h)
|
||||
){
|
||||
iMin = i;
|
||||
nEq = pSample->nEq;
|
||||
h = pSample->iHash;
|
||||
}
|
||||
}
|
||||
p->iMin = iMin;
|
||||
}
|
||||
}
|
||||
static const FuncDef stat3PushFuncdef = {
|
||||
5, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
stat3Push, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"stat3_push", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
|
||||
/*
|
||||
** Implementation of the stat3_get(P,N,...) SQL function. This routine is
|
||||
** used to query the results. Content is returned for the Nth sqlite_stat3
|
||||
** row where N is between 0 and S-1 and S is the number of samples. The
|
||||
** value returned depends on the number of arguments.
|
||||
**
|
||||
** argc==2 result: rowid
|
||||
** argc==3 result: nEq
|
||||
** argc==4 result: nLt
|
||||
** argc==5 result: nDLt
|
||||
*/
|
||||
static void stat3Get(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int n = sqlite3_value_int(argv[1]);
|
||||
Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]);
|
||||
|
||||
assert( p!=0 );
|
||||
if( p->nSample<=n ) return;
|
||||
switch( argc ){
|
||||
case 2: sqlite3_result_int64(context, p->a[n].iRowid); break;
|
||||
case 3: sqlite3_result_int64(context, p->a[n].nEq); break;
|
||||
case 4: sqlite3_result_int64(context, p->a[n].nLt); break;
|
||||
default: sqlite3_result_int64(context, p->a[n].nDLt); break;
|
||||
}
|
||||
}
|
||||
static const FuncDef stat3GetFuncdef = {
|
||||
-1, /* nArg */
|
||||
SQLITE_UTF8, /* iPrefEnc */
|
||||
0, /* flags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
stat3Get, /* xFunc */
|
||||
0, /* xStep */
|
||||
0, /* xFinalize */
|
||||
"stat3_get", /* zName */
|
||||
0, /* pHash */
|
||||
0 /* pDestructor */
|
||||
};
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Generate code to do an analysis of all indices associated with
|
||||
** a single table.
|
||||
@@ -119,20 +444,27 @@ static void analyzeOneTable(
|
||||
int iDb; /* Index of database containing pTab */
|
||||
int regTabname = iMem++; /* Register containing table name */
|
||||
int regIdxname = iMem++; /* Register containing index name */
|
||||
int regSampleno = iMem++; /* Register containing next sample number */
|
||||
int regCol = iMem++; /* Content of a column analyzed table */
|
||||
int regStat1 = iMem++; /* The stat column of sqlite_stat1 */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
int regNumEq = regStat1; /* Number of instances. Same as regStat1 */
|
||||
int regNumLt = iMem++; /* Number of keys less than regSample */
|
||||
int regNumDLt = iMem++; /* Number of distinct keys less than regSample */
|
||||
int regSample = iMem++; /* The next sample value */
|
||||
int regRowid = regSample; /* Rowid of a sample */
|
||||
int regAccum = iMem++; /* Register to hold Stat3Accum object */
|
||||
int regLoop = iMem++; /* Loop counter */
|
||||
int regCount = iMem++; /* Number of rows in the table or index */
|
||||
int regTemp1 = iMem++; /* Intermediate register */
|
||||
int regTemp2 = iMem++; /* Intermediate register */
|
||||
int once = 1; /* One-time initialization */
|
||||
int shortJump = 0; /* Instruction address */
|
||||
int iTabCur = pParse->nTab++; /* Table cursor */
|
||||
#endif
|
||||
int regCol = iMem++; /* Content of a column in analyzed table */
|
||||
int regRec = iMem++; /* Register holding completed record */
|
||||
int regTemp = iMem++; /* Temporary use register */
|
||||
int regRowid = iMem++; /* Rowid for the inserted record */
|
||||
int regNewRowid = iMem++; /* Rowid for the inserted record */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
int addr = 0; /* Instruction address */
|
||||
int regTemp2 = iMem++; /* Temporary use register */
|
||||
int regSamplerecno = iMem++; /* Index of next sample to record */
|
||||
int regRecno = iMem++; /* Current sample index */
|
||||
int regLast = iMem++; /* Index of last sample to record */
|
||||
int regFirst = iMem++; /* Index of first sample to record */
|
||||
#endif
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 || NEVER(pTab==0) ){
|
||||
@@ -165,9 +497,14 @@ static void analyzeOneTable(
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nCol;
|
||||
KeyInfo *pKey;
|
||||
int addrIfNot = 0; /* address of OP_IfNot */
|
||||
int *aChngAddr; /* Array of jump instruction addresses */
|
||||
|
||||
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
|
||||
VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
|
||||
nCol = pIdx->nColumn;
|
||||
aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol);
|
||||
if( aChngAddr==0 ) continue;
|
||||
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
if( iMem+1+(nCol*2)>pParse->nMem ){
|
||||
pParse->nMem = iMem+1+(nCol*2);
|
||||
@@ -182,31 +519,20 @@ static void analyzeOneTable(
|
||||
/* Populate the register containing the index name. */
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
|
||||
/* If this iteration of the loop is generating code to analyze the
|
||||
** first index in the pTab->pIndex list, then register regLast has
|
||||
** not been populated. In this case populate it now. */
|
||||
if( pTab->pIndex==pIdx ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst);
|
||||
sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( once ){
|
||||
once = 0;
|
||||
sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
|
||||
}
|
||||
|
||||
/* Zero the regSampleno and regRecno registers. */
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno);
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
|
||||
(char*)&stat3InitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2);
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/* The block of memory cells initialized here is used as follows.
|
||||
**
|
||||
@@ -236,75 +562,83 @@ static void analyzeOneTable(
|
||||
endOfLoop = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
|
||||
topOfLoop = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); /* Increment row counter */
|
||||
|
||||
for(i=0; i<nCol; i++){
|
||||
CollSeq *pColl;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
|
||||
if( i==0 ){
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
/* Check if the record that cursor iIdxCur points to contains a
|
||||
** value that should be stored in the sqlite_stat2 table. If so,
|
||||
** store it. */
|
||||
int ne = sqlite3VdbeAddOp3(v, OP_Ne, regRecno, 0, regSamplerecno);
|
||||
assert( regTabname+1==regIdxname
|
||||
&& regTabname+2==regSampleno
|
||||
&& regTabname+3==regCol
|
||||
);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
|
||||
|
||||
/* Calculate new values for regSamplerecno and regSampleno.
|
||||
**
|
||||
** sampleno = sampleno + 1
|
||||
** samplerecno = samplerecno+(remaining records)/(remaining samples)
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regSampleno, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Subtract, regRecno, regLast, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2);
|
||||
sqlite3VdbeAddOp3(v, OP_Subtract, regSampleno, regTemp2, regTemp2);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regTemp, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regSamplerecno, regTemp, regSamplerecno);
|
||||
|
||||
sqlite3VdbeJumpHere(v, ne);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
|
||||
#endif
|
||||
|
||||
/* Always record the very first row */
|
||||
sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
|
||||
addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
|
||||
}
|
||||
assert( pIdx->azColl!=0 );
|
||||
assert( pIdx->azColl[i]!=0 );
|
||||
pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
|
||||
sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
|
||||
(char*)pColl, P4_COLLSEQ);
|
||||
aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
|
||||
(char*)pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
/* If a malloc failure has occurred, then the result of the expression
|
||||
** passed as the second argument to the call to sqlite3VdbeJumpHere()
|
||||
** below may be negative. Which causes an assert() to fail (or an
|
||||
** out-of-bounds write if SQLITE_DEBUG is not defined). */
|
||||
return;
|
||||
VdbeComment((v, "jump if column %d changed", i));
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( i==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1);
|
||||
VdbeComment((v, "incr repeat count"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
|
||||
for(i=0; i<nCol; i++){
|
||||
int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
|
||||
sqlite3VdbeJumpHere(v, aChngAddr[i]); /* Set jump dest for the OP_Ne */
|
||||
if( i==0 ){
|
||||
sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
|
||||
sqlite3VdbeJumpHere(v, addrIfNot); /* Jump dest for OP_IfNot */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
|
||||
(char*)&stat3PushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 5);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, pIdx->nColumn, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq);
|
||||
#endif
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
|
||||
}
|
||||
sqlite3DbFree(db, aChngAddr);
|
||||
|
||||
/* End of the analysis loop. */
|
||||
/* Always jump here after updating the iMem+1...iMem+1+nCol counters */
|
||||
sqlite3VdbeResolveLabel(v, endOfLoop);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
|
||||
(char*)&stat3PushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 5);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop);
|
||||
shortJump =
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2);
|
||||
sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample);
|
||||
sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 3);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 4);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt,
|
||||
(char*)&stat3GetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 5);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump);
|
||||
sqlite3VdbeJumpHere(v, shortJump+2);
|
||||
#endif
|
||||
|
||||
/* Store the results in sqlite_stat1.
|
||||
**
|
||||
@@ -324,22 +658,22 @@ static void analyzeOneTable(
|
||||
** If K>0 then it is always the case the D>0 so division by zero
|
||||
** is never possible.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1);
|
||||
if( jZeroRows<0 ){
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
|
||||
}
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
|
||||
sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
|
||||
sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
}
|
||||
|
||||
@@ -349,22 +683,23 @@ static void analyzeOneTable(
|
||||
if( pTab->pIndex==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno);
|
||||
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
|
||||
}else{
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
if( pParse->nMem<regRec ) pParse->nMem = regRec;
|
||||
sqlite3VdbeJumpHere(v, jZeroRows);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code that will cause the most recent index analysis to
|
||||
** be loaded into internal hash tables where is can be used.
|
||||
@@ -388,7 +723,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
|
||||
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
iStatCur = pParse->nTab;
|
||||
pParse->nTab += 2;
|
||||
pParse->nTab += 3;
|
||||
openStatTable(pParse, iDb, iStatCur, 0, 0);
|
||||
iMem = pParse->nMem+1;
|
||||
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
|
||||
@@ -413,7 +748,7 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
iStatCur = pParse->nTab;
|
||||
pParse->nTab += 2;
|
||||
pParse->nTab += 3;
|
||||
if( pOnlyIdx ){
|
||||
openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx");
|
||||
}else{
|
||||
@@ -518,7 +853,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
Index *pIndex;
|
||||
Table *pTable;
|
||||
int i, c, n;
|
||||
unsigned int v;
|
||||
tRowcnt v;
|
||||
const char *z;
|
||||
|
||||
assert( argc==3 );
|
||||
@@ -561,10 +896,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
** and its contents.
|
||||
*/
|
||||
void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( pIdx->aSample ){
|
||||
int j;
|
||||
for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
|
||||
for(j=0; j<pIdx->nSample; j++){
|
||||
IndexSample *p = &pIdx->aSample[j];
|
||||
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
|
||||
sqlite3DbFree(db, p->u.z);
|
||||
@@ -572,25 +907,157 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
|
||||
}
|
||||
sqlite3DbFree(db, pIdx->aSample);
|
||||
}
|
||||
if( db && db->pnBytesFreed==0 ){
|
||||
pIdx->nSample = 0;
|
||||
pIdx->aSample = 0;
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(db);
|
||||
UNUSED_PARAMETER(pIdx);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
|
||||
** Load content from the sqlite_stat3 table into the Index.aSample[]
|
||||
** arrays of all indices.
|
||||
*/
|
||||
static int loadStat3(sqlite3 *db, const char *zDb){
|
||||
int rc; /* Result codes from subroutines */
|
||||
sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
|
||||
char *zSql; /* Text of the SQL statement */
|
||||
Index *pPrevIdx = 0; /* Previous index in the loop */
|
||||
int idx = 0; /* slot in pIdx->aSample[] for next sample */
|
||||
int eType; /* Datatype of a sample */
|
||||
IndexSample *pSample; /* A slot in pIdx->aSample[] */
|
||||
|
||||
if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx,count(*) FROM %Q.sqlite_stat3"
|
||||
" GROUP BY idx", zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( rc ) return rc;
|
||||
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
int nSample; /* Number of samples */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
nSample = sqlite3_column_int(pStmt, 1);
|
||||
pIdx = sqlite3FindIndex(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
assert( pIdx->nSample==0 );
|
||||
pIdx->nSample = nSample;
|
||||
pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) );
|
||||
pIdx->avgEq = pIdx->aiRowEst[1];
|
||||
if( pIdx->aSample==0 ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc ) return rc;
|
||||
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb);
|
||||
if( !zSql ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
if( rc ) return rc;
|
||||
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
int i; /* Loop counter */
|
||||
tRowcnt sumEq; /* Sum of the nEq values */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
pIdx = sqlite3FindIndex(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
if( pIdx==pPrevIdx ){
|
||||
idx++;
|
||||
}else{
|
||||
pPrevIdx = pIdx;
|
||||
idx = 0;
|
||||
}
|
||||
assert( idx<pIdx->nSample );
|
||||
pSample = &pIdx->aSample[idx];
|
||||
pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1);
|
||||
pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2);
|
||||
pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3);
|
||||
if( idx==pIdx->nSample-1 ){
|
||||
if( pSample->nDLt>0 ){
|
||||
for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq;
|
||||
pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt;
|
||||
}
|
||||
if( pIdx->avgEq<=0 ) pIdx->avgEq = 1;
|
||||
}
|
||||
eType = sqlite3_column_type(pStmt, 4);
|
||||
pSample->eType = (u8)eType;
|
||||
switch( eType ){
|
||||
case SQLITE_INTEGER: {
|
||||
pSample->u.i = sqlite3_column_int64(pStmt, 4);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
pSample->u.r = sqlite3_column_double(pStmt, 4);
|
||||
break;
|
||||
}
|
||||
case SQLITE_NULL: {
|
||||
break;
|
||||
}
|
||||
default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); {
|
||||
const char *z = (const char *)(
|
||||
(eType==SQLITE_BLOB) ?
|
||||
sqlite3_column_blob(pStmt, 4):
|
||||
sqlite3_column_text(pStmt, 4)
|
||||
);
|
||||
int n = z ? sqlite3_column_bytes(pStmt, 4) : 0;
|
||||
pSample->nByte = n;
|
||||
if( n < 1){
|
||||
pSample->u.z = 0;
|
||||
}else{
|
||||
pSample->u.z = sqlite3Malloc(n);
|
||||
if( pSample->u.z==0 ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memcpy(pSample->u.z, z, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The
|
||||
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
|
||||
** arrays. The contents of sqlite_stat2 are used to populate the
|
||||
** arrays. The contents of sqlite_stat3 are used to populate the
|
||||
** Index.aSample[] arrays.
|
||||
**
|
||||
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined
|
||||
** during compilation and the sqlite_stat2 table is present, no data is
|
||||
** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined
|
||||
** during compilation and the sqlite_stat3 table is present, no data is
|
||||
** read from it.
|
||||
**
|
||||
** If SQLITE_ENABLE_STAT2 was defined during compilation and the
|
||||
** sqlite_stat2 table is not present in the database, SQLITE_ERROR is
|
||||
** If SQLITE_ENABLE_STAT3 was defined during compilation and the
|
||||
** sqlite_stat3 table is not present in the database, SQLITE_ERROR is
|
||||
** returned. However, in this case, data is read from the sqlite_stat1
|
||||
** table (if it is present) before returning.
|
||||
**
|
||||
@@ -612,8 +1079,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
|
||||
Index *pIdx = sqliteHashData(i);
|
||||
sqlite3DefaultRowEst(pIdx);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3DeleteIndexSamples(db, pIdx);
|
||||
pIdx->aSample = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check to make sure the sqlite_stat1 table exists */
|
||||
@@ -625,7 +1094,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
|
||||
/* Load new statistics out of the sqlite_stat1 table */
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -634,78 +1103,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
}
|
||||
|
||||
|
||||
/* Load the statistics from the sqlite_stat2 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
/* Load the statistics from the sqlite_stat3 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase);
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
sqlite3DbFree(db, zSql);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char *zIndex; /* Index name */
|
||||
Index *pIdx; /* Pointer to the index object */
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0;
|
||||
if( pIdx ){
|
||||
int iSample = sqlite3_column_int(pStmt, 1);
|
||||
if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
|
||||
int eType = sqlite3_column_type(pStmt, 2);
|
||||
|
||||
if( pIdx->aSample==0 ){
|
||||
static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
|
||||
pIdx->aSample = (IndexSample *)sqlite3DbMallocRaw(0, sz);
|
||||
if( pIdx->aSample==0 ){
|
||||
db->mallocFailed = 1;
|
||||
break;
|
||||
}
|
||||
memset(pIdx->aSample, 0, sz);
|
||||
}
|
||||
|
||||
assert( pIdx->aSample );
|
||||
{
|
||||
IndexSample *pSample = &pIdx->aSample[iSample];
|
||||
pSample->eType = (u8)eType;
|
||||
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
pSample->u.r = sqlite3_column_double(pStmt, 2);
|
||||
}else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
|
||||
const char *z = (const char *)(
|
||||
(eType==SQLITE_BLOB) ?
|
||||
sqlite3_column_blob(pStmt, 2):
|
||||
sqlite3_column_text(pStmt, 2)
|
||||
);
|
||||
int n = sqlite3_column_bytes(pStmt, 2);
|
||||
if( n>24 ){
|
||||
n = 24;
|
||||
}
|
||||
pSample->nByte = (u8)n;
|
||||
if( n < 1){
|
||||
pSample->u.z = 0;
|
||||
}else{
|
||||
pSample->u.z = sqlite3DbStrNDup(0, z, n);
|
||||
if( pSample->u.z==0 ){
|
||||
db->mallocFailed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
rc = loadStat3(db, sInfo.zDatabase);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -669,10 +669,18 @@ void sqlite3BackupRestart(sqlite3_backup *pBackup){
|
||||
*/
|
||||
int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
int rc;
|
||||
sqlite3_file *pFd; /* File descriptor for database pTo */
|
||||
sqlite3_backup b;
|
||||
sqlite3BtreeEnter(pTo);
|
||||
sqlite3BtreeEnter(pFrom);
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo) );
|
||||
pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
|
||||
if( pFd->pMethods ){
|
||||
i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
|
||||
sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
|
||||
}
|
||||
|
||||
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
|
||||
** to 0. This is used by the implementations of sqlite3_backup_step()
|
||||
** and sqlite3_backup_finish() to detect that they are being called
|
||||
@@ -698,6 +706,7 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
||||
pTo->pBt->pageSizeFixed = 0;
|
||||
}
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo)==0 );
|
||||
sqlite3BtreeLeave(pFrom);
|
||||
sqlite3BtreeLeave(pTo);
|
||||
return rc;
|
||||
|
60
src/btree.c
60
src/btree.c
@@ -3938,21 +3938,55 @@ static int accessPayload(
|
||||
/* Need to read this page properly. It contains some of the
|
||||
** range of data that is being read (eOp==0) or written (eOp!=0).
|
||||
*/
|
||||
DbPage *pDbPage;
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
sqlite3_file *fd;
|
||||
#endif
|
||||
int a = amt;
|
||||
rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
aPayload = sqlite3PagerGetData(pDbPage);
|
||||
nextPage = get4byte(aPayload);
|
||||
if( a + offset > ovflSize ){
|
||||
a = ovflSize - offset;
|
||||
}
|
||||
rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
offset = 0;
|
||||
amt -= a;
|
||||
pBuf += a;
|
||||
if( a + offset > ovflSize ){
|
||||
a = ovflSize - offset;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
/* If all the following are true:
|
||||
**
|
||||
** 1) this is a read operation, and
|
||||
** 2) data is required from the start of this overflow page, and
|
||||
** 3) the database is file-backed, and
|
||||
** 4) there is no open write-transaction, and
|
||||
** 5) the database is not a WAL database,
|
||||
**
|
||||
** then data can be read directly from the database file into the
|
||||
** output buffer, bypassing the page-cache altogether. This speeds
|
||||
** up loading large records that span many overflow pages.
|
||||
*/
|
||||
if( eOp==0 /* (1) */
|
||||
&& offset==0 /* (2) */
|
||||
&& pBt->inTransaction==TRANS_READ /* (4) */
|
||||
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
|
||||
&& pBt->pPage1->aData[19]==0x01 /* (5) */
|
||||
){
|
||||
u8 aSave[4];
|
||||
u8 *aWrite = &pBuf[-4];
|
||||
memcpy(aSave, aWrite, 4);
|
||||
rc = sqlite3OsRead(fd, aWrite, a+4, pBt->pageSize * (nextPage-1));
|
||||
nextPage = get4byte(aWrite);
|
||||
memcpy(aWrite, aSave, 4);
|
||||
}else
|
||||
#endif
|
||||
|
||||
{
|
||||
DbPage *pDbPage;
|
||||
rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
aPayload = sqlite3PagerGetData(pDbPage);
|
||||
nextPage = get4byte(aPayload);
|
||||
rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
amt -= a;
|
||||
pBuf += a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
152
src/build.c
152
src/build.c
@@ -1990,7 +1990,11 @@ static void sqlite3ClearStatTables(
|
||||
const char *zType, /* "idx" or "tbl" */
|
||||
const char *zName /* Name of index or table */
|
||||
){
|
||||
static const char *azStatTab[] = { "sqlite_stat1", "sqlite_stat2" };
|
||||
static const char *azStatTab[] = {
|
||||
"sqlite_stat1",
|
||||
"sqlite_stat2",
|
||||
"sqlite_stat3",
|
||||
};
|
||||
int i;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zName;
|
||||
for(i=0; i<ArraySize(azStatTab); i++){
|
||||
@@ -2003,6 +2007,76 @@ static void sqlite3ClearStatTables(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code to drop a table.
|
||||
*/
|
||||
void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
|
||||
Vdbe *v;
|
||||
sqlite3 *db = pParse->db;
|
||||
Trigger *pTrigger;
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp0(v, OP_VBegin);
|
||||
}
|
||||
#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.
|
||||
*/
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
assert( pTrigger->pSchema==pTab->pSchema ||
|
||||
pTrigger->pSchema==db->aDb[1].pSchema );
|
||||
sqlite3DropTriggerPtr(pParse, pTrigger);
|
||||
pTrigger = pTrigger->pNext;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
/* Remove any entries of the sqlite_sequence table associated with
|
||||
** the table being dropped. This is done before the table is dropped
|
||||
** at the btree level, in case the sqlite_sequence table needs to
|
||||
** move as a result of the drop (can happen in auto-vacuum mode).
|
||||
*/
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop all SQLITE_MASTER table and index entries that refer to the
|
||||
** table. The program name loops through the master table and deletes
|
||||
** every row that refers to a table of the same name as the one being
|
||||
** dropped. Triggers are handled seperately 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->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
|
||||
/* Remove the table entry from SQLite's internal schema and modify
|
||||
** the schema cookie.
|
||||
*/
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqliteViewResetAll(db, iDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to do the work of a DROP TABLE statement.
|
||||
** pName is the name of the table to be dropped.
|
||||
@@ -2071,7 +2145,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
|
||||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
|
||||
&& sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
|
||||
goto exit_drop_table;
|
||||
}
|
||||
@@ -2095,68 +2170,11 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
|
||||
*/
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v ){
|
||||
Trigger *pTrigger;
|
||||
Db *pDb = &db->aDb[iDb];
|
||||
sqlite3BeginWriteOperation(pParse, 1, iDb);
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp0(v, OP_VBegin);
|
||||
}
|
||||
#endif
|
||||
sqlite3FkDropTable(pParse, pName, pTab);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
pTrigger = sqlite3TriggerList(pParse, pTab);
|
||||
while( pTrigger ){
|
||||
assert( pTrigger->pSchema==pTab->pSchema ||
|
||||
pTrigger->pSchema==db->aDb[1].pSchema );
|
||||
sqlite3DropTriggerPtr(pParse, pTrigger);
|
||||
pTrigger = pTrigger->pNext;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
/* Remove any entries of the sqlite_sequence table associated with
|
||||
** the table being dropped. This is done before the table is dropped
|
||||
** at the btree level, in case the sqlite_sequence table needs to
|
||||
** move as a result of the drop (can happen in auto-vacuum mode).
|
||||
*/
|
||||
if( pTab->tabFlags & TF_Autoincrement ){
|
||||
sqlite3NestedParse(pParse,
|
||||
"DELETE FROM %s.sqlite_sequence WHERE name=%Q",
|
||||
pDb->zName, pTab->zName
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop all SQLITE_MASTER table and index entries that refer to the
|
||||
** table. The program name loops through the master table and deletes
|
||||
** every row that refers to a table of the same name as the one being
|
||||
** dropped. Triggers are handled seperately 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->zName, SCHEMA_TABLE(iDb), pTab->zName);
|
||||
sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
destroyTable(pParse, pTab);
|
||||
}
|
||||
|
||||
/* Remove the table entry from SQLite's internal schema and modify
|
||||
** the schema cookie.
|
||||
*/
|
||||
if( IsVirtual(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3FkDropTable(pParse, pName, pTab);
|
||||
sqlite3CodeDropTable(pParse, pTab, iDb, isView);
|
||||
}
|
||||
sqliteViewResetAll(db, iDb);
|
||||
|
||||
exit_drop_table:
|
||||
sqlite3SrcListDelete(db, pName);
|
||||
@@ -2639,8 +2657,8 @@ Index *sqlite3CreateIndex(
|
||||
nCol = pList->nExpr;
|
||||
pIndex = sqlite3DbMallocZero(db,
|
||||
sizeof(Index) + /* Index structure */
|
||||
sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
|
||||
sizeof(int)*nCol + /* Index.aiColumn */
|
||||
sizeof(int)*(nCol+1) + /* Index.aiRowEst */
|
||||
sizeof(char *)*nCol + /* Index.azColl */
|
||||
sizeof(u8)*nCol + /* Index.aSortOrder */
|
||||
nName + 1 + /* Index.zName */
|
||||
@@ -2649,10 +2667,10 @@ Index *sqlite3CreateIndex(
|
||||
if( db->mallocFailed ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->azColl = (char**)(&pIndex[1]);
|
||||
pIndex->aiRowEst = (tRowcnt*)(&pIndex[1]);
|
||||
pIndex->azColl = (char**)(&pIndex->aiRowEst[nCol+1]);
|
||||
pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
|
||||
pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]);
|
||||
pIndex->aSortOrder = (u8 *)(&pIndex->aiRowEst[nCol+1]);
|
||||
pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]);
|
||||
pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
|
||||
zExtra = (char *)(&pIndex->zName[nName+1]);
|
||||
memcpy(pIndex->zName, zName, nName+1);
|
||||
@@ -2929,9 +2947,9 @@ exit_create_index:
|
||||
** are based on typical values found in actual indices.
|
||||
*/
|
||||
void sqlite3DefaultRowEst(Index *pIdx){
|
||||
unsigned *a = pIdx->aiRowEst;
|
||||
tRowcnt *a = pIdx->aiRowEst;
|
||||
int i;
|
||||
unsigned n;
|
||||
tRowcnt n;
|
||||
assert( a!=0 );
|
||||
a[0] = pIdx->pTable->nRowEst;
|
||||
if( a[0]<10 ) a[0] = 10;
|
||||
|
@@ -117,6 +117,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
"ENABLE_STAT2",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
"ENABLE_STAT3",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
"ENABLE_UNLOCK_NOTIFY",
|
||||
#endif
|
||||
@@ -329,9 +332,6 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_OMIT_XFER_OPT
|
||||
"OMIT_XFER_OPT",
|
||||
#endif
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
"PAGECACHE_BLOCKALLOC",
|
||||
#endif
|
||||
#ifdef SQLITE_PERFORMANCE_TRACE
|
||||
"PERFORMANCE_TRACE",
|
||||
#endif
|
||||
|
@@ -1757,6 +1757,9 @@ static int xferOptimization(
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we get this far, it means either:
|
||||
**
|
||||
|
@@ -4888,8 +4888,16 @@ static int findCreateFileMode(
|
||||
** used by the test_multiplex.c module.
|
||||
*/
|
||||
nDb = sqlite3Strlen30(zPath) - 1;
|
||||
while( nDb>0 && zPath[nDb]!='-' ) nDb--;
|
||||
if( nDb==0 ) return SQLITE_OK;
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
while( nDb>0 && zPath[nDb]!='-' && zPath[nDb]!='/' ) nDb--;
|
||||
if( nDb==0 || zPath[nDb]=='/' ) return SQLITE_OK;
|
||||
#else
|
||||
while( zPath[nDb]!='-' ){
|
||||
assert( nDb>0 );
|
||||
assert( zPath[nDb]!='\n' );
|
||||
nDb--;
|
||||
}
|
||||
#endif
|
||||
memcpy(zDb, zPath, nDb);
|
||||
zDb[nDb] = '\0';
|
||||
|
||||
|
@@ -2615,7 +2615,7 @@ static int winOpen(
|
||||
pFile->lastErrno = GetLastError();
|
||||
winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
|
||||
free(zConverted);
|
||||
if( isReadWrite ){
|
||||
if( isReadWrite && !isExclusive ){
|
||||
return winOpen(pVfs, zName, id,
|
||||
((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
|
||||
}else{
|
||||
|
33
src/pager.c
33
src/pager.c
@@ -670,8 +670,8 @@ struct Pager {
|
||||
char *zJournal; /* Name of the journal file */
|
||||
int (*xBusyHandler)(void*); /* Function to call when busy */
|
||||
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
|
||||
int nHit, nMiss; /* Total cache hits and misses */
|
||||
#ifdef SQLITE_TEST
|
||||
int nHit, nMiss; /* Cache hits and missing */
|
||||
int nRead, nWrite; /* Database pages read/written */
|
||||
#endif
|
||||
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
|
||||
@@ -4169,7 +4169,7 @@ static int pagerStress(void *p, PgHdr *pPg){
|
||||
**
|
||||
** Spilling is also prohibited when in an error state since that could
|
||||
** lead to database corruption. In the current implementaton it
|
||||
** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
|
||||
** is impossible for sqlite3PcacheFetch() to be called with createFlag==1
|
||||
** while in the error state, hence it is impossible for this routine to
|
||||
** be called in the error state. Nevertheless, we include a NEVER()
|
||||
** test for the error state as a safeguard against future changes.
|
||||
@@ -5005,14 +5005,13 @@ int sqlite3PagerAcquire(
|
||||
/* In this case the pcache already contains an initialized copy of
|
||||
** the page. Return without further ado. */
|
||||
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
|
||||
PAGER_INCR(pPager->nHit);
|
||||
pPager->nHit++;
|
||||
return SQLITE_OK;
|
||||
|
||||
}else{
|
||||
/* The pager cache has created a new page. Its content needs to
|
||||
** be initialized. */
|
||||
|
||||
PAGER_INCR(pPager->nMiss);
|
||||
pPg = *ppPage;
|
||||
pPg->pPager = pPager;
|
||||
|
||||
@@ -5048,6 +5047,7 @@ int sqlite3PagerAcquire(
|
||||
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
||||
}else{
|
||||
assert( pPg->pPager==pPager );
|
||||
pPager->nMiss++;
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto pager_acquire_err;
|
||||
@@ -6082,6 +6082,31 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
|
||||
** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
|
||||
** current cache hit or miss count, according to the value of eStat. If the
|
||||
** reset parameter is non-zero, the cache hit or miss count is zeroed before
|
||||
** returning.
|
||||
*/
|
||||
void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
|
||||
int *piStat;
|
||||
|
||||
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
|
||||
);
|
||||
if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){
|
||||
piStat = &pPager->nHit;
|
||||
}else{
|
||||
piStat = &pPager->nMiss;
|
||||
}
|
||||
|
||||
*pnVal += *piStat;
|
||||
if( reset ){
|
||||
*piStat = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if this is an in-memory pager.
|
||||
*/
|
||||
|
@@ -155,6 +155,7 @@ const char *sqlite3PagerJournalname(Pager*);
|
||||
int sqlite3PagerNosync(Pager*);
|
||||
void *sqlite3PagerTempSpace(Pager*);
|
||||
int sqlite3PagerIsMemdb(Pager*);
|
||||
void sqlite3PagerCacheStat(Pager *, int, int, int *);
|
||||
|
||||
/* Functions used to truncate the database file. */
|
||||
void sqlite3PagerTruncateImage(Pager*,Pgno);
|
||||
|
250
src/pcache1.c
250
src/pcache1.c
@@ -24,8 +24,6 @@ typedef struct PgHdr1 PgHdr1;
|
||||
typedef struct PgFreeslot PgFreeslot;
|
||||
typedef struct PGroup PGroup;
|
||||
|
||||
typedef struct PGroupBlock PGroupBlock;
|
||||
typedef struct PGroupBlockList PGroupBlockList;
|
||||
|
||||
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
||||
** of one or more PCaches that are able to recycle each others unpinned
|
||||
@@ -56,66 +54,8 @@ struct PGroup {
|
||||
int mxPinned; /* nMaxpage + 10 - nMinPage */
|
||||
int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
int isBusy; /* Do not run ReleaseMemory() if true */
|
||||
PGroupBlockList *pBlockList; /* List of block-lists for this group */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** If SQLITE_PAGECACHE_BLOCKALLOC is defined when the library is built,
|
||||
** each PGroup structure has a linked list of the the following starting
|
||||
** at PGroup.pBlockList. There is one entry for each distinct page-size
|
||||
** currently used by members of the PGroup (i.e. 1024 bytes, 4096 bytes
|
||||
** etc.). Variable PGroupBlockList.nByte is set to the actual allocation
|
||||
** size requested by each pcache, which is the database page-size plus
|
||||
** the various header structures used by the pcache, pager and btree layers.
|
||||
** Usually around (pgsz+200) bytes.
|
||||
**
|
||||
** This size (pgsz+200) bytes is not allocated efficiently by some
|
||||
** implementations of malloc. In particular, some implementations are only
|
||||
** able to allocate blocks of memory chunks of 2^N bytes, where N is some
|
||||
** integer value. Since the page-size is a power of 2, this means we
|
||||
** end up wasting (pgsz-200) bytes in each allocation.
|
||||
**
|
||||
** If SQLITE_PAGECACHE_BLOCKALLOC is defined, the (pgsz+200) byte blocks
|
||||
** are not allocated directly. Instead, blocks of roughly M*(pgsz+200) bytes
|
||||
** are requested from malloc allocator. After a block is returned,
|
||||
** sqlite3MallocSize() is used to determine how many (pgsz+200) byte
|
||||
** allocations can fit in the space returned by malloc(). This value may
|
||||
** be more than M.
|
||||
**
|
||||
** The blocks are stored in a doubly-linked list. Variable PGroupBlock.nEntry
|
||||
** contains the number of allocations that will fit in the aData[] space.
|
||||
** nEntry is limited to the number of bits in bitmask mUsed. If a slot
|
||||
** within aData is in use, the corresponding bit in mUsed is set. Thus
|
||||
** when (mUsed+1==(1 << nEntry)) the block is completely full.
|
||||
**
|
||||
** Each time a slot within a block is freed, the block is moved to the start
|
||||
** of the linked-list. And if a block becomes completely full, then it is
|
||||
** moved to the end of the list. As a result, when searching for a free
|
||||
** slot, only the first block in the list need be examined. If it is full,
|
||||
** then it is guaranteed that all blocks are full.
|
||||
*/
|
||||
struct PGroupBlockList {
|
||||
int nByte; /* Size of each allocation in bytes */
|
||||
PGroupBlock *pFirst; /* First PGroupBlock in list */
|
||||
PGroupBlock *pLast; /* Last PGroupBlock in list */
|
||||
PGroupBlockList *pNext; /* Next block-list attached to group */
|
||||
};
|
||||
|
||||
struct PGroupBlock {
|
||||
Bitmask mUsed; /* Mask of used slots */
|
||||
int nEntry; /* Maximum number of allocations in aData[] */
|
||||
u8 *aData; /* Pointer to data block */
|
||||
PGroupBlock *pNext; /* Next PGroupBlock in list */
|
||||
PGroupBlock *pPrev; /* Previous PGroupBlock in list */
|
||||
PGroupBlockList *pList; /* Owner list */
|
||||
};
|
||||
|
||||
/* Minimum value for PGroupBlock.nEntry */
|
||||
#define PAGECACHE_BLOCKALLOC_MINENTRY 15
|
||||
|
||||
/* Each page cache is an instance of the following object. Every
|
||||
** open database file (including each in-memory database and each
|
||||
** temporary or transient database) has a single page cache which
|
||||
@@ -219,17 +159,6 @@ static SQLITE_WSD struct PCacheGlobal {
|
||||
#define PGHDR1_TO_PAGE(p) (void*)(((char*)p) - p->pCache->szPage)
|
||||
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
|
||||
|
||||
/*
|
||||
** Blocks used by the SQLITE_PAGECACHE_BLOCKALLOC blocks to store/retrieve
|
||||
** a PGroupBlock pointer based on a pointer to a page buffer.
|
||||
*/
|
||||
#define PAGE_SET_BLOCKPTR(pCache, pPg, pBlock) \
|
||||
( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) = pBlock )
|
||||
|
||||
#define PAGE_GET_BLOCKPTR(pCache, pPg) \
|
||||
( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) )
|
||||
|
||||
|
||||
/*
|
||||
** Macros to enter and leave the PCache LRU mutex.
|
||||
*/
|
||||
@@ -355,139 +284,14 @@ static int pcache1MemSize(void *p){
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
|
||||
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
/*
|
||||
** The block pBlock belongs to list pList but is not currently linked in.
|
||||
** Insert it into the start of the list.
|
||||
*/
|
||||
static void addBlockToList(PGroupBlockList *pList, PGroupBlock *pBlock){
|
||||
pBlock->pPrev = 0;
|
||||
pBlock->pNext = pList->pFirst;
|
||||
pList->pFirst = pBlock;
|
||||
if( pBlock->pNext ){
|
||||
pBlock->pNext->pPrev = pBlock;
|
||||
}else{
|
||||
assert( pList->pLast==0 );
|
||||
pList->pLast = pBlock;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If there are no blocks in the list headed by pList, remove pList
|
||||
** from the pGroup->pBlockList list and free it with sqlite3_free().
|
||||
*/
|
||||
static void freeListIfEmpty(PGroup *pGroup, PGroupBlockList *pList){
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
if( pList->pFirst==0 ){
|
||||
PGroupBlockList **pp;
|
||||
for(pp=&pGroup->pBlockList; *pp!=pList; pp=&(*pp)->pNext);
|
||||
*pp = (*pp)->pNext;
|
||||
sqlite3_free(pList);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_PAGECACHE_BLOCKALLOC */
|
||||
|
||||
/*
|
||||
** Allocate a new page object initially associated with cache pCache.
|
||||
*/
|
||||
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
int nByte = sizeof(PgHdr1) + pCache->szPage;
|
||||
void *pPg = 0;
|
||||
PgHdr1 *p;
|
||||
PgHdr1 *p = 0;
|
||||
void *pPg;
|
||||
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
PGroupBlockList *pList;
|
||||
PGroupBlock *pBlock;
|
||||
int i;
|
||||
|
||||
nByte += sizeof(PGroupBlockList *);
|
||||
nByte = ROUND8(nByte);
|
||||
|
||||
for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
|
||||
if( pList->nByte==nByte ) break;
|
||||
}
|
||||
if( pList==0 ){
|
||||
PGroupBlockList *pNew;
|
||||
assert( pGroup->isBusy==0 );
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
pGroup->isBusy = 1; /* Disable sqlite3PcacheReleaseMemory() */
|
||||
pNew = (PGroupBlockList *)sqlite3MallocZero(sizeof(PGroupBlockList));
|
||||
pGroup->isBusy = 0; /* Reenable sqlite3PcacheReleaseMemory() */
|
||||
if( pNew==0 ){
|
||||
/* malloc() failure. Return early. */
|
||||
return 0;
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
|
||||
assert( pList->nByte!=nByte );
|
||||
}
|
||||
#endif
|
||||
pNew->nByte = nByte;
|
||||
pNew->pNext = pGroup->pBlockList;
|
||||
pGroup->pBlockList = pNew;
|
||||
pList = pNew;
|
||||
}
|
||||
|
||||
pBlock = pList->pFirst;
|
||||
if( pBlock==0 || pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) ){
|
||||
int sz;
|
||||
|
||||
/* Allocate a new block. Try to allocate enough space for the PGroupBlock
|
||||
** structure and MINENTRY allocations of nByte bytes each. If the
|
||||
** allocator returns more memory than requested, then more than MINENTRY
|
||||
** allocations may fit in it. */
|
||||
assert( sqlite3_mutex_held(pGroup->mutex) );
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
sz = sizeof(PGroupBlock) + PAGECACHE_BLOCKALLOC_MINENTRY * nByte;
|
||||
pBlock = (PGroupBlock *)sqlite3Malloc(sz);
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
|
||||
if( !pBlock ){
|
||||
freeListIfEmpty(pGroup, pList);
|
||||
return 0;
|
||||
}
|
||||
pBlock->nEntry = (sqlite3MallocSize(pBlock) - sizeof(PGroupBlock)) / nByte;
|
||||
if( pBlock->nEntry>=BMS ){
|
||||
pBlock->nEntry = BMS-1;
|
||||
}
|
||||
pBlock->pList = pList;
|
||||
pBlock->mUsed = 0;
|
||||
pBlock->aData = (u8 *)&pBlock[1];
|
||||
addBlockToList(pList, pBlock);
|
||||
|
||||
sz = sqlite3MallocSize(pBlock);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}
|
||||
|
||||
for(i=0; pPg==0 && ALWAYS(i<pBlock->nEntry); i++){
|
||||
if( 0==(pBlock->mUsed & ((Bitmask)1<<i)) ){
|
||||
pBlock->mUsed |= ((Bitmask)1<<i);
|
||||
pPg = (void *)&pBlock->aData[pList->nByte * i];
|
||||
}
|
||||
}
|
||||
assert( pPg );
|
||||
PAGE_SET_BLOCKPTR(pCache, pPg, pBlock);
|
||||
|
||||
/* If the block is now full, shift it to the end of the list */
|
||||
if( pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) && pList->pLast!=pBlock ){
|
||||
assert( pList->pFirst==pBlock );
|
||||
assert( pBlock->pPrev==0 );
|
||||
assert( pList->pLast->pNext==0 );
|
||||
pList->pFirst = pBlock->pNext;
|
||||
pList->pFirst->pPrev = 0;
|
||||
pBlock->pPrev = pList->pLast;
|
||||
pBlock->pNext = 0;
|
||||
pList->pLast->pNext = pBlock;
|
||||
pList->pLast = pBlock;
|
||||
}
|
||||
p = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage++;
|
||||
}
|
||||
#else
|
||||
/* The group mutex must be released before pcache1Alloc() is called. This
|
||||
** is because it may call sqlite3_release_memory(), which assumes that
|
||||
** this mutex is not held. */
|
||||
@@ -495,15 +299,13 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
pPg = pcache1Alloc(nByte);
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
|
||||
if( pPg ){
|
||||
p = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage++;
|
||||
}
|
||||
}else{
|
||||
p = 0;
|
||||
}
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -517,49 +319,8 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
static void pcache1FreePage(PgHdr1 *p){
|
||||
if( ALWAYS(p) ){
|
||||
PCache1 *pCache = p->pCache;
|
||||
void *pPg = PGHDR1_TO_PAGE(p);
|
||||
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
PGroupBlock *pBlock = PAGE_GET_BLOCKPTR(pCache, pPg);
|
||||
PGroupBlockList *pList = pBlock->pList;
|
||||
int i = ((u8 *)pPg - pBlock->aData) / pList->nByte;
|
||||
|
||||
assert( pPg==(void *)&pBlock->aData[i*pList->nByte] );
|
||||
assert( pBlock->mUsed & ((Bitmask)1<<i) );
|
||||
pBlock->mUsed &= ~((Bitmask)1<<i);
|
||||
|
||||
/* Remove the block from the list. If it is completely empty, free it.
|
||||
** Or if it is not completely empty, re-insert it at the start of the
|
||||
** list. */
|
||||
if( pList->pFirst==pBlock ){
|
||||
pList->pFirst = pBlock->pNext;
|
||||
if( pList->pFirst ) pList->pFirst->pPrev = 0;
|
||||
}else{
|
||||
pBlock->pPrev->pNext = pBlock->pNext;
|
||||
}
|
||||
if( pList->pLast==pBlock ){
|
||||
pList->pLast = pBlock->pPrev;
|
||||
if( pList->pLast ) pList->pLast->pNext = 0;
|
||||
}else{
|
||||
pBlock->pNext->pPrev = pBlock->pPrev;
|
||||
}
|
||||
|
||||
if( pBlock->mUsed==0 ){
|
||||
PGroup *pGroup = p->pCache->pGroup;
|
||||
|
||||
int sz = sqlite3MallocSize(pBlock);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -sz);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
freeListIfEmpty(pGroup, pList);
|
||||
sqlite3_free(pBlock);
|
||||
}else{
|
||||
addBlockToList(pList, pBlock);
|
||||
}
|
||||
#else
|
||||
assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
|
||||
pcache1Free(pPg);
|
||||
#endif
|
||||
pcache1Free(PGHDR1_TO_PAGE(p));
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage--;
|
||||
}
|
||||
@@ -1170,9 +931,6 @@ void sqlite3PCacheSetDefault(void){
|
||||
*/
|
||||
int sqlite3PcacheReleaseMemory(int nReq){
|
||||
int nFree = 0;
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
if( pcache1.grp.isBusy ) return 0;
|
||||
#endif
|
||||
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
|
||||
assert( sqlite3_mutex_notheld(pcache1.mutex) );
|
||||
if( pcache1.pStart==0 ){
|
||||
|
@@ -533,8 +533,10 @@ void sqlite3Pragma(
|
||||
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
|
||||
int ii; /* Loop counter */
|
||||
|
||||
/* Force the schema to be loaded on all databases. This cases all
|
||||
** database files to be opened and the journal_modes set. */
|
||||
/* Force the schema to be loaded on all databases. This causes all
|
||||
** database files to be opened and the journal_modes set. This is
|
||||
** necessary because subsequent processing must know if the databases
|
||||
** are in WAL mode. */
|
||||
if( sqlite3ReadSchema(pParse) ){
|
||||
goto pragma_out;
|
||||
}
|
||||
|
11
src/shell.c
11
src/shell.c
@@ -1029,7 +1029,12 @@ static int display_stats(
|
||||
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur);
|
||||
fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache hits: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache misses: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
|
||||
@@ -1673,7 +1678,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(stderr, "Error: non-null separator required for import\n");
|
||||
return 1;
|
||||
}
|
||||
zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
|
||||
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
|
||||
if( zSql==0 ){
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
return 1;
|
||||
@@ -1695,7 +1700,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
|
||||
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
|
||||
j = strlen30(zSql);
|
||||
for(i=1; i<nCol; i++){
|
||||
zSql[j++] = ',';
|
||||
|
@@ -767,6 +767,10 @@ struct sqlite3_io_methods {
|
||||
** WAL mode. If the integer is -1, then it is overwritten with the current
|
||||
** WAL persistence setting.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
|
||||
** a write transaction to indicate that, unless it is rolled back for some
|
||||
** reason, the entire database file will be overwritten by the current
|
||||
** transaction. This is used by VACUUM operations.
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
#define SQLITE_GET_LOCKPROXYFILE 2
|
||||
@@ -778,6 +782,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_SYNC_OMITTED 8
|
||||
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
|
||||
#define SQLITE_FCNTL_PERSIST_WAL 10
|
||||
#define SQLITE_FCNTL_OVERWRITE 11
|
||||
|
||||
/*
|
||||
** CAPI3REF: Mutex Handle
|
||||
@@ -2845,7 +2850,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** ^The specific value of WHERE-clause [parameter] might influence the
|
||||
** choice of query plan if the parameter is the left-hand side of a [LIKE]
|
||||
** or [GLOB] operator or if the parameter is compared to an indexed column
|
||||
** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled.
|
||||
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
|
||||
** the
|
||||
** </li>
|
||||
** </ol>
|
||||
@@ -3348,6 +3353,12 @@ int sqlite3_step(sqlite3_stmt*);
|
||||
** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
|
||||
** interfaces) then sqlite3_data_count(P) returns 0.
|
||||
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
|
||||
** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
|
||||
** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P)
|
||||
** will return non-zero if previous call to [sqlite3_step](P) returned
|
||||
** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]
|
||||
** where it always returns zero since each step of that multi-step
|
||||
** pragma returns 0 columns of data.
|
||||
**
|
||||
** See also: [sqlite3_column_count()]
|
||||
*/
|
||||
@@ -5810,6 +5821,18 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** the database connection.)^
|
||||
** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
|
||||
** <dd>This parameter returns the number of pager cache hits that have
|
||||
** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
|
||||
** is always 0.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
|
||||
** <dd>This parameter returns the number of pager cache misses that have
|
||||
** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
|
||||
** is always 0.
|
||||
** </dd>
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
|
||||
@@ -5819,7 +5842,9 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
|
||||
#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */
|
||||
#define SQLITE_DBSTATUS_CACHE_HIT 7
|
||||
#define SQLITE_DBSTATUS_CACHE_MISS 8
|
||||
#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */
|
||||
|
||||
|
||||
/*
|
||||
@@ -5873,7 +5898,6 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
||||
** A non-zero value in this counter may indicate an opportunity to
|
||||
** improvement performance by adding permanent indices that do not
|
||||
** need to be reinitialized each time the statement is run.</dd>
|
||||
**
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
|
||||
|
@@ -156,7 +156,7 @@
|
||||
** assertion will be triggered.
|
||||
**
|
||||
** (Historical note: There used to be several other options, but we've
|
||||
** pared it down to just these two.)
|
||||
** pared it down to just these three.)
|
||||
**
|
||||
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
|
||||
** the default.
|
||||
@@ -451,6 +451,18 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
|
||||
*/
|
||||
#define SQLITE_MAX_U32 ((((u64)1)<<32)-1)
|
||||
|
||||
/*
|
||||
** The datatype used to store estimates of the number of rows in a
|
||||
** table or index. This is an unsigned integer type. For 99.9% of
|
||||
** the world, a 32-bit integer is sufficient. But a 64-bit integer
|
||||
** can be used at compile-time if desired.
|
||||
*/
|
||||
#ifdef SQLITE_64BIT_STATS
|
||||
typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */
|
||||
#else
|
||||
typedef u32 tRowcnt; /* 32-bit is the default */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macros to determine whether the machine is big or little endian,
|
||||
** evaluated at runtime.
|
||||
@@ -1292,7 +1304,7 @@ struct Table {
|
||||
Column *aCol; /* Information about each column */
|
||||
Index *pIndex; /* List of SQL indexes on this table. */
|
||||
int tnum; /* Root BTree node for this table (see note above) */
|
||||
unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
Select *pSelect; /* NULL for tables. Points to definition if a view. */
|
||||
u16 nRef; /* Number of pointers to this Table */
|
||||
u8 tabFlags; /* Mask of TF_* values */
|
||||
@@ -1491,7 +1503,7 @@ struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
int nColumn; /* Number of columns in the table used by this index */
|
||||
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
|
||||
tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
int tnum; /* Page containing root of this index in database file */
|
||||
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
|
||||
@@ -1502,7 +1514,11 @@ struct Index {
|
||||
Schema *pSchema; /* Schema containing this index */
|
||||
u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
|
||||
char **azColl; /* Array of collation sequence names for index */
|
||||
IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
tRowcnt avgEq; /* Average nEq value for key values not in aSample */
|
||||
IndexSample *aSample; /* Samples of the left-most key */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1512,10 +1528,14 @@ struct Index {
|
||||
struct IndexSample {
|
||||
union {
|
||||
char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
|
||||
double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */
|
||||
double r; /* Value if eType is SQLITE_FLOAT */
|
||||
i64 i; /* Value if eType is SQLITE_INTEGER */
|
||||
} u;
|
||||
u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
|
||||
u8 nByte; /* Size in byte of text or blob. */
|
||||
int nByte; /* Size in byte of text or blob. */
|
||||
tRowcnt nEq; /* Est. number of rows where the key equals this sample */
|
||||
tRowcnt nLt; /* Est. number of rows where key is less than this sample */
|
||||
tRowcnt nDLt; /* Est. number of distinct keys less than this sample */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1967,10 +1987,10 @@ struct WhereLevel {
|
||||
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
|
||||
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
|
||||
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
|
||||
#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */
|
||||
#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
|
||||
#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
|
||||
#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */
|
||||
#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */
|
||||
#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
|
||||
#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
|
||||
#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */
|
||||
|
||||
/*
|
||||
** The WHERE clause processing routine has two halves. The
|
||||
@@ -2725,6 +2745,7 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
|
||||
#endif
|
||||
|
||||
void sqlite3DropTable(Parse*, SrcList*, int, int);
|
||||
void sqlite3CodeDropTable(Parse*, Table*, int, int);
|
||||
void sqlite3DeleteTable(sqlite3*, Table*);
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
void sqlite3AutoincrementBegin(Parse *pParse);
|
||||
@@ -2981,7 +3002,7 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
|
||||
void sqlite3ValueFree(sqlite3_value*);
|
||||
sqlite3_value *sqlite3ValueNew(sqlite3 *);
|
||||
char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
|
||||
#endif
|
||||
int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
|
||||
|
22
src/status.c
22
src/status.c
@@ -218,6 +218,28 @@ int sqlite3_db_status(
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set *pCurrent to the total cache hits or misses encountered by all
|
||||
** pagers the database handle is connected to. *pHighwater is always set
|
||||
** to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_HIT:
|
||||
case SQLITE_DBSTATUS_CACHE_MISS: {
|
||||
int i;
|
||||
int nRet = 0;
|
||||
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt ){
|
||||
Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
|
||||
sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
|
||||
}
|
||||
}
|
||||
*pHighwater = 0;
|
||||
*pCurrent = nRet;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
@@ -3685,33 +3685,34 @@ int Md5_Register(sqlite3 *db){
|
||||
** the TCL interpreter reads and evaluates that file.
|
||||
*/
|
||||
#if TCLSH==1
|
||||
static char zMainloop[] =
|
||||
"set line {}\n"
|
||||
"while {![eof stdin]} {\n"
|
||||
"if {$line!=\"\"} {\n"
|
||||
"puts -nonewline \"> \"\n"
|
||||
"} else {\n"
|
||||
"puts -nonewline \"% \"\n"
|
||||
"}\n"
|
||||
"flush stdout\n"
|
||||
"append line [gets stdin]\n"
|
||||
"if {[info complete $line]} {\n"
|
||||
"if {[catch {uplevel #0 $line} result]} {\n"
|
||||
"puts stderr \"Error: $result\"\n"
|
||||
"} elseif {$result!=\"\"} {\n"
|
||||
"puts $result\n"
|
||||
static const char *tclsh_main_loop(void){
|
||||
static const char zMainloop[] =
|
||||
"set line {}\n"
|
||||
"while {![eof stdin]} {\n"
|
||||
"if {$line!=\"\"} {\n"
|
||||
"puts -nonewline \"> \"\n"
|
||||
"} else {\n"
|
||||
"puts -nonewline \"% \"\n"
|
||||
"}\n"
|
||||
"flush stdout\n"
|
||||
"append line [gets stdin]\n"
|
||||
"if {[info complete $line]} {\n"
|
||||
"if {[catch {uplevel #0 $line} result]} {\n"
|
||||
"puts stderr \"Error: $result\"\n"
|
||||
"} elseif {$result!=\"\"} {\n"
|
||||
"puts $result\n"
|
||||
"}\n"
|
||||
"set line {}\n"
|
||||
"} else {\n"
|
||||
"append line \\n\n"
|
||||
"}\n"
|
||||
"set line {}\n"
|
||||
"} else {\n"
|
||||
"append line \\n\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
;
|
||||
;
|
||||
return zMainloop;
|
||||
}
|
||||
#endif
|
||||
#if TCLSH==2
|
||||
static char zMainloop[] =
|
||||
#include "spaceanal_tcl.h"
|
||||
;
|
||||
static const char *tclsh_main_loop(void);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
@@ -3795,6 +3796,17 @@ static void init_all(Tcl_Interp *interp){
|
||||
Md5_Init(interp);
|
||||
#endif
|
||||
|
||||
/* Install the [register_dbstat_vtab] command to access the implementation
|
||||
** of virtual table dbstat (source file test_stat.c). This command is
|
||||
** required for testfixture and sqlite3_analyzer, but not by the production
|
||||
** Tcl extension. */
|
||||
#if defined(SQLITE_TEST) || TCLSH==2
|
||||
{
|
||||
extern int SqlitetestStat_Init(Tcl_Interp*);
|
||||
SqlitetestStat_Init(interp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
{
|
||||
extern int Sqliteconfig_Init(Tcl_Interp*);
|
||||
@@ -3824,7 +3836,6 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitetestbackup_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestintarray_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestvfs_Init(Tcl_Interp *);
|
||||
extern int SqlitetestStat_Init(Tcl_Interp*);
|
||||
extern int Sqlitetestrtree_Init(Tcl_Interp*);
|
||||
extern int Sqlitequota_Init(Tcl_Interp*);
|
||||
extern int Sqlitemultiplex_Init(Tcl_Interp*);
|
||||
@@ -3870,7 +3881,6 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitetestbackup_Init(interp);
|
||||
Sqlitetestintarray_Init(interp);
|
||||
Sqlitetestvfs_Init(interp);
|
||||
SqlitetestStat_Init(interp);
|
||||
Sqlitetestrtree_Init(interp);
|
||||
Sqlitequota_Init(interp);
|
||||
Sqlitemultiplex_Init(interp);
|
||||
@@ -3908,12 +3918,13 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
** sqlite3_initialize() is. */
|
||||
sqlite3_shutdown();
|
||||
|
||||
Tcl_FindExecutable(argv[0]);
|
||||
interp = Tcl_CreateInterp();
|
||||
|
||||
#if TCLSH==2
|
||||
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
||||
#endif
|
||||
Tcl_FindExecutable(argv[0]);
|
||||
|
||||
interp = Tcl_CreateInterp();
|
||||
init_all(interp);
|
||||
if( argc>=2 ){
|
||||
int i;
|
||||
@@ -3934,7 +3945,7 @@ int TCLSH_MAIN(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
if( TCLSH==2 || argc<=1 ){
|
||||
Tcl_GlobalEval(interp, zMainloop);
|
||||
Tcl_GlobalEval(interp, tclsh_main_loop());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@@ -55,6 +55,12 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "debug", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
Tcl_SetVar2(interp, "sqlite_options", "direct_read", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "direct_read", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DISABLE_DIRSYNC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "dirsync", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
@@ -436,6 +442,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat2", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
# if defined(__APPLE__)
|
||||
# define SQLITE_ENABLE_LOCKING_STYLE 1
|
||||
@@ -567,12 +579,6 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_PAGECACHE_BLOCKALLOC
|
||||
Tcl_SetVar2(interp, "sqlite_options", "blockalloc", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "blockalloc", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#define LINKVAR(x) { \
|
||||
static const int cv_ ## x = SQLITE_ ## x; \
|
||||
Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
|
||||
|
@@ -1325,11 +1325,13 @@ static int test_db_status(
|
||||
{ "STMT_USED", SQLITE_DBSTATUS_STMT_USED },
|
||||
{ "LOOKASIDE_HIT", SQLITE_DBSTATUS_LOOKASIDE_HIT },
|
||||
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
|
||||
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }
|
||||
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
|
||||
{ "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
|
||||
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS }
|
||||
};
|
||||
Tcl_Obj *pResult;
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB PARAMETER RESETFLAG");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
@@ -18,7 +18,9 @@
|
||||
** for an example implementation.
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
# include "sqliteInt.h"
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
@@ -62,20 +64,11 @@
|
||||
" ncell INTEGER, /* Cells on page (0 for overflow) */" \
|
||||
" payload INTEGER, /* Bytes of payload on this page */" \
|
||||
" unused INTEGER, /* Bytes of unused space on this page */" \
|
||||
" mx_payload INTEGER /* Largest payload size of all cells */" \
|
||||
" mx_payload INTEGER, /* Largest payload size of all cells */" \
|
||||
" pgoffset INTEGER, /* Offset of page in file */" \
|
||||
" pgsize INTEGER /* Size of the page */" \
|
||||
");"
|
||||
|
||||
#if 0
|
||||
#define VTAB_SCHEMA2 \
|
||||
"CREATE TABLE yy( " \
|
||||
" pageno INTEGER, /* B-tree page number */" \
|
||||
" cellno INTEGER, /* Cell number within page */" \
|
||||
" local INTEGER, /* Bytes of content stored locally */" \
|
||||
" payload INTEGER, /* Total cell payload size */" \
|
||||
" novfl INTEGER /* Number of overflow pages */" \
|
||||
");"
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct StatTable StatTable;
|
||||
typedef struct StatCursor StatCursor;
|
||||
@@ -124,6 +117,8 @@ struct StatCursor {
|
||||
int nPayload; /* Value of 'payload' column */
|
||||
int nUnused; /* Value of 'unused' column */
|
||||
int nMxPayload; /* Value of 'mx_payload' column */
|
||||
i64 iOffset; /* Value of 'pgOffset' column */
|
||||
int szPage; /* Value of 'pgSize' column */
|
||||
};
|
||||
|
||||
struct StatTable {
|
||||
@@ -281,6 +276,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
int iOff;
|
||||
int nHdr;
|
||||
int isLeaf;
|
||||
int szPage;
|
||||
|
||||
u8 *aData = sqlite3PagerGetData(p->pPg);
|
||||
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
|
||||
@@ -301,10 +297,11 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
}
|
||||
p->nUnused = nUnused;
|
||||
p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
|
||||
szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
|
||||
if( p->nCell ){
|
||||
int i; /* Used to iterate through cells */
|
||||
int nUsable = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt);
|
||||
int nUsable = szPage - sqlite3BtreeGetReserve(pBt);
|
||||
|
||||
p->aCell = sqlite3_malloc((p->nCell+1) * sizeof(StatCell));
|
||||
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
|
||||
@@ -359,6 +356,32 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on
|
||||
** the current value of pCsr->iPageno.
|
||||
*/
|
||||
static void statSizeAndOffset(StatCursor *pCsr){
|
||||
StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab;
|
||||
Btree *pBt = pTab->db->aDb[0].pBt;
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
sqlite3_file *fd;
|
||||
sqlite3_int64 x[2];
|
||||
|
||||
/* The default page size and offset */
|
||||
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->iOffset = pCsr->szPage * (pCsr->iPageno - 1);
|
||||
|
||||
/* If connected to a ZIPVFS backend, override the page size and
|
||||
** offset with actual values obtained from ZIPVFS.
|
||||
*/
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
x[0] = pCsr->iPageno;
|
||||
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
||||
pCsr->iOffset = x[0];
|
||||
pCsr->szPage = x[1];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a statvfs cursor to the next entry in the file.
|
||||
*/
|
||||
@@ -417,6 +440,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
|
||||
}
|
||||
pCell->iOvfl++;
|
||||
statSizeAndOffset(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( p->iRightChildPg ) break;
|
||||
@@ -454,6 +478,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
|
||||
pCsr->iPageno = p->iPgno;
|
||||
|
||||
statDecodePage(pBt, p);
|
||||
statSizeAndOffset(pCsr);
|
||||
|
||||
switch( p->flags ){
|
||||
case 0x05: /* table internal */
|
||||
@@ -529,6 +554,12 @@ static int statColumn(
|
||||
case 7: /* mx_payload */
|
||||
sqlite3_result_int(ctx, pCsr->nMxPayload);
|
||||
break;
|
||||
case 8: /* pgoffset */
|
||||
sqlite3_result_int64(ctx, pCsr->iOffset);
|
||||
break;
|
||||
case 9: /* pgsize */
|
||||
sqlite3_result_int(ctx, pCsr->szPage);
|
||||
break;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -568,7 +599,7 @@ int sqlite3_dbstat_register(sqlite3 *db){
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#if defined(SQLITE_TEST) || TCLSH==2
|
||||
#include <tcl.h>
|
||||
|
||||
static int test_dbstat(
|
||||
@@ -604,4 +635,4 @@ int SqlitetestStat_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "register_dbstat_vtab", test_dbstat, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
#endif /* if defined(SQLITE_TEST) || TCLSH==2 */
|
||||
|
@@ -464,7 +464,7 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
|
||||
** If a malloc failure occurs, NULL is returned and the db.mallocFailed
|
||||
** flag set.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
|
||||
Mem m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
|
14
src/vacuum.c
14
src/vacuum.c
@@ -45,7 +45,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
|
||||
return sqlite3_errcode(db);
|
||||
}
|
||||
VVA_ONLY( rc = ) sqlite3_step(pStmt);
|
||||
assert( rc!=SQLITE_ROW );
|
||||
assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
|
||||
return vacuumFinalize(db, pStmt, pzErrMsg);
|
||||
}
|
||||
|
||||
@@ -263,13 +263,11 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
);
|
||||
if( rc ) goto end_of_vacuum;
|
||||
|
||||
/* At this point, unless the main db was completely empty, there is now a
|
||||
** transaction open on the vacuum database, but not on the main database.
|
||||
** Open a btree level transaction on the main database. This allows a
|
||||
** call to sqlite3BtreeCopyFile(). The main database btree level
|
||||
** transaction is then committed, so the SQL level never knows it was
|
||||
** opened for writing. This way, the SQL transaction used to create the
|
||||
** temporary database never needs to be committed.
|
||||
/* At this point, there is a write transaction open on both the
|
||||
** vacuum database and the main database. Assuming no error occurs,
|
||||
** both transactions are closed by this block - the main database
|
||||
** transaction by sqlite3BtreeCopyFile() and the other by an explicit
|
||||
** call to sqlite3BtreeCommit().
|
||||
*/
|
||||
{
|
||||
u32 meta;
|
||||
|
@@ -576,8 +576,8 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
|
||||
** the address of the next instruction to be coded.
|
||||
*/
|
||||
void sqlite3VdbeJumpHere(Vdbe *p, int addr){
|
||||
assert( addr>=0 );
|
||||
sqlite3VdbeChangeP2(p, addr, p->nOp);
|
||||
assert( addr>=0 || p->db->mallocFailed );
|
||||
if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp);
|
||||
}
|
||||
|
||||
|
||||
@@ -1143,7 +1143,7 @@ int sqlite3VdbeList(
|
||||
sqlite3 *db = p->db; /* The database connection */
|
||||
int i; /* Loop counter */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */
|
||||
Mem *pMem = &p->aMem[1]; /* First Mem of result set */
|
||||
|
||||
assert( p->explain );
|
||||
assert( p->magic==VDBE_MAGIC_RUN );
|
||||
@@ -1154,6 +1154,7 @@ int sqlite3VdbeList(
|
||||
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
|
||||
*/
|
||||
releaseMemArray(pMem, 8);
|
||||
p->pResultSet = 0;
|
||||
|
||||
if( p->rc==SQLITE_NOMEM ){
|
||||
/* This happens if a malloc() inside a call to sqlite3_column_text() or
|
||||
@@ -1308,6 +1309,7 @@ int sqlite3VdbeList(
|
||||
}
|
||||
|
||||
p->nResColumn = 8 - 4*(p->explain-1);
|
||||
p->pResultSet = &p->aMem[1];
|
||||
p->rc = SQLITE_OK;
|
||||
rc = SQLITE_ROW;
|
||||
}
|
||||
|
@@ -1026,11 +1026,11 @@ int sqlite3ValueFromExpr(
|
||||
}
|
||||
op = pExpr->op;
|
||||
|
||||
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
|
||||
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3.
|
||||
** The ifdef here is to enable us to achieve 100% branch test coverage even
|
||||
** when SQLITE_ENABLE_STAT2 is omitted.
|
||||
** when SQLITE_ENABLE_STAT3 is omitted.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
#else
|
||||
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
||||
|
516
src/where.c
516
src/where.c
@@ -118,21 +118,31 @@ struct WhereTerm {
|
||||
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
|
||||
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
|
||||
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
|
||||
#else
|
||||
# define TERM_VNULL 0x00 /* Disabled if not using stat2 */
|
||||
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** An instance of the following structure holds all information about a
|
||||
** WHERE clause. Mostly this is a container for one or more WhereTerms.
|
||||
**
|
||||
** Explanation of pOuter: For a WHERE clause of the form
|
||||
**
|
||||
** a AND ((b AND c) OR (d AND e)) AND f
|
||||
**
|
||||
** There are separate WhereClause objects for the whole clause and for
|
||||
** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the
|
||||
** subclauses points to the WhereClause object for the whole clause.
|
||||
*/
|
||||
struct WhereClause {
|
||||
Parse *pParse; /* The parser context */
|
||||
WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */
|
||||
Bitmask vmask; /* Bitmask identifying virtual table cursors */
|
||||
WhereClause *pOuter; /* Outer conjunction */
|
||||
u8 op; /* Split operator. TK_AND or TK_OR */
|
||||
u16 wctrlFlags; /* Might include WHERE_AND_ONLY */
|
||||
int nTerm; /* Number of terms */
|
||||
int nSlot; /* Number of entries in a[] */
|
||||
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
|
||||
@@ -261,14 +271,17 @@ struct WhereCost {
|
||||
static void whereClauseInit(
|
||||
WhereClause *pWC, /* The WhereClause to be initialized */
|
||||
Parse *pParse, /* The parsing context */
|
||||
WhereMaskSet *pMaskSet /* Mapping from table cursor numbers to bitmasks */
|
||||
WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */
|
||||
u16 wctrlFlags /* Might include WHERE_AND_ONLY */
|
||||
){
|
||||
pWC->pParse = pParse;
|
||||
pWC->pMaskSet = pMaskSet;
|
||||
pWC->pOuter = 0;
|
||||
pWC->nTerm = 0;
|
||||
pWC->nSlot = ArraySize(pWC->aStatic);
|
||||
pWC->a = pWC->aStatic;
|
||||
pWC->vmask = 0;
|
||||
pWC->wctrlFlags = wctrlFlags;
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
@@ -584,36 +597,38 @@ static WhereTerm *findTerm(
|
||||
int k;
|
||||
assert( iCur>=0 );
|
||||
op &= WO_ALL;
|
||||
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
|
||||
if( pTerm->leftCursor==iCur
|
||||
&& (pTerm->prereqRight & notReady)==0
|
||||
&& pTerm->u.leftColumn==iColumn
|
||||
&& (pTerm->eOperator & op)!=0
|
||||
){
|
||||
if( pIdx && pTerm->eOperator!=WO_ISNULL ){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
CollSeq *pColl;
|
||||
char idxaff;
|
||||
int j;
|
||||
Parse *pParse = pWC->pParse;
|
||||
for(; pWC; pWC=pWC->pOuter){
|
||||
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
|
||||
if( pTerm->leftCursor==iCur
|
||||
&& (pTerm->prereqRight & notReady)==0
|
||||
&& pTerm->u.leftColumn==iColumn
|
||||
&& (pTerm->eOperator & op)!=0
|
||||
){
|
||||
if( pIdx && pTerm->eOperator!=WO_ISNULL ){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
CollSeq *pColl;
|
||||
char idxaff;
|
||||
int j;
|
||||
Parse *pParse = pWC->pParse;
|
||||
|
||||
idxaff = pIdx->pTable->aCol[iColumn].affinity;
|
||||
if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
|
||||
idxaff = pIdx->pTable->aCol[iColumn].affinity;
|
||||
if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
|
||||
|
||||
/* Figure out the collation sequence required from an index for
|
||||
** it to be useful for optimising expression pX. Store this
|
||||
** value in variable pColl.
|
||||
*/
|
||||
assert(pX->pLeft);
|
||||
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
|
||||
assert(pColl || pParse->nErr);
|
||||
/* Figure out the collation sequence required from an index for
|
||||
** it to be useful for optimising expression pX. Store this
|
||||
** value in variable pColl.
|
||||
*/
|
||||
assert(pX->pLeft);
|
||||
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
|
||||
assert(pColl || pParse->nErr);
|
||||
|
||||
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
|
||||
if( NEVER(j>=pIdx->nColumn) ) return 0;
|
||||
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
|
||||
if( NEVER(j>=pIdx->nColumn) ) return 0;
|
||||
}
|
||||
if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
|
||||
}
|
||||
if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
|
||||
return pTerm;
|
||||
}
|
||||
return pTerm;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -690,7 +705,7 @@ static int isLikeOrGlob(
|
||||
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
|
||||
z = (char *)sqlite3_value_text(pVal);
|
||||
}
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */
|
||||
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
|
||||
}else if( op==TK_STRING ){
|
||||
z = pRight->u.zToken;
|
||||
@@ -708,7 +723,7 @@ static int isLikeOrGlob(
|
||||
*ppPrefix = pPrefix;
|
||||
if( op==TK_VARIABLE ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */
|
||||
sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */
|
||||
if( *pisComplete && pRight->u.zToken[1] ){
|
||||
/* If the rhs of the LIKE expression is a variable, and the current
|
||||
** value of the variable means there is no need to invoke the LIKE
|
||||
@@ -877,7 +892,7 @@ static void exprAnalyzeOrTerm(
|
||||
if( pOrInfo==0 ) return;
|
||||
pTerm->wtFlags |= TERM_ORINFO;
|
||||
pOrWc = &pOrInfo->wc;
|
||||
whereClauseInit(pOrWc, pWC->pParse, pMaskSet);
|
||||
whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags);
|
||||
whereSplit(pOrWc, pExpr, TK_OR);
|
||||
exprAnalyzeAll(pSrc, pOrWc);
|
||||
if( db->mallocFailed ) return;
|
||||
@@ -904,9 +919,10 @@ static void exprAnalyzeOrTerm(
|
||||
pOrTerm->wtFlags |= TERM_ANDINFO;
|
||||
pOrTerm->eOperator = WO_AND;
|
||||
pAndWC = &pAndInfo->wc;
|
||||
whereClauseInit(pAndWC, pWC->pParse, pMaskSet);
|
||||
whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags);
|
||||
whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
|
||||
exprAnalyzeAll(pSrc, pAndWC);
|
||||
pAndWC->pOuter = pWC;
|
||||
testcase( db->mallocFailed );
|
||||
if( !db->mallocFailed ){
|
||||
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
|
||||
@@ -1340,8 +1356,8 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
/* When sqlite_stat2 histogram data is available an operator of the
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/* When sqlite_stat3 histogram data is available an operator of the
|
||||
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
||||
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
|
||||
** virtual term of that form.
|
||||
@@ -1379,7 +1395,7 @@ static void exprAnalyze(
|
||||
pNewTerm->prereqAll = pTerm->prereqAll;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT2 */
|
||||
#endif /* SQLITE_ENABLE_STAT */
|
||||
|
||||
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
|
||||
** an index for tables to the left of the join.
|
||||
@@ -1801,11 +1817,14 @@ static void bestOrClauseIndex(
|
||||
WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
|
||||
WhereTerm *pTerm; /* A single term of the WHERE clause */
|
||||
|
||||
/* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses
|
||||
** are used */
|
||||
/* The OR-clause optimization is disallowed if the INDEXED BY or
|
||||
** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */
|
||||
if( pSrc->notIndexed || pSrc->pIndex!=0 ){
|
||||
return;
|
||||
}
|
||||
if( pWC->wctrlFlags & WHERE_AND_ONLY ){
|
||||
return;
|
||||
}
|
||||
|
||||
/* Search the WHERE clause terms for a usable WO_OR term. */
|
||||
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
||||
@@ -1833,6 +1852,7 @@ static void bestOrClauseIndex(
|
||||
WhereClause tempWC;
|
||||
tempWC.pParse = pWC->pParse;
|
||||
tempWC.pMaskSet = pWC->pMaskSet;
|
||||
tempWC.pOuter = pWC;
|
||||
tempWC.op = TK_AND;
|
||||
tempWC.a = pOrTerm;
|
||||
tempWC.nTerm = 1;
|
||||
@@ -2427,67 +2447,86 @@ static void bestVirtualIndex(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Argument pIdx is a pointer to an index structure that has an array of
|
||||
** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column
|
||||
** stored in Index.aSample. These samples divide the domain of values stored
|
||||
** the index into (SQLITE_INDEX_SAMPLES+1) regions.
|
||||
** Region 0 contains all values less than the first sample value. Region
|
||||
** 1 contains values between the first and second samples. Region 2 contains
|
||||
** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES
|
||||
** contains values larger than the last sample.
|
||||
** Estimate the location of a particular key among all keys in an
|
||||
** index. Store the results in aStat as follows:
|
||||
**
|
||||
** If the index contains many duplicates of a single value, then it is
|
||||
** possible that two or more adjacent samples can hold the same value.
|
||||
** When that is the case, the smallest possible region code is returned
|
||||
** when roundUp is false and the largest possible region code is returned
|
||||
** when roundUp is true.
|
||||
** aStat[0] Est. number of rows less than pVal
|
||||
** aStat[1] Est. number of rows equal to pVal
|
||||
**
|
||||
** If successful, this function determines which of the regions value
|
||||
** pVal lies in, sets *piRegion to the region index (a value between 0
|
||||
** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK.
|
||||
** Or, if an OOM occurs while converting text values between encodings,
|
||||
** SQLITE_NOMEM is returned and *piRegion is undefined.
|
||||
** Return SQLITE_OK on success.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
static int whereRangeRegion(
|
||||
static int whereKeyStats(
|
||||
Parse *pParse, /* Database connection */
|
||||
Index *pIdx, /* Index to consider domain of */
|
||||
sqlite3_value *pVal, /* Value to consider */
|
||||
int roundUp, /* Return largest valid region if true */
|
||||
int *piRegion /* OUT: Region of domain in which value lies */
|
||||
int roundUp, /* Round up if true. Round down if false */
|
||||
tRowcnt *aStat /* OUT: stats written here */
|
||||
){
|
||||
assert( roundUp==0 || roundUp==1 );
|
||||
if( ALWAYS(pVal) ){
|
||||
IndexSample *aSample = pIdx->aSample;
|
||||
int i = 0;
|
||||
int eType = sqlite3_value_type(pVal);
|
||||
tRowcnt n;
|
||||
IndexSample *aSample;
|
||||
int i, eType;
|
||||
int isEq = 0;
|
||||
i64 v;
|
||||
double r, rS;
|
||||
|
||||
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
double r = sqlite3_value_double(pVal);
|
||||
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
|
||||
if( aSample[i].eType==SQLITE_NULL ) continue;
|
||||
if( aSample[i].eType>=SQLITE_TEXT ) break;
|
||||
if( roundUp ){
|
||||
if( aSample[i].u.r>r ) break;
|
||||
}else{
|
||||
if( aSample[i].u.r>=r ) break;
|
||||
assert( roundUp==0 || roundUp==1 );
|
||||
assert( pIdx->nSample>0 );
|
||||
if( pVal==0 ) return SQLITE_ERROR;
|
||||
n = pIdx->aiRowEst[0];
|
||||
aSample = pIdx->aSample;
|
||||
i = 0;
|
||||
eType = sqlite3_value_type(pVal);
|
||||
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
v = sqlite3_value_int64(pVal);
|
||||
r = (i64)v;
|
||||
for(i=0; i<pIdx->nSample; i++){
|
||||
if( aSample[i].eType==SQLITE_NULL ) continue;
|
||||
if( aSample[i].eType>=SQLITE_TEXT ) break;
|
||||
if( aSample[i].eType==SQLITE_INTEGER ){
|
||||
if( aSample[i].u.i>=v ){
|
||||
isEq = aSample[i].u.i==v;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
assert( aSample[i].eType==SQLITE_FLOAT );
|
||||
if( aSample[i].u.r>=r ){
|
||||
isEq = aSample[i].u.r==r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if( eType==SQLITE_NULL ){
|
||||
i = 0;
|
||||
if( roundUp ){
|
||||
while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++;
|
||||
}
|
||||
}else if( eType==SQLITE_FLOAT ){
|
||||
r = sqlite3_value_double(pVal);
|
||||
for(i=0; i<pIdx->nSample; i++){
|
||||
if( aSample[i].eType==SQLITE_NULL ) continue;
|
||||
if( aSample[i].eType>=SQLITE_TEXT ) break;
|
||||
if( aSample[i].eType==SQLITE_FLOAT ){
|
||||
rS = aSample[i].u.r;
|
||||
}else{
|
||||
rS = aSample[i].u.i;
|
||||
}
|
||||
}else{
|
||||
if( rS>=r ){
|
||||
isEq = rS==r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if( eType==SQLITE_NULL ){
|
||||
i = 0;
|
||||
if( aSample[0].eType==SQLITE_NULL ) isEq = 1;
|
||||
}else{
|
||||
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
||||
for(i=0; i<pIdx->nSample; i++){
|
||||
if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i<pIdx->nSample ){
|
||||
sqlite3 *db = pParse->db;
|
||||
CollSeq *pColl;
|
||||
const u8 *z;
|
||||
int n;
|
||||
|
||||
/* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */
|
||||
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
||||
|
||||
if( eType==SQLITE_BLOB ){
|
||||
z = (const u8 *)sqlite3_value_blob(pVal);
|
||||
pColl = db->pDfltColl;
|
||||
@@ -2507,11 +2546,11 @@ static int whereRangeRegion(
|
||||
}
|
||||
n = sqlite3ValueBytes(pVal, pColl->enc);
|
||||
|
||||
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
|
||||
for(; i<pIdx->nSample; i++){
|
||||
int c;
|
||||
int eSampletype = aSample[i].eType;
|
||||
if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
|
||||
if( (eSampletype!=eType) ) break;
|
||||
if( eSampletype<eType ) continue;
|
||||
if( eSampletype!=eType ) break;
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( pColl->enc!=SQLITE_UTF8 ){
|
||||
int nSample;
|
||||
@@ -2529,16 +2568,47 @@ static int whereRangeRegion(
|
||||
{
|
||||
c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
|
||||
}
|
||||
if( c-roundUp>=0 ) break;
|
||||
if( c>=0 ){
|
||||
if( c==0 ) isEq = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert( i>=0 && i<=SQLITE_INDEX_SAMPLES );
|
||||
*piRegion = i;
|
||||
/* At this point, aSample[i] is the first sample that is greater than
|
||||
** or equal to pVal. Or if i==pIdx->nSample, then all samples are less
|
||||
** than pVal. If aSample[i]==pVal, then isEq==1.
|
||||
*/
|
||||
if( isEq ){
|
||||
assert( i<pIdx->nSample );
|
||||
aStat[0] = aSample[i].nLt;
|
||||
aStat[1] = aSample[i].nEq;
|
||||
}else{
|
||||
tRowcnt iLower, iUpper, iGap;
|
||||
if( i==0 ){
|
||||
iLower = 0;
|
||||
iUpper = aSample[0].nLt;
|
||||
}else{
|
||||
iUpper = i>=pIdx->nSample ? n : aSample[i].nLt;
|
||||
iLower = aSample[i-1].nEq + aSample[i-1].nLt;
|
||||
}
|
||||
aStat[1] = pIdx->avgEq;
|
||||
if( iLower>=iUpper ){
|
||||
iGap = 0;
|
||||
}else{
|
||||
iGap = iUpper - iLower;
|
||||
}
|
||||
if( roundUp ){
|
||||
iGap = (iGap*2)/3;
|
||||
}else{
|
||||
iGap = iGap/3;
|
||||
}
|
||||
aStat[0] = iLower + iGap;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* #ifdef SQLITE_ENABLE_STAT2 */
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/*
|
||||
** If expression pExpr represents a literal value, set *pp to point to
|
||||
@@ -2556,7 +2626,7 @@ static int whereRangeRegion(
|
||||
**
|
||||
** If an error occurs, return an error code. Otherwise, SQLITE_OK.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
static int valueFromExpr(
|
||||
Parse *pParse,
|
||||
Expr *pExpr,
|
||||
@@ -2567,7 +2637,7 @@ static int valueFromExpr(
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
|
||||
){
|
||||
int iVar = pExpr->iColumn;
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */
|
||||
*pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -2604,17 +2674,15 @@ static int valueFromExpr(
|
||||
**
|
||||
** then nEq should be passed 0.
|
||||
**
|
||||
** The returned value is an integer between 1 and 100, inclusive. A return
|
||||
** value of 1 indicates that the proposed range scan is expected to visit
|
||||
** approximately 1/100th (1%) of the rows selected by the nEq equality
|
||||
** constraints (if any). A return value of 100 indicates that it is expected
|
||||
** that the range scan will visit every row (100%) selected by the equality
|
||||
** constraints.
|
||||
** The returned value is an integer divisor to reduce the estimated
|
||||
** search space. A return value of 1 means that range constraints are
|
||||
** no help at all. A return value of 2 means range constraints are
|
||||
** expected to reduce the search space by half. And so forth...
|
||||
**
|
||||
** In the absence of sqlite_stat2 ANALYZE data, each range inequality
|
||||
** reduces the search space by 3/4ths. Hence a single constraint (x>?)
|
||||
** results in a return of 25 and a range constraint (x>? AND x<?) results
|
||||
** in a return of 6.
|
||||
** In the absence of sqlite_stat3 ANALYZE data, each range inequality
|
||||
** reduces the search space by a factor of 4. Hence a single constraint (x>?)
|
||||
** results in a return of 4 and a range constraint (x>? AND x<?) results
|
||||
** in a return of 16.
|
||||
*/
|
||||
static int whereRangeScanEst(
|
||||
Parse *pParse, /* Parsing & code generating context */
|
||||
@@ -2622,84 +2690,72 @@ static int whereRangeScanEst(
|
||||
int nEq, /* index into p->aCol[] of the range-compared column */
|
||||
WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
|
||||
WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
|
||||
int *piEst /* OUT: Return value */
|
||||
double *pRangeDiv /* OUT: Reduce search space by this divisor */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
|
||||
if( nEq==0 && p->aSample ){
|
||||
sqlite3_value *pLowerVal = 0;
|
||||
sqlite3_value *pUpperVal = 0;
|
||||
int iEst;
|
||||
int iLower = 0;
|
||||
int iUpper = SQLITE_INDEX_SAMPLES;
|
||||
int roundUpUpper = 0;
|
||||
int roundUpLower = 0;
|
||||
if( nEq==0 && p->nSample ){
|
||||
sqlite3_value *pRangeVal;
|
||||
tRowcnt iLower = 0;
|
||||
tRowcnt iUpper = p->aiRowEst[0];
|
||||
tRowcnt a[2];
|
||||
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
|
||||
|
||||
if( pLower ){
|
||||
Expr *pExpr = pLower->pExpr->pRight;
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal);
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
|
||||
assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
|
||||
roundUpLower = (pLower->eOperator==WO_GT) ?1:0;
|
||||
if( rc==SQLITE_OK
|
||||
&& whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
|
||||
){
|
||||
iLower = a[0];
|
||||
if( pLower->eOperator==WO_GT ) iLower += a[1];
|
||||
}
|
||||
sqlite3ValueFree(pRangeVal);
|
||||
}
|
||||
if( rc==SQLITE_OK && pUpper ){
|
||||
Expr *pExpr = pUpper->pExpr->pRight;
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal);
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
|
||||
assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
|
||||
roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){
|
||||
sqlite3ValueFree(pLowerVal);
|
||||
sqlite3ValueFree(pUpperVal);
|
||||
goto range_est_fallback;
|
||||
}else if( pLowerVal==0 ){
|
||||
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
|
||||
if( pLower ) iLower = iUpper/2;
|
||||
}else if( pUpperVal==0 ){
|
||||
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
|
||||
if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2;
|
||||
}else{
|
||||
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
|
||||
if( rc==SQLITE_OK
|
||||
&& whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
|
||||
){
|
||||
iUpper = a[0];
|
||||
if( pUpper->eOperator==WO_LE ) iUpper += a[1];
|
||||
}
|
||||
sqlite3ValueFree(pRangeVal);
|
||||
}
|
||||
WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper));
|
||||
|
||||
iEst = iUpper - iLower;
|
||||
testcase( iEst==SQLITE_INDEX_SAMPLES );
|
||||
assert( iEst<=SQLITE_INDEX_SAMPLES );
|
||||
if( iEst<1 ){
|
||||
*piEst = 50/SQLITE_INDEX_SAMPLES;
|
||||
}else{
|
||||
*piEst = (iEst*100)/SQLITE_INDEX_SAMPLES;
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iUpper<=iLower ){
|
||||
*pRangeDiv = (double)p->aiRowEst[0];
|
||||
}else{
|
||||
*pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower);
|
||||
}
|
||||
WHERETRACE(("range scan regions: %u..%u div=%g\n",
|
||||
(u32)iLower, (u32)iUpper, *pRangeDiv));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3ValueFree(pLowerVal);
|
||||
sqlite3ValueFree(pUpperVal);
|
||||
return rc;
|
||||
}
|
||||
range_est_fallback:
|
||||
#else
|
||||
UNUSED_PARAMETER(pParse);
|
||||
UNUSED_PARAMETER(p);
|
||||
UNUSED_PARAMETER(nEq);
|
||||
#endif
|
||||
assert( pLower || pUpper );
|
||||
*piEst = 100;
|
||||
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4;
|
||||
if( pUpper ) *piEst /= 4;
|
||||
*pRangeDiv = (double)1;
|
||||
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4;
|
||||
if( pUpper ) *pRangeDiv *= (double)4;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Estimate the number of rows that will be returned based on
|
||||
** an equality constraint x=VALUE and where that VALUE occurs in
|
||||
** the histogram data. This only works when x is the left-most
|
||||
** column of an index and sqlite_stat2 histogram data is available
|
||||
** column of an index and sqlite_stat3 histogram data is available
|
||||
** for that index. When pExpr==NULL that means the constraint is
|
||||
** "x IS NULL" instead of "x=VALUE".
|
||||
**
|
||||
@@ -2719,12 +2775,12 @@ static int whereEqualScanEst(
|
||||
double *pnRow /* Write the revised row estimate here */
|
||||
){
|
||||
sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
|
||||
int iLower, iUpper; /* Range of histogram regions containing pRhs */
|
||||
u8 aff; /* Column affinity */
|
||||
int rc; /* Subfunction return code */
|
||||
double nRowEst; /* New estimate of the number of rows */
|
||||
tRowcnt a[2]; /* Statistics */
|
||||
|
||||
assert( p->aSample!=0 );
|
||||
assert( p->nSample>0 );
|
||||
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
|
||||
if( pExpr ){
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
|
||||
@@ -2733,26 +2789,18 @@ static int whereEqualScanEst(
|
||||
pRhs = sqlite3ValueNew(pParse->db);
|
||||
}
|
||||
if( pRhs==0 ) return SQLITE_NOTFOUND;
|
||||
rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower);
|
||||
if( rc ) goto whereEqualScanEst_cancel;
|
||||
rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper);
|
||||
if( rc ) goto whereEqualScanEst_cancel;
|
||||
WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper));
|
||||
if( iLower>=iUpper ){
|
||||
nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2);
|
||||
if( nRowEst<*pnRow ) *pnRow = nRowEst;
|
||||
}else{
|
||||
nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES;
|
||||
*pnRow = nRowEst;
|
||||
rc = whereKeyStats(pParse, p, pRhs, 0, a);
|
||||
if( rc==SQLITE_OK ){
|
||||
WHERETRACE(("equality scan regions: %d\n", (int)a[1]));
|
||||
*pnRow = a[1];
|
||||
}
|
||||
|
||||
whereEqualScanEst_cancel:
|
||||
sqlite3ValueFree(pRhs);
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_STAT2) */
|
||||
#endif /* defined(SQLITE_ENABLE_STAT3) */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/*
|
||||
** Estimate the number of rows that will be returned based on
|
||||
** an IN constraint where the right-hand side of the IN operator
|
||||
@@ -2775,60 +2823,25 @@ static int whereInScanEst(
|
||||
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
|
||||
double *pnRow /* Write the revised row estimate here */
|
||||
){
|
||||
sqlite3_value *pVal = 0; /* One value from list */
|
||||
int iLower, iUpper; /* Range of histogram regions containing pRhs */
|
||||
u8 aff; /* Column affinity */
|
||||
int rc = SQLITE_OK; /* Subfunction return code */
|
||||
double nRowEst; /* New estimate of the number of rows */
|
||||
int nSpan = 0; /* Number of histogram regions spanned */
|
||||
int nSingle = 0; /* Histogram regions hit by a single value */
|
||||
int nNotFound = 0; /* Count of values that are not constants */
|
||||
int i; /* Loop counter */
|
||||
u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */
|
||||
u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */
|
||||
int rc = SQLITE_OK; /* Subfunction return code */
|
||||
double nEst; /* Number of rows for a single term */
|
||||
double nRowEst = (double)0; /* New estimate of the number of rows */
|
||||
int i; /* Loop counter */
|
||||
|
||||
assert( p->aSample!=0 );
|
||||
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
|
||||
memset(aSpan, 0, sizeof(aSpan));
|
||||
memset(aSingle, 0, sizeof(aSingle));
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3ValueFree(pVal);
|
||||
rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal);
|
||||
if( rc ) break;
|
||||
if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
|
||||
nNotFound++;
|
||||
continue;
|
||||
}
|
||||
rc = whereRangeRegion(pParse, p, pVal, 0, &iLower);
|
||||
if( rc ) break;
|
||||
rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper);
|
||||
if( rc ) break;
|
||||
if( iLower>=iUpper ){
|
||||
aSingle[iLower] = 1;
|
||||
}else{
|
||||
assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES );
|
||||
while( iLower<iUpper ) aSpan[iLower++] = 1;
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
|
||||
nEst = p->aiRowEst[0];
|
||||
rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst);
|
||||
nRowEst += nEst;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){
|
||||
if( aSpan[i] ){
|
||||
nSpan++;
|
||||
}else if( aSingle[i] ){
|
||||
nSingle++;
|
||||
}
|
||||
}
|
||||
nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES)
|
||||
+ nNotFound*p->aiRowEst[1];
|
||||
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
|
||||
*pnRow = nRowEst;
|
||||
WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n",
|
||||
nSpan, nSingle, nNotFound, nRowEst));
|
||||
WHERETRACE(("IN row estimate: est=%g\n", nRowEst));
|
||||
}
|
||||
sqlite3ValueFree(pVal);
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_STAT2) */
|
||||
#endif /* defined(SQLITE_ENABLE_STAT3) */
|
||||
|
||||
|
||||
/*
|
||||
@@ -2875,7 +2888,7 @@ static void bestBtreeIndex(
|
||||
int eqTermMask; /* Current mask of valid equality operators */
|
||||
int idxEqTermMask; /* Index mask of valid equality operators */
|
||||
Index sPk; /* A fake index object for the primary key */
|
||||
unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
|
||||
tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
|
||||
int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
|
||||
int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */
|
||||
|
||||
@@ -2930,10 +2943,10 @@ static void bestBtreeIndex(
|
||||
/* Loop over all indices looking for the best one to use
|
||||
*/
|
||||
for(; pProbe; pIdx=pProbe=pProbe->pNext){
|
||||
const unsigned int * const aiRowEst = pProbe->aiRowEst;
|
||||
const tRowcnt * const aiRowEst = pProbe->aiRowEst;
|
||||
double cost; /* Cost of using pProbe */
|
||||
double nRow; /* Estimated number of rows in result set */
|
||||
double log10N; /* base-10 logarithm of nRow (inexact) */
|
||||
double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
|
||||
int rev; /* True to scan in reverse order */
|
||||
int wsFlags = 0;
|
||||
Bitmask used = 0;
|
||||
@@ -2973,14 +2986,12 @@ static void bestBtreeIndex(
|
||||
** IN operator must be a SELECT, not a value list, for this variable
|
||||
** to be true.
|
||||
**
|
||||
** estBound:
|
||||
** An estimate on the amount of the table that must be searched. A
|
||||
** value of 100 means the entire table is searched. Range constraints
|
||||
** might reduce this to a value less than 100 to indicate that only
|
||||
** a fraction of the table needs searching. In the absence of
|
||||
** sqlite_stat2 ANALYZE data, a single inequality reduces the search
|
||||
** space to 1/4rd its original size. So an x>? constraint reduces
|
||||
** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6.
|
||||
** rangeDiv:
|
||||
** An estimate of a divisor by which to reduce the search space due
|
||||
** to inequality constraints. In the absence of sqlite_stat3 ANALYZE
|
||||
** data, a single inequality reduces the search space to 1/4rd its
|
||||
** original size (rangeDiv==4). Two inequalities reduce the search
|
||||
** space to 1/16th of its original size (rangeDiv==16).
|
||||
**
|
||||
** bSort:
|
||||
** Boolean. True if there is an ORDER BY clause that will require an
|
||||
@@ -3005,13 +3016,13 @@ static void bestBtreeIndex(
|
||||
int nEq; /* Number of == or IN terms matching index */
|
||||
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
|
||||
int nInMul = 1; /* Number of distinct equalities to lookup */
|
||||
int estBound = 100; /* Estimated reduction in search space */
|
||||
double rangeDiv = (double)1; /* Estimated reduction in search space */
|
||||
int nBound = 0; /* Number of range constraints seen */
|
||||
int bSort = !!pOrderBy; /* True if external sort required */
|
||||
int bDist = !!pDistinct; /* True if index cannot help with DISTINCT */
|
||||
int bLookup = 0; /* True if not a covering index */
|
||||
WhereTerm *pTerm; /* A single term of the WHERE clause */
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
WhereTerm *pFirstTerm = 0; /* First term matching the index */
|
||||
#endif
|
||||
|
||||
@@ -3021,6 +3032,7 @@ static void bestBtreeIndex(
|
||||
pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx);
|
||||
if( pTerm==0 ) break;
|
||||
wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
|
||||
testcase( pTerm->pWC!=pWC );
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
wsFlags |= WHERE_COLUMN_IN;
|
||||
@@ -3035,28 +3047,30 @@ static void bestBtreeIndex(
|
||||
}else if( pTerm->eOperator & WO_ISNULL ){
|
||||
wsFlags |= WHERE_COLUMN_NULL;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
|
||||
#endif
|
||||
used |= pTerm->prereqRight;
|
||||
}
|
||||
|
||||
/* Determine the value of estBound. */
|
||||
/* Determine the value of rangeDiv */
|
||||
if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){
|
||||
int j = pProbe->aiColumn[nEq];
|
||||
if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
|
||||
WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
|
||||
WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx);
|
||||
whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound);
|
||||
whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv);
|
||||
if( pTop ){
|
||||
nBound = 1;
|
||||
wsFlags |= WHERE_TOP_LIMIT;
|
||||
used |= pTop->prereqRight;
|
||||
testcase( pTop->pWC!=pWC );
|
||||
}
|
||||
if( pBtm ){
|
||||
nBound++;
|
||||
wsFlags |= WHERE_BTM_LIMIT;
|
||||
used |= pBtm->prereqRight;
|
||||
testcase( pBtm->pWC!=pWC );
|
||||
}
|
||||
wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
|
||||
}
|
||||
@@ -3119,7 +3133,7 @@ static void bestBtreeIndex(
|
||||
nInMul = (int)(nRow / aiRowEst[nEq]);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT2
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
/* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
|
||||
** and we do not think that values of x are unique and if histogram
|
||||
** data is available for column x, then it might be possible
|
||||
@@ -3127,20 +3141,22 @@ static void bestBtreeIndex(
|
||||
** VALUE and how common that value is according to the histogram.
|
||||
*/
|
||||
if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
|
||||
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
|
||||
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
|
||||
testcase( pFirstTerm->eOperator==WO_EQ );
|
||||
testcase( pFirstTerm->eOperator==WO_ISNULL );
|
||||
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
|
||||
}else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){
|
||||
}else if( bInEst==0 ){
|
||||
assert( pFirstTerm->eOperator==WO_IN );
|
||||
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT2 */
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/* Adjust the number of output rows and downward to reflect rows
|
||||
** that are excluded by range constraints.
|
||||
*/
|
||||
nRow = (nRow * (double)estBound) / (double)100;
|
||||
nRow = nRow/rangeDiv;
|
||||
if( nRow<1 ) nRow = 1;
|
||||
|
||||
/* Experiments run on real SQLite databases show that the time needed
|
||||
@@ -3269,10 +3285,10 @@ static void bestBtreeIndex(
|
||||
|
||||
|
||||
WHERETRACE((
|
||||
"%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
|
||||
"%s(%s): nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
|
||||
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n",
|
||||
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
|
||||
nEq, nInMul, estBound, bSort, bLookup, wsFlags,
|
||||
nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags,
|
||||
notReady, log10N, nRow, cost, used
|
||||
));
|
||||
|
||||
@@ -3776,7 +3792,8 @@ static Bitmask codeOneLoopStart(
|
||||
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
|
||||
int iLevel, /* Which level of pWInfo->a[] should be coded */
|
||||
u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
|
||||
Bitmask notReady /* Which tables are currently available */
|
||||
Bitmask notReady, /* Which tables are currently available */
|
||||
Expr *pWhere /* Complete WHERE clause */
|
||||
){
|
||||
int j, k; /* Loop counters */
|
||||
int iCur; /* The VDBE cursor for the table */
|
||||
@@ -4258,7 +4275,8 @@ static Bitmask codeOneLoopStart(
|
||||
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
|
||||
int iRetInit; /* Address of regReturn init */
|
||||
int untestedTerms = 0; /* Some terms not completely tested */
|
||||
int ii;
|
||||
int ii; /* Loop counter */
|
||||
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
|
||||
|
||||
pTerm = pLevel->plan.u.pTerm;
|
||||
assert( pTerm!=0 );
|
||||
@@ -4308,13 +4326,28 @@ static Bitmask codeOneLoopStart(
|
||||
}
|
||||
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
|
||||
|
||||
/* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
|
||||
** Then for every term xN, evaluate as the subexpression: xN AND z
|
||||
** That way, terms in y that are factored into the disjunction will
|
||||
** be picked up by the recursive calls to sqlite3WhereBegin() below.
|
||||
*/
|
||||
if( pWC->nTerm>1 ){
|
||||
pAndExpr = sqlite3ExprAlloc(pParse->db, TK_AND, 0, 0);
|
||||
pAndExpr->pRight = pWhere;
|
||||
}
|
||||
|
||||
for(ii=0; ii<pOrWc->nTerm; ii++){
|
||||
WhereTerm *pOrTerm = &pOrWc->a[ii];
|
||||
if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
|
||||
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
|
||||
Expr *pOrExpr = pOrTerm->pExpr;
|
||||
if( pAndExpr ){
|
||||
pAndExpr->pLeft = pOrExpr;
|
||||
pOrExpr = pAndExpr;
|
||||
}
|
||||
/* Loop through table entries that match term pOrTerm. */
|
||||
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
|
||||
WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
|
||||
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
|
||||
WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
|
||||
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
|
||||
if( pSubWInfo ){
|
||||
explainOneScan(
|
||||
@@ -4342,6 +4375,7 @@ static Bitmask codeOneLoopStart(
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(pParse->db, pAndExpr);
|
||||
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
|
||||
sqlite3VdbeResolveLabel(v, iLoopBody);
|
||||
@@ -4623,7 +4657,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** subexpression is separated by an AND operator.
|
||||
*/
|
||||
initMaskSet(pMaskSet);
|
||||
whereClauseInit(pWC, pParse, pMaskSet);
|
||||
whereClauseInit(pWC, pParse, pMaskSet, wctrlFlags);
|
||||
sqlite3ExprCodeConstants(pParse, pWhere);
|
||||
whereSplit(pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
|
||||
|
||||
@@ -4951,7 +4985,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}else
|
||||
#endif
|
||||
if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN)==0 ){
|
||||
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
|
||||
int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
|
||||
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
|
||||
testcase( pTab->nCol==BMS-1 );
|
||||
@@ -4996,7 +5030,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
for(i=0; i<nTabList; i++){
|
||||
pLevel = &pWInfo->a[i];
|
||||
explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags);
|
||||
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
|
||||
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady, pWhere);
|
||||
pWInfo->iContinue = pLevel->addrCont;
|
||||
}
|
||||
|
||||
@@ -5131,7 +5165,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
assert( pTab!=0 );
|
||||
if( (pTab->tabFlags & TF_Ephemeral)==0
|
||||
&& pTab->pSelect==0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0
|
||||
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
|
||||
){
|
||||
int ws = pLevel->plan.wsFlags;
|
||||
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
|
||||
|
@@ -847,6 +847,7 @@ set system_table_list {1 sqlite_master}
|
||||
catchsql ANALYZE
|
||||
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
|
||||
ifcapable stat2 { lappend system_table_list 3 sqlite_stat2 }
|
||||
ifcapable stat3 { lappend system_table_list 4 sqlite_stat3 }
|
||||
|
||||
foreach {tn tbl} $system_table_list {
|
||||
do_test alter-15.$tn.1 {
|
||||
|
@@ -288,7 +288,7 @@ do_test analyze-4.3 {
|
||||
} {}
|
||||
|
||||
# Verify that DROP TABLE and DROP INDEX remove entries from the
|
||||
# sqlite_stat1 and sqlite_stat2 tables.
|
||||
# sqlite_stat1 and sqlite_stat3 tables.
|
||||
#
|
||||
do_test analyze-5.0 {
|
||||
execsql {
|
||||
@@ -306,11 +306,11 @@ do_test analyze-5.0 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat2 {
|
||||
ifcapable stat3 {
|
||||
do_test analyze-5.1 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat2 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat2 ORDER BY 1;
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
@@ -321,11 +321,11 @@ do_test analyze-5.2 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat2 {
|
||||
ifcapable stat3 {
|
||||
do_test analyze-5.3 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat2 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat2 ORDER BY 1;
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
@@ -336,11 +336,11 @@ do_test analyze-5.4 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
ifcapable stat2 {
|
||||
ifcapable stat3 {
|
||||
do_test analyze-5.5 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat2 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat2 ORDER BY 1;
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -97,10 +97,10 @@ do_test analyze3-1.1.1 {
|
||||
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~100 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~179 rows)}}
|
||||
do_eqp_test analyze3-1.1.3 {
|
||||
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SCAN TABLE t1 (~111 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~959 rows)}}
|
||||
|
||||
do_test analyze3-1.1.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
|
||||
@@ -117,17 +117,17 @@ do_test analyze3-1.1.6 {
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.1.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.1.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.1.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
|
||||
|
||||
# The following tests are similar to the block above. The difference is
|
||||
@@ -146,10 +146,10 @@ do_test analyze3-1.2.1 {
|
||||
} {}
|
||||
do_eqp_test analyze3-1.2.2 {
|
||||
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~200 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~196 rows)}}
|
||||
do_eqp_test analyze3-1.2.3 {
|
||||
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
|
||||
} {0 0 0 {SCAN TABLE t2 (~111 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~968 rows)}}
|
||||
do_test analyze3-1.2.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
|
||||
} {161 0 4760}
|
||||
@@ -165,17 +165,17 @@ do_test analyze3-1.2.6 {
|
||||
} {161 0 integer integer 4760}
|
||||
do_test analyze3-1.2.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
|
||||
} {999 999 490555}
|
||||
} {1981 0 490555}
|
||||
do_test analyze3-1.2.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "99" 0 end]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {999 999 text text 490555}
|
||||
} {1981 0 text text 490555}
|
||||
do_test analyze3-1.2.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(99)]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {999 999 integer integer 490555}
|
||||
} {1981 0 integer integer 490555}
|
||||
|
||||
# Same tests a third time. This time, column x has INTEGER affinity and
|
||||
# is not the leftmost column of the table. This triggered a bug causing
|
||||
@@ -193,10 +193,10 @@ do_test analyze3-1.3.1 {
|
||||
} {}
|
||||
do_eqp_test analyze3-1.3.2 {
|
||||
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~100 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~156 rows)}}
|
||||
do_eqp_test analyze3-1.3.3 {
|
||||
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SCAN TABLE t3 (~111 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~989 rows)}}
|
||||
|
||||
do_test analyze3-1.3.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
|
||||
@@ -213,17 +213,17 @@ do_test analyze3-1.3.6 {
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.3.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.3.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
do_test analyze3-1.3.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
} {2000 0 499500}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the values of bound SQL variables may be used for the LIKE
|
||||
@@ -248,7 +248,7 @@ do_test analyze3-2.1 {
|
||||
} {}
|
||||
do_eqp_test analyze3-2.2 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~30000 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~31250 rows)}}
|
||||
do_eqp_test analyze3-2.3 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE '%a'
|
||||
} {0 0 0 {SCAN TABLE t1 (~500000 rows)}}
|
||||
|
@@ -10,14 +10,14 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements tests for SQLite library. The focus of the tests
|
||||
# in this file is the use of the sqlite_stat2 histogram data on tables
|
||||
# in this file is the use of the sqlite_stat3 histogram data on tables
|
||||
# with many repeated values and only a few distinct values.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -55,114 +55,102 @@ do_test analyze5-1.0 {
|
||||
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
|
||||
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
|
||||
ANALYZE;
|
||||
SELECT sample FROM sqlite_stat2 WHERE idx='t1u' ORDER BY sampleno;
|
||||
SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta}
|
||||
do_test analyze5-1.1 {
|
||||
string tolower \
|
||||
[db eval {SELECT sample from sqlite_stat2 WHERE idx='t1v' ORDER BY sampleno}]
|
||||
} {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1w' ORDER BY sampleno}
|
||||
} {{} 0 0 0 0 1 1 1 2 2}
|
||||
do_test analyze5-1.3 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno}
|
||||
} {{} {} {} {} 1 1 1 2 2 3}
|
||||
do_test analyze5-1.4 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1y' ORDER BY sampleno}
|
||||
} {0 0 0 0 0 0 0 0 0 0}
|
||||
do_test analyze5-1.5 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1z' ORDER BY sampleno}
|
||||
} {0 0 0 0 1 1 1 2 2 3}
|
||||
do_test analyze5-1.6 {
|
||||
db eval {SELECT sample from sqlite_stat2 WHERE idx='t1t' ORDER BY sampleno}
|
||||
} {0.5 0.5 0.5 0.5 1.5 1.5 1.5 2.5 2.5 3.5}
|
||||
} {alpha bravo charlie delta}
|
||||
|
||||
do_test analyze5-1.1 {
|
||||
db eval {SELECT DISTINCT lower(sample) FROM sqlite_stat3 WHERE idx='t1v'
|
||||
ORDER BY 1}
|
||||
} {alpha bravo charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
|
||||
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
foreach {testid where index rows} {
|
||||
1 {z>=0 AND z<=0} t1z 400
|
||||
2 {z>=1 AND z<=1} t1z 300
|
||||
3 {z>=2 AND z<=2} t1z 200
|
||||
4 {z>=3 AND z<=3} t1z 100
|
||||
5 {z>=4 AND z<=4} t1z 50
|
||||
6 {z>=-1 AND z<=-1} t1z 50
|
||||
7 {z>1 AND z<3} t1z 200
|
||||
3 {z>=2 AND z<=2} t1z 175
|
||||
4 {z>=3 AND z<=3} t1z 125
|
||||
5 {z>=4 AND z<=4} t1z 1
|
||||
6 {z>=-1 AND z<=-1} t1z 1
|
||||
7 {z>1 AND z<3} t1z 175
|
||||
8 {z>0 AND z<100} t1z 600
|
||||
9 {z>=1 AND z<100} t1z 600
|
||||
10 {z>1 AND z<100} t1z 300
|
||||
11 {z>=2 AND z<100} t1z 300
|
||||
12 {z>2 AND z<100} t1z 100
|
||||
13 {z>=3 AND z<100} t1z 100
|
||||
14 {z>3 AND z<100} t1z 50
|
||||
15 {z>=4 AND z<100} t1z 50
|
||||
16 {z>=-100 AND z<=-1} t1z 50
|
||||
12 {z>2 AND z<100} t1z 125
|
||||
13 {z>=3 AND z<100} t1z 125
|
||||
14 {z>3 AND z<100} t1z 1
|
||||
15 {z>=4 AND z<100} t1z 1
|
||||
16 {z>=-100 AND z<=-1} t1z 1
|
||||
17 {z>=-100 AND z<=0} t1z 400
|
||||
18 {z>=-100 AND z<0} t1z 50
|
||||
18 {z>=-100 AND z<0} t1z 1
|
||||
19 {z>=-100 AND z<=1} t1z 700
|
||||
20 {z>=-100 AND z<2} t1z 700
|
||||
21 {z>=-100 AND z<=2} t1z 900
|
||||
22 {z>=-100 AND z<3} t1z 900
|
||||
21 {z>=-100 AND z<=2} t1z 875
|
||||
22 {z>=-100 AND z<3} t1z 875
|
||||
|
||||
31 {z>=0.0 AND z<=0.0} t1z 400
|
||||
32 {z>=1.0 AND z<=1.0} t1z 300
|
||||
33 {z>=2.0 AND z<=2.0} t1z 200
|
||||
34 {z>=3.0 AND z<=3.0} t1z 100
|
||||
35 {z>=4.0 AND z<=4.0} t1z 50
|
||||
36 {z>=-1.0 AND z<=-1.0} t1z 50
|
||||
37 {z>1.5 AND z<3.0} t1z 200
|
||||
38 {z>0.5 AND z<100} t1z 600
|
||||
33 {z>=2.0 AND z<=2.0} t1z 175
|
||||
34 {z>=3.0 AND z<=3.0} t1z 125
|
||||
35 {z>=4.0 AND z<=4.0} t1z 1
|
||||
36 {z>=-1.0 AND z<=-1.0} t1z 1
|
||||
37 {z>1.5 AND z<3.0} t1z 174
|
||||
38 {z>0.5 AND z<100} t1z 599
|
||||
39 {z>=1.0 AND z<100} t1z 600
|
||||
40 {z>1.5 AND z<100} t1z 300
|
||||
40 {z>1.5 AND z<100} t1z 299
|
||||
41 {z>=2.0 AND z<100} t1z 300
|
||||
42 {z>2.1 AND z<100} t1z 100
|
||||
43 {z>=3.0 AND z<100} t1z 100
|
||||
44 {z>3.2 AND z<100} t1z 50
|
||||
45 {z>=4.0 AND z<100} t1z 50
|
||||
46 {z>=-100 AND z<=-1.0} t1z 50
|
||||
42 {z>2.1 AND z<100} t1z 124
|
||||
43 {z>=3.0 AND z<100} t1z 125
|
||||
44 {z>3.2 AND z<100} t1z 1
|
||||
45 {z>=4.0 AND z<100} t1z 1
|
||||
46 {z>=-100 AND z<=-1.0} t1z 1
|
||||
47 {z>=-100 AND z<=0.0} t1z 400
|
||||
48 {z>=-100 AND z<0.0} t1z 50
|
||||
48 {z>=-100 AND z<0.0} t1z 1
|
||||
49 {z>=-100 AND z<=1.0} t1z 700
|
||||
50 {z>=-100 AND z<2.0} t1z 700
|
||||
51 {z>=-100 AND z<=2.0} t1z 900
|
||||
52 {z>=-100 AND z<3.0} t1z 900
|
||||
51 {z>=-100 AND z<=2.0} t1z 875
|
||||
52 {z>=-100 AND z<3.0} t1z 875
|
||||
|
||||
101 {z=-1} t1z 50
|
||||
101 {z=-1} t1z 1
|
||||
102 {z=0} t1z 400
|
||||
103 {z=1} t1z 300
|
||||
104 {z=2} t1z 200
|
||||
105 {z=3} t1z 100
|
||||
106 {z=4} t1z 50
|
||||
107 {z=-10.0} t1z 50
|
||||
104 {z=2} t1z 175
|
||||
105 {z=3} t1z 125
|
||||
106 {z=4} t1z 1
|
||||
107 {z=-10.0} t1z 1
|
||||
108 {z=0.0} t1z 400
|
||||
109 {z=1.0} t1z 300
|
||||
110 {z=2.0} t1z 200
|
||||
111 {z=3.0} t1z 100
|
||||
112 {z=4.0} t1z 50
|
||||
113 {z=1.5} t1z 50
|
||||
114 {z=2.5} t1z 50
|
||||
110 {z=2.0} t1z 175
|
||||
111 {z=3.0} t1z 125
|
||||
112 {z=4.0} t1z 1
|
||||
113 {z=1.5} t1z 1
|
||||
114 {z=2.5} t1z 1
|
||||
|
||||
201 {z IN (-1)} t1z 50
|
||||
201 {z IN (-1)} t1z 1
|
||||
202 {z IN (0)} t1z 400
|
||||
203 {z IN (1)} t1z 300
|
||||
204 {z IN (2)} t1z 200
|
||||
205 {z IN (3)} t1z 100
|
||||
206 {z IN (4)} t1z 50
|
||||
207 {z IN (0.5)} t1z 50
|
||||
204 {z IN (2)} t1z 175
|
||||
205 {z IN (3)} t1z 125
|
||||
206 {z IN (4)} t1z 1
|
||||
207 {z IN (0.5)} t1z 1
|
||||
208 {z IN (0,1)} t1z 700
|
||||
209 {z IN (0,1,2)} t1z 900
|
||||
209 {z IN (0,1,2)} t1z 875
|
||||
210 {z IN (0,1,2,3)} {} 100
|
||||
211 {z IN (0,1,2,3,4,5)} {} 100
|
||||
212 {z IN (1,2)} t1z 500
|
||||
212 {z IN (1,2)} t1z 475
|
||||
213 {z IN (2,3)} t1z 300
|
||||
214 {z=3 OR z=2} t1z 300
|
||||
215 {z IN (-1,3)} t1z 150
|
||||
216 {z=-1 OR z=3} t1z 150
|
||||
215 {z IN (-1,3)} t1z 126
|
||||
216 {z=-1 OR z=3} t1z 126
|
||||
|
||||
300 {y=0} {} 100
|
||||
301 {y=1} t1y 50
|
||||
302 {y=0.1} t1y 50
|
||||
300 {y=0} t1y 974
|
||||
301 {y=1} t1y 26
|
||||
302 {y=0.1} t1y 1
|
||||
|
||||
400 {x IS NULL} t1x 400
|
||||
|
||||
@@ -204,16 +192,17 @@ db eval {
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
foreach {testid where index rows} {
|
||||
500 {x IS NULL AND u='charlie'} t1u 20
|
||||
501 {x=1 AND u='charlie'} t1x 5
|
||||
502 {x IS NULL} {} 100
|
||||
503 {x=1} t1x 50
|
||||
504 {x IS NOT NULL} t1x 25
|
||||
500 {x IS NULL AND u='charlie'} t1u 17
|
||||
501 {x=1 AND u='charlie'} t1x 1
|
||||
502 {x IS NULL} t1x 995
|
||||
503 {x=1} t1x 1
|
||||
504 {x IS NOT NULL} t1x 2
|
||||
505 {+x IS NOT NULL} {} 500
|
||||
506 {upper(x) IS NOT NULL} {} 500
|
||||
|
||||
} {
|
||||
# Verify that the expected index is used with the expected row count
|
||||
if {$testid==50299} {breakpoint; set sqlite_where_trace 1}
|
||||
do_test analyze5-1.${testid}a {
|
||||
set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
|
||||
set idx {}
|
||||
@@ -221,6 +210,7 @@ foreach {testid where index rows} {
|
||||
regexp {~([0-9]+) rows} $x all nrow
|
||||
list $idx $nrow
|
||||
} [list $index $rows]
|
||||
if {$testid==50299} exit
|
||||
|
||||
# Verify that the same result is achieved regardless of whether or not
|
||||
# the index is used
|
||||
|
@@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
@@ -82,14 +82,14 @@ do_test analyze7-3.1 {
|
||||
do_test analyze7-3.2.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}}
|
||||
ifcapable stat2 {
|
||||
# If ENABLE_STAT2 is defined, SQLite comes up with a different estimated
|
||||
ifcapable stat3 {
|
||||
# If ENABLE_STAT3 is defined, SQLite comes up with a different estimated
|
||||
# row count for (c=2) than it does for (c=?).
|
||||
do_test analyze7-3.2.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~51 rows)}}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~57 rows)}}
|
||||
} else {
|
||||
# If ENABLE_STAT2 is not defined, the expected row count for (c=2) is the
|
||||
# If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the
|
||||
# same as that for (c=?).
|
||||
do_test analyze7-3.2.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
@@ -98,12 +98,14 @@ ifcapable stat2 {
|
||||
do_test analyze7-3.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test analyze7-3.5 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
ifcapable {!stat3} {
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test analyze7-3.5 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
}
|
||||
do_test analyze7-3.6 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}}
|
||||
|
103
test/analyze8.test
Normal file
103
test/analyze8.test
Normal file
@@ -0,0 +1,103 @@
|
||||
# 2011 August 13
|
||||
#
|
||||
# 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 tests for SQLite library. The focus of the tests
|
||||
# in this file is testing the capabilities of sqlite_stat3.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze8
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
# Scenario:
|
||||
#
|
||||
# Two indices. One has mostly singleton entries, but for a few
|
||||
# values there are hundreds of entries. The other has 10-20
|
||||
# entries per value.
|
||||
#
|
||||
# Verify that the query planner chooses the first index for the singleton
|
||||
# entries and the second index for the others.
|
||||
#
|
||||
do_test 1.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b,c,d);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
}
|
||||
for {set i 0} {$i<1000} {incr i} {
|
||||
if {$i%2==0} {set a $i} {set a [expr {($i%8)*100}]}
|
||||
set b [expr {$i/10}]
|
||||
set c [expr {$i/8}]
|
||||
set c [expr {$c*$c*$c}]
|
||||
db eval {INSERT INTO t1 VALUES($a,$b,$c,$i)}
|
||||
}
|
||||
db eval {ANALYZE}
|
||||
} {}
|
||||
|
||||
# The a==100 comparison is expensive because there are many rows
|
||||
# with a==100. And so for those cases, choose the t1b index.
|
||||
#
|
||||
# Buf ro a==99 and a==101, there are far fewer rows so choose
|
||||
# the t1a index.
|
||||
#
|
||||
do_test 1.1 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b=55}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test 1.2 {
|
||||
eqp {SELECT * FROM t1 WHERE a=99 AND b=55}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 1.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=101 AND b=55}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 1.4 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b=56}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
|
||||
do_test 1.5 {
|
||||
eqp {SELECT * FROM t1 WHERE a=99 AND b=56}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 1.6 {
|
||||
eqp {SELECT * FROM t1 WHERE a=101 AND b=56}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
do_test 2.1 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b BETWEEN 50 AND 54}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~2 rows)}}
|
||||
|
||||
# There are many more values of c between 0 and 100000 than there are
|
||||
# between 800000 and 900000. So t1c is more selective for the latter
|
||||
# range.
|
||||
#
|
||||
do_test 3.1 {
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~6 rows)}}
|
||||
do_test 3.2 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE b BETWEEN 50 AND 54 AND c BETWEEN 800000 AND 900000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~4 rows)}}
|
||||
do_test 3.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~63 rows)}}
|
||||
do_test 3.4 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE a=100 AND c BETWEEN 800000 AND 900000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~2 rows)}}
|
||||
|
||||
finish_test
|
@@ -75,10 +75,12 @@ set L [list]
|
||||
set S ""
|
||||
foreach {name f} $files {
|
||||
if {[permutation] == "journaltest"} {
|
||||
lappend L delete
|
||||
set mode delete
|
||||
} else {
|
||||
lappend L wal
|
||||
set mode wal
|
||||
}
|
||||
ifcapable !wal { set mode delete }
|
||||
lappend L $mode
|
||||
append S "
|
||||
PRAGMA $name.journal_mode = WAL;
|
||||
UPDATE $name.tbl SET x = '$name';
|
||||
|
@@ -2324,7 +2324,11 @@ ifcapable compound&&subquery {
|
||||
ifcapable stat2 {
|
||||
set stat2 "sqlite_stat2 "
|
||||
} else {
|
||||
set stat2 ""
|
||||
ifcapable stat3 {
|
||||
set stat2 "sqlite_stat3 "
|
||||
} else {
|
||||
set stat2 ""
|
||||
}
|
||||
}
|
||||
do_test auth-5.2 {
|
||||
execsql {
|
||||
|
@@ -25,7 +25,7 @@ do_not_use_codec
|
||||
|
||||
# We must have the page_size pragma for these tests to work.
|
||||
#
|
||||
ifcapable !pager_pragmas {
|
||||
ifcapable !pager_pragmas||direct_read {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
@@ -16,8 +16,6 @@
|
||||
#
|
||||
# $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $
|
||||
|
||||
catch {forcedelete test.db test.db-journal test.bu}
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
@@ -223,16 +223,5 @@ do_test ctime-2.5.$tc {
|
||||
} ]
|
||||
} {0 {{}}}
|
||||
|
||||
ifcapable blockalloc {
|
||||
do_test ctime-3.1a {
|
||||
db eval {SELECT sqlite_compileoption_used('PAGECACHE_BLOCKALLOC')}
|
||||
} {1}
|
||||
} else {
|
||||
do_test ctime-3.1b {
|
||||
db eval {SELECT sqlite_compileoption_used('PAGECACHE_BLOCKALLOC')}
|
||||
} {0}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
@@ -56,6 +56,12 @@ proc lookaside {db} {
|
||||
}
|
||||
}
|
||||
|
||||
ifcapable stat3 {
|
||||
set STAT3 1
|
||||
} else {
|
||||
set STAT3 0
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the dbstatus-2 and dbstatus-3 tests with several of different
|
||||
# lookaside buffer sizes.
|
||||
@@ -118,7 +124,7 @@ foreach ::lookaside_buffer_size {0 64 120} {
|
||||
CREATE TABLE t2(c, d);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1 UNION SELECT * FROM t2;
|
||||
}
|
||||
6 {
|
||||
6y {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(a,b);
|
||||
@@ -198,7 +204,11 @@ foreach ::lookaside_buffer_size {0 64 120} {
|
||||
# much greater than just that reported by DBSTATUS_SCHEMA_USED in this
|
||||
# case.
|
||||
#
|
||||
if {[string match *x $tn] || $AUTOVACUUM} {
|
||||
# Some of the memory used for sqlite_stat3 is unaccounted for by
|
||||
# dbstatus.
|
||||
#
|
||||
if {[string match *x $tn] || $AUTOVACUUM
|
||||
|| ([string match *y $tn] && $STAT3)} {
|
||||
do_test dbstatus-2.$tn.ax { expr {($nSchema1-$nSchema2)<=$nFree} } 1
|
||||
} else {
|
||||
do_test dbstatus-2.$tn.a { expr {$nSchema1-$nSchema2} } $nFree
|
||||
|
76
test/dbstatus2.test
Normal file
76
test/dbstatus2.test
Normal file
@@ -0,0 +1,76 @@
|
||||
# 2011 September 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests for the sqlite3_stmt_status() function
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set ::testprefix dbstatus2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA auto_vacuum = 0;
|
||||
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, randomblob(600));
|
||||
INSERT INTO t1 VALUES(2, randomblob(600));
|
||||
INSERT INTO t1 VALUES(3, randomblob(600));
|
||||
}
|
||||
|
||||
proc db_hit_miss {db {reset 0}} {
|
||||
set nHit [sqlite3_db_status $db CACHE_HIT $reset]
|
||||
set nMiss [sqlite3_db_status $db CACHE_MISS $reset]
|
||||
list $nHit $nMiss
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
expr {[file size test.db] / 1024}
|
||||
} 6
|
||||
|
||||
do_test 1.2 {
|
||||
execsql { SELECT b FROM t1 WHERE a=2 }
|
||||
db_hit_miss db
|
||||
} {{0 2 0} {0 4 0}}
|
||||
|
||||
do_test 1.3 {
|
||||
execsql { SELECT b FROM t1 WHERE a=2 }
|
||||
db_hit_miss db
|
||||
} {{0 6 0} {0 4 0}}
|
||||
|
||||
do_test 1.4 {
|
||||
execsql { SELECT b FROM t1 WHERE a=2 }
|
||||
db_hit_miss db
|
||||
} {{0 10 0} {0 4 0}}
|
||||
|
||||
do_test 1.5 {
|
||||
db_hit_miss db 1
|
||||
} {{0 10 0} {0 4 0}}
|
||||
|
||||
do_test 1.6 {
|
||||
db_hit_miss db 0
|
||||
} {{0 0 0} {0 0 0}}
|
||||
|
||||
do_test 1.7 {
|
||||
set fd [db incrblob main t1 b 1]
|
||||
fconfigure $fd -translation binary
|
||||
set len [string length [read $fd]]
|
||||
close $fd
|
||||
set len
|
||||
} 600
|
||||
do_test 1.8 { sqlite3_db_status db CACHE_HIT 0 } {0 2 0}
|
||||
do_test 1.9 { sqlite3_db_status db CACHE_MISS 0 } {0 1 0}
|
||||
|
||||
|
||||
finish_test
|
@@ -219,7 +219,7 @@ do_test malloc5-4.2 {
|
||||
execsql {SELECT * FROM abc}
|
||||
set nMaxBytes [sqlite3_memory_highwater 1]
|
||||
puts -nonewline " (Highwater mark: $nMaxBytes) "
|
||||
expr $nMaxBytes <= 100000
|
||||
expr $nMaxBytes <= 110000
|
||||
} {1}
|
||||
do_test malloc5-4.3 {
|
||||
# Check that the content of table abc is at least roughly as expected.
|
||||
|
@@ -407,7 +407,7 @@ do_test memdb-8.2 {
|
||||
|
||||
# Test that auto-vacuum works with in-memory databases.
|
||||
#
|
||||
ifcapable autovacuum&&!blockalloc {
|
||||
ifcapable autovacuum {
|
||||
do_test memdb-9.1 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
@@ -25,13 +25,6 @@ if {[permutation] == "memsubsys1"} {
|
||||
return
|
||||
}
|
||||
|
||||
# Nor will it work if the pager is allocating memory in blocks.
|
||||
#
|
||||
ifcapable blockalloc {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# This procedure constructs a new database in test.db. It fills
|
||||
# this database with many small records (enough to force multiple
|
||||
# rebalance operations in the btree-layer and to require a large
|
||||
|
@@ -270,7 +270,11 @@ ifcapable {explain} {
|
||||
CREATE UNIQUE INDEX ex1i1 ON ex1(a);
|
||||
EXPLAIN REINDEX;
|
||||
}]
|
||||
regexp { SorterCompare \d+ \d+ \d+ } $x
|
||||
ifcapable mergesort {
|
||||
regexp { SorterCompare \d+ \d+ \d+ } $x
|
||||
} else {
|
||||
regexp { IsUnique \d+ \d+ \d+ \d+ } $x
|
||||
}
|
||||
} {1}
|
||||
if {[regexp {16} [db one {PRAGMA encoding}]]} {
|
||||
do_test misc3-6.11-utf16 {
|
||||
|
@@ -97,23 +97,25 @@ do_re_test 1.4.2 { lindex $::log 0 } {^os_unix.c:\d*: \(\d+\) open\(.*test.db\)
|
||||
#--------------------------------------------------------------------------
|
||||
# Tests oserror-1.* test failures in the unlink() system call.
|
||||
#
|
||||
do_test 2.1.1 {
|
||||
set ::log [list]
|
||||
file mkdir test.db-wal
|
||||
forcedelete test.db
|
||||
list [catch {
|
||||
sqlite3 dbh test.db
|
||||
execsql { SELECT * FROM sqlite_master } dbh
|
||||
} msg] $msg
|
||||
} {1 {disk I/O error}}
|
||||
ifcapable wal {
|
||||
do_test 2.1.1 {
|
||||
set ::log [list]
|
||||
file mkdir test.db-wal
|
||||
forcedelete test.db
|
||||
list [catch {
|
||||
sqlite3 dbh test.db
|
||||
execsql { SELECT * FROM sqlite_master } dbh
|
||||
} msg] $msg
|
||||
} {1 {disk I/O error}}
|
||||
|
||||
do_re_test 2.1.2 {
|
||||
lindex $::log 0
|
||||
} {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - }
|
||||
do_test 2.1.3 {
|
||||
catch { dbh close }
|
||||
forcedelete test.db-wal
|
||||
} {}
|
||||
do_re_test 2.1.2 {
|
||||
lindex $::log 0
|
||||
} {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - }
|
||||
do_test 2.1.3 {
|
||||
catch { dbh close }
|
||||
forcedelete test.db-wal
|
||||
} {}
|
||||
}
|
||||
|
||||
|
||||
test_syscall reset
|
||||
|
@@ -1766,6 +1766,10 @@ do_multiclient_test tn {
|
||||
# + Page 0,
|
||||
# + A page with a page number greater than (2^31-1).
|
||||
#
|
||||
# These tests will not work if SQLITE_DIRECT_OVERFLOW_READ is defined. In
|
||||
# that case IO errors are sometimes reported instead of SQLITE_CORRUPT.
|
||||
#
|
||||
ifcapable !direct_read {
|
||||
do_test pager1-18.1 {
|
||||
faultsim_delete_and_reopen
|
||||
db func a_string a_string
|
||||
@@ -1841,6 +1845,7 @@ do_test pager1-18.6 {
|
||||
sqlite3 db test.db
|
||||
catchsql { SELECT length(x) FROM t1 }
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
|
||||
do_test pager1-19.1 {
|
||||
sqlite3 db ""
|
||||
@@ -1992,31 +1997,33 @@ ifcapable wal {
|
||||
# pager1-22.1.*: is a no-op on a non-WAL db, and
|
||||
# pager1-22.2.*: does not cause xSync calls with a synchronous=off db.
|
||||
#
|
||||
do_test pager1-22.1.1 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def');
|
||||
INSERT INTO ko DEFAULT VALUES;
|
||||
}
|
||||
execsql { PRAGMA wal_checkpoint }
|
||||
} {0 -1 -1}
|
||||
do_test pager1-22.2.1 {
|
||||
testvfs tv -default 1
|
||||
tv filter xSync
|
||||
tv script xSyncCb
|
||||
proc xSyncCb {args} {incr ::synccount}
|
||||
set ::synccount 0
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA synchronous = off;
|
||||
PRAGMA journal_mode = WAL;
|
||||
INSERT INTO ko DEFAULT VALUES;
|
||||
}
|
||||
execsql { PRAGMA wal_checkpoint }
|
||||
set synccount
|
||||
} {0}
|
||||
db close
|
||||
tv delete
|
||||
ifcapable wal {
|
||||
do_test pager1-22.1.1 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def');
|
||||
INSERT INTO ko DEFAULT VALUES;
|
||||
}
|
||||
execsql { PRAGMA wal_checkpoint }
|
||||
} {0 -1 -1}
|
||||
do_test pager1-22.2.1 {
|
||||
testvfs tv -default 1
|
||||
tv filter xSync
|
||||
tv script xSyncCb
|
||||
proc xSyncCb {args} {incr ::synccount}
|
||||
set ::synccount 0
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA synchronous = off;
|
||||
PRAGMA journal_mode = WAL;
|
||||
INSERT INTO ko DEFAULT VALUES;
|
||||
}
|
||||
execsql { PRAGMA wal_checkpoint }
|
||||
set synccount
|
||||
} {0}
|
||||
db close
|
||||
tv delete
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests for changing journal mode.
|
||||
|
@@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable {!pager_pragmas||secure_delete} {
|
||||
ifcapable {!pager_pragmas||secure_delete||direct_read} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
@@ -16,14 +16,6 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If compiled with blockalloc, pagecache memory is not used. Which
|
||||
# causes these tests to fail.
|
||||
#
|
||||
ifcapable blockalloc {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Set up a pcache memory pool so that we can easily track how many
|
||||
# pages are being used for cache.
|
||||
#
|
||||
|
@@ -36,7 +36,8 @@ ifcapable wal {
|
||||
do_execsql_test stat-0.1 {
|
||||
PRAGMA journal_mode = WAL;
|
||||
PRAGMA journal_mode = delete;
|
||||
SELECT * FROM stat;
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat;
|
||||
} {wal delete sqlite_master / 1 leaf 0 0 916 0}
|
||||
}
|
||||
|
||||
@@ -50,17 +51,20 @@ do_test stat-1.0 {
|
||||
} {}
|
||||
do_test stat-1.1 {
|
||||
execsql {
|
||||
SELECT * FROM stat WHERE name = 't1';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name = 't1';
|
||||
}
|
||||
} {t1 / 2 leaf 2 10 998 5}
|
||||
do_test stat-1.2 {
|
||||
execsql {
|
||||
SELECT * FROM stat WHERE name = 'i1';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name = 'i1';
|
||||
}
|
||||
} {i1 / 3 leaf 2 10 1000 5}
|
||||
do_test stat-1.3 {
|
||||
execsql {
|
||||
SELECT * FROM stat WHERE name = 'sqlite_master';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name = 'sqlite_master';
|
||||
}
|
||||
} {sqlite_master / 1 leaf 2 77 831 40}
|
||||
do_test stat-1.4 {
|
||||
@@ -77,7 +81,8 @@ do_execsql_test stat-2.1 {
|
||||
INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3;
|
||||
INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3;
|
||||
INSERT INTO t3 SELECT a_string(110+rowid), a_string(221+rowid) FROM t3;
|
||||
SELECT * FROM stat WHERE name != 'sqlite_master';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name != 'sqlite_master';
|
||||
} [list \
|
||||
sqlite_autoindex_t3_1 / 3 internal 3 368 623 125 \
|
||||
sqlite_autoindex_t3_1 /000/ 8 leaf 8 946 46 123 \
|
||||
@@ -108,7 +113,8 @@ do_execsql_test stat-3.1 {
|
||||
CREATE TABLE t4(x);
|
||||
CREATE INDEX i4 ON t4(x);
|
||||
INSERT INTO t4(rowid, x) VALUES(2, a_string(7777));
|
||||
SELECT * FROM stat WHERE name != 'sqlite_master';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name != 'sqlite_master';
|
||||
} [list \
|
||||
i4 / 3 leaf 1 103 905 7782 \
|
||||
i4 /000+000000 9 overflow 0 1020 0 0 \
|
||||
@@ -132,7 +138,8 @@ do_execsql_test stat-3.1 {
|
||||
do_execsql_test stat-4.1 {
|
||||
CREATE TABLE t5(x);
|
||||
CREATE INDEX i5 ON t5(x);
|
||||
SELECT * FROM stat WHERE name = 't5' OR name = 'i5';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name = 't5' OR name = 'i5';
|
||||
} [list \
|
||||
i5 / 5 leaf 0 0 1016 0 \
|
||||
t5 / 4 leaf 0 0 1016 0 \
|
||||
@@ -149,7 +156,8 @@ do_execsql_test stat-5.1 {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(zeroblob(1513));
|
||||
INSERT INTO t1 VALUES(zeroblob(1514));
|
||||
SELECT * FROM stat WHERE name = 't1';
|
||||
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
|
||||
FROM stat WHERE name = 't1';
|
||||
} [list \
|
||||
t1 / 2 leaf 2 993 5 1517 \
|
||||
t1 /000+000000 3 overflow 0 1020 0 0 \
|
||||
|
@@ -48,6 +48,11 @@ do_execsql_test 1.1 {
|
||||
PRAGMA journal_mode = DELETE;
|
||||
} {delete}
|
||||
|
||||
ifcapable !wal {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock}
|
||||
do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}}
|
||||
do_test 1.4 { unlock } {}
|
||||
|
@@ -260,6 +260,18 @@ do_test table-5.2 {
|
||||
catchsql {DROP TABLE IF EXISTS sqlite_master}
|
||||
} {1 {table sqlite_master may not be dropped}}
|
||||
|
||||
# Dropping sqlite_statN tables is OK.
|
||||
#
|
||||
do_test table-5.2.1 {
|
||||
db eval {
|
||||
ANALYZE;
|
||||
DROP TABLE IF EXISTS sqlite_stat1;
|
||||
DROP TABLE IF EXISTS sqlite_stat2;
|
||||
DROP TABLE IF EXISTS sqlite_stat3;
|
||||
SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_stat*';
|
||||
}
|
||||
} {}
|
||||
|
||||
# Make sure an EXPLAIN does not really create a new table
|
||||
#
|
||||
do_test table-5.3 {
|
||||
|
26
test/tkt-c48d99d690.test
Normal file
26
test/tkt-c48d99d690.test
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set ::testprefix tkt-c48d99d690
|
||||
|
||||
do_test 1.0 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t1 VALUES('one' , 1);
|
||||
INSERT INTO t1 VALUES('two' , 5);
|
||||
INSERT INTO t1 VALUES('two' , 2);
|
||||
INSERT INTO t1 VALUES('three', 3);
|
||||
PRAGMA count_changes = 1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.1 {
|
||||
execsql { INSERT INTO t2 SELECT * FROM t1 }
|
||||
} {4}
|
||||
|
||||
do_test 1.2 { execsql VACUUM } {}
|
||||
|
||||
finish_test
|
||||
|
@@ -16,7 +16,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat2 {
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -46,7 +46,7 @@ do_test tkt-cbd05-1.2 {
|
||||
do_test tkt-cbd05-1.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
FROM sqlite_stat3
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
@@ -78,7 +78,7 @@ do_test tkt-cbd05-2.2 {
|
||||
do_test tkt-cbd05-2.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat2
|
||||
FROM sqlite_stat3
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
|
@@ -31,11 +31,13 @@ do_execsql_test 1.0 {
|
||||
} {}
|
||||
|
||||
foreach idxmode {ordered unordered} {
|
||||
catchsql { DELETE FROM sqlite_stat2 }
|
||||
catchsql { DELETE FROM sqlite_stat3 }
|
||||
if {$idxmode == "unordered"} {
|
||||
execsql { UPDATE sqlite_stat1 SET stat = stat || ' unordered' }
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
foreach {tn sql r(ordered) r(unordered)} {
|
||||
1 "SELECT * FROM t1 ORDER BY a"
|
||||
{0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}}
|
||||
|
@@ -219,44 +219,46 @@ do_test 4.3.2 {
|
||||
# set, where X is the file-name the method is called on. Calls to the above
|
||||
# methods using "tvfs2" set entries in the global T2 array.
|
||||
#
|
||||
testvfs tvfs1
|
||||
tvfs1 filter {xOpen xDelete xAccess xFullPathname}
|
||||
tvfs1 script tvfs1_callback
|
||||
proc tvfs1_callback {method filename args} {
|
||||
set ::T1([file tail $filename]) 1
|
||||
}
|
||||
testvfs tvfs2
|
||||
tvfs2 filter {xOpen xDelete xAccess xFullPathname}
|
||||
tvfs2 script tvfs2_callback
|
||||
proc tvfs2_callback {method filename args} {
|
||||
set ::T2([file tail $filename]) 1
|
||||
}
|
||||
|
||||
catch {db close}
|
||||
eval forcedelete [glob test.db*]
|
||||
do_test 5.1.1 {
|
||||
sqlite3 db file:test.db1?vfs=tvfs1
|
||||
execsql {
|
||||
ATTACH 'file:test.db2?vfs=tvfs2' AS aux;
|
||||
PRAGMA main.journal_mode = PERSIST;
|
||||
PRAGMA aux.journal_mode = PERSIST;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE aux.t2(a, b);
|
||||
PRAGMA main.journal_mode = WAL;
|
||||
PRAGMA aux.journal_mode = WAL;
|
||||
INSERT INTO t1 VALUES('x', 'y');
|
||||
INSERT INTO t2 VALUES('x', 'y');
|
||||
ifcapable wal {
|
||||
testvfs tvfs1
|
||||
tvfs1 filter {xOpen xDelete xAccess xFullPathname}
|
||||
tvfs1 script tvfs1_callback
|
||||
proc tvfs1_callback {method filename args} {
|
||||
set ::T1([file tail $filename]) 1
|
||||
}
|
||||
testvfs tvfs2
|
||||
tvfs2 filter {xOpen xDelete xAccess xFullPathname}
|
||||
tvfs2 script tvfs2_callback
|
||||
proc tvfs2_callback {method filename args} {
|
||||
set ::T2([file tail $filename]) 1
|
||||
}
|
||||
lsort [array names ::T1]
|
||||
} {test.db1 test.db1-journal test.db1-wal}
|
||||
|
||||
do_test 5.1.2 {
|
||||
lsort [array names ::T2]
|
||||
} {test.db2 test.db2-journal test.db2-wal}
|
||||
catch {db close}
|
||||
eval forcedelete [glob test.db*]
|
||||
do_test 5.1.1 {
|
||||
sqlite3 db file:test.db1?vfs=tvfs1
|
||||
execsql {
|
||||
ATTACH 'file:test.db2?vfs=tvfs2' AS aux;
|
||||
PRAGMA main.journal_mode = PERSIST;
|
||||
PRAGMA aux.journal_mode = PERSIST;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE aux.t2(a, b);
|
||||
PRAGMA main.journal_mode = WAL;
|
||||
PRAGMA aux.journal_mode = WAL;
|
||||
INSERT INTO t1 VALUES('x', 'y');
|
||||
INSERT INTO t2 VALUES('x', 'y');
|
||||
}
|
||||
lsort [array names ::T1]
|
||||
} {test.db1 test.db1-journal test.db1-wal}
|
||||
|
||||
db close
|
||||
tvfs1 delete
|
||||
tvfs2 delete
|
||||
do_test 5.1.2 {
|
||||
lsort [array names ::T2]
|
||||
} {test.db2 test.db2-journal test.db2-wal}
|
||||
db close
|
||||
|
||||
tvfs1 delete
|
||||
tvfs2 delete
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that only "" and "localhost" are acceptable as authorities.
|
||||
|
@@ -385,4 +385,20 @@ ifcapable {autoinc} {
|
||||
|
||||
forcedelete {a'z.db}
|
||||
|
||||
# Test that "PRAGMA count_changes" does not interfere with VACUUM or cause
|
||||
# it to return any rows to the user.
|
||||
#
|
||||
do_test vacuum-10.1 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
CREATE TABLE t8(a, b);
|
||||
INSERT INTO t8 VALUES('a', 'b');
|
||||
INSERT INTO t8 VALUES('c', 'd');
|
||||
PRAGMA count_changes = 1;
|
||||
}
|
||||
} {}
|
||||
do_test vacuum-10.2 { execsql VACUUM } {}
|
||||
|
||||
finish_test
|
||||
|
@@ -17,6 +17,11 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !wal {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Do not use a codec for this file, as the database is manipulated using
|
||||
# external methods (the [fake_big_file] and [hexio_write] commands).
|
||||
#
|
||||
|
@@ -17,6 +17,11 @@ source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
set ::testprefix walpersist
|
||||
|
||||
ifcapable !wal {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test walpersist-1.0 {
|
||||
db eval {
|
||||
PRAGMA journal_mode=WAL;
|
||||
|
@@ -24,6 +24,13 @@ if {$::tcl_platform(platform) != "unix"} {
|
||||
return
|
||||
}
|
||||
|
||||
# And only if the build is WAL-capable.
|
||||
#
|
||||
ifcapable !wal {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_multiclient_test tn {
|
||||
# Do not run tests with the connections in the same process.
|
||||
#
|
||||
|
@@ -10,8 +10,6 @@
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the multi-index OR clause optimizer.
|
||||
#
|
||||
# $Id: where7.test,v 1.9 2009/06/07 23:45:11 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -23341,7 +23339,7 @@ do_execsql_test where7-3.1 {
|
||||
OR t301.c8 = 1407424651264000)
|
||||
ORDER BY t302.c5 LIMIT 200;
|
||||
} {
|
||||
0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)}
|
||||
0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~5 rows)}
|
||||
0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
|
@@ -11,7 +11,6 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the multi-index OR clause optimizer.
|
||||
#
|
||||
# $Id: where9.test,v 1.9 2009/06/05 17:09:12 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -365,7 +364,7 @@ ifcapable explain {
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)}
|
||||
}
|
||||
do_execsql_test where9-3.2 {
|
||||
EXPLAIN QUERY PLAN
|
||||
@@ -375,7 +374,7 @@ ifcapable explain {
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~5 rows)}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,8 +453,8 @@ ifcapable explain {
|
||||
do_execsql_test where9-5.1 {
|
||||
EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL)
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~10 rows)}
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~10 rows)}
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~2 rows)}
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~2 rows)}
|
||||
}
|
||||
|
||||
# In contrast, b=1000 is preferred over any OR-clause.
|
||||
@@ -783,4 +782,79 @@ do_test where9-6.8.2 {
|
||||
}
|
||||
} {1 {cannot use index: t1b}}
|
||||
|
||||
############################################################################
|
||||
# Test cases where terms inside an OR series are combined with AND terms
|
||||
# external to the OR clause. In other words, cases where
|
||||
#
|
||||
# x AND (y OR z)
|
||||
#
|
||||
# is able to use indices on x,y and x,z, or indices y,x and z,x.
|
||||
#
|
||||
do_test where9-7.0 {
|
||||
execsql {
|
||||
CREATE TABLE t5(a, b, c, d, e, f, g, x, y);
|
||||
INSERT INTO t5
|
||||
SELECT a, b, c, e, d, f, g,
|
||||
CASE WHEN (a&1)!=0 THEN 'y' ELSE 'n' END,
|
||||
CASE WHEN (a&2)!=0 THEN 'y' ELSE 'n' END
|
||||
FROM t1;
|
||||
CREATE INDEX t5xb ON t5(x, b);
|
||||
CREATE INDEX t5xc ON t5(x, c);
|
||||
CREATE INDEX t5xd ON t5(x, d);
|
||||
CREATE INDEX t5xe ON t5(x, e);
|
||||
CREATE INDEX t5xf ON t5(x, f);
|
||||
CREATE INDEX t5xg ON t5(x, g);
|
||||
CREATE INDEX t5yb ON t5(y, b);
|
||||
CREATE INDEX t5yc ON t5(y, c);
|
||||
CREATE INDEX t5yd ON t5(y, d);
|
||||
CREATE INDEX t5ye ON t5(y, e);
|
||||
CREATE INDEX t5yf ON t5(y, f);
|
||||
CREATE INDEX t5yg ON t5(y, g);
|
||||
CREATE TABLE t6(a, b, c, e, d, f, g, x, y);
|
||||
INSERT INTO t6 SELECT * FROM t5;
|
||||
ANALYZE t5;
|
||||
}
|
||||
} {}
|
||||
do_test where9-7.1.1 {
|
||||
count_steps {
|
||||
SELECT a FROM t5 WHERE x='y' AND (b=913 OR c=27027) ORDER BY a;
|
||||
}
|
||||
} {79 81 83 scan 0 sort 1}
|
||||
do_test where9-7.1.2 {
|
||||
execsql {
|
||||
SELECT a FROM t6 WHERE x='y' AND (b=913 OR c=27027) ORDER BY a;
|
||||
}
|
||||
} {79 81 83}
|
||||
do_test where9-7.1.3 {
|
||||
count_steps {
|
||||
SELECT a FROM t5 WHERE x='n' AND (b=913 OR c=27027) ORDER BY a;
|
||||
}
|
||||
} {80 scan 0 sort 1}
|
||||
do_test where9-7.1.4 {
|
||||
execsql {
|
||||
SELECT a FROM t6 WHERE x='n' AND (b=913 OR c=27027) ORDER BY a;
|
||||
}
|
||||
} {80}
|
||||
do_test where9-7.2.1 {
|
||||
count_steps {
|
||||
SELECT a FROM t5 WHERE (x='y' OR y='y') AND b=913 ORDER BY a;
|
||||
}
|
||||
} {83 scan 0 sort 1}
|
||||
do_test where9-7.2.2 {
|
||||
execsql {
|
||||
SELECT a FROM t6 WHERE (x='y' OR y='y') AND b=913 ORDER BY a;
|
||||
}
|
||||
} {83}
|
||||
do_test where9-7.3.1 {
|
||||
count_steps {
|
||||
SELECT a FROM t5 WHERE (x='y' OR y='y') AND c=27027 ORDER BY a;
|
||||
}
|
||||
} {79 81 scan 0 sort 1}
|
||||
do_test where9-7.3.2 {
|
||||
execsql {
|
||||
SELECT a FROM t6 WHERE (x='y' OR y='y') AND c=27027 ORDER BY a;
|
||||
}
|
||||
} {79 81}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@@ -4,18 +4,32 @@
|
||||
#
|
||||
|
||||
if {[catch {
|
||||
if {![info exists argv0]} {
|
||||
set argv0 [file rootname [file tail [info nameofexecutable]]]
|
||||
}
|
||||
|
||||
# Get the name of the database to analyze
|
||||
#
|
||||
#set argv $argv0
|
||||
if {![info exists argv] || [llength $argv]!=1} {
|
||||
proc usage {} {
|
||||
set argv0 [file rootname [file tail [info nameofexecutable]]]
|
||||
puts stderr "Usage: $argv0 database-name"
|
||||
exit 1
|
||||
}
|
||||
set file_to_analyze [lindex $argv 0]
|
||||
set file_to_analyze {}
|
||||
set flags(-pageinfo) 0
|
||||
set flags(-stats) 0
|
||||
append argv {}
|
||||
foreach arg $argv {
|
||||
if {[regexp {^-+pageinfo$} $arg]} {
|
||||
set flags(-pageinfo) 1
|
||||
} elseif {[regexp {^-+stats$} $arg]} {
|
||||
set flags(-stats) 1
|
||||
} elseif {[regexp {^-} $arg]} {
|
||||
puts stderr "Unknown option: $arg"
|
||||
usage
|
||||
} elseif {$file_to_analyze!=""} {
|
||||
usage
|
||||
} else {
|
||||
set file_to_analyze $arg
|
||||
}
|
||||
}
|
||||
if {$file_to_analyze==""} usage
|
||||
if {![file exists $file_to_analyze]} {
|
||||
puts stderr "No such file: $file_to_analyze"
|
||||
exit 1
|
||||
@@ -24,19 +38,76 @@ if {![file readable $file_to_analyze]} {
|
||||
puts stderr "File is not readable: $file_to_analyze"
|
||||
exit 1
|
||||
}
|
||||
if {[file size $file_to_analyze]<512} {
|
||||
set true_file_size [file size $file_to_analyze]
|
||||
if {$true_file_size<512} {
|
||||
puts stderr "Empty or malformed database: $file_to_analyze"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Compute the total file size assuming test_multiplexor is being used.
|
||||
# Assume that SQLITE_ENABLE_8_3_NAMES might be enabled
|
||||
#
|
||||
set extension [file extension $file_to_analyze]
|
||||
set pattern $file_to_analyze
|
||||
append pattern {[0-9][0-9]}
|
||||
foreach f [glob -nocomplain $pattern] {
|
||||
incr true_file_size [file size $f]
|
||||
set extension {}
|
||||
}
|
||||
if {[string length $extension]>=2 && [string length $extension]<=4} {
|
||||
set pattern [file rootname $file_to_analyze]
|
||||
append pattern [string range $extension 0 1]
|
||||
append pattern {[0-9][0-9]}
|
||||
foreach f [glob -nocomplain $pattern] {
|
||||
incr true_file_size [file size $f]
|
||||
}
|
||||
}
|
||||
|
||||
# Open the database
|
||||
#
|
||||
sqlite3 db $file_to_analyze
|
||||
register_dbstat_vtab db
|
||||
|
||||
set pageSize [db one {PRAGMA page_size}]
|
||||
db eval {SELECT count(*) FROM sqlite_master}
|
||||
set pageSize [expr {wide([db one {PRAGMA page_size}])}]
|
||||
|
||||
#set DB [btree_open $file_to_analyze 1000 0]
|
||||
if {$flags(-pageinfo)} {
|
||||
db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
||||
db eval {SELECT name, path, pageno FROM temp.stat ORDER BY pageno} {
|
||||
puts "$pageno $name $path"
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
if {$flags(-stats)} {
|
||||
db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
||||
puts "BEGIN;"
|
||||
puts "CREATE TABLE stats("
|
||||
puts " name STRING, /* Name of table or index */"
|
||||
puts " path INTEGER, /* Path to page from root */"
|
||||
puts " pageno INTEGER, /* Page number */"
|
||||
puts " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */"
|
||||
puts " ncell INTEGER, /* Cells on page (0 for overflow) */"
|
||||
puts " payload INTEGER, /* Bytes of payload on this page */"
|
||||
puts " unused INTEGER, /* Bytes of unused space on this page */"
|
||||
puts " mx_payload INTEGER, /* Largest payload size of all cells */"
|
||||
puts " pgoffset INTEGER, /* Offset of page in file */"
|
||||
puts " pgsize INTEGER /* Size of the page */"
|
||||
puts ");"
|
||||
db eval {SELECT quote(name) || ',' ||
|
||||
quote(path) || ',' ||
|
||||
quote(pageno) || ',' ||
|
||||
quote(pagetype) || ',' ||
|
||||
quote(ncell) || ',' ||
|
||||
quote(payload) || ',' ||
|
||||
quote(unused) || ',' ||
|
||||
quote(mx_payload) || ',' ||
|
||||
quote(pgoffset) || ',' ||
|
||||
quote(pgsize) AS x FROM stat} {
|
||||
puts "INSERT INTO stats VALUES($x);"
|
||||
}
|
||||
puts "COMMIT;"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# In-memory database for collecting statistics. This script loops through
|
||||
# the tables and indices in the database being analyzed, adding a row for each
|
||||
@@ -60,17 +131,17 @@ set tabledef {CREATE TABLE space_used(
|
||||
int_unused int, -- Number of unused bytes on interior pages
|
||||
leaf_unused int, -- Number of unused bytes on primary pages
|
||||
ovfl_unused int, -- Number of unused bytes on overflow pages
|
||||
gap_cnt int -- Number of gaps in the page layout
|
||||
gap_cnt int, -- Number of gaps in the page layout
|
||||
compressed_size int -- Total bytes stored on disk
|
||||
);}
|
||||
mem eval $tabledef
|
||||
|
||||
# Create a temporary "dbstat" virtual table.
|
||||
#
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE temp.stat USING dbstat;
|
||||
CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat ORDER BY name, path;
|
||||
DROP TABLE temp.stat;
|
||||
}
|
||||
db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
|
||||
db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat
|
||||
ORDER BY name, path}
|
||||
db eval {DROP TABLE temp.stat}
|
||||
|
||||
proc isleaf {pagetype is_index} {
|
||||
return [expr {$pagetype == "leaf" || ($pagetype == "internal" && $is_index)}]
|
||||
@@ -86,6 +157,8 @@ db func isleaf isleaf
|
||||
db func isinternal isinternal
|
||||
db func isoverflow isoverflow
|
||||
|
||||
set isCompressed 0
|
||||
set compressOverhead 0
|
||||
set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
|
||||
foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
||||
|
||||
@@ -103,10 +176,18 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
||||
sum(isoverflow(pagetype, $is_index)) AS ovfl_pages,
|
||||
sum(isinternal(pagetype, $is_index) * unused) AS int_unused,
|
||||
sum(isleaf(pagetype, $is_index) * unused) AS leaf_unused,
|
||||
sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused
|
||||
sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused,
|
||||
sum(pgsize) AS compressed_size
|
||||
FROM temp.dbstat WHERE name = $name
|
||||
} break
|
||||
|
||||
set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
|
||||
set storage [expr {$total_pages*$pageSize}]
|
||||
if {!$isCompressed && $storage>$compressed_size} {
|
||||
set isCompressed 1
|
||||
set compressOverhead 14
|
||||
}
|
||||
|
||||
# Column 'gap_cnt' is set to the number of non-contiguous entries in the
|
||||
# list of pages visited if the b-tree structure is traversed in a top-down
|
||||
# fashion (each node visited before its child-tree is passed). Any overflow
|
||||
@@ -140,14 +221,15 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
|
||||
$int_unused,
|
||||
$leaf_unused,
|
||||
$ovfl_unused,
|
||||
$gap_cnt
|
||||
$gap_cnt,
|
||||
$compressed_size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
proc integerify {real} {
|
||||
if {[string is double -strict $real]} {
|
||||
return [expr {int($real)}]
|
||||
return [expr {wide($real)}]
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
@@ -202,7 +284,7 @@ proc divide {num denom} {
|
||||
# the $where clause determines which subset to analyze.
|
||||
#
|
||||
proc subreport {title where} {
|
||||
global pageSize file_pgcnt
|
||||
global pageSize file_pgcnt compressOverhead
|
||||
|
||||
# Query the in-memory database for the sum of various statistics
|
||||
# for the subset of tables/indices identified by the WHERE clause in
|
||||
@@ -226,7 +308,8 @@ proc subreport {title where} {
|
||||
int(sum(leaf_unused)) AS leaf_unused,
|
||||
int(sum(int_unused)) AS int_unused,
|
||||
int(sum(ovfl_unused)) AS ovfl_unused,
|
||||
int(sum(gap_cnt)) AS gap_cnt
|
||||
int(sum(gap_cnt)) AS gap_cnt,
|
||||
int(sum(compressed_size)) AS compressed_size
|
||||
FROM space_used WHERE $where" {} {}
|
||||
|
||||
# Output the sub-report title, nicely decorated with * characters.
|
||||
@@ -276,6 +359,12 @@ proc subreport {title where} {
|
||||
statline {Percentage of total database} $total_pages_percent
|
||||
statline {Number of entries} $nleaf
|
||||
statline {Bytes of storage consumed} $storage
|
||||
if {$compressed_size!=$storage} {
|
||||
set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}]
|
||||
set pct [expr {$compressed_size*100.0/$storage}]
|
||||
set pct [format {%5.1f%%} $pct]
|
||||
statline {Bytes used after compression} $compressed_size $pct
|
||||
}
|
||||
statline {Bytes of payload} $payload $payload_percent
|
||||
statline {Average payload per entry} $avg_payload
|
||||
statline {Average unused bytes per entry} $avg_unused
|
||||
@@ -332,7 +421,7 @@ proc autovacuum_overhead {filePages pageSize} {
|
||||
set ptrsPerPage [expr double($pageSize/5)]
|
||||
|
||||
# Return the number of pointer map pages in the database.
|
||||
return [expr int(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
|
||||
return [expr wide(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
|
||||
}
|
||||
|
||||
|
||||
@@ -359,17 +448,24 @@ proc autovacuum_overhead {filePages pageSize} {
|
||||
# (not including sqlite_master)
|
||||
# user_percent: $user_payload as a percentage of total file size.
|
||||
|
||||
set file_bytes [file size $file_to_analyze]
|
||||
set file_pgcnt [expr {$file_bytes/$pageSize}]
|
||||
### The following, setting $file_bytes based on the actual size of the file
|
||||
### on disk, causes this tool to choke on zipvfs databases. So set it based
|
||||
### on the return of [PRAGMA page_count] instead.
|
||||
if 0 {
|
||||
set file_bytes [file size $file_to_analyze]
|
||||
set file_pgcnt [expr {$file_bytes/$pageSize}]
|
||||
}
|
||||
set file_pgcnt [db one {PRAGMA page_count}]
|
||||
set file_bytes [expr {$file_pgcnt * $pageSize}]
|
||||
|
||||
set av_pgcnt [autovacuum_overhead $file_pgcnt $pageSize]
|
||||
set av_percent [percent $av_pgcnt $file_pgcnt]
|
||||
|
||||
set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
|
||||
set inuse_pgcnt [expr int([mem eval $sql])]
|
||||
set inuse_pgcnt [expr wide([mem eval $sql])]
|
||||
set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
|
||||
|
||||
set free_pgcnt [expr $file_pgcnt-$inuse_pgcnt-$av_pgcnt]
|
||||
set free_pgcnt [expr {$file_pgcnt-$inuse_pgcnt-$av_pgcnt}]
|
||||
set free_percent [percent $free_pgcnt $file_pgcnt]
|
||||
set free_pgcnt2 [db one {PRAGMA freelist_count}]
|
||||
set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
|
||||
@@ -405,7 +501,13 @@ statline {Number of tables in the database} $ntable
|
||||
statline {Number of indices} $nindex
|
||||
statline {Number of named indices} $nmanindex
|
||||
statline {Automatically generated indices} $nautoindex
|
||||
statline {Size of the file in bytes} $file_bytes
|
||||
if {$isCompressed} {
|
||||
statline {Size of uncompressed content in bytes} $file_bytes
|
||||
set efficiency [percent $true_file_size $file_bytes]
|
||||
statline {Size of compressed file on disk} $true_file_size $efficiency
|
||||
} else {
|
||||
statline {Size of the file in bytes} $file_bytes
|
||||
}
|
||||
statline {Bytes of user payload stored} $user_payload $user_percent
|
||||
|
||||
# Output table rankings
|
||||
@@ -418,6 +520,24 @@ mem eval {SELECT tblname, count(*) AS cnt,
|
||||
FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
|
||||
statline [string toupper $tblname] $size [percent $size $file_pgcnt]
|
||||
}
|
||||
if {$isCompressed} {
|
||||
puts ""
|
||||
puts "*** Bytes of disk space used after compression ***********************"
|
||||
puts ""
|
||||
set csum 0
|
||||
mem eval {SELECT tblname,
|
||||
int(sum(compressed_size)) +
|
||||
$compressOverhead*sum(int_pages+leaf_pages+ovfl_pages)
|
||||
AS csize
|
||||
FROM space_used GROUP BY tblname ORDER BY csize+0 DESC, tblname} {} {
|
||||
incr csum $csize
|
||||
statline [string toupper $tblname] $csize [percent $csize $true_file_size]
|
||||
}
|
||||
set overhead [expr {$true_file_size - $csum}]
|
||||
if {$overhead>0} {
|
||||
statline {Header and free space} $overhead [percent $overhead $true_file_size]
|
||||
}
|
||||
}
|
||||
|
||||
# Output subreports
|
||||
#
|
||||
|
@@ -9,9 +9,9 @@ echo '********** No optimizations. Includes FTS4 and RTREE *********'
|
||||
gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
|
||||
-ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \
|
||||
sqlite3.c
|
||||
echo '********** No optimizations. ENABLE_STAT2. THREADSAFE=0 *******'
|
||||
echo '********** No optimizations. ENABLE_STAT3. THREADSAFE=0 *******'
|
||||
gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
|
||||
-ansi -DSQLITE_ENABLE_STAT2 -DSQLITE_THREADSAFE=0 \
|
||||
-ansi -DSQLITE_ENABLE_STAT3 -DSQLITE_THREADSAFE=0 \
|
||||
sqlite3.c
|
||||
echo '********** Optimized -O3. Includes FTS4 and RTREE ************'
|
||||
gcc -O3 -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
|
||||
|
Reference in New Issue
Block a user