1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Merge the latest trunk changes into the threads branch.

FossilOrigin-Name: ae23a65eb1547fbe8b86ab71477071990a22d31d
This commit is contained in:
drh
2014-06-30 20:25:03 +00:00
27 changed files with 1629 additions and 237 deletions

View File

@ -943,8 +943,23 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TO
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.c
$(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.c $(TLIBS)
showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS)
showstat4$(TEXE): $(TOP)/tool/showstat4.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showstat4.c sqlite3.lo $(TLIBS)
showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showjournal.c sqlite3.lo $(TLIBS)
showwal$(TEXE): $(TOP)/tool/showwal.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showwal.c sqlite3.lo $(TLIBS)
rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/rollback-test.c sqlite3.lo $(TLIBS)
LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h
$(LTLINK) -I. -o $@ $(TOP)/tool/logest.c
wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c
$(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.c $(TLIBS)

View File

@ -1439,6 +1439,25 @@ showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\tool\showdb.c $(SQLITE3C)
showstat4.exe: $(TOP)\tool\showstat4.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\tool\showstat4.c $(SQLITE3C)
showjournal.exe: $(TOP)\tool\showjournal.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\tool\showjournal.c $(SQLITE3C)
showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\tool\showwal.c $(SQLITE3C)
rollback-test.exe: $(TOP)\tool\rollback-test.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\tool\rollback-test.c $(SQLITE3C)
LogEst.exe: $(TOP)\tool\logest.c sqlite3.h
$(LTLINK) -Fe$@ $(TOP)\tool\LogEst.c
wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C)
$(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
$(TOP)\test\wordcount.c $(SQLITE3C)

View File

@ -1 +1 @@
3.8.5
3.8.6

18
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.62 for sqlite 3.8.5.
# Generated by GNU Autoconf 2.62 for sqlite 3.8.6.
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.8.5'
PACKAGE_STRING='sqlite 3.8.5'
PACKAGE_VERSION='3.8.6'
PACKAGE_STRING='sqlite 3.8.6'
PACKAGE_BUGREPORT=''
# Factoring default headers for most tests.
@ -1483,7 +1483,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures sqlite 3.8.5 to adapt to many kinds of systems.
\`configure' configures sqlite 3.8.6 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1548,7 +1548,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.8.5:";;
short | recursive ) echo "Configuration of sqlite 3.8.6:";;
esac
cat <<\_ACEOF
@ -1664,7 +1664,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
sqlite configure 3.8.5
sqlite configure 3.8.6
generated by GNU Autoconf 2.62
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@ -1678,7 +1678,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.8.5, which was
It was created by sqlite $as_me 3.8.6, which was
generated by GNU Autoconf 2.62. Invocation command line was
$ $0 $@
@ -14021,7 +14021,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by sqlite $as_me 3.8.5, which was
This file was extended by sqlite $as_me 3.8.6, which was
generated by GNU Autoconf 2.62. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -14074,7 +14074,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_version="\\
sqlite config.status 3.8.5
sqlite config.status 3.8.6
configured by $0, generated by GNU Autoconf 2.62,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

23
main.mk
View File

@ -630,9 +630,28 @@ $(TEST_EXTENSION): $(TOP)/src/test_loadext.c
extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
./testfixture$(EXE) $(TOP)/test/loadext.test
showdb$(EXE): $(TOP)/tool/showdb.c sqlite3.c
showdb$(EXE): $(TOP)/tool/showdb.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showdb$(EXE) \
$(TOP)/tool/showdb.c sqlite3.c
$(TOP)/tool/showdb.c sqlite3.o $(THREADLIB)
showstat4$(EXE): $(TOP)/tool/showstat4.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showstat4$(EXE) \
$(TOP)/tool/showstat4.c sqlite3.o $(THREADLIB)
showjournal$(EXE): $(TOP)/tool/showjournal.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showjournal$(EXE) \
$(TOP)/tool/showjournal.c sqlite3.o $(THREADLIB)
showwal$(EXE): $(TOP)/tool/showwal.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showwal$(EXE) \
$(TOP)/tool/showwal.c sqlite3.o $(THREADLIB)
rollback-test$(EXE): $(TOP)/tool/rollback-test.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o rollback-test$(EXE) \
$(TOP)/tool/rollback-test.c sqlite3.o $(THREADLIB)
LogEst$(EXE): $(TOP)/tool/logest.c sqlite3.h
$(TCC) -o LogEst$(EXE) $(TOP)/tool/logest.c
wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \

View File

@ -1,12 +1,12 @@
C Merge\sin\sall\srecent\schanges\sfrom\strunk,\sand\sespecially\sthe\sautomatic\sindex\nenhancements.
D 2014-06-18T15:18:12.521
C Merge\sthe\slatest\strunk\schanges\sinto\sthe\sthreads\sbranch.
D 2014-06-30T20:25:03.546
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 0a3830965158ca1c9ec2998c9b95a1b052b0f378
F Makefile.in 6d74383a20b94af19190af16c73fad69e8127f70
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc 5c7513a1e829a33271d1469c6483d7150d6a64a9
F Makefile.msc 936a6cb0ed87b2ad69fc2abf654795f25bc2ccb7
F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0
F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8
F VERSION 9f823c026c6a32fc5f84d212a8aae0a221dba45c
F VERSION 1c877615a9db323e3cd301e3d57d853f9d5c4a07
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
@ -38,7 +38,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 5de98fac90452f876511fee8a35565a55dfb191f x
F configure 513f1e2464c3673bcdb5471b13d98e7eeb6f5ca2 x
F configure.ac 4cf9f60785143fa141b10962ccc885d973792e9a
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
@ -146,7 +146,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk ec69ae867a105e5dcae7acdeb7643ee368e45144
F main.mk bb54c720b96fec4f98c7befae9c29cd23227819b
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@ -161,7 +161,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c b00900877f766f116f9e16116f1ccacdc21d82f1
F src/analyze.c e8c8a9d20beb2ad156321330e8f4fea002d8deee
F src/analyze.c ec6e0691a6a23e0239dc733109b906ee04b89cc3
F src/attach.c 3801129015ef59d76bf23c95ef9b0069d18a0c52
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
@ -176,7 +176,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
F src/delete.c bcf8f72126cea80fc3d5bc5494cf19b3f8935aaf
F src/expr.c 4f9e497c66e2f25a4d139357a778c84d5713207c
F src/expr.c 40d06d1543b1355aa02efa9666178f7642a96ed6
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c a549cff9fe8b736cdae21650ea0af6de29b77619
F src/func.c 3bc223ea36cd29a91c481485343d0ee4257ab8dc
@ -189,7 +189,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303
F src/main.c 67b07f2b22da21b28d9162511b1aba7244ef1e57
F src/main.c f761e7548a1feaccde82d04c9e008ba9c85ede5a
F src/malloc.c 0203ebce9152c6a0e5de520140b8ba65187350be
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b
@ -207,7 +207,7 @@ F src/os.c 1b147e4cf7cc39e618115c14a086aed44bc91ace
F src/os.h 60d419395e32a8029fa380a80a3da2e9030f635e
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c 9a97268f1ea97ddea17f1c392f7c76197ac4cea9
F src/os_unix.c a7baf1b30f3c58ba20b813e01aab23b18ae44f85
F src/os_win.c 5f8c5568cc749d6ab44006124e7701f463559223
F src/os_win.h 057344a6720b4c8405d9bd98f58cb37a6ee46c25
F src/pager.c f6bb1fa6cdf2062f2d8aec3e64db302bca519ab8
@ -223,11 +223,11 @@ F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
F src/resolve.c 5fc110baeacf120a73fe34e103f052632ff11a02
F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be
F src/select.c 2657f35be771d14f56402e4aa8a7e26562415ae6
F src/shell.c 3ac174189f52b149a30b0021c4e8428c877d90d1
F src/shell.c 2aa1fd880d91bbb70d06e70e3097c3b29f946890
F src/sqlite.h.in faf9dadad407b0d5dd9e5bea26c021e1e31813b1
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
F src/sqliteInt.h 00cd3cf7be9ea28b963773de165eeb800c94d17c
F src/sqliteInt.h 54cf6813e9bce8f249f16fd1edcb899a5d19fe37
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@ -281,23 +281,23 @@ F src/threads.c 3c63f60ce0aae4c40ed4b8dc745490ff42a05308
F src/tokenize.c 6da2de6e12218ccb0aea5184b56727d011f4bee7
F src/trigger.c 66f3470b03b52b395e839155786966e3e037fddb
F src/update.c 01564b3c430f6c7b0a35afaf7aba7987206fa3a5
F src/utf.c 6dc9ec9f1b3db43ae8ba0365377f11df1ee4c01c
F src/utf.c a0314e637768a030e6e84a957d0c4f6ba910cc05
F src/util.c 049fe1d3c0e2209c1bee107aec2fcff6285f909f
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
F src/vdbe.c 1dff86d8a861b24a7636a2f586953ceb483b445f
F src/vdbe.h 394464909ed682334aa3d5831aae0c2fe2abef94
F src/vdbeInt.h c78ace64dc37495806dd50596eded1f6cd2b5a64
F src/vdbe.h c63fad052c9e7388d551e556e119c0bcf6bebdf8
F src/vdbeInt.h 027a5757abfed6492b017b45cb2daade9642e467
F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4
F src/vdbeaux.c 44d4d1f5711f71eaf0d624de5c3e4976fe4e180b
F src/vdbeaux.c 49c799bd26f9443eb08ed1bf558cf6ec5d166801
F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac
F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
F src/vdbemem.c d90a1e8acf8b63dc9d14cbbea12bfec6cec31394
F src/vdbesort.c e2784e2e1f1819a55ce6f22c6ab22eca576ae6d8
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
F src/wal.c 264df50a1b33124130b23180ded2e2c5663c652a
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
F src/where.c 7b9e13cff91a2f14ac61e6a1bc3a83b5113e6298
F src/where.c 6ff6f7e3b272fad66eea601af489777adea3cc1f
F src/whereInt.h 929c1349b5355fd44f22cee5c14d72b3329c58a6
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@ -317,7 +317,7 @@ F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
F test/analyze6.test d31defa011a561b938b4608d3538c1b4e0b5e92c
F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88
F test/analyze9.test 623e02a99a78fa12fe5def2fd559032d5d887e0f
F test/analyze9.test 93619368fff2db833747a6dfa9b1146a82e5d4d2
F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944
F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
@ -694,7 +694,7 @@ F test/mallocG.test 0ff91b65c50bdaba680fb75d87fe4ad35bb7934f
F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
F test/mallocK.test 3cff7c0f64735f6883bacdd294e45a6ed5714817
F test/malloc_common.tcl 58e54229c4132ef882a11fab6419ec4cd3073589
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
@ -830,6 +830,7 @@ F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
F test/skipscan1.test 28c7faa41a0d7265040ecb0a0abd90c0904270b2
F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
F test/skipscan5.test d8b9692b702745a0e41c23f9da6beac81df01196
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
F test/sort.test 688468cef8c9a66fcc1d54235de8e4deac745690
@ -909,6 +910,7 @@ F test/tkt-868145d012.test a5f941107ece6a64410ca4755c6329b7eb57a356
F test/tkt-8c63ff0ec.test 258b7fc8d7e4e1cb5362c7d65c143528b9c4cbed
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
F test/tkt-94c04eaadb.test f738c57c7f68ab8be1c054415af7774617cb6223
F test/tkt-9a8b09f8e6.test b2ef151d0984b2ebf237760dbeaa50724e5a0667
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
F test/tkt-9f2eb3abac.test 85bc63e749f050e6a61c8f9207f1eee65c9d3395
F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4
@ -1126,7 +1128,7 @@ F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
F test/with1.test 268081a6b14817a262ced4d0ee34d4d2a1dd2068
F test/with2.test ee227a663586aa09771cafd4fa269c5217eaf775
F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
F test/without_rowid1.test e00a0a9dc9f0be651f011d61e8a32b7add5afb30
F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
F test/without_rowid3.test eac3d5c8a1924725b58503a368f2cbd24fd6c8a0
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
@ -1163,8 +1165,9 @@ F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/showdb.c 1f3fe634d6f690b8d39ab1b9fd34583d468921e1
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
F tool/showdb.c 213e0288501b2cf67c1b2c72a9e5b8acda4738b3
F tool/showjournal.c 053eb1cc774710c6890b7dd6293300cc297b16a5
F tool/showstat4.c c39279d6bd37cb999b634f0064f6f86ad7af008f
F tool/showwal.c 3f7f7da5ec0cba51b1449a75f700493377da57b5
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
@ -1184,7 +1187,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P e4b01676d7e7ac14f5120ca3a618cc34dbf5dab2 0a52bddd9db49b2b79fc24888b50023d0fe74f7b
R 577e5bd1b61fe7b4ae68cf3df66c1682
P 0e1b73496f861bd497f92f1ead936b2d94aa52d5 f925e9baafea625f63105f8013abb3807b418379
R e2fe9819eb548cf2ae1e2b1637c3598f
U drh
Z d5db82952ebf5e7f1ce903e5ad76fefc
Z 09ae0863e1f9ef792685936307cd2a9a

View File

@ -1 +1 @@
0e1b73496f861bd497f92f1ead936b2d94aa52d5
ae23a65eb1547fbe8b86ab71477071990a22d31d

View File

@ -246,6 +246,7 @@ static void openStatTable(
assert( i<ArraySize(aTable) );
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
VdbeComment((v, aTable[i].zName));
}
}
@ -281,7 +282,8 @@ struct Stat4Sample {
struct Stat4Accum {
tRowcnt nRow; /* Number of rows in the entire table */
tRowcnt nPSample; /* How often to do a periodic sample */
int nCol; /* Number of columns in index + rowid */
int nCol; /* Number of columns in index + pk/rowid */
int nKeyCol; /* Number of index columns w/o the pk/rowid */
int mxSample; /* Maximum number of samples to accumulate */
Stat4Sample current; /* Current row as a Stat4Sample */
u32 iPrn; /* Pseudo-random number used for sampling */
@ -367,9 +369,17 @@ static void stat4Destructor(void *pOld){
}
/*
** Implementation of the stat_init(N,C) SQL function. The two parameters
** are the number of rows in the table or index (C) and the number of columns
** in the index (N). The second argument (C) is only used for STAT3 and STAT4.
** Implementation of the stat_init(N,K,C) SQL function. The three parameters
** are:
** N: The number of columns in the index including the rowid/pk
** K: The number of columns in the index excluding the rowid/pk
** C: The number of rows in the index
**
** C is only used for STAT3 and STAT4.
**
** For ordinary rowid tables, N==K+1. But for WITHOUT ROWID tables,
** N=K+P where P is the number of columns in the primary key. For the
** covering index that implements the original WITHOUT ROWID table, N==K.
**
** This routine allocates the Stat4Accum object in heap memory. The return
** value is a pointer to the the Stat4Accum object encoded as a blob (i.e.
@ -382,6 +392,7 @@ static void statInit(
){
Stat4Accum *p;
int nCol; /* Number of columns in index being sampled */
int nKeyCol; /* Number of key columns */
int nColUp; /* nCol rounded up for alignment */
int n; /* Bytes of space to allocate */
sqlite3 *db; /* Database connection */
@ -392,8 +403,11 @@ static void statInit(
/* Decode the three function arguments */
UNUSED_PARAMETER(argc);
nCol = sqlite3_value_int(argv[0]);
assert( nCol>1 ); /* >1 because it includes the rowid column */
assert( nCol>0 );
nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
nKeyCol = sqlite3_value_int(argv[1]);
assert( nKeyCol<=nCol );
assert( nKeyCol>0 );
/* Allocate the space required for the Stat4Accum object */
n = sizeof(*p)
@ -415,6 +429,7 @@ static void statInit(
p->db = db;
p->nRow = 0;
p->nCol = nCol;
p->nKeyCol = nKeyCol;
p->current.anDLt = (tRowcnt*)&p[1];
p->current.anEq = &p->current.anDLt[nColUp];
@ -425,9 +440,9 @@ static void statInit(
p->iGet = -1;
p->mxSample = mxSample;
p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[1])/(mxSample/3+1) + 1);
p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
p->current.anLt = &p->current.anEq[nColUp];
p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[1])*0xd0944565;
p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565;
/* Set up the Stat4Accum.a[] and aBest[] arrays */
p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
@ -450,7 +465,7 @@ static void statInit(
sqlite3_result_blob(context, p, sizeof(p), stat4Destructor);
}
static const FuncDef statInitFuncdef = {
1+IsStat34, /* nArg */
2+IsStat34, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@ -691,7 +706,7 @@ static void statPush(
UNUSED_PARAMETER( argc );
UNUSED_PARAMETER( context );
assert( p->nCol>1 ); /* Includes rowid field */
assert( p->nCol>0 );
assert( iChng<p->nCol );
if( p->nRow==0 ){
@ -819,7 +834,7 @@ static void statGet(
char *z;
int i;
char *zRet = sqlite3MallocZero(p->nCol * 25);
char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 );
if( zRet==0 ){
sqlite3_result_error_nomem(context);
return;
@ -827,7 +842,7 @@ static void statGet(
sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
z = zRet + sqlite3Strlen30(zRet);
for(i=0; i<(p->nCol-1); i++){
for(i=0; i<p->nKeyCol; i++){
u64 nDistinct = p->current.anDLt[i] + 1;
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
sqlite3_snprintf(24, z, " %llu", iVal);
@ -996,18 +1011,19 @@ static void analyzeOneTable(
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0;
VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
nCol = pIdx->nKeyCol;
if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIdx) ){
nCol = pIdx->nKeyCol;
zIdxName = pTab->zName;
}else{
nCol = pIdx->nColumn;
zIdxName = pIdx->zName;
}
aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*(nCol+1));
if( aGotoChng==0 ) continue;
/* Populate the register containing the index name. */
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
zIdxName = pTab->zName;
}else{
zIdxName = pIdx->zName;
}
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, zIdxName, 0);
VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName));
/*
** Pseudo-code for loop that calls stat_push():
@ -1061,12 +1077,13 @@ static void analyzeOneTable(
** The second argument is only used for STAT3 and STAT4
*/
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+2);
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
#endif
sqlite3VdbeAddOp2(v, OP_Integer, nCol+1, regStat4+1);
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4+1, regStat4);
sqlite3VdbeChangeP4(v, -1, (char*)&statInitFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 1+IsStat34);
sqlite3VdbeChangeP5(v, 2+IsStat34);
/* Implementation of the following:
**
@ -1168,7 +1185,7 @@ static void analyzeOneTable(
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
pParse->nMem = MAX(pParse->nMem, regCol+nCol+1);
pParse->nMem = MAX(pParse->nMem, regCol+nCol);
addrNext = sqlite3VdbeCurrentAddr(v);
callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid);
@ -1190,7 +1207,7 @@ static void analyzeOneTable(
i16 iCol = pIdx->aiColumn[i];
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
#endif
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);

View File

@ -33,7 +33,7 @@
char sqlite3ExprAffinity(Expr *pExpr){
int op;
pExpr = sqlite3ExprSkipCollate(pExpr);
if( pExpr->flags & EP_Generic ) return SQLITE_AFF_NONE;
if( pExpr->flags & EP_Generic ) return 0;
op = pExpr->op;
if( op==TK_SELECT ){
assert( pExpr->flags&EP_xIsSelect );

View File

@ -3138,10 +3138,10 @@ int sqlite3_test_control(int op, ...){
case SQLITE_TESTCTRL_FAULT_INSTALL: {
/* MSVC is picky about pulling func ptrs from va lists.
** http://support.microsoft.com/kb/47961
** sqlite3Config.xTestCallback = va_arg(ap, int(*)(int));
** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int));
*/
typedef int(*TESTCALLBACKFUNC_t)(int);
sqlite3Config.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t);
sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t);
rc = sqlite3FaultSim(0);
break;
}

View File

@ -445,11 +445,11 @@ static struct unix_syscall {
{ "mremap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
#endif
{ "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent)
#endif
}; /* End of the overrideable system calls */
/*
@ -3964,8 +3964,25 @@ static int unixDeviceCharacteristics(sqlite3_file *id){
return rc;
}
#ifndef SQLITE_OMIT_WAL
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
/*
** Return the system page size.
**
** This function should not be called directly by other code in this file.
** Instead, it should be called via macro osGetpagesize().
*/
static int unixGetpagesize(void){
#if defined(_BSD_SOURCE)
return getpagesize();
#else
return (int)sysconf(_SC_PAGESIZE);
#endif
}
#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */
#ifndef SQLITE_OMIT_WAL
/*
** Object used to represent an shared memory buffer.
@ -4116,20 +4133,6 @@ static int unixShmSystemLock(
return rc;
}
/*
** Return the system page size.
**
** This function should not be called directly by other code in this file.
** Instead, it should be called via macro osGetpagesize().
*/
static int unixGetpagesize(void){
#if defined(_BSD_SOURCE)
return getpagesize();
#else
return (int)sysconf(_SC_PAGESIZE);
#endif
}
/*
** Return the minimum number of 32KB shm regions that should be mapped at
** a time, assuming that each mapping must be an integer multiple of the

View File

@ -1583,6 +1583,7 @@ static char zHelp[] =
".exit Exit this program\n"
".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n"
" With no args, it turns EXPLAIN on.\n"
".fullschema Show schema and the content of sqlite_stat tables\n"
".headers on|off Turn display of headers on or off\n"
".help Show this message\n"
".import FILE TABLE Import data from FILE into TABLE\n"
@ -2412,6 +2413,44 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
if( nArg!=1 ){
fprintf(stderr, "Usage: .fullschema\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_Semi;
rc = sqlite3_exec(p->db,
"SELECT sql FROM"
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_master UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
"WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
"ORDER BY rowid",
callback, &data, &zErrMsg
);
sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master;'",
callback, &data, &zErrMsg);
data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1";
shell_exec(p->db, "SELECT * FROM sqlite_stat1",
shell_callback, &data,&zErrMsg);
data.zDestTable = "sqlite_stat3";
shell_exec(p->db, "SELECT * FROM sqlite_stat3",
shell_callback, &data,&zErrMsg);
data.zDestTable = "sqlite_stat4";
shell_exec(p->db, "SELECT * FROM sqlite_stat4",
shell_callback, &data, &zErrMsg);
data.mode = MODE_Semi;
shell_exec(p->db, "SELECT 'ANALYZE sqlite_master;'",
shell_callback, &data, &zErrMsg);
}else
if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){
if( nArg==2 ){
p->showHeader = booleanValue(azArg[1]);

View File

@ -3454,7 +3454,9 @@ void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
void sqlite3AnalyzeFunctions(void);
int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
void sqlite3Stat4ProbeFree(UnpackedRecord*);
int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
#endif
/*

View File

@ -148,8 +148,8 @@ static const unsigned char sqlite3Utf8Trans1[] = {
** and rendered as themselves even though they are technically
** invalid characters.
**
** * This routine accepts an infinite number of different UTF8 encodings
** for unicode values 0x80 and greater. It do not change over-length
** * This routine accepts over-length UTF8 encodings
** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
#define READ_UTF8(zIn, zTerm, c) \

View File

@ -209,6 +209,7 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*,int);

View File

@ -394,7 +394,6 @@ void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*);
int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *);
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3VdbeExec(Vdbe*);
int sqlite3VdbeList(Vdbe*);
int sqlite3VdbeHalt(Vdbe*);

View File

@ -3602,7 +3602,10 @@ int sqlite3VdbeRecordCompare(
/* rc==0 here means that one or both of the keys ran out of fields and
** all the fields up to that point were equal. Return the the default_rc
** value. */
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc) );
assert( CORRUPT_DB
|| vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
|| pKeyInfo->db->mallocFailed
);
return pPKey2->default_rc;
}
@ -3759,7 +3762,10 @@ static int vdbeRecordCompareString(
}
}
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res)
|| CORRUPT_DB
|| pPKey2->pKeyInfo->db->mallocFailed
);
return res;
}

View File

@ -1152,6 +1152,68 @@ void sqlite3AnalyzeFunctions(void){
}
}
/*
** Attempt to extract a value from pExpr and use it to construct *ppVal.
**
** If pAlloc is not NULL, then an UnpackedRecord object is created for
** pAlloc if one does not exist and the new value is added to the
** UnpackedRecord object.
**
** A value is extracted in the following cases:
**
** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
**
** * The expression is a bound variable, and this is a reprepare, or
**
** * The expression is a literal value.
**
** On success, *ppVal is made to point to the extracted value. The caller
** is responsible for ensuring that the value is eventually freed.
*/
static int stat4ValueFromExpr(
Parse *pParse, /* Parse context */
Expr *pExpr, /* The expression to extract a value from */
u8 affinity, /* Affinity to use */
struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */
sqlite3_value **ppVal /* OUT: New value object (or NULL) */
){
int rc = SQLITE_OK;
sqlite3_value *pVal = 0;
sqlite3 *db = pParse->db;
/* Skip over any TK_COLLATE nodes */
pExpr = sqlite3ExprSkipCollate(pExpr);
if( !pExpr ){
pVal = valueNew(db, pAlloc);
if( pVal ){
sqlite3VdbeMemSetNull((Mem*)pVal);
}
}else if( pExpr->op==TK_VARIABLE
|| NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
){
Vdbe *v;
int iBindVar = pExpr->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
if( (v = pParse->pReprepare)!=0 ){
pVal = valueNew(db, pAlloc);
if( pVal ){
rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
if( rc==SQLITE_OK ){
sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
}
pVal->db = pParse->db;
}
}
}else{
rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc);
}
assert( pVal==0 || pVal->db==db );
*ppVal = pVal;
return rc;
}
/*
** This function is used to allocate and populate UnpackedRecord
** structures intended to be compared against sample index keys stored
@ -1191,50 +1253,88 @@ int sqlite3Stat4ProbeSetValue(
int iVal, /* Array element to populate */
int *pbOk /* OUT: True if value was extracted */
){
int rc = SQLITE_OK;
int rc;
sqlite3_value *pVal = 0;
sqlite3 *db = pParse->db;
struct ValueNewStat4Ctx alloc;
alloc.pParse = pParse;
alloc.pIdx = pIdx;
alloc.ppRec = ppRec;
alloc.iVal = iVal;
/* Skip over any TK_COLLATE nodes */
pExpr = sqlite3ExprSkipCollate(pExpr);
if( !pExpr ){
pVal = valueNew(db, &alloc);
if( pVal ){
sqlite3VdbeMemSetNull((Mem*)pVal);
}
}else if( pExpr->op==TK_VARIABLE
|| NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
){
Vdbe *v;
int iBindVar = pExpr->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
if( (v = pParse->pReprepare)!=0 ){
pVal = valueNew(db, &alloc);
if( pVal ){
rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
if( rc==SQLITE_OK ){
sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
}
pVal->db = pParse->db;
}
}
}else{
rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc);
}
rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
assert( pVal==0 || pVal->db==pParse->db );
*pbOk = (pVal!=0);
assert( pVal==0 || pVal->db==db );
return rc;
}
/*
** Attempt to extract a value from expression pExpr using the methods
** as described for sqlite3Stat4ProbeSetValue() above.
**
** If successful, set *ppVal to point to a new value object and return
** SQLITE_OK. If no value can be extracted, but no other error occurs
** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error
** does occur, return an SQLite error code. The final value of *ppVal
** is undefined in this case.
*/
int sqlite3Stat4ValueFromExpr(
Parse *pParse, /* Parse context */
Expr *pExpr, /* The expression to extract a value from */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* OUT: New value object (or NULL) */
){
return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal);
}
/*
** Extract the iCol-th column from the nRec-byte record in pRec. Write
** the column value into *ppVal. If *ppVal is initially NULL then a new
** sqlite3_value object is allocated.
**
** If *ppVal is initially NULL then the caller is responsible for
** ensuring that the value written into *ppVal is eventually freed.
*/
int sqlite3Stat4Column(
sqlite3 *db, /* Database handle */
const void *pRec, /* Pointer to buffer containing record */
int nRec, /* Size of buffer pRec in bytes */
int iCol, /* Column to extract */
sqlite3_value **ppVal /* OUT: Extracted value */
){
u32 t; /* a column type code */
int nHdr; /* Size of the header in the record */
int iHdr; /* Next unread header byte */
int iField; /* Next unread data byte */
int szField; /* Size of the current data field */
int i; /* Column index */
u8 *a = (u8*)pRec; /* Typecast byte array */
Mem *pMem = *ppVal; /* Write result into this Mem object */
assert( iCol>0 );
iHdr = getVarint32(a, nHdr);
if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
iField = nHdr;
for(i=0; i<=iCol; i++){
iHdr += getVarint32(&a[iHdr], t);
testcase( iHdr==nHdr );
testcase( iHdr==nHdr+1 );
if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT;
szField = sqlite3VdbeSerialTypeLen(t);
iField += szField;
}
testcase( iField==nRec );
testcase( iField==nRec+1 );
if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
if( pMem==0 ){
pMem = *ppVal = sqlite3ValueNew(db);
if( pMem==0 ) return SQLITE_NOMEM;
}
sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
pMem->enc = ENC(db);
return SQLITE_OK;
}
/*
** Unless it is NULL, the argument must be an UnpackedRecord object returned
** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes

View File

@ -544,7 +544,7 @@ static WhereTerm *whereScanInit(
if( pIdx && iColumn>=0 ){
pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
if( NEVER(j>=pIdx->nKeyCol) ) return 0;
if( NEVER(j>pIdx->nColumn) ) return 0;
}
pScan->zCollName = pIdx->azColl[j];
}else{
@ -1998,6 +1998,114 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
return nRet;
}
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** This function is called to estimate the number of rows visited by a
** range-scan on a skip-scan index. For example:
**
** CREATE INDEX i1 ON t1(a, b, c);
** SELECT * FROM t1 WHERE a=? AND c BETWEEN ? AND ?;
**
** Value pLoop->nOut is currently set to the estimated number of rows
** visited for scanning (a=? AND b=?). This function reduces that estimate
** by some factor to account for the (c BETWEEN ? AND ?) expression based
** on the stat4 data for the index. this scan will be peformed multiple
** times (once for each (a,b) combination that matches a=?) is dealt with
** by the caller.
**
** It does this by scanning through all stat4 samples, comparing values
** extracted from pLower and pUpper with the corresponding column in each
** sample. If L and U are the number of samples found to be less than or
** equal to the values extracted from pLower and pUpper respectively, and
** N is the total number of samples, the pLoop->nOut value is adjusted
** as follows:
**
** nOut = nOut * ( min(U - L, 1) / N )
**
** If pLower is NULL, or a value cannot be extracted from the term, L is
** set to zero. If pUpper is NULL, or a value cannot be extracted from it,
** U is set to N.
**
** Normally, this function sets *pbDone to 1 before returning. However,
** if no value can be extracted from either pLower or pUpper (and so the
** estimate of the number of rows delivered remains unchanged), *pbDone
** is left as is.
**
** If an error occurs, an SQLite error code is returned. Otherwise,
** SQLITE_OK.
*/
static int whereRangeSkipScanEst(
Parse *pParse, /* Parsing & code generating context */
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 */
WhereLoop *pLoop, /* Update the .nOut value of this loop */
int *pbDone /* Set to true if at least one expr. value extracted */
){
Index *p = pLoop->u.btree.pIndex;
int nEq = pLoop->u.btree.nEq;
sqlite3 *db = pParse->db;
int nLower = -1;
int nUpper = p->nSample+1;
int rc = SQLITE_OK;
u8 aff = p->pTable->aCol[ p->aiColumn[nEq] ].affinity;
CollSeq *pColl;
sqlite3_value *p1 = 0; /* Value extracted from pLower */
sqlite3_value *p2 = 0; /* Value extracted from pUpper */
sqlite3_value *pVal = 0; /* Value extracted from record */
pColl = sqlite3LocateCollSeq(pParse, p->azColl[nEq]);
if( pLower ){
rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight, aff, &p1);
nLower = 0;
}
if( pUpper && rc==SQLITE_OK ){
rc = sqlite3Stat4ValueFromExpr(pParse, pUpper->pExpr->pRight, aff, &p2);
nUpper = p2 ? 0 : p->nSample;
}
if( p1 || p2 ){
int i;
int nDiff;
for(i=0; rc==SQLITE_OK && i<p->nSample; i++){
rc = sqlite3Stat4Column(db, p->aSample[i].p, p->aSample[i].n, nEq, &pVal);
if( rc==SQLITE_OK && p1 ){
int res = sqlite3MemCompare(p1, pVal, pColl);
if( res>=0 ) nLower++;
}
if( rc==SQLITE_OK && p2 ){
int res = sqlite3MemCompare(p2, pVal, pColl);
if( res>=0 ) nUpper++;
}
}
nDiff = (nUpper - nLower);
if( nDiff<=0 ) nDiff = 1;
/* If there is both an upper and lower bound specified, and the
** comparisons indicate that they are close together, use the fallback
** method (assume that the scan visits 1/64 of the rows) for estimating
** the number of rows visited. Otherwise, estimate the number of rows
** using the method described in the header comment for this function. */
if( nDiff!=1 || pUpper==0 || pLower==0 ){
int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff));
pLoop->nOut -= nAdjust;
*pbDone = 1;
WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
nLower, nUpper, nAdjust*-1, pLoop->nOut));
}
}else{
assert( *pbDone==0 );
}
sqlite3ValueFree(p1);
sqlite3ValueFree(p2);
sqlite3ValueFree(pVal);
return rc;
}
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
** by scanning an index for a range of values. The range may have an upper
@ -2054,95 +2162,100 @@ static int whereRangeScanEst(
int nEq = pLoop->u.btree.nEq;
if( p->nSample>0
&& nEq==pBuilder->nRecValid
&& nEq<p->nSampleCol
&& OptimizationEnabled(pParse->db, SQLITE_Stat3)
){
UnpackedRecord *pRec = pBuilder->pRec;
tRowcnt a[2];
u8 aff;
if( nEq==pBuilder->nRecValid ){
UnpackedRecord *pRec = pBuilder->pRec;
tRowcnt a[2];
u8 aff;
/* Variable iLower will be set to the estimate of the number of rows in
** the index that are less than the lower bound of the range query. The
** lower bound being the concatenation of $P and $L, where $P is the
** key-prefix formed by the nEq values matched against the nEq left-most
** columns of the index, and $L is the value in pLower.
**
** Or, if pLower is NULL or $L cannot be extracted from it (because it
** is not a simple variable or literal value), the lower bound of the
** range is $P. Due to a quirk in the way whereKeyStats() works, even
** if $L is available, whereKeyStats() is called for both ($P) and
** ($P:$L) and the larger of the two returned values used.
**
** Similarly, iUpper is to be set to the estimate of the number of rows
** less than the upper bound of the range query. Where the upper bound
** is either ($P) or ($P:$U). Again, even if $U is available, both values
** of iUpper are requested of whereKeyStats() and the smaller used.
*/
tRowcnt iLower;
tRowcnt iUpper;
/* Variable iLower will be set to the estimate of the number of rows in
** the index that are less than the lower bound of the range query. The
** lower bound being the concatenation of $P and $L, where $P is the
** key-prefix formed by the nEq values matched against the nEq left-most
** columns of the index, and $L is the value in pLower.
**
** Or, if pLower is NULL or $L cannot be extracted from it (because it
** is not a simple variable or literal value), the lower bound of the
** range is $P. Due to a quirk in the way whereKeyStats() works, even
** if $L is available, whereKeyStats() is called for both ($P) and
** ($P:$L) and the larger of the two returned values used.
**
** Similarly, iUpper is to be set to the estimate of the number of rows
** less than the upper bound of the range query. Where the upper bound
** is either ($P) or ($P:$U). Again, even if $U is available, both values
** of iUpper are requested of whereKeyStats() and the smaller used.
*/
tRowcnt iLower;
tRowcnt iUpper;
if( nEq==p->nKeyCol ){
aff = SQLITE_AFF_INTEGER;
}else{
aff = p->pTable->aCol[p->aiColumn[nEq]].affinity;
}
/* Determine iLower and iUpper using ($P) only. */
if( nEq==0 ){
iLower = 0;
iUpper = sqlite3LogEstToInt(p->aiRowLogEst[0]);
}else{
/* Note: this call could be optimized away - since the same values must
** have been requested when testing key $P in whereEqualScanEst(). */
whereKeyStats(pParse, p, pRec, 0, a);
iLower = a[0];
iUpper = a[0] + a[1];
}
/* If possible, improve on the iLower estimate using ($P:$L). */
if( pLower ){
int bOk; /* True if value is extracted from pExpr */
Expr *pExpr = pLower->pExpr->pRight;
assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
if( rc==SQLITE_OK && bOk ){
tRowcnt iNew;
whereKeyStats(pParse, p, pRec, 0, a);
iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0);
if( iNew>iLower ) iLower = iNew;
nOut--;
}
}
/* If possible, improve on the iUpper estimate using ($P:$U). */
if( pUpper ){
int bOk; /* True if value is extracted from pExpr */
Expr *pExpr = pUpper->pExpr->pRight;
assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
if( rc==SQLITE_OK && bOk ){
tRowcnt iNew;
whereKeyStats(pParse, p, pRec, 1, a);
iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0);
if( iNew<iUpper ) iUpper = iNew;
nOut--;
}
}
pBuilder->pRec = pRec;
if( rc==SQLITE_OK ){
if( iUpper>iLower ){
nNew = sqlite3LogEst(iUpper - iLower);
if( nEq==p->nKeyCol ){
aff = SQLITE_AFF_INTEGER;
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
aff = p->pTable->aCol[p->aiColumn[nEq]].affinity;
}
if( nNew<nOut ){
nOut = nNew;
/* Determine iLower and iUpper using ($P) only. */
if( nEq==0 ){
iLower = 0;
iUpper = sqlite3LogEstToInt(p->aiRowLogEst[0]);
}else{
/* Note: this call could be optimized away - since the same values must
** have been requested when testing key $P in whereEqualScanEst(). */
whereKeyStats(pParse, p, pRec, 0, a);
iLower = a[0];
iUpper = a[0] + a[1];
}
pLoop->nOut = (LogEst)nOut;
WHERETRACE(0x10, ("range scan regions: %u..%u est=%d\n",
(u32)iLower, (u32)iUpper, nOut));
return SQLITE_OK;
/* If possible, improve on the iLower estimate using ($P:$L). */
if( pLower ){
int bOk; /* True if value is extracted from pExpr */
Expr *pExpr = pLower->pExpr->pRight;
assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
if( rc==SQLITE_OK && bOk ){
tRowcnt iNew;
whereKeyStats(pParse, p, pRec, 0, a);
iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0);
if( iNew>iLower ) iLower = iNew;
nOut--;
}
}
/* If possible, improve on the iUpper estimate using ($P:$U). */
if( pUpper ){
int bOk; /* True if value is extracted from pExpr */
Expr *pExpr = pUpper->pExpr->pRight;
assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
if( rc==SQLITE_OK && bOk ){
tRowcnt iNew;
whereKeyStats(pParse, p, pRec, 1, a);
iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0);
if( iNew<iUpper ) iUpper = iNew;
nOut--;
}
}
pBuilder->pRec = pRec;
if( rc==SQLITE_OK ){
if( iUpper>iLower ){
nNew = sqlite3LogEst(iUpper - iLower);
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
}
if( nNew<nOut ){
nOut = nNew;
}
pLoop->nOut = (LogEst)nOut;
WHERETRACE(0x10, ("range scan regions: %u..%u est=%d\n",
(u32)iLower, (u32)iUpper, nOut));
return SQLITE_OK;
}
}else{
int bDone = 0;
rc = whereRangeSkipScanEst(pParse, pLower, pUpper, pLoop, &bDone);
if( bDone ) return rc;
}
}
#else
@ -2201,7 +2314,7 @@ static int whereEqualScanEst(
int bOk;
assert( nEq>=1 );
assert( nEq<=(p->nKeyCol+1) );
assert( nEq<=p->nColumn );
assert( p->aSample!=0 );
assert( p->nSample>0 );
assert( pBuilder->nRecValid<nEq );
@ -2214,7 +2327,7 @@ static int whereEqualScanEst(
/* This is an optimization only. The call to sqlite3Stat4ProbeSetValue()
** below would return the same value. */
if( nEq>p->nKeyCol ){
if( nEq>=p->nColumn ){
*pnRow = 1;
return SQLITE_OK;
}
@ -2645,7 +2758,7 @@ static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
txt.db = db;
sqlite3StrAccumAppend(&txt, " (", 2);
for(i=0; i<nEq; i++){
char *z = (i==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[i]].zName;
char *z = aiColumn[i] < 0 ? "rowid" : aCol[aiColumn[i]].zName;
if( i>=nSkip ){
explainAppendTerm(&txt, i, z, "=");
}else{
@ -2658,11 +2771,11 @@ static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
j = i;
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
char *z = (j==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[j]].zName;
char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i++, z, ">");
}
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
char *z = (j==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[j]].zName;
char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i, z, "<");
}
sqlite3StrAccumAppend(&txt, ")", 1);
@ -4164,12 +4277,9 @@ static int whereLoopAddBtreeIndex(
}
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
assert( pNew->u.btree.nEq<=pProbe->nKeyCol );
if( pNew->u.btree.nEq < pProbe->nKeyCol ){
iCol = pProbe->aiColumn[pNew->u.btree.nEq];
}else{
iCol = -1;
}
assert( pNew->u.btree.nEq<pProbe->nColumn );
iCol = pProbe->aiColumn[pNew->u.btree.nEq];
pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
opMask, pProbe);
saved_nEq = pNew->u.btree.nEq;
@ -4359,7 +4469,7 @@ static int whereLoopAddBtreeIndex(
}
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
&& pNew->u.btree.nEq<(pProbe->nKeyCol + (pProbe->zName!=0))
&& pNew->u.btree.nEq<pProbe->nColumn
){
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
}
@ -4506,6 +4616,7 @@ static int whereLoopAddBtree(
Index *pFirst; /* First of real indices on the table */
memset(&sPk, 0, sizeof(Index));
sPk.nKeyCol = 1;
sPk.nColumn = 1;
sPk.aiColumn = &aiColumnPk;
sPk.aiRowLogEst = aiRowEstPk;
sPk.onError = OE_Replace;

View File

@ -953,4 +953,72 @@ for {set i 0} {$i<16} {incr i} {
} {1}
}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 21.0 {
CREATE TABLE t2(a, b);
CREATE INDEX i2 ON t2(a);
}
do_test 21.1 {
for {set i 1} {$i < 100} {incr i} {
execsql {
INSERT INTO t2 VALUES(CASE WHEN $i < 80 THEN 'one' ELSE 'two' END, $i)
}
}
execsql ANALYZE
} {}
# Condition (a='one') matches 80% of the table. (rowid<10) reduces this to
# 10%, but (rowid<50) only reduces it to 50%. So in the first case below
# the index is used. In the second, it is not.
#
do_eqp_test 21.2 {
SELECT * FROM t2 WHERE a='one' AND rowid < 10
} {/*USING INDEX i2 (a=? AND rowid<?)*/}
do_eqp_test 21.3 {
SELECT * FROM t2 WHERE a='one' AND rowid < 50
} {/*USING INTEGER PRIMARY KEY*/}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 22.0 {
CREATE TABLE t3(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID;
}
do_execsql_test 22.1 {
WITH r(x) AS (
SELECT 1
UNION ALL
SELECT x+1 FROM r WHERE x<=100
)
INSERT INTO t3 SELECT
CASE WHEN (x>45 AND x<96) THEN 'B' ELSE 'A' END, /* Column "a" */
x, /* Column "b" */
CASE WHEN (x<51) THEN 'one' ELSE 'two' END, /* Column "c" */
x /* Column "d" */
FROM r;
CREATE INDEX i3 ON t3(c);
CREATE INDEX i4 ON t3(d);
ANALYZE;
}
# Expression (c='one' AND a='B') matches 5 table rows. But (c='one' AND a=A')
# matches 45. Expression (d<?) matches 20. Neither index is a covering index.
#
# Therefore, with stat4 data, SQLite prefers (c='one' AND a='B') over (d<20),
# and (d<20) over (c='one' AND a='A').
foreach {tn where res} {
1 "c='one' AND a='B' AND d < 20" {/*INDEX i3 (c=? AND a=?)*/}
2 "c='one' AND a='A' AND d < 20" {/*INDEX i4 (d<?)*/}
} {
do_eqp_test 22.2.$tn "SELECT * FROM t3 WHERE $where" $res
}
finish_test

View File

@ -16,6 +16,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set testprefix mallocK
set sql {SELECT * FROM t1, t2 WHERE (a=1 OR a=2)}
for {set x 1} {$x<5} {incr x} {
@ -68,5 +69,70 @@ ifcapable vtab {
}
}
#-------------------------------------------------------------------------
# Test that OOM errors are correctly handled by the code that uses stat4
# data to estimate the number of rows visited by a skip-scan range query.
#
add_alignment_test_collations db
do_execsql_test 6.0 {
CREATE TABLE t3(a TEXT, b TEXT COLLATE utf16_aligned, c);
INSERT INTO t3 VALUES('one', '.....', 0);
INSERT INTO t3 VALUES('one', '....x', 1);
INSERT INTO t3 VALUES('one', '...x.', 2);
INSERT INTO t3 VALUES('one', '...xx', 3);
INSERT INTO t3 VALUES('one', '..x..', 4);
INSERT INTO t3 VALUES('one', '..x.x', 5);
INSERT INTO t3 VALUES('one', '..xx.', 6);
INSERT INTO t3 VALUES('one', '..xxx', 7);
INSERT INTO t3 VALUES('one', '.x...', 8);
INSERT INTO t3 VALUES('one', '.x..x', 9);
INSERT INTO t3 VALUES('one', '.x.x.', 10);
INSERT INTO t3 VALUES('one', '.x.xx', 11);
INSERT INTO t3 VALUES('one', '.xx..', 12);
INSERT INTO t3 VALUES('one', '.xx.x', 13);
INSERT INTO t3 VALUES('one', '.xxx.', 14);
INSERT INTO t3 VALUES('one', '.xxxx', 15);
INSERT INTO t3 VALUES('two', 'x....', 16);
INSERT INTO t3 VALUES('two', 'x...x', 17);
INSERT INTO t3 VALUES('two', 'x..x.', 18);
INSERT INTO t3 VALUES('two', 'x..xx', 19);
INSERT INTO t3 VALUES('two', 'x.x..', 20);
INSERT INTO t3 VALUES('two', 'x.x.x', 21);
INSERT INTO t3 VALUES('two', 'x.xx.', 22);
INSERT INTO t3 VALUES('two', 'x.xxx', 23);
INSERT INTO t3 VALUES('two', 'xx...', 24);
INSERT INTO t3 VALUES('two', 'xx..x', 25);
INSERT INTO t3 VALUES('two', 'xx.x.', 26);
INSERT INTO t3 VALUES('two', 'xx.xx', 27);
INSERT INTO t3 VALUES('two', 'xxx..', 28);
INSERT INTO t3 VALUES('two', 'xxx.x', 29);
INSERT INTO t3 VALUES('two', 'xxxx.', 30);
INSERT INTO t3 VALUES('two', 'xxxxx', 31);
INSERT INTO t3 SELECT * FROM t3;
CREATE INDEX i3 ON t3(a, b);
ANALYZE;
SELECT 'x' > '.';
} {1}
ifcapable stat4 {
do_eqp_test 6.1 {
SELECT DISTINCT c FROM t3 WHERE b BETWEEN '.xx..' AND '.xxxx';
} {
0 0 0 {SEARCH TABLE t3 USING INDEX i3 (ANY(a) AND b>? AND b<?)}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
}
}
do_faultsim_test 6 -faults oom* -body {
db cache flush
db eval { SELECT DISTINCT c FROM t3 WHERE b BETWEEN '.xx..' AND '.xxxx' }
} -test {
faultsim_test_result {0 {12 13 14 15}}
}
finish_test

186
test/skipscan5.test Normal file
View File

@ -0,0 +1,186 @@
# 2013-11-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 of the "skip-scan" query strategy. In
# particular it tests that stat4 data can be used by a range query
# that uses the skip-scan approach.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix skipscan5
ifcapable !stat4 {
finish_test
return
}
do_execsql_test 1.1 {
CREATE TABLE t1(a INT, b INT, c INT);
CREATE INDEX i1 ON t1(a, b);
} {}
expr srand(4)
do_test 1.2 {
for {set i 0} {$i < 100} {incr i} {
set a [expr int(rand()*4.0) + 1]
set b [expr int(rand()*20.0) + 1]
execsql { INSERT INTO t1 VALUES($a, $b, NULL) }
}
execsql ANALYZE
} {}
foreach {tn q res} {
1 "b = 5" {/*ANY(a) AND b=?*/}
2 "b > 12 AND b < 16" {/*ANY(a) AND b>? AND b<?*/}
3 "b > 2 AND b < 16" {/*SCAN TABLE t1*/}
4 "b > 18 AND b < 25" {/*ANY(a) AND b>? AND b<?*/}
5 "b > 15" {/*ANY(a) AND b>?*/}
6 "b > 5" {/*SCAN TABLE t1*/}
7 "b < 15" {/*SCAN TABLE t1*/}
8 "b < 5" {/*ANY(a) AND b<?*/}
9 "5 > b" {/*ANY(a) AND b<?*/}
10 "b = '5'" {/*ANY(a) AND b=?*/}
11 "b > '12' AND b < '16'" {/*ANY(a) AND b>? AND b<?*/}
12 "b > '2' AND b < '16'" {/*SCAN TABLE t1*/}
13 "b > '18' AND b < '25'" {/*ANY(a) AND b>? AND b<?*/}
14 "b > '15'" {/*ANY(a) AND b>?*/}
15 "b > '5'" {/*SCAN TABLE t1*/}
16 "b < '15'" {/*SCAN TABLE t1*/}
17 "b < '5'" {/*ANY(a) AND b<?*/}
18 "'5' > b" {/*ANY(a) AND b<?*/}
} {
set sql "EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE $q"
do_execsql_test 1.3.$tn $sql $res
}
#-------------------------------------------------------------------------
# Test that range-query/skip-scan estimation works with text values.
# And on UTF-16 databases when there is no UTF-16 collation sequence
# available.
#
proc test_collate {enc lhs rhs} {
string compare $lhs $rhs
}
foreach {tn dbenc coll} {
1 UTF-8 { add_test_collate db 0 0 1 }
2 UTF-16 { add_test_collate db 1 0 0 }
3 UTF-8 { add_test_collate db 0 1 0 }
} {
reset_db
eval $coll
do_execsql_test 2.$tn.1 " PRAGMA encoding = '$dbenc' "
do_execsql_test 2.$tn.2 {
CREATE TABLE t2(a TEXT, b TEXT, c TEXT COLLATE test_collate, d TEXT);
CREATE INDEX i2 ON t2(a, b, c);
}
set vocab(d) { :) }
set vocab(c) { a b c d e f g h i j k l m n o p q r s t }
set vocab(b) { one two three }
set vocab(a) { sql }
do_test 2.$tn.3 {
for {set i 0} {$i < 100} {incr i} {
foreach var {a b c d} {
set $var [lindex $vocab($var) [expr $i % [llength $vocab($var)]]]
}
execsql { INSERT INTO t2 VALUES($a, $b, $c, $d) }
}
execsql ANALYZE
} {}
foreach {tn2 q res} {
1 { c BETWEEN 'd' AND 'e' } {/*ANY(a) AND ANY(b) AND c>? AND c<?*/}
2 { c BETWEEN 'b' AND 'r' } {/*SCAN TABLE t2*/}
3 { c > 'q' } {/*ANY(a) AND ANY(b) AND c>?*/}
4 { c > 'e' } {/*SCAN TABLE t2*/}
5 { c < 'q' } {/*SCAN TABLE t2*/}
4 { c < 'e' } {/*ANY(a) AND ANY(b) AND c<?*/}
} {
set sql "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE $q"
do_execsql_test 2.$tn.$tn2 $sql $res
}
}
#-------------------------------------------------------------------------
# Test that range-query/skip-scan estimation works on columns that contain
# a variety of types.
#
reset_db
do_execsql_test 3.1 {
CREATE TABLE t3(a, b, c);
CREATE INDEX i3 ON t3(a, b);
}
set values {
NULL NULL NULL
NULL -9567 -9240
-8725 -8659 -8248.340244520614
-8208 -7939 -7746.985758536954
-7057 -6550 -5916
-5363 -4935.781822975623 -4935.063633571875
-3518.4554911770183 -2537 -2026
-1511.2603881914456 -1510.4195994839156 -1435
-1127.4210136045804 -1045 99
1353 1457 1563.2908193223611
2245 2286 2552
2745.18831295203 2866.279926554429 3075.0468527316334
3447 3867 4237.892420141907
4335 5052.9775000424015 5232.178240656935
5541.784919585003 5749.725576373621 5758
6005 6431 7263.477992854769
7441 7541 8667.279760663994
8857 9199.638673662972 'dl'
'dro' 'h' 'igprfq'
'jnbd' 'k' 'kordee'
'lhwcv' 'mzlb' 'nbjked'
'nufpo' 'nxqkdq' 'shelln'
'tvzn' 'wpnt' 'wylf'
'ydkgu' 'zdb' X''
X'0a' X'203f6429f1f33f' X'23858e324545e0362b'
X'3f9f8a' X'516f7ddd4b' X'68f1df0930ac6b'
X'9ea60d' X'a06f' X'aefd342a39ce36df'
X'afaa020fe2' X'be201c' X'c47d97b209601e45'
}
do_test 3.2 {
set c 0
foreach v $values {
execsql "INSERT INTO t3 VALUES($c % 2, $v, $c)"
incr c
}
execsql ANALYZE
} {}
foreach {tn q res} {
1 "b BETWEEN -10000 AND -8000" {/*ANY(a) AND b>? AND b<?*/}
2 "b BETWEEN -10000 AND 'qqq'" {/*SCAN TABLE t3*/}
3 "b < X'5555'" {/*SCAN TABLE t3*/}
4 "b > X'5555'" {/*ANY(a) AND b>?*/}
5 "b > 'zzz'" {/*ANY(a) AND b>?*/}
6 "b < 'zzz'" {/*SCAN TABLE t3*/}
} {
set sql "EXPLAIN QUERY PLAN SELECT * FROM t3 WHERE $q"
do_execsql_test 3.3.$tn $sql $res
}
finish_test

323
test/tkt-9a8b09f8e6.test Normal file
View File

@ -0,0 +1,323 @@
# 2014 June 26
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# This file implements tests to verify that ticket [9a8b09f8e6] has been
# fixed.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tkt-9a8b09f8e6
do_test 1.1 {
execsql {
CREATE TABLE t1(x TEXT);
INSERT INTO t1 VALUES('1');
}
} {}
do_test 1.2 {
execsql {
CREATE TABLE t2(x INTEGER);
INSERT INTO t2 VALUES(1);
}
} {}
do_test 1.3 {
execsql {
CREATE TABLE t3(x REAL);
INSERT INTO t3 VALUES(1.0);
}
} {}
do_test 1.4 {
execsql {
CREATE TABLE t4(x REAL);
INSERT INTO t4 VALUES(1.11);
}
} {}
do_test 1.5 {
execsql {
CREATE TABLE t5(x, y);
INSERT INTO t5 VALUES('1', 'one');
INSERT INTO t5 VALUES(1, 'two');
INSERT INTO t5 VALUES('1.0', 'three');
INSERT INTO t5 VALUES(1.0, 'four');
}
} {}
do_test 2.1 {
execsql {
SELECT x FROM t1 WHERE x IN (1);
}
} {1}
do_test 2.2 {
execsql {
SELECT x FROM t1 WHERE x IN (1.0);
}
} {}
do_test 2.3 {
execsql {
SELECT x FROM t1 WHERE x IN ('1');
}
} {1}
do_test 2.4 {
execsql {
SELECT x FROM t1 WHERE x IN ('1.0');
}
} {}
do_test 2.5 {
execsql {
SELECT x FROM t1 WHERE 1 IN (x);
}
} {}
do_test 2.6 {
execsql {
SELECT x FROM t1 WHERE 1.0 IN (x);
}
} {}
do_test 2.7 {
execsql {
SELECT x FROM t1 WHERE '1' IN (x);
}
} {1}
do_test 2.8 {
execsql {
SELECT x FROM t1 WHERE '1.0' IN (x);
}
} {}
do_test 3.1 {
execsql {
SELECT x FROM t2 WHERE x IN (1);
}
} {1}
do_test 3.2 {
execsql {
SELECT x FROM t2 WHERE x IN (1.0);
}
} {1}
do_test 3.3 {
execsql {
SELECT x FROM t2 WHERE x IN ('1');
}
} {1}
do_test 3.4 {
execsql {
SELECT x FROM t2 WHERE x IN ('1.0');
}
} {1}
do_test 3.5 {
execsql {
SELECT x FROM t2 WHERE 1 IN (x);
}
} {1}
do_test 3.6 {
execsql {
SELECT x FROM t2 WHERE 1.0 IN (x);
}
} {1}
do_test 3.7 {
execsql {
SELECT x FROM t2 WHERE '1' IN (x);
}
} {}
do_test 3.8 {
execsql {
SELECT x FROM t2 WHERE '1.0' IN (x);
}
} {}
do_test 4.1 {
execsql {
SELECT x FROM t3 WHERE x IN (1);
}
} {1.0}
do_test 4.2 {
execsql {
SELECT x FROM t3 WHERE x IN (1.0);
}
} {1.0}
do_test 4.3 {
execsql {
SELECT x FROM t3 WHERE x IN ('1');
}
} {1.0}
do_test 4.4 {
execsql {
SELECT x FROM t3 WHERE x IN ('1.0');
}
} {1.0}
do_test 4.5 {
execsql {
SELECT x FROM t3 WHERE 1 IN (x);
}
} {1.0}
do_test 4.6 {
execsql {
SELECT x FROM t3 WHERE 1.0 IN (x);
}
} {1.0}
do_test 4.7 {
execsql {
SELECT x FROM t3 WHERE '1' IN (x);
}
} {}
do_test 4.8 {
execsql {
SELECT x FROM t3 WHERE '1.0' IN (x);
}
} {}
do_test 5.1 {
execsql {
SELECT x FROM t4 WHERE x IN (1);
}
} {}
do_test 5.2 {
execsql {
SELECT x FROM t4 WHERE x IN (1.0);
}
} {}
do_test 5.3 {
execsql {
SELECT x FROM t4 WHERE x IN ('1');
}
} {}
do_test 5.4 {
execsql {
SELECT x FROM t4 WHERE x IN ('1.0');
}
} {}
do_test 5.5 {
execsql {
SELECT x FROM t4 WHERE x IN (1.11);
}
} {1.11}
do_test 5.6 {
execsql {
SELECT x FROM t4 WHERE x IN ('1.11');
}
} {1.11}
do_test 5.7 {
execsql {
SELECT x FROM t4 WHERE 1 IN (x);
}
} {}
do_test 5.8 {
execsql {
SELECT x FROM t4 WHERE 1.0 IN (x);
}
} {}
do_test 5.9 {
execsql {
SELECT x FROM t4 WHERE '1' IN (x);
}
} {}
do_test 5.10 {
execsql {
SELECT x FROM t4 WHERE '1.0' IN (x);
}
} {}
do_test 5.11 {
execsql {
SELECT x FROM t4 WHERE 1.11 IN (x);
}
} {1.11}
do_test 5.12 {
execsql {
SELECT x FROM t4 WHERE '1.11' IN (x);
}
} {}
do_test 6.1 {
execsql {
SELECT x, y FROM t5 WHERE x IN (1);
}
} {1 two 1.0 four}
do_test 6.2 {
execsql {
SELECT x, y FROM t5 WHERE x IN (1.0);
}
} {1 two 1.0 four}
do_test 6.3 {
execsql {
SELECT x, y FROM t5 WHERE x IN ('1');
}
} {1 one}
do_test 6.4 {
execsql {
SELECT x, y FROM t5 WHERE x IN ('1.0');
}
} {1.0 three}
do_test 6.5 {
execsql {
SELECT x, y FROM t5 WHERE 1 IN (x);
}
} {1 two 1.0 four}
do_test 6.6 {
execsql {
SELECT x, y FROM t5 WHERE 1.0 IN (x);
}
} {1 two 1.0 four}
do_test 6.7 {
execsql {
SELECT x, y FROM t5 WHERE '1' IN (x);
}
} {1 one}
do_test 6.8 {
execsql {
SELECT x, y FROM t5 WHERE '1.0' IN (x);
}
} {1.0 three}
finish_test

View File

@ -213,5 +213,69 @@ do_execsql_test 4.1 {
do_execsql_test 4.2 {
SELECT t42.rowid FROM t42, t41;
} {1}
#--------------------------------------------------------------------------
# The following tests verify that the trailing PK fields added to each
# entry in an index on a WITHOUT ROWID table are used correctly.
#
do_execsql_test 5.0 {
CREATE TABLE t45(a PRIMARY KEY, b, c) WITHOUT ROWID;
CREATE INDEX i45 ON t45(b);
INSERT INTO t45 VALUES(2, 'one', 'x');
INSERT INTO t45 VALUES(4, 'one', 'x');
INSERT INTO t45 VALUES(6, 'one', 'x');
INSERT INTO t45 VALUES(8, 'one', 'x');
INSERT INTO t45 VALUES(10, 'one', 'x');
INSERT INTO t45 VALUES(1, 'two', 'x');
INSERT INTO t45 VALUES(3, 'two', 'x');
INSERT INTO t45 VALUES(5, 'two', 'x');
INSERT INTO t45 VALUES(7, 'two', 'x');
INSERT INTO t45 VALUES(9, 'two', 'x');
}
do_eqp_test 5.1 {
SELECT * FROM t45 WHERE b=? AND a>?
} {/*USING INDEX i45 (b=? AND a>?)*/}
do_execsql_test 5.2 {
SELECT * FROM t45 WHERE b='two' AND a>4
} {5 two x 7 two x 9 two x}
do_execsql_test 5.3 {
SELECT * FROM t45 WHERE b='one' AND a<8
} { 2 one x 4 one x 6 one x }
do_execsql_test 5.4 {
CREATE TABLE t46(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID;
WITH r(x) AS (
SELECT 1 UNION ALL SELECT x+1 FROM r WHERE x<100
)
INSERT INTO t46 SELECT x / 20, x % 20, x % 10, x FROM r;
}
set queries {
1 2 "c = 5 AND a = 1" {/*i46 (c=? AND a=?)*/}
2 6 "c = 4 AND a < 3" {/*i46 (c=? AND a<?)*/}
3 4 "c = 2 AND a >= 3" {/*i46 (c=? AND a>?)*/}
4 1 "c = 2 AND a = 1 AND b<10" {/*i46 (c=? AND a=? AND b<?)*/}
5 1 "c = 0 AND a = 0 AND b>5" {/*i46 (c=? AND a=? AND b>?)*/}
}
foreach {tn cnt where eqp} $queries {
do_execsql_test 5.5.$tn.1 "SELECT count(*) FROM t46 WHERE $where" $cnt
}
do_execsql_test 5.6 {
CREATE INDEX i46 ON t46(c);
}
foreach {tn cnt where eqp} $queries {
do_execsql_test 5.7.$tn.1 "SELECT count(*) FROM t46 WHERE $where" $cnt
do_eqp_test 5.7.$tn.2 "SELECT count(*) FROM t46 WHERE $where" $eqp
}
finish_test

View File

@ -66,7 +66,7 @@ static unsigned char *getContent(int ofst, int nByte){
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
lseek(db, ofst, SEEK_SET);
read(db, aData, nByte);
if( read(db, aData, nByte)<nByte ) memset(aData, 0, nByte);
return aData;
}
@ -129,6 +129,7 @@ static void print_page(int iPg){
free(aData);
}
/* Print a line of decode output showing a 4-byte integer.
*/
static void print_decode_line(
@ -341,6 +342,179 @@ static int describeCell(
return nLocal+n;
}
/* Print an offset followed by nByte bytes. Add extra white-space
** at the end so that subsequent text is aligned.
*/
static void printBytes(
unsigned char *aData, /* Content being decoded */
unsigned char *aStart, /* Start of content to be printed */
int nByte /* Number of bytes to print */
){
int j;
printf(" %03x: ", (int)(aStart-aData));
for(j=0; j<9; j++){
if( j>=nByte ){
printf(" ");
}else{
printf("%02x ", aStart[j]);
}
}
}
/*
** Write a full decode on stdout for the cell at a[ofst].
** Assume the page contains a header of size szPgHdr bytes.
*/
static void decodeCell(
unsigned char *a, /* Page content (without the page-1 header) */
unsigned pgno, /* Page number */
int iCell, /* Cell index */
int szPgHdr, /* Size of the page header. 0 or 100 */
int ofst /* Cell begins at a[ofst] */
){
int i, j, k;
int leftChild;
i64 nPayload;
i64 rowid;
i64 nHdr;
i64 iType;
int nLocal;
unsigned char *x = a + ofst;
unsigned char *end;
unsigned char cType = a[0];
int nCol = 0;
int szCol[2000];
int ofstCol[2000];
int typeCol[2000];
printf("Cell[%d]:\n", iCell);
if( cType<=5 ){
leftChild = ((x[0]*256 + x[1])*256 + x[2])*256 + x[3];
printBytes(a, x, 4);
printf("left child page:: %d\n", leftChild);
x += 4;
}
if( cType!=5 ){
i = decodeVarint(x, &nPayload);
printBytes(a, x, i);
nLocal = localPayload(nPayload, cType);
if( nLocal==nPayload ){
printf("payload-size: %d\n", (int)nPayload);
}else{
printf("payload-size: %d (%d local, %d overflow)\n",
(int)nPayload, nLocal, (int)(nPayload-nLocal));
}
x += i;
}else{
nPayload = nLocal = 0;
}
end = x + nLocal;
if( cType==5 || cType==13 ){
i = decodeVarint(x, &rowid);
printBytes(a, x, i);
printf("rowid: %lld\n", rowid);
x += i;
}
if( nLocal>0 ){
i = decodeVarint(x, &nHdr);
printBytes(a, x, i);
printf("record-header-size: %d\n", (int)nHdr);
j = i;
nCol = 0;
k = nHdr;
while( x+j<end && j<nHdr ){
const char *zTypeName;
int sz = 0;
char zNm[30];
i = decodeVarint(x+j, &iType);
printBytes(a, x+j, i);
printf("typecode[%d]: %d - ", nCol, (int)iType);
switch( iType ){
case 0: zTypeName = "NULL"; sz = 0; break;
case 1: zTypeName = "int8"; sz = 1; break;
case 2: zTypeName = "int16"; sz = 2; break;
case 3: zTypeName = "int24"; sz = 3; break;
case 4: zTypeName = "int32"; sz = 4; break;
case 5: zTypeName = "int48"; sz = 6; break;
case 6: zTypeName = "int64"; sz = 8; break;
case 7: zTypeName = "double"; sz = 8; break;
case 8: zTypeName = "zero"; sz = 0; break;
case 9: zTypeName = "one"; sz = 0; break;
case 10:
case 11: zTypeName = "error"; sz = 0; break;
default: {
sz = (int)(iType-12)/2;
sprintf(zNm, (iType&1)==0 ? "blob(%d)" : "text(%d)", sz);
zTypeName = zNm;
break;
}
}
printf("%s\n", zTypeName);
szCol[nCol] = sz;
ofstCol[nCol] = k;
typeCol[nCol] = (int)iType;
k += sz;
nCol++;
j += i;
}
for(i=0; i<nCol && ofstCol[i]+szCol[i]<=nLocal; i++){
int s = ofstCol[i];
i64 v;
const unsigned char *pData;
if( szCol[i]==0 ) continue;
printBytes(a, x+s, szCol[i]);
printf("data[%d]: ", i);
pData = x+s;
if( typeCol[i]<=7 ){
v = (signed char)pData[0];
for(k=1; k<szCol[i]; k++){
v = (v<<8) + pData[k];
}
if( typeCol[i]==7 ){
double r;
memcpy(&r, &v, sizeof(r));
printf("%#g\n", r);
}else{
printf("%lld\n", v);
}
}else{
int ii, jj;
char zConst[32];
if( (typeCol[i]&1)==0 ){
zConst[0] = 'x';
zConst[1] = '\'';
for(ii=2, jj=0; jj<szCol[i] && ii<24; jj++, ii+=2){
sprintf(zConst+ii, "%02x", pData[jj]);
}
}else{
zConst[0] = '\'';
for(ii=1, jj=0; jj<szCol[i] && ii<24; jj++, ii++){
zConst[ii] = isprint(pData[jj]) ? pData[jj] : '.';
}
zConst[ii] = 0;
}
if( jj<szCol[i] ){
memcpy(zConst+ii, "...'", 5);
}else{
memcpy(zConst+ii, "'", 2);
}
printf("%s\n", zConst);
}
j = ofstCol[i] + szCol[i];
}
}
if( j<nLocal ){
printBytes(a, x+j, 0);
printf("... %d bytes of content ...\n", nLocal-j);
}
if( nLocal<nPayload ){
printBytes(a, x+nLocal, 4);
printf("overflow-page: %d\n", decodeInt32(x+nLocal));
}
}
/*
** Decode a btree page
*/
@ -356,6 +530,7 @@ static void decode_btree_page(
int iCellPtr;
int showCellContent = 0;
int showMap = 0;
int cellToDecode = -2;
char *zMap = 0;
switch( a[0] ){
case 2: zType = "index interior node"; break;
@ -367,23 +542,37 @@ static void decode_btree_page(
switch( zArgs[0] ){
case 'c': showCellContent = 1; break;
case 'm': showMap = 1; break;
case 'd': {
if( !isdigit(zArgs[1]) ){
cellToDecode = -1;
}else{
cellToDecode = 0;
while( isdigit(zArgs[1]) ){
zArgs++;
cellToDecode = cellToDecode*10 + zArgs[0] - '0';
}
}
break;
}
}
zArgs++;
}
printf("Decode of btree page %d:\n", pgno);
nCell = a[3]*256 + a[4];
iCellPtr = (a[0]==2 || a[0]==5) ? 12 : 8;
if( cellToDecode>=nCell ){
printf("Page %d has only %d cells\n", pgno, nCell);
return;
}
printf("Header on btree page %d:\n", pgno);
print_decode_line(a, 0, 1, zType);
print_decode_line(a, 1, 2, "Offset to first freeblock");
print_decode_line(a, 3, 2, "Number of cells on this page");
nCell = a[3]*256 + a[4];
print_decode_line(a, 5, 2, "Offset to cell content area");
print_decode_line(a, 7, 1, "Fragmented byte count");
if( a[0]==2 || a[0]==5 ){
print_decode_line(a, 8, 4, "Right child");
iCellPtr = 12;
}else{
iCellPtr = 8;
}
if( nCell>0 ){
if( cellToDecode==(-2) && nCell>0 ){
printf(" key: lx=left-child n=payload-size r=rowid\n");
}
if( showMap ){
@ -409,14 +598,19 @@ static void decode_btree_page(
j = strlen(zBuf);
if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
}
printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
if( cellToDecode==(-2) ){
printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
}else if( cellToDecode==(-1) || cellToDecode==i ){
decodeCell(a, pgno, i, hdrSize, cofst-hdrSize);
}
}
if( showMap ){
printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n");
for(i=0; i<pagesize; i+=64){
printf(" %03x: %.64s\n", i, &zMap[i]);
}
free(zMap);
}
}
}
/*
@ -757,6 +951,7 @@ static void usage(const char *argv0){
" NNNb Decode btree page NNN\n"
" NNNbc Decode btree page NNN and show content\n"
" NNNbm Decode btree page NNN and show a layout map\n"
" NNNbdCCC Decode cell CCC on btree page NNN\n"
" NNNt Decode freelist trunk page NNN\n"
" NNNtd Show leaf freelist pages on the decode\n"
" NNNtr Recurisvely decode freelist starting at NNN\n"
@ -778,7 +973,7 @@ int main(int argc, char **argv){
zPgSz[0] = 0;
zPgSz[1] = 0;
lseek(db, 16, SEEK_SET);
read(db, zPgSz, 2);
if( read(db, zPgSz, 2)<2 ) memset(zPgSz, 0, 2);
pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
if( pagesize==0 ) pagesize = 1024;
printf("Pagesize: %d\n", pagesize);

View File

@ -12,7 +12,6 @@
static int pageSize = 1024;
static int sectorSize = 512;
static FILE *db = 0;
static int showPageContent = 0;
static int fileSize = 0;
static unsigned cksumNonce = 0;
@ -26,9 +25,9 @@ static void out_of_memory(void){
** Read N bytes of memory starting at iOfst into space obtained
** from malloc().
*/
static char *read_content(int N, int iOfst){
static unsigned char *read_content(int N, int iOfst){
int got;
char *pBuf = malloc(N);
unsigned char *pBuf = malloc(N);
if( pBuf==0 ) out_of_memory();
fseek(db, iOfst, SEEK_SET);
got = fread(pBuf, 1, N, db);
@ -46,14 +45,14 @@ static char *read_content(int N, int iOfst){
/* Print a line of decode output showing a 4-byte integer.
*/
static unsigned print_decode_line(
unsigned char *aData, /* Content being decoded */
int ofst, int nByte, /* Start and size of decode */
const char *zMsg /* Message to append */
const unsigned char *aData, /* Content being decoded */
int ofst, int nByte, /* Start and size of decode */
const char *zMsg /* Message to append */
){
int i, j;
unsigned val = aData[ofst];
char zBuf[100];
sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
sprintf(zBuf, " %05x: %02x", ofst, aData[ofst]);
i = strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
@ -74,7 +73,7 @@ static unsigned print_decode_line(
** in global variables.
*/
static unsigned decode_journal_header(int iOfst){
char *pHdr = read_content(64, iOfst);
unsigned char *pHdr = read_content(64, iOfst);
unsigned nPage;
printf("Header at offset %d:\n", iOfst);
print_decode_line(pHdr, 0, 4, "Header part 1 (3654616569)");
@ -101,12 +100,11 @@ static void print_page(int iOfst){
char zTitle[50];
aData = read_content(pageSize+8, iOfst);
sprintf(zTitle, "page number for page at offset %d", iOfst);
print_decode_line(aData, 0, 4, zTitle);
print_decode_line(aData-iOfst, iOfst, 4, zTitle);
free(aData);
}
int main(int argc, char **argv){
int rc;
int nPage, cnt;
int iOfst;
if( argc!=2 ){
@ -136,4 +134,5 @@ int main(int argc, char **argv){
iOfst = (iOfst/sectorSize + 1)*sectorSize;
}
fclose(db);
return 0;
}

157
tool/showstat4.c Normal file
View File

@ -0,0 +1,157 @@
/*
** This utility program decodes and displays the content of the
** sqlite_stat4 table in the database file named on the command
** line.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "sqlite3.h"
typedef sqlite3_int64 i64; /* 64-bit signed integer type */
/*
** Convert the var-int format into i64. Return the number of bytes
** in the var-int. Write the var-int value into *pVal.
*/
static int decodeVarint(const unsigned char *z, i64 *pVal){
i64 v = 0;
int i;
for(i=0; i<8; i++){
v = (v<<7) + (z[i]&0x7f);
if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
}
v = (v<<8) + (z[i]&0xff);
*pVal = v;
return 9;
}
int main(int argc, char **argv){
sqlite3 *db;
sqlite3_stmt *pStmt;
char *zIdx = 0;
int rc, j, x, y, mxHdr;
const unsigned char *aSample;
int nSample;
i64 iVal;
const char *zSep;
if( argc!=2 ){
fprintf(stderr, "Usage: %s DATABASE-FILE\n", argv[0]);
exit(1);
}
rc = sqlite3_open(argv[1], &db);
if( rc!=SQLITE_OK || db==0 ){
fprintf(stderr, "Cannot open database file [%s]\n", argv[1]);
exit(1);
}
rc = sqlite3_prepare_v2(db,
"SELECT tbl||'.'||idx, nEq, nLT, nDLt, sample "
"FROM sqlite_stat4 ORDER BY 1", -1,
&pStmt, 0);
if( rc!=SQLITE_OK || pStmt==0 ){
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
if( zIdx==0 || strcmp(zIdx, (const char*)sqlite3_column_text(pStmt,0))!=0 ){
if( zIdx ) printf("\n");
sqlite3_free(zIdx);
zIdx = sqlite3_mprintf("%s", sqlite3_column_text(pStmt,0));
printf("%s:\n", zIdx);
}else{
printf(" -----------------------------------------------------------\n");
}
printf(" nEq = %s\n", sqlite3_column_text(pStmt,1));
printf(" nLt = %s\n", sqlite3_column_text(pStmt,2));
printf(" nDLt = %s\n", sqlite3_column_text(pStmt,3));
printf(" sample = x'");
aSample = sqlite3_column_blob(pStmt,4);
nSample = sqlite3_column_bytes(pStmt,4);
for(j=0; j<nSample; j++) printf("%02x", aSample[j]);
printf("'\n ");
zSep = " ";
x = decodeVarint(aSample, &iVal);
if( iVal<x || iVal>nSample ){
printf(" <error>\n");
continue;
}
y = mxHdr = (int)iVal;
while( x<mxHdr ){
int sz;
i64 v;
x += decodeVarint(aSample+x, &iVal);
if( x>mxHdr ) break;
if( iVal<0 ) break;
switch( iVal ){
case 0: sz = 0; break;
case 1: sz = 1; break;
case 2: sz = 2; break;
case 3: sz = 3; break;
case 4: sz = 4; break;
case 5: sz = 6; break;
case 6: sz = 8; break;
case 7: sz = 8; break;
case 8: sz = 0; break;
case 9: sz = 0; break;
case 10:
case 11: sz = 0; break;
default: sz = (int)(iVal-12)/2; break;
}
if( y+sz>nSample ) break;
if( iVal==0 ){
printf("%sNULL", zSep);
}else if( iVal==8 || iVal==9 ){
printf("%s%d", zSep, ((int)iVal)-8);
}else if( iVal<=7 ){
v = (signed char)aSample[y];
for(j=1; j<sz; j++){
v = (v<<8) + aSample[y+j];
}
if( iVal==7 ){
double r;
memcpy(&r, &v, sizeof(r));
printf("%s%#g", zSep, r);
}else{
printf("%s%lld", zSep, v);
}
}else if( (iVal&1)==0 ){
printf("%sx'", zSep);
for(j=0; j<sz; j++){
printf("%02x", aSample[y+j]);
}
printf("'");
}else{
printf("%s\"", zSep);
for(j=0; j<sz; j++){
char c = (char)aSample[y+j];
if( isprint(c) ){
if( c=='"' || c=='\\' ) putchar('\\');
putchar(c);
}else if( c=='\n' ){
printf("\\n");
}else if( c=='\t' ){
printf("\\t");
}else if( c=='\r' ){
printf("\\r");
}else{
printf("\\%03o", c);
}
}
printf("\"");
}
zSep = ",";
y += sz;
}
printf("\n");
}
sqlite3_free(zIdx);
sqlite3_finalize(pStmt);
sqlite3_close(db);
return 0;
}