diff options
author | drh <> | 2022-10-13 21:08:34 +0000 |
---|---|---|
committer | drh <> | 2022-10-13 21:08:34 +0000 |
commit | 4bc1cc18476aaa674f8a24f859e1ed754c365505 (patch) | |
tree | 8615989555bb36f06e4d5e40728c1d865d216c17 /src | |
parent | d92c652ac1fe570e27acd83e15903fb695386880 (diff) | |
download | sqlite-4bc1cc18476aaa674f8a24f859e1ed754c365505.tar.gz sqlite-4bc1cc18476aaa674f8a24f859e1ed754c365505.zip |
This experimental branch attempts to use columns for an index-on-expression
in place of the expression that is being indexed. This particular check-in
mostly works, but there are still issues.
FossilOrigin-Name: 2e8d4fd4cfd9e82f33c707ba246fe2bb3ca01762cf5ac5905058fbc7adf0abe7
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze.c | 1 | ||||
-rw-r--r-- | src/build.c | 1 | ||||
-rw-r--r-- | src/expr.c | 28 | ||||
-rw-r--r-- | src/insert.c | 1 | ||||
-rw-r--r-- | src/sqliteInt.h | 20 | ||||
-rw-r--r-- | src/upsert.c | 1 | ||||
-rw-r--r-- | src/where.c | 52 | ||||
-rw-r--r-- | src/whereexpr.c | 1 |
8 files changed, 105 insertions, 0 deletions
diff --git a/src/analyze.c b/src/analyze.c index 39009899a..8562b9d7f 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -953,6 +953,7 @@ static void analyzeVdbeCommentIndexWithColumnName( if( NEVER(i==XN_ROWID) ){ VdbeComment((v,"%s.rowid",pIdx->zName)); }else if( i==XN_EXPR ){ + assert( pIdx->bHasExpr ); VdbeComment((v,"%s.expr(%d)",pIdx->zName, k)); }else{ VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName)); diff --git a/src/build.c b/src/build.c index be6b01eb0..502826bfc 100644 --- a/src/build.c +++ b/src/build.c @@ -4192,6 +4192,7 @@ void sqlite3CreateIndex( j = XN_EXPR; pIndex->aiColumn[i] = XN_EXPR; pIndex->uniqNotNull = 0; + pIndex->bHasExpr = 1; }else{ j = pCExpr->iColumn; assert( j<=0x7fff ); diff --git a/src/expr.c b/src/expr.c index bceb5efb0..3630b47aa 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4043,6 +4043,26 @@ static int exprCodeInlineFunction( return target; } +/* +** Check to see if pExpr is one of the indexed expression on pParse->pIdxExpr. +** If it is, then resolve the expression by reading from the index and +** return the register into which the value has been read. If there is +** no match, return negative. +*/ +static SQLITE_NOINLINE int sqlite3ExprIndexLookup( + Parse *pParse, /* The parsing context */ + Expr *pExpr, /* The expression to potentially bypass */ + int target /* Where to store the result of the expression */ +){ + IndexExpr *p; + for(p=pParse->pIdxExpr; p; p=p->pIENext){ + if( sqlite3ExprCompare(0, pExpr, p->pExpr, p->iDataCur)!=0 ) continue; + sqlite3VdbeAddOp3(pParse->pVdbe, OP_Column, p->iIdxCur, p->iIdxCol, target); + return target; + } + return -1; /* Not found */ +} + /* ** Generate code into the current Vdbe to evaluate the given @@ -4068,6 +4088,14 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ assert( target>0 && target<=pParse->nMem ); assert( v!=0 ); + if( pParse->pIdxExpr!=0 + && pExpr!=0 + && !ExprHasProperty(pExpr, EP_Leaf) + && (r1 = sqlite3ExprIndexLookup(pParse, pExpr, target))>=0 + ){ + return r1; + } + expr_code_doover: if( pExpr==0 ){ op = TK_NULL; diff --git a/src/insert.c b/src/insert.c index 6c71391a1..3e0d696d7 100644 --- a/src/insert.c +++ b/src/insert.c @@ -96,6 +96,7 @@ const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){ aff = SQLITE_AFF_INTEGER; }else{ assert( x==XN_EXPR ); + assert( pIdx->bHasExpr ); assert( pIdx->aColExpr!=0 ); aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6f1109f2f..caf7610ad 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1189,6 +1189,7 @@ typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; +typedef struct IndexExpr IndexExpr; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; @@ -2616,6 +2617,7 @@ struct Index { 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 */ + unsigned bHasExpr:1; /* This is an index on an expression */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ @@ -3564,6 +3566,23 @@ struct TriggerPrg { #endif /* +** If there is an index on an expression in scope such that the value +** of the expression pExpr can be read out of the index with cursor iCur +** at column iCol, then an instance of this object records that fact. +** +** A linked list of these objects is attached to Parse and records all +** expressions that can be short-circuited by extracting a valid from +** an index. +*/ +struct IndexExpr { + Expr *pExpr; /* The expression contained in the index */ + int iDataCur; /* The data cursor associated with the index */ + int iIdxCur; /* The index cursor */ + int iIdxCol; /* The column of the index that contains pExpr */ + IndexExpr *pIENext; /* Next in a list of all indexed expressions */ +}; + +/* ** An instance of the ParseCleanup object specifies an operation that ** should be performed after parsing to deallocation resources obtained ** during the parse and which are no longer needed. @@ -3621,6 +3640,7 @@ struct Parse { int nLabelAlloc; /* Number of slots in aLabel */ int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ + IndexExpr *pIdxExpr; /* List of all expression in active indexes */ Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ diff --git a/src/upsert.c b/src/upsert.c index fb6c7c0c0..85994020c 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -166,6 +166,7 @@ int sqlite3UpsertAnalyzeTarget( if( pIdx->aiColumn[ii]==XN_EXPR ){ assert( pIdx->aColExpr!=0 ); assert( pIdx->aColExpr->nExpr>ii ); + assert( pIdx->bHasExpr ); pExpr = pIdx->aColExpr->a[ii].pExpr; if( pExpr->op!=TK_COLLATE ){ sCol[0].pLeft = pExpr; diff --git a/src/where.c b/src/where.c index b0c0ea7d4..220ea5139 100644 --- a/src/where.c +++ b/src/where.c @@ -5384,6 +5384,55 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } /* +** This is an sqlite3ParserAddCleanup() callback that is invoked to +** free the Parse->pIdxExpr list when the Parse object is destroyed. +*/ +static void whereIndexExprCleanup(sqlite3 *db, void *pObject){ + Parse *pParse = (Parse*)pObject; + while( pParse->pIdxExpr!=0 ){ + IndexExpr *p = pParse->pIdxExpr; + pParse->pIdxExpr = p->pIENext; + sqlite3ExprDelete(db, p->pExpr); + sqlite3DbFreeNN(db, p); + } +} + +/* +** The index pIdx is used by a query and contains one or more expressions. +** In other words pIdx is an index on an expression. iIdxCur is the cursor +** number for the index and iDataCur is the cursor number for the corresponding +** table. +** +** This routine adds IndexExpr entries to the Parse->pIdxExpr field for +** each of the expressions in the index so that the expression code generator +** will know to replace occurrences of the indexed expression with +** references to the corresponding column of the index. +*/ +static SQLITE_NOINLINE void whereAddIndexExpr( + Parse *pParse, /* Add IndexExpr entries to pParse->pIdxExpr */ + Index *pIdx, /* The index-on-expression that contains the expressions */ + int iIdxCur, /* Cursor number for pIdx */ + int iDataCur /* Cursor for the corresponding table */ +){ + int i; + IndexExpr *p; + for(i=0; i<pIdx->nColumn; i++){ + if( pIdx->aiColumn[i]!=XN_EXPR ) continue; + p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexExpr)); + if( p==0 ) break; + p->pIENext = pParse->pIdxExpr; + p->pExpr = sqlite3ExprDup(pParse->db, pIdx->aColExpr->a[i].pExpr, 0); + p->iDataCur = iDataCur; + p->iIdxCur = iIdxCur; + p->iIdxCol = i; + pParse->pIdxExpr = p; + if( p->pIENext==0 ){ + sqlite3ParserAddCleanup(pParse, whereIndexExprCleanup, pParse); + } + } +} + +/* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains ** information needed to terminate the loop. Later, the calling routine @@ -5928,6 +5977,9 @@ WhereInfo *sqlite3WhereBegin( op = OP_ReopenIdx; }else{ iIndexCur = pParse->nTab++; + if( pIx->bHasExpr ){ + whereAddIndexExpr(pParse, pIx, iIndexCur, pTabItem->iCursor); + } } pLevel->iIdxCur = iIndexCur; assert( pIx!=0 ); diff --git a/src/whereexpr.c b/src/whereexpr.c index 8c9233ebd..20e569642 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -989,6 +989,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2( if( pIdx->aColExpr==0 ) continue; for(i=0; i<pIdx->nKeyCol; i++){ if( pIdx->aiColumn[i]!=XN_EXPR ) continue; + assert( pIdx->bHasExpr ); if( sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){ aiCurCol[0] = iCur; aiCurCol[1] = XN_EXPR; |