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:
19
Makefile.in
19
Makefile.in
@ -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)
|
||||
|
19
Makefile.msc
19
Makefile.msc
@ -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)
|
||||
|
18
configure
vendored
18
configure
vendored
@ -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
23
main.mk
@ -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) \
|
||||
|
57
manifest
57
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
0e1b73496f861bd497f92f1ead936b2d94aa52d5
|
||||
ae23a65eb1547fbe8b86ab71477071990a22d31d
|
@ -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);
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
39
src/shell.c
39
src/shell.c
@ -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]);
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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*);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
166
src/vdbemem.c
166
src/vdbemem.c
@ -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
|
||||
|
299
src/where.c
299
src/where.c
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
186
test/skipscan5.test
Normal 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
323
test/tkt-9a8b09f8e6.test
Normal 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
|
@ -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
|
||||
|
215
tool/showdb.c
215
tool/showdb.c
@ -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);
|
||||
|
@ -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
157
tool/showstat4.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user