aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in6
-rw-r--r--Makefile.msc6
-rw-r--r--ext/rtree/rtree.c2
-rw-r--r--main.mk3
-rw-r--r--manifest40
-rw-r--r--manifest.uuid2
-rw-r--r--src/global.c13
-rw-r--r--src/journal.c258
-rw-r--r--src/main.c5
-rw-r--r--src/memjournal.c42
-rw-r--r--src/pager.c6
-rw-r--r--src/sqlite.h.in15
-rw-r--r--src/sqliteInt.h3
-rw-r--r--src/test_bestindex.c90
-rw-r--r--src/util.c4
-rw-r--r--src/where.c350
-rw-r--r--test/bestindex2.test138
-rw-r--r--tool/mksqlite3c.tcl1
18 files changed, 505 insertions, 479 deletions
diff --git a/Makefile.in b/Makefile.in
index a46e8b582..1b50b4e51 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -175,7 +175,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \
fts5.lo \
func.lo global.lo hash.lo \
- icu.lo insert.lo journal.lo json1.lo legacy.lo loadext.lo \
+ icu.lo insert.lo json1.lo legacy.lo loadext.lo \
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
memjournal.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
@@ -227,7 +227,6 @@ SRC = \
$(TOP)/src/hash.h \
$(TOP)/src/hwtime.h \
$(TOP)/src/insert.c \
- $(TOP)/src/journal.c \
$(TOP)/src/legacy.c \
$(TOP)/src/loadext.c \
$(TOP)/src/main.c \
@@ -746,9 +745,6 @@ hash.lo: $(TOP)/src/hash.c $(HDR)
insert.lo: $(TOP)/src/insert.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/insert.c
-journal.lo: $(TOP)/src/journal.c $(HDR)
- $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/journal.c
-
legacy.lo: $(TOP)/src/legacy.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/legacy.c
diff --git a/Makefile.msc b/Makefile.msc
index d6db4880d..a6a2394a6 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -995,7 +995,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \
fts5.lo \
func.lo global.lo hash.lo \
- icu.lo insert.lo journal.lo legacy.lo loadext.lo \
+ icu.lo insert.lo legacy.lo loadext.lo \
main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \
memjournal.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
@@ -1060,7 +1060,6 @@ SRC00 = \
$(TOP)\src\global.c \
$(TOP)\src\hash.c \
$(TOP)\src\insert.c \
- $(TOP)\src\journal.c \
$(TOP)\src\legacy.c \
$(TOP)\src\loadext.c \
$(TOP)\src\main.c \
@@ -1636,9 +1635,6 @@ hash.lo: $(TOP)\src\hash.c $(HDR)
insert.lo: $(TOP)\src\insert.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\insert.c
-journal.lo: $(TOP)\src\journal.c $(HDR)
- $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\journal.c
-
legacy.lo: $(TOP)\src\legacy.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\legacy.c
diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c
index 4e473a22c..012c48d6d 100644
--- a/ext/rtree/rtree.c
+++ b/ext/rtree/rtree.c
@@ -1741,7 +1741,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
return SQLITE_NOMEM;
}
- nRow = pRtree->nRowEst / (iIdx + 1);
+ nRow = pRtree->nRowEst >> (iIdx/2);
pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
setEstimatedRows(pIdxInfo, nRow);
diff --git a/main.mk b/main.mk
index 401054f8c..5c8621d03 100644
--- a/main.mk
+++ b/main.mk
@@ -62,7 +62,7 @@ LIBOBJ+= vdbe.o parse.o \
fts3_tokenize_vtab.o \
fts3_unicode.o fts3_unicode2.o \
fts3_write.o fts5.o func.o global.o hash.o \
- icu.o insert.o journal.o json1.o legacy.o loadext.o \
+ icu.o insert.o json1.o legacy.o loadext.o \
main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
memjournal.o \
mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \
@@ -106,7 +106,6 @@ SRC = \
$(TOP)/src/hash.h \
$(TOP)/src/hwtime.h \
$(TOP)/src/insert.c \
- $(TOP)/src/journal.c \
$(TOP)/src/legacy.c \
$(TOP)/src/loadext.c \
$(TOP)/src/main.c \
diff --git a/manifest b/manifest
index b62147b7c..ee71dadf9 100644
--- a/manifest
+++ b/manifest
@@ -1,8 +1,8 @@
-C Merge\srecent\senhancements\sfrom\strunk.\s\sDefault\spage\ssize\sis\s4096.\s\sWrites\nto\sstatement\sjournals\sare\savoided.
-D 2016-03-04T16:42:43.133
-F Makefile.in 055473c5b509b37ae18d4c3ed39af4b124476f0d
+C Merge\sthe\svirtual\stable\squery\splanner\senhancement,\sthe\sRTREE\scost\sestimate\nfix,\sand\sthe\sstatement\sjournal\sspill\sdelay\senhancement\sfrom\strunk.
+D 2016-03-07T17:49:17.995
+F Makefile.in e812bb732d7af01baa09f1278bd4f4a2e3a09449
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
-F Makefile.msc ea016bfc6d772cb81c2ba2722460937f04ae07f9
+F Makefile.msc e6ee58b849c116d5554024f524cbf61f064f6f01
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
F VERSION c6b1f51809551d60ad001e6d87cf3ab2c7f54b6f
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -245,7 +245,7 @@ F ext/rbu/sqlite3rbu.c 371e8bf06cfb3f691adac47eb15ab1073ed92dcf
F ext/rbu/sqlite3rbu.h 0bdeb3be211aaba7d85445fa36f4701a25a3dbde
F ext/rbu/test_rbu.c 4a4cdcef4ef9379fc2a21f008805c80b27bcf573
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c 0f9b595bd0debcbedf1d7a63d0e0678d619e6c9c
+F ext/rtree/rtree.c 0b870ccb7b58b734a2a8e1e2755a7c0ded070920
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@@ -293,7 +293,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk b61722224cf37810ee3160a5c3f927e1a90cc25b
+F main.mk 942e1fd30eb352492cf599aa7fcb5334783e4cbf
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -326,22 +326,21 @@ F src/expr.c c4dad2cd6cec00387b75fef4551aff655430dcd2
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 5da47ff524e2f687997a74737ce598f043e1342a
F src/func.c 552d300265aed09eea21f68ac742a440550c0062
-F src/global.c ded7b97efd16efda5062b65e857198e46c40e652
+F src/global.c 884d4c7eba9f5fc25c96a23b21520da19b7713e2
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
F src/insert.c 8f4e9fcbd8e95e85f15647ba8b413b18d556ec2b
-F src/journal.c 673cbdde5676eb0c55848f561575d45b609c820d
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
F src/loadext.c 9e2a41adcaff16ebc1ebff1f336cbf33de55396f
-F src/main.c 56edbba4bd69e5f650bd75ae45f1a7d081e1e892
+F src/main.c e5675877b35bd894e5803588974fc59369d5a6f2
F src/malloc.c 1443d1ad95d67c21d77af7ae3f44678252f0efec
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
F src/mem3.c 8768ac94694f31ffaf8b4d0ea5dc08af7010a35a
F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
-F src/memjournal.c 56c7688565cf407c5f22f03e3be478d3a92bdc81
+F src/memjournal.c 011da5236a7250385cc74c253f14bbee04c0d61e
F src/msvc.h d9ba56c6851227ab44b3f228a35f3f5772296495
F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
@@ -356,7 +355,7 @@ F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c f5bac8e74aaefc4ea520e43b4540793c3b8a9e8f
F src/os_win.c f0d7aa603eb6262143d7169a222aea07c4fca91d
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
-F src/pager.c 2bc43817697b5a4e88fd6a2cdb2cb25f2223505c
+F src/pager.c d40cf1e890a0582b6ac7cb208c24619d72d2c900
F src/pager.h e1d38a2f14849e219df0f91f8323504d134c8a56
F src/parse.y 5ea8c81c5c41b27887f41b4a7e1c58470d7d3821
F src/pcache.c 647bb53a86b7bbcf55ad88089b3ea5a9170b90df
@@ -371,10 +370,10 @@ F src/resolve.c b8f7174e5f8c33c44ded3a25a973d0bb89228c20
F src/rowset.c 9fe4b3ad7cc00944386bb600233d8f523de07a6e
F src/select.c 137b31daa84d57d67847bf621bb54f3353e2077b
F src/shell.c cd3f82fdc5c895b817a375b7ab8319cb41f447ce
-F src/sqlite.h.in a51577ed847b75fff9c10b7ea35e37e8154cee71
+F src/sqlite.h.in 86884a006a451c22d342da18d8e373aa70e65ec0
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
-F src/sqliteInt.h b0e28df3739ef7113eb1297db0766abf74635c90
+F src/sqliteInt.h cd85b90d949c7270266f997ed9796371e6019c53
F src/sqliteLimit.h 7b28cf72cbd52f178bfc97ea266445e351f2cd24
F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
@@ -391,7 +390,7 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803
-F src/test_bestindex.c cd36324f05404df5f1a82608a321b91932a549ea
+F src/test_bestindex.c 29af3cc3b963ffe5760c85d142b9b3e5302c1e3d
F src/test_blob.c b2551a9b5573232db5f66f292307c37067937239
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
F src/test_config.c a0e8eeb86a7d9393f3bbf7315932c1cccd0e8010
@@ -433,7 +432,7 @@ F src/treeview.c e4b41a37530a191579d3c53142cc44ee2eb99373
F src/trigger.c e14840ee0c3e549e758ec9bf3e4146e166002280
F src/update.c c0016d277a418360456ff6af29363effbd4272f7
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
-F src/util.c a64585a74aef6feb16dfe4e090b5de01e26eba3b
+F src/util.c 12800a93f0664f41575f96799eb881a786d565e6
F src/vacuum.c feb1eabb20987983d9350cad98299b21fa811f52
F src/vdbe.c 9ccb138cb7ac6c1bcda23dfaf52eff1f7761dfe5
F src/vdbe.h 594aef1a7dcfc2944e2f266f148140c3427fd0f0
@@ -449,7 +448,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 10deb6b43887662691e5f53d10b3c171c401169b
F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
-F src/where.c 56948ada5aacc3bf2628db3776986e8bf4085383
+F src/where.c ccc62c39af1e6340f6af36fcf68efb96482d4c3a
F src/whereInt.h 93297d56edd137b7ea004490690fb6e2ce028a34
F src/wherecode.c 3ca820435c5b597bb50e63ed11e938786fe5c23e
F src/whereexpr.c fb87944b1254234e5bba671aaf6dee476241506a
@@ -514,6 +513,7 @@ F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/bc_common.tcl 3eda41ef9cda7d5f6c205462c96228b301da4191
F test/bestindex1.test e228fe1e3794dbe20271481164e000d695abcd24
+F test/bestindex2.test 10f2c6791f1cd0de414012528cd10a114648fd8f
F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
@@ -1417,7 +1417,7 @@ F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkpragmatab.tcl f0d5bb266d1d388cf86fce5ba01a891e95d72d41
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 87240b09c20042999b41d5fabe091b7111287835
-F tool/mksqlite3c.tcl a52d7e8c0888f9384fbfa2c6ddd5f357409758b9
+F tool/mksqlite3c.tcl 63af8429841f08552e6da1d93b3dee4a93ff8071
F tool/mksqlite3h.tcl e7b106fc4f29fbc258e8ba9b88d9108332ea2ade
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl 4abcaf3267171b2faadaf9b82a0dfbaa6e98f8b7
@@ -1474,7 +1474,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8fea1166016c659ece83e0045bc348f478a9ba6a cb9302cca423de41305719a49208daa392ec09da
-R dd40074d0cb69f94c136cb2921dcb114
+P 456df3365e2df60e34762f2024bb551538b3f72b b6c4202432dc96f8f1740f52d0bf872116357fcc
+R 2f497816caa21168c1331f3fb71a39ba
U drh
-Z 1bfaeec3e683dbb2e879f4e9437ca024
+Z a204fd45fac574847e4285c8445e5721
diff --git a/manifest.uuid b/manifest.uuid
index a33810a4c..4c042e948 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-456df3365e2df60e34762f2024bb551538b3f72b \ No newline at end of file
+17fd8f3cf0ec565e08403dc8e10a1cffc2bbe165 \ No newline at end of file
diff --git a/src/global.c b/src/global.c
index 294d62fea..bd70b3827 100644
--- a/src/global.c
+++ b/src/global.c
@@ -159,6 +159,18 @@ const unsigned char sqlite3CtypeMap[256] = {
# define SQLITE_SORTER_PMASZ 250
#endif
+/* Statement journals spill to disk when their size exceeds the following
+** threashold (in bytes). 0 means that statement journals are created and
+** written to disk immediately (the default behavior for SQLite versions
+** before 3.12.0). -1 means always keep the entire statement journal in
+** memory. (The statement journal is also always held entirely in memory
+** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this
+** setting.)
+*/
+#ifndef SQLITE_STMTJRNL_SPILL
+# define SQLITE_STMTJRNL_SPILL (64*1024)
+#endif
+
/*
** The following singleton contains the global configuration for
** the SQLite library.
@@ -173,6 +185,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* neverCorrupt */
128, /* szLookaside */
500, /* nLookaside */
+ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
diff --git a/src/journal.c b/src/journal.c
deleted file mode 100644
index da59db0e5..000000000
--- a/src/journal.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
-** 2007 August 22
-**
-** 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 a special kind of sqlite3_file object used
-** by SQLite to create journal files if the atomic-write optimization
-** is enabled.
-**
-** The distinctive characteristic of this sqlite3_file is that the
-** actual on disk file is created lazily. When the file is created,
-** the caller specifies a buffer size for an in-memory buffer to
-** be used to service read() and write() requests. The actual file
-** on disk is not created or populated until either:
-**
-** 1) The in-memory representation grows too large for the allocated
-** buffer, or
-** 2) The sqlite3JournalCreate() function is called.
-*/
-#if 0
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-#include "sqliteInt.h"
-
-
-/*
-** A JournalFile object is a subclass of sqlite3_file used by
-** as an open file handle for journal files.
-*/
-struct JournalFile {
- sqlite3_io_methods *pMethod; /* I/O methods on journal files */
- int nBuf; /* Size of zBuf[] in bytes */
- char *zBuf; /* Space to buffer journal writes */
- int iSize; /* Amount of zBuf[] currently used */
- int flags; /* xOpen flags */
- sqlite3_vfs *pVfs; /* The "real" underlying VFS */
- sqlite3_file *pReal; /* The "real" underlying file descriptor */
- const char *zJournal; /* Name of the journal file */
-};
-typedef struct JournalFile JournalFile;
-
-/*
-** If it does not already exists, create and populate the on-disk file
-** for JournalFile p.
-*/
-static int createFile(JournalFile *p){
- int rc = SQLITE_OK;
- if( !p->pReal ){
- sqlite3_file *pReal = (sqlite3_file *)&p[1];
- rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
- if( rc==SQLITE_OK ){
- p->pReal = pReal;
- if( p->iSize>0 ){
- assert(p->iSize<=p->nBuf);
- rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
- }
- if( rc!=SQLITE_OK ){
- /* If an error occurred while writing to the file, close it before
- ** returning. This way, SQLite uses the in-memory journal data to
- ** roll back changes made to the internal page-cache before this
- ** function was called. */
- sqlite3OsClose(pReal);
- p->pReal = 0;
- }
- }
- }
- return rc;
-}
-
-/*
-** Close the file.
-*/
-static int jrnlClose(sqlite3_file *pJfd){
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- sqlite3OsClose(p->pReal);
- }
- sqlite3_free(p->zBuf);
- return SQLITE_OK;
-}
-
-/*
-** Read data from the file.
-*/
-static int jrnlRead(
- sqlite3_file *pJfd, /* The journal file from which to read */
- void *zBuf, /* Put the results here */
- int iAmt, /* Number of bytes to read */
- sqlite_int64 iOfst /* Begin reading at this offset */
-){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
- }else if( (iAmt+iOfst)>p->iSize ){
- rc = SQLITE_IOERR_SHORT_READ;
- }else{
- memcpy(zBuf, &p->zBuf[iOfst], iAmt);
- }
- return rc;
-}
-
-/*
-** Write data to the file.
-*/
-static int jrnlWrite(
- sqlite3_file *pJfd, /* The journal file into which to write */
- const void *zBuf, /* Take data to be written from here */
- int iAmt, /* Number of bytes to write */
- sqlite_int64 iOfst /* Begin writing at this offset into the file */
-){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
- rc = createFile(p);
- }
- if( rc==SQLITE_OK ){
- if( p->pReal ){
- rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
- }else{
- memcpy(&p->zBuf[iOfst], zBuf, iAmt);
- if( p->iSize<(iOfst+iAmt) ){
- p->iSize = (iOfst+iAmt);
- }
- }
- }
- return rc;
-}
-
-/*
-** Truncate the file.
-*/
-static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsTruncate(p->pReal, size);
- }else if( size<p->iSize ){
- p->iSize = size;
- }
- return rc;
-}
-
-/*
-** Sync the file.
-*/
-static int jrnlSync(sqlite3_file *pJfd, int flags){
- int rc;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsSync(p->pReal, flags);
- }else{
- rc = SQLITE_OK;
- }
- return rc;
-}
-
-/*
-** Query the size of the file in bytes.
-*/
-static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsFileSize(p->pReal, pSize);
- }else{
- *pSize = (sqlite_int64) p->iSize;
- }
- return rc;
-}
-
-/*
-** Table of methods for JournalFile sqlite3_file object.
-*/
-static struct sqlite3_io_methods JournalFileMethods = {
- 1, /* iVersion */
- jrnlClose, /* xClose */
- jrnlRead, /* xRead */
- jrnlWrite, /* xWrite */
- jrnlTruncate, /* xTruncate */
- jrnlSync, /* xSync */
- jrnlFileSize, /* xFileSize */
- 0, /* xLock */
- 0, /* xUnlock */
- 0, /* xCheckReservedLock */
- 0, /* xFileControl */
- 0, /* xSectorSize */
- 0, /* xDeviceCharacteristics */
- 0, /* xShmMap */
- 0, /* xShmLock */
- 0, /* xShmBarrier */
- 0 /* xShmUnmap */
-};
-
-/*
-** Open a journal file.
-*/
-int sqlite3JournalOpen(
- sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
- const char *zName, /* Name of the journal file */
- sqlite3_file *pJfd, /* Preallocated, blank file handle */
- int flags, /* Opening flags */
- int nBuf /* Bytes buffered before opening the file */
-){
- JournalFile *p = (JournalFile *)pJfd;
- memset(p, 0, sqlite3JournalSize(pVfs));
- if( nBuf>0 ){
- p->zBuf = sqlite3MallocZero(nBuf);
- if( !p->zBuf ){
- return SQLITE_NOMEM_BKPT;
- }
- }else{
- return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
- }
- p->pMethod = &JournalFileMethods;
- p->nBuf = nBuf;
- p->flags = flags;
- p->zJournal = zName;
- p->pVfs = pVfs;
- return SQLITE_OK;
-}
-
-/*
-** If the argument p points to a JournalFile structure, and the underlying
-** file has not yet been created, create it now.
-*/
-int sqlite3JournalCreate(sqlite3_file *p){
- if( p->pMethods!=&JournalFileMethods ){
- return SQLITE_OK;
- }
- return createFile((JournalFile *)p);
-}
-
-/*
-** The file-handle passed as the only argument is guaranteed to be an open
-** file. It may or may not be of class JournalFile. If the file is a
-** JournalFile, and the underlying file on disk has not yet been opened,
-** return 0. Otherwise, return 1.
-*/
-int sqlite3JournalExists(sqlite3_file *p){
- return (p->pMethods!=&JournalFileMethods || ((JournalFile *)p)->pReal!=0);
-}
-
-/*
-** Return the number of bytes required to store a JournalFile that uses vfs
-** pVfs to create the underlying on-disk files.
-*/
-int sqlite3JournalSize(sqlite3_vfs *pVfs){
- return (pVfs->szOsFile+sizeof(JournalFile));
-}
-#endif
-#endif
diff --git a/src/main.c b/src/main.c
index 116353abe..422b970cc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -633,6 +633,11 @@ int sqlite3_config(int op, ...){
break;
}
+ case SQLITE_CONFIG_STMTJRNL_SPILL: {
+ sqlite3GlobalConfig.nStmtSpill = va_arg(ap, int);
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
diff --git a/src/memjournal.c b/src/memjournal.c
index 9ecd2a9ba..04780df99 100644
--- a/src/memjournal.c
+++ b/src/memjournal.c
@@ -60,7 +60,7 @@ struct MemJournal {
const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
int nChunkSize; /* In-memory chunk-size */
- int nBuf; /* Bytes of data before flushing */
+ int nSpill; /* Bytes of data before flushing */
int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
@@ -109,7 +109,7 @@ static int memjrnlRead(
do {
int iSpace = p->nChunkSize - iChunkOffset;
int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
- memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
+ memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy);
zOut += nCopy;
nRead -= iSpace;
iChunkOffset = 0;
@@ -137,7 +137,7 @@ static void memjrnlFreeChunks(MemJournal *p){
/*
** Flush the contents of memory to a real file on disk.
*/
-static int createFile(MemJournal *p){
+static int memjrnlCreateFile(MemJournal *p){
int rc = SQLITE_OK;
if( !p->pReal ){
sqlite3_file *pReal = (sqlite3_file *)&p[1];
@@ -153,7 +153,7 @@ static int createFile(MemJournal *p){
nWrite = p->endpoint.iOffset % p->nChunkSize;
if( nWrite==0 ) nWrite = p->nChunkSize;
}
- rc = sqlite3OsWrite(pReal, pIter->zChunk, nWrite, iOff);
+ rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nWrite, iOff);
iOff += nWrite;
}
if( rc!=SQLITE_OK ){
@@ -192,8 +192,8 @@ static int memjrnlWrite(
}
/* If the file should be created now. */
- else if( p->nBuf>0 && (iAmt+iOfst)>p->nBuf ){
- int rc = createFile(p);
+ else if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){
+ int rc = memjrnlCreateFile(p);
if( rc==SQLITE_OK ){
rc = memjrnlWrite(pJfd, zBuf, iAmt, iOfst);
}
@@ -210,7 +210,7 @@ static int memjrnlWrite(
assert( iOfst==p->endpoint.iOffset || iOfst==0 );
if( iOfst==0 && p->pFirst ){
assert( p->nChunkSize>iAmt );
- memcpy(p->pFirst->zChunk, zBuf, iAmt);
+ memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
}else{
while( nWrite>0 ){
FileChunk *pChunk = p->endpoint.pChunk;
@@ -234,7 +234,7 @@ static int memjrnlWrite(
p->endpoint.pChunk = pNew;
}
- memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
+ memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
zWrite += iSpace;
nWrite -= iSpace;
p->endpoint.iOffset += iSpace;
@@ -333,12 +333,12 @@ static const struct sqlite3_io_methods MemJournalMethods = {
** Open a journal file.
**
** The behaviour of the journal file depends on the value of parameter
-** nBuf. If nBuf is 0, then the journal file is always create and
-** accessed using the underlying VFS. If nBuf is less than zero, then
-** all content is always stored in main-memory. Finally, if nBuf is a
+** nSpill. If nSpill is 0, then the journal file is always create and
+** accessed using the underlying VFS. If nSpill is less than zero, then
+** all content is always stored in main-memory. Finally, if nSpill is a
** positive value, then the journal file is initially created in-memory
** but may be flushed to disk later on. In this case the journal file is
-** flushed to disk either when it grows larger than nBuf bytes in size,
+** flushed to disk either when it grows larger than nSpill bytes in size,
** or when sqlite3JournalCreate() is called.
*/
int sqlite3JournalOpen(
@@ -346,28 +346,28 @@ int sqlite3JournalOpen(
const char *zName, /* Name of the journal file */
sqlite3_file *pJfd, /* Preallocated, blank file handle */
int flags, /* Opening flags */
- int nBuf /* Bytes buffered before opening the file */
+ int nSpill /* Bytes buffered before opening the file */
){
MemJournal *p = (MemJournal*)pJfd;
- /* Zero the file-handle object. If nBuf was passed zero, initialize
+ /* Zero the file-handle object. If nSpill was passed zero, initialize
** it using the sqlite3OsOpen() function of the underlying VFS. In this
** case none of the code in this module is executed as a result of calls
** made on the journal file-handle. */
memset(p, 0, sizeof(MemJournal) + (pVfs ? pVfs->szOsFile : 0));
- if( nBuf==0 ){
+ if( nSpill==0 ){
return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
}
- if( nBuf>0 ){
- p->nChunkSize = nBuf;
+ if( nSpill>0 ){
+ p->nChunkSize = nSpill;
}else{
p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
}
p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
- p->nBuf = nBuf;
+ p->nSpill = nSpill;
p->flags = flags;
p->zJournal = zName;
p->pVfs = pVfs;
@@ -385,13 +385,13 @@ void sqlite3MemJournalOpen(sqlite3_file *pJfd){
/*
** If the argument p points to a MemJournal structure that is not an
** in-memory-only journal file (i.e. is one that was opened with a +ve
-** nBuf parameter), and the underlying file has not yet been created,
+** nSpill parameter), and the underlying file has not yet been created,
** create it now.
*/
int sqlite3JournalCreate(sqlite3_file *p){
int rc = SQLITE_OK;
- if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nBuf>0 ){
- rc = createFile((MemJournal*)p);
+ if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
+ rc = memjrnlCreateFile((MemJournal*)p);
}
return rc;
}
diff --git a/src/pager.c b/src/pager.c
index c74748c8e..f045ce0ef 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -4358,11 +4358,11 @@ static int openSubJournal(Pager *pPager){
const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
| SQLITE_OPEN_DELETEONCLOSE;
- int nBuf = 64*1024;
+ int nStmtSpill = sqlite3Config.nStmtSpill;
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
- nBuf = -1;
+ nStmtSpill = -1;
}
- rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nBuf);
+ rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill);
}
return rc;
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 31202ecc2..cce23aa22 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -1820,6 +1820,20 @@ struct sqlite3_mem_methods {
** is enabled (using the [PRAGMA threads] command) and the amount of content
** to be sorted exceeds the page size times the minimum of the
** [PRAGMA cache_size] setting and this value.
+**
+** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
+** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
+** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
+** becomes the [statement journal] spill-to-disk threshold.
+** [Statement journals] are held in memory until their size (in bytes)
+** exceeds this threshold, at which point they are written to disk.
+** Or if the threshold is -1, statement journals are always held
+** exclusively in memory.
+** Since many statement journals never become large, setting the spill
+** threshold to a value such as 64KiB can greatly reduce the amount of
+** I/O required to support statement rollback.
+** The default value for this setting is controlled by the
+** [SQLITE_STMTJRNL_SPILL] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -1847,6 +1861,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
+#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 55e751574..e1e37b61d 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3053,6 +3053,7 @@ struct Sqlite3Config {
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
int nLookaside; /* Default lookaside buffer count */
+ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */
sqlite3_mem_methods m; /* Low-level memory allocation interface */
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */
@@ -3697,7 +3698,7 @@ LogEst sqlite3LogEstAdd(LogEst,LogEst);
#ifndef SQLITE_OMIT_VIRTUALTABLE
LogEst sqlite3LogEstFromDouble(double);
#endif
-#if defined(SQLITE_ENABLE_STMT_SCANSTAT) || \
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
u64 sqlite3LogEstToInt(LogEst);
diff --git a/src/test_bestindex.c b/src/test_bestindex.c
index ab10463fd..a955c2782 100644
--- a/src/test_bestindex.c
+++ b/src/test_bestindex.c
@@ -117,6 +117,40 @@ struct tcl_cursor {
};
/*
+** Dequote string z in place.
+*/
+static void tclDequote(char *z){
+ char q = z[0];
+
+ /* Set stack variable q to the close-quote character */
+ if( q=='[' || q=='\'' || q=='"' || q=='`' ){
+ int iIn = 1;
+ int iOut = 0;
+ if( q=='[' ) q = ']';
+
+ while( ALWAYS(z[iIn]) ){
+ if( z[iIn]==q ){
+ if( z[iIn+1]!=q ){
+ /* Character iIn was the close quote. */
+ iIn++;
+ break;
+ }else{
+ /* Character iIn and iIn+1 form an escaped quote character. Skip
+ ** the input cursor past both and copy a single quote character
+ ** to the output buffer. */
+ iIn += 2;
+ z[iOut++] = q;
+ }
+ }else{
+ z[iOut++] = z[iIn++];
+ }
+ }
+
+ z[iOut] = '\0';
+ }
+}
+
+/*
** This function is the implementation of both the xConnect and xCreate
** methods of the fs virtual table.
**
@@ -135,43 +169,49 @@ static int tclConnect(
char **pzErr
){
Tcl_Interp *interp = (Tcl_Interp*)pAux;
- tcl_vtab *pTab;
- const char *zCmd;
+ tcl_vtab *pTab = 0;
+ char *zCmd = 0;
Tcl_Obj *pScript = 0;
- int rc;
+ int rc = SQLITE_OK;
if( argc!=4 ){
*pzErr = sqlite3_mprintf("wrong number of arguments");
return SQLITE_ERROR;
}
- zCmd = argv[3];
+ zCmd = sqlite3_malloc(strlen(argv[3])+1);
pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab));
- if( pTab==0 ) return SQLITE_NOMEM;
- memset(pTab, 0, sizeof(tcl_vtab));
+ if( zCmd && pTab ){
+ memcpy(zCmd, argv[3], strlen(argv[3])+1);
+ tclDequote(zCmd);
+ memset(pTab, 0, sizeof(tcl_vtab));
- pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
- pTab->interp = interp;
- pTab->db = db;
- Tcl_IncrRefCount(pTab->pCmd);
+ pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
+ pTab->interp = interp;
+ pTab->db = db;
+ Tcl_IncrRefCount(pTab->pCmd);
- pScript = Tcl_DuplicateObj(pTab->pCmd);
- Tcl_IncrRefCount(pScript);
- Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
+ pScript = Tcl_DuplicateObj(pTab->pCmd);
+ Tcl_IncrRefCount(pScript);
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
- rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
- if( rc!=TCL_OK ){
- *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
- rc = SQLITE_ERROR;
- }else{
- rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
- }
+ rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+ if( rc!=TCL_OK ){
+ *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
+ }
- if( rc!=SQLITE_OK ){
- sqlite3_free(pTab);
- pTab = 0;
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pTab);
+ pTab = 0;
+ }
+ }else{
+ rc = SQLITE_NOMEM;
}
+ sqlite3_free(zCmd);
*ppVtab = &pTab->base;
return rc;
}
@@ -454,7 +494,9 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->needToFreeIdxStr = 1;
}else
if( sqlite3_stricmp("rows", zCmd)==0 ){
- rc = Tcl_GetWideIntFromObj(interp, p, &pIdxInfo->estimatedRows);
+ Tcl_WideInt x = 0;
+ rc = Tcl_GetWideIntFromObj(interp, p, &x);
+ pIdxInfo->estimatedRows = (tRowcnt)x;
}else
if( sqlite3_stricmp("use", zCmd)==0
|| sqlite3_stricmp("omit", zCmd)==0
diff --git a/src/util.c b/src/util.c
index 81274260f..6aead47fa 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1093,7 +1093,7 @@ u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
*/
int sqlite3VarintLen(u64 v){
int i;
- for(i=1; (v >>= 7)!=0; i++){ assert( i<9 ); }
+ for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); }
return i;
}
@@ -1408,7 +1408,7 @@ LogEst sqlite3LogEstFromDouble(double x){
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-#if defined(SQLITE_ENABLE_STMT_SCANSTAT) || \
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
/*
diff --git a/src/where.c b/src/where.c
index ed90f6144..ea62617c5 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2748,6 +2748,151 @@ static int whereLoopAddBtree(
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/*
+** Argument pIdxInfo is already populated with all constraints that may
+** be used by the virtual table identified by pBuilder->pNew->iTab. This
+** function marks a subset of those constraints usable, invokes the
+** xBestIndex method and adds the returned plan to pBuilder.
+**
+** A constraint is marked usable if:
+**
+** * Argument mUsable indicates that its prerequisites are available, and
+**
+** * It is not one of the operators specified in the mExclude mask passed
+** as the fourth argument (which in practice is either WO_IN or 0).
+**
+** Argument mExtra is a mask of tables that must be scanned before the
+** virtual table in question. These are added to the plans prerequisites
+** before it is added to pBuilder.
+**
+** Output parameter *pbIn is set to true if the plan added to pBuilder
+** uses one or more WO_IN terms, or false otherwise.
+*/
+static int whereLoopAddVirtualOne(
+ WhereLoopBuilder *pBuilder,
+ Bitmask mExtra, /* Mask of tables that must be used. */
+ Bitmask mUsable, /* Mask of usable prereqs */
+ u16 mExclude, /* Exclude terms for this operator */
+ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
+ int *pbIn /* OUT: True if plan uses an IN(...) op */
+){
+ WhereClause *pWC = pBuilder->pWC;
+ struct sqlite3_index_constraint *pIdxCons;
+ struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage;
+ int i;
+ int mxTerm;
+ int rc = SQLITE_OK;
+ WhereLoop *pNew = pBuilder->pNew;
+ Parse *pParse = pBuilder->pWInfo->pParse;
+ struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
+ int nConstraint = pIdxInfo->nConstraint;
+
+ assert( (mUsable & mExtra)==mExtra );
+ *pbIn = 0;
+ pNew->prereq = mExtra;
+
+ /* Set the usable flag on the subset of constraints identified by
+ ** arguments mUsable and mExclude. */
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ WhereTerm *pTerm = &pWC->a[pIdxCons->iTermOffset];
+ pIdxCons->usable = 0;
+ if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
+ && (pTerm->eOperator & mExclude)==0
+ ){
+ pIdxCons->usable = 1;
+ }
+ }
+
+ /* Initialize the output fields of the sqlite3_index_info structure */
+ memset(pUsage, 0, sizeof(pUsage[0])*nConstraint);
+ if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
+ pIdxInfo->estimatedRows = 25;
+ pIdxInfo->idxFlags = 0;
+ pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed;
+
+ /* Invoke the virtual table xBestIndex() method */
+ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
+ if( rc ) return rc;
+
+ mxTerm = -1;
+ assert( pNew->nLSlot>=nConstraint );
+ for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
+ pNew->u.vtab.omitMask = 0;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ int iTerm;
+ if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
+ WhereTerm *pTerm;
+ int j = pIdxCons->iTermOffset;
+ if( iTerm>=nConstraint
+ || j<0
+ || j>=pWC->nTerm
+ || pNew->aLTerm[iTerm]!=0
+ ){
+ rc = SQLITE_ERROR;
+ sqlite3ErrorMsg(pParse,"%s.xBestIndex() malfunction",pSrc->pTab->zName);
+ return rc;
+ }
+ testcase( iTerm==nConstraint-1 );
+ testcase( j==0 );
+ testcase( j==pWC->nTerm-1 );
+ pTerm = &pWC->a[j];
+ pNew->prereq |= pTerm->prereqRight;
+ assert( iTerm<pNew->nLSlot );
+ pNew->aLTerm[iTerm] = pTerm;
+ if( iTerm>mxTerm ) mxTerm = iTerm;
+ testcase( iTerm==15 );
+ testcase( iTerm==16 );
+ if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ /* A virtual table that is constrained by an IN clause may not
+ ** consume the ORDER BY clause because (1) the order of IN terms
+ ** is not necessarily related to the order of output terms and
+ ** (2) Multiple outputs from a single IN value will not merge
+ ** together. */
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
+ *pbIn = 1;
+ }
+ }
+ }
+
+ pNew->nLTerm = mxTerm+1;
+ assert( pNew->nLTerm<=pNew->nLSlot );
+ pNew->u.vtab.idxNum = pIdxInfo->idxNum;
+ pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pNew->u.vtab.idxStr = pIdxInfo->idxStr;
+ pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
+ pIdxInfo->nOrderBy : 0);
+ pNew->rSetup = 0;
+ pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
+ pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
+
+ /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated
+ ** that the scan will visit at most one row. Clear it otherwise. */
+ if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){
+ pNew->wsFlags |= WHERE_ONEROW;
+ }else{
+ pNew->wsFlags &= ~WHERE_ONEROW;
+ }
+ whereLoopInsert(pBuilder, pNew);
+ if( pNew->u.vtab.needFree ){
+ sqlite3_free(pNew->u.vtab.idxStr);
+ pNew->u.vtab.needFree = 0;
+ }
+
+ return SQLITE_OK;
+}
+
+
/*
** Add all WhereLoop objects for a table of the join identified by
** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
@@ -2778,167 +2923,102 @@ static int whereLoopAddVirtual(
Bitmask mExtra, /* Tables that must be scanned before this one */
Bitmask mUnusable /* Tables that must be scanned after this one */
){
+ int rc = SQLITE_OK; /* Return code */
WhereInfo *pWInfo; /* WHERE analysis context */
Parse *pParse; /* The parsing context */
WhereClause *pWC; /* The WHERE clause */
struct SrcList_item *pSrc; /* The FROM clause term to search */
- Table *pTab;
- sqlite3 *db;
- sqlite3_index_info *pIdxInfo;
- struct sqlite3_index_constraint *pIdxCons;
- struct sqlite3_index_constraint_usage *pUsage;
- WhereTerm *pTerm;
- int i, j;
- int iTerm, mxTerm;
- int nConstraint;
- int seenIn = 0; /* True if an IN operator is seen */
- int seenVar = 0; /* True if a non-constant constraint is seen */
- int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */
+ sqlite3_index_info *p; /* Object to pass to xBestIndex() */
+ int nConstraint; /* Number of constraints in p */
+ int bIn; /* True if plan uses IN(...) operator */
WhereLoop *pNew;
- int rc = SQLITE_OK;
+ Bitmask mBest; /* Tables used by best possible plan */
assert( (mExtra & mUnusable)==0 );
pWInfo = pBuilder->pWInfo;
pParse = pWInfo->pParse;
- db = pParse->db;
pWC = pBuilder->pWC;
pNew = pBuilder->pNew;
pSrc = &pWInfo->pTabList->a[pNew->iTab];
- pTab = pSrc->pTab;
- assert( IsVirtual(pTab) );
- pIdxInfo = allocateIndexInfo(pParse, pWC, mUnusable, pSrc,pBuilder->pOrderBy);
- if( pIdxInfo==0 ) return SQLITE_NOMEM_BKPT;
- pNew->prereq = 0;
+ assert( IsVirtual(pSrc->pTab) );
+ p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc,pBuilder->pOrderBy);
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
pNew->rSetup = 0;
pNew->wsFlags = WHERE_VIRTUALTABLE;
pNew->nLTerm = 0;
pNew->u.vtab.needFree = 0;
- pUsage = pIdxInfo->aConstraintUsage;
- nConstraint = pIdxInfo->nConstraint;
- if( whereLoopResize(db, pNew, nConstraint) ){
- sqlite3DbFree(db, pIdxInfo);
+ nConstraint = p->nConstraint;
+ if( whereLoopResize(pParse->db, pNew, nConstraint) ){
+ sqlite3DbFree(pParse->db, p);
return SQLITE_NOMEM_BKPT;
}
- for(iPhase=0; iPhase<=3; iPhase++){
- if( !seenIn && (iPhase&1)!=0 ){
- iPhase++;
- if( iPhase>3 ) break;
- }
- if( !seenVar && iPhase>1 ) break;
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- switch( iPhase ){
- case 0: /* Constants without IN operator */
- pIdxCons->usable = 0;
- if( (pTerm->eOperator & WO_IN)!=0 ){
- seenIn = 1;
- }
- if( (pTerm->prereqRight & ~mExtra)!=0 ){
- seenVar = 1;
- }else if( (pTerm->eOperator & WO_IN)==0 ){
- pIdxCons->usable = 1;
- }
- break;
- case 1: /* Constants with IN operators */
- assert( seenIn );
- pIdxCons->usable = (pTerm->prereqRight & ~mExtra)==0;
- break;
- case 2: /* Variables without IN */
- assert( seenVar );
- pIdxCons->usable = (pTerm->eOperator & WO_IN)==0;
- break;
- default: /* Variables with IN */
- assert( seenVar && seenIn );
- pIdxCons->usable = 1;
- break;
- }
- }
- memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
- if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
- pIdxInfo->idxStr = 0;
- pIdxInfo->idxNum = 0;
- pIdxInfo->needToFreeIdxStr = 0;
- pIdxInfo->orderByConsumed = 0;
- pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
- pIdxInfo->estimatedRows = 25;
- pIdxInfo->idxFlags = 0;
- pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed;
- rc = vtabBestIndex(pParse, pTab, pIdxInfo);
- if( rc ) goto whereLoopAddVtab_exit;
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- pNew->prereq = mExtra;
- mxTerm = -1;
- assert( pNew->nLSlot>=nConstraint );
- for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
- pNew->u.vtab.omitMask = 0;
- for(i=0; i<nConstraint; i++, pIdxCons++){
- if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
- j = pIdxCons->iTermOffset;
- if( iTerm>=nConstraint
- || j<0
- || j>=pWC->nTerm
- || pNew->aLTerm[iTerm]!=0
- ){
- rc = SQLITE_ERROR;
- sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName);
- goto whereLoopAddVtab_exit;
- }
- testcase( iTerm==nConstraint-1 );
- testcase( j==0 );
- testcase( j==pWC->nTerm-1 );
- pTerm = &pWC->a[j];
- pNew->prereq |= pTerm->prereqRight;
- assert( iTerm<pNew->nLSlot );
- pNew->aLTerm[iTerm] = pTerm;
- if( iTerm>mxTerm ) mxTerm = iTerm;
- testcase( iTerm==15 );
- testcase( iTerm==16 );
- if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
- if( (pTerm->eOperator & WO_IN)!=0 ){
- /* A virtual table that is constrained by an IN clause may not
- ** consume the ORDER BY clause because (1) the order of IN terms
- ** is not necessarily related to the order of output terms and
- ** (2) Multiple outputs from a single IN value will not merge
- ** together. */
- pIdxInfo->orderByConsumed = 0;
- pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
- }
+ /* First call xBestIndex() with all constraints usable. */
+ rc = whereLoopAddVirtualOne(pBuilder, mExtra, (Bitmask)(-1), 0, p, &bIn);
+ mBest = pNew->prereq & ~mExtra;
+
+ /* If the call to xBestIndex() with all terms enabled produced a plan
+ ** that does not require any source tables, there is no point in making
+ ** any further calls - if the xBestIndex() method is sane they will all
+ ** return the same plan anyway.
+ */
+ if( mBest ){
+ int seenZero = 0; /* True if a plan with no prereqs seen */
+ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */
+ Bitmask mPrev = 0;
+ Bitmask mBestNoIn = 0;
+
+ /* If the plan produced by the earlier call uses an IN(...) term, call
+ ** xBestIndex again, this time with IN(...) terms disabled. */
+ if( rc==SQLITE_OK && bIn ){
+ rc = whereLoopAddVirtualOne(pBuilder, mExtra, (Bitmask)-1, WO_IN, p,&bIn);
+ mBestNoIn = pNew->prereq & ~mExtra;
+ if( mBestNoIn==0 ){
+ seenZero = 1;
+ if( bIn==0 ) seenZeroNoIN = 1;
}
}
- if( i>=nConstraint ){
- pNew->nLTerm = mxTerm+1;
- assert( pNew->nLTerm<=pNew->nLSlot );
- pNew->u.vtab.idxNum = pIdxInfo->idxNum;
- pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
- pIdxInfo->needToFreeIdxStr = 0;
- pNew->u.vtab.idxStr = pIdxInfo->idxStr;
- pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
- pIdxInfo->nOrderBy : 0);
- pNew->rSetup = 0;
- pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
- pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
- /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated
- ** that the scan will visit at most one row. Clear it otherwise. */
- if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){
- pNew->wsFlags |= WHERE_ONEROW;
- }else{
- pNew->wsFlags &= ~WHERE_ONEROW;
+ /* Call xBestIndex once for each distinct value of (prereqRight & ~mExtra)
+ ** in the set of terms that apply to the current virtual table. */
+ while( rc==SQLITE_OK ){
+ int i;
+ Bitmask mNext = (Bitmask)(-1);
+ assert( mNext>0 );
+ for(i=0; i<nConstraint; i++){
+ Bitmask mThis = (
+ pWC->a[p->aConstraint[i].iTermOffset].prereqRight & ~mExtra
+ );
+ if( mThis>mPrev && mThis<mNext ) mNext = mThis;
}
- whereLoopInsert(pBuilder, pNew);
- if( pNew->u.vtab.needFree ){
- sqlite3_free(pNew->u.vtab.idxStr);
- pNew->u.vtab.needFree = 0;
+ mPrev = mNext;
+ if( mNext==(Bitmask)(-1) ) break;
+ if( mNext==mBest || mNext==mBestNoIn ) continue;
+ rc = whereLoopAddVirtualOne(pBuilder, mExtra, mNext|mExtra, 0, p, &bIn);
+ if( pNew->prereq==mExtra ){
+ seenZero = 1;
+ if( bIn==0 ) seenZeroNoIN = 1;
}
}
- }
-whereLoopAddVtab_exit:
- if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
- sqlite3DbFree(db, pIdxInfo);
+ /* If the calls to xBestIndex() in the above loop did not find a plan
+ ** that requires no source tables at all (i.e. one guaranteed to be
+ ** usable), make a call here with all source tables disabled */
+ if( rc==SQLITE_OK && seenZero==0 ){
+ rc = whereLoopAddVirtualOne(pBuilder, mExtra, mExtra, 0, p, &bIn);
+ if( bIn==0 ) seenZeroNoIN = 1;
+ }
+
+ /* If the calls to xBestIndex() have so far failed to find a plan
+ ** that requires no source tables at all and does not use an IN(...)
+ ** operator, make a final call to obtain one here. */
+ if( rc==SQLITE_OK && seenZeroNoIN==0 ){
+ rc = whereLoopAddVirtualOne(pBuilder, mExtra, mExtra, WO_IN, p, &bIn);
+ }
+ }
+
+ if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
+ sqlite3DbFree(pParse->db, p);
return rc;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/test/bestindex2.test b/test/bestindex2.test
new file mode 100644
index 000000000..7ccd61640
--- /dev/null
+++ b/test/bestindex2.test
@@ -0,0 +1,138 @@
+# 2016 March 3
+#
+# 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.
+#
+#***********************************************************************
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix bestindex2
+
+
+#-------------------------------------------------------------------------
+# Virtual table callback for table named $tbl, with the columns specified
+# by list argument $cols. e.g. if the function is invoked as:
+#
+# vtab_cmd t1 {a b c} ...
+#
+# The table created is:
+#
+# "CREATE TABLE t1 (a, b, c)"
+#
+# The tables xBestIndex method behaves as if all possible combinations of
+# "=" constraints (but no others) may be optimized. The cost of a full table
+# scan is:
+#
+# "WHERE 1" "cost 1000000 rows 1000000"
+#
+# If one or more "=" constraints are in use, the cost and estimated number
+# of rows returned are both is (11 - nCons)*1000, where nCons is the number
+# of constraints used. e.g.
+#
+# "WHERE a=? AND b=?" -> "cost 900 rows 900"
+# "WHERE c=? AND b<?" -> "cost 1000 rows 1000"
+#
+proc vtab_cmd {tbl cols method args} {
+ switch -- $method {
+ xConnect {
+ return "CREATE TABLE $tbl ([join $cols ,])"
+ }
+ xBestIndex {
+ foreach {clist orderby mask} $args {}
+
+ set cons [list]
+ set used [list]
+
+ for {set i 0} {$i < [llength $clist]} {incr i} {
+ array unset C
+ array set C [lindex $clist $i]
+ if {$C(op)=="eq" && $C(usable) && [lsearch $cons $C(column)]<0} {
+ lappend used use $i
+ lappend cons $C(column)
+ }
+ }
+
+ set nCons [llength $cons]
+ if {$nCons==0} {
+ return "cost 1000000 rows 1000000"
+ } else {
+ set cost [expr (11-$nCons) * 1000]
+ set ret [concat $used "cost $cost rows $cost"]
+
+ set txt [list]
+ foreach c $cons { lappend txt "[lindex $cols $c]=?" }
+ lappend ret idxstr "indexed([join $txt { AND }])"
+
+ return $ret
+ }
+ }
+ }
+ return ""
+}
+
+register_tcl_module db
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE t1 USING tcl("vtab_cmd t1 {a b}");
+ CREATE VIRTUAL TABLE t2 USING tcl("vtab_cmd t2 {c d}");
+ CREATE VIRTUAL TABLE t3 USING tcl("vtab_cmd t3 {e f}");
+}
+
+do_eqp_test 1.1 {
+ SELECT * FROM t1 WHERE a='abc'
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
+}
+do_eqp_test 1.2 {
+ SELECT * FROM t1 WHERE a='abc' AND b='def'
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=? AND b=?)}
+}
+do_eqp_test 1.3 {
+ SELECT * FROM t1 WHERE a='abc' AND a='def'
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
+}
+do_eqp_test 1.4 {
+ SELECT * FROM t1,t2 WHERE c=a
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
+ 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
+}
+
+do_eqp_test 1.5 {
+ SELECT * FROM t1, t2 CROSS JOIN t3 WHERE t2.c = +t1.b AND t3.e=t2.d
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
+ 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
+ 0 2 2 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)}
+}
+
+do_eqp_test 1.6 {
+ SELECT * FROM t1, t2, t3 WHERE t2.c = +t1.b AND t3.e = t2.d
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
+ 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
+ 0 2 2 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)}
+}
+
+do_execsql_test 1.7.1 {
+ CREATE TABLE x1(a, b);
+}
+do_eqp_test 1.7.2 {
+ SELECT * FROM x1 CROSS JOIN t1, t2, t3
+ WHERE t1.a = t2.c AND t1.b = t3.e
+} {
+ 0 0 0 {SCAN TABLE x1}
+ 0 1 1 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
+ 0 2 2 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
+ 0 3 3 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)}
+}
+
+finish_test
+
diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl
index 97b42a475..7e8558d2a 100644
--- a/tool/mksqlite3c.tcl
+++ b/tool/mksqlite3c.tcl
@@ -326,7 +326,6 @@ foreach file {
vdbe.c
vdbeblob.c
vdbesort.c
- journal.c
memjournal.c
walker.c