aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.msc3
-rw-r--r--VERSION2
-rwxr-xr-xconfigure18
-rw-r--r--manifest84
-rw-r--r--manifest.uuid2
-rw-r--r--src/analyze.c1
-rw-r--r--src/delete.c1
-rw-r--r--src/expr.c298
-rw-r--r--src/global.c2
-rw-r--r--src/insert.c1
-rw-r--r--src/main.c39
-rw-r--r--src/mem1.c89
-rw-r--r--src/os.h30
-rw-r--r--src/os_unix.c2
-rw-r--r--src/os_win.c85
-rw-r--r--src/parse.y5
-rw-r--r--src/printf.c6
-rw-r--r--src/resolve.c14
-rw-r--r--src/select.c179
-rw-r--r--src/shell.c9
-rw-r--r--src/sqlite.h.in22
-rw-r--r--src/sqliteInt.h77
-rw-r--r--src/test1.c28
-rw-r--r--src/trigger.c1
-rw-r--r--src/update.c11
-rw-r--r--src/vdbe.c64
-rw-r--r--src/vdbe.h1
-rw-r--r--src/vdbeInt.h23
-rw-r--r--src/vdbeapi.c8
-rw-r--r--src/vdbeaux.c24
-rw-r--r--src/vdbetrace.c117
-rw-r--r--src/wal.c41
-rw-r--r--src/where.c4
-rw-r--r--test/capi3d.test26
-rw-r--r--test/multiplex2.test59
-rw-r--r--test/multiplex3.test2
-rw-r--r--test/selectB.test71
-rw-r--r--test/tkt-3a77c9714e.test68
-rw-r--r--test/tkt-7bbfb7d442.test154
-rw-r--r--test/walpersist.test22
40 files changed, 1302 insertions, 391 deletions
diff --git a/Makefile.msc b/Makefile.msc
index ea7efa758..f40936d91 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -49,9 +49,6 @@ BCC = cl.exe
#
TCC = cl.exe -W3 -DSQLITE_OS_WIN=1 -I. -I$(TOP)\src -fp:precise
-# We always have the _msize function available when using MSVC.
-TCC = $(TCC) -DHAVE_MALLOC_USABLE_SIZE -Dmalloc_usable_size=_msize
-
# The mksqlite3c.tcl and mksqlite3h.tcl scripts will pull in
# any extension header files by default. For non-amalgamation
# builds, we need to make sure the compiler can find these.
diff --git a/VERSION b/VERSION
index c77a7de85..f06fb9e91 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.7.9
+3.7.10
diff --git a/configure b/configure
index 14ff67506..87b6a8410 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for sqlite 3.7.9.
+# Generated by GNU Autoconf 2.68 for sqlite 3.7.10.
#
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -706,8 +706,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
-PACKAGE_VERSION='3.7.9'
-PACKAGE_STRING='sqlite 3.7.9'
+PACKAGE_VERSION='3.7.10'
+PACKAGE_STRING='sqlite 3.7.10'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1440,7 +1440,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.7.9 to adapt to many kinds of systems.
+\`configure' configures sqlite 3.7.10 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1505,7 +1505,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sqlite 3.7.9:";;
+ short | recursive ) echo "Configuration of sqlite 3.7.10:";;
esac
cat <<\_ACEOF
@@ -1622,7 +1622,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sqlite configure 3.7.9
+sqlite configure 3.7.10
generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
@@ -2041,7 +2041,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.7.9, which was
+It was created by sqlite $as_me 3.7.10, which was
generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@
@@ -11868,7 +11868,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by sqlite $as_me 3.7.9, which was
+This file was extended by sqlite $as_me 3.7.10, which was
generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -11934,7 +11934,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-sqlite config.status 3.7.9
+sqlite config.status 3.7.10
configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\"
diff --git a/manifest b/manifest
index e9459b224..6fcb03d69 100644
--- a/manifest
+++ b/manifest
@@ -1,12 +1,12 @@
-C Merge\sthe\sVFSNAME\sfile-control\saddition\sinto\sthe\snx-devkit\sbranch.
-D 2011-12-14T18:28:18.920
+C Merge\sthe\snx-devkit\schanges\sinto\strunk.\s\sThis\sincludes\sthe\snew\nSQLITE_FCNTL_VFSNAME\sfile-control.
+D 2011-12-14T18:33:13.731
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
-F Makefile.msc 3bd3641a345d488a9601c0cc7f9d35aeede5d12b
+F Makefile.msc dcad80fa69f17d46fe6778ba873fc108ca16298d
F Makefile.vxworks 1deb39c8bb047296c30161ffa10c1b5423e632f9
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
-F VERSION bb37c274b503bbe73f00ea4f374eb817cba4b171
+F VERSION af03cd6400f9d71d38bdb7a9d66a1aefdc2f3e0d
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
@@ -23,7 +23,7 @@ F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 31cc8c4943f56e60c4aa4fba929c9d4c70e418b4
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
-F configure 194ed7797c67c24ebbeb001fcfc557116fe5eba9 x
+F configure cdfb8a907ded1f8e75b6967e3e3bd35718884aff x
F configure.ac 75323bdac56fb0e69f6a3fc5b23f24359550b9d9
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 3091574143dd3415669b6745843ff8d011d33549
@@ -119,7 +119,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
-F src/analyze.c 5a1db16a651ce6310c8b046b2cbb736e030e14b9
+F src/analyze.c f32ff304da413851eefa562b04e61ff6cb88248b
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 4368158da74d4711888e03264105c5c527d76caf
@@ -133,24 +133,24 @@ F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
-F src/delete.c ff68e5ef23aee08c0ff528f699a19397ed8bbed8
-F src/expr.c fbf116f90cabc917ae50bba24a73a0b55519a0c8
+F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
+F src/expr.c 537591e95eac74af783e4eb033954fb218cf398e
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
-F src/global.c 107ccaacb4b30895cf3a3a39decf417c804acfa1
+F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c 8f283d6734dd837ed7531b26d7622fda70874390
+F src/insert.c ea820fe9af748075b3b6827fb6f23f25079bf1f7
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
-F src/main.c e7e6985365795ef6c500d05d766222222efbb0ac
+F src/main.c c837e24182e8ec7121c61e44f547b59cbe413e7f
F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
-F src/mem1.c 7456e2ca0524609ebc06a9befeda5289d4575ad4
+F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f
@@ -163,33 +163,33 @@ F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579
F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
F src/os.c 28bbdab2170dfce84d86c45456a18eab1d0f99a9
-F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
+F src/os.h 549b1a2e5e0ed1e1499f252dac126c4973e7379c
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
-F src/os_unix.c baad28b27adc563579170b6d765af0b0cc1d98c8
-F src/os_win.c 823f55855ceb062d72231409b828d0486f09125f
+F src/os_unix.c 7dc7df10331942b139032328449a3723e051979e
+F src/os_win.c 197d23ce8a0dff748e766e034bf95ff756dd3884
F src/pager.c d981f3bfcc0e4460537d983899620700ccf8f539
F src/pager.h 5cd760857707529b403837d813d86b68938d6183
-F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
+F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
F src/pcache.c 1fdd77978c1525d1ca4b9ef48eb80abca710cb4c
F src/pcache.h b1d8775a9bddf44e65edb0d20bfc57a4982f840f
F src/pcache1.c 9d735349ac87ef08076c6b1230f04cd83b15c6da
F src/pragma.c dd66f21fafe7be40e1a48ad4195764cc191cf583
F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
-F src/printf.c 03104cbff6959ff45df69dc9060ba6212f60a869
+F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
-F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
+F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
-F src/select.c 80f3ac44a8514b1d107b80f5df4a424ae059d2b6
-F src/shell.c 670644f7bd82073ce0c97c6bff94be04c326beb8
-F src/sqlite.h.in 84cd7be33f5bf39329e018f93eb34b4a345c9c23
+F src/select.c a1d075db66a0ea42807353501b62997969e5be79
+F src/shell.c aa4183d4a5243d8110b1d3d77faa4aea7e9c9c2d
+F src/sqlite.h.in e3e45b5c69ab3236c7ec4591a5858221863cecd4
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
-F src/sqliteInt.h f412e020e1009163c74be56eaac1bf7f6c0a4515
+F src/sqliteInt.h 165409fa8adc8701148830804febeded3f2e4448
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c de581e2e71f5e7f98366156afad83b4742ac6fe0
-F src/test1.c d019682c4480d612e4eca73412011a120e1468db
+F src/test1.c 33b9c49f728cf28937eea7b246c28e89e3cf5e9e
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
@@ -234,25 +234,25 @@ F src/test_vfstrace.c 065c7270a614254b2c68fbc7ba8d1fb1d5cbc823
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
-F src/trigger.c 1cfb80e2290ef66ea89cb4e821caae65a02c0d56
-F src/update.c 25e046a8f69d5e557aabde2000487b8545509d8d
+F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
+F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
F src/util.c 01238e2b0f24a14779181dbf991fe02620a80e31
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
-F src/vdbe.c a7ab9993ec5a4d9479dc99671faec061fbf9b889
-F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
-F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176
-F src/vdbeapi.c 4dbba7f94f127f6ea8d2d0505ee1f98e5ffbf546
-F src/vdbeaux.c 45713a5f8f4f36195f503b30153ddef292323f88
+F src/vdbe.c 029add0c5197a61db588824a58570547330b9d8f
+F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
+F src/vdbeInt.h 48c158b2fceca9682d1577e61c62da3c58cf0748
+F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
+F src/vdbeaux.c 61a3cc2b3a713aa244598a13035471cc30b307e1
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
-F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
+F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
-F src/wal.c 5fe1ba55b8fab9d3936bc9093af61ab9f1c580a1
+F src/wal.c 7e6e7fe68ee649505dca38c8ab83eda0d0d96ae5
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c f73752ca85c0ed221753fda98aeaf6b9d4616e0e
+F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -315,7 +315,7 @@ F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
F test/capi3.test 7200dff6acb17b9a4b6f9918f554eaae04968ddd
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
F test/capi3c.test ccf0acf045dbacd09f6229aa4efed670aaba76a9
-F test/capi3d.test cd36571f014f34bdc4421967f6453cbb597d5d16
+F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3
@@ -606,8 +606,8 @@ F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test eafaa41b9133d7a2ded4641bbe5f340731d35a52
F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
F test/multiplex.test 8bc3c71f73fe833bc8a659d454d320044a33b5da
-F test/multiplex2.test 896ff138d27688b68c894b8166606454ce915793
-F test/multiplex3.test e17b73e904dbd789de37192b6b94424e6f847bc3
+F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
+F test/multiplex3.test 15903c343f1eaa4b00998b7ceacfc4987e4ccfe9
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
@@ -668,7 +668,7 @@ F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
-F test/selectB.test 0d072c5846071b569766e6cd7f923f646a8b2bfa
+F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test f9bf1bc4581b5b8158caa6e4e4f682acb379fb25
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/shared.test 34945a516532b11182c3eb26e31247eee3c9ae48
@@ -730,6 +730,7 @@ F test/tkt-31338dca7e.test 1f714c14b6682c5db715e0bda347926a3456f7a9
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
F test/tkt-38cb5df375.test 9e9b19857dba0896a8efdaf334d405ba423492f2
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
+F test/tkt-3a77c9714e.test 1675c22a5be71d7fa026e5db5daeeb4dd64f7824
F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00
F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894
@@ -738,6 +739,7 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
+F test/tkt-7bbfb7d442.test 8e7658f77d1ccea9d88dc9e255d3ed7fb68f8bdf
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
@@ -913,7 +915,7 @@ F test/walfault.test efb0d5724893133e71b8d9d90abdb781845a6bb0
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
-F test/walpersist.test fd40d33765b2693f721c90c66d97f99757559006
+F test/walpersist.test 710b1b6cf6f8333e984f437724d1fa9e0511c5aa
F test/walro.test e6bb27762c9f22601cbb8bff6e0acfd124e74b63
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
@@ -978,7 +980,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P 08bbbd8e38697908ab8ae0f6b512d01c32f7e4da cdbfb553af02d10767a905489d9dfc57fc6be54e
-R d213e3c5e90076919d50d6149e76976e
+P c65e5a36f1a1c91cb3415158ebe0f5759cbcdf96 08c1dc517c1340737a55ad9012b7b06f72899c6f
+R 2cdc49e30cc728bae752e402fff2c131
U drh
-Z 8e788ffb68fb72b933914781f94a7874
+Z 5856c8e102de685918e2741a72465352
diff --git a/manifest.uuid b/manifest.uuid
index d03e81ee9..ab55230bd 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-08c1dc517c1340737a55ad9012b7b06f72899c6f \ No newline at end of file
+da118e02c0576ce16f7a26663f59413316223d55 \ No newline at end of file
diff --git a/src/analyze.c b/src/analyze.c
index b6a987ab8..cbfdc8587 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -529,6 +529,7 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum);
sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
(char*)&stat3InitFuncdef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, 2);
diff --git a/src/delete.c b/src/delete.c
index 147a5ca89..f666b90c8 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -148,7 +148,6 @@ Expr *sqlite3LimitWhere(
*/
if( pOrderBy && (pLimit == 0) ) {
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
- pParse->parseError = 1;
goto limit_where_cleanup_2;
}
diff --git a/src/expr.c b/src/expr.c
index d50617377..22643ff33 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -870,7 +870,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
- pItem->iCol = pOldItem->iCol;
+ pItem->iOrderByCol = pOldItem->iOrderByCol;
pItem->iAlias = pOldItem->iAlias;
}
return pNew;
@@ -940,7 +940,7 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
return pNew;
}
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
- Select *pNew;
+ Select *pNew, *pPrior;
if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
if( pNew==0 ) return 0;
@@ -951,7 +951,9 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
pNew->op = p->op;
- pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
+ pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags);
+ if( pPrior ) pPrior->pNext = pNew;
+ pNew->pNext = 0;
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
pNew->iLimit = 0;
@@ -1374,6 +1376,15 @@ static int isCandidateForInOpt(Select *p){
#endif /* SQLITE_OMIT_SUBQUERY */
/*
+** Code an OP_Once instruction and allocate space for its flag. Return the
+** address of the new instruction.
+*/
+int sqlite3CodeOnce(Parse *pParse){
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
+ return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
+}
+
+/*
** This function is used by the implementation of the IN (...) operator.
** It's job is to find or create a b-tree structure that may be used
** either to test for membership of the (...) set or to iterate through
@@ -1433,6 +1444,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
int eType = 0; /* Type of RHS table. IN_INDEX_* */
int iTab = pParse->nTab++; /* Cursor of the RHS table */
int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
assert( pX->op==TK_IN );
@@ -1443,7 +1455,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
sqlite3 *db = pParse->db; /* Database connection */
- Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
Table *pTab; /* Table <table>. */
Expr *pExpr; /* Expression <column> */
int iCol; /* Index of column <column> */
@@ -1468,10 +1479,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
*/
assert(v);
if( iCol<0 ){
- int iMem = ++pParse->nMem;
int iAddr;
- iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+ iAddr = sqlite3CodeOnce(pParse);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
eType = IN_INDEX_ROWID;
@@ -1497,12 +1507,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
&& sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
&& (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
){
- int iMem = ++pParse->nMem;
int iAddr;
char *pKey;
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
- iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+ iAddr = sqlite3CodeOnce(pParse);
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
pKey,P4_KEYINFO_HANDOFF);
@@ -1512,6 +1521,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
sqlite3VdbeJumpHere(v, iAddr);
if( prNotFound && !pTab->aCol[iCol].notNull ){
*prNotFound = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
}
}
}
@@ -1527,6 +1537,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
eType = IN_INDEX_EPH;
if( prNotFound ){
*prNotFound = rMayHaveNull = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
}else{
testcase( pParse->nQueryLoop>(double)1 );
pParse->nQueryLoop = (double)1;
@@ -1599,9 +1610,8 @@ int sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
- if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
- int mem = ++pParse->nMem;
- testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
+ if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
+ testAddr = sqlite3CodeOnce(pParse);
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -2939,6 +2949,264 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
return inReg;
}
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+/*
+** Generate a human-readable explanation of an expression tree.
+*/
+void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
+ int op; /* The opcode being coded */
+ const char *zBinOp = 0; /* Binary operator */
+ const char *zUniOp = 0; /* Unary operator */
+ if( pExpr==0 ){
+ op = TK_NULL;
+ }else{
+ op = pExpr->op;
+ }
+ switch( op ){
+ case TK_AGG_COLUMN: {
+ sqlite3ExplainPrintf(pOut, "AGG{%d:%d}",
+ pExpr->iTable, pExpr->iColumn);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iTable<0 ){
+ /* This only happens when coding check constraints */
+ sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn);
+ }else{
+ sqlite3ExplainPrintf(pOut, "{%d:%d}",
+ pExpr->iTable, pExpr->iColumn);
+ }
+ break;
+ }
+ case TK_INTEGER: {
+ if( pExpr->flags & EP_IntValue ){
+ sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue);
+ }else{
+ sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_FLOATING_POINT
+ case TK_FLOAT: {
+ sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_STRING: {
+ sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken);
+ break;
+ }
+ case TK_NULL: {
+ sqlite3ExplainPrintf(pOut,"NULL");
+ break;
+ }
+#ifndef SQLITE_OMIT_BLOB_LITERAL
+ case TK_BLOB: {
+ sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_VARIABLE: {
+ sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)",
+ pExpr->u.zToken, pExpr->iColumn);
+ break;
+ }
+ case TK_REGISTER: {
+ sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable);
+ break;
+ }
+ case TK_AS: {
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ break;
+ }
+#ifndef SQLITE_OMIT_CAST
+ case TK_CAST: {
+ /* Expressions of the form: CAST(pLeft AS token) */
+ const char *zAff = "unk";
+ switch( sqlite3AffinityType(pExpr->u.zToken) ){
+ case SQLITE_AFF_TEXT: zAff = "TEXT"; break;
+ case SQLITE_AFF_NONE: zAff = "NONE"; break;
+ case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break;
+ case SQLITE_AFF_INTEGER: zAff = "INTEGER"; break;
+ case SQLITE_AFF_REAL: zAff = "REAL"; break;
+ }
+ sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff);
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+#endif /* SQLITE_OMIT_CAST */
+ case TK_LT: zBinOp = "LT"; break;
+ case TK_LE: zBinOp = "LE"; break;
+ case TK_GT: zBinOp = "GT"; break;
+ case TK_GE: zBinOp = "GE"; break;
+ case TK_NE: zBinOp = "NE"; break;
+ case TK_EQ: zBinOp = "EQ"; break;
+ case TK_IS: zBinOp = "IS"; break;
+ case TK_ISNOT: zBinOp = "ISNOT"; break;
+ case TK_AND: zBinOp = "AND"; break;
+ case TK_OR: zBinOp = "OR"; break;
+ case TK_PLUS: zBinOp = "ADD"; break;
+ case TK_STAR: zBinOp = "MUL"; break;
+ case TK_MINUS: zBinOp = "SUB"; break;
+ case TK_REM: zBinOp = "REM"; break;
+ case TK_BITAND: zBinOp = "BITAND"; break;
+ case TK_BITOR: zBinOp = "BITOR"; break;
+ case TK_SLASH: zBinOp = "DIV"; break;
+ case TK_LSHIFT: zBinOp = "LSHIFT"; break;
+ case TK_RSHIFT: zBinOp = "RSHIFT"; break;
+ case TK_CONCAT: zBinOp = "CONCAT"; break;
+
+ case TK_UMINUS: zUniOp = "UMINUS"; break;
+ case TK_UPLUS: zUniOp = "UPLUS"; break;
+ case TK_BITNOT: zUniOp = "BITNOT"; break;
+ case TK_NOT: zUniOp = "NOT"; break;
+ case TK_ISNULL: zUniOp = "ISNULL"; break;
+ case TK_NOTNULL: zUniOp = "NOTNULL"; break;
+
+ case TK_AGG_FUNCTION:
+ case TK_CONST_FUNC:
+ case TK_FUNCTION: {
+ ExprList *pFarg; /* List of function arguments */
+ if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ pFarg = 0;
+ }else{
+ pFarg = pExpr->x.pList;
+ }
+ sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(",
+ op==TK_AGG_FUNCTION ? "AGG_" : "",
+ pExpr->u.zToken);
+ if( pFarg ){
+ sqlite3ExplainExprList(pOut, pFarg);
+ }
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_EXISTS: {
+ sqlite3ExplainPrintf(pOut, "EXISTS(");
+ sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+ sqlite3ExplainPrintf(pOut,")");
+ break;
+ }
+ case TK_SELECT: {
+ sqlite3ExplainPrintf(pOut, "(");
+ sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+ case TK_IN: {
+ sqlite3ExplainPrintf(pOut, "IN(");
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut, ",");
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+ }else{
+ sqlite3ExplainExprList(pOut, pExpr->x.pList);
+ }
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+#endif /* SQLITE_OMIT_SUBQUERY */
+
+ /*
+ ** x BETWEEN y AND z
+ **
+ ** This is equivalent to
+ **
+ ** x>=y AND x<=z
+ **
+ ** X is stored in pExpr->pLeft.
+ ** Y is stored in pExpr->pList->a[0].pExpr.
+ ** Z is stored in pExpr->pList->a[1].pExpr.
+ */
+ case TK_BETWEEN: {
+ Expr *pX = pExpr->pLeft;
+ Expr *pY = pExpr->x.pList->a[0].pExpr;
+ Expr *pZ = pExpr->x.pList->a[1].pExpr;
+ sqlite3ExplainPrintf(pOut, "BETWEEN(");
+ sqlite3ExplainExpr(pOut, pX);
+ sqlite3ExplainPrintf(pOut, ",");
+ sqlite3ExplainExpr(pOut, pY);
+ sqlite3ExplainPrintf(pOut, ",");
+ sqlite3ExplainExpr(pOut, pZ);
+ sqlite3ExplainPrintf(pOut, ")");
+ break;
+ }
+ case TK_TRIGGER: {
+ /* If the opcode is TK_TRIGGER, then the expression is a reference
+ ** to a column in the new.* or old.* pseudo-tables available to
+ ** trigger programs. In this case Expr.iTable is set to 1 for the
+ ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
+ ** is set to the column of the pseudo-table to read, or to -1 to
+ ** read the rowid field.
+ */
+ sqlite3ExplainPrintf(pOut, "%s(%d)",
+ pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
+ break;
+ }
+ case TK_CASE: {
+ sqlite3ExplainPrintf(pOut, "CASE(");
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut, ",");
+ sqlite3ExplainExprList(pOut, pExpr->x.pList);
+ break;
+ }
+#ifndef SQLITE_OMIT_TRIGGER
+ case TK_RAISE: {
+ const char *zType = "unk";
+ switch( pExpr->affinity ){
+ case OE_Rollback: zType = "rollback"; break;
+ case OE_Abort: zType = "abort"; break;
+ case OE_Fail: zType = "fail"; break;
+ case OE_Ignore: zType = "ignore"; break;
+ }
+ sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken);
+ break;
+ }
+#endif
+ }
+ if( zBinOp ){
+ sqlite3ExplainPrintf(pOut,"%s(", zBinOp);
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut,",");
+ sqlite3ExplainExpr(pOut, pExpr->pRight);
+ sqlite3ExplainPrintf(pOut,")");
+ }else if( zUniOp ){
+ sqlite3ExplainPrintf(pOut,"%s(", zUniOp);
+ sqlite3ExplainExpr(pOut, pExpr->pLeft);
+ sqlite3ExplainPrintf(pOut,")");
+ }
+}
+#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
+
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+/*
+** Generate a human-readable explanation of an expression list.
+*/
+void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
+ int i;
+ if( pList==0 || pList->nExpr==0 ){
+ sqlite3ExplainPrintf(pOut, "(empty-list)");
+ return;
+ }else if( pList->nExpr==1 ){
+ sqlite3ExplainExpr(pOut, pList->a[0].pExpr);
+ }else{
+ sqlite3ExplainPush(pOut);
+ for(i=0; i<pList->nExpr; i++){
+ sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
+ sqlite3ExplainPush(pOut);
+ sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
+ sqlite3ExplainPop(pOut);
+ if( i<pList->nExpr-1 ){
+ sqlite3ExplainNL(pOut);
+ }
+ }
+ sqlite3ExplainPop(pOut);
+ }
+}
+#endif /* SQLITE_DEBUG */
+
/*
** Return TRUE if pExpr is an constant expression that is appropriate
** for factoring out of a loop. Appropriate expressions are:
@@ -3762,3 +4030,11 @@ void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
pParse->iRangeReg = iReg;
}
}
+
+/*
+** Mark all temporary registers as being unavailable for reuse.
+*/
+void sqlite3ClearTempRegCache(Parse *pParse){
+ pParse->nTempReg = 0;
+ pParse->nRangeReg = 0;
+}
diff --git a/src/global.c b/src/global.c
index b8a8a49d3..7de066825 100644
--- a/src/global.c
+++ b/src/global.c
@@ -147,7 +147,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
500, /* nLookaside */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
- {0,0,0,0,0,0,0,0,0,0,0}, /* pcache2 */
+ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
(void*)0, /* pHeap */
0, /* nHeap */
0, 0, /* mnHeap, mxHeap */
diff --git a/src/insert.c b/src/insert.c
index eca3c12dd..dadb10acd 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -239,6 +239,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){
memId = p->regCtr;
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9);
diff --git a/src/main.c b/src/main.c
index 7abde24e7..690b73c2e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1659,7 +1659,6 @@ static int createCollation(
sqlite3* db,
const char *zName,
u8 enc,
- u8 collType,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*),
void(*xDel)(void*)
@@ -1724,7 +1723,6 @@ static int createCollation(
pColl->pUser = pCtx;
pColl->xDel = xDel;
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
- pColl->type = collType;
sqlite3Error(db, SQLITE_OK, 0);
return SQLITE_OK;
}
@@ -2185,14 +2183,10 @@ static int openDatabase(
** and UTF-16, so add a version for each to avoid any unnecessary
** conversions. The only error that can occur here is a malloc() failure.
*/
- createCollation(db, "BINARY", SQLITE_UTF8, SQLITE_COLL_BINARY, 0,
- binCollFunc, 0);
- createCollation(db, "BINARY", SQLITE_UTF16BE, SQLITE_COLL_BINARY, 0,
- binCollFunc, 0);
- createCollation(db, "BINARY", SQLITE_UTF16LE, SQLITE_COLL_BINARY, 0,
- binCollFunc, 0);
- createCollation(db, "RTRIM", SQLITE_UTF8, SQLITE_COLL_USER, (void*)1,
- binCollFunc, 0);
+ createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0);
+ createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0);
+ createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
+ createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
if( db->mallocFailed ){
goto opendb_out;
}
@@ -2200,8 +2194,7 @@ static int openDatabase(
assert( db->pDfltColl!=0 );
/* Also add a UTF-8 case-insensitive collation sequence. */
- createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0,
- nocaseCollatingFunc, 0);
+ createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
/* Parse the filename/URI argument. */
db->openFlags = flags;
@@ -2397,7 +2390,7 @@ int sqlite3_create_collation(
int rc;
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
- rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0);
+ rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, 0);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@@ -2417,7 +2410,7 @@ int sqlite3_create_collation_v2(
int rc;
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
- rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, xDel);
+ rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xDel);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@@ -2440,7 +2433,7 @@ int sqlite3_create_collation16(
assert( !db->mallocFailed );
zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE);
if( zName8 ){
- rc = createCollation(db, zName8, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0);
+ rc = createCollation(db, zName8, (u8)enc, pCtx, xCompare, 0);
sqlite3DbFree(db, zName8);
}
rc = sqlite3ApiExit(db, rc);
@@ -2950,6 +2943,22 @@ int sqlite3_test_control(int op, ...){
break;
}
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ /* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
+ ** sqlite3_stmt*,const char**);
+ **
+ ** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds
+ ** a string that describes the optimized parse tree. This test-control
+ ** returns a pointer to that string.
+ */
+ case SQLITE_TESTCTRL_EXPLAIN_STMT: {
+ sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*);
+ const char **pzRet = va_arg(ap, const char**);
+ *pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt);
+ break;
+ }
+#endif
+
}
va_end(ap);
#endif /* SQLITE_OMIT_BUILTIN_TEST */
diff --git a/src/mem1.c b/src/mem1.c
index e0d1dd6e8..bf84ce090 100644
--- a/src/mem1.c
+++ b/src/mem1.c
@@ -26,10 +26,47 @@
*/
#ifdef SQLITE_SYSTEM_MALLOC
+/*
+** Windows systems have malloc_usable_size() but it is called _msize()
+*/
+#if !defined(HAVE_MALLOC_USABLE_SIZE) && SQLITE_OS_WIN
+# define HAVE_MALLOC_USABLE_SIZE 1
+# define malloc_usable_size _msize
+#endif
+
+#if defined(__APPLE__)
+
+/*
+** Use the zone allocator available on apple products
+*/
+#include <sys/sysctl.h>
+#include <malloc/malloc.h>
+#include <libkern/OSAtomic.h>
+static malloc_zone_t* _sqliteZone_;
+#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
+#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
+#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
+#define SQLITE_MALLOCSIZE(x) \
+ (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x))
+
+#else /* if not __APPLE__ */
+
+/*
+** Use standard C library malloc and free on non-Apple systems.
+*/
+#define SQLITE_MALLOC(x) malloc(x)
+#define SQLITE_FREE(x) free(x)
+#define SQLITE_REALLOC(x,y) realloc((x),(y))
+
#ifdef HAVE_MALLOC_USABLE_SIZE
#include <malloc.h>
+#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
+#else
+#undef SQLITE_MALLOCSIZE
#endif
+#endif /* __APPLE__ or not __APPLE__ */
+
/*
** Like malloc(), but remember the size of the allocation
** so that we can find it later using sqlite3MemSize().
@@ -39,8 +76,8 @@
** routines.
*/
static void *sqlite3MemMalloc(int nByte){
-#ifdef HAVE_MALLOC_USABLE_SIZE
- void *p = malloc( nByte );
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_MALLOC( nByte );
if( p==0 ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
@@ -50,7 +87,7 @@ static void *sqlite3MemMalloc(int nByte){
sqlite3_int64 *p;
assert( nByte>0 );
nByte = ROUND8(nByte);
- p = malloc( nByte+8 );
+ p = SQLITE_MALLOC( nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -71,13 +108,13 @@ static void *sqlite3MemMalloc(int nByte){
** by higher-level routines.
*/
static void sqlite3MemFree(void *pPrior){
-#if HAVE_MALLOC_USABLE_SIZE
- free(pPrior);
+#ifdef SQLITE_MALLOCSIZE
+ SQLITE_FREE(pPrior);
#else
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 );
p--;
- free(p);
+ SQLITE_FREE(p);
#endif
}
@@ -86,8 +123,8 @@ static void sqlite3MemFree(void *pPrior){
** or xRealloc().
*/
static int sqlite3MemSize(void *pPrior){
-#if HAVE_MALLOC_USABLE_SIZE
- return pPrior ? (int)malloc_usable_size(pPrior) : 0;
+#ifdef SQLITE_MALLOCSIZE
+ return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
#else
sqlite3_int64 *p;
if( pPrior==0 ) return 0;
@@ -108,13 +145,13 @@ static int sqlite3MemSize(void *pPrior){
** routines and redirected to xFree.
*/
static void *sqlite3MemRealloc(void *pPrior, int nByte){
-#if HAVE_MALLOC_USABLE_SIZE
- void *p = realloc(pPrior, nByte);
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_REALLOC(pPrior, nByte);
if( p==0 ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM,
"failed memory resize %u to %u bytes",
- malloc_usable_size(pPrior), nByte);
+ SQLITE_MALLOCSIZE(pPrior), nByte);
}
return p;
#else
@@ -122,7 +159,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
assert( pPrior!=0 && nByte>0 );
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
p--;
- p = realloc(p, nByte+8 );
+ p = SQLITE_REALLOC(p, nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -147,6 +184,34 @@ static int sqlite3MemRoundup(int n){
** Initialize this module.
*/
static int sqlite3MemInit(void *NotUsed){
+#if defined(__APPLE__)
+ int cpuCount;
+ size_t len;
+ if( _sqliteZone_ ){
+ return SQLITE_OK;
+ }
+ len = sizeof(cpuCount);
+ /* One usually wants to use hw.acctivecpu for MT decisions, but not here */
+ sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
+ if( cpuCount>1 ){
+ /* defer MT decisions to system malloc */
+ _sqliteZone_ = malloc_default_zone();
+ }else{
+ /* only 1 core, use our own zone to contention over global locks,
+ ** e.g. we have our own dedicated locks */
+ bool success;
+ malloc_zone_t* newzone = malloc_create_zone(4096, 0);
+ malloc_set_zone_name(newzone, "Sqlite_Heap");
+ do{
+ success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
+ (void * volatile *)&_sqliteZone_);
+ }while(!_sqliteZone_);
+ if( !success ){
+ /* somebody registered a zone first */
+ malloc_destroy_zone(newzone);
+ }
+ }
+#endif
UNUSED_PARAMETER(NotUsed);
return SQLITE_OK;
}
diff --git a/src/os.h b/src/os.h
index 7f17c203e..80afc516f 100644
--- a/src/os.h
+++ b/src/os.h
@@ -66,17 +66,6 @@
#endif
/*
-** Determine if we are dealing with WindowsCE - which has a much
-** reduced API.
-*/
-#if defined(_WIN32_WCE)
-# define SQLITE_OS_WINCE 1
-#else
-# define SQLITE_OS_WINCE 0
-#endif
-
-
-/*
** Define the maximum size of a temporary filename
*/
#if SQLITE_OS_WIN
@@ -100,6 +89,25 @@
# define SQLITE_TEMPNAME_SIZE 200
#endif
+/*
+** Determine if we are dealing with Windows NT.
+*/
+#if defined(_WIN32_WINNT)
+# define SQLITE_OS_WINNT 1
+#else
+# define SQLITE_OS_WINNT 0
+#endif
+
+/*
+** Determine if we are dealing with WindowsCE - which has a much
+** reduced API.
+*/
+#if defined(_WIN32_WCE)
+# define SQLITE_OS_WINCE 1
+#else
+# define SQLITE_OS_WINCE 0
+#endif
+
/* If the SET_FULLSYNC macro is not defined above, then make it
** a no-op
*/
diff --git a/src/os_unix.c b/src/os_unix.c
index 3cb28b058..ee5971f10 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -2015,8 +2015,8 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
rc = osRmdir(zLockFile);
if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
if( rc<0 ){
- int rc = 0;
int tErrno = errno;
+ rc = 0;
if( ENOENT != tErrno ){
rc = SQLITE_IOERR_UNLOCK;
}
diff --git a/src/os_win.c b/src/os_win.c
index 53cf52353..ab70eebbf 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -181,7 +181,7 @@ static int sqlite3_os_type = 0;
# define SQLITE_WIN32_HAS_ANSI
#endif
-#if SQLITE_OS_WINCE || defined(_WIN32_WINNT)
+#if SQLITE_OS_WINCE || SQLITE_OS_WINNT
# define SQLITE_WIN32_HAS_WIDE
#endif
@@ -918,6 +918,9 @@ static LPWSTR utf8ToUnicode(const char *zFilename){
LPWSTR zWideFilename;
nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) );
if( zWideFilename==0 ){
return 0;
@@ -940,6 +943,9 @@ static char *unicodeToUtf8(LPCWSTR zWideFilename){
char *zFilename;
nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
zFilename = sqlite3_malloc( nByte );
if( zFilename==0 ){
return 0;
@@ -967,6 +973,9 @@ static LPWSTR mbcsToUnicode(const char *zFilename){
nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL,
0)*sizeof(WCHAR);
+ if( nByte==0 ){
+ return 0;
+ }
zMbcsFilename = sqlite3_malloc( nByte*sizeof(zMbcsFilename[0]) );
if( zMbcsFilename==0 ){
return 0;
@@ -993,6 +1002,9 @@ static char *unicodeToMbcs(LPCWSTR zWideFilename){
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
zFilename = sqlite3_malloc( nByte );
if( zFilename==0 ){
return 0;
@@ -1170,12 +1182,14 @@ static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
** to see if it should be retried. Return TRUE to retry. Return FALSE
** to give up with an error.
*/
-static int retryIoerr(int *pnRetry){
- DWORD e;
+static int retryIoerr(int *pnRetry, DWORD *pError){
+ DWORD e = osGetLastError();
if( *pnRetry>=win32IoerrRetry ){
+ if( pError ){
+ *pError = e;
+ }
return 0;
}
- e = osGetLastError();
if( e==ERROR_ACCESS_DENIED ||
e==ERROR_LOCK_VIOLATION ||
e==ERROR_SHARING_VIOLATION ){
@@ -1183,6 +1197,9 @@ static int retryIoerr(int *pnRetry){
++*pnRetry;
return 1;
}
+ if( pError ){
+ *pError = e;
+ }
return 0;
}
@@ -1539,6 +1556,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
+ DWORD lastErrno; /* Value returned by GetLastError() */
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
@@ -1551,8 +1569,10 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
** GetLastError().
*/
dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
- if( (dwRet==INVALID_SET_FILE_POINTER && osGetLastError()!=NO_ERROR) ){
- pFile->lastErrno = osGetLastError();
+
+ if( (dwRet==INVALID_SET_FILE_POINTER
+ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
+ pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
"seekWinFile", pFile->zPath);
return 1;
@@ -1628,8 +1648,9 @@ static int winRead(
return SQLITE_FULL;
}
while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
- if( retryIoerr(&nRetry) ) continue;
- pFile->lastErrno = osGetLastError();
+ DWORD lastErrno;
+ if( retryIoerr(&nRetry, &lastErrno) ) continue;
+ pFile->lastErrno = lastErrno;
return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
"winRead", pFile->zPath);
}
@@ -1669,10 +1690,11 @@ static int winWrite(
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */
+ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
while( nRem>0 ){
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
- if( retryIoerr(&nRetry) ) continue;
+ if( retryIoerr(&nRetry, &lastErrno) ) continue;
break;
}
if( nWrite<=0 ) break;
@@ -1680,7 +1702,7 @@ static int winWrite(
nRem -= nWrite;
}
if( nRem>0 ){
- pFile->lastErrno = osGetLastError();
+ pFile->lastErrno = lastErrno;
rc = 1;
}
}
@@ -1810,15 +1832,15 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
DWORD upperBits;
DWORD lowerBits;
winFile *pFile = (winFile*)id;
- DWORD error;
+ DWORD lastErrno;
assert( id!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT);
lowerBits = osGetFileSize(pFile->h, &upperBits);
if( (lowerBits == INVALID_FILE_SIZE)
- && ((error = osGetLastError()) != NO_ERROR) )
+ && ((lastErrno = osGetLastError())!=NO_ERROR) )
{
- pFile->lastErrno = error;
+ pFile->lastErrno = lastErrno;
return winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
"winFileSize", pFile->zPath);
}
@@ -1869,6 +1891,7 @@ static int getReadLock(winFile *pFile){
*/
static int unlockReadLock(winFile *pFile){
int res;
+ DWORD lastErrno;
if( isNT() ){
res = osUnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
@@ -1878,8 +1901,8 @@ static int unlockReadLock(winFile *pFile){
res = osUnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
#endif
}
- if( res==0 && osGetLastError()!=ERROR_NOT_LOCKED ){
- pFile->lastErrno = osGetLastError();
+ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
+ pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
"unlockReadLock", pFile->zPath);
}
@@ -1918,7 +1941,7 @@ static int winLock(sqlite3_file *id, int locktype){
int newLocktype; /* Set pFile->locktype to this value before exiting */
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
winFile *pFile = (winFile*)id;
- DWORD error = NO_ERROR;
+ DWORD lastErrno = NO_ERROR;
assert( id!=0 );
OSTRACE(("LOCK %d %d was %d(%d)\n",
@@ -1960,7 +1983,7 @@ static int winLock(sqlite3_file *id, int locktype){
}
gotPendingLock = res;
if( !res ){
- error = osGetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -1972,7 +1995,7 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
newLocktype = SHARED_LOCK;
}else{
- error = osGetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -1984,7 +2007,7 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
newLocktype = RESERVED_LOCK;
}else{
- error = osGetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -2005,8 +2028,8 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
- error = osGetLastError();
- OSTRACE(("error-code = %d\n", error));
+ lastErrno = osGetLastError();
+ OSTRACE(("error-code = %d\n", lastErrno));
getReadLock(pFile);
}
}
@@ -2026,7 +2049,7 @@ static int winLock(sqlite3_file *id, int locktype){
}else{
OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h,
locktype, newLocktype));
- pFile->lastErrno = error;
+ pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
}
pFile->locktype = (u8)newLocktype;
@@ -2968,6 +2991,7 @@ static int winOpen(
int *pOutFlags /* Status return flags */
){
HANDLE h;
+ DWORD lastErrno;
DWORD dwDesiredAccess;
DWORD dwShareMode;
DWORD dwCreationDisposition;
@@ -3104,7 +3128,7 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt) ){}
+ retryIoerr(&cnt, &lastErrno) ){}
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ANSI version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds.
@@ -3117,7 +3141,7 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt) ){}
+ retryIoerr(&cnt, &lastErrno) ){}
#endif
}
@@ -3128,7 +3152,7 @@ static int winOpen(
h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
if( h==INVALID_HANDLE_VALUE ){
- pFile->lastErrno = osGetLastError();
+ pFile->lastErrno = lastErrno;
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
sqlite3_free(zConverted);
if( isReadWrite && !isExclusive ){
@@ -3195,6 +3219,7 @@ static int winDelete(
){
int cnt = 0;
int rc;
+ DWORD lastErrno;
void *zConverted;
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(syncDir);
@@ -3207,7 +3232,7 @@ static int winDelete(
if( isNT() ){
rc = 1;
while( osGetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
- (rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){}
+ (rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){}
rc = rc ? SQLITE_OK : SQLITE_ERROR;
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ANSI version of these Windows API do not exist for WINCE,
@@ -3217,12 +3242,12 @@ static int winDelete(
}else{
rc = 1;
while( osGetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
- (rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){}
+ (rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){}
rc = rc ? SQLITE_OK : SQLITE_ERROR;
#endif
}
if( rc ){
- rc = winLogError(SQLITE_IOERR_DELETE, osGetLastError(),
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno,
"winDelete", zFilename);
}else{
logIoerr(cnt);
@@ -3243,6 +3268,7 @@ static int winAccess(
){
DWORD attr;
int rc = 0;
+ DWORD lastErrno;
void *zConverted;
UNUSED_PARAMETER(pVfs);
@@ -3257,7 +3283,7 @@ static int winAccess(
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt) ){}
+ &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
@@ -3270,7 +3296,6 @@ static int winAccess(
attr = sAttrData.dwFileAttributes;
}
}else{
- DWORD lastErrno = osGetLastError();
logIoerr(cnt);
if( lastErrno!=ERROR_FILE_NOT_FOUND ){
winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename);
diff --git a/src/parse.y b/src/parse.y
index 92abd5ce5..2b8e9170f 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -33,12 +33,10 @@
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
- pParse->parseError = 1;
}
%stack_overflow {
UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */
sqlite3ErrorMsg(pParse, "parser stack overflow");
- pParse->parseError = 1;
}
// The name of the generated procedure that implements the parser
@@ -396,6 +394,9 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
cmd ::= select(X). {
SelectDest dest = {SRT_Output, 0, 0, 0, 0};
sqlite3Select(pParse, X, &dest);
+ sqlite3ExplainBegin(pParse->pVdbe);
+ sqlite3ExplainSelect(pParse->pVdbe, X);
+ sqlite3ExplainFinish(pParse->pVdbe);
sqlite3SelectDelete(pParse->db, X);
}
diff --git a/src/printf.c b/src/printf.c
index 0babee514..58cfd2bd4 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -136,7 +136,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
/*
** Append N space characters to the given string buffer.
*/
-static void appendSpace(StrAccum *pAccum, int N){
+void sqlite3AppendSpace(StrAccum *pAccum, int N){
static const char zSpaces[] = " ";
while( N>=(int)sizeof(zSpaces)-1 ){
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
@@ -664,7 +664,7 @@ void sqlite3VXPrintf(
register int nspace;
nspace = width-length;
if( nspace>0 ){
- appendSpace(pAccum, nspace);
+ sqlite3AppendSpace(pAccum, nspace);
}
}
if( length>0 ){
@@ -674,7 +674,7 @@ void sqlite3VXPrintf(
register int nspace;
nspace = width-length;
if( nspace>0 ){
- appendSpace(pAccum, nspace);
+ sqlite3AppendSpace(pAccum, nspace);
}
}
sqlite3_free(zExtra);
diff --git a/src/resolve.c b/src/resolve.c
index 6d857f007..3da48136f 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -799,7 +799,7 @@ static int resolveCompoundOrderBy(
pE->pColl = pColl;
pE->flags |= EP_IntValue | flags;
pE->u.iValue = iCol;
- pItem->iCol = (u16)iCol;
+ pItem->iOrderByCol = (u16)iCol;
pItem->done = 1;
}else{
moreToDo = 1;
@@ -848,12 +848,12 @@ int sqlite3ResolveOrderGroupBy(
pEList = pSelect->pEList;
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
- if( pItem->iCol ){
- if( pItem->iCol>pEList->nExpr ){
+ if( pItem->iOrderByCol ){
+ if( pItem->iOrderByCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
+ resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType);
}
}
return 0;
@@ -900,7 +900,7 @@ static int resolveOrderGroupBy(
** a copy of the iCol-th result-set column. The subsequent call to
** sqlite3ResolveOrderGroupBy() will convert the expression to a
** copy of the iCol-th result-set expression. */
- pItem->iCol = (u16)iCol;
+ pItem->iOrderByCol = (u16)iCol;
continue;
}
if( sqlite3ExprIsInteger(pE, &iCol) ){
@@ -911,12 +911,12 @@ static int resolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, nResult);
return 1;
}
- pItem->iCol = (u16)iCol;
+ pItem->iOrderByCol = (u16)iCol;
continue;
}
/* Otherwise, treat the ORDER BY term as an ordinary expression */
- pItem->iCol = 0;
+ pItem->iOrderByCol = 0;
if( sqlite3ResolveExprNames(pNC, pE) ){
return 1;
}
diff --git a/src/select.c b/src/select.c
index 571a77822..188050e8d 100644
--- a/src/select.c
+++ b/src/select.c
@@ -2219,8 +2219,8 @@ static int multiSelectOrderBy(
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
- assert( pItem->iCol>0 );
- if( pItem->iCol==i ) break;
+ assert( pItem->iOrderByCol>0 );
+ if( pItem->iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
@@ -2228,7 +2228,7 @@ static int multiSelectOrderBy(
pNew->flags |= EP_IntValue;
pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
- pOrderBy->a[nOrderBy++].iCol = (u16)i;
+ pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i;
}
}
}
@@ -2244,8 +2244,8 @@ static int multiSelectOrderBy(
if( aPermute ){
struct ExprList_item *pItem;
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
- assert( pItem->iCol>0 && pItem->iCol<=p->pEList->nExpr );
- aPermute[i] = pItem->iCol - 1;
+ assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
+ aPermute[i] = pItem->iOrderByCol - 1;
}
pKeyMerge =
sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
@@ -2588,9 +2588,8 @@ static void substSelect(
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
-** This routine attempts to flatten subqueries in order to speed
-** execution. It returns 1 if it makes changes and 0 if no flattening
-** occurs.
+** This routine attempts to flatten subqueries as a performance optimization.
+** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
** query:
@@ -2632,7 +2631,10 @@ static void substSelect(
** (6) The subquery does not use aggregates or the outer query is not
** DISTINCT.
**
-** (7) The subquery has a FROM clause.
+** (7) The subquery has a FROM clause. TODO: For subqueries without
+** A FROM clause, consider adding a FROM close with the special
+** table sqlite_once that consists of a single row containing a
+** single NULL.
**
** (8) The subquery does not use LIMIT or the outer query is not a join.
**
@@ -2665,11 +2667,14 @@ static void substSelect(
**
** * is not itself part of a compound select,
** * is not an aggregate or DISTINCT query, and
-** * has no other tables or sub-selects in the FROM clause.
+** * is not a join
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
-** LIMIT and OFFSET clauses.
+** LIMIT and OFFSET clauses. The subquery cannot use any compound
+** operator other than UNION ALL because all the other compound
+** operators have an implied DISTINCT which is disallowed by
+** restriction (4).
**
** (18) If the sub-query is a compound select, then all terms of the
** ORDER by clause of the parent must be simple references to
@@ -2681,7 +2686,7 @@ static void substSelect(
** (20) If the sub-query is a compound select, then it must not use
** an ORDER BY clause. Ticket #3773. We could relax this constraint
** somewhat by saying that the terms of the ORDER BY clause must
-** appear as unmodified result columns in the outer query. But
+** appear as unmodified result columns in the outer query. But we
** have other optimizations in mind to deal with that case.
**
** (21) The subquery does not use LIMIT or the outer query is not
@@ -2810,19 +2815,21 @@ static int flattenSubquery(
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
+ assert( pSub->pSrc!=0 );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
- || NEVER(pSub1->pSrc==0) || pSub1->pSrc->nSrc!=1
+ || pSub1->pSrc->nSrc<1
){
return 0;
}
+ testcase( pSub1->pSrc->nSrc>1 );
}
/* Restriction 18. */
if( p->pOrderBy ){
int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
- if( p->pOrderBy->a[ii].iCol==0 ) return 0;
+ if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0;
}
}
}
@@ -3845,12 +3852,11 @@ int sqlite3Select(
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
- if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
+ if( pItem->isCorrelated==0 ){
/* If the subquery is no correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
- int regOnce = ++pParse->nMem;
- onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
+ onceAddr = sqlite3CodeOnce(pParse);
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
@@ -3860,7 +3866,7 @@ int sqlite3Select(
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
-
+ sqlite3ClearTempRegCache(pParse);
}
if( /*pParse->nErr ||*/ db->mallocFailed ){
goto select_end;
@@ -4155,6 +4161,7 @@ int sqlite3Select(
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
VdbeComment((v, "indicate accumulator empty"));
+ sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
@@ -4494,98 +4501,98 @@ select_end:
return rc;
}
-#if defined(SQLITE_DEBUG)
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
/*
-*******************************************************************************
-** The following code is used for testing and debugging only. The code
-** that follows does not appear in normal builds.
-**
-** These routines are used to print out the content of all or part of a
-** parse structures such as Select or Expr. Such printouts are useful
-** for helping to understand what is happening inside the code generator
-** during the execution of complex SELECT statements.
-**
-** These routine are not called anywhere from within the normal
-** code base. Then are intended to be called from within the debugger
-** or from temporary "printf" statements inserted for debugging.
+** Generate a human-readable description of a the Select object.
*/
-void sqlite3PrintExpr(Expr *p){
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- sqlite3DebugPrintf("(%s", p->u.zToken);
- }else{
- sqlite3DebugPrintf("(%d", p->op);
- }
- if( p->pLeft ){
- sqlite3DebugPrintf(" ");
- sqlite3PrintExpr(p->pLeft);
- }
- if( p->pRight ){
- sqlite3DebugPrintf(" ");
- sqlite3PrintExpr(p->pRight);
- }
- sqlite3DebugPrintf(")");
-}
-void sqlite3PrintExprList(ExprList *pList){
- int i;
- for(i=0; i<pList->nExpr; i++){
- sqlite3PrintExpr(pList->a[i].pExpr);
- if( i<pList->nExpr-1 ){
- sqlite3DebugPrintf(", ");
+static void explainOneSelect(Vdbe *pVdbe, Select *p){
+ sqlite3ExplainPrintf(pVdbe, "SELECT ");
+ if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
+ if( p->selFlags & SF_Distinct ){
+ sqlite3ExplainPrintf(pVdbe, "DISTINCT ");
+ }
+ if( p->selFlags & SF_Aggregate ){
+ sqlite3ExplainPrintf(pVdbe, "agg_flag ");
}
+ sqlite3ExplainNL(pVdbe);
+ sqlite3ExplainPrintf(pVdbe, " ");
}
-}
-void sqlite3PrintSelect(Select *p, int indent){
- sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
- sqlite3PrintExprList(p->pEList);
- sqlite3DebugPrintf("\n");
- if( p->pSrc ){
- char *zPrefix;
+ sqlite3ExplainExprList(pVdbe, p->pEList);
+ sqlite3ExplainNL(pVdbe);
+ if( p->pSrc && p->pSrc->nSrc ){
int i;
- zPrefix = "FROM";
+ sqlite3ExplainPrintf(pVdbe, "FROM ");
+ sqlite3ExplainPush(pVdbe);
for(i=0; i<p->pSrc->nSrc; i++){
struct SrcList_item *pItem = &p->pSrc->a[i];
- sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
- zPrefix = "";
+ sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor);
if( pItem->pSelect ){
- sqlite3DebugPrintf("(\n");
- sqlite3PrintSelect(pItem->pSelect, indent+10);
- sqlite3DebugPrintf("%*s)", indent+8, "");
+ sqlite3ExplainSelect(pVdbe, pItem->pSelect);
+ if( pItem->pTab ){
+ sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName);
+ }
}else if( pItem->zName ){
- sqlite3DebugPrintf("%s", pItem->zName);
- }
- if( pItem->pTab ){
- sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
+ sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName);
}
if( pItem->zAlias ){
- sqlite3DebugPrintf(" AS %s", pItem->zAlias);
+ sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
}
- if( i<p->pSrc->nSrc-1 ){
- sqlite3DebugPrintf(",");
+ if( pItem->jointype & JT_LEFT ){
+ sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN");
}
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainNL(pVdbe);
}
+ sqlite3ExplainPop(pVdbe);
}
if( p->pWhere ){
- sqlite3DebugPrintf("%*s WHERE ", indent, "");
- sqlite3PrintExpr(p->pWhere);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "WHERE ");
+ sqlite3ExplainExpr(pVdbe, p->pWhere);
+ sqlite3ExplainNL(pVdbe);
}
if( p->pGroupBy ){
- sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
- sqlite3PrintExprList(p->pGroupBy);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "GROUPBY ");
+ sqlite3ExplainExprList(pVdbe, p->pGroupBy);
+ sqlite3ExplainNL(pVdbe);
}
if( p->pHaving ){
- sqlite3DebugPrintf("%*s HAVING ", indent, "");
- sqlite3PrintExpr(p->pHaving);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "HAVING ");
+ sqlite3ExplainExpr(pVdbe, p->pHaving);
+ sqlite3ExplainNL(pVdbe);
}
if( p->pOrderBy ){
- sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
- sqlite3PrintExprList(p->pOrderBy);
- sqlite3DebugPrintf("\n");
+ sqlite3ExplainPrintf(pVdbe, "ORDERBY ");
+ sqlite3ExplainExprList(pVdbe, p->pOrderBy);
+ sqlite3ExplainNL(pVdbe);
+ }
+ if( p->pLimit ){
+ sqlite3ExplainPrintf(pVdbe, "LIMIT ");
+ sqlite3ExplainExpr(pVdbe, p->pLimit);
+ sqlite3ExplainNL(pVdbe);
+ }
+ if( p->pOffset ){
+ sqlite3ExplainPrintf(pVdbe, "OFFSET ");
+ sqlite3ExplainExpr(pVdbe, p->pOffset);
+ sqlite3ExplainNL(pVdbe);
+ }
+}
+void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
+ if( p==0 ){
+ sqlite3ExplainPrintf(pVdbe, "(null-select)");
+ return;
}
+ while( p->pPrior ) p = p->pPrior;
+ sqlite3ExplainPush(pVdbe);
+ while( p ){
+ explainOneSelect(pVdbe, p);
+ p = p->pNext;
+ if( p==0 ) break;
+ sqlite3ExplainNL(pVdbe);
+ sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op));
+ }
+ sqlite3ExplainPrintf(pVdbe, "END");
+ sqlite3ExplainPop(pVdbe);
}
+
/* End of the structure debug printing code
*****************************************************************************/
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
diff --git a/src/shell.c b/src/shell.c
index 503d8a6a8..31d3dd8fc 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1127,6 +1127,15 @@ static int shell_exec(
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
+ /* Output TESTCTRL_EXPLAIN text of requested */
+ if( pArg && pArg->mode==MODE_Explain ){
+ const char *zExplain = 0;
+ sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
+ if( zExplain && zExplain[0] ){
+ fprintf(pArg->out, "%s", zExplain);
+ }
+ }
+
/* perform the first step. this will tell us if we
** have a result set or not and how wide it is.
*/
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 553372803..0e0e3af6b 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -2945,6 +2945,25 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt);
int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Determine If A Prepared Statement Has Been Reset
+**
+** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
+** [prepared statement] S has been stepped at least once using
+** [sqlite3_step(S)] but has not run to completion and/or has not
+** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
+** interface returns false if S is a NULL pointer. If S is not a
+** NULL pointer and is not a pointer to a valid [prepared statement]
+** object, then the behavior is undefined and probably undesirable.
+**
+** This interface can be used in combination [sqlite3_next_stmt()]
+** to locate all prepared statements associated with a database
+** connection that are in need of being reset. This can be used,
+** for example, in diagnostic routines to search for prepared
+** statements that are holding a transaction open.
+*/
+int sqlite3_stmt_busy(sqlite3_stmt*);
+
+/*
** CAPI3REF: Dynamically Typed Value Object
** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
**
@@ -5675,7 +5694,8 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ISKEYWORD 16
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
-#define SQLITE_TESTCTRL_LAST 18
+#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
+#define SQLITE_TESTCTRL_LAST 19
/*
** CAPI3REF: SQLite Runtime Status
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 663b805b0..8613d865a 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1144,21 +1144,12 @@ struct Column {
struct CollSeq {
char *zName; /* Name of the collating sequence, UTF-8 encoded */
u8 enc; /* Text encoding handled by xCmp() */
- u8 type; /* One of the SQLITE_COLL_... values below */
void *pUser; /* First argument to xCmp() */
int (*xCmp)(void*,int, const void*, int, const void*);
void (*xDel)(void*); /* Destructor for pUser */
};
/*
-** Allowed values of CollSeq.type:
-*/
-#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */
-#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */
-#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */
-#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */
-
-/*
** A sort order can be either ASC or DESC.
*/
#define SQLITE_SO_ASC 0 /* Sort in ascending order */
@@ -1443,7 +1434,7 @@ struct KeyInfo {
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
u16 nField; /* Number of entries in apMem[] */
- u16 flags; /* Boolean settings. UNPACKED_... below */
+ u8 flags; /* Boolean settings. UNPACKED_... below */
i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
Mem *aMem; /* Values */
};
@@ -1451,11 +1442,9 @@ struct UnpackedRecord {
/*
** Allowed values of UnpackedRecord.flags
*/
-#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */
-#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */
-#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
-#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
-#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
+#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */
+#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */
+#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */
/*
** Each SQL index is represented in memory by an
@@ -1718,10 +1707,10 @@ struct Expr {
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
-
-#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
+#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */
+#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
+#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
/*
** The following are the meanings of bits in the Expr.flags2 field.
@@ -1783,7 +1772,7 @@ struct ExprList {
char *zSpan; /* Original text of the expression */
u8 sortOrder; /* 1 for DESC or 0 for ASC */
u8 done; /* A flag to indicate when processing is finished */
- u16 iCol; /* For ORDER BY, column number in result set */
+ u16 iOrderByCol; /* For ORDER BY, column number in result set */
u16 iAlias; /* Index into Parse.aAlias[] for zName */
} *a; /* One entry for each expression */
};
@@ -2083,13 +2072,13 @@ struct Select {
** Allowed values for Select.selFlags. The "SF" prefix stands for
** "Select Flag".
*/
-#define SF_Distinct 0x0001 /* Output should be DISTINCT */
-#define SF_Resolved 0x0002 /* Identifiers have been resolved */
-#define SF_Aggregate 0x0004 /* Contains aggregate functions */
-#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
-#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
-#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
-#define SF_UseSorter 0x0040 /* Sort using a sorter */
+#define SF_Distinct 0x01 /* Output should be DISTINCT */
+#define SF_Resolved 0x02 /* Identifiers have been resolved */
+#define SF_Aggregate 0x04 /* Contains aggregate functions */
+#define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */
+#define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */
+#define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */
+#define SF_UseSorter 0x40 /* Sort using a sorter */
/*
@@ -2204,10 +2193,8 @@ struct Parse {
char *zErrMsg; /* An error message */
Vdbe *pVdbe; /* An engine for executing database bytecode */
u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
- u8 nameClash; /* A permanent table name clashes with temp table name */
u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */
- u8 parseError; /* True after a parsing error. Ticket #1794 */
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
u8 nTempInUse; /* Number of aTempReg[] currently checked out */
int aTempReg[8]; /* Holding area for temporary registers */
@@ -2217,11 +2204,12 @@ struct Parse {
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
int nSet; /* Number of sets used so far */
+ int nOnce; /* Number of OP_Once instructions so far */
int ckBase; /* Base register of data during check constraints */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
- u8 nColCache; /* Number of entries in the column cache */
- u8 iColCache; /* Next entry of the cache to replace */
+ u8 nColCache; /* Number of entries in aColCache[] */
+ u8 iColCache; /* Next entry in aColCache[] to replace */
struct yColCache {
int iTable; /* Table cursor number */
int iColumn; /* Table column number */
@@ -2263,7 +2251,6 @@ struct Parse {
char **azVar; /* Pointers to names of parameters */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
int nAlias; /* Number of aliased result set columns */
- int nAliasAlloc; /* Number of allocated slots for aAlias[] */
int *aAlias; /* Register used to hold aliased result */
u8 explain; /* True if the EXPLAIN flag is found on the query */
Token sNameToken; /* Token with unqualified schema object name */
@@ -2664,6 +2651,29 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#if defined(SQLITE_TEST)
void *sqlite3TestTextToPtr(const char*);
#endif
+
+/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ void sqlite3ExplainBegin(Vdbe*);
+ void sqlite3ExplainPrintf(Vdbe*, const char*, ...);
+ void sqlite3ExplainNL(Vdbe*);
+ void sqlite3ExplainPush(Vdbe*);
+ void sqlite3ExplainPop(Vdbe*);
+ void sqlite3ExplainFinish(Vdbe*);
+ void sqlite3ExplainSelect(Vdbe*, Select*);
+ void sqlite3ExplainExpr(Vdbe*, Expr*);
+ void sqlite3ExplainExprList(Vdbe*, ExprList*);
+ const char *sqlite3VdbeExplanation(Vdbe*);
+#else
+# define sqlite3ExplainBegin(X)
+# define sqlite3ExplainSelect(A,B)
+# define sqlite3ExplainExpr(A,B)
+# define sqlite3ExplainExprList(A,B)
+# define sqlite3ExplainFinish(X)
+# define sqlite3VdbeExplanation(X) 0
+#endif
+
+
void sqlite3SetString(char **, sqlite3*, const char*, ...);
void sqlite3ErrorMsg(Parse*, const char*, ...);
int sqlite3Dequote(char*);
@@ -2674,6 +2684,7 @@ int sqlite3GetTempReg(Parse*);
void sqlite3ReleaseTempReg(Parse*,int);
int sqlite3GetTempRange(Parse*,int);
void sqlite3ReleaseTempRange(Parse*,int,int);
+void sqlite3ClearTempRegCache(Parse*);
Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
Expr *sqlite3Expr(sqlite3*,int,const char*);
void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
@@ -2705,6 +2716,7 @@ void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
+int sqlite3CodeOnce(Parse *);
Bitvec *sqlite3BitvecCreate(u32);
int sqlite3BitvecTest(Bitvec*, u32);
@@ -3043,6 +3055,7 @@ int sqlite3OpenTempDatabase(Parse *);
void sqlite3StrAccumInit(StrAccum*, char*, int, int);
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+void sqlite3AppendSpace(StrAccum*,int);
char *sqlite3StrAccumFinish(StrAccum*);
void sqlite3StrAccumReset(StrAccum*);
void sqlite3SelectDestInit(SelectDest*,int,int);
diff --git a/src/test1.c b/src/test1.c
index 3c6874368..798366a8c 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -2331,6 +2331,33 @@ static int test_stmt_readonly(
}
/*
+** Usage: sqlite3_stmt_busy STMT
+**
+** Return true if STMT is a non-NULL pointer to a statement
+** that has been stepped but not to completion.
+*/
+static int test_stmt_busy(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ if( objc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
+ return TCL_ERROR;
+ }
+
+ if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
+ rc = sqlite3_stmt_busy(pStmt);
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc));
+ return TCL_OK;
+}
+
+/*
** Usage: uses_stmt_journal STMT
**
** Return true if STMT uses a statement journal.
@@ -5993,6 +6020,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_sql", test_sql ,0 },
{ "sqlite3_next_stmt", test_next_stmt ,0 },
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
+ { "sqlite3_stmt_busy", test_stmt_busy ,0 },
{ "uses_stmt_journal", uses_stmt_journal ,0 },
{ "sqlite3_release_memory", test_release_memory, 0},
diff --git a/src/trigger.c b/src/trigger.c
index 22c4877b6..3c4bf62a1 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -904,6 +904,7 @@ static TriggerPrg *codeRowTrigger(
}
pProgram->nMem = pSubParse->nMem;
pProgram->nCsr = pSubParse->nTab;
+ pProgram->nOnce = pSubParse->nOnce;
pProgram->token = (void *)pTrigger;
pPrg->aColmask[0] = pSubParse->oldmask;
pPrg->aColmask[1] = pSubParse->newmask;
diff --git a/src/update.c b/src/update.c
index 1e3052218..73d22690b 100644
--- a/src/update.c
+++ b/src/update.c
@@ -126,8 +126,8 @@ void sqlite3Update(
int regRowCount = 0; /* A count of rows changed */
int regOldRowid; /* The old rowid */
int regNewRowid; /* The new rowid */
- int regNew;
- int regOld = 0;
+ int regNew; /* Content of the NEW.* table in triggers */
+ int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
memset(&sContext, 0, sizeof(sContext));
@@ -276,6 +276,7 @@ void sqlite3Update(
#endif
/* Allocate required registers. */
+ regRowSet = ++pParse->nMem;
regOldRowid = regNewRowid = ++pParse->nMem;
if( pTrigger || hasFK ){
regOld = pParse->nMem + 1;
@@ -310,7 +311,7 @@ void sqlite3Update(
/* Begin the database scan
*/
- sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
pWInfo = sqlite3WhereBegin(
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
);
@@ -321,7 +322,6 @@ void sqlite3Update(
*/
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
if( !okOnePass ){
- regRowSet = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
}
@@ -425,9 +425,10 @@ void sqlite3Update(
newmask = sqlite3TriggerColmask(
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
);
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+ /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/
}else{
j = aXRef[i];
if( j>=0 ){
diff --git a/src/vdbe.c b/src/vdbe.c
index e7fa05037..64ae54e3d 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -764,7 +764,8 @@ case OP_Goto: { /* jump */
** Write the current address onto register P1
** and then jump to address P2.
*/
-case OP_Gosub: { /* jump, in1 */
+case OP_Gosub: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=p->nMem );
pIn1 = &aMem[pOp->p1];
assert( (pIn1->flags & MEM_Dyn)==0 );
memAboutToChange(p, pIn1);
@@ -961,12 +962,25 @@ case OP_String: { /* out2-prerelease */
break;
}
-/* Opcode: Null * P2 * * *
+/* Opcode: Null * P2 P3 * *
**
-** Write a NULL into register P2.
+** Write a NULL into registers P2. If P3 greater than P2, then also write
+** NULL into register P3 and ever register in between P2 and P3. If P3
+** is less than P2 (typically P3 is zero) then only register P2 is
+** set to NULL
*/
case OP_Null: { /* out2-prerelease */
+ int cnt;
+ cnt = pOp->p3-pOp->p2;
+ assert( pOp->p3<=p->nMem );
pOut->flags = MEM_Null;
+ while( cnt>0 ){
+ pOut++;
+ memAboutToChange(p, pOut);
+ MemReleaseExt(pOut);
+ pOut->flags = MEM_Null;
+ cnt--;
+ }
break;
}
@@ -2023,27 +2037,33 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
/* Opcode: Once P1 P2 * * *
**
-** Jump to P2 if the value in register P1 is a not null or zero. If
-** the value is NULL or zero, fall through and change the P1 register
-** to an integer 1.
+** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
+** set the flag and fall through to the next instruction.
**
-** When P1 is not used otherwise in a program, this opcode falls through
-** once and jumps on all subsequent invocations. It is the equivalent
-** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
+** See also: JumpOnce
*/
+case OP_Once: { /* jump */
+ assert( pOp->p1<p->nOnceFlag );
+ if( p->aOnceFlag[pOp->p1] ){
+ pc = pOp->p2-1;
+ }else{
+ p->aOnceFlag[pOp->p1] = 1;
+ }
+ break;
+}
+
/* Opcode: If P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is true. The value
** is considered true if it is numeric and non-zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** in P1 is NULL then take the jump if P3 is non-zero.
*/
/* Opcode: IfNot P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is False. The value
-** is considered true if it has a numeric value of zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** is considered false if it has a numeric value of zero. If the value
+** in P1 is NULL then take the jump if P3 is zero.
*/
-case OP_Once: /* jump, in1 */
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
int c;
@@ -2060,12 +2080,6 @@ case OP_IfNot: { /* jump, in1 */
}
if( c ){
pc = pOp->p2-1;
- }else if( pOp->opcode==OP_Once ){
- assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
- memAboutToChange(p, pIn1);
- pIn1->flags = MEM_Int;
- pIn1->u.i = 1;
- REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
@@ -5071,7 +5085,6 @@ case OP_Program: { /* jump */
pProgram = pOp->p4.pProgram;
pRt = &aMem[pOp->p3];
- assert( memIsValid(pRt) );
assert( pProgram->nOp>0 );
/* If the p5 flag is clear, then recursive invocation of triggers is
@@ -5110,7 +5123,8 @@ case OP_Program: { /* jump */
nMem = pProgram->nMem + pProgram->nCsr;
nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem)
- + pProgram->nCsr * sizeof(VdbeCursor *);
+ + pProgram->nCsr * sizeof(VdbeCursor *)
+ + pProgram->nOnce * sizeof(u8);
pFrame = sqlite3DbMallocZero(db, nByte);
if( !pFrame ){
goto no_mem;
@@ -5130,10 +5144,12 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
+ pFrame->aOnceFlag = p->aOnceFlag;
+ pFrame->nOnceFlag = p->nOnceFlag;
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
- pMem->flags = MEM_Null;
+ pMem->flags = MEM_Invalid;
pMem->db = db;
}
}else{
@@ -5155,7 +5171,11 @@ case OP_Program: { /* jump */
p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
+ p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
+ p->nOnceFlag = pProgram->nOnce;
+ p->nOp = pProgram->nOp;
pc = -1;
+ memset(p->aOnceFlag, 0, p->nOnceFlag);
break;
}
diff --git a/src/vdbe.h b/src/vdbe.h
index 948c73bca..90a43ce6e 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -82,6 +82,7 @@ struct SubProgram {
int nOp; /* Elements in aOp[] */
int nMem; /* Number of memory cells required */
int nCsr; /* Number of cursors required */
+ int nOnce; /* Number of OP_Once instructions */
void *token; /* id that may be used to recursive triggers */
SubProgram *pNext; /* Next sub-program already visited */
};
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 803ae1630..a56dedf6a 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -33,6 +33,9 @@ typedef unsigned char Bool;
/* Opaque type used by code in vdbesort.c */
typedef struct VdbeSorter VdbeSorter;
+/* Opaque type used by the explainer */
+typedef struct Explain Explain;
+
/*
** A cursor is a pointer into a single BTree within a database file.
** The cursor can seek to a BTree entry with a particular key, or
@@ -117,6 +120,8 @@ struct VdbeFrame {
int nOp; /* Size of aOp array */
Mem *aMem; /* Array of memory cells for parent frame */
int nMem; /* Number of entries in aMem */
+ u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
+ int nOnceFlag; /* Number of entries in aOnceFlag */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u16 nCursor; /* Number of entries in apCsr */
void *token; /* Copy of SubProgram.token */
@@ -256,6 +261,18 @@ struct sqlite3_context {
};
/*
+** An Explain object accumulates indented output which is helpful
+** in describing recursive data structures.
+*/
+struct Explain {
+ Vdbe *pVdbe; /* Attach the explanation to this Vdbe */
+ StrAccum str; /* The string being accumulated */
+ int nIndent; /* Number of elements in aIndent */
+ u16 aIndent[100]; /* Levels of indentation */
+ char zBase[100]; /* Initial space */
+};
+
+/*
** An instance of the virtual machine. This structure contains the complete
** state of the virtual machine.
**
@@ -321,11 +338,17 @@ struct Vdbe {
#ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */
#endif
+#ifdef SQLITE_ENABLE_TREE_EXPLAIN
+ Explain *pExplain; /* The explainer */
+ char *zExplain; /* Explanation of data structures */
+#endif
VdbeFrame *pFrame; /* Parent frame */
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
+ int nOnceFlag; /* Size of array aOnceFlag[] */
+ u8 *aOnceFlag; /* Flags for OP_Once */
};
/*
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index adc9dba2f..c6c54854f 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -1278,6 +1278,14 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
}
/*
+** Return true if the prepared statement is in need of being reset.
+*/
+int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
+ Vdbe *v = (Vdbe*)pStmt;
+ return v!=0 && v->pc>0 && v->magic==VDBE_MAGIC_RUN;
+}
+
+/*
** Return a pointer to the next prepared statement after pStmt associated
** with database connection pDb. If pStmt is NULL, return the first
** prepared statement for the database connection. Return NULL if there
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 7c69d28cf..747c846d0 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -913,13 +913,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
case P4_MEM: {
Mem *pMem = pOp->p4.pMem;
- assert( (pMem->flags & MEM_Null)==0 );
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
+ }else if( pMem->flags & MEM_Null ){
+ sqlite3_snprintf(nTemp, zTemp, "NULL");
}else{
assert( pMem->flags & MEM_Blob );
zP4 = "(blob)";
@@ -1094,7 +1095,7 @@ static void releaseMemArray(Mem *p, int N){
p->zMalloc = 0;
}
- p->flags = MEM_Null;
+ p->flags = MEM_Invalid;
}
db->mallocFailed = malloc_failed;
}
@@ -1469,6 +1470,7 @@ void sqlite3VdbeMakeReady(
int nMem; /* Number of VM memory registers */
int nCursor; /* Number of cursors required */
int nArg; /* Number of arguments in subprograms */
+ int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
u8 *zCsr; /* Memory available for allocation */
u8 *zEnd; /* First byte past allocated memory */
@@ -1484,6 +1486,7 @@ void sqlite3VdbeMakeReady(
nMem = pParse->nMem;
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
+ nOnce = pParse->nOnce;
/* For each cursor required, also allocate a memory cell. Memory
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
@@ -1530,6 +1533,7 @@ void sqlite3VdbeMakeReady(
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
&zCsr, zEnd, &nByte);
+ p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
if( nByte ){
p->pFree = sqlite3DbMallocZero(db, nByte);
}
@@ -1538,6 +1542,7 @@ void sqlite3VdbeMakeReady(
}while( nByte && !db->mallocFailed );
p->nCursor = (u16)nCursor;
+ p->nOnceFlag = nOnce;
if( p->aVar ){
p->nVar = (ynVar)nVar;
for(n=0; n<nVar; n++){
@@ -1554,7 +1559,7 @@ void sqlite3VdbeMakeReady(
p->aMem--; /* aMem[] goes from 1..nMem */
p->nMem = nMem; /* not from 0..nMem-1 */
for(n=1; n<=nMem; n++){
- p->aMem[n].flags = MEM_Null;
+ p->aMem[n].flags = MEM_Invalid;
p->aMem[n].db = db;
}
}
@@ -1596,6 +1601,8 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
*/
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
+ v->aOnceFlag = pFrame->aOnceFlag;
+ v->nOnceFlag = pFrame->nOnceFlag;
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -1658,8 +1665,10 @@ static void Cleanup(Vdbe *p){
/* Execute assert() statements to ensure that the Vdbe.apCsr[] and
** Vdbe.aMem[] arrays have already been cleaned up. */
int i;
- for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
- for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
+ if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
+ if( p->aMem ){
+ for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid );
+ }
#endif
sqlite3DbFree(db, p->zErrMsg);
@@ -2127,6 +2136,7 @@ int sqlite3VdbeHalt(Vdbe *p){
if( p->db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
+ if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
closeAllCursors(p);
if( p->magic!=VDBE_MAGIC_RUN ){
return SQLITE_OK;
@@ -2464,6 +2474,10 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ sqlite3DbFree(db, p->zExplain);
+ sqlite3DbFree(db, p->pExplain);
+#endif
sqlite3DbFree(db, p);
}
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index de123b550..4c4be1373 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -12,6 +12,8 @@
**
** This file contains code used to insert the values of host parameters
** (aka "wildcards") into the SQL text output by sqlite3_trace().
+**
+** The Vdbe parse-tree explainer is also found here.
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
@@ -152,3 +154,118 @@ char *sqlite3VdbeExpandSql(
}
#endif /* #ifndef SQLITE_OMIT_TRACE */
+
+/*****************************************************************************
+** The following code implements the data-structure explaining logic
+** for the Vdbe.
+*/
+
+#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+
+/*
+** Allocate a new Explain object
+*/
+void sqlite3ExplainBegin(Vdbe *pVdbe){
+ if( pVdbe ){
+ sqlite3BeginBenignMalloc();
+ Explain *p = sqlite3_malloc( sizeof(Explain) );
+ if( p ){
+ memset(p, 0, sizeof(*p));
+ p->pVdbe = pVdbe;
+ sqlite3_free(pVdbe->pExplain);
+ pVdbe->pExplain = p;
+ sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase),
+ SQLITE_MAX_LENGTH);
+ p->str.useMalloc = 2;
+ }else{
+ sqlite3EndBenignMalloc();
+ }
+ }
+}
+
+/*
+** Return true if the Explain ends with a new-line.
+*/
+static int endsWithNL(Explain *p){
+ return p && p->str.zText && p->str.nChar
+ && p->str.zText[p->str.nChar-1]=='\n';
+}
+
+/*
+** Append text to the indentation
+*/
+void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){
+ Explain *p;
+ if( pVdbe && (p = pVdbe->pExplain)!=0 ){
+ va_list ap;
+ if( p->nIndent && endsWithNL(p) ){
+ int n = p->nIndent;
+ if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent);
+ sqlite3AppendSpace(&p->str, p->aIndent[n-1]);
+ }
+ va_start(ap, zFormat);
+ sqlite3VXPrintf(&p->str, 1, zFormat, ap);
+ va_end(ap);
+ }
+}
+
+/*
+** Append a '\n' if there is not already one.
+*/
+void sqlite3ExplainNL(Vdbe *pVdbe){
+ Explain *p;
+ if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){
+ sqlite3StrAccumAppend(&p->str, "\n", 1);
+ }
+}
+
+/*
+** Push a new indentation level. Subsequent lines will be indented
+** so that they begin at the current cursor position.
+*/
+void sqlite3ExplainPush(Vdbe *pVdbe){
+ Explain *p;
+ if( pVdbe && (p = pVdbe->pExplain)!=0 ){
+ if( p->str.zText && p->nIndent<ArraySize(p->aIndent) ){
+ const char *z = p->str.zText;
+ int i = p->str.nChar-1;
+ int x;
+ while( i>=0 && z[i]!='\n' ){ i--; }
+ x = (p->str.nChar - 1) - i;
+ if( p->nIndent && x<p->aIndent[p->nIndent-1] ){
+ x = p->aIndent[p->nIndent-1];
+ }
+ p->aIndent[p->nIndent] = x;
+ }
+ p->nIndent++;
+ }
+}
+
+/*
+** Pop the indentation stack by one level.
+*/
+void sqlite3ExplainPop(Vdbe *p){
+ if( p && p->pExplain ) p->pExplain->nIndent--;
+}
+
+/*
+** Free the indentation structure
+*/
+void sqlite3ExplainFinish(Vdbe *pVdbe){
+ if( pVdbe && pVdbe->pExplain ){
+ sqlite3_free(pVdbe->zExplain);
+ sqlite3ExplainNL(pVdbe);
+ pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str);
+ sqlite3_free(pVdbe->pExplain);
+ pVdbe->pExplain = 0;
+ sqlite3EndBenignMalloc();
+ }
+}
+
+/*
+** Return the explanation of a virtual machine.
+*/
+const char *sqlite3VdbeExplanation(Vdbe *pVdbe){
+ return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0;
+}
+#endif /* defined(SQLITE_DEBUG) */
diff --git a/src/wal.c b/src/wal.c
index 2c905a29e..209c90975 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -1782,6 +1782,26 @@ static int walCheckpoint(
}
/*
+** Attempt to limit the WAL size to the size limit defined by
+** PRAGMA journal_size_limit.
+*/
+static void walLimitSize(Wal *pWal){
+ if( pWal->mxWalSize>=0 ){
+ i64 sz;
+ int rx;
+ sqlite3BeginBenignMalloc();
+ rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
+ if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
+ rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
+ }
+ sqlite3EndBenignMalloc();
+ if( rx ){
+ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
+ }
+ }
+}
+
+/*
** Close a connection to a log file.
*/
int sqlite3WalClose(
@@ -1814,6 +1834,8 @@ int sqlite3WalClose(
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
if( rc==SQLITE_OK && bPersistWal!=1 ){
isDelete = 1;
+ }else{
+ walLimitSize(pWal);
}
}
@@ -2518,6 +2540,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
return rc;
}
+
/*
** This function is called just before writing a set of frames to the log
** file (see sqlite3WalFrames()). It checks to see if, instead of appending
@@ -2555,23 +2578,7 @@ static int walRestartLog(Wal *pWal){
int i; /* Loop counter */
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
- /* Limit the size of WAL file if the journal_size_limit PRAGMA is
- ** set to a non-negative value. Log errors encountered
- ** during the truncation attempt. */
- if( pWal->mxWalSize>=0 ){
- i64 sz;
- int rx;
- sqlite3BeginBenignMalloc();
- rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
- if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
- rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
- }
- sqlite3EndBenignMalloc();
- if( rx ){
- sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
- }
- }
-
+ walLimitSize(pWal);
pWal->nCkpt++;
pWal->hdr.mxFrame = 0;
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
diff --git a/src/where.c b/src/where.c
index a604d788c..78efbf979 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2005,7 +2005,6 @@ static void constructAutomaticIndex(
int nByte; /* Byte of memory needed for pIdx */
Index *pIdx; /* Object describing the transient index */
Vdbe *v; /* Prepared statement under construction */
- int regIsInit; /* Register set by initialization */
int addrInit; /* Address of the initialization bypass jump */
Table *pTable; /* The table being indexed */
KeyInfo *pKeyinfo; /* Key information for the index */
@@ -2022,8 +2021,7 @@ static void constructAutomaticIndex(
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
- regIsInit = ++pParse->nMem;
- addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
+ addrInit = sqlite3CodeOnce(pParse);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
diff --git a/test/capi3d.test b/test/capi3d.test
index 49e64476e..746ec20b2 100644
--- a/test/capi3d.test
+++ b/test/capi3d.test
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library.
#
# This file is devoted to testing the sqlite3_next_stmt and
-# sqlite3_stmt_readonly interfaces.
+# sqlite3_stmt_readonly and sqlite3_stmt_busy interfaces.
#
# $Id: capi3d.test,v 1.2 2008/07/14 15:11:20 drh Exp $
#
@@ -112,5 +112,29 @@ do_test capi3-2.99 {
sqlite3_stmt_readonly 0
} 1
+# Tests for sqlite3_stmt_busy
+#
+do_test capi3d-3.1 {
+ db eval {INSERT INTO t1 VALUES(6); INSERT INTO t1 VALUES(7);}
+ set STMT [sqlite3_prepare db {SELECT * FROM t1} -1 TAIL]
+ sqlite3_stmt_busy $STMT
+} {0}
+do_test capi3d-3.2 {
+ sqlite3_step $STMT
+ sqlite3_stmt_busy $STMT
+} {1}
+do_test capi3d-3.3 {
+ sqlite3_step $STMT
+ sqlite3_stmt_busy $STMT
+} {1}
+do_test capi3d-3.4 {
+ sqlite3_reset $STMT
+ sqlite3_stmt_busy $STMT
+} {0}
+
+do_test capi3d-3.99 {
+ sqlite3_finalize $STMT
+ sqlite3_stmt_busy 0
+} {0}
finish_test
diff --git a/test/multiplex2.test b/test/multiplex2.test
index bf5791fe8..bdfc05b82 100644
--- a/test/multiplex2.test
+++ b/test/multiplex2.test
@@ -14,12 +14,9 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
source $testdir/lock_common.tcl
-set testprefix multiplex2
-db close
-do_multiclient_test tn {
- foreach f [glob -nocomplain test.*] { forcedelete $f }
+do_multiclient_test tn {
code1 { catch { sqlite3_multiplex_initialize "" 0 } }
code2 { catch { sqlite3_multiplex_initialize "" 0 } }
@@ -47,10 +44,10 @@ do_multiclient_test tn {
SELECT count(*) FROM t1;
}
- do_test 1.$tn.1 { sql1 { SELECT count(*) FROM t1 } } 512
- do_test 1.$tn.2 { sql2 { SELECT count(*) FROM t1 } } 512
+ do_test multiplex-1.$tn.1 { sql1 { SELECT count(*) FROM t1 } } 512
+ do_test multiplex-1.$tn.2 { sql2 { SELECT count(*) FROM t1 } } 512
sql2 { DELETE FROM t1 ; VACUUM }
- do_test 1.$tn.3 { sql1 { SELECT count(*) FROM t1 } } 0
+ do_test multiplex-1.$tn.3 { sql1 { SELECT count(*) FROM t1 } } 0
sql1 {
INSERT INTO t1 VALUES(randomblob(10), randomblob(4000)); -- 1
@@ -66,54 +63,8 @@ do_multiclient_test tn {
SELECT count(*) FROM t1;
}
- do_test 1.$tn.4 { sql2 { SELECT count(*) FROM t1 } } 512
-}
-
-catch {db close}
-foreach f [glob -nocomplain test.*] { forcedelete $f }
-
-ifcapable 8_3_names {
- sqlite3 db test.db -vfs multiplex
- sqlite3_multiplex_control db main chunk_size [expr 256*1024]
-
- # Insert 512 * 256K (128MB) of data. If each row is around 4K, this means
- # we need 32768 rows.
- do_catchsql_test 2.1 {
- CREATE TABLE t1(a, b);
- INSERT INTO t1 VALUES(randomblob(10), randomblob(4000)); -- 1
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 2
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 4
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 8
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 16
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 32
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 64
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 128
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 256
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 512
-
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 1K
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 2K
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 4K
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 8K
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 16K
- INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 32K
-
- } {1 {database or disk is full}}
-
- do_execsql_test 2.2 {
- UPDATE t1 SET a=randomblob(9), b=randomblob(3900);
- PRAGMA integrity_check;
- } ok
-
- db close
- sqlite3 db test.db -vfs multiplex
- sqlite3_multiplex_control db main chunk_size [expr 256*1024]
-
- do_execsql_test 2.3 {
- PRAGMA integrity_check;
- } ok
+ do_test multiplex-1.$tn.4 { sql2 { SELECT count(*) FROM t1 } } 512
}
-catch { db close }
catch { sqlite3_multiplex_shutdown }
finish_test
diff --git a/test/multiplex3.test b/test/multiplex3.test
index bdb264fde..0f52d763b 100644
--- a/test/multiplex3.test
+++ b/test/multiplex3.test
@@ -21,7 +21,7 @@ set ::testprefix multiplex3
ifcapable !8_3_names {
puts -nonewline "SQLite compiled without SQLITE_ENABLE_8_3_NAMES. "
- puts "Skipping tests zipvfsD-*."
+ puts "Skipping tests multiplex3-*."
finish_test
return
}
diff --git a/test/selectB.test b/test/selectB.test
index b9d979acb..05ec9c6bd 100644
--- a/test/selectB.test
+++ b/test/selectB.test
@@ -194,19 +194,28 @@ do_test selectB-3.0 {
}
} {}
-for {set ii 3} {$ii <= 4} {incr ii} {
+for {set ii 3} {$ii <= 6} {incr ii} {
- if {$ii == 4} {
- do_test selectB-4.0 {
- execsql {
- CREATE INDEX i1 ON t1(a);
- CREATE INDEX i2 ON t1(b);
- CREATE INDEX i3 ON t1(c);
- CREATE INDEX i4 ON t2(d);
- CREATE INDEX i5 ON t2(e);
- CREATE INDEX i6 ON t2(f);
- }
- } {}
+ switch $ii {
+ 4 {
+ optimization_control db query-flattener off
+ }
+ 5 {
+ optimization_control db query-flattener on
+ do_test selectB-5.0 {
+ execsql {
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+ CREATE INDEX i3 ON t1(c);
+ CREATE INDEX i4 ON t2(d);
+ CREATE INDEX i5 ON t2(e);
+ CREATE INDEX i6 ON t2(f);
+ }
+ } {}
+ }
+ 6 {
+ optimization_control db query-flattener off
+ }
}
do_test selectB-$ii.1 {
@@ -371,11 +380,47 @@ for {set ii 3} {$ii <= 4} {incr ii} {
}
} {2 4 6 3 6 9 8 10 12 12 15 18 14 16 18 21 24 27}
- do_test selectB-$ii.21 {
+ do_test selectB-$ii.22 {
execsql {
SELECT * FROM (SELECT 345 UNION ALL SELECT d FROM t2) ORDER BY 1;
}
} {3 12 21 345}
+
+ do_test selectB-$ii.23 {
+ execsql {
+ SELECT x, y FROM (
+ SELECT a AS x, b AS y FROM t1
+ UNION ALL
+ SELECT a*10 + 0.1, f*10 + 0.1 FROM t1 JOIN t2 ON (c=d)
+ UNION ALL
+ SELECT a*100, b*100 FROM t1
+ ) ORDER BY 1;
+ }
+ } {2 4 8 10 14 16 80.1 180.1 200 400 800 1000 1400 1600}
+
+ do_test selectB-$ii.24 {
+ execsql {
+ SELECT x, y FROM (
+ SELECT a AS x, b AS y FROM t1
+ UNION ALL
+ SELECT a*10 + 0.1, f*10 + 0.1 FROM t1 LEFT JOIN t2 ON (c=d)
+ UNION ALL
+ SELECT a*100, b*100 FROM t1
+ ) ORDER BY 1;
+ }
+ } {2 4 8 10 14 16 20.1 {} 80.1 180.1 140.1 {} 200 400 800 1000 1400 1600}
+
+ do_test selectB-$ii.25 {
+ execsql {
+ SELECT x+y FROM (
+ SELECT a AS x, b AS y FROM t1
+ UNION ALL
+ SELECT a*10 + 0.1, f*10 + 0.1 FROM t1 LEFT JOIN t2 ON (c=d)
+ UNION ALL
+ SELECT a*100, b*100 FROM t1
+ ) WHERE y+x NOT NULL ORDER BY 1;
+ }
+ } {6 18 30 260.2 600 1800 3000}
}
finish_test
diff --git a/test/tkt-3a77c9714e.test b/test/tkt-3a77c9714e.test
new file mode 100644
index 000000000..d77ee42bb
--- /dev/null
+++ b/test/tkt-3a77c9714e.test
@@ -0,0 +1,68 @@
+# 2011 December 06
+#
+# 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 [3a77c9714e] has been
+# fixed.
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set testprefix "tkt-3a77c9714e"
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(t1_id INTEGER PRIMARY KEY, t1_title TEXT);
+ CREATE TABLE t2(t2_id INTEGER PRIMARY KEY, t2_title TEXT);
+ CREATE TABLE t3(t3_id INTEGER PRIMARY KEY, t3_title TEXT);
+
+ INSERT INTO t1 (t1_id, t1_title) VALUES (888, 'ABCDEF');
+ INSERT INTO t2 (t2_id, t2_title) VALUES (999, 'ABCDEF');
+ INSERT INTO t3 (t3_id, t3_title) VALUES (999, 'ABCDEF');
+}
+
+do_execsql_test 1.2 {
+ SELECT t1_title, t2_title
+ FROM t1 LEFT JOIN t2
+ WHERE
+ t2_id = (SELECT t3_id FROM
+ ( SELECT t3_id FROM t3 WHERE t3_title=t1_title LIMIT 500 )
+ )
+} {ABCDEF ABCDEF}
+
+do_execsql_test 2.1 {
+ CREATE TABLE [Beginnings] (
+ [Id] INTEGER PRIMARY KEY AUTOINCREMENT,[Title] TEXT, [EndingId] INTEGER
+ );
+ CREATE TABLE [Endings] (Id INT,Title TEXT,EndingId INT);
+ INSERT INTO Beginnings (Id, Title, EndingId) VALUES (1, 'FACTOR', 18);
+ INSERT INTO Beginnings (Id, Title, EndingId) VALUES (2, 'SWIMM', 18);
+ INSERT INTO Endings (Id, Title, EndingId) VALUES (1, 'ING', 18);
+}
+
+do_execsql_test 2.2 {
+ SELECT
+ SrcWord, Beginnings.Title
+ FROM
+ (SELECT 'FACTORING' AS SrcWord UNION SELECT 'SWIMMING' AS SrcWord )
+ LEFT JOIN
+ Beginnings
+ WHERE Beginnings.Id= (
+ SELECT BeginningId FROM (
+ SELECT SrcWord, B.Id as BeginningId, B.Title || E.Title As Connected
+ FROM Beginnings B LEFT JOIN Endings E ON B.EndingId=E.EndingId
+ WHERE Connected=SrcWord LIMIT 1
+ )
+ )
+} {FACTORING FACTOR SWIMMING SWIMM}
+
+
+finish_test
+
diff --git a/test/tkt-7bbfb7d442.test b/test/tkt-7bbfb7d442.test
new file mode 100644
index 000000000..e560a0d4d
--- /dev/null
+++ b/test/tkt-7bbfb7d442.test
@@ -0,0 +1,154 @@
+# 2011 December 9
+#
+# 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 [7bbfb7d442] has been
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-7bbfb7d442
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+
+ CREATE TABLE t2(c, d);
+ INSERT INTO t2 VALUES('one', 'I');
+ INSERT INTO t2 VALUES('two', 'II');
+ INSERT INTO t2 VALUES('three', 'III');
+
+ CREATE TABLE t3(t3_a PRIMARY KEY, t3_d);
+ CREATE TRIGGER t3t AFTER INSERT ON t3 WHEN new.t3_d IS NULL BEGIN
+ UPDATE t3 SET t3_d = (
+ SELECT d FROM
+ (SELECT * FROM t2 WHERE (new.t3_a%2)=(rowid%2) LIMIT 10),
+ (SELECT * FROM t1 WHERE (new.t3_a%2)=(rowid%2) LIMIT 10)
+ WHERE a = new.t3_a AND b = c
+ ) WHERE t3_a = new.t3_a;
+ END;
+}
+
+do_execsql_test 1.2 {
+ INSERT INTO t3(t3_a) VALUES(1);
+ INSERT INTO t3(t3_a) VALUES(2);
+ INSERT INTO t3(t3_a) VALUES(3);
+ SELECT * FROM t3;
+} {1 I 2 II 3 III}
+
+do_execsql_test 1.3 { DELETE FROM t3 }
+
+do_execsql_test 1.4 {
+ INSERT INTO t3(t3_a) SELECT 1 UNION SELECT 2 UNION SELECT 3;
+ SELECT * FROM t3;
+} {1 I 2 II 3 III}
+
+
+
+#-------------------------------------------------------------------------
+# The following test case - 2.* - is from the original bug report as
+# posted to the mailing list.
+#
+do_execsql_test 2.1 {
+ CREATE TABLE InventoryControl (
+ InventoryControlId INTEGER PRIMARY KEY AUTOINCREMENT,
+ SKU INTEGER NOT NULL,
+ Variant INTEGER NOT NULL DEFAULT 0,
+ ControlDate DATE NOT NULL,
+ ControlState INTEGER NOT NULL DEFAULT -1,
+ DeliveredQty VARCHAR(30)
+ );
+
+ CREATE TRIGGER TGR_InventoryControl_AfterInsert
+ AFTER INSERT ON InventoryControl
+ FOR EACH ROW WHEN NEW.ControlState=-1 BEGIN
+
+ INSERT OR REPLACE INTO InventoryControl(
+ InventoryControlId,SKU,Variant,ControlDate,ControlState,DeliveredQty
+ ) SELECT
+ T1.InventoryControlId AS InventoryControlId,
+ T1.SKU AS SKU,
+ T1.Variant AS Variant,
+ T1.ControlDate AS ControlDate,
+ 1 AS ControlState,
+ COALESCE(T2.DeliveredQty,0) AS DeliveredQty
+ FROM (
+ SELECT
+ NEW.InventoryControlId AS InventoryControlId,
+ II.SKU AS SKU,
+ II.Variant AS Variant,
+ COALESCE(LastClosedIC.ControlDate,NEW.ControlDate) AS ControlDate
+ FROM
+ InventoryItem II
+ LEFT JOIN
+ InventoryControl LastClosedIC
+ ON LastClosedIC.InventoryControlId IN ( SELECT 99999 )
+ WHERE
+ II.SKU=NEW.SKU AND
+ II.Variant=NEW.Variant
+ ) T1
+ LEFT JOIN (
+ SELECT
+ TD.SKU AS SKU,
+ TD.Variant AS Variant,
+ 10 AS DeliveredQty
+ FROM
+ TransactionDetail TD
+ WHERE
+ TD.SKU=NEW.SKU AND
+ TD.Variant=NEW.Variant
+ ) T2
+ ON T2.SKU=T1.SKU AND
+ T2.Variant=T1.Variant;
+ END;
+
+ CREATE TABLE InventoryItem (
+ SKU INTEGER NOT NULL,
+ Variant INTEGER NOT NULL DEFAULT 0,
+ DeptCode INTEGER NOT NULL,
+ GroupCode INTEGER NOT NULL,
+ ItemDescription VARCHAR(120) NOT NULL,
+ PRIMARY KEY(SKU, Variant)
+ );
+
+ INSERT INTO InventoryItem VALUES(220,0,1,170,'Scoth Tampon Recurer');
+ INSERT INTO InventoryItem VALUES(31,0,1,110,'Fromage');
+
+ CREATE TABLE TransactionDetail (
+ TransactionId INTEGER NOT NULL,
+ SKU INTEGER NOT NULL,
+ Variant INTEGER NOT NULL DEFAULT 0,
+ PRIMARY KEY(TransactionId, SKU, Variant)
+ );
+ INSERT INTO TransactionDetail(TransactionId, SKU, Variant) VALUES(44, 31, 0);
+
+
+ INSERT INTO InventoryControl(SKU, Variant, ControlDate) SELECT
+ II.SKU AS SKU, II.Variant AS Variant, '2011-08-30' AS ControlDate
+ FROM InventoryItem II;
+}
+
+do_execsql_test 2.2 {
+ SELECT SKU, DeliveredQty FROM InventoryControl WHERE SKU=31
+} {31 10}
+
+do_execsql_test 2.3 {
+ SELECT CASE WHEN DeliveredQty=10 THEN "TEST PASSED!" ELSE "TEST FAILED!" END
+ FROM InventoryControl WHERE SKU=31;
+} {{TEST PASSED!}}
+
+
+finish_test
+
+
diff --git a/test/walpersist.test b/test/walpersist.test
index 175dcbf89..bf65bd1e1 100644
--- a/test/walpersist.test
+++ b/test/walpersist.test
@@ -67,7 +67,25 @@ do_test walpersist-1.11 {
list [file exists test.db] [file exists test.db-wal] [file exists test.db-shm]
} {1 1 1}
-
-
+# Make sure the journal_size_limit works to limit the size of the
+# persisted wal file.
+forcedelete test.db test.db-shm test.db-wal
+do_test walpersist-2.1 {
+ sqlite3 db test.db
+ db eval {
+ PRAGMA journal_mode=WAL;
+ PRAGMA wal_autocheckpoint=OFF;
+ PRAGMA journal_size_limit=12000;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(randomblob(50000));
+ UPDATE t1 SET x=randomblob(50000);
+ }
+ expr {[file size test.db-wal]>100000}
+} {1}
+do_test walpersist-2.2 {
+ file_control_persist_wal db 1
+ db close
+ file size test.db-wal
+} {12000}
finish_test