diff options
author | drh <drh@noemail.net> | 2019-10-24 20:29:25 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2019-10-24 20:29:25 +0000 |
commit | c7476735c9b9e377958ab404f0130f3f43a219dc (patch) | |
tree | 84b125525773fd68b0d0150b4aab06aceee45dc1 /src | |
parent | 035f6d909fcdd2b36320b07aec608a6c75187593 (diff) | |
download | sqlite-c7476735c9b9e377958ab404f0130f3f43a219dc.tar.gz sqlite-c7476735c9b9e377958ab404f0130f3f43a219dc.zip |
Fix handling of covering indexes that use virtual columns.
FossilOrigin-Name: e0f7e321eca91c49102649f70728de69347cbd7b16599b854203c24cfdc348e6
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 20 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/wherecode.c | 52 |
3 files changed, 63 insertions, 10 deletions
diff --git a/src/build.c b/src/build.c index ecf54fe8c..84440af67 100644 --- a/src/build.c +++ b/src/build.c @@ -1923,15 +1923,24 @@ static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){ ** high-order bit of colNotIdxed is always 1. All unindexed columns ** of the table have a 1. ** +** 2019-10-24: For the purpose of this computation, virtual columns are +** not considered to be covered by the index, even if they are in the +** index, because we do not trust the logic in whereIndexExprTrans() to be +** able to find all instances of a reference to the indexed table column +** and convert them into references to the index. Hence we always want +** the actual table at hand in order to recompute the virtual column, if +** necessary. +** ** The colNotIdxed mask is AND-ed with the SrcList.a[].colUsed mask ** to determine if the index is covering index. */ static void recomputeColumnsNotIndexed(Index *pIdx){ Bitmask m = 0; int j; + Table *pTab = pIdx->pTable; for(j=pIdx->nColumn-1; j>=0; j--){ int x = pIdx->aiColumn[j]; - if( x>=0 ){ + if( x>=0 && (pTab->aCol[x].colFlags & COLFLAG_VIRTUAL)==0 ){ testcase( x==BMS-1 ); testcase( x==BMS-2 ); if( x<BMS-1 ) m |= MASKBIT(x); @@ -3617,8 +3626,13 @@ void sqlite3CreateIndex( assert( j<=0x7fff ); if( j<0 ){ j = pTab->iPKey; - }else if( pTab->aCol[j].notNull==0 ){ - pIndex->uniqNotNull = 0; + }else{ + if( pTab->aCol[j].notNull==0 ){ + pIndex->uniqNotNull = 0; + } + if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){ + pIndex->bHasVCol = 1; + } } pIndex->aiColumn[i] = (i16)j; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index e2002810f..d8668a278 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2295,6 +2295,7 @@ struct Index { unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */ unsigned bNoQuery:1; /* Do not use this index to optimize queries */ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ + unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ diff --git a/src/wherecode.c b/src/wherecode.c index 686a8d6f9..0438cca54 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -1106,6 +1106,7 @@ typedef struct IdxExprTrans { int iTabCur; /* The cursor of the corresponding table */ int iIdxCur; /* The cursor for the index */ int iIdxCol; /* The column for the index */ + int iTabCol; /* The column for the table */ } IdxExprTrans; /* The walker node callback used to transform matching expressions into @@ -1128,10 +1129,30 @@ static int whereIndexExprTransNode(Walker *p, Expr *pExpr){ } } +#ifndef SQLITE_OMIT_GENERATED_COLUMNS +/* A walker node callback that translates a column reference to a table +** into a corresponding column reference of an index. +*/ +static int whereIndexExprTransColumn(Walker *p, Expr *pExpr){ + if( pExpr->op==TK_COLUMN ){ + IdxExprTrans *pX = p->u.pIdxTrans; + if( pExpr->iTable==pX->iTabCur && pExpr->iColumn==pX->iTabCol ){ + pExpr->iTable = pX->iIdxCur; + pExpr->iColumn = pX->iIdxCol; + } + } + return WRC_Continue; +} +#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ + /* ** For an indexes on expression X, locate every instance of expression X ** in pExpr and change that subexpression into a reference to the appropriate ** column of the index. +** +** 2019-10-24: Updated to also translate references to a VIRTUAL column in +** the table into references to the corresponding (stored) column of the +** index. */ static void whereIndexExprTrans( Index *pIdx, /* The Index */ @@ -1141,20 +1162,35 @@ static void whereIndexExprTrans( ){ int iIdxCol; /* Column number of the index */ ExprList *aColExpr; /* Expressions that are indexed */ + Table *pTab; Walker w; IdxExprTrans x; aColExpr = pIdx->aColExpr; - if( aColExpr==0 ) return; /* Not an index on expressions */ + if( aColExpr==0 && !pIdx->bHasVCol ){ + /* The index does not reference any expressions or virtual columns + ** so no translations are needed. */ + return; + } + pTab = pIdx->pTable; memset(&w, 0, sizeof(w)); - w.xExprCallback = whereIndexExprTransNode; w.u.pIdxTrans = &x; x.iTabCur = iTabCur; x.iIdxCur = iIdxCur; - for(iIdxCol=0; iIdxCol<aColExpr->nExpr; iIdxCol++){ - if( pIdx->aiColumn[iIdxCol]!=XN_EXPR ) continue; - assert( aColExpr->a[iIdxCol].pExpr!=0 ); + for(iIdxCol=0; iIdxCol<pIdx->nColumn; iIdxCol++){ + i16 iRef = pIdx->aiColumn[iIdxCol]; + if( iRef==XN_EXPR ){ + assert( aColExpr->a[iIdxCol].pExpr!=0 ); + x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; + w.xExprCallback = whereIndexExprTransNode; +#ifndef SQLITE_OMIT_GENERATED_COLUMNS + }else if( iRef>=0 && (pTab->aCol[iRef].colFlags & COLFLAG_VIRTUAL)!=0 ){ + x.iTabCol = iRef; + w.xExprCallback = whereIndexExprTransColumn; +#endif /* SQLITE_OMIT_GENERATED_COLUMNS */ + }else{ + continue; + } x.iIdxCol = iIdxCol; - x.pIdxExpr = aColExpr->a[iIdxCol].pExpr; sqlite3WalkExpr(&w, pWInfo->pWhere); sqlite3WalkExprList(&w, pWInfo->pOrderBy); sqlite3WalkExprList(&w, pWInfo->pResultSet); @@ -1835,7 +1871,9 @@ Bitmask sqlite3WhereCodeOneLoopStart( /* If pIdx is an index on one or more expressions, then look through ** all the expressions in pWInfo and try to transform matching expressions - ** into reference to index columns. + ** into reference to index columns. Also attempt to translate references + ** to virtual columns in the table into references to (stored) columns + ** of the index. ** ** Do not do this for the RHS of a LEFT JOIN. This is because the ** expression may be evaluated after OP_NullRow has been executed on |