diff options
author | drh <drh@noemail.net> | 2018-09-15 21:38:48 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2018-09-15 21:38:48 +0000 |
commit | e9816d82a79ec748f4280fabf7ba30717d0c43a3 (patch) | |
tree | bd754231cedc923ae6621cddf9034186fd85317c /src | |
parent | f7d3b7a56cd6ebb85ba999913acb335de83b53ec (diff) | |
download | sqlite-e9816d82a79ec748f4280fabf7ba30717d0c43a3.tar.gz sqlite-e9816d82a79ec748f4280fabf7ba30717d0c43a3.zip |
Optimization: when doing an UPDATE on a table with indexes on an expression,
do not update the expression indexes if they do not refer to any of the
columns of the table being updated.
FossilOrigin-Name: a71b101635ed28a4c99734dabb20bd65ef1018c1d63ac143b7321cdb0fafa5d7
Diffstat (limited to 'src')
-rw-r--r-- | src/insert.c | 34 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/update.c | 31 |
3 files changed, 54 insertions, 12 deletions
diff --git a/src/insert.c b/src/insert.c index 12a6bb805..d110ab763 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1126,14 +1126,15 @@ insert_cleanup: #endif /* -** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged() +** Meanings of bits in of pWalker->eCode for +** sqlite3ExprReferencesUpdatedColumn() */ #define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */ #define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */ -/* This is the Walker callback from checkConstraintUnchanged(). Set -** bit 0x01 of pWalker->eCode if -** pWalker->eCode to 0 if this expression node references any of the +/* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn(). +* Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this +** expression node references any of the ** columns that are being modifed by an UPDATE statement. */ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ @@ -1155,12 +1156,21 @@ static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){ ** only columns that are modified by the UPDATE are those for which ** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true. ** -** Return true if CHECK constraint pExpr does not use any of the +** Return true if CHECK constraint pExpr uses any of the ** changing columns (or the rowid if it is changing). In other words, -** return true if this CHECK constraint can be skipped when validating +** return true if this CHECK constraint must be validated for ** the new row in the UPDATE statement. +** +** 2018-09-15: pExpr might also be an expression for an index-on-expressions. +** The operation of this routine is the same - return true if an only if +** the expression uses one or more of columns identified by the second and +** third arguments. */ -static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ +int sqlite3ExprReferencesUpdatedColumn( + Expr *pExpr, /* The expression to be checked */ + int *aiChng, /* aiChng[x]>=0 if column x changed by the UPDATE */ + int chngRowid /* True if UPDATE changes the rowid */ +){ Walker w; memset(&w, 0, sizeof(w)); w.eCode = 0; @@ -1175,7 +1185,7 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ testcase( w.eCode==CKCNSTRNT_COLUMN ); testcase( w.eCode==CKCNSTRNT_ROWID ); testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) ); - return !w.eCode; + return w.eCode!=0; } /* @@ -1381,7 +1391,13 @@ void sqlite3GenerateConstraintChecks( for(i=0; i<pCheck->nExpr; i++){ int allOk; Expr *pExpr = pCheck->a[i].pExpr; - if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue; + if( aiChng + && !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng) + ){ + /* The check constraints do not reference any of the columns being + ** updated so there is no point it verifying the check constraint */ + continue; + } allOk = sqlite3VdbeMakeLabel(v); sqlite3VdbeVerifyAbortable(v, onError); sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 79d41e8a0..74438b5ab 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4002,6 +4002,7 @@ void sqlite3GenerateRowDelete( void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); void sqlite3ResolvePartIdxLabel(Parse*,int); +int sqlite3ExprReferencesUpdatedColumn(Expr*,int*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*,int*,Upsert*); #ifdef SQLITE_ENABLE_NULL_TRIM diff --git a/src/update.c b/src/update.c index 94e169fd0..c9ec95bb6 100644 --- a/src/update.c +++ b/src/update.c @@ -80,6 +80,29 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ } /* +** Check to see if column iCol of index pIdx references any of the +** columns defined by aXRef and chngRowid. Return true if it does +** and false if not. +** +** The iCol-th column of pIdx will be an expression. +** +** aXRef[j] will be non-negative if column j of the original table is +** being updated. chngRowid will be true if the rowid of the table is +** being updated. +*/ +static int indexExprRefsUpdatedColumn( + Index *pIdx, /* The index containing the expression to analyze */ + int iCol, /* Which column of the index is the expression */ + int *aXRef, /* aXRef[j]>=0 if column j is being updated */ + int chngRowid /* true if the rowid is being updated */ +){ + assert( pIdx->aColExpr!=0 ); + assert( pIdx->aColExpr->a[iCol].pExpr!=0 ); + return sqlite3ExprReferencesUpdatedColumn(pIdx->aColExpr->a[iCol].pExpr, + aXRef,chngRowid); +} + +/* ** Process an UPDATE statement. ** ** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; @@ -302,8 +325,6 @@ void sqlite3Update( /* There is one entry in the aRegIdx[] array for each index on the table ** being updated. Fill in aRegIdx[] with a register number that will hold ** the key for accessing each index. - ** - ** FIXME: Be smarter about omitting indexes that use expressions. */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; @@ -314,7 +335,11 @@ void sqlite3Update( reg = 0; for(i=0; i<pIdx->nKeyCol; i++){ i16 iIdxCol = pIdx->aiColumn[i]; - if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){ + if( (iIdxCol>=0 && aXRef[iIdxCol]>=0) + || iIdxCol==XN_ROWID + || (iIdxCol==XN_EXPR + && indexExprRefsUpdatedColumn(pIdx,i,aXRef,chngRowid)) + ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; if( (onError==OE_Replace) |