1
0
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:
dan
2024-05-31 19:26:22 +00:00
parent 4c86b2db0f
commit cadfdd4e09
5 changed files with 178 additions and 76 deletions

View File

@ -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);

View File

@ -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;