mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
Better handle WHERE terms that are common to two or more OR branches when planning virtual table queries.
FossilOrigin-Name: 4edd9b29f58621335b8a562280c991c34804bbba090f90c951261d043cff1965
This commit is contained in:
@ -700,6 +700,10 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
pIdxInfo->aConstraintUsage[iCons].omit = bOmit;
|
||||
}
|
||||
}
|
||||
}else
|
||||
if( sqlite3_stricmp("constraint", zCmd)==0 ){
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", Tcl_GetString(p));
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
|
||||
|
152
src/where.c
152
src/where.c
@ -1346,6 +1346,20 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Return term iTerm of the WhereClause passed as the first argument. Terms
|
||||
** are numbered from 0 upwards, starting with the terms in pWC->a[], then
|
||||
** those in pWC->pOuter->a[] (if any), and so on.
|
||||
*/
|
||||
static WhereTerm *termFromWhereClause(WhereClause *pWC, int iTerm){
|
||||
WhereClause *p;
|
||||
for(p=pWC; p; p=p->pOuter){
|
||||
if( iTerm<p->nTerm ) return &p->a[iTerm];
|
||||
iTerm -= p->nTerm;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and populate an sqlite3_index_info structure. It is the
|
||||
** responsibility of the caller to eventually release the structure
|
||||
@ -1372,6 +1386,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
const Table *pTab;
|
||||
int eDistinct = 0;
|
||||
ExprList *pOrderBy = pWInfo->pOrderBy;
|
||||
WhereClause *p;
|
||||
|
||||
assert( pSrc!=0 );
|
||||
pTab = pSrc->pTab;
|
||||
@ -1382,28 +1397,30 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
** Mark each term with the TERM_OK flag. Set nTerm to the number of
|
||||
** terms found.
|
||||
*/
|
||||
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
pTerm->wtFlags &= ~TERM_OK;
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
if( pTerm->prereqRight & mUnusable ) continue;
|
||||
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
testcase( pTerm->eOperator & WO_IS );
|
||||
testcase( pTerm->eOperator & WO_ALL );
|
||||
if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
for(p=pWC, nTerm=0; p; p=p->pOuter){
|
||||
for(i=0, pTerm=p->a; i<p->nTerm; i++, pTerm++){
|
||||
pTerm->wtFlags &= ~TERM_OK;
|
||||
if( pTerm->leftCursor != pSrc->iCursor ) continue;
|
||||
if( pTerm->prereqRight & mUnusable ) continue;
|
||||
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
|
||||
testcase( pTerm->eOperator & WO_IN );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
testcase( pTerm->eOperator & WO_IS );
|
||||
testcase( pTerm->eOperator & WO_ALL );
|
||||
if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
|
||||
if( pTerm->wtFlags & TERM_VNULL ) continue;
|
||||
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
assert( pTerm->u.x.leftColumn>=XN_ROWID );
|
||||
assert( pTerm->u.x.leftColumn<pTab->nCol );
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
|
||||
&& !constraintCompatibleWithOuterJoin(pTerm,pSrc)
|
||||
){
|
||||
continue;
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
assert( pTerm->u.x.leftColumn>=XN_ROWID );
|
||||
assert( pTerm->u.x.leftColumn<pTab->nCol );
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
|
||||
&& !constraintCompatibleWithOuterJoin(pTerm,pSrc)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
nTerm++;
|
||||
pTerm->wtFlags |= TERM_OK;
|
||||
}
|
||||
nTerm++;
|
||||
pTerm->wtFlags |= TERM_OK;
|
||||
}
|
||||
|
||||
/* If the ORDER BY clause contains only columns in the current
|
||||
@ -1482,49 +1499,52 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
pHidden->pParse = pParse;
|
||||
pHidden->eDistinct = eDistinct;
|
||||
pHidden->mIn = 0;
|
||||
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
u16 op;
|
||||
if( (pTerm->wtFlags & TERM_OK)==0 ) continue;
|
||||
pIdxCons[j].iColumn = pTerm->u.x.leftColumn;
|
||||
pIdxCons[j].iTermOffset = i;
|
||||
op = pTerm->eOperator & WO_ALL;
|
||||
if( op==WO_IN ){
|
||||
if( (pTerm->wtFlags & TERM_SLICE)==0 ){
|
||||
pHidden->mIn |= SMASKBIT32(j);
|
||||
for(p=pWC, i=j=0; p; p=p->pOuter){
|
||||
int nLast = i+p->nTerm;;
|
||||
for(pTerm=p->a; i<nLast; i++, pTerm++){
|
||||
u16 op;
|
||||
if( (pTerm->wtFlags & TERM_OK)==0 ) continue;
|
||||
pIdxCons[j].iColumn = pTerm->u.x.leftColumn;
|
||||
pIdxCons[j].iTermOffset = i;
|
||||
op = pTerm->eOperator & WO_ALL;
|
||||
if( op==WO_IN ){
|
||||
if( (pTerm->wtFlags & TERM_SLICE)==0 ){
|
||||
pHidden->mIn |= SMASKBIT32(j);
|
||||
}
|
||||
op = WO_EQ;
|
||||
}
|
||||
op = WO_EQ;
|
||||
}
|
||||
if( op==WO_AUX ){
|
||||
pIdxCons[j].op = pTerm->eMatchOp;
|
||||
}else if( op & (WO_ISNULL|WO_IS) ){
|
||||
if( op==WO_ISNULL ){
|
||||
pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL;
|
||||
if( op==WO_AUX ){
|
||||
pIdxCons[j].op = pTerm->eMatchOp;
|
||||
}else if( op & (WO_ISNULL|WO_IS) ){
|
||||
if( op==WO_ISNULL ){
|
||||
pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL;
|
||||
}else{
|
||||
pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS;
|
||||
}
|
||||
}else{
|
||||
pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS;
|
||||
}
|
||||
}else{
|
||||
pIdxCons[j].op = (u8)op;
|
||||
/* The direct assignment in the previous line is possible only because
|
||||
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
|
||||
** following asserts verify this fact. */
|
||||
assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
|
||||
assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
|
||||
assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
|
||||
assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
|
||||
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
|
||||
assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) );
|
||||
pIdxCons[j].op = (u8)op;
|
||||
/* The direct assignment in the previous line is possible only because
|
||||
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
|
||||
** following asserts verify this fact. */
|
||||
assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
|
||||
assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
|
||||
assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
|
||||
assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
|
||||
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
|
||||
assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) );
|
||||
|
||||
if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
|
||||
&& sqlite3ExprIsVector(pTerm->pExpr->pRight)
|
||||
){
|
||||
testcase( j!=i );
|
||||
if( j<16 ) mNoOmit |= (1 << j);
|
||||
if( op==WO_LT ) pIdxCons[j].op = WO_LE;
|
||||
if( op==WO_GT ) pIdxCons[j].op = WO_GE;
|
||||
if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
|
||||
&& sqlite3ExprIsVector(pTerm->pExpr->pRight)
|
||||
){
|
||||
testcase( j!=i );
|
||||
if( j<16 ) mNoOmit |= (1 << j);
|
||||
if( op==WO_LT ) pIdxCons[j].op = WO_LE;
|
||||
if( op==WO_GT ) pIdxCons[j].op = WO_GE;
|
||||
}
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
assert( j==nTerm );
|
||||
pIdxInfo->nConstraint = j;
|
||||
@ -4159,7 +4179,7 @@ static int whereLoopAddVirtualOne(
|
||||
** arguments mUsable and mExclude. */
|
||||
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
|
||||
for(i=0; i<nConstraint; i++, pIdxCons++){
|
||||
WhereTerm *pTerm = &pWC->a[pIdxCons->iTermOffset];
|
||||
WhereTerm *pTerm = termFromWhereClause(pWC, pIdxCons->iTermOffset);
|
||||
pIdxCons->usable = 0;
|
||||
if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
|
||||
&& (pTerm->eOperator & mExclude)==0
|
||||
@ -4207,7 +4227,7 @@ static int whereLoopAddVirtualOne(
|
||||
int j = pIdxCons->iTermOffset;
|
||||
if( iTerm>=nConstraint
|
||||
|| j<0
|
||||
|| j>=pWC->nTerm
|
||||
|| (pTerm = termFromWhereClause(pWC, j))==0
|
||||
|| pNew->aLTerm[iTerm]!=0
|
||||
|| pIdxCons->usable==0
|
||||
){
|
||||
@ -4218,7 +4238,6 @@ static int whereLoopAddVirtualOne(
|
||||
testcase( iTerm==nConstraint-1 );
|
||||
testcase( j==0 );
|
||||
testcase( j==pWC->nTerm-1 );
|
||||
pTerm = &pWC->a[j];
|
||||
pNew->prereq |= pTerm->prereqRight;
|
||||
assert( iTerm<pNew->nLSlot );
|
||||
pNew->aLTerm[iTerm] = pTerm;
|
||||
@ -4335,7 +4354,7 @@ const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){
|
||||
if( iCons>=0 && iCons<pIdxInfo->nConstraint ){
|
||||
CollSeq *pC = 0;
|
||||
int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset;
|
||||
Expr *pX = pHidden->pWC->a[iTerm].pExpr;
|
||||
Expr *pX = termFromWhereClause(pHidden->pWC, iTerm)->pExpr;
|
||||
if( pX->pLeft ){
|
||||
pC = sqlite3ExprCompareCollSeq(pHidden->pParse, pX);
|
||||
}
|
||||
@ -4381,7 +4400,9 @@ int sqlite3_vtab_rhs_value(
|
||||
rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */
|
||||
}else{
|
||||
if( pH->aRhs[iCons]==0 ){
|
||||
WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset];
|
||||
WhereTerm *pTerm = termFromWhereClause(
|
||||
pH->pWC, pIdxInfo->aConstraint[iCons].iTermOffset
|
||||
);
|
||||
rc = sqlite3ValueFromExpr(
|
||||
pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db),
|
||||
SQLITE_AFF_BLOB, &pH->aRhs[iCons]
|
||||
@ -4537,9 +4558,8 @@ static int whereLoopAddVirtual(
|
||||
Bitmask mNext = ALLBITS;
|
||||
assert( mNext>0 );
|
||||
for(i=0; i<nConstraint; i++){
|
||||
Bitmask mThis = (
|
||||
pWC->a[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq
|
||||
);
|
||||
int iTerm = p->aConstraint[i].iTermOffset;
|
||||
Bitmask mThis = termFromWhereClause(pWC, iTerm)->prereqRight & ~mPrereq;
|
||||
if( mThis>mPrev && mThis<mNext ) mNext = mThis;
|
||||
}
|
||||
mPrev = mNext;
|
||||
|
Reference in New Issue
Block a user