mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-22 20:22:44 +03:00
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
This commit is contained in:
18
manifest
18
manifest
@@ -1,8 +1,8 @@
|
|||||||
-----BEGIN PGP SIGNED MESSAGE-----
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
Hash: SHA1
|
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.
|
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:33:07
|
D 2010-04-07T14:59:45
|
||||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||||
F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b
|
F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b
|
||||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||||
@@ -224,7 +224,7 @@ F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1
|
|||||||
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
|
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
|
||||||
F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
|
F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
|
||||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||||
F src/where.c 6831620423f91e2e767a51de1403e8f3b6316fcd
|
F src/where.c 186b72b01f9afe0b4dbb68e90e14d6dceb287aed
|
||||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||||
F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45
|
F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45
|
||||||
@@ -797,14 +797,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
|||||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||||
P 064c283eee82c5053e84058a0e1908e468da6ce3
|
P c0f67ea131c3e237e46311b1a3dcb3febf2ea036
|
||||||
R 2ea9a93d8f9ff175759bacc0a1fd3296
|
R 0e5421f4e341a57c85b79d77e22a68e3
|
||||||
U drh
|
U drh
|
||||||
Z 49063b48e9e24945a681e3cbaa857e35
|
Z b97f0b4c3618aad21acc127e7b33602d
|
||||||
-----BEGIN PGP SIGNATURE-----
|
-----BEGIN PGP SIGNATURE-----
|
||||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||||
|
|
||||||
iD8DBQFLvJepoxKgR168RlERAvdbAJ4j8n07sFWyrBxSwl1Z/sTtVx4x8gCfX6OS
|
iD8DBQFLvJ3koxKgR168RlERAm7oAJ0Xzm3BtJ0ZfA9RjpPh9mHBaxTARgCfQDNh
|
||||||
0ygpWKStUSB8+v8Z3feY2PY=
|
ievdlvN7dk9MClGNwMCoKvk=
|
||||||
=69dZ
|
=ZzMO
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
c0f67ea131c3e237e46311b1a3dcb3febf2ea036
|
2364313142668b3d0ad144447b4862709be929cd
|
||||||
104
src/where.c
104
src/where.c
@@ -1637,9 +1637,28 @@ static void bestOrClauseIndex(
|
|||||||
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
|
#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
|
** 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
|
** possible to construct a transient index that would perform better
|
||||||
** than a full table scan even when the cost of constructing the index
|
** 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
|
** is taken into account, then alter the query plan to use the
|
||||||
@@ -1682,12 +1701,7 @@ static void bestTransientIndex(
|
|||||||
pTable = pSrc->pTab;
|
pTable = pSrc->pTab;
|
||||||
pWCEnd = &pWC->a[pWC->nTerm];
|
pWCEnd = &pWC->a[pWC->nTerm];
|
||||||
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
||||||
if( pTerm->leftCursor==pSrc->iCursor
|
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
|
||||||
&& (pTerm->prereqRight & notReady)==0
|
|
||||||
&& (pTerm->eOperator & WO_EQ)!=0
|
|
||||||
&& sqlite3IndexAffinityOk(pTerm->pExpr,
|
|
||||||
pTable->aCol[pTerm->u.leftColumn].affinity)
|
|
||||||
){
|
|
||||||
WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n",
|
WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n",
|
||||||
pCost->rCost, costTempIdx));
|
pCost->rCost, costTempIdx));
|
||||||
pCost->rCost = costTempIdx;
|
pCost->rCost = costTempIdx;
|
||||||
@@ -1723,7 +1737,11 @@ static void constructTransientIndex(
|
|||||||
int addrTop; /* Top of the index fill loop */
|
int addrTop; /* Top of the index fill loop */
|
||||||
int regRecord; /* Register holding an index record */
|
int regRecord; /* Register holding an index record */
|
||||||
int n; /* Column counter */
|
int n; /* Column counter */
|
||||||
|
int i; /* Loop counter */
|
||||||
|
int mxBitCol; /* Maximum column in pSrc->colUsed */
|
||||||
CollSeq *pColl; /* Collating sequence to on a column */
|
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
|
/* Generate code to skip over the creation and initialization of the
|
||||||
** transient index on 2nd and subsequent iterations of the loop. */
|
** transient index on 2nd and subsequent iterations of the loop. */
|
||||||
@@ -1733,23 +1751,39 @@ static void constructTransientIndex(
|
|||||||
addrInit = sqlite3VdbeAddOp1(v, OP_If, regIsInit);
|
addrInit = sqlite3VdbeAddOp1(v, OP_If, regIsInit);
|
||||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, 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;
|
nColumn = 0;
|
||||||
pTable = pSrc->pTab;
|
pTable = pSrc->pTab;
|
||||||
pWCEnd = &pWC->a[pWC->nTerm];
|
pWCEnd = &pWC->a[pWC->nTerm];
|
||||||
|
idxCols = 0;
|
||||||
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
||||||
if( pTerm->leftCursor==pSrc->iCursor
|
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
|
||||||
&& (pTerm->prereqRight & notReady)==0
|
int iCol = pTerm->u.leftColumn;
|
||||||
&& (pTerm->eOperator & WO_EQ)!=0
|
if( iCol<BMS && iCol>=0 ) idxCols |= 1<<iCol;
|
||||||
&& sqlite3IndexAffinityOk(pTerm->pExpr,
|
|
||||||
pTable->aCol[pTerm->u.leftColumn].affinity)
|
|
||||||
){
|
|
||||||
nColumn++;
|
nColumn++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert( nColumn>0 );
|
assert( nColumn>0 );
|
||||||
pLevel->plan.nEq = nColumn;
|
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; i<mxBitCol; i++){
|
||||||
|
if( extraCols & (1<<i) ) nColumn++;
|
||||||
|
}
|
||||||
|
if( pSrc->colUsed & (((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 */
|
/* Construct the Index object to describe this index */
|
||||||
nByte = sizeof(Index);
|
nByte = sizeof(Index);
|
||||||
@@ -1767,22 +1801,32 @@ static void constructTransientIndex(
|
|||||||
pIdx->pTable = pTable;
|
pIdx->pTable = pTable;
|
||||||
n = 0;
|
n = 0;
|
||||||
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
|
||||||
if( pTerm->leftCursor==pSrc->iCursor
|
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
|
||||||
&& (pTerm->prereqRight & notReady)==0
|
Expr *pX = pTerm->pExpr;
|
||||||
&& (pTerm->eOperator & WO_EQ)!=0
|
pIdx->aiColumn[n] = pTerm->u.leftColumn;
|
||||||
&& sqlite3IndexAffinityOk(pTerm->pExpr,
|
|
||||||
pTable->aCol[pTerm->u.leftColumn].affinity)
|
|
||||||
){
|
|
||||||
int iCol = pTerm->u.leftColumn;
|
|
||||||
Expr *pX;
|
|
||||||
pIdx->aiColumn[n] = iCol;
|
|
||||||
pX = pTerm->pExpr;
|
|
||||||
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
|
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
|
||||||
pIdx->azColl[n] = pColl->zName;
|
pIdx->azColl[n] = pColl->zName;
|
||||||
n++;
|
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; i<mxBitCol; i++){
|
||||||
|
if( extraCols & (1<<i) ){
|
||||||
|
pIdx->aiColumn[n] = i;
|
||||||
|
pIdx->azColl[n] = "BINARY";
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
|
||||||
|
for(i=BMS-1; i<pTable->nCol; i++){
|
||||||
|
pIdx->aiColumn[n] = i;
|
||||||
|
pIdx->azColl[n] = "BINARY";
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert( n==nColumn );
|
||||||
|
|
||||||
/* Create the transient index */
|
/* Create the transient index */
|
||||||
pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx);
|
pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||||
@@ -2594,7 +2638,7 @@ static void bestBtreeIndex(
|
|||||||
|
|
||||||
/* If currently calculating the cost of using an index (not the IPK
|
/* If currently calculating the cost of using an index (not the IPK
|
||||||
** index), determine if all required column data may be obtained without
|
** 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
|
** index for this query). If it is, set the WHERE_IDX_ONLY flag in
|
||||||
** wsFlags. Otherwise, set the bLookup variable to true. */
|
** wsFlags. Otherwise, set the bLookup variable to true. */
|
||||||
if( pIdx && wsFlags ){
|
if( pIdx && wsFlags ){
|
||||||
@@ -4264,8 +4308,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
|||||||
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
|
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
|
||||||
Table *pTab = pTabItem->pTab;
|
Table *pTab = pTabItem->pTab;
|
||||||
assert( pTab!=0 );
|
assert( pTab!=0 );
|
||||||
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue;
|
if( (pTab->tabFlags & TF_Ephemeral)==0
|
||||||
if( (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0 ){
|
&& pTab->pSelect==0
|
||||||
|
&& (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0
|
||||||
|
){
|
||||||
int ws = pLevel->plan.wsFlags;
|
int ws = pLevel->plan.wsFlags;
|
||||||
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
|
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
|
||||||
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
|
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
|
||||||
|
|||||||
Reference in New Issue
Block a user