diff --git a/ext/fts5/tool/mkfts5c.tcl b/ext/fts5/tool/mkfts5c.tcl index b1a55fa4ae..9ea34a01e2 100644 --- a/ext/fts5/tool/mkfts5c.tcl +++ b/ext/fts5/tool/mkfts5c.tcl @@ -23,7 +23,27 @@ set G(src) [string map [list %dir% $srcdir] { }] set G(hdr) { - +/* +** This, the "fts5.c" source file, is a composite file that is itself +** assembled from the following files: +** +** fts5.h +** fts5Int.h +** fts5parse.h <--- Generated from fts5parse.y by Lemon +** fts5parse.c <--- Generated from fts5parse.y by Lemon +** fts5_aux.c +** fts5_buffer.c +** fts5_config.c +** fts5_expr.c +** fts5_hash.c +** fts5_index.c +** fts5_main.c +** fts5_storage.c +** fts5_tokenize.c +** fts5_unicode2.c +** fts5_varint.c +** fts5_vocab.c +*/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) @@ -33,10 +53,16 @@ set G(hdr) { # undef NDEBUG #endif +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif } set G(footer) { - +/* Here ends the fts5.c composite file. */ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ } diff --git a/manifest b/manifest index 7b770e633b..96fccfbf92 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\sversion\snumber\sfor\sthe\sTEA\sextension. -D 2024-10-24T11:11:31.638 +C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\sautosetup\sbranch. +D 2024-10-24T11:20:25.409 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -273,7 +273,7 @@ F ext/fts5/test/fts5vocab2.test bbba149c254375d00055930c1a501c9a51e80b0d20bf7b98 F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85 F ext/fts5/tool/fts5txt2db.tcl c0d43c8590656f8240e622b00957b3a0facc49482411a9fdc2870b45c0c82f9f F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093 -F ext/fts5/tool/mkfts5c.tcl 3eba8e9bee4221ed165f3304b51b2a74a705f4ec5df3d044573a2be539534af8 +F ext/fts5/tool/mkfts5c.tcl 6649ed963a9135e36866f7cc9f8de5c8dcec85b5df089388274cee6381702cb7 F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/icu.c 3add8197e0a86c1761771a39500ebae749438bcf1836160b407a56b4eaa8721c @@ -771,10 +771,10 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c 14d0f80e4779f5f76bcc71e7af97e3efe318fa3d0b22335623c59fab7d39302b -F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a +F src/os_unix.c 0ad4e0885294b3a0e135a18533590ec9ad91ffe82f6a08e55b40babd51772928 +F src/os_win.c 69fa1aaff68270423c85cff4327ba17ef99a1eb017e1a2bfb97416d9b8398b05 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c b08600ebf0db90b6d1e9b8b6577c6fa3877cbe1a100bd0b2899e4c6e9adad4b3 +F src/pager.c 9656ad4e8331efb8a4f94f7a0c6440b98caea073950a367ea0c728a53b8e62c9 F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a F src/parse.y a7a8d42eeff01d267444ddb476029b0b1726fb70ae3d77984140f17ad02e2d61 F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484 @@ -789,15 +789,15 @@ F src/resolve.c c8a5372b97b2a2e972a280676f06ddb5b74e885d3b1f5ce383f839907b57ef68 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 4b14337a2742f0c0beeba490e9a05507e9b4b12184b9cd12773501d08d48e3fe F src/shell.c.in 0662f9bcf0725461778d0254a06150e5d61c08c5a87a7281ccdf45552050c79d -F src/sqlite.h.in add9e064d6b42af8f1a4a3322bddadec76696e520aedebd83e0d3211c15ac999 +F src/sqlite.h.in 29fc900a58f394c7488d093fd7a8dcb14d3fa6399d5178cb20adcf88dbedfe39 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 F src/sqliteInt.h baae24292817e13e7fe748851c62efc381dcc4dac241b1182eac3d2f05eae52c F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 -F src/tclsqlite.c e2b752dd08034c834e3639afa3300940d6e847fcd94207e1f2f1b83ce08b87be -F src/tclsqlite.h c6af51f31a2b2172d674608763a4b98fdf5cd587e4025053e546fb8077757262 +F src/tclsqlite.c 47d4bb6eb06aab48d643eeb8c4f65b5fa9529fa5526fdcbf223ea32277ed1b56 +F src/tclsqlite.h 529047feec49e7f463374749147f64d3f17505b0ebd84b3477a364c6f46a9de1 F src/test1.c 8bf8b74145b768f42386787f93f6d6dad7bc400a4ee2d50e4ad5a06a20a97ef1 F src/test2.c 7ebc518e6735939d8979273a6f7b1d9b5702babf059f6ad62499f7f60a9eb9a3 F src/test3.c e7573aa0f78ee4e070a4bc8c3493941c1aa64d5c66d4825c74c0f055451f432b @@ -872,9 +872,9 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 8b7e309a8012659ac9275ad8cdcc6acaf73fa04b1090e38a01335f230fd10681 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014 -F src/where.c ed0e1e9ef15f01f5da54168ae2fdb5eb789a881fd20d7b4820e38454dedf9947 -F src/whereInt.h a5d079c346a658b7a6e9e47bb943d021e02fa1e6aed3b964ca112112a4892192 -F src/wherecode.c 5172d647798134e7c92536ddffe7e530c393d79b5dedd648b88faf2646c65baf +F src/where.c 4de9e7ca5f49e4a21c1d733e2b2fbbc8b62b1a157a58a562c569da84cfcb005b +F src/whereInt.h 1e36ec50392f7cc3d93d1152d4338064cd522b87156a0739388b7e273735f0ca +F src/wherecode.c 81b9af89f4f85c8097d0da6a31499f015eabc4d3584963d30ba7b7b782e26514 F src/whereexpr.c 0f93a29cabd3a338d09a1f5c6770620a1ac51ec1157f3229502a7e7767c60b6f F src/window.c 499d48f315a09242dc68f2fac635ed27dcf6bbb0d9ab9084857898c64489e975 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 @@ -1340,9 +1340,9 @@ F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a91 F test/index9.test 2ac891806a4136ef3e91280477e23114e67575207dc331e6797fa0ed9379f997 F test/indexA.test 11d84f6995e6e5b9d8315953fb1b6d29772ee7c7803ee9112715e7e4dd3e4974 F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0 -F test/indexexpr1.test 24fa85a12da384dd1d56f7b24e593c51a8a54b4c5e2e8bbb9e5fdf1099427faf +F test/indexexpr1.test 928671af9d7374bb56ed4dcfbc157f4eeddb1e86ab5615ceb3ac97a713c2dd8f F test/indexexpr2.test 1c382e81ef996d8ae8b834a74f2a9013dddf59214c32201d7c8a656d739f999a -F test/indexexpr3.test 9d893bf440937ebcc1e59c7c9c1505c40c918346a3ddde76a69078f3c733c45d +F test/indexexpr3.test 47b91bc7999805c9a34d356f672259bc49295ecc797448511cae554a309b47cd F test/indexfault.test 98d78a8ff1f5335628b62f886a1cb7c7dac1ef6d48fa39c51ec871c87dce9811 F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 F test/insert.test 4e3f0de67aac3c5be1f4aaedbcea11638f1b5cdc9a3115be14d19aa9db7623c6 @@ -1736,7 +1736,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 7b44f1a9b9a2de8112695b908afc21dd9a68cd2d44e84b73f1b27b53492c0d59 -F test/testrunner.tcl 3dd75b45593d2afa2e3bca76121297a9f163bebb98474b13536f326829a71db1 x +F test/testrunner.tcl bc1a8d21a1aa3a5cf7c4883cbee4b6748790fe960fad06ca5db74ec914bd6525 x F test/testrunner_data.tcl c7b3b911e44f7e8c01cc6bc7571e16115cdc2e4db46630bd2acd7a931a46380e F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2197,7 +2197,7 @@ F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d F tool/split-sqlite3c.tcl 5aa60643afca558bc732b1444ae81a522326f91e1dc5665b369c54f09e20de60 F tool/sqldiff.c 2a0987d183027c795ced13d6749061c1d2f38e24eddb428f56fa64c3a8f51e4b F tool/sqlite3_analyzer.c.in 348ba349bbdc93c9866439f9f935d7284866a2a4e6898bc906ae1204ade56918 -F tool/sqlite3_rsync.c d832c69265d32d3694e469d5ccb2da03360d2088d7871743a76a32f71854ad91 +F tool/sqlite3_rsync.c 6c9cac5a9197f591985b271aeff803ec4fb4db36c8eab97e1331ff64aa1b8d94 F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898 F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848 F tool/src-verify.c d00f93263aa2fa6ba0cba0106d95458e6effb94fdb5fc634f56834f90c05bbb4 @@ -2236,8 +2236,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 58a0f7e79ce913b432dfd4db018e5a92efa8a6bd8a50bd78ab705ceac0322e9c -R c66389447c3b7b2175096c4d28a4e3c7 +P 56353bcffa1bad425b1645b09e7192aa418d6b989df5958e0a770226bfc2ce63 da9124fee28c155c4d1cc0d3949eb7b588a7236c12883a010af7909ad8e534ef +R cc1b154ed902ce297ea76c4a560a28a4 U drh -Z 8cb6d5a8498acf453471a00efb00ba92 +Z 11c0afa471c73db054eb415d8e2d3805 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4089841f69..54623038d2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -56353bcffa1bad425b1645b09e7192aa418d6b989df5958e0a770226bfc2ce63 +d0554ac46866b5ad467b88de6fae06695fd81e7fdf38157702a395f7282cbe9d diff --git a/src/os_unix.c b/src/os_unix.c index f71fa8e029..c13c5cece0 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4128,6 +4128,7 @@ static void setDeviceCharacteristics(unixFile *pFd){ if( pFd->ctrlFlags & UNIXFILE_PSOW ){ pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; } + pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ; pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; } diff --git a/src/os_win.c b/src/os_win.c index 442c108e9d..97743412e9 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -3660,7 +3660,7 @@ static int winSectorSize(sqlite3_file *id){ */ static int winDeviceCharacteristics(sqlite3_file *id){ winFile *p = (winFile*)id; - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ | ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); } diff --git a/src/pager.c b/src/pager.c index 1ac858a070..4f616f0c7f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -808,20 +808,28 @@ static const unsigned char aJournalMagic[] = { ** Return true if page pgno can be read directly from the database file ** by the b-tree layer. This is the case if: ** -** * the database file is open, -** * there are no dirty pages in the cache, and -** * the desired page is not currently in the wal file. +** (1) the database file is open +** (2) the VFS for the database is able to do unaligned sub-page reads +** (3) there are no dirty pages in the cache, and +** (4) the desired page is not currently in the wal file. */ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ - if( pPager->fd->pMethods==0 ) return 0; - if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; + assert( pPager!=0 ); + assert( pPager->fd!=0 ); + if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */ + if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */ #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return iRead==0; + return iRead==0; /* Condition (4) */ } #endif + assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); + if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) + & SQLITE_IOCAP_SUBPAGE_READ)==0 ){ + return 0; /* Case (2) */ + } return 1; } #endif diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 7097e6bb55..f91088a772 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -652,6 +652,13 @@ int sqlite3_exec( ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. +** +** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read +** from the database file in amounts that are not a multiple of the +** page size and that do not begin at a page boundary. Without this +** property, SQLite is careful to only do full-page reads and write +** on aligned pages, with the one exception that it will do a sub-page +** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 @@ -668,6 +675,7 @@ int sqlite3_exec( #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 +#define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels @@ -814,6 +822,7 @@ struct sqlite3_file { **
  • [SQLITE_IOCAP_POWERSAFE_OVERWRITE] **
  • [SQLITE_IOCAP_IMMUTABLE] **
  • [SQLITE_IOCAP_BATCH_ATOMIC] +**
  • [SQLITE_IOCAP_SUBPAGE_READ] ** ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 9ed6669515..e572d1d6c0 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -47,7 +47,7 @@ /* Compatability between Tcl8.6 and Tcl9.0 */ #if TCL_MAJOR_VERSION==9 # define CONST const -#else +#elif !defined(Tcl_Size) typedef int Tcl_Size; #endif /**** End copy of tclsqlite.h ****/ diff --git a/src/tclsqlite.h b/src/tclsqlite.h index b9a948ef14..9997e4de26 100644 --- a/src/tclsqlite.h +++ b/src/tclsqlite.h @@ -35,7 +35,7 @@ /* Compatability between Tcl8.6 and Tcl9.0 */ #if TCL_MAJOR_VERSION==9 # define CONST const -#else +#elif !defined(Tcl_Size) typedef int Tcl_Size; #endif diff --git a/src/where.c b/src/where.c index ff22c5e4b1..f24cf7f769 100644 --- a/src/where.c +++ b/src/where.c @@ -7441,14 +7441,28 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; OpcodeRewriteTrace(db, k, pOp); - }else{ - /* Unable to translate the table reference into an index - ** reference. Verify that this is harmless - that the - ** table being referenced really is open. - */ + }else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ if( pLoop->wsFlags & WHERE_IDX_ONLY ){ + /* An error. pLoop is supposed to be a covering index loop, + ** and yet the VM code refers to a column of the table that + ** is not part of the index. */ sqlite3ErrorMsg(pParse, "internal query planner error"); pParse->rc = SQLITE_INTERNAL; + }else{ + /* The WHERE_EXPRIDX flag is set by the planner when it is likely + ** that pLoop is a covering index loop, but it is not possible + ** to be 100% sure. In this case, any OP_Explain opcode + ** corresponding to this loop describes the index as a "COVERING + ** INDEX". But, pOp proves that pLoop is not actually a covering + ** index loop. So clear the WHERE_EXPRIDX flag and rewrite the + ** text that accompanies the OP_Explain opcode, if any. */ + pLoop->wsFlags &= ~WHERE_EXPRIDX; + sqlite3WhereAddExplainText(pParse, + pLevel->addrBody-1, + pTabList, + pLevel, + pWInfo->wctrlFlags + ); } } }else if( pOp->opcode==OP_Rowid ){ diff --git a/src/whereInt.h b/src/whereInt.h index 8247528a93..f262b0eebc 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -533,9 +533,17 @@ int sqlite3WhereExplainBloomFilter( const WhereInfo *pWInfo, /* WHERE clause */ const WhereLevel *pLevel /* Bloom filter on this level */ ); +void sqlite3WhereAddExplainText( + Parse *pParse, /* Parse context */ + int addr, + SrcList *pTabList, /* Table list this loop refers to */ + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ +); #else # define sqlite3WhereExplainOneScan(u,v,w,x) 0 # define sqlite3WhereExplainBloomFilter(u,v,w) 0 +# define sqlite3WhereAddExplainText(u,v,w,x,y) #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS void sqlite3WhereAddScanStatus( diff --git a/src/wherecode.c b/src/wherecode.c index 0951e5e204..0bd1733b76 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -110,38 +110,38 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){ } /* -** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN -** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG -** was defined at compile-time. If it is not a no-op, a single OP_Explain -** opcode is added to the output to describe the table scan strategy in pLevel. -** -** If an OP_Explain opcode is added to the VM, its address is returned. -** Otherwise, if no OP_Explain is coded, zero is returned. +** This function sets the P4 value of an existing OP_Explain opcode to +** text describing the loop in pLevel. If the OP_Explain opcode already has +** a P4 value, it is freed before it is overwritten. */ -int sqlite3WhereExplainOneScan( +void sqlite3WhereAddExplainText( Parse *pParse, /* Parse context */ + int addr, /* Address of OP_Explain opcode */ SrcList *pTabList, /* Table list this loop refers to */ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ){ - int ret = 0; #if !defined(SQLITE_DEBUG) if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) #endif { + VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr); + SrcItem *pItem = &pTabList->a[pLevel->iFrom]; - Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ int isSearch; /* True for a SEARCH. False for SCAN. */ WhereLoop *pLoop; /* The controlling WhereLoop object */ u32 flags; /* Flags that describe this loop */ +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) char *zMsg; /* Text to add to EQP output */ +#endif StrAccum str; /* EQP output string */ char zBuf[100]; /* Initial space for EQP output string */ + if( db->mallocFailed ) return; + pLoop = pLevel->pWLoop; flags = pLoop->wsFlags; - if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0; isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) @@ -165,7 +165,7 @@ int sqlite3WhereExplainOneScan( zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; }else if( flags & WHERE_AUTO_INDEX ){ zFmt = "AUTOMATIC COVERING INDEX"; - }else if( flags & WHERE_IDX_ONLY ){ + }else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){ zFmt = "COVERING INDEX %s"; }else{ zFmt = "INDEX %s"; @@ -217,11 +217,50 @@ int sqlite3WhereExplainOneScan( sqlite3_str_append(&str, " (~1 row)", 9); } #endif +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) zMsg = sqlite3StrAccumFinish(&str); sqlite3ExplainBreakpoint("",zMsg); - ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), - pParse->addrExplain, pLoop->rRun, - zMsg, P4_DYNAMIC); +#endif + + assert( pOp->opcode==OP_Explain ); + assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 ); + sqlite3DbFree(db, pOp->p4.z); + pOp->p4type = P4_DYNAMIC; + pOp->p4.z = sqlite3StrAccumFinish(&str); + } +} + + +/* +** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN +** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG +** was defined at compile-time. If it is not a no-op, a single OP_Explain +** opcode is added to the output to describe the table scan strategy in pLevel. +** +** If an OP_Explain opcode is added to the VM, its address is returned. +** Otherwise, if no OP_Explain is coded, zero is returned. +*/ +int sqlite3WhereExplainOneScan( + Parse *pParse, /* Parse context */ + SrcList *pTabList, /* Table list this loop refers to */ + WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ + u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ +){ + int ret = 0; +#if !defined(SQLITE_DEBUG) + if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) ) +#endif + { + if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0 + && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 + ){ + Vdbe *v = pParse->pVdbe; + int addr = sqlite3VdbeCurrentAddr(v); + ret = sqlite3VdbeAddOp3( + v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun + ); + sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags); + } } return ret; } @@ -320,9 +359,10 @@ void sqlite3WhereAddScanStatus( } }else{ int addr; + VdbeOp *pOp; assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; - VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1); + pOp = sqlite3VdbeGetOp(v, addr-1); assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); diff --git a/test/indexexpr1.test b/test/indexexpr1.test index 3ba59449d5..d5c47e403e 100644 --- a/test/indexexpr1.test +++ b/test/indexexpr1.test @@ -49,7 +49,7 @@ do_execsql_test indexexpr1-130 { do_execsql_test indexexpr1-130eqp { EXPLAIN QUERY PLAN SELECT c FROM t1 WHERE b=1 AND substr(a,2,3)='nd_' ORDER BY c; -} {/USING INDEX t1ba/} +} {/USING COVERING INDEX t1ba/} do_execsql_test indexexpr1-140 { SELECT rowid, substr(a,b,3), '|' FROM t1 ORDER BY 2; @@ -61,7 +61,7 @@ do_execsql_test indexexpr1-141 { do_execsql_test indexexpr1-141eqp { EXPLAIN QUERY PLAN SELECT rowid FROM t1 WHERE substr(a,b,3)<='and' ORDER BY +rowid; -} {/USING INDEX t1abx/} +} {/USING COVERING INDEX t1abx/} do_execsql_test indexexpr1-142 { SELECT rowid FROM t1 WHERE +substr(a,b,3)<='and' ORDER BY +rowid; } {1 2 3} @@ -73,7 +73,7 @@ do_execsql_test indexexpr1-150eqp { EXPLAIN QUERY PLAN SELECT rowid FROM t1 WHERE substr(a,b,3) IN ('and','l_t','xyz') ORDER BY +rowid; -} {/USING INDEX t1abx/} +} {/USING COVERING INDEX t1abx/} ifcapable altertable { do_execsql_test indexexpr1-160 { @@ -99,14 +99,14 @@ do_execsql_test indexexpr1-170 { do_execsql_test indexexpr1-170eqp { EXPLAIN QUERY PLAN SELECT length(a) FROM t1 ORDER BY length(a); -} {/SCAN t1 USING INDEX t1alen/} +} {/SCAN t1 USING COVERING INDEX t1alen/} do_execsql_test indexexpr1-171 { SELECT length(a) FROM t1 ORDER BY length(a) DESC; } {52 38 29 27 25 20} do_execsql_test indexexpr1-171eqp { EXPLAIN QUERY PLAN SELECT length(a) FROM t1 ORDER BY length(a) DESC; -} {/SCAN t1 USING INDEX t1alen/} +} {/SCAN t1 USING COVERING INDEX t1alen/} do_execsql_test indexexpr1-200 { DROP TABLE t1; @@ -142,7 +142,7 @@ do_execsql_test indexexpr1-230 { do_execsql_test indexexpr1-230eqp { EXPLAIN QUERY PLAN SELECT c FROM t1 WHERE b=1 AND substr(a,2,3)='nd_' ORDER BY c; -} {/USING INDEX t1ba/} +} {/USING COVERING INDEX t1ba/} do_execsql_test indexexpr1-240 { SELECT id, substr(a,b,3), '|' FROM t1 ORDER BY 2; @@ -154,7 +154,7 @@ do_execsql_test indexexpr1-241 { do_execsql_test indexexpr1-241eqp { EXPLAIN QUERY PLAN SELECT id FROM t1 WHERE substr(a,b,3)<='and' ORDER BY +id; -} {/USING INDEX t1abx/} +} {/USING COVERING INDEX t1abx/} do_execsql_test indexexpr1-242 { SELECT id FROM t1 WHERE +substr(a,b,3)<='and' ORDER BY +id; } {1 2 3} @@ -166,7 +166,7 @@ do_execsql_test indexexpr1-250eqp { EXPLAIN QUERY PLAN SELECT id FROM t1 WHERE substr(a,b,3) IN ('and','l_t','xyz') ORDER BY +id; -} {/USING INDEX t1abx/} +} {/USING COVERING INDEX t1abx/} ifcapable altertable { do_execsql_test indexexpr1-260 { @@ -238,7 +238,7 @@ do_execsql_test indexexpr1-510 { do_execsql_test indexexpr1-510eqp { EXPLAIN QUERY PLAN SELECT substr(a,4,3) AS k FROM cnt, t5 WHERE k=printf('%03d',x); -} {/USING INDEX t5ax/} +} {/USING COVERING INDEX t5ax/} # Skip-scan on an indexed expression # @@ -547,7 +547,7 @@ do_execsql_test indexexpr1-2030 { (3, '{"x":1}', 6, 7); CREATE INDEX t1x ON t1(d, a, b->>'x', c); } -do_execsql_test indexexpr1-2030 { +do_execsql_test indexexpr1-2040 { SELECT a, SUM(1) AS t1, SUM(CASE WHEN b->>'x'=1 THEN 1 END) AS t2, @@ -555,7 +555,7 @@ do_execsql_test indexexpr1-2030 { SUM(CASE WHEN b->>'x'=1 THEN c END) AS t4 FROM t1; } {1 6 4 54 46} -do_execsql_test indexexpr1-2030 { +do_execsql_test indexexpr1-2050 { explain query plan SELECT a, SUM(1) AS t1, @@ -563,7 +563,7 @@ do_execsql_test indexexpr1-2030 { SUM(c) AS t3, SUM(CASE WHEN b->>'x'=1 THEN c END) AS t4 FROM t1; -} {/.*SCAN t1 USING INDEX t1x.*/} +} {/.*SCAN t1 USING COVERING INDEX t1x.*/} reset_db do_execsql_test indexexpr1-2100 { diff --git a/test/indexexpr3.test b/test/indexexpr3.test index 21e1d329ad..76d3331f75 100644 --- a/test/indexexpr3.test +++ b/test/indexexpr3.test @@ -78,6 +78,44 @@ do_hasfunction_test 1.6 { 2 {{"y":"two"}} } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, j); + CREATE INDEX i1 ON t1( a, json_extract(j, '$.x') ); +} + +do_eqp_test 2.1 { + SELECT json_extract(j, '$.x') FROM t1 WHERE a=? +} { + t1 USING COVERING INDEX i1 +} + +do_eqp_test 2.2 { + SELECT b, json_extract(j, '$.x') FROM t1 WHERE a=? +} { + t1 USING INDEX i1 +} + +do_eqp_test 2.3 { + SELECT json_insert( '{}', json_extract(j, '$.x') ) FROM t1 WHERE a=? +} { + t1 USING INDEX i1 +} + +do_eqp_test 2.4 { + SELECT sum( json_extract(j, '$.x') ) FROM t1 WHERE a=? +} { + t1 USING COVERING INDEX i1 +} + +do_eqp_test 2.5 { + SELECT json_extract(j, '$.x'), sum( json_extract(j, '$.x') ) FROM t1 WHERE a=? +} { + t1 USING INDEX i1 +} + + finish_test diff --git a/test/testrunner.tcl b/test/testrunner.tcl index a4a71a85e0..964465d9b2 100755 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -91,6 +91,7 @@ Usage: $a0 njob ?NJOB? $a0 script ?-msvc? CONFIG $a0 status ?-d SECS? ?--cls? + $a0 halt where SWITCHES are: --buildonly Build test exes but do not run tests @@ -245,6 +246,15 @@ switch -nocase -glob -- $tcl_platform(os) { set TRG(run) run.sh set TRG(runcmd) "bash run.sh" } + *openbsd* { + set TRG(platform) linux + set TRG(make) make.sh + set TRG(makecmd) "sh make.sh" + set TRG(testfixture) testfixture + set TRG(shell) sqlite3 + set TRG(run) run.sh + set TRG(runcmd) "sh run.sh" + } *win* { set TRG(platform) win set TRG(make) make.bat @@ -315,7 +325,9 @@ set TRG(schema) { /* Fields updated as jobs run */ starttime INTEGER, -- Start time (milliseconds since 1970) endtime INTEGER, -- End time - state TEXT CHECK( state IN ('','ready','running','done','failed','omit') ), + span INTEGER, -- Total run-time in milliseconds + estwork INTEGER, -- Estimated amount of work + state TEXT CHECK( state IN ('','ready','running','done','failed','omit','halt') ), ntest INT, -- Number of test cases run nerr INT, -- Number of errors reported svers TEXT, -- Reported SQLite version @@ -333,6 +345,13 @@ set TRG(schema) { } #------------------------------------------------------------------------- +# Estimated amount of work required by displaytype, relative to 'tcl' +# +set estwork(tcl) 1 +set estwork(fuzz) 11 +set estwork(bld) 56 +set estwork(make) 97 + #-------------------------------------------------------------------------- # Check if this script is being invoked to run a single file. If so, # run it. @@ -406,6 +425,19 @@ if {([llength $argv]==2 || [llength $argv]==1) } #-------------------------------------------------------------------------- +#-------------------------------------------------------------------------- +# Check if this is the "halt" command: +# +if {[llength $argv]==1 + && [string compare -nocase halt [lindex $argv 0]]==0 +} { + sqlite3 mydb $TRG(dbname) + mydb eval {UPDATE jobs SET state='halt' WHERE state IN ('ready','')} + mydb close + exit +} +#-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- # Check if this is the "help" command: # @@ -486,19 +518,21 @@ proc show_status {db cls} { }] set total 0 - foreach s {"" ready running done failed} { set S($s) 0 } + foreach s {"" ready running done failed omit} { set S($s) 0; set W($s) 0; } + set workpending 0 $db eval { - SELECT state, count(*) AS cnt FROM jobs GROUP BY 1 + SELECT state, count(*) AS cnt, sum(estwork) AS ew FROM jobs GROUP BY 1 } { incr S($state) $cnt - incr total $cnt + incr W($state) $ew + incr totalw $ew } set nt 0 set ne 0 $db eval { SELECT sum(ntest) AS nt, sum(nerr) AS ne FROM jobs HAVING nt>0 } break - set fin [expr $S(done)+$S(failed)] + set fin [expr $W(done)+$W(failed)+$W(omit)] if {$cmdline!=""} {set cmdline " $cmdline"} if {$cls} { @@ -513,15 +547,12 @@ proc show_status {db cls} { set srcdir [file dirname [file dirname $TRG(info_script)]] set line "Running: $S(running) (max: $nJob)" - if {$S(running)>0 && $fin>100 && $fin>0.05*$total} { - # Only estimate the time remaining after completing at least 100 - # jobs amounting to 10% of the total. Never estimate less than - # 2% of the total time used so far. - set tmleft [expr {($tm/$fin)*($total-$fin)}] + if {$S(running)>0 && $fin>10} { + set tmleft [expr {($tm/$fin)*($totalw-$fin)}] if {$tmleft<0.02*$tm} { set tmleft [expr {$tm*0.02}] } - append line " est time left [elapsetime $tmleft]" + append line " ETC [elapsetime $tmleft]" } puts [format %-79.79s $line] if {$S(running)>0} { @@ -905,6 +936,7 @@ proc r_get_next_job {iJob} { # Returns the jobid value for the new job. # proc add_job {args} { + global estwork set options { -displaytype -displayname -build -dirname @@ -929,25 +961,29 @@ proc add_job {args} { set state "" if {$A(-depid)==""} { set state ready } + set type $A(-displaytype) + set ew $estwork($type) trdb eval { INSERT INTO jobs( - displaytype, displayname, build, dirname, cmd, depid, priority, + displaytype, displayname, build, dirname, cmd, depid, priority, estwork, state ) VALUES ( - $A(-displaytype), + $type, $A(-displayname), $A(-build), $A(-dirname), $A(-cmd), $A(-depid), $A(-priority), + $ew, $state ) } trdb last_insert_rowid } + # Argument $build is either an empty string, or else a list of length 3 # describing the job to build testfixture. In the usual form: @@ -1274,8 +1310,8 @@ proc make_new_testset {} { trdb eval { REPLACE INTO config VALUES('start', $tm ); } add_jobs_from_cmdline $TRG(patternlist) - } + } } proc mark_job_as_finished {jobid output state endtm} { @@ -1300,10 +1336,12 @@ proc mark_job_as_finished {jobid output state endtm} { if {[info exists pltfm]} {set pltfm [string trim $pltfm]} trdb eval { UPDATE jobs - SET output=$output, state=$state, endtime=$endtm, + SET output=$output, state=$state, endtime=$endtm, span=$endtm-starttime, ntest=$ntest, nerr=$nerr, svers=$svers, pltfm=$pltfm WHERE jobid=$jobid; - UPDATE jobs SET state=$childstate WHERE depid=$jobid; + UPDATE jobs SET state=$childstate WHERE depid=$jobid AND state!='halt'; + UPDATE config SET value=value+$nerr WHERE name='nfail'; + UPDATE config SET value=value+$ntest WHERE name='ntest'; } } } @@ -1451,17 +1489,23 @@ proc progress_report {} { show_status trdb 1 } } else { - set tm [expr [clock_milliseconds] - $TRG(starttime)] - set tm [format "%d" [expr int($tm/1000.0 + 0.5)]] + set tmms [expr [clock_milliseconds] - $TRG(starttime)] + set tm [format "%d" [expr int($tmms/1000.0 + 0.5)]] + set wtotal 0 + set wdone 0 r_write_db { trdb eval { - SELECT displaytype, state, count(*) AS cnt + SELECT displaytype, state, count(*) AS cnt, sum(estwork) AS ew FROM jobs GROUP BY 1, 2 } { set v($state,$displaytype) $cnt incr t($displaytype) $cnt + incr wtotal $ew + if {$state=="done" || $state=="failed" || $state=="omit"} { + incr wdone $ew + } } } @@ -1477,11 +1521,15 @@ proc progress_report {} { lappend text "r$v(running,$j)" } } + if {$wdone>0} { + set tmleft [expr {($tmms/$wdone)*($wtotal-$wdone)}] + append text " ETC [elapsetime $tmleft]" + } if {[info exists TRG(reportlength)]} { puts -nonewline "[string repeat " " $TRG(reportlength)]\r" } - set report "${tm} [join $text { }]" + set report "[elapsetime $tmms] [join $text { }]" set TRG(reportlength) [string length $report] if {[string length $report]<100} { puts -nonewline "$report\r" diff --git a/tool/sqlite3_rsync.c b/tool/sqlite3_rsync.c index ae8107c8cd..7a453b6cc3 100644 --- a/tool/sqlite3_rsync.c +++ b/tool/sqlite3_rsync.c @@ -93,6 +93,7 @@ struct SQLiteRsync { ****************************************************************************/ #ifdef _WIN32 #include +#include #include /* ** Print a fatal error and quit. @@ -240,9 +241,9 @@ static int popen2( hStdinRd, hStdoutWr, hStderr,&childPid); *pChildPid = childPid; fd = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0); - *ppIn = fdopen(fd, "r"); + *ppIn = fdopen(fd, "rb"); fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0); - *ppOut = _fdopen(fd, "w"); + *ppOut = _fdopen(fd, "wb"); CloseHandle(hStdinRd); CloseHandle(hStdoutWr); return 0; @@ -1756,6 +1757,10 @@ int main(int argc, char const * const *argv){ ctx.pIn = stdin; ctx.pOut = stdout; ctx.isRemote = 1; +#ifdef _WIN32 + _setmode(_fileno(ctx.pIn), _O_BINARY); + _setmode(_fileno(ctx.pOut), _O_BINARY); +#endif originSide(&ctx); return 0; } @@ -1763,6 +1768,10 @@ int main(int argc, char const * const *argv){ ctx.pIn = stdin; ctx.pOut = stdout; ctx.isRemote = 1; +#ifdef _WIN32 + _setmode(_fileno(ctx.pIn), _O_BINARY); + _setmode(_fileno(ctx.pOut), _O_BINARY); +#endif replicaSide(&ctx); return 0; }