📄 where.c
字号:
case TK_GT: iDirectGt[i] = j; break; } } } /* If we found a term that tests ROWID with == or IN, that term ** will be used to locate the rows in the database table. There ** is not need to continue into the code below that looks for ** an index. We will always use the ROWID over an index. */ if( iDirectEq[i]>=0 ){ loopMask |= mask; pLevel->pIdx = 0; continue; } /* Do a search for usable indices. Leave pBestIdx pointing to ** the "best" index. pBestIdx is left set to NULL if no indices ** are usable. ** ** The best index is the one with the highest score. The score ** for the index is determined as follows. For each of the ** left-most terms that is fixed by an equality operator, add ** 32 to the score. The right-most term of the index may be ** constrained by an inequality. Add 4 if for an "x<..." constraint ** and add 8 for an "x>..." constraint. If both constraints ** are present, add 12. ** ** If the left-most term of the index uses an IN operator ** (ex: "x IN (...)") then add 16 to the score. ** ** If an index can be used for sorting, add 2 to the score. ** If an index contains all the terms of a table that are ever ** used by any expression in the SQL statement, then add 1 to ** the score. ** ** This scoring system is designed so that the score can later be ** used to determine how the index is used. If the score&0x1c is 0 ** then all constraints are equalities. If score&0x4 is not 0 then ** there is an inequality used as a termination key. (ex: "x<...") ** If score&0x8 is not 0 then there is an inequality used as the ** start key. (ex: "x>..."). A score or 0x10 is the special case ** of an IN operator constraint. (ex: "x IN ..."). ** ** The IN operator (as in "<expr> IN (...)") is treated the same as ** an equality comparison except that it can only be used on the ** left-most column of an index and other terms of the WHERE clause ** cannot be used in conjunction with the IN operator to help satisfy ** other columns of the index. */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ Bitmask eqMask = 0; /* Index columns covered by an x=... term */ Bitmask ltMask = 0; /* Index columns covered by an x<... term */ Bitmask gtMask = 0; /* Index columns covered by an x>... term */ Bitmask inMask = 0; /* Index columns covered by an x IN .. term */ Bitmask m; int nEq, score, bRev = 0; if( pIdx->nColumn>sizeof(eqMask)*8 ){ continue; /* Ignore indices with too many columns to analyze */ } for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){ Expr *pX = pTerm->p; CollSeq *pColl = sqlite3ExprCollSeq(pParse, pX->pLeft); if( !pColl && pX->pRight ){ pColl = sqlite3ExprCollSeq(pParse, pX->pRight); } if( !pColl ){ pColl = pParse->db->pDfltColl; } if( pTerm->idxLeft==iCur && (pTerm->prereqRight & loopMask)==pTerm->prereqRight ){ int iColumn = pX->pLeft->iColumn; int k; char idxaff = iColumn>=0 ? pIdx->pTable->aCol[iColumn].affinity : 0; for(k=0; k<pIdx->nColumn; k++){ /* If the collating sequences or affinities don't match, ** ignore this index. */ if( pColl!=pIdx->keyInfo.aColl[k] ) continue; if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; if( pIdx->aiColumn[k]==iColumn ){ switch( pX->op ){ case TK_IN: { if( k==0 ) inMask |= 1; break; } case TK_EQ: { eqMask |= ((Bitmask)1)<<k; break; } case TK_LE: case TK_LT: { ltMask |= ((Bitmask)1)<<k; break; } case TK_GE: case TK_GT: { gtMask |= ((Bitmask)1)<<k; break; } default: { /* CANT_HAPPEN */ assert( 0 ); break; } } break; } } } } /* The following loop ends with nEq set to the number of columns ** on the left of the index with == constraints. */ for(nEq=0; nEq<pIdx->nColumn; nEq++){ m = (((Bitmask)1)<<(nEq+1))-1; if( (m & eqMask)!=m ) break; } /* Begin assemblying the score */ score = nEq*32; /* Base score is 32 times number of == constraints */ m = ((Bitmask)1)<<nEq; if( m & ltMask ) score+=4; /* Increase score for a < constraint */ if( m & gtMask ) score+=8; /* Increase score for a > constraint */ if( score==0 && inMask ) score = 16; /* Default score for IN constraint */ /* Give bonus points if this index can be used for sorting */ if( i==0 && score!=16 && ppOrderBy && *ppOrderBy ){ int base = pTabList->a[0].iCursor; if( isSortingIndex(pParse, pIdx, pTab, base, *ppOrderBy, nEq, &bRev) ){ score += 2; } } /* Check to see if we can get away with using just the index without ** ever reading the table. If that is the case, then add one bonus ** point to the score. */ if( score && pTabItem->colUsed < (((Bitmask)1)<<(BMS-1)) ){ for(m=0, j=0; j<pIdx->nColumn; j++){ int x = pIdx->aiColumn[j]; if( x<BMS-1 ){ m |= ((Bitmask)1)<<x; } } if( (pTabItem->colUsed & m)==pTabItem->colUsed ){ score++; } } /* If the score for this index is the best we have seen so far, then ** save it */ if( score>bestScore ){ pBestIdx = pIdx; bestScore = score; bestRev = bRev; } } pLevel->pIdx = pBestIdx; pLevel->score = bestScore; pLevel->bRev = bestRev; loopMask |= mask; if( pBestIdx ){ pLevel->iIdxCur = pParse->nTab++; } } /* Check to see if the ORDER BY clause is or can be satisfied by the ** use of an index on the first table. */ if( ppOrderBy && *ppOrderBy && pTabList->nSrc>0 ){ Index *pIdx; /* Index derived from the WHERE clause */ Table *pTab; /* Left-most table in the FROM clause */ int bRev = 0; /* True to reverse the output order */ int iCur; /* Btree-cursor that will be used by pTab */ WhereLevel *pLevel0 = &pWInfo->a[0]; pTab = pTabList->a[0].pTab; pIdx = pLevel0->pIdx; iCur = pTabList->a[0].iCursor; if( pIdx==0 && sortableByRowid(iCur, *ppOrderBy, &bRev) ){ /* The ORDER BY clause specifies ROWID order, which is what we ** were going to be doing anyway... */ *ppOrderBy = 0; pLevel0->bRev = bRev; }else if( pLevel0->score==16 ){ /* If there is already an IN index on the left-most table, ** it will not give the correct sort order. ** So, pretend that no suitable index is found. */ }else if( iDirectEq[0]>=0 || iDirectLt[0]>=0 || iDirectGt[0]>=0 ){ /* If the left-most column is accessed using its ROWID, then do ** not try to sort by index. But do delete the ORDER BY clause ** if it is redundant. */ }else if( (pLevel0->score&2)!=0 ){ /* The index that was selected for searching will cause rows to ** appear in sorted order. */ *ppOrderBy = 0; } } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ pLevel = pWInfo->a; for(i=0, pTabItem=pTabList->a; i<pTabList->nSrc; i++, pTabItem++, pLevel++){ Table *pTab; Index *pIx; int iIdxCur = pLevel->iIdxCur; pTab = pTabItem->pTab; if( pTab->isTransient || pTab->pSelect ) continue; if( (pLevel->score & 1)==0 ){ sqlite3OpenTableForReading(v, pTabItem->iCursor, pTab); } pLevel->iTabCur = pTabItem->iCursor; if( (pIx = pLevel->pIdx)!=0 ){ sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0); sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum, (char*)&pIx->keyInfo, P3_KEYINFO); } if( (pLevel->score & 1)!=0 ){ sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1); } sqlite3CodeVerifySchema(pParse, pTab->iDb); } pWInfo->iTop = sqlite3VdbeCurrentAddr(v); /* Generate the code to do the search */ loopMask = 0; pLevel = pWInfo->a; pTabItem = pTabList->a; for(i=0; i<pTabList->nSrc; i++, pTabItem++, pLevel++){ int j, k; int iCur = pTabItem->iCursor; /* The VDBE cursor for the table */ Index *pIdx; /* The index we will be using */ int iIdxCur; /* The VDBE cursor for the index */ int omitTable; /* True if we use the index only */ pIdx = pLevel->pIdx; iIdxCur = pLevel->iIdxCur; pLevel->inOp = OP_Noop; /* Check to see if it is appropriate to omit the use of the table ** here and use its index instead. */ omitTable = (pLevel->score&1)!=0; /* If this is the right table of a LEFT OUTER JOIN, allocate and ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ if( i>0 && (pTabList->a[i-1].jointype & JT_LEFT)!=0 ){ if( !pParse->nMem ) pParse->nMem++; pLevel->iLeftJoin = pParse->nMem++; sqlite3VdbeAddOp(v, OP_Null, 0, 0); sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1); VdbeComment((v, "# init LEFT JOIN no-match flag")); } if( i<ARRAYSIZE(iDirectEq) && (k = iDirectEq[i])>=0 ){ /* Case 1: We can directly reference a single row using an ** equality comparison against the ROWID field. Or ** we reference multiple rows using a "rowid IN (...)" ** construct. */ assert( k<nExpr ); pTerm = &aExpr[k]; assert( pTerm->p!=0 ); assert( pTerm->idxLeft==iCur ); assert( omitTable==0 ); brk = pLevel->brk = sqlite3VdbeMakeLabel(v); codeEqualityTerm(pParse, pTerm, brk, pLevel); cont = pLevel->cont = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp(v, OP_MustBeInt, 1, brk); sqlite3VdbeAddOp(v, OP_NotExists, iCur, brk); VdbeComment((v, "pk")); pLevel->op = OP_Noop; }else if( pIdx!=0 && pLevel->score>3 && (pLevel->score&0x0c)==0 ){ /* Case 2: There is an index and all terms of the WHERE clause that ** refer to the index using the "==" or "IN" operators. */ int start; int nColumn = (pLevel->score+16)/32; brk = pLevel->brk = sqlite3VdbeMakeLabel(v); /* For each column of the index, find the term of the WHERE clause that ** constraints that column. If the WHERE clause term is X=expr, then ** evaluation expr and leave the result on the stack */ for(j=0; j<nColumn; j++){ for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){ Expr *pX = pTerm->p; if( pX==0 ) continue; if( pTerm->idxLeft==iCur && (pTerm->prereqRight & loopMask)==pTerm->prereqRight && pX->pLeft->iColumn==pIdx->aiColumn[j] && (pX->op==TK_EQ || pX->op==TK_IN) ){ char idxaff = pIdx->pTable->aCol[pX->pLeft->iColumn].affinity; if( sqlite3IndexAffinityOk(pX, idxaff) ){ codeEqualityTerm(pParse, pTerm, brk, pLevel); break; } } } } pLevel->iMem = pParse->nMem++; cont = pLevel->cont = sqlite3VdbeMakeLabel(v); buildIndexProbe(v, nColumn, brk, pIdx); sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0); /* Generate code (1) to move to the first matching element of the table. ** Then generate code (2) that jumps to "brk" after the cursor is past ** the last matching element of the table. The code (1) is executed ** once to initialize the search, the code (2) is executed before each ** iteration of the scan to see if the scan has finished. */ if( pLevel->bRev ){ /* Scan in reverse order */ sqlite3VdbeAddOp(v, OP_MoveLe, iIdxCur, brk); start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); sqlite3VdbeAddOp(v, OP_IdxLT, iIdxCur, brk); pLevel->op = OP_Prev; }else{ /* Scan in the forward order */ sqlite3VdbeAddOp(v, OP_MoveGe, iIdxCur, brk); start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, brk, "+", P3_STATIC); pLevel->op = OP_Next; } sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0); sqlite3VdbeAddOp(v, OP_IdxIsNull, nColumn, cont); if( !omitTable ){ sqlite3VdbeAddOp(v, OP_IdxRowid, iIdxCur, 0); sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); } pLevel->p1 = iIdxCur; pLevel->p2 = start; }else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[i]>=0 || iDirectGt[i]>=0) ){ /* Case 3: We have an inequality comparison against the ROWID field. */ int testOp = OP_Noop; int start; int bRev = pLevel->bRev; assert( omitTable==0 ); brk = pLevel->brk = sqlite3VdbeMakeLabel(v); cont = pLevel->cont = sqlite3VdbeMakeLabel(v); if( bRev ){ int t = iDirectGt[i]; iDirectGt[i] = iDirectLt[i]; iDirectLt[i] = t;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -