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
This commit is contained in:
parent
f7d3b7a56c
commit
e9816d82a7
21
manifest
21
manifest
@ -1,5 +1,5 @@
|
||||
C Version\s3.25.0
|
||||
D 2018-09-15T04:01:47.208
|
||||
C Optimization:\swhen\sdoing\san\sUPDATE\son\sa\stable\swith\sindexes\son\san\sexpression,\ndo\snot\supdate\sthe\sexpression\sindexes\sif\sthey\sdo\snot\srefer\sto\sany\sof\sthe\ncolumns\sof\sthe\stable\sbeing\supdated.
|
||||
D 2018-09-15T21:38:48.076
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 6b650013511fd9d8b094203ac268af9220d292cc7d4e1bc9fbca15aacd8c7995
|
||||
@ -462,7 +462,7 @@ F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c c723716f0de7aa0a679300f7d3541c89645f4a9882161cecdb3093fc07f8cc4b
|
||||
F src/insert.c 0a214201afec77880a31a59c33d86b473a160fc5cc31981eab2041ae03d8bf2f
|
||||
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
|
||||
F src/loadext.c 30b140d0e5031924c56f802760506c0a235ced0dff9f3d95119aa86df12856e2
|
||||
F src/main.c 1f54ee71990bfbf4cdc2dc79bdc33e7c4f54eef6922447b4c910f9b5885a4478
|
||||
@ -506,7 +506,7 @@ F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1
|
||||
F src/sqlite.h.in 4b4c2f2daeeed4412ba9d81bc78092c69831fe6eda4f0ae5bf951da51a8dccec
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 305adca1b5da4a33ce2db5bd236935768e951d5651bfe5560ed55cfcdbce6a63
|
||||
F src/sqliteInt.h ce34da1aacca2cd4b63803db697f682af95c02d1e1750f240438b0d96a59bdb8
|
||||
F src/sqliteInt.h 83ee9b55db521b70294b2858872e69b96d0a4810d8221796def3e99cca5ee663
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@ -567,7 +567,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 9f55961518f77793edd56eee860ecf035d4370ebbb0726ad2f6cada6637fd16b
|
||||
F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f
|
||||
F src/trigger.c d3d78568f37fb2e6cdcc2d1e7b60156f15b0b600adec55b83c5d42f6cad250bd
|
||||
F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b
|
||||
F src/update.c deb215e3532be46e1e2b6aa38ed9e3eb96e42ebc4cbe7a498cd365788220a432
|
||||
F src/upsert.c 0dd81b40206841814d46942a7337786932475f085716042d0cb2fc7791bf8ca4
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
|
||||
@ -1007,7 +1007,7 @@ F test/index8.test bc2e3db70e8e62459aaa1bd7e4a9b39664f8f9d7
|
||||
F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
|
||||
F test/indexedby.test a52c8c6abfae4fbfb51d99440de4ca1840dbacc606b05e29328a2a8ba7cd914e
|
||||
F test/indexexpr1.test 635261197bcdc19b9b2c59bbfa7227d525c00e9587faddb2d293c44d287ce60e
|
||||
F test/indexexpr2.test 08551aa8956a91582979e17b6d369f321a5cb6ed6d3e952925a3e54e9e7de216
|
||||
F test/indexexpr2.test 05f490de32b07bce041947cb63475eb04cbd5af2ea2979ca6b9a694cde176efd
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
F test/insert.test 5604b1ff5675cc84c34a5b315792b958f48c32edccc0dafcc81d3b776270b70a
|
||||
@ -1765,10 +1765,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 7921dd91a1745aa79ea157b91b22c380e9331800050861ee6ca1f0efa0a97628
|
||||
R cd1dec78fa9aef334fc060d76165a055
|
||||
T +bgcolor * #d0c0ff
|
||||
T +sym-release *
|
||||
T +sym-version-3.25.0 *
|
||||
P b63af6c3bd33152742648d5d2e8dc5d5fcbcdd27df409272b6aea00a6f761760
|
||||
R 3dbbbb152576c732493eb0793eda163f
|
||||
U drh
|
||||
Z 7af864335c95fcb0688b7f5b8af875de
|
||||
Z 14fba317833d2195e2283bec86cb6487
|
||||
|
@ -1 +1 @@
|
||||
b63af6c3bd33152742648d5d2e8dc5d5fcbcdd27df409272b6aea00a6f761760
|
||||
a71b101635ed28a4c99734dabb20bd65ef1018c1d63ac143b7321cdb0fafa5d7
|
34
src/insert.c
34
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);
|
||||
|
@ -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
|
||||
|
31
src/update.c
31
src/update.c
@ -79,6 +79,29 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
**
|
||||
@ -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)
|
||||
|
@ -159,5 +159,43 @@ do_execsql_test 3.4.6 {
|
||||
SELECT * FROM t4 ORDER BY Substr(a,-2) COLLATE binary;
|
||||
} {.ABC1 1 .ABC3 3 .abc2 2 .abc4 4}
|
||||
|
||||
# 2014-09-15: Verify that UPDATEs of columns not referenced by a
|
||||
# index on expression do not modify the index.
|
||||
#
|
||||
unset -nocomplain cnt
|
||||
set cnt 0
|
||||
proc refcnt {x} {
|
||||
global cnt
|
||||
incr cnt
|
||||
return $x
|
||||
}
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
db function refcnt -deterministic refcnt
|
||||
do_test 4.100 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b,c,d,e,f);
|
||||
CREATE INDEX t1abc ON t1(refcnt(a+b+c));
|
||||
}
|
||||
set ::cnt
|
||||
} {0}
|
||||
do_test 4.110 {
|
||||
db eval {INSERT INTO t1 VALUES(1,2,3,4,5,6);}
|
||||
set ::cnt
|
||||
# The refcnt() function is invoked once to compute the index value
|
||||
} {1}
|
||||
do_test 4.120 {
|
||||
set ::cnt 0
|
||||
db eval {UPDATE t1 SET b=b+1;}
|
||||
set ::cnt
|
||||
# The refcnt() function is invoked twice, once to remove the old index
|
||||
# entry and a second time to insert the new one.
|
||||
} {2}
|
||||
do_test 4.130 {
|
||||
set ::cnt 0
|
||||
db eval {UPDATE t1 SET d=d+1;}
|
||||
set ::cnt
|
||||
# Refcnt() should not be invoked because that index does not change.
|
||||
} {0}
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user