1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-21 09:00:59 +03:00

In where.c, make findTerm() a wrapper around methods to a new WhereScan object

which is capable of finding all suitable matching terms, not just the first.
This check-in includes some prototype functions for building WhereLoop objects.

FossilOrigin-Name: dd92b8fa929badaf2f79e8a00c83667a9d589096
This commit is contained in:
drh
2013-05-04 20:25:23 +00:00
parent f1b5f5b855
commit 1c8148fffb
3 changed files with 292 additions and 106 deletions

View File

@@ -42,6 +42,8 @@ typedef struct WhereCost WhereCost;
typedef struct WhereLoop WhereLoop;
typedef struct WherePath WherePath;
typedef struct WhereTerm WhereTerm;
typedef struct WhereLoopBuilder WhereLoopBuilder;
typedef struct WhereScan WhereScan;
/*
** Each instance of this object represents a way of evaluating one
@@ -60,7 +62,7 @@ struct WhereLoop {
u16 nEq; /* Number of equality constraints */
u16 nTerm; /* Number of entries in aTerm[] */
Index *pIndex; /* Index used */
WhereTerm *aTerm; /* WhereTerms used */
WhereTerm **aTerm; /* WhereTerms used */
WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */
};
@@ -161,6 +163,23 @@ struct WhereTerm {
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
#endif
/*
** An instance of the WhereScan object is used as an iterator for locating
** terms in the WHERE clause that are useful to the query planner.
*/
struct WhereScan {
WhereTerm *pCurrent; /* Most recent match */
WhereClause *pOrigWC; /* Original, innermost WhereClause */
WhereClause *pWC; /* WhereClause currently being scanned */
char *zCollName; /* Must have this collating sequence, if not NULL */
char idxaff; /* Must match this affinity, if zCollName!=NULL */
unsigned char nEquiv; /* Number of entries in aEquiv[] */
unsigned char iEquiv; /* Next unused slot in aEquiv[] */
u32 opMask; /* Acceptable operators */
int k; /* Resume scanning at this->pWC->a[this->k] */
int aEquiv[22]; /* Cursor,Column pairs for equivalence classes */
};
/*
** An instance of the following structure holds all information about a
** WHERE clause. Mostly this is a container for one or more WhereTerms.
@@ -247,6 +266,19 @@ struct WhereCost {
Bitmask used; /* Bitmask of cursors used by this plan */
};
/*
** This object is a factory for WhereLoop objects for a particular query.
*/
struct WhereLoopBuilder {
WhereInfo *pWInfo; /* Information about this WHERE */
sqlite3 *db; /* Database connection */
Parse *pParse; /* Parsing context */
WhereClause *pWC; /* WHERE clause terms */
SrcList *pTabList; /* FROM clause */
ExprList *pOrderBy; /* ORDER BY clause */
WhereLoop *pNew; /* Template WhereLoop */
};
/*
** Bitmasks for the operators that indices are able to exploit. An
** OR-ed combination of these values can be used when searching for
@@ -661,6 +693,114 @@ static u16 operatorMask(int op){
return c;
}
/*
** Advance to the next WhereTerm that matches according to the criteria
** established when the pScan object was initialized by whereScanInit().
** Return NULL if there are no more matching WhereTerms.
*/
WhereTerm *whereScanNext(WhereScan *pScan){
int iCur; /* The cursor on the LHS of the term */
int iColumn; /* The column on the LHS of the term. -1 for IPK */
Expr *pX; /* An expression being tested */
WhereClause *pWC; /* Shorthand for pScan->pWC */
WhereTerm *pTerm; /* The term being tested */
while( pScan->iEquiv<=pScan->nEquiv ){
iCur = pScan->aEquiv[pScan->iEquiv-2];
iColumn = pScan->aEquiv[pScan->iEquiv-1];
while( (pWC = pScan->pWC)!=0 ){
for(pTerm=pWC->a+pScan->k; pScan->k<pWC->nTerm; pScan->k++, pTerm++){
if( pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn ){
if( (pTerm->eOperator & WO_EQUIV)!=0
&& pScan->nEquiv<ArraySize(pScan->aEquiv)
){
int j;
pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
assert( pX->op==TK_COLUMN );
for(j=0; j<pScan->nEquiv; j+=2){
if( pScan->aEquiv[j]==pX->iTable
&& pScan->aEquiv[j+1]==pX->iColumn ){
break;
}
}
if( j==pScan->nEquiv ){
pScan->aEquiv[j] = pX->iTable;
pScan->aEquiv[j+1] = pX->iColumn;
pScan->nEquiv += 2;
}
}
if( (pTerm->eOperator & pScan->opMask)!=0 ){
/* Verify the affinity and collating sequence match */
if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
CollSeq *pColl;
pX = pTerm->pExpr;
if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
continue;
}
assert(pX->pLeft);
pColl = sqlite3BinaryCompareCollSeq(pWC->pParse,
pX->pLeft, pX->pRight);
if( pColl==0 ) pColl = pWC->pParse->db->pDfltColl;
if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
continue;
}
}
pScan->pCurrent = pTerm;
pScan->k++;
return pTerm;
}
}
}
pWC = pScan->pWC = pScan->pWC->pOuter;
pScan->k = 0;
}
pScan->pWC = pScan->pOrigWC;
pScan->k = 0;
pScan->iEquiv += 2;
}
pScan->pCurrent = 0;
return 0;
}
/*
** Initialize a WHERE clause scanner object. Return a pointer to the
** first match. Return NULL if there are no matches.
**
** The scanner will be searching the WHERE clause pWC. It will look
** for terms of the form "X <op> <expr>" where X is column iColumn of table
** iCur. The <op> must be one of the operators described by opMask.
**
** If X is not the INTEGER PRIMARY KEY then X must be compatible with
** index pIdx.
*/
WhereTerm *whereScanInit(
WhereScan *pScan, /* The WhereScan object being initialized */
WhereClause *pWC, /* The WHERE clause to be scanned */
int iCur, /* Cursor to scan for */
int iColumn, /* Column to scan for */
u32 opMask, /* Operator(s) to scan for */
Index *pIdx /* Must be compatible with this index */
){
int j;
memset(pScan, 0, sizeof(*pScan));
pScan->pOrigWC = pWC;
pScan->pWC = pWC;
if( pIdx && iColumn>=0 ){
pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
if( NEVER(j>=pIdx->nColumn) ) return 0;
}
pScan->zCollName = pIdx->azColl[j];
}
pScan->opMask = opMask;
pScan->aEquiv[0] = iCur;
pScan->aEquiv[1] = iColumn;
pScan->nEquiv = 2;
pScan->iEquiv = 2;
return whereScanNext(pScan);
}
/*
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
** where X is a reference to the iColumn of table iCur and <op> is one of
@@ -692,84 +832,20 @@ static WhereTerm *findTerm(
u32 op, /* Mask of WO_xx values describing operator */
Index *pIdx /* Must be compatible with this index, if not NULL */
){
WhereTerm *pTerm; /* Term being examined as possible result */
WhereTerm *pResult = 0; /* The answer to return */
WhereClause *pWCOrig = pWC; /* Original pWC value */
int j, k; /* Loop counters */
Expr *pX; /* Pointer to an expression */
Parse *pParse; /* Parsing context */
int iOrigCol = iColumn; /* Original value of iColumn */
int nEquiv = 2; /* Number of entires in aEquiv[] */
int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
WhereTerm *pResult = 0;
WhereTerm *p;
WhereScan scan;
assert( iCur>=0 );
aEquiv[0] = iCur;
aEquiv[1] = iColumn;
for(;;){
for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
if( pTerm->leftCursor==iCur
&& pTerm->u.leftColumn==iColumn
){
if( (pTerm->prereqRight & notReady)==0
&& (pTerm->eOperator & op & WO_ALL)!=0
){
if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
CollSeq *pColl;
char idxaff;
pX = pTerm->pExpr;
pParse = pWC->pParse;
idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
if( !sqlite3IndexAffinityOk(pX, idxaff) ){
continue;
}
/* Figure out the collation sequence required from an index for
** it to be useful for optimising expression pX. Store this
** value in variable pColl.
*/
assert(pX->pLeft);
pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
if( pColl==0 ) pColl = pParse->db->pDfltColl;
for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
if( NEVER(j>=pIdx->nColumn) ) return 0;
}
if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
continue;
}
}
if( pTerm->prereqRight==0 && (pTerm->eOperator&WO_EQ)!=0 ){
pResult = pTerm;
goto findTerm_success;
}else if( pResult==0 ){
pResult = pTerm;
}
}
if( (pTerm->eOperator & WO_EQUIV)!=0
&& nEquiv<ArraySize(aEquiv)
){
pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
assert( pX->op==TK_COLUMN );
for(j=0; j<nEquiv; j+=2){
if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
}
if( j==nEquiv ){
aEquiv[j] = pX->iTable;
aEquiv[j+1] = pX->iColumn;
nEquiv += 2;
}
}
}
p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
while( p ){
if( (p->prereqRight & notReady)==0 ){
if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){
return p;
}
if( pResult==0 ) pResult = p;
}
if( iEquiv>=nEquiv ) break;
iCur = aEquiv[iEquiv++];
iColumn = aEquiv[iEquiv++];
p = whereScanNext(&scan);
}
findTerm_success:
return pResult;
}
@@ -5058,38 +5134,139 @@ static int whereLoopInsert(WhereInfo *pWInfo, WhereLoop *pTemplate){
p->pNextLoop = pWInfo->pLoops;
pWInfo->pLoops = p;
if( pTemplate->nTerm<=0 ) return SQLITE_OK;
if( p->pIndex && p->pIndex->tnum==0 ) p->pIndex = 0;
p->aTerm = sqlite3DbMallocRaw(db, pTemplate->nTerm*sizeof(p->aTerm[0]));
if( p->aTerm==0 ){
p->nTerm = 0;
sqlite3DbFree(db, p);
return SQLITE_NOMEM;
}
memcpy(p->aTerm, pTemplate->aTerm, pTemplate->nTerm*sizeof(p->aTerm[0]));
return SQLITE_OK;
}
/*
** We have so far matched pBuilder->pNew->nEq terms of the index pIndex.
** Try to match one more.
**
** If pProbe->tnum==0, that means pIndex is a fake index used for the
** INTEGER PRIMARY KEY.
*/
static void whereLoopAddBtreeIndex(
WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
struct SrcList_item *pSrc, /* FROM clause term being analyzed */
Index *pProbe, /* An index on pSrc */
int nInMul /* Number of iterations due to IN */
){
sqlite3 *db; /* Database connection malloc context */
WhereLoop *pNew; /* Template WhereLoop under construction */
WhereTerm *pTerm; /* A WhereTerm under consideration */
int eqTermMask; /* Valid equality operators */
WhereScan scan; /* Iterator for WHERE terms */
db = pBuilder->db;
pNew = pBuilder->pNew;
if( db->mallocFailed ) return;
if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
eqTermMask = WO_EQ|WO_IN;
}else{
eqTermMask = WO_EQ|WO_IN|WO_ISNULL;
}
if( pNew->nEq<pProbe->nColumn ){
int iCol; /* Index of the column in the table */
iCol = pProbe->aiColumn[pNew->nEq];
pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
eqTermMask, iCol>=0 ? pProbe : 0);
pNew->nEq++;
for(; pTerm!=0; pTerm = whereScanNext(&scan)){
pNew->aTerm[pNew->nEq-1] = pTerm;
}
pNew->nEq--;
}
}
/*
** Add all WhereLoop objects for the iTab-th table of the join. That
** table is guaranteed to be a b-tree table, not a virtual table.
*/
static void whereLoopAddBtree(
WhereInfo *pWInfo, /* WHERE clause information */
int iTab, /* The table to process */
Bitmask mExtra /* Extra prerequesites for using this table */
WhereLoopBuilder *pBuilder, /* WHERE clause information */
int iTab, /* The table to process */
Bitmask mExtra /* Extra prerequesites for using this table */
){
WhereLoop *pNew; /* Template WhereLoop object */
sqlite3 *db = pWInfo->pParse->db;
Index *pProbe; /* An index we are evaluating */
int eqTermMask; /* Current mask of valid equality operators */
Index sPk; /* A fake index object for the primary key */
tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
struct SrcList_item *pSrc; /* The FROM clause btree term to add */
sqlite3 *db; /* The database connection */
WhereLoop *pNew; /* Template WhereLoop object */
pNew = sqlite3DbMallocZero(db, sizeof(*pNew));
if( pNew==0 ) return;
pNew = pBuilder->pNew;
db = pBuilder->db;
pSrc = pBuilder->pTabList->a + iTab;
if( pSrc->pIndex ){
/* An INDEXED BY clause specifies a particular index to use */
pProbe = pSrc->pIndex;
}else{
/* There is no INDEXED BY clause. Create a fake Index object in local
** variable sPk to represent the rowid primary key index. Make this
** fake index the first in a chain of Index objects with all of the real
** indices to follow */
Index *pFirst; /* First of real indices on the table */
memset(&sPk, 0, sizeof(Index));
sPk.nColumn = 1;
sPk.aiColumn = &aiColumnPk;
sPk.aiRowEst = aiRowEstPk;
sPk.onError = OE_Replace;
sPk.pTable = pSrc->pTab;
aiRowEstPk[0] = pSrc->pTab->nRowEst;
aiRowEstPk[1] = 1;
pFirst = pSrc->pTab->pIndex;
if( pSrc->notIndexed==0 ){
/* The real indices of the table are only considered if the
** NOT INDEXED qualifier is omitted from the FROM clause */
sPk.pNext = pFirst;
}
pProbe = &sPk;
}
/* Loop over all indices
*/
for(; pProbe; pProbe=pProbe->pNext){
pNew->prereq = mExtra;
pNew->iTab = iTab;
pNew->nEq = 0;
pNew->nTerm = 0;
pNew->aTerm = sqlite3DbRealloc(db, pNew->aTerm,
(pProbe->nColumn+1)*sizeof(pNew->aTerm[0]));
if( pNew->aTerm==0 ) break;
pNew->pIndex = pProbe;
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 1);
/* If there was an INDEXED BY clause, then only that one index is
** considered. */
if( pSrc->pIndex ) break;
}
#if 0
/* Insert a full table scan */
pNew->iTab = iTab;
pNew->rSetup = (double)0;
pNew->rRun = (double)1000000;
pNew->nOut = (double)1000000;
whereLoopInsert(pWInfo, pNew);
whereLoopDelete(db, pNew);
whereLoopInsert(pBuilder->pWInfo, pNew);
#endif
}
/*
@@ -5097,30 +5274,32 @@ static void whereLoopAddBtree(
** table is guaranteed to be a virtual table.
*/
static void whereLoopAddVirtual(
WhereInfo *pWInfo, /* WHERE clause information */
int iTab, /* The table to process */
Bitmask mExtra /* Extra prerequesites for using this table */
WhereLoopBuilder *pBuilder, /* WHERE clause information */
int iTab, /* The table to process */
Bitmask mExtra /* Extra prerequesites for using this table */
){
}
/*
** Add all WhereLoop objects for all tables
*/
static void whereLoopAddAll(WhereInfo *pWInfo){
static void whereLoopAddAll(WhereLoopBuilder *pBuilder){
Bitmask mExtra = 0;
Bitmask mPrior = 0;
int iTab;
SrcList *pTabList = pWInfo->pTabList;
SrcList *pTabList = pBuilder->pTabList;
struct SrcList_item *pItem;
WhereClause *pWC = pWInfo->pWC;
sqlite3 *db = pWInfo->pParse->db;
WhereClause *pWC = pBuilder->pWC;
sqlite3 *db = pBuilder->db;
/* Loop over the tables in the join, from left to right */
pBuilder->pNew = sqlite3DbMallocZero(db, sizeof(WhereLoop));
if( pBuilder->pNew==0 ) return;
for(iTab=0, pItem=pTabList->a; iTab<pTabList->nSrc; iTab++, pItem++){
if( IsVirtual(pItem->pTab) ){
whereLoopAddVirtual(pWInfo, iTab, mExtra);
whereLoopAddVirtual(pBuilder, iTab, mExtra);
}else{
whereLoopAddBtree(pWInfo, iTab, mExtra);
whereLoopAddBtree(pBuilder, iTab, mExtra);
}
mPrior |= getMask(pWC->pMaskSet, pItem->iCursor);
if( (pItem->jointype & (JT_LEFT|JT_CROSS))!=0 ){
@@ -5128,6 +5307,8 @@ static void whereLoopAddAll(WhereInfo *pWInfo){
}
if( db->mallocFailed ) break;
}
whereLoopDelete(db, pBuilder->pNew);
pBuilder->pNew = 0;
}
@@ -5234,6 +5415,7 @@ WhereInfo *sqlite3WhereBegin(
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
Bitmask notReady; /* Cursors that are not yet positioned */
WhereBestIdx sWBI; /* Best index search context */
WhereLoopBuilder sWLB; /* The WhereLoop builder */
WhereMaskSet *pMaskSet; /* The expression mask set */
WhereLevel *pLevel; /* A single level in pWInfo->a[] */
int iFrom; /* First unused FROM clause element */
@@ -5245,6 +5427,11 @@ WhereInfo *sqlite3WhereBegin(
/* Variable initialization */
memset(&sWBI, 0, sizeof(sWBI));
sWBI.pParse = pParse;
memset(&sWLB, 0, sizeof(sWLB));
sWLB.pParse = pParse;
sWLB.db = pParse->db;
sWLB.pTabList = pTabList;
sWLB.pOrderBy = pOrderBy;
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -5290,6 +5477,8 @@ WhereInfo *sqlite3WhereBegin(
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
pMaskSet = (WhereMaskSet*)&sWBI.pWC[1];
sWBI.aLevel = pWInfo->a;
sWLB.pWInfo = pWInfo;
sWLB.pWC = pWInfo->pWC;
/* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
@@ -5362,7 +5551,7 @@ WhereInfo *sqlite3WhereBegin(
/* Construct the WhereLoop objects */
WHERETRACE(("*** Optimizer Start ***\n"));
whereLoopAddAll(pWInfo);
/*whereLoopAddAll(&sWLB);*/
/* Display all of the WhereLoop objects if wheretrace is enabled */
#if defined(SQLITE_DEBUG) \