Give the virtual table xBestIndex method access to (some) OFFSET and LIMIT

clauses.

FossilOrigin-Name: 74fa5757ee0a8499bcd6546dac1a3ecc6048ba9cff9e3e574e28e6e82e894a3d
This commit is contained in:
drh
2022-01-27 16:14:50 +00:00
parent 4b51e8bd68
commit 895bab3383
14 changed files with 174 additions and 35 deletions
+1 -1
View File
@@ -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 );
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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;
}
+2
View File
@@ -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
/*
+2 -1
View File
@@ -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*);
+4
View File
@@ -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));
+4 -2
View File
@@ -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. */
+47 -6
View File
@@ -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
+4
View File
@@ -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*);
+1 -1
View File
@@ -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 ){
+74
View File
@@ -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; ii<pWC->nTerm; ii++){
if( pWC->a[ii].leftCursor!=iCsr ) return;
}
/* Check condition (5). Return early if it is not met. */
if( pOrderBy ){
for(ii=0; ii<pOrderBy->nExpr; 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.
*/