diff options
-rw-r--r-- | ext/fts5/fts5_index.c | 2 | ||||
-rw-r--r-- | ext/misc/vtablog.c | 48 | ||||
-rw-r--r-- | manifest | 24 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/btree.c | 4 | ||||
-rw-r--r-- | src/expr.c | 29 | ||||
-rw-r--r-- | src/select.c | 1 | ||||
-rw-r--r-- | src/whereexpr.c | 20 | ||||
-rw-r--r-- | test/bestindexC.test | 74 |
9 files changed, 171 insertions, 33 deletions
diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 1c3053386..5a5063748 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -5847,7 +5847,7 @@ static Fts5Structure *fts5IndexOptimizeStruct( } nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); - assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); + assert( nByte==(i64)SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); if( pNew ){ diff --git a/ext/misc/vtablog.c b/ext/misc/vtablog.c index e8f084e1b..e8e29a86e 100644 --- a/ext/misc/vtablog.c +++ b/ext/misc/vtablog.c @@ -436,6 +436,39 @@ static int vtablogFilter( } /* +** Return an sqlite3_index_info operator name in static space. +** The name is possibly overwritten on subsequent calls. +*/ +static char *vtablogOpName(unsigned char op){ + static char zUnknown[30]; + char *zOut; + switch( op ){ + case SQLITE_INDEX_CONSTRAINT_EQ: zOut = "EQ"; break; + case SQLITE_INDEX_CONSTRAINT_GT: zOut = "GT"; break; + case SQLITE_INDEX_CONSTRAINT_LE: zOut = "LE"; break; + case SQLITE_INDEX_CONSTRAINT_LT: zOut = "LT"; break; + case SQLITE_INDEX_CONSTRAINT_GE: zOut = "GE"; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: zOut = "MATCH"; break; + case SQLITE_INDEX_CONSTRAINT_LIKE: zOut = "LIKE"; break; + case SQLITE_INDEX_CONSTRAINT_GLOB: zOut = "GLOB"; break; + case SQLITE_INDEX_CONSTRAINT_REGEXP: zOut = "REGEXP"; break; + case SQLITE_INDEX_CONSTRAINT_NE: zOut = "NE"; break; + case SQLITE_INDEX_CONSTRAINT_ISNOT: zOut = "ISNOT"; break; + case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: zOut = "ISNOTNULL"; break; + case SQLITE_INDEX_CONSTRAINT_ISNULL: zOut = "ISNULL"; break; + case SQLITE_INDEX_CONSTRAINT_IS: zOut = "IS"; break; + case SQLITE_INDEX_CONSTRAINT_LIMIT: zOut = "LIMIT"; break; + case SQLITE_INDEX_CONSTRAINT_OFFSET: zOut = "OFFSET"; break; + case SQLITE_INDEX_CONSTRAINT_FUNCTION: zOut = "FUNCTION"; break; + default: + sqlite3_snprintf(sizeof(zUnknown),zUnknown,"%d",op); + zOut = zUnknown; + break; + } + return zOut; +} + +/* ** SQLite will invoke this method one or more times while planning a query ** that uses the vtablog virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that @@ -451,14 +484,23 @@ static int vtablogBestIndex( printf(" colUsed: 0x%016llx\n", p->colUsed); printf(" nConstraint: %d\n", p->nConstraint); for(i=0; i<p->nConstraint; i++){ + sqlite3_value *pVal = 0; + int rc = sqlite3_vtab_rhs_value(p, i, &pVal); printf( - " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", + " constraint[%d]: col=%d termid=%d op=%s usabled=%d coll=%s rhs=", i, p->aConstraint[i].iColumn, p->aConstraint[i].iTermOffset, - p->aConstraint[i].op, + vtablogOpName(p->aConstraint[i].op), p->aConstraint[i].usable, - sqlite3_vtab_collation(p,i)); + sqlite3_vtab_collation(p,i) + ); + if( rc==SQLITE_OK ){ + vtablogQuote(pVal); + printf("\n"); + }else{ + printf("N/A\n"); + } } printf(" nOrderBy: %d\n", p->nOrderBy); for(i=0; i<p->nOrderBy; i++){ @@ -1,5 +1,5 @@ -C Improve\sthe\sbytecode\sfor\sjoins\ssuch\sthat\sit\sexits\searlier\sif\sit\sdetermines\nthat\sno\soutput\sis\spossible. -D 2025-07-02T11:47:54.123 +C Fix\sharmless\scompiler\swarnings. +D 2025-07-04T12:25:24.884 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -113,7 +113,7 @@ F ext/fts5/fts5_buffer.c f1e6d0324d7c55329d340673befc26681a372a4d36086caa8d1ec7d F ext/fts5/fts5_config.c e7d8dd062b44a66cd77e5a0f74f23a2354cd1f3f8575afb967b2773c3384f7f8 F ext/fts5/fts5_expr.c be9e5f7f11d87e7bd3680832c93c13050fe351994b5052b0215c2ef40312c23a F ext/fts5/fts5_hash.c a6266cedd801ab7964fa9e74ebcdda6d30ec6a96107fa24148ec6b7b5b80f6e0 -F ext/fts5/fts5_index.c fe99dbb3622609e358113cfc8f49aa7a4e6312cba047e7abd0f867b7d6c4c9d5 +F ext/fts5/fts5_index.c ed562b75c87efaa80c40e55f6c1102904728e35baa293c371f96b1c8e151399d F ext/fts5/fts5_main.c e558225168845dc708abeb2ad10415696e5a3249bcba1810ba3c7ef80764962e F ext/fts5/fts5_storage.c 19bc7c4cbe1e6a2dd9849ef7d84b5ca1fcbf194cefc3e386b901e00e08bf05c2 F ext/fts5/fts5_tcl.c 7fb5a3d3404099075aaa2457307cb459bbc257c0de3dbd52b1e80a5b503e0329 @@ -456,7 +456,7 @@ F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505 F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20 F ext/misc/vfsstat.c 0b23c0a69a2b63dc0ef0af44f9c1fc977300c480a1f7a9814500369d8211f56e F ext/misc/vfstrace.c 0e4b8b17ac0675ea90f6d168d8214687e06ca3efbc0060aad4814994d82b41fb -F ext/misc/vtablog.c a197addbbd1e267a5476274b74953e1b6f050e28516f0a5fe7d6382753165ee6 +F ext/misc/vtablog.c 62d54fb0f4ff9df72ffa44f4a226451a964e1d96ac550f2b8906feebdad00b58 F ext/misc/vtshim.c e5bce24ab8c532f4fdc600148718fe1802cb6ed57417f1c1032d8961f72b0e8f F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f65b4fc0668 F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c @@ -726,7 +726,7 @@ F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523 F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399 F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea -F src/btree.c 81fb44041929a605e293185bd4091cb24c468a108afe8ba1d93a81a8823de47f +F src/btree.c 783f9999f9ca56846619ba902f5970e181d897c23cc923c915fef225af6dda8a F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50 F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886 F src/build.c 67c1db4c5e89a8519fe9b6dafc287f6bc3627696b5b8536dc5e06db570d8c05f @@ -736,7 +736,7 @@ F src/date.c 9db4d604e699a73e10b8e85a44db074a1f04c0591a77e2abfd77703f50dce1e9 F src/dbpage.c b3e218f8ed74fcbb7fa805df8ca669a3718d397617b3d8a8aac3307dc315c4d6 F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42 -F src/expr.c e3d3b6e60b86fac772bd4d4f0c6fe1d01bef3851754a9c1215d2f8574296cbd2 +F src/expr.c 12d8a79f6a0eb9bfae2d468a6d5bd7b9a0ff00988b3f5326ec9bbaad61bbf385 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f F src/func.c de47a8295503aa130baae5e6d9868ecf4f7c4dbffa65d83ad1f70bdbac0ee2d6 @@ -785,7 +785,7 @@ F src/printf.c 71b6d3a0093bf23f473e25480ca0024e8962681506c75f4ffd3d343a3f0ab113 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c d40fe18d7c2fd0339f5846ffcf7d6809866e380acdf14c76fb2af87e9fe13f64 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 0258c6c36372e64e3ecc5f9ed4ebb598c0688e112e28f9c9c0f9b61dc6500609 +F src/select.c 700e98061a61bf8e8b0f2707ed22ffc44c7a7b660dbf7c569430e04d2f95d8a5 F src/shell.c.in 4f14a1f5196b6006abc8e73cc8fd6c1a62cf940396f8ba909d6711f35f074bb6 F src/sqlite.h.in 5c54f2461a1ea529bab8499148a2b238e2d4bb571d59e8ea5322d0c190abb693 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 @@ -870,7 +870,7 @@ F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014 F src/where.c f58d41d0923eeb21cab8e4fc87a0b36c0724ff4f279ce95ab2731b4696b8e75a F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da F src/wherecode.c 504f3c1270c3ffd51ebcdf7a31de08aa51a63b33a2ccdf8f5736afe3dfa73d45 -F src/whereexpr.c 566ca4382e07a4ba1fd86c97ae0781cdf84004c7d9c59466bf5db75733548807 +F src/whereexpr.c d007dc41364de5902181739632380afd671e14f0c5cc9978e64a2c6df8f28c6c F src/window.c d01227141f622f24fbe36ca105fbe6ef023f9fd98f1ccd65da95f88886565db5 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test 4d7a34d328e58ca2a2d78fd76c27614a41ca7ddf4312ded9c68c04f430b3b47d @@ -962,7 +962,7 @@ F test/bestindex8.test b63a4f171a2c83d481bb14c431a8b72e85d27b2ffdaa0435a95d58ca9 F test/bestindex9.test 1a4b93db117fd8abe74ae9be982f86aa72f01e60cd4ac541e6ede39673a451a0 F test/bestindexA.test e1b5def6b190797cacf008e6815ffb78fb30261999030d60a728d572eef44c7f F test/bestindexB.test 328b97b69cd1a20928d5997f9ecb04d2e00f1d18e19ab27f9e9adb44d7bc51ce -F test/bestindexC.test 2df6ada16d8f00d9bb6a9664d9c323560aeed0e0ebc7a32b99d85d70037fd250 +F test/bestindexC.test 95b4a527b1a5d07951d731604a6d4cf7e5a806b39cea0e7819d4c9667e11c3fc F test/bestindexD.test 6a8f6f84990bcf17dfa59652a1f935beddb7afd96f8302830fbc86b0a13df3c3 F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263 F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 @@ -2208,8 +2208,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P d27d34fb746280e7e81335db4e195914b15403ef0da7b2955550553dd78fbe9a 63306e447efb3ac17e789a331ed3bb65459eb8b79d66e9c185ba3bd852f34ce3 -R b92b209ccf559f2de96150026272807a +P cc8171461bf35f584888a24b844c8b79ad30155b19b61161e9f3abc276e8aae5 +R dcce4951e27b93fcf8c3ca4486981e14 U drh -Z 48380f5a742f367bd4ced6b10cb684be +Z 588ae666a6340faba85996112e26e13c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ed9a08086..957933770 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2d2b61cba44a756a3a41ef5c95bbb0c0b7111f4b679c578fec9bd0b214cca367 +13af4acebe09b047756c22b800136cffaba532e7fcaa448a4edf4fedb94e9bbc diff --git a/src/btree.c b/src/btree.c index 1806a914a..00cdc9760 100644 --- a/src/btree.c +++ b/src/btree.c @@ -1929,10 +1929,10 @@ static int freeSpace(MemPage *pPage, int iStart, int iSize){ assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); + assert( CORRUPT_DB || iEnd <= (int)pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( iSize>=4 ); /* Minimum cell size is 4 */ - assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 ); + assert( CORRUPT_DB || iStart<=(int)pPage->pBt->usableSize-4 ); /* The list of freeblocks must be in ascending order. Find the ** spot on the list where iStart should be inserted. diff --git a/src/expr.c b/src/expr.c index c8dfd3af3..3f040309a 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2428,6 +2428,7 @@ static int exprComputeOperands( VdbeComment((v, "skip left operand")); VdbeCoverage(v); }else{ + r2 = 0; /* Silence a false-positive uninit-var warning in MSVC */ addrIsNull = 0; } r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1); @@ -3896,17 +3897,23 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ VdbeComment((v, "Init EXISTS result")); } if( pSel->pLimit ){ - /* The subquery already has a limit. If the pre-existing limit is X - ** then make the new limit X<>0 so that the new limit is either 1 or 0 */ - sqlite3 *db = pParse->db; - pLimit = sqlite3Expr(db, TK_INTEGER, "0"); - if( pLimit ){ - pLimit->affExpr = SQLITE_AFF_NUMERIC; - pLimit = sqlite3PExpr(pParse, TK_NE, - sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit); - } - sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft); - pSel->pLimit->pLeft = pLimit; + /* The subquery already has a limit. If the pre-existing limit X is + ** not already integer value 1 or 0, then make the new limit X<>0 so that + ** the new limit is either 1 or 0 */ + Expr *pLeft = pSel->pLimit->pLeft; + if( ExprHasProperty(pLeft, EP_IntValue)==0 + || (pLeft->u.iValue!=1 && pLeft->u.iValue!=0) + ){ + sqlite3 *db = pParse->db; + pLimit = sqlite3Expr(db, TK_INTEGER, "0"); + if( pLimit ){ + pLimit->affExpr = SQLITE_AFF_NUMERIC; + pLimit = sqlite3PExpr(pParse, TK_NE, + sqlite3ExprDup(db, pLeft, 0), pLimit); + } + sqlite3ExprDeferredDelete(pParse, pLeft); + pSel->pLimit->pLeft = pLimit; + } }else{ /* If there is no pre-existing limit add a limit of 1 */ pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1"); diff --git a/src/select.c b/src/select.c index 95b2925e3..1e54747fc 100644 --- a/src/select.c +++ b/src/select.c @@ -5366,6 +5366,7 @@ static int pushDownWhereTerms( x.iTable = pSrc->iCursor; x.iNewTable = pSrc->iCursor; x.isOuterJoin = 0; + x.nSelDepth = 0; x.pEList = pSubq->pEList; x.pCList = findLeftmostExprlist(pSubq); pNew = substExpr(&x, pNew); diff --git a/src/whereexpr.c b/src/whereexpr.c index 53c8508e5..e4be8d9d6 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1652,7 +1652,7 @@ static void whereAddLimitExpr( ** ** 1. The SELECT statement has a LIMIT clause, and ** 2. The SELECT statement is not an aggregate or DISTINCT query, and -** 3. The SELECT statement has exactly one object in its from clause, and +** 3. The SELECT statement has exactly one object in its FROM clause, and ** that object is a virtual table, and ** 4. There are no terms in the WHERE clause that will not be passed ** to the virtual table xBestIndex method. @@ -1689,8 +1689,22 @@ void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ ** (leftCursor==iCsr) test below. */ continue; } - if( pWC->a[ii].leftCursor!=iCsr ) return; - if( pWC->a[ii].prereqRight!=0 ) return; + if( pWC->a[ii].leftCursor==iCsr && pWC->a[ii].prereqRight==0 ) continue; + + /* If this term has a parent with exactly one child, and the parent will + ** be passed through to xBestIndex, then this term can be ignored. */ + if( pWC->a[ii].iParent>=0 ){ + WhereTerm *pParent = &pWC->a[ pWC->a[ii].iParent ]; + if( pParent->leftCursor==iCsr + && pParent->prereqRight==0 + && pParent->nChild==1 + ){ + continue; + } + } + + /* This term will not be passed through. Do not add a LIMIT clause. */ + return; } /* Check condition (5). Return early if it is not met. */ diff --git a/test/bestindexC.test b/test/bestindexC.test index 48f3a2765..8b96a19e6 100644 --- a/test/bestindexC.test +++ b/test/bestindexC.test @@ -349,4 +349,78 @@ do_execsql_test 5.9 { three six seven } +#-------------------------------------------------------------------------- + +reset_db +register_tcl_module db + +proc quote {str} { + return "'[string map {' ''} $str]'" +} + +proc vtab_command {lVal method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b, c, d)" + } + + xBestIndex { + set hdl [lindex $args 0] + set clist [$hdl constraints] + + set idx 0 + set idxnum 0 + + foreach c $clist { + array set a $c + if {$a(usable)==0} continue + + if {$a(op)=="limit"} { + set idxnum [$hdl rhs_value $idx 555] + } + + incr idx + } + + return "cost 1000 rows 1000 idxnum $idxnum" + + } + + xFilter { + foreach {idxnum idxstr lArg} $args {} + return [list sql "SELECT 0, $idxnum, $idxnum, $idxnum, $idxnum"] + } + } + + return {} +} + +do_execsql_test 6.0 { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(2, 2); + CREATE VIRTUAL TABLE x1 USING tcl(vtab_command t1); +} + +do_execsql_test 6.1 { SELECT * FROM x1 LIMIT 50 } {50 50 50 50} + +do_execsql_test 6.2 { SELECT * FROM x1 WHERE b=c LIMIT 5 } {0 0 0 0} + +do_execsql_test 6.3 { + SELECT (SELECT a FROM x1 WHERE t1.x=t1.y LIMIT 10) FROM t1 +} {0} + +do_execsql_test 6.4 { + SELECT (SELECT a FROM x1 WHERE x1.a=1) FROM t1 +} {1} + +do_execsql_test 6.5 { + SELECT (SELECT a FROM x1 WHERE x1.a=1 LIMIT 1) FROM t1 +} {1} + +do_execsql_test 6.6 { + SELECT (SELECT a FROM x1 WHERE x1.a=555 LIMIT 2) FROM t1 +} {555} + finish_test + + |