From 895bab33836282dcb9b4e225c1195230067e1b2c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 16:14:50 +0000 Subject: [PATCH 1/7] Give the virtual table xBestIndex method access to (some) OFFSET and LIMIT clauses. FossilOrigin-Name: 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d --- ext/misc/qpvtab.c | 12 +++++-- manifest | 37 ++++++++++++---------- manifest.uuid | 2 +- src/delete.c | 2 +- src/fkey.c | 2 +- src/select.c | 6 ++-- src/sqlite.h.in | 2 ++ src/sqliteInt.h | 3 +- src/test_bestindex.c | 4 +++ src/update.c | 6 ++-- src/where.c | 53 +++++++++++++++++++++++++++---- src/whereInt.h | 4 +++ src/wherecode.c | 2 +- src/whereexpr.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 174 insertions(+), 35 deletions(-) diff --git a/ext/misc/qpvtab.c b/ext/misc/qpvtab.c index 828a5ba1b8..67f3cfc519 100644 --- a/ext/misc/qpvtab.c +++ b/ext/misc/qpvtab.c @@ -103,7 +103,8 @@ static const char *azColname[] = { "ux", "rhs", "a", "b", "c", "d", "e", - "flags" + "flags", + "" }; /* @@ -143,6 +144,7 @@ static int qpvtabConnect( #define QPVTAB_D 9 #define QPVTAB_E 10 #define QPVTAB_FLAGS 11 +#define QPVTAB_NONE 12 if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; @@ -338,6 +340,7 @@ static int qpvtabBestIndex( for(i=0; inConstraint; i++){ sqlite3_value *pVal; int iCol = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; if( iCol==QPVTAB_FLAGS && pIdxInfo->aConstraint[i].usable ){ pVal = 0; rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal); @@ -347,10 +350,15 @@ static int qpvtabBestIndex( if( pIdxInfo->idxNum & 2 ) pIdxInfo->orderByConsumed = 1; } } + if( op==SQLITE_INDEX_CONSTRAINT_LIMIT + || op==SQLITE_INDEX_CONSTRAINT_OFFSET + ){ + iCol = QPVTAB_NONE; + } sqlite3_str_appendf(pStr,"aConstraint,%d,%s,%d,%d,", i, azColname[iCol], - pIdxInfo->aConstraint[i].op, + op, pIdxInfo->aConstraint[i].usable); pVal = 0; rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal); diff --git a/manifest b/manifest index 616d06e0b5..81522fbabb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enforce\sthe\srestriction\sthat\s'unixepoch'\sonly\sworks\sas\sthe\sfirst\smodifier\nafter\sthe\stime-value.\s\sThis\shas\sbeen\sdocumented\ssince\s2004,\sbut\shas\snever\nactually\sbeen\senforced\sbefore.\s\sAlso\sadd\snew\stest\scases\sfor\sdate/time\nfunctions\swith\sevidence\smarks. -D 2022-01-27T13:52:01.555 +C Give\sthe\svirtual\stable\sxBestIndex\smethod\saccess\sto\s(some)\sOFFSET\sand\sLIMIT\nclauses. +D 2022-01-27T16:14:50.962 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -316,7 +316,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1 F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196 -F ext/misc/qpvtab.c c662fa0a452ad286e49b6c83ac917600656b2eb47d2225ff6185c56bf80cf8d2 +F ext/misc/qpvtab.c 03986dca441274933dd924170ceee50ad248d24a4cd6a686ff45d5e65e5deac4 F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c @@ -502,10 +502,10 @@ F src/ctime.c 2cce39df1a13e05b7633e6d21b651f21492471f991dd7b323a4ee4e7b7f0b7f1 F src/date.c 41627dec396f3d33e2c317a065f9d59bb535982b2ea3a561c96e4d4cf1137b65 F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d -F src/delete.c 52897a8516dc40753503c25eed0e305f09cc50ae474f22b0b4fd31d3b879cc08 +F src/delete.c b5f1716b4d723db48254ee0f896e362cd029e865e05414139ea7f539f3884e1d F src/expr.c 9658bccd1598211ace848c8ca9480dbf8be08dfee1db5cf03897b34b7b6e8fef F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 -F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560 +F src/fkey.c 06e4ac33031b02dde7130c12e79cddf4dc5cfa72b23d8e63a3c26878fc9c1d3c F src/func.c 8fddc42bce95d17938252a543f86fe29e479366e80fbd112a1822913b6247776 F src/global.c 1f56aead86e8a18c4415638f5e6c4d0a0550427f4b3f5d065ba5164cc09c22e8 F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 @@ -552,12 +552,12 @@ F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c 5799f5b15e27bcb8262caec7821f3ef89575e539237db3141be0d26cf8efd22c +F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2 F src/shell.c.in e80a140e92e342e2f92d405a77155c8e3a67c9b1d0bdbacb92885960cd4fc8f2 -F src/sqlite.h.in 31c2c8d737814369bd3b71f3849c4a97ef7ede0aa3ce976ecb11632fa5f1f863 +F src/sqlite.h.in da0e94f140f3f054f0eca8fc4a183167bedc7ae9c51822390c62369455eae5f8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a -F src/sqliteInt.h 33fbafb55b48f63b791c563378345ebdbbcb73e8fd63a1c22e4df05f435d4714 +F src/sqliteInt.h 8ef2996e02476f73e41ba977f819bda0cc68b7ce238cf404b9b8930df57bc1d0 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -574,7 +574,7 @@ F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5 F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871 F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0 -F src/test_bestindex.c 78809f11026f18a93fcfd798d9479cba37e1201c830260bf1edc674b2fa9b857 +F src/test_bestindex.c ba0314d3bca8b00fa0daaf113a314986f73ea8ad85a7562f983638d09a29045c F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274 F src/test_config.c 284c29912736f68b0a583a920bf63fd8f9125dffb8a75cb0676e58502b2f7908 @@ -619,7 +619,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c b74d878aa7c82ec8460779468061a96185e22257f68ab785b69abce354b70446 F src/treeview.c 9dfdb7ff7f6645d0a6458dbdf4ffac041c071c4533a6db8bb6e502b979ac67bc F src/trigger.c 692972e4393dfc8017a1a527c1ea1b96ce3d101e84584cd832fcfb83d22b50b2 -F src/update.c 7dfa3866cdfb28bf952c38054c1722720d8ff64750dea421790aecc91f640516 +F src/update.c f875b0d59da5c3055a0b2ac20560e1650229c6787e78de5e9836267b5cbb8359 F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23 @@ -639,10 +639,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c a14990c7b35e95f8f9cb0dc0d6d2e32fa99135a716a04027cefa48138d280ecb -F src/whereInt.h 8a215acde0f833a4dea3d30a7bbed9f48b4b547b5d5e34cd02acee366476ab80 -F src/wherecode.c 8da0f873278ed6aad42bf2028404d7178dd9cfcdc7179ecc61a87591a15a07d2 -F src/whereexpr.c 9f64c39e53070584e99e4d20c1dd3397e125fabbae8fd414ffec574c410ac7d3 +F src/where.c f89c21296cdf23bbc73333c77a01e0bfdccc9d1a767d38a145e3a8658456b66c +F src/whereInt.h e23780eb06810dc8bd7d855c97a667bf116cb929d8aa107ce1db91df8e47aaa8 +F src/wherecode.c 37878a0a6f790b77f41e7b3d77c42f14bded09aef3d363eaf19b3ee02e765666 +F src/whereexpr.c 50b09b3583301d0a5aef0e5b5d225f4d2b0eba2e36e3f9d0b914e110d4b2f6ce F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a8db69411b0d1275909adeb21027784ada17af24efe3a59ae0ae2a897659ff17 -R a92b0ae1c265a80dc4f7e5c12c916bf4 +P 64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc +R c3554501c3177117c353a66ed5c4eb7c +T *branch * vtab-limit-offset +T *sym-vtab-limit-offset * +T -sym-trunk * U drh -Z 47b9e1bb1e6a61a53486ee72f7ca6190 +Z 853246eb39d3c5347b902f7c653bc6b9 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 40297e7019..173289557e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc \ No newline at end of file +74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index b51e038918..7b769e6a18 100644 --- a/src/delete.c +++ b/src/delete.c @@ -478,7 +478,7 @@ void sqlite3DeleteFrom( ** ONEPASS_SINGLE: One-pass approach - at most one row deleted. ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted. */ - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); diff --git a/src/fkey.c b/src/fkey.c index 6ee35bf320..b464743c78 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -651,7 +651,7 @@ static void fkScanChildren( ** clause. For each row found, increment either the deferred or immediate ** foreign key constraint counter. */ if( pParse->nErr==0 ){ - pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); + pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); if( pWInfo ){ sqlite3WhereEnd(pWInfo); diff --git a/src/select.c b/src/select.c index b280ed2b3f..a3985b70af 100644 --- a/src/select.c +++ b/src/select.c @@ -6898,7 +6898,7 @@ int sqlite3Select( /* Begin the database scan. */ SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy, - p->pEList, wctrlFlags, p->nSelectRow); + p->pEList, p, wctrlFlags, p->nSelectRow); if( pWInfo==0 ) goto select_end; if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo); @@ -7162,7 +7162,7 @@ int sqlite3Select( sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct, - WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0 + 0, (WHERE_GROUPBY|(orderByGrp ? WHERE_SORTBYGROUP : 0)|distFlag), 0 ); if( pWInfo==0 ){ sqlite3ExprListDelete(db, pDistinct); @@ -7460,7 +7460,7 @@ int sqlite3Select( SELECTTRACE(1,pParse,p,("WhereBegin\n")); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy, - pDistinct, minMaxFlag|distFlag, 0); + pDistinct, 0, minMaxFlag|distFlag, 0); if( pWInfo==0 ){ goto select_end; } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 4b36233b37..d549a1ba4b 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -7148,6 +7148,8 @@ struct sqlite3_index_info { #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 #define SQLITE_INDEX_CONSTRAINT_IS 72 +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7a266403fb..6d93650713 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4577,7 +4577,8 @@ void sqlite3CodeChangeCount(Vdbe*,int,const char*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*, Upsert*); -WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); +WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*, + ExprList*,Select*,u16,int); void sqlite3WhereEnd(WhereInfo*); LogEst sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); diff --git a/src/test_bestindex.c b/src/test_bestindex.c index 2cd79baf2b..6cda889e5e 100644 --- a/src/test_bestindex.c +++ b/src/test_bestindex.c @@ -424,6 +424,10 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ zOp = "isnull"; break; case SQLITE_INDEX_CONSTRAINT_IS: zOp = "is"; break; + case SQLITE_INDEX_CONSTRAINT_LIMIT: + zOp = "limit"; break; + case SQLITE_INDEX_CONSTRAINT_OFFSET: + zOp = "offset"; break; } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); diff --git a/src/update.c b/src/update.c index ccad38df49..a43d0eac53 100644 --- a/src/update.c +++ b/src/update.c @@ -723,7 +723,7 @@ void sqlite3Update( if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ flags |= WHERE_ONEPASS_MULTIROW; } - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur); if( pWInfo==0 ) goto update_cleanup; /* A one-pass strategy that might update more than one row may not @@ -1245,7 +1245,9 @@ static void updateVirtualTable( regRowid = ++pParse->nMem; /* Start scanning the virtual table */ - pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0); + pWInfo = sqlite3WhereBegin( + pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0 + ); if( pWInfo==0 ) return; /* Populate the argument registers. */ diff --git a/src/where.c b/src/where.c index 32ec17ec42..2afc012f60 100644 --- a/src/where.c +++ b/src/where.c @@ -3459,6 +3459,16 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return true if pTerm is a virtual table LIMIT or OFFSET term. +*/ +static int isLimitTerm(WhereTerm *pTerm){ + return pTerm->eOperator==WO_AUX && ( + pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT + || pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET + ); +} + /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This @@ -3486,7 +3496,8 @@ static int whereLoopAddVirtualOne( u16 mExclude, /* Exclude terms using these operators */ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ u16 mNoOmit, /* Do not omit these constraints */ - int *pbIn /* OUT: True if plan uses an IN(...) op */ + int *pbIn, /* OUT: True if plan uses an IN(...) op */ + int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */ ){ WhereClause *pWC = pBuilder->pWC; struct sqlite3_index_constraint *pIdxCons; @@ -3511,6 +3522,7 @@ static int whereLoopAddVirtualOne( pIdxCons->usable = 0; if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 + && (pbRetryLimit || !isLimitTerm(pTerm)) ){ pIdxCons->usable = 1; } @@ -3589,6 +3601,21 @@ static int whereLoopAddVirtualOne( pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + + if( isLimitTerm(pTerm) && *pbIn ){ + /* If there is an IN(...) term handled as an == (separate call to + ** xFilter for each value on the RHS of the IN) and a LIMIT or + ** OFFSET term handled as well, the plan is unusable. Set output + ** variable *pbRetryLimit to true to tell the caller to retry with + ** LIMIT and OFFSET disabled. */ + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->needToFreeIdxStr = 0; + } + *pbRetryLimit = 1; + return SQLITE_OK; + } } } @@ -3751,6 +3778,7 @@ static int whereLoopAddVirtual( WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; + int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */ assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -3774,7 +3802,15 @@ static int whereLoopAddVirtual( /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); - rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry + ); + if( bRetry ){ + assert( rc==SQLITE_OK ); + rc = whereLoopAddVirtualOne( + pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0 + ); + } /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0) @@ -3792,7 +3828,7 @@ static int whereLoopAddVirtual( if( bIn ){ WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0); assert( bIn==0 ); mBestNoIn = pNew->prereq & ~mPrereq; if( mBestNoIn==0 ){ @@ -3819,7 +3855,7 @@ static int whereLoopAddVirtual( WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n", (sqlite3_uint64)mPrev, (sqlite3_uint64)mNext)); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0); if( pNew->prereq==mPrereq ){ seenZero = 1; if( bIn==0 ) seenZeroNoIN = 1; @@ -3832,7 +3868,7 @@ static int whereLoopAddVirtual( if( rc==SQLITE_OK && seenZero==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0); if( bIn==0 ) seenZeroNoIN = 1; } @@ -3842,7 +3878,7 @@ static int whereLoopAddVirtual( if( rc==SQLITE_OK && seenZeroNoIN==0 ){ WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( - pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); + pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); } } @@ -5248,6 +5284,7 @@ WhereInfo *sqlite3WhereBegin( Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ + Select *pLimit, /* Use this LIMIT/OFFSET clause, if any */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ @@ -5324,6 +5361,9 @@ WhereInfo *sqlite3WhereBegin( pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; +#ifndef SQLITE_OMIT_VIRTUALTABLE + pWInfo->pLimit = pLimit; +#endif memset(&pWInfo->nOBSat, 0, offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); @@ -5392,6 +5432,7 @@ WhereInfo *sqlite3WhereBegin( /* Analyze all of the subexpressions. */ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); + sqlite3WhereAddLimit(&pWInfo->sWC, pLimit); if( db->mallocFailed ) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join diff --git a/src/whereInt.h b/src/whereInt.h index 1304a40a77..cdddc10524 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -453,6 +453,9 @@ struct WhereInfo { ExprList *pOrderBy; /* The ORDER BY clause or NULL */ ExprList *pResultSet; /* Result set of the query */ Expr *pWhere; /* The complete WHERE clause */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + Select *pLimit; /* Used to access LIMIT expr/registers for vtabs */ +#endif int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ @@ -538,6 +541,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( void sqlite3WhereClauseInit(WhereClause*,WhereInfo*); void sqlite3WhereClauseClear(WhereClause*); void sqlite3WhereSplit(WhereClause*,Expr*,u8); +void sqlite3WhereAddLimit(WhereClause*, Select*); Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*); Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*); Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); diff --git a/src/wherecode.c b/src/wherecode.c index b3ae06cae2..4990cda0ce 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -2358,7 +2358,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( /* Loop through table entries that match term pOrTerm. */ ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); - pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0, WHERE_OR_SUBCLAUSE, iCovCur); assert( pSubWInfo || pParse->nErr ); if( pSubWInfo ){ diff --git a/src/whereexpr.c b/src/whereexpr.c index f0660a990e..1cc5ce5370 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1522,6 +1522,80 @@ void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ } } +/* +** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or +** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the +** where-clause passed as the first argument. The value for the term +** is found in register iReg. +*/ +void whereAddLimitExpr(WhereClause *pWC, int iReg, int iCsr, int eMatchOp){ + Parse *pParse = pWC->pWInfo->pParse; + sqlite3 *db = pParse->db; + Expr *pExpr; + + pExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3Expr(db,TK_REGISTER,0)); + if( pExpr ){ + WhereTerm *pTerm; + int idx; + pExpr->pRight->iTable = iReg; + idx = whereClauseInsert(pWC, pExpr, TERM_DYNAMIC|TERM_VIRTUAL); + pTerm = &pWC->a[idx]; + pTerm->leftCursor = iCsr; + pTerm->eOperator = WO_AUX; + pTerm->eMatchOp = eMatchOp; + } +} + +/* +** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the +** SELECT statement passed as the second argument. These terms are only +** added if: +** +** 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 +** 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. +** 5. The ORDER BY clause, if any, will be made available to the xBestIndex +** method. +** +** LIMIT and OFFSET terms are ignored by most of the planner code. They +** exist only so that they may be passed to the xBestIndex method of the +** single virtual table in the FROM clause of the SELECT. +*/ +void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ + assert( p==0 || (p->pGroupBy==0 && (p->selFlags & SF_Aggregate)==0) ); + if( (p && p->pLimit) /* 1 */ + && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ + && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */ + ){ + ExprList *pOrderBy = p->pOrderBy; + int iCsr = p->pSrc->a[0].iCursor; + int ii; + + /* Check condition (4). Return early if it is not met. */ + for(ii=0; iinTerm; ii++){ + if( pWC->a[ii].leftCursor!=iCsr ) return; + } + + /* Check condition (5). Return early if it is not met. */ + if( pOrderBy ){ + for(ii=0; iinExpr; ii++){ + Expr *pExpr = pOrderBy->a[ii].pExpr; + if( pExpr->op!=TK_COLUMN || pExpr->iTable!=iCsr ) return; + if( pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_BIGNULL ) return; + } + } + + /* All conditions are met. Add the terms to the where-clause object. */ + whereAddLimitExpr(pWC, p->iLimit, iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + if( p->iOffset>0 ){ + whereAddLimitExpr(pWC, p->iOffset, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + } + } +} + /* ** Initialize a preallocated WhereClause structure. */ From 233ba1b8eb0dc3e87942d08455ed43f32f25441c Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 18:45:26 +0000 Subject: [PATCH 2/7] Make LIMIT and OFFSET values available to sqlite3_vtab_rhs_value(). FossilOrigin-Name: b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/whereexpr.c | 41 +++++++++++++++++++++++++++++++++-------- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/manifest b/manifest index 81522fbabb..55c1838999 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Give\sthe\svirtual\stable\sxBestIndex\smethod\saccess\sto\s(some)\sOFFSET\sand\sLIMIT\nclauses. -D 2022-01-27T16:14:50.962 +C Make\sLIMIT\sand\sOFFSET\svalues\savailable\sto\ssqlite3_vtab_rhs_value(). +D 2022-01-27T18:45:26.825 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f89c21296cdf23bbc73333c77a01e0bfdccc9d1a767d38a145e3a8658456b66c F src/whereInt.h e23780eb06810dc8bd7d855c97a667bf116cb929d8aa107ce1db91df8e47aaa8 F src/wherecode.c 37878a0a6f790b77f41e7b3d77c42f14bded09aef3d363eaf19b3ee02e765666 -F src/whereexpr.c 50b09b3583301d0a5aef0e5b5d225f4d2b0eba2e36e3f9d0b914e110d4b2f6ce +F src/whereexpr.c 455b5eeef49693ac6f7303e4529bb997fd4e0f0bd594a6f8e49c0ea7e399368f F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,11 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 64fa9e8c87179211cec248e6dfd7578502e6e969a19e91a4f0e21ed9b972a6bc -R c3554501c3177117c353a66ed5c4eb7c -T *branch * vtab-limit-offset -T *sym-vtab-limit-offset * -T -sym-trunk * +P 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d +R 4f1dec533377c1a483f6b7fee31b9110 U drh -Z 853246eb39d3c5347b902f7c653bc6b9 +Z 8464b51a4c3f5e11d93482ca57b3dd6d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 173289557e..93e902aff7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d \ No newline at end of file +b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 1cc5ce5370..183f22f599 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1527,18 +1527,40 @@ void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ ** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the ** where-clause passed as the first argument. The value for the term ** is found in register iReg. +** +** In the common case where the value is a simple integer +** (example: "LIMIT 5 OFFSET 10") then the expression codes as a +** TK_INTEGER so that it will be available to sqlite3_vtab_rhs_value(). +** If not, then it codes as a TK_REGISTER expression. */ -void whereAddLimitExpr(WhereClause *pWC, int iReg, int iCsr, int eMatchOp){ +void whereAddLimitExpr( + WhereClause *pWC, /* Add the constraint to this WHERE clause */ + int iReg, /* Register that will hold value of the limit/offset */ + Expr *pExpr, /* Expression that defines the limit/offset */ + int iCsr, /* Cursor to which the constraint applies */ + int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */ +){ Parse *pParse = pWC->pWInfo->pParse; sqlite3 *db = pParse->db; - Expr *pExpr; + Expr *pNew; + int iVal = 0; - pExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3Expr(db,TK_REGISTER,0)); - if( pExpr ){ + if( sqlite3ExprIsInteger(pExpr, &iVal) ){ + Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); + if( pVal==0 ) return; + ExprSetProperty(pVal, EP_IntValue); + pVal->u.iValue = iVal; + pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + }else{ + Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0); + if( pVal==0 ) return; + pVal->iTable = iReg; + pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); + } + if( pNew ){ WhereTerm *pTerm; int idx; - pExpr->pRight->iTable = iReg; - idx = whereClauseInsert(pWC, pExpr, TERM_DYNAMIC|TERM_VIRTUAL); + idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL); pTerm = &pWC->a[idx]; pTerm->leftCursor = iCsr; pTerm->eOperator = WO_AUX; @@ -1589,9 +1611,12 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ } /* All conditions are met. Add the terms to the where-clause object. */ - whereAddLimitExpr(pWC, p->iLimit, iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + assert( p->pLimit->op==TK_LIMIT ); + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); if( p->iOffset>0 ){ - whereAddLimitExpr(pWC, p->iOffset, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); + whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, + iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); } } } From 8f2c0b59dae1b8f194b73004d73047ce4f79a544 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 27 Jan 2022 21:18:14 +0000 Subject: [PATCH 3/7] Query planner always honors the aConstraintUsage[].omit flag for the SQLITE_INDEX_CONSTRAINT_OFFSET constraint. FossilOrigin-Name: 38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 --- ext/misc/qpvtab.c | 27 ++++++++++++++++++++++----- manifest | 20 ++++++++++---------- manifest.uuid | 2 +- src/where.c | 14 ++++++++------ src/whereInt.h | 4 +++- src/wherecode.c | 9 +++++++++ src/whereexpr.c | 1 + 7 files changed, 54 insertions(+), 23 deletions(-) diff --git a/ext/misc/qpvtab.c b/ext/misc/qpvtab.c index 67f3cfc519..fb0c155a27 100644 --- a/ext/misc/qpvtab.c +++ b/ext/misc/qpvtab.c @@ -40,7 +40,7 @@ ** ** Option 1 is the default behavior. 2 is use if there is a usable ** constraint on "flags" with an integer right-hand side that where the -** value of the right-hand side has its 0x01 bit set. +** value of the right-hand side has its 0x001 bit set. ** ** All constraints on columns "a" through "e" are marked as "omit". ** @@ -48,6 +48,12 @@ ** is an integer and that integer has its 0x02 bit set, then the ** orderByConsumed flag is set. ** +** FLAGS SUMMARY: +** +** 0x001 Columns 'a' through 'e' have INT values +** 0x002 orderByConsumed is set +** 0x004 OFFSET and LIMIT have omit set +** ** COMPILE: ** ** gcc -Wall -g -shared -fPIC -I. qpvtab.c -o qqvtab.so @@ -228,7 +234,7 @@ static int qpvtabColumn( sqlite3_result_text64(ctx, z, zEnd-z, SQLITE_TRANSIENT, SQLITE_UTF8); } }else if( i>=QPVTAB_A && i<=QPVTAB_E ){ - if( pCur->flags & 1 ){ + if( pCur->flags & 0x001 ){ sqlite3_result_int(ctx, i-QPVTAB_A+1); }else{ char x = 'a'+i-QPVTAB_A; @@ -347,7 +353,7 @@ static int qpvtabBestIndex( assert( rc==SQLITE_OK || pVal==0 ); if( pVal ){ pIdxInfo->idxNum = sqlite3_value_int(pVal); - if( pIdxInfo->idxNum & 2 ) pIdxInfo->orderByConsumed = 1; + if( pIdxInfo->idxNum & 0x002 ) pIdxInfo->orderByConsumed = 1; } } if( op==SQLITE_INDEX_CONSTRAINT_LIMIT @@ -367,9 +373,20 @@ static int qpvtabBestIndex( qpvtabStrAppendValue(pStr, pVal); } sqlite3_str_append(pStr, "\n", 1); + } + for(i=0; inConstraint; i++){ + int iCol = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; + if( op==SQLITE_INDEX_CONSTRAINT_LIMIT + || op==SQLITE_INDEX_CONSTRAINT_OFFSET + ){ + iCol = QPVTAB_NONE; + } if( iCol>=QPVTAB_A && pIdxInfo->aConstraint[i].usable ){ - pIdxInfo->aConstraintUsage[i].argvIndex = ++k; - pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = ++k; + if( iCol<=QPVTAB_FLAGS || (pIdxInfo->idxNum & 0x004)!=0 ){ + pIdxInfo->aConstraintUsage[i].omit = 1; + } } } sqlite3_str_appendf(pStr, "nOrderBy,%d,,,,\n", pIdxInfo->nOrderBy); diff --git a/manifest b/manifest index 55c1838999..f1f49fa502 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sLIMIT\sand\sOFFSET\svalues\savailable\sto\ssqlite3_vtab_rhs_value(). -D 2022-01-27T18:45:26.825 +C Query\splanner\salways\shonors\sthe\saConstraintUsage[].omit\sflag\sfor\sthe\nSQLITE_INDEX_CONSTRAINT_OFFSET\sconstraint. +D 2022-01-27T21:18:14.656 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -316,7 +316,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1 F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196 -F ext/misc/qpvtab.c 03986dca441274933dd924170ceee50ad248d24a4cd6a686ff45d5e65e5deac4 +F ext/misc/qpvtab.c 09738419e25f603a35c0ac8bd0a04daab794f48d08a9bc07a6085b9057b99009 F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c @@ -639,10 +639,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c f89c21296cdf23bbc73333c77a01e0bfdccc9d1a767d38a145e3a8658456b66c -F src/whereInt.h e23780eb06810dc8bd7d855c97a667bf116cb929d8aa107ce1db91df8e47aaa8 -F src/wherecode.c 37878a0a6f790b77f41e7b3d77c42f14bded09aef3d363eaf19b3ee02e765666 -F src/whereexpr.c 455b5eeef49693ac6f7303e4529bb997fd4e0f0bd594a6f8e49c0ea7e399368f +F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba +F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a +F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 +F src/whereexpr.c 391d04108e4a5a701d2fad374ff54db8629bce12b55a7ee30a478d440ea4bfe0 F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d -R 4f1dec533377c1a483f6b7fee31b9110 +P b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 +R 87c4173ca514b2a053765a7d35c4d8eb U drh -Z 8464b51a4c3f5e11d93482ca57b3dd6d +Z 05d67635fa5a91d132911c78dd294cd6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 93e902aff7..5e209241f6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 \ No newline at end of file +38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 2afc012f60..ce6ae30709 100644 --- a/src/where.c +++ b/src/where.c @@ -3463,10 +3463,9 @@ static int whereLoopAddBtree( ** Return true if pTerm is a virtual table LIMIT or OFFSET term. */ static int isLimitTerm(WhereTerm *pTerm){ - return pTerm->eOperator==WO_AUX && ( - pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT - || pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET - ); + assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 ); + return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT + && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } /* @@ -3555,8 +3554,8 @@ static int whereLoopAddVirtualOne( mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); - for(i=0; iaLTerm[i] = 0; - pNew->u.vtab.omitMask = 0; + memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint ); + memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab)); pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for(i=0; ieMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){ + pNew->u.vtab.bOmitOffset = 1; + } } if( (pTerm->eOperator & WO_IN)!=0 ){ /* A virtual table that is constrained by an IN clause may not diff --git a/src/whereInt.h b/src/whereInt.h index cdddc10524..14c40fcb43 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -123,7 +123,8 @@ struct WhereLoop { } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ - u8 needFree; /* True if sqlite3_free(idxStr) is needed */ + u8 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ + u8 bOmitOffset : 1; /* True to let virtual table handle offset */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ @@ -612,5 +613,6 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); #define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */ #define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ #define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ +#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ #endif /* !defined(SQLITE_WHEREINT_H) */ diff --git a/src/wherecode.c b/src/wherecode.c index 4990cda0ce..2beb596e69 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1537,6 +1537,15 @@ Bitmask sqlite3WhereCodeOneLoopStart( }else{ Expr *pRight = pTerm->pExpr->pRight; codeExprOrVector(pParse, pRight, iTarget, 1); + if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET + && pLoop->u.vtab.bOmitOffset + ){ + assert( pTerm->eOperator==WO_AUX ); + assert( pWInfo->pLimit!=0 ); + assert( pWInfo->pLimit->iOffset>0 ); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pLimit->iOffset); + VdbeComment((v,"Zero OFFSET counter")); + } } } sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); diff --git a/src/whereexpr.c b/src/whereexpr.c index 183f22f599..8381994e2b 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1656,6 +1656,7 @@ void sqlite3WhereClauseClear(WhereClause *pWC){ } #endif while(1){ + assert( a->eMatchOp==0 || a->eOperator==WO_AUX ); if( a->wtFlags & TERM_DYNAMIC ){ sqlite3ExprDelete(db, a->pExpr); } From 0ae56fbdd01bfefe64c33d105b5407125b11c7ef Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 13:18:40 +0000 Subject: [PATCH 4/7] Enable SQLITE_INDEX_CONSTRAINT_LIMIT/OFFSET even if there are vector comparison operators in the WHERE clause. Also: Do not enable sqlite3_vtab_rhs_value() for LIMIT/OFFSET if the value is negative, as this violates an invariant on Expr.u.iValue. FossilOrigin-Name: 140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index f1f49fa502..278f5cdb0a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Query\splanner\salways\shonors\sthe\saConstraintUsage[].omit\sflag\sfor\sthe\nSQLITE_INDEX_CONSTRAINT_OFFSET\sconstraint. -D 2022-01-27T21:18:14.656 +C Enable\sSQLITE_INDEX_CONSTRAINT_LIMIT/OFFSET\seven\sif\sthere\sare\svector\ncomparison\soperators\sin\sthe\sWHERE\sclause.\s\sAlso:\s\sDo\snot\senable\nsqlite3_vtab_rhs_value()\sfor\sLIMIT/OFFSET\sif\sthe\svalue\sis\snegative,\sas\sthis\nviolates\san\sinvariant\son\sExpr.u.iValue. +D 2022-01-28T13:18:40.313 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c 391d04108e4a5a701d2fad374ff54db8629bce12b55a7ee30a478d440ea4bfe0 +F src/whereexpr.c 869596d69a498604fe3e00ff06475038560732227bc42de0cf5dc92248740a4f F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b2d37c086c87fe4aee013bba4eab26f642207cd2a797fe4888a6ef868884c191 -R 87c4173ca514b2a053765a7d35c4d8eb +P 38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 +R 05c94023401bdef7db1e43fcf139e4c2 U drh -Z 05d67635fa5a91d132911c78dd294cd6 +Z b99686de01cbc6d8d45082533904b9d3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5e209241f6..a2fc430536 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 \ No newline at end of file +140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index 8381994e2b..d47cfd7e92 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1379,7 +1379,10 @@ static void exprAnalyze( ** no longer used. ** ** This is only required if at least one side of the comparison operation - ** is not a sub-select. */ + ** is not a sub-select. + ** + ** tag-20220128a + */ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS) && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1 && sqlite3ExprVectorSize(pExpr->pRight)==nLeft @@ -1545,7 +1548,7 @@ void whereAddLimitExpr( Expr *pNew; int iVal = 0; - if( sqlite3ExprIsInteger(pExpr, &iVal) ){ + if( sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){ Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); if( pVal==0 ) return; ExprSetProperty(pVal, EP_IntValue); @@ -1598,6 +1601,13 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ /* Check condition (4). Return early if it is not met. */ for(ii=0; iinTerm; ii++){ + if( pWC->a[ii].wtFlags & TERM_CODED ){ + /* This term is a vector operation that has been decomposed into + ** other, subsequent terms. It can be ignored. See tag-20220128a */ + assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); + assert( pWC->a[ii].eOperator==0 ); + continue; + } if( pWC->a[ii].leftCursor!=iCsr ) return; } From d0c7baf5050ec9e9c51b496a5b00bd4b1f716aa1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 18:43:33 +0000 Subject: [PATCH 5/7] Remove a NEVER() that has become reachable now that we have the sqlite3_vtab_rhs_value() interface. FossilOrigin-Name: c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/vdbemem.c | 4 ---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 278f5cdb0a..995fa2e5f4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enable\sSQLITE_INDEX_CONSTRAINT_LIMIT/OFFSET\seven\sif\sthere\sare\svector\ncomparison\soperators\sin\sthe\sWHERE\sclause.\s\sAlso:\s\sDo\snot\senable\nsqlite3_vtab_rhs_value()\sfor\sLIMIT/OFFSET\sif\sthe\svalue\sis\snegative,\sas\sthis\nviolates\san\sinvariant\son\sExpr.u.iValue. -D 2022-01-28T13:18:40.313 +C Remove\sa\sNEVER()\sthat\shas\sbecome\sreachable\snow\sthat\swe\shave\sthe\nsqlite3_vtab_rhs_value()\sinterface. +D 2022-01-28T18:43:33.716 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -630,7 +630,7 @@ F src/vdbeInt.h d89d5d2150500cfb08615329fd20aea9d746bba5f2c3ecb8a17e2d2d9be029e5 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a F src/vdbeaux.c e761b8011baec7a4773f0a7594783f2cd71f699ab187c4aad917529ab8acd3fe F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd -F src/vdbemem.c da4d594084d581be6436582bb44bb128feeb138a3e6c313eda6749ebdc3a65ec +F src/vdbemem.c eb6042667c02c3ef1f968235b4a170e31b23a4b6a57f65a8454eab4d36f14b7f F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 38c5151eb8cc789ad082a1b40d16b2ddb55454219bd685b6e420995af6a15f55 -R 05c94023401bdef7db1e43fcf139e4c2 +P 140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 +R b5457dcd74cf42bf2b317f01e195dfec U drh -Z b99686de01cbc6d8d45082533904b9d3 +Z 9ee62f5941b268da60926e062d6f8568 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a2fc430536..4c6ca64435 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 \ No newline at end of file +c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 \ No newline at end of file diff --git a/src/vdbemem.c b/src/vdbemem.c index 5a9d15f465..5d2d69268e 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1530,11 +1530,7 @@ static int valueFromExpr( assert( pExpr!=0 ); while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft; -#if defined(SQLITE_ENABLE_STAT4) if( op==TK_REGISTER ) op = pExpr->op2; -#else - if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; -#endif /* Compressed expressions only appear when parsing the DEFAULT clause ** on a table column definition, and hence only when pCtx==0. This From cee5cb484de348ef8aa798ed7e740d03d4e8218d Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 19:53:37 +0000 Subject: [PATCH 6/7] Include hidden columns of table valued functions that are constrained by the function arguments in the colUsed bitmask. FossilOrigin-Name: 8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 995fa2e5f4..5ac5027c9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sNEVER()\sthat\shas\sbecome\sreachable\snow\sthat\swe\shave\sthe\nsqlite3_vtab_rhs_value()\sinterface. -D 2022-01-28T18:43:33.716 +C Include\shidden\scolumns\sof\stable\svalued\sfunctions\sthat\sare\sconstrained\nby\sthe\sfunction\sarguments\sin\sthe\scolUsed\sbitmask. +D 2022-01-28T19:53:37.840 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c 869596d69a498604fe3e00ff06475038560732227bc42de0cf5dc92248740a4f +F src/whereexpr.c 42195849443c3648fa9900c82837b7592c91bd43934ad9369d30f5d55d7fe19e F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 140480b3ec5466513b165d4818f1180cff6e4a5edcd47efb54cbff74a7107507 -R b5457dcd74cf42bf2b317f01e195dfec +P c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 +R b21847307144bd6983c5bb4141c2b106 U drh -Z 9ee62f5941b268da60926e062d6f8568 +Z c180a25bc067b4731882777cea55422e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4c6ca64435..1fe4679ade 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 \ No newline at end of file +8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index d47cfd7e92..e4ddf49328 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1824,6 +1824,7 @@ void sqlite3WhereTabFuncArgs( pColRef->iColumn = k++; assert( ExprUseYTab(pColRef) ); pColRef->y.pTab = pTab; + pItem->colUsed |= sqlite3ExprColUsed(pColRef); pRhs = sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); From f6f7888622e01e0d5326a82d17d42f682d3a5ae8 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 28 Jan 2022 23:19:01 +0000 Subject: [PATCH 7/7] Add a NEVER() on an unreachable branch. FossilOrigin-Name: 388926254b9da6169da7267bd4d5a1a66a26576be435c88269ab8db9eaedc079 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/whereexpr.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/manifest b/manifest index 5ac5027c9a..ac1e0268cc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Include\shidden\scolumns\sof\stable\svalued\sfunctions\sthat\sare\sconstrained\nby\sthe\sfunction\sarguments\sin\sthe\scolUsed\sbitmask. -D 2022-01-28T19:53:37.840 +C Add\sa\sNEVER()\son\san\sunreachable\sbranch. +D 2022-01-28T23:19:01.285 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -642,7 +642,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c f61ca6dbb4bbb7171aa5f5459982d8d98aac24852572bcee250f9c469a1963ba F src/whereInt.h 0748a6fce98b41862445906922a809146ff7ef4de16ed9022b0bc4e5c43aa60a F src/wherecode.c c313ccf5ed13dc7e88c64f93733f414dee369a212508a866878696d83c64fc36 -F src/whereexpr.c 42195849443c3648fa9900c82837b7592c91bd43934ad9369d30f5d55d7fe19e +F src/whereexpr.c becee225f104abd0253e0ceecd2a6253589f269395d98fc3ff98fdd64fecb34c F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -1942,8 +1942,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c4c30df7cd858315359bd9bfa90a07152d87a2301eaec865c5bc1c65e272eec8 -R b21847307144bd6983c5bb4141c2b106 +P 8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 +R 2886f1ce9cf06aa83d320b384893e940 U drh -Z c180a25bc067b4731882777cea55422e +Z 20c096da0533f9910b77ed2d57db0dcc # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1fe4679ade..ae1add637c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8f8cab0d0e219f0c4bc2b8061c0013d90a2a5e598dd6cf420d302dfd1b7cbe11 \ No newline at end of file +388926254b9da6169da7267bd4d5a1a66a26576be435c88269ab8db9eaedc079 \ No newline at end of file diff --git a/src/whereexpr.c b/src/whereexpr.c index e4ddf49328..45e0ea007c 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1615,7 +1615,8 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ if( pOrderBy ){ for(ii=0; iinExpr; ii++){ Expr *pExpr = pOrderBy->a[ii].pExpr; - if( pExpr->op!=TK_COLUMN || pExpr->iTable!=iCsr ) return; + if( pExpr->op!=TK_COLUMN ) return; + if( NEVER(pExpr->iTable!=iCsr) ) return; if( pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_BIGNULL ) return; } }