aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2012-10-09 14:58:15 +0000
committerdrh <drh@noemail.net>2012-10-09 14:58:15 +0000
commitd1102b1ec7d50045cbbcf2575eacb2d423361fc7 (patch)
treea586e444e7362a6cf02fa05c85311851e36abb97
parentc4645dacfb435002911b187b474fa0788db9cde0 (diff)
parent5a55826bcda624f8fa4a3b7cc0c8845a689d8957 (diff)
downloadsqlite-d1102b1ec7d50045cbbcf2575eacb2d423361fc7.tar.gz
sqlite-d1102b1ec7d50045cbbcf2575eacb2d423361fc7.zip
Merge the latest trunk changes, especially the ORDER BY optimizer enhancements
but also other fixes, onto the sessions branch. FossilOrigin-Name: f1fbb8c5bfa84e84e0b8e2872d83b06a0c0d5acc
-rw-r--r--Makefile.msc46
-rw-r--r--ext/rtree/rtree.c4
-rw-r--r--manifest86
-rw-r--r--manifest.uuid2
-rw-r--r--src/alter.c4
-rw-r--r--src/attach.c8
-rw-r--r--src/backup.c7
-rw-r--r--src/btree.c26
-rw-r--r--src/btree.h3
-rw-r--r--src/build.c39
-rw-r--r--src/callback.c12
-rw-r--r--src/delete.c2
-rw-r--r--src/expr.c1
-rw-r--r--src/insert.c29
-rw-r--r--src/main.c2
-rw-r--r--src/os_unix.c4
-rw-r--r--src/os_win.c74
-rw-r--r--src/pager.c33
-rw-r--r--src/pager.h1
-rw-r--r--src/pragma.c1
-rw-r--r--src/select.c3
-rw-r--r--src/sqlite.h.in12
-rw-r--r--src/sqliteInt.h5
-rw-r--r--src/tclsqlite.c54
-rw-r--r--src/test1.c6
-rw-r--r--src/test_intarray.c5
-rw-r--r--src/test_quota.c9
-rw-r--r--src/vdbeaux.c2
-rw-r--r--src/vtab.c10
-rw-r--r--src/wal.c2
-rw-r--r--src/where.c731
-rw-r--r--test/bigfile.test1
-rw-r--r--test/bigfile2.test1
-rw-r--r--test/fuzzer1.test2
-rw-r--r--test/orderby1.test40
-rw-r--r--test/orderby2.test117
-rw-r--r--test/shared9.test207
-rw-r--r--test/tclsqlite.test11
-rw-r--r--test/trigger1.test56
-rw-r--r--test/where.test4
-rwxr-xr-xtool/build-all-msvc.bat6
41 files changed, 1104 insertions, 564 deletions
diff --git a/Makefile.msc b/Makefile.msc
index f64781a9d..083e588d8 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -30,6 +30,10 @@ XCOMPILE = 0
#
USE_NATIVE_LIBPATHS = 0
+# Set this 0 to skip the compiling and embedding of version resources.
+#
+USE_RC = 1
+
# Set this non-0 to compile binaries suitable for the WinRT environment.
# This setting does not apply to any binaries that require Tcl to operate
# properly (i.e. the text fixture, etc).
@@ -168,8 +172,8 @@ RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src
!IF $(FOR_WINRT)!=0
TCC = $(TCC) -DSQLITE_OS_WINRT=1
RCC = $(RCC) -DSQLITE_OS_WINRT=1
-TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_PARTITION_APP
-RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_PARTITION_APP
+TCC = $(TCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
+RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_FAMILY_APP
!ENDIF
# Also, we need to dynamically link to the correct MSVC runtime
@@ -180,14 +184,18 @@ RCC = $(RCC) -DWINAPI_FAMILY=WINAPI_PARTITION_APP
!IF $(FOR_WINRT)!=0 || $(USE_CRT_DLL)!=0
!IF $(DEBUG)>0
TCC = $(TCC) -MDd
+BCC = $(BCC) -MDd
!ELSE
TCC = $(TCC) -MD
+BCC = $(BCC) -MD
!ENDIF
!ELSE
!IF $(DEBUG)>0
TCC = $(TCC) -MTd
+BCC = $(BCC) -MTd
!ELSE
TCC = $(TCC) -MT
+BCC = $(BCC) -MT
!ENDIF
!ENDIF
@@ -474,6 +482,14 @@ LIBOBJ = $(LIBOBJS0)
LIBOBJ = $(LIBOBJS1)
!ENDIF
+# Determine if embedded resource compilation and usage are enabled.
+#
+!IF $(USE_RC)!=0
+LIBRESOBJS = sqlite3res.lo
+!ELSE
+LIBRESOBJS =
+!ENDIF
+
# All of the source code files.
#
SRC = \
@@ -782,10 +798,10 @@ libsqlite3.lib: $(LIBOBJ)
libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib
$(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS)
-sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib sqlite3res.lo sqlite3.h
+sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h
$(LTLINK) $(READLINE_FLAGS) \
$(TOP)\src\shell.c \
- /link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib sqlite3res.lo $(LIBREADLINE) $(LTLIBS) $(TLIBS)
+ /link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
# This target creates a directory named "tsrc" and fills it with
# copies of all of the C source code and header files needed to
@@ -835,14 +851,16 @@ opcodes.lo: opcodes.c
# Rule to build the Win32 resources object file.
#
-sqlite3res.lo: $(TOP)\src\sqlite3.rc $(HDR)
+!IF $(USE_RC)!=0
+$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(HDR)
echo #ifndef SQLITE_RESOURCE_VERSION > sqlite3rc.h
for /F %%V in ('type "$(TOP)\VERSION"') do ( \
echo #define SQLITE_RESOURCE_VERSION %%V \
| $(NAWK) "/.*/ { gsub(/[.]/,\",\");print }" >> sqlite3rc.h \
)
echo #endif >> sqlite3rc.h
- $(LTRCOMPILE) -fo sqlite3res.lo $(TOP)\src\sqlite3.rc
+ $(LTRCOMPILE) -fo $(LIBRESOBJS) $(TOP)\src\sqlite3.rc
+!ENDIF
# Rules to build individual *.lo files from files in the src directory.
#
@@ -1059,8 +1077,8 @@ tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR)
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR)
$(LTCOMPILE) -DTCLSH=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
-tclsqlite3.exe: tclsqlite-shell.lo libsqlite3.lib sqlite3res.lo
- $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ libsqlite3.lib tclsqlite-shell.lo sqlite3res.lo $(LTLIBS) $(TLIBS)
+tclsqlite3.exe: tclsqlite-shell.lo libsqlite3.lib $(LIBRESOBJS)
+ $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ libsqlite3.lib tclsqlite-shell.lo $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
# Rules to build opcodes.c and opcodes.h
#
@@ -1173,11 +1191,11 @@ TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
!ENDIF
-testfixture.exe: $(TESTFIXTURE_SRC) sqlite3res.lo $(HDR)
+testfixture.exe: $(TESTFIXTURE_SRC) $(LIBRESOBJS) $(HDR)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
-DBUILD_sqlite -I$(TCLINCDIR) \
$(TESTFIXTURE_SRC) \
- /link $(LTLINKOPTS) $(LTLIBPATHS) sqlite3res.lo $(LTLIBS) $(TLIBS)
+ /link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
fulltest: testfixture.exe sqlite3.exe
.\testfixture.exe $(TOP)\test\all.test
@@ -1198,9 +1216,9 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TO
$(NAWK) -f $(TOP)\tool\tostr.awk $(TOP)\tool\spaceanal.tcl >> $@
echo ; return zMainloop; } >> $@
-sqlite3_analyzer.exe: sqlite3_analyzer.c sqlite3res.lo
+sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
$(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \
- /link $(LTLINKOPTS) $(LTLIBPATHS) sqlite3res.lo $(LTLIBS) $(TLIBS)
+ /link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
clean:
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
@@ -1233,5 +1251,5 @@ sqlite3.def: libsqlite3.lib
| $(NAWK) "/ 1 _?sqlite3_/ { sub(/^.* _?/,\"\");print }" \
| sort >> sqlite3.def
-sqlite3.dll: $(LIBOBJ) sqlite3res.lo sqlite3.def
- $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ) sqlite3res.lo $(LTLIBS) $(TLIBS)
+sqlite3.dll: $(LIBOBJ) $(LIBRESOBJS) sqlite3.def
+ $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:sqlite3.def /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c
index 66da481e0..731cb32be 100644
--- a/ext/rtree/rtree.c
+++ b/ext/rtree/rtree.c
@@ -2660,7 +2660,7 @@ static int newRowid(Rtree *pRtree, i64 *piRowid){
*/
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
int rc; /* Return code */
- RtreeNode *pLeaf; /* Leaf node containing record iDelete */
+ RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */
int iCell; /* Index of iDelete cell in pLeaf */
RtreeNode *pRoot; /* Root node of rtree structure */
@@ -2863,7 +2863,7 @@ static int rtreeUpdate(
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert the new record into the r-tree */
- RtreeNode *pLeaf;
+ RtreeNode *pLeaf = 0;
/* Figure out the rowid of the new row. */
if( bHaveRowid==0 ){
diff --git a/manifest b/manifest
index 46c1815db..72157a169 100644
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Merge\sthe\slatest\strunk\schanges\s(especially\s"PRAGMA\sbusy_timeout"\sand\sthe\nORDER\sBY\squery\splanner\soptimizations)\sinto\sthe\ssessions\sbranch.
-D 2012-09-28T13:05:48.372
+C Merge\sthe\slatest\strunk\schanges,\sespecially\sthe\sORDER\sBY\soptimizer\senhancements\nbut\salso\sother\sfixes,\sonto\sthe\ssessions\sbranch.
+D 2012-10-09T14:58:15.175
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
-F Makefile.msc 321efe6f572a1d4f8e27794c4575dd9b51d3e731
+F Makefile.msc a26021086defb54e6cb61439a90be5960b713017
F Makefile.vxworks b18ad88e9a8c6a001f5cf4a389116a4f1a7ab45f
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION edab4af5a4623f8198833ea481ce98ab53750a8d
@@ -83,7 +83,7 @@ F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c d17aecb7a92762efa7b1f5d5fd7c88fd77d70827
+F ext/rtree/rtree.c 47064ee2995a396bfb626337d2b43f12cc0af687
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test e474a2b5eff231496dbd073fe67e5fbaf7f444c9
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@@ -126,23 +126,23 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
-F src/alter.c 0c1716aa8d248bd6bc750e23be4c68ad05f8668c
+F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410
-F src/attach.c 577bf5675b0c50495fc28549f2fcbdb1bac71143
+F src/attach.c 34c15ecd686e58f08e5bb1389e28a0b65c2c83db
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
-F src/backup.c 5b31b24d6814b11de763debf342c8cd0a15a4910
+F src/backup.c afc067b9a9050ff48b9d46285c53d096c556a73d
F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
-F src/btree.c 9cf6de113d23d47967df24b8d8ce6501c879d7e6
-F src/btree.h 4aee02e879211bfcfd3f551769578d2e940ab6c2
+F src/btree.c 14de53ebb334633ee632ab3c5b9262cfe7cbe455
+F src/btree.h 078f76b28c338ab6eb6dd7324d63ee54463aeb6c
F src/btreeInt.h 4e5c2bd0f9b36b2a815a6d84f771a61a65830621
-F src/build.c c4555e16f8ccdadb2616014c617ed8166c5a93f7
-F src/callback.c 0cb4228cdcd827dcc5def98fb099edcc9142dbcd
+F src/build.c b21c130017820dfe8721e380325e128fd0c6e019
+F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 72a70dcfda75d3a1f81041ce4573e7afddcd8e4e
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
-F src/delete.c c856ca31177a5cc1612b47ca0c628cd227aa072e
-F src/expr.c 4de967b85f577ba00a7cdcb53d22070def6198db
+F src/delete.c e44a1b66cd3dc1e9e2c74147ee4991214e6e6978
+F src/expr.c 57fb8e7a05d4147e40b9f4c439e37ed2abab9332
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c c82a04e7a92bb728f9ab972b76590403283be2af
F src/func.c cbb90dc84b22eea25caf39528d342279e61b8898
@@ -150,12 +150,12 @@ F src/global.c fb44b11e02e06c995e6ed6642509edd23599d584
F src/hash.c a4031441741932da9e7a65bee2b36b5d0e81c073
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c 08ec5cec28c692fc27a23659875cc167f5c6ece7
+F src/insert.c 6273647b67e27e3f81b7d1fd144307ea726841d0
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
-F src/main.c febaf66b42c36433b170e4704fef5b8b073a6d61
+F src/main.c 5cee8048dde78b2b47f5c2090cbb31b10d190673
F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa
@@ -172,31 +172,31 @@ F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c
F src/os.h 027491c77d2404c0a678bb3fb06286f331eb9b57
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
-F src/os_unix.c 69b2fe66316524eebf5f1ce85c1fdfe2952307e9
-F src/os_win.c 90c7a1fe2698867555ba4266f5bd436c85d0d1dc
-F src/pager.c 9c59818c480261c1c5a4772532e0df92a27745a1
-F src/pager.h 8b8c9bc065a3c66769df8724dfdf492ee1aab3c5
+F src/os_unix.c a5a45a2857c43b37bac145b521064a85a544cd7a
+F src/os_win.c 28bd027791252a4012dffd4d64355a1eb84d761c
+F src/pager.c a7ad8c38809edf0be545e8f52da5bcbb88885b38
+F src/pager.h bdbc379557eb2e233dfec10986b3086877e72db7
F src/parse.y f29df90bd3adc64b33114ab1de9fb7768fcf2099
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c
F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9
-F src/pragma.c 7f5a0fa6dead752d9af3a1e1d16bace81d22ef95
+F src/pragma.c 4f31fe4ae4b885a8be8b1db9d94f9f5b6c8b5624
F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c
F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 9e28280ec98035f31900fdd1db01f86f68ca6c32
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
-F src/select.c 75c5e37cc882c468383c9d9e07496b9a16cfae3e
+F src/select.c 2a82736faeca1fe93315eda20c691d68ec13bb6c
F src/shell.c 8ee5a3cb502e2d574f97b43972e6c1e275e7bec7
-F src/sqlite.h.in 2f2822ac789d820e05afb19831e62fd83e69c60b
+F src/sqlite.h.in d460ae07ecdd1c820272d9c217547c7b572cb4b7
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
-F src/sqliteInt.h 34844b666512c2c5656c5d470f165ed1db0fcd48
+F src/sqliteInt.h fec16c0932b30aedf5d3b577aaf5f881589c8dd0
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
-F src/tclsqlite.c 79ac64bdd8555b8967032e1e69051d88366f9b77
-F src/test1.c 0354b555639c92d2a63c0ad4e74ed152ba47e604
+F src/tclsqlite.c 289be7b639406314813219ee7bc043d21f36ab12
+F src/test1.c 936afc02766403e5debca49a1817a780e116df7e
F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf
F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d
F src/test4.c bf9fa9bece01de08e6f5e02314e4af5c13590dfa
@@ -216,7 +216,7 @@ F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170
F src/test_fuzzer.c 1d26aa965120420bc14807da29d4d4541bfa6148
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
-F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
+F src/test_intarray.c 07ddcebe4097d400ffca362770f1d883c112387a
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
@@ -227,7 +227,7 @@ F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25
F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
-F src/test_quota.c 8ab295092c70903ca6f3209fa4c75f5cb6c1bf8e
+F src/test_quota.c e5fdb7d28e5afae1b619922804e544db0041ec81
F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
@@ -252,16 +252,16 @@ F src/vdbe.c e8e2e34c23a474c38f494d572a0d8accaabd0b4a
F src/vdbe.h 87b8ff40de3f55dbcdc33029416862f517c37a2f
F src/vdbeInt.h 39acf85fa83f98e27e728722a0f53daf7d174b61
F src/vdbeapi.c 58fdcd56109c05876f69c25d47a138ef370d3647
-F src/vdbeaux.c 857c8372d3b06d66e37428712265f9033932f3ec
+F src/vdbeaux.c 1005a2ea2cbbe5d2841367eebae67ce848a6247a
F src/vdbeblob.c 11248c6362389569764682eb0f59ce910f3cc381
F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74
F src/vdbesort.c 0dc1b274dcb4d4c8e71b0b2b15261f286caba39b
F src/vdbetrace.c 8bd5da325fc90f28464335e4cc4ad1407fe30835
-F src/vtab.c d8020c0a0e8ccc490ca449d7e665311b6e9f3ba9
-F src/wal.c 5acb3e7bbd31f10ba39acad9ce6b399055337a9d
+F src/vtab.c 9c64ae18af78c740610df841c6f49fc2d240a279
+F src/wal.c e1fe8f92a0ea0fef8faa87ec43a127a478589d22
F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b
-F src/where.c d836df3a2096c41c39e48ab5636f09f94ba02676
+F src/where.c 410017c604713ab6c010a9e3731a7688a0c7a059
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00
@@ -307,8 +307,8 @@ F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/bc_common.tcl 5c8689cc6d2fb44b7c0968ae4f85eb26d50022fa
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
-F test/bigfile.test 8f88b5ef065e31c615c49d725ede94155fbe9609
-F test/bigfile2.test 8a3c242c3c3481e7cde5a6ef2a66fdc367a095f7
+F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
+F test/bigfile2.test 7c79f1ef0c6c2c2bc1e7bd895596fab32bfb4796
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
@@ -524,7 +524,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
-F test/fuzzer1.test 69cf1036b92fd3b8e1fd65bef4d7ee3f085c28fb
+F test/fuzzer1.test a2e93bb1e19513dd6bf9c63d3d7c4673c983ca19
F test/fuzzerfault.test ff2282c81797b6a355f0748d8b54c7287c5d2b25
F test/hook.test 94b927b15883f5c1477ab09eecd16275addb08f4
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
@@ -646,7 +646,8 @@ F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
-F test/orderby1.test 31c9865626046666e81cd22ecf8e1c24a4ea41b6
+F test/orderby1.test ef4f7c40df81b9a4303a718433d34052f07db47d
+F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
F test/pager1.test 2163c6ef119f497a71a84137c957c63763e640ab
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
@@ -714,6 +715,7 @@ F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d
F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9
F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d
+F test/shared9.test 614a3ca431adc73c857632deb4eff75bcaee40ec
F test/shared_err.test 91e26ec4f3fbe07951967955585137e2f18993de
F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
F test/shell1.test 272384163432c0efd2c6817396beb0d119565d53
@@ -749,7 +751,7 @@ F test/syscall.test bea9bf329bff733c791310244617c2a76974e64a
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
-F test/tclsqlite.test 02b2c268d38203987dd1839598ac524f7612ec3c
+F test/tclsqlite.test a7308276aad2e6c0bfb5b0414424dd0d9cc0cad7
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2
F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
@@ -896,7 +898,7 @@ F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
-F test/trigger1.test de42feb7cd442787d38185ae74f5a1d7afa400cb
+F test/trigger1.test 30f343f91586765874a28ad539c06f5a5f049931
F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816
F test/trigger3.test d2c60d8be271c355d61727411e753181e877230a
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
@@ -968,7 +970,7 @@ F test/walro.test a31deb621033442a76c3a61e44929250d06f81b1
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
-F test/where.test a6bfb5a29286811d798d326a8f1153a58c0fb2bd
+F test/where.test 41b65069a227a61238c1387b050f029480ca5677
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
@@ -986,7 +988,7 @@ F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test e7f77fded01dfcdf92ac2c5400f1e35d7a21463c
-F tool/build-all-msvc.bat 1a18aa39983ae7354d834bc55a850a54fc007576 x
+F tool/build-all-msvc.bat cd505f24717ca4ecd94c651f763d7bb4e7fe68af x
F tool/build-shell.sh b64a481901fc9ffe5ca8812a2a9255b6cfb77381
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
@@ -1030,7 +1032,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9
-P fae9eb197fcef726fd2c7c701afe6805fc008cf9 1e874629d7cf568368b912b295bd3001147d0b52
-R 3dd4bfa3bb20bc9f3b355292d5659141
+P 6ca8eae1f89d19ee23cbc3a869d85b57d29b4a7d a02599ad85d02470c9effa51c02dbda7796bfa16
+R 05272e22ec105aad09507c576198ab3e
U drh
-Z d6a15b3f33482cbc762a2fcdf6bf0623
+Z 8b270f262a5da1e0ba6e8c7b897dd596
diff --git a/manifest.uuid b/manifest.uuid
index 8adce7bd3..f368e5856 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-6ca8eae1f89d19ee23cbc3a869d85b57d29b4a7d \ No newline at end of file
+f1fbb8c5bfa84e84e0b8e2872d83b06a0c0d5acc \ No newline at end of file
diff --git a/src/alter.c b/src/alter.c
index 8a96ab704..a49d3349d 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -414,7 +414,7 @@ void sqlite3AlterRenameTable(
assert( pSrc->nSrc==1 );
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
- pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zName;
@@ -757,7 +757,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
assert( pParse->pNewTable==0 );
assert( sqlite3BtreeHoldsAllMutexes(db) );
if( db->mallocFailed ) goto exit_begin_add_column;
- pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_begin_add_column;
#ifndef SQLITE_OMIT_VIRTUALTABLE
diff --git a/src/attach.c b/src/attach.c
index e295c3011..3e045aa36 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -434,6 +434,7 @@ int sqlite3FixInit(
assert( db->nDb>iDb );
pFix->pParse = pParse;
pFix->zDb = db->aDb[iDb].zName;
+ pFix->pSchema = db->aDb[iDb].pSchema;
pFix->zType = zType;
pFix->pName = pName;
return 1;
@@ -464,14 +465,15 @@ int sqlite3FixSrcList(
if( NEVER(pList==0) ) return 0;
zDb = pFix->zDb;
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pItem->zDatabase==0 ){
- pItem->zDatabase = sqlite3DbStrDup(pFix->pParse->db, zDb);
- }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){
+ if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
sqlite3ErrorMsg(pFix->pParse,
"%s %T cannot reference objects in database %s",
pFix->zType, pFix->pName, pItem->zDatabase);
return 1;
}
+ sqlite3_free(pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->pSchema = pFix->pSchema;
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
diff --git a/src/backup.c b/src/backup.c
index 4881215e9..6abc11c20 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -219,13 +219,16 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
#ifdef SQLITE_HAS_CODEC
- int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc);
+ /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
+ ** guaranteed that the shared-mutex is held by this thread, handle
+ ** p->pSrc may not actually be the owner. */
+ int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
#endif
-
int rc = SQLITE_OK;
i64 iOff;
+ assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
assert( p->bDestLocked );
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
diff --git a/src/btree.c b/src/btree.c
index 47dd7db99..4ee0c860a 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -2200,6 +2200,24 @@ int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
+/*
+** This function is similar to sqlite3BtreeGetReserve(), except that it
+** may only be called if it is guaranteed that the b-tree mutex is already
+** held.
+**
+** This is useful in one special case in the backup API code where it is
+** known that the shared b-tree mutex is held, but the mutex on the
+** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
+** were to be called, it might collide with some other operation on the
+** database handle that owns *p, causing undefined behaviour.
+*/
+int sqlite3BtreeGetReserveNoMutex(Btree *p){
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ return p->pBt->pageSize - p->pBt->usableSize;
+}
+#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */
+
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
/*
** Return the number of bytes of space at the end of every page that
@@ -5256,7 +5274,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
- return SQLITE_CORRUPT; /* Cell extends past end of page */
+ return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
}
ovflPgno = get4byte(&pCell[info.iOverflow]);
assert( pBt->usableSize > 4 );
@@ -5922,6 +5940,9 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", off)
+#endif
static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
@@ -6552,6 +6573,9 @@ balance_cleanup:
return rc;
}
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", on)
+#endif
/*
diff --git a/src/btree.h b/src/btree.h
index 95897d566..0efa0cdcd 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -71,6 +71,9 @@ int sqlite3BtreeMaxPageCount(Btree*,int);
u32 sqlite3BtreeLastPage(Btree*);
int sqlite3BtreeSecureDelete(Btree*,int);
int sqlite3BtreeGetReserve(Btree*);
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
+int sqlite3BtreeGetReserveNoMutex(Btree *p);
+#endif
int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
diff --git a/src/build.c b/src/build.c
index 8910bbd98..00ff705f7 100644
--- a/src/build.c
+++ b/src/build.c
@@ -320,6 +320,31 @@ Table *sqlite3LocateTable(
}
/*
+** Locate the table identified by *p.
+**
+** This is a wrapper around sqlite3LocateTable(). The difference between
+** sqlite3LocateTable() and this function is that this function restricts
+** the search to schema (p->pSchema) if it is not NULL. p->pSchema may be
+** non-NULL if it is part of a view or trigger program definition. See
+** sqlite3FixSrcList() for details.
+*/
+Table *sqlite3LocateTableItem(
+ Parse *pParse,
+ int isView,
+ struct SrcList_item *p
+){
+ const char *zDb;
+ assert( p->pSchema==0 || p->zDatabase==0 );
+ if( p->pSchema ){
+ int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+ zDb = pParse->db->aDb[iDb].zName;
+ }else{
+ zDb = p->zDatabase;
+ }
+ return sqlite3LocateTable(pParse, isView, p->zName, zDb);
+}
+
+/*
** Locate the in-memory structure that describes
** a particular index given the name of that index
** and the name of the database that contains the index.
@@ -1295,10 +1320,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
if( !initbusy && (!pColl || !pColl->xCmp) ){
- pColl = sqlite3GetCollSeq(db, enc, pColl, zName);
- if( !pColl ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
- }
+ pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName);
}
return pColl;
@@ -2114,8 +2136,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
assert( pParse->nErr==0 );
assert( pName->nSrc==1 );
if( noErr ) db->suppressErr++;
- pTab = sqlite3LocateTable(pParse, isView,
- pName->a[0].zName, pName->a[0].zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
if( noErr ) db->suppressErr--;
if( pTab==0 ){
@@ -2555,9 +2576,9 @@ Index *sqlite3CreateIndex(
** sqlite3FixSrcList can never fail. */
assert(0);
}
- pTab = sqlite3LocateTable(pParse, 0, pTblName->a[0].zName,
- pTblName->a[0].zDatabase);
- if( !pTab || db->mallocFailed ) goto exit_create_index;
+ pTab = sqlite3LocateTableItem(pParse, 0, &pTblName->a[0]);
+ assert( db->mallocFailed==0 || pTab==0 );
+ if( pTab==0 ) goto exit_create_index;
assert( db->aDb[iDb].pSchema==pTab->pSchema );
}else{
assert( pName==0 );
diff --git a/src/callback.c b/src/callback.c
index a515e05e2..d40c65cb9 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -75,17 +75,18 @@ static int synthCollSeq(sqlite3 *db, CollSeq *pColl){
**
** The return value is either the collation sequence to be used in database
** db for collation type name zName, length nName, or NULL, if no collation
-** sequence can be found.
+** sequence can be found. If no collation is found, leave an error message.
**
** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
*/
CollSeq *sqlite3GetCollSeq(
- sqlite3* db, /* The database connection */
+ Parse *pParse, /* Parsing context */
u8 enc, /* The desired encoding for the collating sequence */
CollSeq *pColl, /* Collating sequence with native encoding, or NULL */
const char *zName /* Collating sequence name */
){
CollSeq *p;
+ sqlite3 *db = pParse->db;
p = pColl;
if( !p ){
@@ -102,6 +103,9 @@ CollSeq *sqlite3GetCollSeq(
p = 0;
}
assert( !p || p->xCmp );
+ if( p==0 ){
+ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
+ }
return p;
}
@@ -120,10 +124,8 @@ int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){
if( pColl ){
const char *zName = pColl->zName;
sqlite3 *db = pParse->db;
- CollSeq *p = sqlite3GetCollSeq(db, ENC(db), pColl, zName);
+ CollSeq *p = sqlite3GetCollSeq(pParse, ENC(db), pColl, zName);
if( !p ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
- pParse->nErr++;
return SQLITE_ERROR;
}
assert( p==pColl );
diff --git a/src/delete.c b/src/delete.c
index a69a62902..1411b7c61 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -32,7 +32,7 @@ Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
struct SrcList_item *pItem = pSrc->a;
Table *pTab;
assert( pItem && pSrc->nSrc==1 );
- pTab = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, 0, pItem);
sqlite3DeleteTable(pParse->db, pItem->pTab);
pItem->pTab = pTab;
if( pTab ){
diff --git a/src/expr.c b/src/expr.c
index 2d1fb38ed..04216b171 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -930,6 +930,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
struct SrcList_item *pNewItem = &pNew->a[i];
struct SrcList_item *pOldItem = &p->a[i];
Table *pTab;
+ pNewItem->pSchema = pOldItem->pSchema;
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
diff --git a/src/insert.c b/src/insert.c
index 097b6fcad..3950dbd3b 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1274,20 +1274,25 @@ void sqlite3GenerateConstraintChecks(
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
for(i=0; i<pCheck->nExpr; i++){
int allOk = sqlite3VdbeMakeLabel(v);
- sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL);
- if( onError==OE_Ignore ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
- }else{
- char *zConsName = pCheck->a[i].zName;
- if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
- if( zConsName ){
- zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName);
+ Expr *pDup = sqlite3ExprDup(db, pCheck->a[i].pExpr, 0);
+ if( !db->mallocFailed ){
+ assert( pDup!=0 );
+ sqlite3ExprIfTrue(pParse, pDup, allOk, SQLITE_JUMPIFNULL);
+ if( onError==OE_Ignore ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
}else{
- zConsName = 0;
+ char *zConsName = pCheck->a[i].zName;
+ if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
+ if( zConsName ){
+ zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName);
+ }else{
+ zConsName = 0;
+ }
+ sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC);
}
- sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC);
+ sqlite3VdbeResolveLabel(v, allOk);
}
- sqlite3VdbeResolveLabel(v, allOk);
+ sqlite3ExprDelete(db, pDup);
}
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
@@ -1753,7 +1758,7 @@ static int xferOptimization(
** we have to check the semantics.
*/
pItem = pSelect->pSrc->a;
- pSrc = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
+ pSrc = sqlite3LocateTableItem(pParse, 0, pItem);
if( pSrc==0 ){
return 0; /* FROM clause does not contain a real table */
}
diff --git a/src/main.c b/src/main.c
index 5778f8780..0d1e092c5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3040,7 +3040,7 @@ int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
+ db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff);
break;
}
diff --git a/src/os_unix.c b/src/os_unix.c
index c0df66e8e..0852eb1a8 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -3010,6 +3010,8 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
i64 newOffset;
#endif
TIMER_START;
+ assert( cnt==(cnt&0x1ffff) );
+ cnt &= 0x1ffff;
do{
#if defined(USE_PREAD)
got = osPread(id->h, pBuf, cnt, offset);
@@ -3099,6 +3101,8 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
+ assert( cnt==(cnt&0x1ffff) );
+ cnt &= 0x1ffff;
TIMER_START;
#if defined(USE_PREAD)
do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
diff --git a/src/os_win.c b/src/os_win.c
index c4d87b92c..2d4c74af9 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -34,6 +34,57 @@
#endif
/*
+** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
+** based on the sub-platform)?
+*/
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+# define SQLITE_WIN32_HAS_ANSI
+#endif
+
+/*
+** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
+** based on the sub-platform)?
+*/
+#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT
+# define SQLITE_WIN32_HAS_WIDE
+#endif
+
+/*
+** Do we need to manually define the Win32 file mapping APIs for use with WAL
+** mode (e.g. these APIs are available in the Windows CE SDK; however, they
+** are not present in the header file)?
+*/
+#if SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL)
+/*
+** Two of the file mapping APIs are different under WinRT. Figure out which
+** set we need.
+*/
+#if SQLITE_OS_WINRT
+WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \
+ LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR);
+
+WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T);
+#else
+#if defined(SQLITE_WIN32_HAS_ANSI)
+WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \
+ DWORD, DWORD, DWORD, LPCSTR);
+#endif /* defined(SQLITE_WIN32_HAS_ANSI) */
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \
+ DWORD, DWORD, DWORD, LPCWSTR);
+#endif /* defined(SQLITE_WIN32_HAS_WIDE) */
+
+WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
+#endif /* SQLITE_OS_WINRT */
+
+/*
+** This file mapping API is common to both Win32 and WinRT.
+*/
+WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
+#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */
+
+/*
** Macro to find the minimum of two numeric values.
*/
#ifndef MIN
@@ -238,14 +289,6 @@ int sqlite3_os_type = 0;
static int sqlite3_os_type = 0;
#endif
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
-# define SQLITE_WIN32_HAS_ANSI
-#endif
-
-#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT
-# define SQLITE_WIN32_HAS_WIDE
-#endif
-
#ifndef SYSCALL
# define SYSCALL sqlite3_syscall_ptr
#endif
@@ -402,7 +445,11 @@ static struct win_syscall {
#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \
DWORD,va_list*))aSyscall[15].pCurrent)
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "FreeLibrary", (SYSCALL)FreeLibrary, 0 },
+#else
+ { "FreeLibrary", (SYSCALL)0, 0 },
+#endif
#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent)
@@ -483,6 +530,7 @@ static struct win_syscall {
#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
#if SQLITE_OS_WINCE
/* The GetProcAddressA() routine is only available on Windows CE. */
{ "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 },
@@ -491,6 +539,9 @@ static struct win_syscall {
** an ANSI string regardless of the _UNICODE setting */
{ "GetProcAddressA", (SYSCALL)GetProcAddress, 0 },
#endif
+#else
+ { "GetProcAddressA", (SYSCALL)0, 0 },
+#endif
#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \
LPCSTR))aSyscall[27].pCurrent)
@@ -594,7 +645,7 @@ static struct win_syscall {
#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
LPCVOID))aSyscall[41].pCurrent)
-#if defined(SQLITE_WIN32_HAS_ANSI)
+#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
#else
{ "LoadLibraryA", (SYSCALL)0, 0 },
@@ -602,7 +653,8 @@ static struct win_syscall {
#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent)
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 },
#else
{ "LoadLibraryW", (SYSCALL)0, 0 },
@@ -791,7 +843,7 @@ static struct win_syscall {
#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[66].pCurrent)
-#if SQLITE_OS_WINRT
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
#else
{ "LoadPackagedLibrary", (SYSCALL)0, 0 },
diff --git a/src/pager.c b/src/pager.c
index a094e0da2..a767d747f 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -2510,6 +2510,21 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
}
/*
+** Return a sanitized version of the sector-size of OS file pFile. The
+** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE.
+*/
+int sqlite3SectorSize(sqlite3_file *pFile){
+ int iRet = sqlite3OsSectorSize(pFile);
+ if( iRet<32 ){
+ iRet = 512;
+ }else if( iRet>MAX_SECTOR_SIZE ){
+ assert( MAX_SECTOR_SIZE>=512 );
+ iRet = MAX_SECTOR_SIZE;
+ }
+ return iRet;
+}
+
+/*
** Set the value of the Pager.sectorSize variable for the given
** pager based on the value returned by the xSectorSize method
** of the open database file. The sector size will be used used
@@ -2544,14 +2559,7 @@ static void setSectorSize(Pager *pPager){
** call will segfault. */
pPager->sectorSize = 512;
}else{
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
- if( pPager->sectorSize<32 ){
- pPager->sectorSize = 512;
- }
- if( pPager->sectorSize>MAX_SECTOR_SIZE ){
- assert( MAX_SECTOR_SIZE>=512 );
- pPager->sectorSize = MAX_SECTOR_SIZE;
- }
+ pPager->sectorSize = sqlite3SectorSize(pPager->fd);
}
}
@@ -3468,9 +3476,16 @@ void sqlite3PagerSetBusyhandler(
Pager *pPager, /* Pager object */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
-){
+){
pPager->xBusyHandler = xBusyHandler;
pPager->pBusyHandlerArg = pBusyHandlerArg;
+
+ if( isOpen(pPager->fd) ){
+ void **ap = (void **)&pPager->xBusyHandler;
+ assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
+ assert( ap[1]==pBusyHandlerArg );
+ sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
+ }
}
/*
diff --git a/src/pager.h b/src/pager.h
index 2b60e058d..5fb0f0133 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -160,6 +160,7 @@ void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerIsMemdb(Pager*);
void sqlite3PagerCacheStat(Pager *, int, int, int *);
void sqlite3PagerClearCache(Pager *);
+int sqlite3SectorSize(sqlite3_file *);
/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);
diff --git a/src/pragma.c b/src/pragma.c
index f41f2db06..8eaec51ec 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -357,6 +357,7 @@ void sqlite3Pragma(
aFcntl[1] = zLeft;
aFcntl[2] = zRight;
aFcntl[3] = 0;
+ db->busyHandler.nBusy = 0;
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
if( rc==SQLITE_OK ){
if( aFcntl[0] ){
diff --git a/src/select.c b/src/select.c
index 2da14d93e..51847ffbd 100644
--- a/src/select.c
+++ b/src/select.c
@@ -3332,8 +3332,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}else{
/* An ordinary table or view name in the FROM clause */
assert( pFrom->pTab==0 );
- pFrom->pTab = pTab =
- sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase);
+ pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom);
if( pTab==0 ) return WRC_Abort;
pTab->nRef++;
#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index b2edffa5a..1d5406f0f 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -852,6 +852,17 @@ struct sqlite3_io_methods {
** file control occurs at the beginning of pragma statement analysis and so
** it is able to override built-in [PRAGMA] statements.
** </ul>
+**
+** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
+** ^This file-control may be invoked by SQLite on the database file handle
+** shortly after it is opened in order to provide a custom VFS with access
+** to the connections busy-handler callback. The argument is of type (void **)
+** - an array of two (void *) values. The first (void *) actually points
+** to a function of type (int (*)(void *)). In order to invoke the connections
+** busy-handler, this function should be invoked with the second (void *) in
+** the array as the only argument. If it returns non-zero, then the operation
+** should be retried. If it returns zero, the custom VFS should abandon the
+** current operation.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
@@ -867,6 +878,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_VFSNAME 12
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
#define SQLITE_FCNTL_PRAGMA 14
+#define SQLITE_FCNTL_BUSYHANDLER 15
/*
** CAPI3REF: Mutex Handle
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index b8451aba2..b440f8497 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1877,6 +1877,7 @@ struct SrcList {
i16 nSrc; /* Number of tables or subqueries in the FROM clause */
i16 nAlloc; /* Number of entries allocated in a[] below */
struct SrcList_item {
+ Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
char *zName; /* Name of the table */
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
@@ -2449,6 +2450,7 @@ struct TriggerStep {
typedef struct DbFixer DbFixer;
struct DbFixer {
Parse *pParse; /* The parsing context. Error messages written here */
+ Schema *pSchema; /* Fix items to this schema */
const char *zDb; /* Make sure all objects are contained in this database */
const char *zType; /* Type of the container - used for error messages */
const Token *pName; /* Name of the container - used for error messages */
@@ -2859,6 +2861,7 @@ void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
Table *sqlite3FindTable(sqlite3*,const char*, const char*);
Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*);
+Table *sqlite3LocateTableItem(Parse*,int isView,struct SrcList_item *);
Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
@@ -3089,7 +3092,7 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
void sqlite3AlterFinishAddColumn(Parse *, Token *);
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
-CollSeq *sqlite3GetCollSeq(sqlite3*, u8, CollSeq *, const char*);
+CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
char sqlite3AffinityType(const char*);
void sqlite3Analyze(Parse*, Token*, Token*);
int sqlite3InvokeBusyHandler(BusyHandler*);
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index cad7ef7f1..f1a05c9a6 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -53,15 +53,8 @@
#define NUM_PREPARED_STMTS 10
#define MAX_PREPARED_STMTS 100
-/*
-** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we
-** have to do a translation when going between the two. Set the
-** UTF_TRANSLATION_NEEDED macro to indicate that we need to do
-** this translation.
-*/
-#if defined(TCL_UTF_MAX) && !defined(SQLITE_UTF8)
-# define UTF_TRANSLATION_NEEDED 1
-#endif
+/* Forward declaration */
+typedef struct SqliteDb SqliteDb;
/*
** New SQL functions can be created as TCL scripts. Each such function
@@ -71,6 +64,7 @@ typedef struct SqlFunc SqlFunc;
struct SqlFunc {
Tcl_Interp *interp; /* The TCL interpret to execute the function */
Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */
+ SqliteDb *pDb; /* Database connection that owns this function */
int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */
char *zName; /* Name of this function */
SqlFunc *pNext; /* Next function on the list of them all */
@@ -113,7 +107,6 @@ typedef struct IncrblobChannel IncrblobChannel;
** sqlite3_prepare_v2() or sqlite3_prepare() to prepare SQL statements.
** If SqliteDb.bLegacyPrepare is true, sqlite3_prepare() is used.
*/
-typedef struct SqliteDb SqliteDb;
struct SqliteDb {
sqlite3 *db; /* The "real" database structure. MUST BE FIRST */
Tcl_Interp *interp; /* The interpreter used for this database */
@@ -432,6 +425,7 @@ static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
}
}
pNew->interp = pDb->interp;
+ pNew->pDb = pDb;
pNew->pScript = 0;
pNew->pNext = pDb->pFunc;
pDb->pFunc = pNew;
@@ -479,6 +473,7 @@ static void DbDeleteCmd(void *db){
while( pDb->pFunc ){
SqlFunc *pFunc = pDb->pFunc;
pDb->pFunc = pFunc->pNext;
+ assert( pFunc->pDb==pDb );
Tcl_DecrRefCount(pFunc->pScript);
Tcl_Free((char*)pFunc);
}
@@ -838,7 +833,7 @@ static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
break;
}
case SQLITE_NULL: {
- pVal = Tcl_NewStringObj("", 0);
+ pVal = Tcl_NewStringObj(p->pDb->zNull, -1);
break;
}
default: {
@@ -978,26 +973,6 @@ static int auth_callback(
#endif /* SQLITE_OMIT_AUTHORIZATION */
/*
-** zText is a pointer to text obtained via an sqlite3_result_text()
-** or similar interface. This routine returns a Tcl string object,
-** reference count set to 0, containing the text. If a translation
-** between iso8859 and UTF-8 is required, it is preformed.
-*/
-static Tcl_Obj *dbTextToObj(char const *zText){
- Tcl_Obj *pVal;
-#ifdef UTF_TRANSLATION_NEEDED
- Tcl_DString dCol;
- Tcl_DStringInit(&dCol);
- Tcl_ExternalToUtfDString(NULL, zText, -1, &dCol);
- pVal = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1);
- Tcl_DStringFree(&dCol);
-#else
- pVal = Tcl_NewStringObj(zText, -1);
-#endif
- return pVal;
-}
-
-/*
** This routine reads a line of text from FILE in, stores
** the text in memory obtained from malloc() and returns a pointer
** to the text. NULL is returned at end of file, or if malloc()
@@ -1184,13 +1159,13 @@ static int dbPrepareAndBind(
int nByte;
if( SQLITE_OK!=dbPrepare(pDb, zSql, &pStmt, pzOut) ){
- Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1));
return TCL_ERROR;
}
if( pStmt==0 ){
if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){
/* A compile-time error in the statement. */
- Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1));
return TCL_ERROR;
}else{
/* The statement was a no-op. Continue to the next statement
@@ -1409,7 +1384,7 @@ static void dbEvalRowInfo(
if( nCol>0 && (papColName || p->pArray) ){
apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
for(i=0; i<nCol; i++){
- apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i));
+ apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1);
Tcl_IncrRefCount(apColName[i]);
}
p->apColName = apColName;
@@ -1496,7 +1471,8 @@ static int dbEvalStep(DbEvalContext *p){
continue;
}
#endif
- Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
+ Tcl_SetObjResult(pDb->interp,
+ Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1));
return TCL_ERROR;
}else{
dbReleaseStmt(pDb, pPreStmt, 0);
@@ -1553,11 +1529,11 @@ static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){
return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol));
}
case SQLITE_NULL: {
- return dbTextToObj(p->pDb->zNull);
+ return Tcl_NewStringObj(p->pDb->zNull, -1);
}
}
- return dbTextToObj((char *)sqlite3_column_text(pStmt, iCol));
+ return Tcl_NewStringObj((char*)sqlite3_column_text(pStmt, iCol), -1);
}
/*
@@ -2452,7 +2428,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
const char *zDb = "main";
const char *zTable;
const char *zColumn;
- sqlite_int64 iRow;
+ Tcl_WideInt iRow;
/* Check for the -readonly option */
if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){
@@ -2518,7 +2494,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
pDb->zNull = 0;
}
}
- Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(pDb->zNull, -1));
break;
}
diff --git a/src/test1.c b/src/test1.c
index 3d2fb0203..e7a224221 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -3067,7 +3067,7 @@ static int test_bind_int64(
){
sqlite3_stmt *pStmt;
int idx;
- i64 value;
+ Tcl_WideInt value;
int rc;
if( objc!=4 ){
@@ -4703,7 +4703,7 @@ static int test_soft_heap_limit(
Tcl_Obj *CONST objv[]
){
sqlite3_int64 amt;
- sqlite3_int64 N = -1;
+ Tcl_WideInt N = -1;
if( objc!=1 && objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "?N?");
return TCL_ERROR;
@@ -5096,7 +5096,7 @@ static int file_control_sizehint_test(
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
- sqlite3_int64 nSize; /* Hinted size */
+ Tcl_WideInt nSize; /* Hinted size */
char *zDb; /* Db name ("main", "temp" etc.) */
sqlite3 *db; /* Database handle */
int rc; /* file_control() return code */
diff --git a/src/test_intarray.c b/src/test_intarray.c
index 8651d01cf..6ea775617 100644
--- a/src/test_intarray.c
+++ b/src/test_intarray.c
@@ -346,8 +346,9 @@ static int test_intarray_bind(
return TCL_ERROR;
}
for(i=0; i<n; i++){
- a[i] = 0;
- Tcl_GetWideIntFromObj(0, objv[i+2], &a[i]);
+ Tcl_WideInt x = 0;
+ Tcl_GetWideIntFromObj(0, objv[i+2], &x);
+ a[i] = x;
}
rc = sqlite3_intarray_bind(pArray, n, a, sqlite3_free);
if( rc!=SQLITE_OK ){
diff --git a/src/test_quota.c b/src/test_quota.c
index 2ce46acdc..e1ec12d37 100644
--- a/src/test_quota.c
+++ b/src/test_quota.c
@@ -1073,7 +1073,7 @@ size_t sqlite3_quota_fwrite(
/* If the write was incomplete, adjust the file size and group size
** downward */
if( rc<nmemb && pFile ){
- size_t nWritten = rc>=0 ? rc : 0;
+ size_t nWritten = rc;
sqlite3_int64 iNewEnd = iOfst + size*nWritten;
if( iNewEnd<iEnd ) iNewEnd = iEnd;
quotaEnter();
@@ -1354,8 +1354,10 @@ static void tclQuotaCallback(
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
if( rc==TCL_OK ){
+ Tcl_WideInt x;
Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0);
- rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit);
+ rc = Tcl_GetWideIntFromObj(p->interp, pLimit, &x);
+ *piLimit = x;
Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0);
}
@@ -1437,7 +1439,7 @@ static int test_quota_set(
Tcl_Obj *CONST objv[]
){
const char *zPattern; /* File pattern to configure */
- sqlite3_int64 iLimit; /* Initial quota in bytes */
+ Tcl_WideInt iLimit; /* Initial quota in bytes */
Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */
int rc; /* Value returned by quota_set() */
TclQuotaCallback *p; /* Callback object */
@@ -1613,7 +1615,6 @@ static int test_quota_fread(
return TCL_ERROR;
}
got = sqlite3_quota_fread(zBuf, sz, nElem, p);
- if( got<0 ) got = 0;
zBuf[got*sz] = 0;
Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
sqlite3_free(zBuf);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 731375ecc..c10e26a7f 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1771,7 +1771,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( sqlite3BtreeIsInTrans(pBt) ){
needXcommit = 1;
if( i!=1 ) nTrans++;
+ sqlite3BtreeEnter(pBt);
rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
+ sqlite3BtreeLeave(pBt);
}
}
if( rc!=SQLITE_OK ){
diff --git a/src/vtab.c b/src/vtab.c
index 29f097da4..39fbacacb 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -264,7 +264,7 @@ void sqlite3VtabClear(sqlite3 *db, Table *p){
if( p->azModuleArg ){
int i;
for(i=0; i<p->nModuleArg; i++){
- sqlite3DbFree(db, p->azModuleArg[i]);
+ if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]);
}
sqlite3DbFree(db, p->azModuleArg);
}
@@ -324,7 +324,7 @@ void sqlite3VtabBeginParse(
pTable->tabFlags |= TF_Virtual;
pTable->nModuleArg = 0;
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
- addModuleArgument(db, pTable, sqlite3DbStrDup(db, db->aDb[iDb].zName));
+ addModuleArgument(db, pTable, 0);
addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName));
pParse->sNameToken.n = (int)(&pModuleName->z[pModuleName->n] - pName1->z);
@@ -481,6 +481,7 @@ static int vtabCallConstructor(
int nArg = pTab->nModuleArg;
char *zErr = 0;
char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
+ int iDb;
if( !zModuleName ){
return SQLITE_NOMEM;
@@ -494,6 +495,10 @@ static int vtabCallConstructor(
pVTable->db = db;
pVTable->pMod = pMod;
+ assert( pTab->azModuleArg[1]==0 );
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ pTab->azModuleArg[1] = db->aDb[iDb].zName;
+
/* Invoke the virtual table constructor */
assert( &db->pVtabCtx );
assert( xConstruct );
@@ -504,6 +509,7 @@ static int vtabCallConstructor(
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
db->pVtabCtx = pPriorCtx;
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ pTab->azModuleArg[1] = 0;
if( SQLITE_OK!=rc ){
if( zErr==0 ){
diff --git a/src/wal.c b/src/wal.c
index cc166ba43..8394bfa29 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -2828,7 +2828,7 @@ int sqlite3WalFrames(
*/
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
if( pWal->padToSectorBoundary ){
- int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
+ int sectorSize = sqlite3SectorSize(pWal->pWalFd);
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
while( iOffset<w.iSyncPoint ){
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
diff --git a/src/where.c b/src/where.c
index 7f386289c..7a994943c 100644
--- a/src/where.c
+++ b/src/where.c
@@ -257,10 +257,11 @@ struct WhereCost {
#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
-#define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */
-#define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */
-#define WHERE_REVERSE 0x02000000 /* Scan in reverse order */
-#define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */
+#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
+#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
+#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
+#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
+#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
@@ -290,6 +291,17 @@ struct WhereBestIdx {
};
/*
+** Return TRUE if the probe cost is less than the baseline cost
+*/
+static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
+ if( pProbe->rCost<pBaseline->rCost ) return 1;
+ if( pProbe->rCost>pBaseline->rCost ) return 0;
+ if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1;
+ if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1;
+ return 0;
+}
+
+/*
** Initialize a preallocated WhereClause structure.
*/
static void whereClauseInit(
@@ -1431,21 +1443,6 @@ static void exprAnalyze(
}
/*
-** Return TRUE if the given index is UNIQUE and all columns past the
-** first nSkip columns are NOT NULL.
-*/
-static int indexIsUniqueNotNull(Index *pIdx, int nSkip){
- Table *pTab = pIdx->pTable;
- int i;
- if( pIdx->onError==OE_None ) return 0;
- for(i=nSkip; i<pIdx->nColumn; i++){
- int j = pIdx->aiColumn[i];
- if( j>=0 && pTab->aCol[j].notNull==0 ) return 0;
- }
- return 1;
-}
-
-/*
** This function searches the expression list passed as the second argument
** for an expression of type TK_COLUMN that refers to the same column and
** uses the same collation sequence as the iCol'th column of index pIdx.
@@ -1504,7 +1501,8 @@ static int isDistinctIndex(
Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
int i; /* Iterator variable */
- if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0;
+ assert( pDistinct!=0 );
+ if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0;
testcase( pDistinct->nExpr==BMS-1 );
/* Loop through all the expressions in the distinct list. If any of them
@@ -1607,166 +1605,6 @@ static int isDistinctRedundant(
}
/*
-** This routine decides if pIdx can be used to satisfy the ORDER BY
-** clause, either in whole or in part. The return value is the
-** cumulative number of terms in the ORDER BY clause that are satisfied
-** by the index pIdx and other indices in outer loops.
-**
-** The table being queried has a cursor number of "base". pIdx is the
-** index that is postulated for use to access the table.
-**
-** nEqCol is the number of columns of pIdx that are used as equality
-** constraints and where the other side of the == is an ordered column
-** or constant. An "order column" in the previous sentence means a column
-** in table from an outer loop whose values will always appear in the
-** correct order due to othre index, or because the outer loop generates
-** a unique result. Any of the first nEqCol columns of pIdx may be missing
-** from the ORDER BY clause and the match can still be a success.
-**
-** The *pbRev value is set to 0 order 1 depending on whether or not
-** pIdx should be run in the forward order or in reverse order.
-*/
-static int isSortingIndex(
- WhereBestIdx *p, /* Best index search context */
- Index *pIdx, /* The index we are testing */
- int base, /* Cursor number for the table to be sorted */
- int nEqCol, /* Number of index columns with ordered == constraints */
- int wsFlags, /* Index usages flags */
- int bOuterRev, /* True if outer loops scan in reverse order */
- int *pbRev /* Set to 1 for reverse-order scan of pIdx */
-){
- int i; /* Number of pIdx terms used */
- int j; /* Number of ORDER BY terms satisfied */
- int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
- int nTerm; /* Number of ORDER BY terms */
- struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
- ExprList *pOrderBy; /* The ORDER BY clause */
- Parse *pParse = p->pParse; /* Parser context */
- sqlite3 *db = pParse->db; /* Database connection */
- int nPriorSat; /* ORDER BY terms satisfied by outer loops */
- int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
- int nEqOneRow; /* Idx columns that ref unique values */
-
- if( p->i==0 ){
- nPriorSat = 0;
- nEqOneRow = nEqCol;
- }else{
- if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
- nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
- sortOrder = bOuterRev;
- nEqOneRow = 0;
- }
- if( p->i>0 && nEqCol==0 /*&& !allOuterLoopsUnique(p)*/ ) return nPriorSat;
- pOrderBy = p->pOrderBy;
- if( !pOrderBy ) return nPriorSat;
- if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
- if( pIdx->bUnordered ) return nPriorSat;
- nTerm = pOrderBy->nExpr;
- assert( nTerm>0 );
-
- /* Argument pIdx must either point to a 'real' named index structure,
- ** or an index structure allocated on the stack by bestBtreeIndex() to
- ** represent the rowid index that is part of every table. */
- assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
-
- /* Match terms of the ORDER BY clause against columns of
- ** the index.
- **
- ** Note that indices have pIdx->nColumn regular columns plus
- ** one additional column containing the rowid. The rowid column
- ** of the index is also allowed to match against the ORDER BY
- ** clause.
- */
- for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
- Expr *pExpr; /* The expression of the ORDER BY pTerm */
- CollSeq *pColl; /* The collating sequence of pExpr */
- int termSortOrder; /* Sort order for this term */
- int iColumn; /* The i-th column of the index. -1 for rowid */
- int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
- const char *zColl; /* Name of the collating sequence for i-th index term */
-
- pExpr = pTerm->pExpr;
- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
- /* Can not use an index sort on anything that is not a column in the
- ** left-most table of the FROM clause */
- break;
- }
- pColl = sqlite3ExprCollSeq(pParse, pExpr);
- if( !pColl ){
- pColl = db->pDfltColl;
- }
- if( pIdx->zName && i<pIdx->nColumn ){
- iColumn = pIdx->aiColumn[i];
- if( iColumn==pIdx->pTable->iPKey ){
- iColumn = -1;
- }
- iSortOrder = pIdx->aSortOrder[i];
- zColl = pIdx->azColl[i];
- }else{
- iColumn = -1;
- iSortOrder = 0;
- zColl = pColl->zName;
- }
- if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
- /* Term j of the ORDER BY clause does not match column i of the index */
- if( i<nEqCol ){
- /* If an index column that is constrained by == fails to match an
- ** ORDER BY term, that is OK. Just ignore that column of the index
- */
- continue;
- }else if( i==pIdx->nColumn ){
- /* Index column i is the rowid. All other terms match. */
- break;
- }else{
- /* If an index column fails to match and is not constrained by ==
- ** then the index cannot satisfy the ORDER BY constraint.
- */
- return nPriorSat;
- }
- }
- assert( pIdx->aSortOrder!=0 || iColumn==-1 );
- assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
- assert( iSortOrder==0 || iSortOrder==1 );
- termSortOrder = iSortOrder ^ pTerm->sortOrder;
- if( i>nEqOneRow ){
- if( termSortOrder!=sortOrder ){
- /* Indices can only be used if all ORDER BY terms past the
- ** equality constraints are all either DESC or ASC. */
- break;
- }
- }else{
- sortOrder = termSortOrder;
- }
- j++;
- pTerm++;
- if( iColumn<0 ){
- seenRowid = 1;
- break;
- }
- }
- *pbRev = sortOrder;
-
- /* If there was an "ORDER BY rowid" term that matched, or it is only
- ** possible for a single row from this table to match, then skip over
- ** any additional ORDER BY terms dealing with this table.
- */
- if( seenRowid ||
- ( (wsFlags & WHERE_COLUMN_NULL)==0
- && i>=pIdx->nColumn
- && indexIsUniqueNotNull(pIdx, nEqCol)
- )
- ){
- /* Advance j over additional ORDER BY terms associated with base */
- WhereMaskSet *pMS = p->pWC->pMaskSet;
- Bitmask m = ~getMask(pMS, base);
- while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
- j++;
- }
- }
- return j;
-}
-
-/*
** Prepare a crude estimate of the logarithm of the input value.
** The results need not be exact. This is only used for estimating
** the total cost of performing operations with O(logN) or O(NlogN)
@@ -1919,6 +1757,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){
p->cost.rCost = rTotal;
p->cost.used = used;
p->cost.plan.nRow = nRow;
+ p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
p->cost.plan.wsFlags = flags;
p->cost.plan.u.pTerm = pTerm;
}
@@ -2461,7 +2300,10 @@ static void bestVirtualIndex(WhereBestIdx *p){
}
p->cost.plan.u.pVtabIdx = pIdxInfo;
if( pIdxInfo->orderByConsumed ){
- p->cost.plan.wsFlags |= WHERE_ORDERBY;
+ p->cost.plan.wsFlags |= WHERE_ORDERED;
+ p->cost.plan.nOBSat = nOrderBy;
+ }else{
+ p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
}
p->cost.plan.nEq = 0;
pIdxInfo->nOrderBy = nOrderBy;
@@ -2557,10 +2399,8 @@ static int whereKeyStats(
pColl = db->pDfltColl;
assert( pColl->enc==SQLITE_UTF8 );
}else{
- pColl = sqlite3GetCollSeq(db, SQLITE_UTF8, 0, *pIdx->azColl);
+ pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl);
if( pColl==0 ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s",
- *pIdx->azColl);
return SQLITE_ERROR;
}
z = (const u8 *)sqlite3ValueText(pVal, pColl->enc);
@@ -2870,30 +2710,40 @@ static int whereInScanEst(
/*
** Check to see if column iCol of the table with cursor iTab will appear
-** in sorted order according to the current query plan. Return true if
-** it will and false if not.
+** in sorted order according to the current query plan.
**
-** If *pbRev is initially 2 (meaning "unknown") then set *pbRev to the
-** sort order of iTab.iCol. If *pbRev is 0 or 1 but does not match
-** the sort order of iTab.iCol, then consider the column to be unordered.
+** Return values:
+**
+** 0 iCol is not ordered
+** 1 iCol has only a single value
+** 2 iCol is in ASC order
+** 3 iCol is in DESC order
*/
-static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){
+static int isOrderedColumn(
+ WhereBestIdx *p,
+ int iTab,
+ int iCol
+){
int i, j;
WhereLevel *pLevel = &p->aLevel[p->i-1];
Index *pIdx;
u8 sortOrder;
for(i=p->i-1; i>=0; i--, pLevel--){
if( pLevel->iTabCur!=iTab ) continue;
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
- pIdx = pLevel->plan.u.pIdx;
+ if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
+ return 1;
+ }
+ assert( (pLevel->plan.wsFlags & WHERE_ORDERED)!=0 );
+ if( (pIdx = pLevel->plan.u.pIdx)!=0 ){
if( iCol<0 ){
sortOrder = 0;
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
}else{
- for(j=0; j<pIdx->nColumn; j++){
+ int n = pIdx->nColumn;
+ for(j=0; j<n; j++){
if( iCol==pIdx->aiColumn[j] ) break;
}
- if( j>=pIdx->nColumn ) return 0;
+ if( j>=n ) return 0;
sortOrder = pIdx->aSortOrder[j];
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
}
@@ -2907,41 +2757,222 @@ static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){
testcase( sortOrder==1 );
sortOrder = 1 - sortOrder;
}
- if( *pbRev==2 ){
- *pbRev = sortOrder;
- return 1;
- }
- return (*pbRev==sortOrder);
+ return sortOrder+2;
}
return 0;
}
/*
-** pTerm is an == constraint. Check to see if the other side of
-** the == is a constant or a value that is guaranteed to be ordered
-** by outer loops. Return 1 if pTerm is ordered, and 0 if not.
+** This routine decides if pIdx can be used to satisfy the ORDER BY
+** clause, either in whole or in part. The return value is the
+** cumulative number of terms in the ORDER BY clause that are satisfied
+** by the index pIdx and other indices in outer loops.
+**
+** The table being queried has a cursor number of "base". pIdx is the
+** index that is postulated for use to access the table.
+**
+** The *pbRev value is set to 0 order 1 depending on whether or not
+** pIdx should be run in the forward order or in reverse order.
*/
-static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
- Expr *pExpr = pTerm->pExpr;
- assert( pExpr->op==TK_EQ );
- assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
- assert( pExpr->pRight!=0 );
+static int isSortingIndex(
+ WhereBestIdx *p, /* Best index search context */
+ Index *pIdx, /* The index we are testing */
+ int base, /* Cursor number for the table to be sorted */
+ int *pbRev /* Set to 1 for reverse-order scan of pIdx */
+){
+ int i; /* Number of pIdx terms used */
+ int j; /* Number of ORDER BY terms satisfied */
+ int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */
+ int nTerm; /* Number of ORDER BY terms */
+ struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */
+ Table *pTab = pIdx->pTable; /* Table that owns index pIdx */
+ ExprList *pOrderBy; /* The ORDER BY clause */
+ Parse *pParse = p->pParse; /* Parser context */
+ sqlite3 *db = pParse->db; /* Database connection */
+ int nPriorSat; /* ORDER BY terms satisfied by outer loops */
+ int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
+ int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */
+
if( p->i==0 ){
- return 1; /* All == are ordered in the outer loop */
+ nPriorSat = 0;
+ }else{
+ nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
+ if( (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
+ /* This loop cannot be ordered unless the next outer loop is
+ ** also ordered */
+ return nPriorSat;
+ }
+ if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){
+ /* Only look at the outer-most loop if the OrderByIdxJoin
+ ** optimization is disabled */
+ return nPriorSat;
+ }
}
- if( pTerm->prereqRight==0 ){
- return 1; /* RHS of the == is a constant */
+ pOrderBy = p->pOrderBy;
+ assert( pOrderBy!=0 );
+ if( pIdx->bUnordered ){
+ /* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot
+ ** be used for sorting */
+ return nPriorSat;
}
- if( pExpr->pRight->op==TK_COLUMN
- && isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev)
- ){
- return 1;
+ nTerm = pOrderBy->nExpr;
+ uniqueNotNull = pIdx->onError!=OE_None;
+ assert( nTerm>0 );
+
+ /* Argument pIdx must either point to a 'real' named index structure,
+ ** or an index structure allocated on the stack by bestBtreeIndex() to
+ ** represent the rowid index that is part of every table. */
+ assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
+
+ /* Match terms of the ORDER BY clause against columns of
+ ** the index.
+ **
+ ** Note that indices have pIdx->nColumn regular columns plus
+ ** one additional column containing the rowid. The rowid column
+ ** of the index is also allowed to match against the ORDER BY
+ ** clause.
+ */
+ j = nPriorSat;
+ for(i=0,pOBItem=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
+ Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */
+ CollSeq *pColl; /* The collating sequence of pOBExpr */
+ int termSortOrder; /* Sort order for this term */
+ int iColumn; /* The i-th column of the index. -1 for rowid */
+ int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
+ int isEq; /* Subject to an == or IS NULL constraint */
+ int isMatch; /* ORDER BY term matches the index term */
+ const char *zColl; /* Name of collating sequence for i-th index term */
+ WhereTerm *pConstraint; /* A constraint in the WHERE clause */
+
+ /* If the next term of the ORDER BY clause refers to anything other than
+ ** a column in the "base" table, then this index will not be of any
+ ** further use in handling the ORDER BY. */
+ pOBExpr = pOBItem->pExpr;
+ if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){
+ break;
+ }
+
+ /* Find column number and collating sequence for the next entry
+ ** in the index */
+ if( pIdx->zName && i<pIdx->nColumn ){
+ iColumn = pIdx->aiColumn[i];
+ if( iColumn==pIdx->pTable->iPKey ){
+ iColumn = -1;
+ }
+ iSortOrder = pIdx->aSortOrder[i];
+ zColl = pIdx->azColl[i];
+ assert( zColl!=0 );
+ }else{
+ iColumn = -1;
+ iSortOrder = 0;
+ zColl = 0;
+ }
+
+ /* Check to see if the column number and collating sequence of the
+ ** index match the column number and collating sequence of the ORDER BY
+ ** clause entry. Set isMatch to 1 if they both match. */
+ if( pOBExpr->iColumn==iColumn ){
+ if( zColl ){
+ pColl = sqlite3ExprCollSeq(pParse, pOBExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ isMatch = sqlite3StrICmp(pColl->zName, zColl)==0;
+ }else{
+ isMatch = 1;
+ }
+ }else{
+ isMatch = 0;
+ }
+
+ /* termSortOrder is 0 or 1 for whether or not the access loop should
+ ** run forward or backwards (respectively) in order to satisfy this
+ ** term of the ORDER BY clause. */
+ assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 );
+ assert( iSortOrder==0 || iSortOrder==1 );
+ termSortOrder = iSortOrder ^ pOBItem->sortOrder;
+
+ /* If X is the column in the index and ORDER BY clause, check to see
+ ** if there are any X= or X IS NULL constraints in the WHERE clause. */
+ pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
+ WO_EQ|WO_ISNULL|WO_IN, pIdx);
+ if( pConstraint==0 ){
+ isEq = 0;
+ }else if( pConstraint->eOperator==WO_IN ){
+ /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY
+ ** because we do not know in what order the values on the RHS of the IN
+ ** operator will occur. */
+ break;
+ }else if( pConstraint->eOperator==WO_ISNULL ){
+ uniqueNotNull = 0;
+ isEq = 1; /* "X IS NULL" means X has only a single value */
+ }else if( pConstraint->prereqRight==0 ){
+ isEq = 1; /* Constraint "X=constant" means X has only a single value */
+ }else{
+ Expr *pRight = pConstraint->pExpr->pRight;
+ if( pRight->op==TK_COLUMN ){
+ WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)",
+ pRight->iTable, pRight->iColumn));
+ isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn);
+ WHERETRACE((" -> isEq=%d\n", isEq));
+
+ /* If the constraint is of the form X=Y where Y is an ordered value
+ ** in an outer loop, then make sure the sort order of Y matches the
+ ** sort order required for X. */
+ if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){
+ testcase( isEq==2 );
+ testcase( isEq==3 );
+ break;
+ }
+ }else{
+ isEq = 0; /* "X=expr" places no ordering constraints on X */
+ }
+ }
+ if( !isMatch ){
+ if( isEq==0 ){
+ break;
+ }else{
+ continue;
+ }
+ }else if( isEq!=1 ){
+ if( sortOrder==2 ){
+ sortOrder = termSortOrder;
+ }else if( termSortOrder!=sortOrder ){
+ break;
+ }
+ }
+ j++;
+ pOBItem++;
+ if( iColumn<0 ){
+ seenRowid = 1;
+ break;
+ }else if( pTab->aCol[iColumn].notNull==0 && isEq!=1 ){
+ testcase( isEq==0 );
+ testcase( isEq==2 );
+ testcase( isEq==3 );
+ uniqueNotNull = 0;
+ }
}
- /* If we cannot prove that the constraint is ordered, assume it is not */
- return 0;
-}
+ /* If we have not found at least one ORDER BY term that matches the
+ ** index, then show no progress. */
+ if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat;
+ /* Return the necessary scan order back to the caller */
+ *pbRev = sortOrder & 1;
+
+ /* If there was an "ORDER BY rowid" term that matched, or it is only
+ ** possible for a single row from this table to match, then skip over
+ ** any additional ORDER BY terms dealing with this table.
+ */
+ if( seenRowid || (uniqueNotNull && i>=pIdx->nColumn) ){
+ /* Advance j over additional ORDER BY terms associated with base */
+ WhereMaskSet *pMS = p->pWC->pMaskSet;
+ Bitmask m = ~getMask(pMS, base);
+ while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
+ j++;
+ }
+ }
+ return j;
+}
/*
** Find the best query plan for accessing a particular table. Write the
@@ -3036,18 +3067,14 @@ static void bestBtreeIndex(WhereBestIdx *p){
*/
for(; pProbe; pIdx=pProbe=pProbe->pNext){
const tRowcnt * const aiRowEst = pProbe->aiRowEst;
- double cost; /* Cost of using pProbe */
- double nRow; /* Estimated number of rows in result set */
+ WhereCost pc; /* Cost of using pProbe */
double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
- int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */
- int wsFlags = 0;
- Bitmask used = 0;
/* The following variables are populated based on the properties of
** index being evaluated. They are then used to determine the expected
** cost and number of rows returned.
**
- ** nEq:
+ ** pc.plan.nEq:
** Number of equality terms that can be implemented using the index.
** In other words, the number of initial fields in the index that
** are used in == or IN or NOT NULL constraints of the WHERE clause.
@@ -3072,10 +3099,6 @@ static void bestBtreeIndex(WhereBestIdx *p){
** the sub-select is assumed to return 25 rows for the purposes of
** determining nInMul.
**
- ** nOrdered:
- ** The number of equality terms that are constrainted by outer loop
- ** variables that are well-ordered.
- **
** bInEst:
** Set to true if there was at least one "x IN (SELECT ...)" term used
** in determining the value of nInMul. Note that the RHS of the
@@ -3094,7 +3117,7 @@ static void bestBtreeIndex(WhereBestIdx *p){
** external sort (i.e. scanning the index being evaluated will not
** correctly order records).
**
- ** bDistinct:
+ ** bDist:
** Boolean. True if there is a DISTINCT clause that will require an
** external btree.
**
@@ -3113,8 +3136,6 @@ static void bestBtreeIndex(WhereBestIdx *p){
** SELECT a, b FROM tbl WHERE a = 1;
** SELECT a, b, c FROM tbl WHERE a = 1;
*/
- int nEq; /* Number of == or IN terms matching index */
- int nOrdered; /* Number of ordered terms matching index */
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
int nInMul = 1; /* Number of distinct equalities to lookup */
double rangeDiv = (double)1; /* Estimated reduction in search space */
@@ -3122,27 +3143,39 @@ static void bestBtreeIndex(WhereBestIdx *p){
int bSort; /* True if external sort required */
int bDist; /* True if index cannot help with DISTINCT */
int bLookup = 0; /* True if not a covering index */
- int nOBSat = 0; /* Number of ORDER BY terms satisfied */
+ int nPriorSat; /* ORDER BY terms satisfied by outer loops */
int nOrderBy; /* Number of ORDER BY terms */
WhereTerm *pTerm; /* A single term of the WHERE clause */
#ifdef SQLITE_ENABLE_STAT3
WhereTerm *pFirstTerm = 0; /* First term matching the index */
#endif
+ WHERETRACE((
+ " %s(%s):\n",
+ pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk")
+ ));
+ memset(&pc, 0, sizeof(pc));
nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
- bSort = nOrderBy>0 && (p->i==0 || p->aLevel[p->i-1].plan.nOBSat<nOrderBy);
- bDist = p->i==0 && p->pDistinct!=0;
+ if( p->i ){
+ nPriorSat = pc.plan.nOBSat = p->aLevel[p->i-1].plan.nOBSat;
+ bSort = nPriorSat<nOrderBy;
+ bDist = 0;
+ }else{
+ nPriorSat = pc.plan.nOBSat = 0;
+ bSort = nOrderBy>0;
+ bDist = p->pDistinct!=0;
+ }
- /* Determine the values of nEq and nInMul */
- for(nEq=nOrdered=0; nEq<pProbe->nColumn; nEq++){
- int j = pProbe->aiColumn[nEq];
+ /* Determine the values of pc.plan.nEq and nInMul */
+ for(pc.plan.nEq=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
+ int j = pProbe->aiColumn[pc.plan.nEq];
pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
if( pTerm==0 ) break;
- wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
+ pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
testcase( pTerm->pWC!=pWC );
if( pTerm->eOperator & WO_IN ){
Expr *pExpr = pTerm->pExpr;
- wsFlags |= WHERE_COLUMN_IN;
+ pc.plan.wsFlags |= WHERE_COLUMN_IN;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
nInMul *= 25;
@@ -3152,15 +3185,12 @@ static void bestBtreeIndex(WhereBestIdx *p){
nInMul *= pExpr->x.pList->nExpr;
}
}else if( pTerm->eOperator & WO_ISNULL ){
- wsFlags |= WHERE_COLUMN_NULL;
- if( nEq==nOrdered ) nOrdered++;
- }else if( bSort && nEq==nOrdered && isOrderedTerm(p, pTerm, &bRev) ){
- nOrdered++;
+ pc.plan.wsFlags |= WHERE_COLUMN_NULL;
}
#ifdef SQLITE_ENABLE_STAT3
- if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
+ if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
#endif
- used |= pTerm->prereqRight;
+ pc.used |= pTerm->prereqRight;
}
/* If the index being considered is UNIQUE, and there is an equality
@@ -3169,72 +3199,79 @@ static void bestBtreeIndex(WhereBestIdx *p){
** indicate this to the caller.
**
** Otherwise, if the search may find more than one row, test to see if
- ** there is a range constraint on indexed column (nEq+1) that can be
+ ** there is a range constraint on indexed column (pc.plan.nEq+1) that can be
** optimized using the index.
*/
- if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
- testcase( wsFlags & WHERE_COLUMN_IN );
- testcase( wsFlags & WHERE_COLUMN_NULL );
- if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
- wsFlags |= WHERE_UNIQUE;
+ if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
+ testcase( pc.plan.wsFlags & WHERE_COLUMN_IN );
+ testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL );
+ if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
+ pc.plan.wsFlags |= WHERE_UNIQUE;
+ if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
+ pc.plan.wsFlags |= WHERE_ALL_UNIQUE;
+ }
}
}else if( pProbe->bUnordered==0 ){
- int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]);
+ int j;
+ j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]);
if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
WhereTerm *pTop, *pBtm;
pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
- whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv);
+ whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv);
if( pTop ){
nBound = 1;
- wsFlags |= WHERE_TOP_LIMIT;
- used |= pTop->prereqRight;
+ pc.plan.wsFlags |= WHERE_TOP_LIMIT;
+ pc.used |= pTop->prereqRight;
testcase( pTop->pWC!=pWC );
}
if( pBtm ){
nBound++;
- wsFlags |= WHERE_BTM_LIMIT;
- used |= pBtm->prereqRight;
+ pc.plan.wsFlags |= WHERE_BTM_LIMIT;
+ pc.used |= pBtm->prereqRight;
testcase( pBtm->pWC!=pWC );
}
- wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
+ pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
}
}
/* If there is an ORDER BY clause and the index being considered will
** naturally scan rows in the required order, set the appropriate flags
- ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index
- ** will scan rows in a different order, set the bSort variable. */
- assert( bRev>=0 && bRev<=2 );
- if( bSort ){
- testcase( bRev==0 );
- testcase( bRev==1 );
- testcase( bRev==2 );
- nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
- wsFlags, bRev&1, &bRev);
- if( nOrderBy==nOBSat ){
+ ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but
+ ** the index will scan rows in a different order, set the bSort
+ ** variable. */
+ if( bSort && (pSrc->jointype & JT_LEFT)==0 ){
+ int bRev = 2;
+ WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat));
+ pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev);
+ WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n",
+ bRev, pc.plan.nOBSat));
+ if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
+ pc.plan.wsFlags |= WHERE_ORDERED;
+ }
+ if( nOrderBy==pc.plan.nOBSat ){
bSort = 0;
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
+ pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
}
- if( bRev & 1 ) wsFlags |= WHERE_REVERSE;
+ if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE;
}
/* If there is a DISTINCT qualifier and this index will scan rows in
** order of the DISTINCT expressions, clear bDist and set the appropriate
- ** flags in wsFlags. */
+ ** flags in pc.plan.wsFlags. */
if( bDist
- && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, nEq)
- && (wsFlags & WHERE_COLUMN_IN)==0
+ && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq)
+ && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0
){
bDist = 0;
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
+ pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
}
/* If currently calculating the cost of using an index (not the IPK
** index), determine if all required column data may be obtained without
** using the main table (i.e. if the index is a covering
** index for this query). If it is, set the WHERE_IDX_ONLY flag in
- ** wsFlags. Otherwise, set the bLookup variable to true. */
+ ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */
if( pIdx ){
Bitmask m = pSrc->colUsed;
int j;
@@ -3245,7 +3282,7 @@ static void bestBtreeIndex(WhereBestIdx *p){
}
}
if( m==0 ){
- wsFlags |= WHERE_IDX_ONLY;
+ pc.plan.wsFlags |= WHERE_IDX_ONLY;
}else{
bLookup = 1;
}
@@ -3255,10 +3292,10 @@ static void bestBtreeIndex(WhereBestIdx *p){
** Estimate the number of rows of output. For an "x IN (SELECT...)"
** constraint, do not let the estimate exceed half the rows in the table.
*/
- nRow = (double)(aiRowEst[nEq] * nInMul);
- if( bInEst && nRow*2>aiRowEst[0] ){
- nRow = aiRowEst[0]/2;
- nInMul = (int)(nRow / aiRowEst[nEq]);
+ pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul);
+ if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){
+ pc.plan.nRow = aiRowEst[0]/2;
+ nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]);
}
#ifdef SQLITE_ENABLE_STAT3
@@ -3268,15 +3305,18 @@ static void bestBtreeIndex(WhereBestIdx *p){
** to get a better estimate on the number of rows based on
** VALUE and how common that value is according to the histogram.
*/
- if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
+ if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
+ && pFirstTerm!=0 && aiRowEst[1]>1 ){
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator==WO_EQ );
testcase( pFirstTerm->eOperator==WO_ISNULL );
- whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
+ whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
+ &pc.plan.nRow);
}else if( bInEst==0 ){
assert( pFirstTerm->eOperator==WO_IN );
- whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
+ whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
+ &pc.plan.nRow);
}
}
#endif /* SQLITE_ENABLE_STAT3 */
@@ -3284,8 +3324,8 @@ static void bestBtreeIndex(WhereBestIdx *p){
/* Adjust the number of output rows and downward to reflect rows
** that are excluded by range constraints.
*/
- nRow = nRow/rangeDiv;
- if( nRow<1 ) nRow = 1;
+ pc.plan.nRow = pc.plan.nRow/rangeDiv;
+ if( pc.plan.nRow<1 ) pc.plan.nRow = 1;
/* Experiments run on real SQLite databases show that the time needed
** to do a binary search to locate a row in a table or index is roughly
@@ -3300,7 +3340,7 @@ static void bestBtreeIndex(WhereBestIdx *p){
** So this computation assumes table records are about twice as big
** as index records
*/
- if( (wsFlags&~WHERE_REVERSE)==WHERE_IDX_ONLY
+ if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY
&& (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
&& sqlite3GlobalConfig.bUseCis
&& OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
@@ -3309,9 +3349,9 @@ static void bestBtreeIndex(WhereBestIdx *p){
** A full-scan of the index might be a little faster than a full-scan
** of the table, so give this case a cost slightly less than a table
** scan. */
- cost = aiRowEst[0]*3 + pProbe->nColumn;
- wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
- }else if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
+ pc.rCost = aiRowEst[0]*3 + pProbe->nColumn;
+ pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
+ }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
/* The cost of a full table scan is a number of move operations equal
** to the number of rows in the table.
**
@@ -3321,11 +3361,15 @@ static void bestBtreeIndex(WhereBestIdx *p){
** decision and one which we expect to revisit in the future. But
** it seems to be working well enough at the moment.
*/
- cost = aiRowEst[0]*4;
- wsFlags &= ~WHERE_IDX_ONLY;
+ pc.rCost = aiRowEst[0]*4;
+ pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
+ if( pIdx ){
+ pc.plan.wsFlags &= ~WHERE_ORDERED;
+ pc.plan.nOBSat = nPriorSat;
+ }
}else{
log10N = estLog(aiRowEst[0]);
- cost = nRow;
+ pc.rCost = pc.plan.nRow;
if( pIdx ){
if( bLookup ){
/* For an index lookup followed by a table lookup:
@@ -3333,20 +3377,20 @@ static void bestBtreeIndex(WhereBestIdx *p){
** + nRow steps through the index
** + nRow table searches to lookup the table entry using the rowid
*/
- cost += (nInMul + nRow)*log10N;
+ pc.rCost += (nInMul + pc.plan.nRow)*log10N;
}else{
/* For a covering index:
** nInMul index searches to find the initial entry
** + nRow steps through the index
*/
- cost += nInMul*log10N;
+ pc.rCost += nInMul*log10N;
}
}else{
/* For a rowid primary key lookup:
** nInMult table searches to find the initial entry for each range
** + nRow steps through the table
*/
- cost += nInMul*log10N;
+ pc.rCost += nInMul*log10N;
}
}
@@ -3357,10 +3401,12 @@ static void bestBtreeIndex(WhereBestIdx *p){
** difference and select C of 3.0.
*/
if( bSort ){
- cost += nRow*estLog(nRow*(nOrderBy - nOBSat)/nOrderBy)*3;
+ double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy);
+ m *= (double)(pc.plan.nOBSat ? 2 : 3);
+ pc.rCost += pc.plan.nRow*m;
}
if( bDist ){
- cost += nRow*estLog(nRow)*3;
+ pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3;
}
/**** Cost of using this index has now been computed ****/
@@ -3381,25 +3427,25 @@ static void bestBtreeIndex(WhereBestIdx *p){
** might be selected even when there exists an optimal index that has
** no such dependency.
*/
- if( nRow>2 && cost<=p->cost.rCost ){
+ if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){
int k; /* Loop counter */
- int nSkipEq = nEq; /* Number of == constraints to skip */
+ int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */
int nSkipRange = nBound; /* Number of < constraints to skip */
Bitmask thisTab; /* Bitmap for pSrc */
thisTab = getMask(pWC->pMaskSet, iCur);
- for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
+ for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){
if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
if( nSkipEq ){
- /* Ignore the first nEq equality matches since the index
+ /* Ignore the first pc.plan.nEq equality matches since the index
** has already accounted for these */
nSkipEq--;
}else{
/* Assume each additional equality match reduces the result
** set size by a factor of 10 */
- nRow /= 10;
+ pc.plan.nRow /= 10;
}
}else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
if( nSkipRange ){
@@ -3413,39 +3459,32 @@ static void bestBtreeIndex(WhereBestIdx *p){
** more selective intentionally because of the subjective
** observation that indexed range constraints really are more
** selective in practice, on average. */
- nRow /= 3;
+ pc.plan.nRow /= 3;
}
}else if( pTerm->eOperator!=WO_NOOP ){
/* Any other expression lowers the output row count by half */
- nRow /= 2;
+ pc.plan.nRow /= 2;
}
}
- if( nRow<2 ) nRow = 2;
+ if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
}
WHERETRACE((
- "%s(%s):\n"
- " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
- " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
- " used=0x%llx nOrdered=%d nOBSat=%d\n",
- pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
- nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags,
- p->notReady, log10N, nRow, cost, used, nOrdered, nOBSat
+ " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
+ " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
+ " used=0x%llx nOBSat=%d\n",
+ pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
+ p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used,
+ pc.plan.nOBSat
));
/* If this index is the best we have seen so far, then record this
- ** index and its cost in the pCost structure.
+ ** index and its cost in the p->cost structure.
*/
- if( (!pIdx || wsFlags)
- && (cost<p->cost.rCost || (cost<=p->cost.rCost && nRow<p->cost.plan.nRow))
- ){
- p->cost.rCost = cost;
- p->cost.used = used;
- p->cost.plan.nRow = nRow;
- p->cost.plan.wsFlags = (wsFlags&wsFlagMask);
- p->cost.plan.nEq = nEq;
- p->cost.plan.nOBSat = nOBSat;
+ if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){
+ p->cost = pc;
+ p->cost.plan.wsFlags &= wsFlagMask;
p->cost.plan.u.pIdx = pIdx;
}
@@ -3467,17 +3506,15 @@ static void bestBtreeIndex(WhereBestIdx *p){
p->cost.plan.wsFlags |= WHERE_REVERSE;
}
- assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERBY)==0 );
+ assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
assert( pSrc->pIndex==0
|| p->cost.plan.u.pIdx==0
|| p->cost.plan.u.pIdx==pSrc->pIndex
);
- WHERETRACE(("best index is: %s\n",
- ((p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" :
- p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")
- ));
+ WHERETRACE((" best index is: %s\n",
+ p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
bestOrClauseIndex(p);
bestAutomaticIndex(p);
@@ -4205,7 +4242,7 @@ static Bitmask codeOneLoopStart(
** this requires some special handling.
*/
if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && (pLevel->plan.wsFlags&WHERE_ORDERBY)
+ && (pLevel->plan.wsFlags&WHERE_ORDERED)
&& (pIdx->nColumn>nEq)
){
/* assert( pOrderBy->nExpr==1 ); */
@@ -5026,8 +5063,8 @@ WhereInfo *sqlite3WhereBegin(
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
- WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
- j, isOptimal));
+ WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
+ j, sWBI.pSrc->pTab->zName, isOptimal));
assert( sWBI.pSrc->pTab );
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(sWBI.pSrc->pTab) ){
@@ -5068,8 +5105,8 @@ WhereInfo *sqlite3WhereBegin(
** The NEVER() comes about because rule (2) above prevents
** An indexable full-table-scan from reaching rule (3).
**
- ** (4) The plan cost must be lower than prior plans or else the
- ** cost must be the same and the number of rows must be lower.
+ ** (4) The plan cost must be lower than prior plans, where "cost"
+ ** is defined by the compareCost() function above.
*/
if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
&& (bestJ<0 || (notIndexed&m)!=0 /* (2) */
@@ -5077,14 +5114,13 @@ WhereInfo *sqlite3WhereBegin(
|| (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
&& (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
|| NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
- && (bestJ<0 || sWBI.cost.rCost<bestPlan.rCost /* (4) */
- || (sWBI.cost.rCost<=bestPlan.rCost
- && sWBI.cost.plan.nRow<bestPlan.plan.nRow))
+ && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
){
- WHERETRACE(("=== table %d is best so far"
- " with cost=%.1f, nRow=%.1f, nOBSat=%d\n",
- j, sWBI.cost.rCost, sWBI.cost.plan.nRow,
- sWBI.cost.plan.nOBSat));
+ WHERETRACE((" === table %d (%s) is best so far\n"
+ " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
+ j, sWBI.pSrc->pTab->zName,
+ sWBI.cost.rCost, sWBI.cost.plan.nRow,
+ sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
bestPlan = sWBI.cost;
bestJ = j;
}
@@ -5093,19 +5129,18 @@ WhereInfo *sqlite3WhereBegin(
}
assert( bestJ>=0 );
assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
- WHERETRACE(("*** Optimizer selects table %d for loop %d with:\n"
- " cost=%.1f, nRow=%.1f, nOBSat=%d wsFlags=0x%08x\n",
- bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
+ WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
+ " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
+ bestJ, pTabList->a[bestJ].pTab->zName,
+ pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
- if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
- pWInfo->nOBSat = pOrderBy->nExpr;
- }
if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
assert( pWInfo->eDistinct==0 );
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
andFlags &= bestPlan.plan.wsFlags;
pLevel->plan = bestPlan.plan;
+ pLevel->iTabCur = pTabList->a[bestJ].iCursor;
testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
@@ -5147,11 +5182,18 @@ WhereInfo *sqlite3WhereBegin(
if( pParse->nErr || db->mallocFailed ){
goto whereBeginError;
}
+ if( nTabList ){
+ pLevel--;
+ pWInfo->nOBSat = pLevel->plan.nOBSat;
+ }else{
+ pWInfo->nOBSat = 0;
+ }
/* If the total query only selects a single row, then the ORDER BY
** clause is irrelevant.
*/
if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
+ assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 );
pWInfo->nOBSat = pOrderBy->nExpr;
}
@@ -5179,7 +5221,6 @@ WhereInfo *sqlite3WhereBegin(
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
- pLevel->iTabCur = pTabItem->iCursor;
pWInfo->nRowOut *= pLevel->plan.nRow;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
diff --git a/test/bigfile.test b/test/bigfile.test
index 59e9f18c9..31c632322 100644
--- a/test/bigfile.test
+++ b/test/bigfile.test
@@ -16,6 +16,7 @@
#
if {[file exists skip-big-file]} return
+if {$tcl_platform(os)=="Darwin"} return
set testdir [file dirname $argv0]
source $testdir/tester.tcl
diff --git a/test/bigfile2.test b/test/bigfile2.test
index 1f0ea85e5..b67cb34b4 100644
--- a/test/bigfile2.test
+++ b/test/bigfile2.test
@@ -14,6 +14,7 @@
#
if {[file exists skip-big-file]} return
+if {$tcl_platform(os)=="Darwin"} return
set testdir [file dirname $argv0]
source $testdir/tester.tcl
diff --git a/test/fuzzer1.test b/test/fuzzer1.test
index dc8b445ee..827d6a05f 100644
--- a/test/fuzzer1.test
+++ b/test/fuzzer1.test
@@ -1864,5 +1864,3 @@ do_execsql_test 10.3 {
} {1 21 41 61 81}
finish_test
-
-
diff --git a/test/orderby1.test b/test/orderby1.test
index 400659b47..2001e3400 100644
--- a/test/orderby1.test
+++ b/test/orderby1.test
@@ -114,7 +114,7 @@ do_test 1.4c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn
}
-} {/ORDER BY/} ;# separate sorting pass due to mixed DESC/ASC
+} {~/ORDER BY/} ;# optimized out
do_test 1.5a {
@@ -132,7 +132,7 @@ do_test 1.5c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
-} {/ORDER BY/} ;# separate sorting pass due to mixed DESC/ASC
+} {~/ORDER BY/} ;# optimized out
do_test 1.6a {
db eval {
@@ -170,14 +170,14 @@ do_test 2.0 {
name TEXT,
UNIQUE(aid, tn)
);
- INSERT INTO album VALUES(1, '1-one'), (2, '2-two'), (3, '3-three');
+ INSERT INTO album VALUES(1, '1-one'), (20, '2-two'), (3, '3-three');
INSERT INTO track VALUES
- (1, 1, 'one-a'),
- (2, 2, 'two-b'),
- (3, 3, 'three-c'),
- (1, 3, 'one-c'),
- (2, 1, 'two-a'),
- (3, 1, 'three-a');
+ (1, 1, 'one-a'),
+ (20, 2, 'two-b'),
+ (3, 3, 'three-c'),
+ (1, 3, 'one-c'),
+ (20, 1, 'two-a'),
+ (3, 1, 'three-a');
COMMIT;
}
} {}
@@ -196,6 +196,18 @@ do_test 2.1b {
}
} {~/ORDER BY/} ;# ORDER BY optimized out
+do_test 2.1c {
+ db eval {
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn
+ }
+} {one-a one-c two-a two-b three-a three-c}
+do_test 2.1d {
+ db eval {
+ EXPLAIN QUERY PLAN
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn
+ }
+} {~/ORDER BY/} ;# ORDER BY optimized out
+
# The same query with ORDER BY clause optimization disabled via + operators
# should give exactly the same answer.
#
@@ -249,7 +261,7 @@ do_test 2.4c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn
}
-} {/ORDER BY/} ;# separate sorting pass due to mixed DESC/ASC
+} {~/ORDER BY/} ;# optimized out
do_test 2.5a {
@@ -267,7 +279,7 @@ do_test 2.5c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
-} {/ORDER BY/} ;# separate sorting pass due to mixed ASC/DESC
+} {~/ORDER BY/} ;# optimized out
do_test 2.6a {
db eval {
@@ -284,7 +296,7 @@ do_test 2.6c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC
}
-} {~/ORDER BY/} ;# ORDER BY optimized-out
+} {~/ORDER BY/} ;# ORDER BY optimized out
# Generate another test dataset, but this time using mixed ASC/DESC indices.
@@ -384,7 +396,7 @@ do_test 3.4c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
-} {/ORDER BY/} ;# separate sorting pass due to mismatched DESC/ASC
+} {~/ORDER BY/} ;# optimized out
do_test 3.5a {
@@ -402,7 +414,7 @@ do_test 3.5c {
EXPLAIN QUERY PLAN
SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC
}
-} {/ORDER BY/} ;# separate sorting pass due to mismatched ASC/DESC
+} {~/ORDER BY/} ;# optimzed out
do_test 3.6a {
diff --git a/test/orderby2.test b/test/orderby2.test
new file mode 100644
index 000000000..cfd647715
--- /dev/null
+++ b/test/orderby2.test
@@ -0,0 +1,117 @@
+# 2012 Sept 27
+#
+# 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. The
+# focus of this file is testing that the optimizations that disable
+# ORDER BY clauses when the natural order of a query is correct.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix orderby2
+
+# Generate test data for a join. Verify that the join gets the
+# correct answer.
+#
+do_test 1.0 {
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1,11), (2,22);
+ CREATE TABLE t2(d, e, UNIQUE(d,e));
+ INSERT INTO t2 VALUES(10, 'ten'), (11,'eleven'), (12,'twelve'),
+ (11, 'oneteen');
+ }
+} {}
+
+do_test 1.1a {
+ db eval {
+ SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY d, e;
+ }
+} {eleven oneteen}
+do_test 1.1b {
+ db eval {
+ EXPLAIN QUERY PLAN
+ SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY d, e;
+ }
+} {~/ORDER BY/}
+
+do_test 1.2a {
+ db eval {
+ SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY e;
+ }
+} {eleven oneteen}
+do_test 1.2b {
+ db eval {
+ EXPLAIN QUERY PLAN
+ SELECT e FROM t1, t2 WHERE a=1 AND d=b ORDER BY e;
+ }
+} {~/ORDER BY/}
+
+do_test 1.3a {
+ db eval {
+ SELECT e, b FROM t1, t2 WHERE a=1 ORDER BY d, e;
+ }
+} {ten 11 eleven 11 oneteen 11 twelve 11}
+do_test 1.3b {
+ db eval {
+ EXPLAIN QUERY PLAN
+ SELECT e, b FROM t1, t2 WHERE a=1 ORDER BY d, e;
+ }
+} {~/ORDER BY/}
+
+# The following tests derived from TH3 test module cov1/where34.test
+#
+do_test 2.0 {
+ db eval {
+ CREATE TABLE t31(a,b); CREATE INDEX t31ab ON t31(a,b);
+ CREATE TABLE t32(c,d); CREATE INDEX t32cd ON t32(c,d);
+ CREATE TABLE t33(e,f); CREATE INDEX t33ef ON t33(e,f);
+ CREATE TABLE t34(g,h); CREATE INDEX t34gh ON t34(g,h);
+
+ INSERT INTO t31 VALUES(1,4), (2,3), (1,3);
+ INSERT INTO t32 VALUES(4,5), (3,6), (3,7), (4,8);
+ INSERT INTO t33 VALUES(5,9), (7,10), (6,11), (8,12), (8,13), (7,14);
+ INSERT INTO t34 VALUES(11,20), (10,21), (12,22), (9,23), (13,24),
+ (14,25), (12,26);
+ SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34
+ WHERE c=b AND e=d AND g=f
+ ORDER BY a ASC, c ASC, e DESC, g ASC;
+ }
+} {1,3,7,10 1,3,7,14 1,3,6,11 1,4,8,12 1,4,8,12 1,4,8,13 1,4,5,9 2,3,7,10 2,3,7,14 2,3,6,11}
+do_test 2.1 {
+ db eval {
+ SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34
+ WHERE c=b AND e=d AND g=f
+ ORDER BY +a ASC, +c ASC, +e DESC, +g ASC;
+ }
+} {1,3,7,10 1,3,7,14 1,3,6,11 1,4,8,12 1,4,8,12 1,4,8,13 1,4,5,9 2,3,7,10 2,3,7,14 2,3,6,11}
+do_test 2.2 {
+ db eval {
+ SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34
+ WHERE c=b AND e=d AND g=f
+ ORDER BY a ASC, c ASC, e ASC, g ASC;
+ }
+} {1,3,6,11 1,3,7,10 1,3,7,14 1,4,5,9 1,4,8,12 1,4,8,12 1,4,8,13 2,3,6,11 2,3,7,10 2,3,7,14}
+do_test 2.3 {
+ optimization_control db cover-idx-scan off
+ db cache flush
+ db eval {
+ SELECT a||','||c||','||e||','||g FROM t31, t32, t33, t34
+ WHERE c=b AND e=d AND g=f
+ ORDER BY a ASC, c ASC, e ASC, g ASC;
+ }
+} {1,3,6,11 1,3,7,10 1,3,7,14 1,4,5,9 1,4,8,12 1,4,8,12 1,4,8,13 2,3,6,11 2,3,7,10 2,3,7,14}
+optimization_control db all on
+db cache flush
+
+
+
+finish_test
diff --git a/test/shared9.test b/test/shared9.test
new file mode 100644
index 000000000..4ebdc1743
--- /dev/null
+++ b/test/shared9.test
@@ -0,0 +1,207 @@
+# 2012 October 5
+#
+# 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.
+#
+#***********************************************************************
+#
+# The tests in this file are intended to show if two connections attach
+# to the same shared cache using different database names, views and
+# virtual tables may still be accessed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set testprefix shared9
+
+ifcapable !view||!trigger {
+ finish_test
+ return
+}
+
+db close
+set enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+sqlite3 db1 test.db
+sqlite3 db2 test.db
+forcedelete test.db2
+
+do_test 1.1 {
+ db1 eval {
+ ATTACH 'test.db2' AS 'fred';
+ CREATE TABLE fred.t1(a, b, c);
+ CREATE VIEW fred.v1 AS SELECT * FROM t1;
+
+ CREATE TABLE fred.t2(a, b);
+ CREATE TABLE fred.t3(a, b);
+ CREATE TRIGGER fred.trig AFTER INSERT ON t2 BEGIN
+ DELETE FROM t3;
+ INSERT INTO t3 SELECT * FROM t2;
+ END;
+ INSERT INTO t2 VALUES(1, 2);
+ SELECT * FROM t3;
+ }
+} {1 2}
+
+do_test 1.2 { db2 eval "ATTACH 'test.db2' AS 'jones'" } {}
+do_test 1.3 { db2 eval "SELECT * FROM v1" } {}
+do_test 1.4 { db2 eval "INSERT INTO t2 VALUES(3, 4)" } {}
+
+ifcapable fts3 {
+ do_test 1.5 {
+ db1 eval {
+ CREATE VIRTUAL TABLE fred.t4 USING fts4;
+ INSERT INTO t4 VALUES('hello world');
+ }
+ } {}
+
+ do_test 1.6 {
+ db2 eval {
+ INSERT INTO t4 VALUES('shared cache');
+ SELECT * FROM t4 WHERE t4 MATCH 'hello';
+ }
+ } {{hello world}}
+
+ do_test 1.7 {
+ db1 eval {
+ SELECT * FROM t4 WHERE t4 MATCH 'c*';
+ }
+ } {{shared cache}}
+}
+
+db1 close
+db2 close
+
+#-------------------------------------------------------------------------
+# The following tests attempt to find a similar problem with collation
+# sequence names - pointers to database handle specific allocations leaking
+# into schema objects and being used after the original handle has been
+# closed.
+#
+forcedelete test.db test.db2
+sqlite3 db1 test.db
+sqlite3 db2 test.db
+foreach x {collate1 collate2 collate3} {
+ proc $x {a b} { string compare $a $b }
+ db1 collate $x $x
+ db2 collate $x $x
+}
+do_test 2.1 {
+ db1 eval {
+ CREATE TABLE t1(a, b, c COLLATE collate1);
+ CREATE INDEX i1 ON t1(a COLLATE collate2, c, b);
+ }
+} {}
+do_test 2.2 {
+ db1 close
+ db2 eval "INSERT INTO t1 VALUES('abc', 'def', 'ghi')"
+} {}
+db2 close
+
+#-------------------------------------------------------------------------
+# At one point, the following would cause a collation sequence belonging
+# to connection [db1] to be invoked by a call to [db2 eval]. Which is a
+# problem if [db1] has already been closed.
+#
+forcedelete test.db test.db2
+sqlite3 db1 test.db
+sqlite3 db2 test.db
+
+proc mycollate_db1 {a b} {set ::invoked_mycollate_db1 1 ; string compare $a $b}
+proc mycollate_db2 {a b} {string compare $a $b}
+
+db1 collate mycollate mycollate_db1
+db2 collate mycollate mycollate_db2
+
+do_test 2.3 {
+ set ::invoked_mycollate_db1 0
+ db1 eval {
+ CREATE TABLE t1(a COLLATE mycollate, CHECK (a IN ('one', 'two', 'three')));
+ INSERT INTO t1 VALUES('one');
+ }
+ db1 close
+ set ::invoked_mycollate_db1
+} {1}
+do_test 2.4 {
+ set ::invoked_mycollate_db1 0
+ db2 eval {
+ INSERT INTO t1 VALUES('two');
+ }
+ db2 close
+ set ::invoked_mycollate_db1
+} {0}
+
+#-------------------------------------------------------------------------
+# This test verifies that a bug causing a busy-handler belonging to one
+# shared-cache connection to be executed as a result of an sqlite3_step()
+# on another has been fixed.
+#
+forcedelete test.db test.db2
+sqlite3 db1 test.db
+sqlite3 db2 test.db
+
+proc busyhandler {handle args} {
+ set ::busyhandler_invoked_for $handle
+ return 1
+}
+db1 busy [list busyhandler db1]
+db2 busy [list busyhandler db2]
+
+do_test 3.1 {
+ db1 eval {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t2 VALUES(1, 2);
+ }
+ # Keep this next COMMIT as a separate statement. This ensures that COMMIT
+ # has already been compiled and loaded into the tcl interface statement
+ # cache when it is attempted below.
+ db1 eval COMMIT
+ db1 eval {
+ BEGIN;
+ INSERT INTO t1 VALUES(3, 4);
+ }
+} {}
+
+do_test 3.2 {
+ set ::tf [launch_testfixture]
+ testfixture $::tf {
+ sqlite3 db test.db
+ db eval {
+ BEGIN;
+ SELECT * FROM t1;
+ }
+ }
+} {1 2}
+
+do_test 3.3 {
+ db2 eval { SELECT * FROM t2 }
+} {1 2}
+
+do_test 3.4 {
+ list [catch { db1 eval COMMIT } msg] $msg
+} {1 {database is locked}}
+
+# At one point the following would fail, showing that the busy-handler
+# belonging to [db2] was invoked instead.
+do_test 3.5 {
+ set ::busyhandler_invoked_for
+} {db1}
+do_test 3.6 {
+ close $::tf
+ db1 eval COMMIT
+} {}
+
+db1 close
+db2 close
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
+
diff --git a/test/tclsqlite.test b/test/tclsqlite.test
index 51eea9848..d54e4aa8d 100644
--- a/test/tclsqlite.test
+++ b/test/tclsqlite.test
@@ -319,14 +319,23 @@ do_test tcl-8.1 {
execsql {INSERT INTO t1 VALUES(30,NULL)}
db eval {SELECT * FROM t1 WHERE b IS NULL}
} {30 NaN}
+proc concatFunc args {return [join $args {}]}
do_test tcl-8.2 {
+ db function concat concatFunc
+ db eval {SELECT concat('a', b, 'z') FROM t1 WHERE b is NULL}
+} {aNaNz}
+do_test tcl-8.3 {
db nullvalue NULL
db nullvalue
} {NULL}
-do_test tcl-8.3 {
+do_test tcl-8.4 {
db nullvalue {}
db eval {SELECT * FROM t1 WHERE b IS NULL}
} {30 {}}
+do_test tcl-8.5 {
+ db function concat concatFunc
+ db eval {SELECT concat('a', b, 'z') FROM t1 WHERE b is NULL}
+} {az}
# Test the return type of user-defined functions
#
diff --git a/test/trigger1.test b/test/trigger1.test
index 9d917bdb3..c49bc659a 100644
--- a/test/trigger1.test
+++ b/test/trigger1.test
@@ -11,20 +11,20 @@
# with the database COMMIT/ROLLBACK logic.
#
# 1. CREATE and DROP TRIGGER tests
-# trig-1.1: Error if table does not exist
-# trig-1.2: Error if trigger already exists
-# trig-1.3: Created triggers are deleted if the transaction is rolled back
-# trig-1.4: DROP TRIGGER removes trigger
-# trig-1.5: Dropped triggers are restored if the transaction is rolled back
-# trig-1.6: Error if dropped trigger doesn't exist
-# trig-1.7: Dropping the table automatically drops all triggers
-# trig-1.8: A trigger created on a TEMP table is not inserted into sqlite_master
-# trig-1.9: Ensure that we cannot create a trigger on sqlite_master
-# trig-1.10:
-# trig-1.11:
-# trig-1.12: Ensure that INSTEAD OF triggers cannot be created on tables
-# trig-1.13: Ensure that AFTER triggers cannot be created on views
-# trig-1.14: Ensure that BEFORE triggers cannot be created on views
+# trigger1-1.1: Error if table does not exist
+# trigger1-1.2: Error if trigger already exists
+# trigger1-1.3: Created triggers are deleted if the transaction is rolled back
+# trigger1-1.4: DROP TRIGGER removes trigger
+# trigger1-1.5: Dropped triggers are restored if the transaction is rolled back
+# trigger1-1.6: Error if dropped trigger doesn't exist
+# trigger1-1.7: Dropping the table automatically drops all triggers
+# trigger1-1.8: A trigger created on a TEMP table is not inserted into sqlite_master
+# trigger1-1.9: Ensure that we cannot create a trigger on sqlite_master
+# trigger1-1.10:
+# trigger1-1.11:
+# trigger1-1.12: Ensure that INSTEAD OF triggers cannot be created on tables
+# trigger1-1.13: Ensure that AFTER triggers cannot be created on views
+# trigger1-1.14: Ensure that BEFORE triggers cannot be created on views
#
set testdir [file dirname $argv0]
@@ -210,7 +210,7 @@ do_test trigger1-1.12 {
delete from t1 WHERE a=old.a+2;
end;
}
-} {1 {cannot create INSTEAD OF trigger on table: main.t1}}
+} {1 {cannot create INSTEAD OF trigger on table: t1}}
ifcapable view {
# Ensure that we cannot create BEFORE triggers on views
@@ -221,7 +221,7 @@ do_test trigger1-1.13 {
delete from t1 WHERE a=old.a+2;
end;
}
-} {1 {cannot create BEFORE trigger on view: main.v1}}
+} {1 {cannot create BEFORE trigger on view: v1}}
# Ensure that we cannot create AFTER triggers on views
do_test trigger1-1.14 {
catchsql {
@@ -231,7 +231,7 @@ do_test trigger1-1.14 {
delete from t1 WHERE a=old.a+2;
end;
}
-} {1 {cannot create AFTER trigger on view: main.v1}}
+} {1 {cannot create AFTER trigger on view: v1}}
} ;# ifcapable view
# Check for memory leaks in the trigger parser
@@ -265,32 +265,32 @@ ifcapable tempdb {
END;
}
} {0 {}}
- do_test trigger-3.2 {
+ do_test trigger1-3.2 {
catchsql {
INSERT INTO t1 VALUES(1,2);
SELECT * FROM t2;
}
} {1 {no such table: main.t2}}
- do_test trigger-3.3 {
+ do_test trigger1-3.3 {
db close
set rc [catch {sqlite3 db test.db} err]
if {$rc} {lappend rc $err}
set rc
} {0}
- do_test trigger-3.4 {
+ do_test trigger1-3.4 {
catchsql {
INSERT INTO t1 VALUES(1,2);
SELECT * FROM t2;
}
} {1 {no such table: main.t2}}
- do_test trigger-3.5 {
+ do_test trigger1-3.5 {
catchsql {
CREATE TEMP TABLE t2(x,y);
INSERT INTO t1 VALUES(1,2);
SELECT * FROM t2;
}
} {1 {no such table: main.t2}}
- do_test trigger-3.6.1 {
+ do_test trigger1-3.6.1 {
catchsql {
DROP TRIGGER r1;
CREATE TEMP TRIGGER r1 AFTER INSERT ON t1 BEGIN
@@ -300,7 +300,7 @@ ifcapable tempdb {
SELECT * FROM t2;
}
} {0 {1 2 200 100}}
- do_test trigger-3.6.2 {
+ do_test trigger1-3.6.2 {
catchsql {
DROP TRIGGER r1;
DELETE FROM t1;
@@ -312,7 +312,7 @@ ifcapable tempdb {
SELECT * FROM t2;
}
} {0 {1 2}}
- do_test trigger-3.7 {
+ do_test trigger1-3.7 {
execsql {
DROP TABLE t2;
CREATE TABLE t2(x,y);
@@ -320,7 +320,7 @@ ifcapable tempdb {
}
} {}
- # There are two versions of trigger-3.8 and trigger-3.9. One that uses
+ # There are two versions of trigger1-3.8 and trigger1-3.9. One that uses
# compound SELECT statements, and another that does not.
ifcapable compound {
do_test trigger1-3.8 {
@@ -446,7 +446,7 @@ do_test trigger1-6.8 {
execsql {SELECT * FROM t2}
} {3 4 7 8}
-integrity_check trigger-7.1
+integrity_check trigger1-7.1
# Check to make sure the name of a trigger can be quoted so that keywords
# can be used as trigger names. Ticket #468
@@ -491,7 +491,7 @@ do_test trigger1-8.6 {
ifcapable conflict {
# Make sure REPLACE works inside of triggers.
#
- # There are two versions of trigger-9.1 and trigger-9.2. One that uses
+ # There are two versions of trigger1-9.1 and trigger1-9.2. One that uses
# compound SELECT statements, and another that does not.
ifcapable compound {
do_test trigger1-9.1 {
@@ -612,7 +612,7 @@ ifcapable tempdb&&attach {
SELECT * FROM insert_log;
}
} {main 11 12 13 temp 14 15 16 aux 17 18 19}
- do_test trigger1-10.8 {
+ do_test trigger1-10.9 {
# Drop and re-create the insert_log table in a different database. Note
# that we can change the column names because the trigger programs don't
# use them explicitly.
diff --git a/test/where.test b/test/where.test
index 83fec2cf6..461ab8727 100644
--- a/test/where.test
+++ b/test/where.test
@@ -1088,12 +1088,12 @@ do_test where-14.1 {
cksort {
SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b
}
-} {1/4 1/1 4/4 4/1 sort}
+} {1/4 1/1 4/4 4/1 nosort}
do_test where-14.2 {
cksort {
SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b DESC
}
-} {1/1 1/4 4/1 4/4 sort}
+} {1/1 1/4 4/1 4/4 nosort}
do_test where-14.3 {
cksort {
SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, x.b
diff --git a/tool/build-all-msvc.bat b/tool/build-all-msvc.bat
index a2d7dae3a..58e2dca3e 100755
--- a/tool/build-all-msvc.bat
+++ b/tool/build-all-msvc.bat
@@ -142,7 +142,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM be used for the name of the platform-specific binary directory via
REM the environment variables setup earlier.
REM
- CALL :fn_SetVariable %%P_NAME PLATFORMNAME
+ CALL :fn_CopyVariable %%P_NAME PLATFORMNAME
REM
REM NOTE: This is the inner loop. There should be exactly one iteration.
@@ -230,7 +230,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM file used to setup the MSVC environment.
REM
IF DEFINED SET_NSDKLIBPATH (
- CALL :fn_SetVariable WindowsSdkDir NSDKLIBPATH
+ CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH
CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86
)
@@ -339,7 +339,7 @@ GOTO no_errors
VERIFY MAYBE 2> NUL
GOTO :EOF
-:fn_SetVariable
+:fn_CopyVariable
SETLOCAL
IF NOT DEFINED %1 GOTO :EOF
IF "%2" == "" GOTO :EOF