From: drh Date: Wed, 7 Apr 2010 14:59:45 +0000 (+0000) Subject: Make sure that all automatic indices are covering indices. Otherwise, the X-Git-Tag: version-3.7.2~489^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4139c99eba8268b7b17f2128e4308797c887a2e8;p=thirdparty%2Fsqlite.git Make sure that all automatic indices are covering indices. Otherwise, the table and index might be used together in a query but the table could get out of sync with the automatic index through out-of-band changes. FossilOrigin-Name: 2364313142668b3d0ad144447b4862709be929cd --- diff --git a/manifest b/manifest index cf5e6b6d39..b147f9bb0a 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -C Enhance\scomments\son\sthe\sSrcList\sobject\sdefinition\sto\sbetter\sexplain\sthe\noperation\sof\sthe\sSrcList.a[].colUsed\sfield.\s\sNo\schanges\sto\scode. -D 2010-04-07T14:33:07 +C Make\ssure\sthat\sall\sautomatic\sindices\sare\scovering\sindices.\s\sOtherwise,\sthe\ntable\sand\sindex\smight\sbe\sused\stogether\sin\sa\squery\sbut\sthe\stable\scould\sget\nout\sof\ssync\swith\sthe\sautomatic\sindex\sthrough\sout-of-band\schanges. +D 2010-04-07T14:59:45 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -224,7 +224,7 @@ F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f -F src/where.c 6831620423f91e2e767a51de1403e8f3b6316fcd +F src/where.c 186b72b01f9afe0b4dbb68e90e14d6dceb287aed F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 @@ -797,14 +797,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 064c283eee82c5053e84058a0e1908e468da6ce3 -R 2ea9a93d8f9ff175759bacc0a1fd3296 +P c0f67ea131c3e237e46311b1a3dcb3febf2ea036 +R 0e5421f4e341a57c85b79d77e22a68e3 U drh -Z 49063b48e9e24945a681e3cbaa857e35 +Z b97f0b4c3618aad21acc127e7b33602d -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) -iD8DBQFLvJepoxKgR168RlERAvdbAJ4j8n07sFWyrBxSwl1Z/sTtVx4x8gCfX6OS -0ygpWKStUSB8+v8Z3feY2PY= -=69dZ +iD8DBQFLvJ3koxKgR168RlERAm7oAJ0Xzm3BtJ0ZfA9RjpPh9mHBaxTARgCfQDNh +ievdlvN7dk9MClGNwMCoKvk= +=ZzMO -----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 081336fef9..3ea21ea2f3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c0f67ea131c3e237e46311b1a3dcb3febf2ea036 \ No newline at end of file +2364313142668b3d0ad144447b4862709be929cd \ No newline at end of file diff --git a/src/where.c b/src/where.c index dc317dc06a..b5601922af 100644 --- a/src/where.c +++ b/src/where.c @@ -1637,9 +1637,28 @@ static void bestOrClauseIndex( #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ } +/* +** Return TRUE if the WHERE clause term pTerm is of a form where it +** could be used with an index to access pSrc, assuming an appropriate +** index existed. +*/ +static int termCanDriveIndex( + WhereTerm *pTerm, /* WHERE clause term to check */ + struct SrcList_item *pSrc, /* Table we are trying to access */ + Bitmask notReady /* Tables in outer loops of the join */ +){ + char aff; + if( pTerm->leftCursor!=pSrc->iCursor ) return 0; + if( pTerm->eOperator!=WO_EQ ) return 0; + if( (pTerm->prereqRight & notReady)!=0 ) return 0; + aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; + if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; + return 1; +} + /* ** If the query plan for pSrc specified in pCost is a full table scan -** an indexing is allows (if there is no NOT INDEXED clause) and it +** and indexing is allows (if there is no NOT INDEXED clause) and it ** possible to construct a transient index that would perform better ** than a full table scan even when the cost of constructing the index ** is taken into account, then alter the query plan to use the @@ -1682,12 +1701,7 @@ static void bestTransientIndex( pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTermleftCursor==pSrc->iCursor - && (pTerm->prereqRight & notReady)==0 - && (pTerm->eOperator & WO_EQ)!=0 - && sqlite3IndexAffinityOk(pTerm->pExpr, - pTable->aCol[pTerm->u.leftColumn].affinity) - ){ + if( termCanDriveIndex(pTerm, pSrc, notReady) ){ WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; @@ -1723,7 +1737,11 @@ static void constructTransientIndex( int addrTop; /* Top of the index fill loop */ int regRecord; /* Register holding an index record */ int n; /* Column counter */ + int i; /* Loop counter */ + int mxBitCol; /* Maximum column in pSrc->colUsed */ CollSeq *pColl; /* Collating sequence to on a column */ + Bitmask idxCols; /* Bitmap of columns used for indexing */ + Bitmask extraCols; /* Bitmap of additional columns */ /* Generate code to skip over the creation and initialization of the ** transient index on 2nd and subsequent iterations of the loop. */ @@ -1733,23 +1751,39 @@ static void constructTransientIndex( addrInit = sqlite3VdbeAddOp1(v, OP_If, regIsInit); sqlite3VdbeAddOp2(v, OP_Integer, 1, regIsInit); - /* Count the number of columns that will be added to the index */ + /* Count the number of columns that will be added to the index + ** and used to match WHERE clause constraints */ nColumn = 0; pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; + idxCols = 0; for(pTerm=pWC->a; pTermleftCursor==pSrc->iCursor - && (pTerm->prereqRight & notReady)==0 - && (pTerm->eOperator & WO_EQ)!=0 - && sqlite3IndexAffinityOk(pTerm->pExpr, - pTable->aCol[pTerm->u.leftColumn].affinity) - ){ + if( termCanDriveIndex(pTerm, pSrc, notReady) ){ + int iCol = pTerm->u.leftColumn; + if( iCol=0 ) idxCols |= 1<0 ); pLevel->plan.nEq = nColumn; - pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WO_EQ; + + /* Count the number of additional columns needed to create a + ** covering index. A "covering index" is an index that contains all + ** columns that are needed by the query. With a covering index, the + ** original table never needs to be accessed. Automatic indices must + ** be a covering index because the index will not be updated if the + ** original table changes and the index and table cannot both be used + ** if they go out of sync. + */ + extraCols = pSrc->colUsed & ~idxCols; + mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol; + for(i=0; icolUsed & (((Bitmask)1)<<(BMS-1)) ){ + nColumn += pTable->nCol - BMS + 1; + } + pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ; /* Construct the Index object to describe this index */ nByte = sizeof(Index); @@ -1767,22 +1801,32 @@ static void constructTransientIndex( pIdx->pTable = pTable; n = 0; for(pTerm=pWC->a; pTermleftCursor==pSrc->iCursor - && (pTerm->prereqRight & notReady)==0 - && (pTerm->eOperator & WO_EQ)!=0 - && sqlite3IndexAffinityOk(pTerm->pExpr, - pTable->aCol[pTerm->u.leftColumn].affinity) - ){ - int iCol = pTerm->u.leftColumn; - Expr *pX; - pIdx->aiColumn[n] = iCol; - pX = pTerm->pExpr; + if( termCanDriveIndex(pTerm, pSrc, notReady) ){ + Expr *pX = pTerm->pExpr; + pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); pIdx->azColl[n] = pColl->zName; n++; } } - assert( n==pIdx->nColumn ); + assert( n==pLevel->plan.nEq ); + + /* Add additional columns needed to make the index into a covering index */ + for(i=0; iaiColumn[n] = i; + pIdx->azColl[n] = "BINARY"; + n++; + } + } + if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ + for(i=BMS-1; inCol; i++){ + pIdx->aiColumn[n] = i; + pIdx->azColl[n] = "BINARY"; + n++; + } + } + assert( n==nColumn ); /* Create the transient index */ pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); @@ -2594,7 +2638,7 @@ static void bestBtreeIndex( /* If currently calculating the cost of using an index (not the IPK ** index), determine if all required column data may be obtained without - ** seeking to entries in the main table (i.e. if the index is a covering + ** using the main table (i.e. if the index is a covering ** index for this query). If it is, set the WHERE_IDX_ONLY flag in ** wsFlags. Otherwise, set the bLookup variable to true. */ if( pIdx && wsFlags ){ @@ -4264,8 +4308,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); - if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue; - if( (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 ){ + if( (pTab->tabFlags & TF_Ephemeral)==0 + && pTab->pSelect==0 + && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 + ){ int ws = pLevel->plan.wsFlags; if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);