Add the SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
constraints to the sqlite3_index_info for the xBestIndex method of virtual tables. FossilOrigin-Name: 1e227ad9f413227f767b45b91e5439b82c98a3368fb20643414dab5c0f4818c6
This commit is contained in:
+1
-1
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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*);
|
||||
|
||||
@@ -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
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
+51
-8
@@ -3459,6 +3459,15 @@ static int whereLoopAddBtree(
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Return true if pTerm is a virtual table LIMIT or OFFSET term.
|
||||
*/
|
||||
static int isLimitTerm(WhereTerm *pTerm){
|
||||
assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 );
|
||||
return 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 +3495,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 +3521,7 @@ static int whereLoopAddVirtualOne(
|
||||
pIdxCons->usable = 0;
|
||||
if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
|
||||
&& (pTerm->eOperator & mExclude)==0
|
||||
&& (pbRetryLimit || !isLimitTerm(pTerm))
|
||||
){
|
||||
pIdxCons->usable = 1;
|
||||
}
|
||||
@@ -3543,8 +3554,8 @@ static int whereLoopAddVirtualOne(
|
||||
|
||||
mxTerm = -1;
|
||||
assert( pNew->nLSlot>=nConstraint );
|
||||
for(i=0; i<nConstraint; i++) pNew->aLTerm[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; i<nConstraint; i++, pIdxCons++){
|
||||
int iTerm;
|
||||
@@ -3578,6 +3589,9 @@ static int whereLoopAddVirtualOne(
|
||||
}else{
|
||||
testcase( i!=iTerm );
|
||||
}
|
||||
if( pTerm->eMatchOp==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
|
||||
@@ -3589,6 +3603,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 +3780,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 +3804,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 +3830,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 +3857,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 +3870,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 +3880,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 +5286,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 +5363,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 +5434,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
|
||||
|
||||
+7
-1
@@ -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 */
|
||||
@@ -453,6 +454,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 +542,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*);
|
||||
@@ -608,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) */
|
||||
|
||||
+10
-1
@@ -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);
|
||||
@@ -2358,7 +2367,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 ){
|
||||
|
||||
+113
-1
@@ -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
|
||||
@@ -1522,6 +1525,113 @@ 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.
|
||||
**
|
||||
** 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, /* 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 *pNew;
|
||||
int iVal = 0;
|
||||
|
||||
if( sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){
|
||||
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;
|
||||
idx = whereClauseInsert(pWC, pNew, 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].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;
|
||||
}
|
||||
|
||||
/* 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 ) return;
|
||||
if( NEVER(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. */
|
||||
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, p->pLimit->pRight,
|
||||
iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a preallocated WhereClause structure.
|
||||
*/
|
||||
@@ -1557,6 +1667,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);
|
||||
}
|
||||
@@ -1714,6 +1825,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);
|
||||
|
||||
Reference in New Issue
Block a user