Compare commits

...

54 Commits

Author SHA1 Message Date
drh 0a61df670e Add testcase() macros to ensure good test coverage.
FossilOrigin-Name: 5c132592820e9dc2355e26ea14e155c797c335b3
2011-10-07 17:45:58 +00:00
drh 85ae206e69 More test cases for the OR optimization.
FossilOrigin-Name: 4997d8b81cd3ea7c708911bfece00020d11224f9
2011-10-07 16:08:28 +00:00
drh 9ef61f4f4e Prevent infinite recursion of in the query planner for some pathological
test cases by disabling OR-clause processing upon first recursion.

FossilOrigin-Name: 9fca05eac503d712886a05d03794f76c61fb39ed
2011-10-07 14:40:59 +00:00
drh 8871ef5afd Begin an effort to enhance the query planner to do a better job with OR terms
in the WHERE clause.  This change allows ANDs outside of the OR to be factored
into the OR terms if that is helpful in finding better indices.

FossilOrigin-Name: 876bd21aaac444c7e056730e35696a74e9a1af0a
2011-10-07 13:33:10 +00:00
drh f3259997c0 Enhance the sqlite3_data_count() routine so that it can be used to determine
if SQLITE_DONE has been seen on the prepared statement.

FossilOrigin-Name: 9913996e7b0f94ba1c51200b61433193002f3638
2011-10-07 12:59:23 +00:00
drh a624fd5038 Add the --pageinfo and --stats options to the sqlite3_analyzer utility.
FossilOrigin-Name: baa80c7bc31900decae0d8e6090b30fcde377492
2011-10-05 19:46:03 +00:00
drh b770894ec7 Update the sqlite3_analyzer utility program to provide more details about
the compression performance of ZIPVFS database files.

FossilOrigin-Name: fa5ed53296d45a5f963d20fb361555c432e0f31b
2011-10-05 18:18:13 +00:00
dan 2cf7e0a712 Fix test code related to reporting the size of overflow pages in zipvfs databases.
FossilOrigin-Name: ad7c9eed8bbd607babce4f5965f587c873e7bc02
2011-10-05 17:36:27 +00:00
drh c47167a6a8 When finding the appropriate file permissions for journal files with
SQLITE_ENABLE_8_3_NAMES, ignore "-" characters in the name of the
containing directory.

FossilOrigin-Name: 328cc1867ffbbf1c953dfd843649f5f209c8e6ec
2011-10-05 15:26:13 +00:00
drh d4b0ff9926 Merge the STAT3 changes into trunk.
FossilOrigin-Name: 774d0842bcce8862f2aac371f1689536ed234a0a
2011-10-03 15:30:33 +00:00
mistachkin c756dedace Update MSVC makefile to allow targets to be built with support for ICU.
FossilOrigin-Name: eb5da5e1dbe9c198095036827318fb381441cbd0
2011-10-02 05:23:16 +00:00
dan 1696124db7 Disable the xfer optimization if "PRAGMA count_changes=1" is configured. Ticket [c48d99d690].
FossilOrigin-Name: 9ddfe1e41300413bc9af7e5ce0ec9d1daf9136b1
2011-09-30 12:01:01 +00:00
drh 7b075e3a1c In the shell, allow arbitrary table names on the ".import" command.
Ticket [d1d84037b90a449].

FossilOrigin-Name: f4dd32d30ed54f847319664cf749fd0b92b4af3f
2011-09-28 01:10:00 +00:00
drh 4c9f129d60 Enhance the stat VFS to report out the total size of all pages used by
a table, even if the ZIPVFS compression backend is in play.  Update
the sqlite3_analyzer logic to use these new outputs.

FossilOrigin-Name: 19b8eaaf70db82d401d33beb7fd36045d5e8326f
2011-09-28 00:50:14 +00:00
drh f08f3843b7 Changes to sqlite3_analyzer to try to avoid integer overflow problems
when linking against older versions of TCL.

FossilOrigin-Name: 8c846311a0cd510bbf9361213bb60764e9b797aa
2011-09-27 13:40:26 +00:00
dan 64b41c7132 Changes to the analyzer script to make it work with zipvfs databases.
FossilOrigin-Name: d82cffab6acafcf9d91fea4ac47cad73f6f09775
2011-09-26 19:32:47 +00:00
drh 3051dc1c7f If an open as read/write fails, do not try to reopen as read-only if in
exclusive access mode.

FossilOrigin-Name: 263c5fb2802f8c84835e42fc66acb1065b7e42b9
2011-09-25 17:49:26 +00:00
drh 497ffc4c5d If an open as read/write fails, do not try to reopen as read-only if in
exclusive access mode.

FossilOrigin-Name: 4a153f010037db83107314269b240c0ece8430f8
2011-09-25 17:47:58 +00:00
dan a682a6b578 Fix misc3.test so that it works with OMIT_MERGE_SORT builds.
FossilOrigin-Name: 87946c627f7230bea3739fd6aeec3e56115a3f93
2011-09-24 09:54:14 +00:00
dan 5209132a48 Fix some test files so that they work with SQLITE_OMIT_WAL builds.
FossilOrigin-Name: a38668dcff0a4d241d959ea19330ad43295d757d
2011-09-24 05:55:36 +00:00
drh c78e6e4c4b Add SQLITE_DBSTATUS_CACHE_HIT and _MISS to the ".stat" command in the shell.
FossilOrigin-Name: d279e1a309909a0897b6082753a0fadb5066c31a
2011-09-23 18:58:23 +00:00
drh 0bab6c3ef7 Add the -DBUILD_sqlite option to the compiler when building testfixture.exe
using the configure script.

FossilOrigin-Name: 15a13b6c59ddf145c3fc860d993a7fa5587a0799
2011-09-23 16:34:49 +00:00
drh 790a55171e Merge the latest trunk changes into the stat3-trunk branch.
FossilOrigin-Name: 0beb88a92c8f75231df120bef54616c7e4bb68c8
2011-09-23 14:40:31 +00:00
drh 567211ed0e Testability enhancements.
FossilOrigin-Name: be44928cf2a3c063c8228b769d90947acbfad1ef
2011-09-23 13:59:33 +00:00
drh 369980189b Fix typos in the format description comment of analyze.c.
FossilOrigin-Name: 74e27fad339a2d7899c1f42805e615128929f07a
2011-09-23 13:25:03 +00:00
drh 2b9cf669d8 Remove the restriction on the number of entries per index in sqlite_stat3.
FossilOrigin-Name: 374343c8ad53829c4ad715ed623d16635797de9a
2011-09-22 20:52:56 +00:00
drh 5c62486cd8 Fix an issue in ANALYZE when STAT3 is disabled but both sqlite_stat2 and
sqlite_stat3 tables exist.  Also add testability tweaks to the STAT3 code.

FossilOrigin-Name: 3ca7e449e2e20d95e516cf7fe87bfa0b51c07086
2011-09-22 18:46:34 +00:00
dan 40f048f54b Remove a redundant (and undefined) "forcedelete" command from corruptE.test.
FossilOrigin-Name: 6d8d4e1694694c3e961fc1922f732d1da40caa6f
2011-09-22 15:31:30 +00:00
dan b5126dde0d Remove the SQLITE_PAGECACHE_BLOCKALLOC compilation option.
FossilOrigin-Name: 4eb4689834b538b9eeec5a5803528260e1aff78d
2011-09-22 14:56:31 +00:00
dan 58ca31c905 Add the SQLITE_DB_STATUS_CACHE_HIT and MISS options. For querying the number of cache hits and misses on a per-connection basis.
FossilOrigin-Name: 5100b6e9dc5107f0f835d0aac26fe6d4938ffc73
2011-09-22 14:41:16 +00:00
drh 0fe0c466ca Update the version number to 3.7.9.
FossilOrigin-Name: 14e28eb9546906b33eb03e596ad104c8ca049d6c
2011-09-22 00:56:38 +00:00
drh 93c6384ca6 Fix an uninitialized variable and a misuse of memcpy().
FossilOrigin-Name: ee110d5a4a6f29400bb632a9a18c7dcd04638657
2011-09-22 00:28:55 +00:00
mistachkin 9a55e31f4d Integrate build changes for sqlite3_analyzer into the MSVC makefile. Also, more cleanup of build files, including sqlite3_analyzer, for all makefiles.
FossilOrigin-Name: af1c5718292c0326b785416b7109eafefaeb59a7
2011-09-22 00:06:44 +00:00
drh 565621a57e Minor tweaks to the TCL code for sqlite3_analyzer.
FossilOrigin-Name: 3a261f383f11d839d3a23d454b4422298bef9c88
2011-09-21 20:10:42 +00:00
drh 8d43aa10cb Remove unnecessary dependencies from the sqlite3_analyzer targets in
makefiles.

FossilOrigin-Name: 0bd8fd352d290637d8c4d4fb76e8dd28f0149fa0
2011-09-21 18:29:49 +00:00
drh c42cdce5e0 Fix the sqlite3_analyzer target in the configure-generated Makefile.
FossilOrigin-Name: 256cdbdc810cae23388ccf73583c591304294dbb
2011-09-21 17:04:21 +00:00
dan 0ae479dff2 Change the way the sqlite3_analyzer executable is built.
FossilOrigin-Name: 05e3cced8a884e8bca6f208d2f09e335d3929eac
2011-09-21 16:43:07 +00:00
drh d3ed73406c Pull in the latest changes from trunk. Update the STAT3 documentation.
FossilOrigin-Name: 63fc3e4bea6a7f5dc34b2af83f30458eb10b8fe0
2011-09-21 00:09:41 +00:00
drh 72949adc95 Merge in all changes through the 3.7.8 release.
FossilOrigin-Name: 9607600b6ca9b546dd248982fd8e8d64f708be5c
2011-09-19 20:36:42 +00:00
drh 5c4e5e53fe Merge all the latest trunk changes into the experimental STAT3 branch.
FossilOrigin-Name: 51908c8f2bc6c086570f7493a29b096f0a40ce34
2011-09-16 19:29:58 +00:00
drh 5a9f90b574 Merge the latest trunk changes into the stat3-trunk branch.
FossilOrigin-Name: 11ca4ed8bf850dae1a24b7182f70039f32bd8dd1
2011-09-13 19:09:28 +00:00
drh 009a5f811b Merge the stat3-enhancement branch with trunk, but keep the resulting merge
in a separate branch for now.

FossilOrigin-Name: 63f2c7859fa6e5d0e2cdd218ff52a3ec2d44c61d
2011-08-26 13:16:33 +00:00
drh 8e3937ff0d Fix the stat3 analysis loader to be compatible with sqlite3_db_status().
Also fix some OOM issues with the stat3 analysis loader.

FossilOrigin-Name: eaf447ea87b0ff29ae06283204f522fcd005b284
2011-08-18 13:45:23 +00:00
drh fc0ce2a83e Fix a bug in the cleanup of stat tables on a DROP TABLE in autovacuum mode.
FossilOrigin-Name: 3fe5d54f635f7b27851d256e417f21b91febb871
2011-08-18 02:51:21 +00:00
drh 88ab5b08ad Fix an error with OOM processing in the ANALYZE logic.
FossilOrigin-Name: b26ec79c69f44b55bc4bb11e293f11b3afa3b724
2011-08-18 01:10:35 +00:00
drh 6825719667 Fix a few harmless compiler warnings. Add SQLITE_ENABLE_STAT3 to the
standard compiler warning script.

FossilOrigin-Name: 3d68f9afee02f95103eb1682b8f2362f8d249437
2011-08-16 17:06:21 +00:00
drh 8c2cd5dac5 Allow the sqlite3VdbeJumpHere() routine to accept a negative or zero address
if a prior memory allocation error has occurred.  The new sqlite_stat3 logic
needs this.

FossilOrigin-Name: 9650d7962804d61f56cac944ff9bb2c7bc111957
2011-08-16 02:07:04 +00:00
drh 461728d3d0 Add the analyze8.test test module for sqlite_stat3.
FossilOrigin-Name: 2c83ac89dc5a6017587defb541c9f3731b98892a
2011-08-16 01:15:12 +00:00
drh 50a1c56a2e Update some test cases to work with sqlite_stat3 instead of sqlite_stat2.
FossilOrigin-Name: 2504bcfb0cf14b5ce51db0af1269ac28384714e0
2011-08-15 12:58:23 +00:00
dan 23e7c4de7b Fix a couple of typos in comments in analyze.c.
FossilOrigin-Name: ae31dc67aa0637150f964de31a6da6f5797b462a
2011-08-15 12:02:21 +00:00
drh 4e50c5ec48 Further testing and bug fixing for sqlite_stat3. Added the Index.avgEq
field to index statistics.  Fixed several problems in the query planner
associated with stat3.

FossilOrigin-Name: 89b2f70884cad0abdf4c66cb64ecddb2820ded74
2011-08-13 19:35:19 +00:00
drh f404c86ad7 Add the sqlite_stat3.nDLT field. Use an linear congruence PRNG to choose
which samples to select from among those with the same nEq field.

FossilOrigin-Name: 1dcd24283e6c1cc638eb9ffac434046447f88769
2011-08-13 15:25:10 +00:00
drh ade3addfb5 The ANALYZE command picks for 15 samples for sqlite_stat3 with the largest
nEq fields, plus 5 other evenly spaced samples.

FossilOrigin-Name: 8225924ea015a0c331b69134139922ec83f989f8
2011-08-13 00:58:05 +00:00
drh faacf17cc1 Begin a branch that experimentally replaces sqlite_stat2 with a new table
called sqlite_stat3 that will hopefully facilitate better query
planning decisions.

FossilOrigin-Name: 52e1d7e8ddd4bb5ef3a9d00fd2d719a8a784f807
2011-08-12 01:51:45 +00:00
63 changed files with 2295 additions and 1377 deletions
+13 -12
View File
@@ -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
+58 -17
View File
@@ -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)
+1 -1
View File
@@ -1 +1 @@
3.7.8
3.7.9
Vendored
+196 -178
View File
File diff suppressed because it is too large Load Diff
+16 -11
View File
@@ -518,6 +518,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.
#
@@ -560,16 +570,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)
@@ -593,10 +593,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
+66 -62
View File
@@ -1,12 +1,12 @@
C Minor\scomment\schange\sin\sthe\sdescription\sof\sthe\sdifferent\smemory\sallocator\noptions.\s\sNo\schanges\sto\scode.
D 2011-09-19T20:56:59.410
C Add\stestcase()\smacros\sto\sensure\sgood\stest\scoverage.
D 2011-10-07T17:45:58.914
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
@@ -104,7 +104,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk 8744cb76517817170f7fd2c78fbf0006fabb93c1
F main.mk aa43670ca62ce7a3f4d80a5f8980c9a9ad076903
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -118,7 +118,7 @@ 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 775421ced0bd78181cc206fbc63063658066c1da
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 28a4fe55327ff708bfaf9d4326d02686f7a553c3
@@ -127,10 +127,10 @@ F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c 77b09c69d4849a90361e6fe5db36d167f20600c0
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce
F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3
F src/build.c 851e81f26a75abbb98bd99a7c5f10e8670d867bb
F src/build.c 2dc09385981bf7f41eae7d3aeb47cb2453813456
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 ff68e5ef23aee08c0ff528f699a19397ed8bbed8
F src/expr.c f4dcaeb8252c4b16fcdc245660f70ed366bc6cdd
@@ -141,7 +141,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 3eea5a53d2644116fb865afaa4699fabe62b441c
F src/insert.c ca18783512323f74aaf4ee74b46ffd75ec80d031
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
@@ -165,14 +165,14 @@ 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/pcache1.c 24f5e85a78514584b46190260ba7ab0a66312197
F src/pragma.c ebcd20f1e654f5cb3aeef864ed69c4697719fbaa
F src/prepare.c e64261559a3187698a3e7e6c8b001a4f4f98dab4
F src/printf.c 585a36b6a963df832cfb69505afa3a34ed5ef8a1
@@ -180,14 +180,14 @@ 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 0a6c9c23337fd1352c5c75a613ff9533aa7d91cb
F src/shell.c e8fe1251aee84baa2fb232ce83d938de25aa650f
F src/sqlite.h.in 1865923bdb9deb8dde42da5862aca0071adb6061
F src/sqlite3ext.h 1a1a4f784aa9c3b00edd287940197de52487cd93
F src/sqliteInt.h 76d81cd9da0618b231398bfcf90556e971972fca
F src/sqliteInt.h 2f66bf068131f0e499dd5e0abea3f68cd6b27b2d
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c 3ef1dda2f1dc207c792eaadebf9d8adc44648581
F src/tclsqlite.c 20578e66dda94b4d4db3d4478644a4dd9c6084a6
F src/test1.c 0f41b7c67719207a5de24b009e172c4dcf189827
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
@@ -201,7 +201,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 e342660556d64365aacea2b23cbb5e6654d7278f
F src/test_config.c 6b1b69da02eb9a19330d8c2c33403c6eafbc3416
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
@@ -212,7 +212,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
@@ -223,7 +223,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
@@ -235,39 +235,40 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
F src/trigger.c 1cfb80e2290ef66ea89cb4e821caae65a02c0d56
F src/update.c 74a6cfb34e9732c1e2a86278b229913b4b51eeec
F src/utf.c c53eb7404b3eb5c1cbb5655c6a7a0e0ce6bd50f0
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
F src/util.c 06302ffd2b80408d4f6c7af71f7090e0cf8d8ff7
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
F src/vacuum.c 297f39745e05d7a6ed7def7929451474d570a31e
F src/vdbe.c 60340bfb23f456ea0791cb28262a887363773371
F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
F src/vdbeInt.h 693d6ac6810298fc6b4c503cfbe3f99a240f40af
F src/vdbeapi.c 11dc47987abacb76ad016dcf5abc0dc422482a98
F src/vdbeaux.c 49be7a5ce6a1b7df9ef5791133c3e4e6ab2a1ffe
F src/vdbeaux.c 9542e2fba791f14022c23fbe5cbe179eee36abc6
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
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 a6d127dd05daf0f9ffa654edfab4f1236dc759b7
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
@@ -276,9 +277,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
@@ -345,7 +346,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
@@ -358,9 +359,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
@@ -583,16 +585,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
@@ -608,8 +610,8 @@ 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 c8f202a460ba6a1cd7a36888042ca471220bb42d
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
@@ -618,7 +620,7 @@ F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test bc67c6802989dba05cdf3a4574fd882e238c7ecf
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/permutations.test ad17319066a90e2db71823c3ff104795ffc71b31
F test/pragma.test c8108e01da04f16e67e5754e610bc62c1b993f6c
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
@@ -684,13 +686,14 @@ 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/stat3.test 53e1e3282b6e62af3de160d186fa7336fe1b48bf
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
@@ -738,7 +741,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
@@ -855,11 +859,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
@@ -893,7 +897,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
@@ -901,8 +905,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
@@ -912,10 +916,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
@@ -952,7 +956,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 2dc915c21309029dbf0eb54868ad036454d2f77a
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
@@ -962,8 +966,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 3e0da808d2f5b4d12046e05980ca04578f581177
R 8185767b36f8ee5089cf1856ba34ec13
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 4997d8b81cd3ea7c708911bfece00020d11224f9
R b935363a5df75d52ae6d528d229f42f6
U drh
Z dd5a6c14bafead231702b31c9079bdda
Z e878c83927724d1c2481f6ca56873708
+1 -1
View File
@@ -1 +1 @@
36be31ff0af7f811fe2c6f7e26f058cffb7257e1
5c132592820e9dc2355e26ea14e155c797c335b3
+594 -176
View File
@@ -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,14 @@ 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
};
static const char *azToDrop[] = {
"sqlite_stat2",
#ifndef SQLITE_ENABLE_STAT3
"sqlite_stat3",
#endif
};
@@ -59,6 +167,20 @@ static void openStatTable(
assert( sqlite3VdbeDb(v)==db );
pDb = &db->aDb[iDb];
/* Drop all statistics tables that this version of SQLite does not
** understand.
*/
for(i=0; i<ArraySize(azToDrop); i++){
Table *pTab = sqlite3FindTable(db, azToDrop[i], pDb->zName);
if( pTab ){
sqlite3CodeDropTable(pParse, pTab, iDb, 0);
break;
}
}
/* 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 +211,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 +219,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 +461,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 +514,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 +536,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 +579,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 +675,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 +700,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 +740,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 +765,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 +870,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 +913,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 +924,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 +1096,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 +1111,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 +1120,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
+83 -66
View File
@@ -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.
@@ -2095,68 +2169,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 +2656,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 +2666,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 +2946,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;
+3 -3
View File
@@ -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
+3
View File
@@ -1747,6 +1747,9 @@ static int xferOptimization(
return 0;
}
#endif
if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
return 0;
}
/* If we get this far, it means either:
**
+10 -2
View File
@@ -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';
+1 -1
View File
@@ -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{
+29 -4
View File
@@ -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.
*/
+1
View File
@@ -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);
+4 -246
View File
@@ -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 ){
+8 -3
View File
@@ -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++] = ',';
+22 -3
View File
@@ -2845,7 +2845,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 +3348,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 +5816,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 +5837,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 +5893,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
+31 -10
View File
@@ -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.
@@ -1284,7 +1296,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 */
@@ -1483,7 +1495,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 */
@@ -1494,7 +1506,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
};
/*
@@ -1504,10 +1520,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 */
};
/*
@@ -1959,10 +1979,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
@@ -2716,6 +2736,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);
@@ -2972,7 +2993,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
View File
@@ -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;
}
+39 -28
View File
@@ -3539,33 +3539,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
@@ -3649,6 +3650,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*);
@@ -3678,7 +3690,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*);
@@ -3722,7 +3733,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);
@@ -3758,12 +3768,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;
@@ -3784,7 +3795,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;
}
+6 -6
View File
@@ -424,6 +424,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
@@ -555,12 +561,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), \
+4 -2
View File
@@ -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;
+47 -16
View File
@@ -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 */
+1 -1
View File
@@ -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));
+1 -1
View File
@@ -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);
}
+3 -2
View File
@@ -575,8 +575,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);
}
@@ -1153,6 +1153,7 @@ int sqlite3VdbeList(
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
releaseMemArray(pMem, 8);
p->nResColumn = 0;
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
+3 -3
View File
@@ -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;
+276 -242
View File
@@ -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;
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);
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
if( NEVER(j>=pIdx->nColumn) ) return 0;
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;
/* 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;
}
if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
}
if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
return pTerm;
}
return pTerm;
}
}
return 0;
@@ -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;
@@ -2506,12 +2545,12 @@ static int whereRangeRegion(
assert( z && pColl && pColl->xCmp );
}
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,
@@ -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 ){
+1
View File
@@ -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 {
+10 -10
View File
@@ -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 -17
View File
@@ -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)}}
+68 -78
View File
@@ -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
+1 -1
View File
@@ -17,7 +17,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !stat2 {
ifcapable !stat3 {
finish_test
return
}
+12 -10
View File
@@ -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
View 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
+4 -2
View File
@@ -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';
+5 -1
View File
@@ -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 {
-2
View File
@@ -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
-11
View File
@@ -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
+12 -2
View File
@@ -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
View 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
+1 -1
View File
@@ -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
-7
View File
@@ -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
+5 -1
View File
@@ -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 {
+19 -17
View File
@@ -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}}
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
} {}
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
} {}
}
test_syscall reset
+27 -25
View File
@@ -1992,31 +1992,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.
-8
View File
@@ -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.
#
+16 -8
View File
@@ -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 \
+56
View File
@@ -0,0 +1,56 @@
# 2011 August 08
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file implements regression tests for SQLite library. This file
# implements tests for the extra functionality provided by the ANALYZE
# command when the library is compiled with SQLITE_ENABLE_STAT3 defined.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix stat3
# Verify that if not compiled with SQLITE_ENABLE_STAT2 that the ANALYZE
# command will delete the sqlite_stat2 table. Likewise, if not compiled
# with SQLITE_ENABLE_STAT3, the sqlite_stat3 table is deleted.
#
do_test 1.1 {
db eval {
PRAGMA writable_schema=ON;
CREATE TABLE sqlite_stat2(tbl,idx,sampleno,sample);
CREATE TABLE sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);
SELECT name FROM sqlite_master ORDER BY 1;
}
} {sqlite_stat2 sqlite_stat3}
do_test 1.2 {
db close
sqlite3 db test.db
db eval {SELECT name FROM sqlite_master ORDER BY 1}
} {sqlite_stat2 sqlite_stat3}
ifcapable {stat3} {
do_test 1.3 {
db eval {ANALYZE; SELECT name FROM sqlite_master ORDER BY 1}
} {sqlite_stat1 sqlite_stat3}
} else {
do_test 1.4 {
db eval {ANALYZE; ANALYZE; SELECT name FROM sqlite_master ORDER BY 1}
} {sqlite_stat1}
finish_test
return
}
finish_test
+5
View File
@@ -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 } {}
+26
View 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
+3 -3
View File
@@ -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
}
+4 -2
View File
@@ -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)}}
+39 -37
View File
@@ -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
}
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}
db close
tvfs1 delete
tvfs2 delete
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');
}
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}
db close
tvfs1 delete
tvfs2 delete
}
#-------------------------------------------------------------------------
# Check that only "" and "localhost" are acceptable as authorities.
+16
View File
@@ -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
+5
View File
@@ -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).
#
+5
View File
@@ -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;
+7
View File
@@ -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.
#
+1 -3
View File
@@ -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}
+79 -5
View File
@@ -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
+128 -27
View File
@@ -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,7 +38,8 @@ 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
}
@@ -34,9 +49,46 @@ if {[file size $file_to_analyze]<512} {
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 +112,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 +138,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 +157,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 +202,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 +265,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 +289,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 +340,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 +402,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 +429,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 +482,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 +501,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
#
+2 -2
View File
@@ -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 \