diff options
-rw-r--r-- | Makefile.in | 6 | ||||
-rw-r--r-- | Makefile.msc | 6 | ||||
-rw-r--r-- | ext/rtree/rtree.c | 2 | ||||
-rw-r--r-- | main.mk | 3 | ||||
-rw-r--r-- | manifest | 40 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/global.c | 13 | ||||
-rw-r--r-- | src/journal.c | 258 | ||||
-rw-r--r-- | src/main.c | 5 | ||||
-rw-r--r-- | src/memjournal.c | 42 | ||||
-rw-r--r-- | src/pager.c | 6 | ||||
-rw-r--r-- | src/sqlite.h.in | 15 | ||||
-rw-r--r-- | src/sqliteInt.h | 3 | ||||
-rw-r--r-- | src/test_bestindex.c | 90 | ||||
-rw-r--r-- | src/util.c | 4 | ||||
-rw-r--r-- | src/where.c | 350 | ||||
-rw-r--r-- | test/bestindex2.test | 138 | ||||
-rw-r--r-- | tool/mksqlite3c.tcl | 1 |
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); @@ -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 \ @@ -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 |