1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-11 01:42:22 +03:00

Use an iterator for the index loop in sqlite3GenerateConstraintChecks().

The idea is that this iterator can be enhanced to traverse the indexes in
any order, as required by multi-index UPSERT.

FossilOrigin-Name: 64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c
This commit is contained in:
drh
2020-12-10 20:31:25 +00:00
parent 91f2717f22
commit daf2761c62
5 changed files with 75 additions and 36 deletions

View File

@@ -1,5 +1,5 @@
C The\sDO\sUPDATE\scode\sgenerator\ssearches\sfor\sthe\scorrect\sON\sCONFLICT\sclause\sto\nuse. C Use\san\siterator\sfor\sthe\sindex\sloop\sin\ssqlite3GenerateConstraintChecks().\nThe\sidea\sis\sthat\sthis\siterator\scan\sbe\senhanced\sto\straverse\sthe\sindexes\sin\nany\sorder,\sas\srequired\sby\smulti-index\sUPSERT.
D 2020-12-10T12:49:26.425 D 2020-12-10T20:31:25.985
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c df28564b7d79f146266e906549687c74e3a5e9c4d8ba8697ef9c479ad83407e0 F src/insert.c ac236526a7f239d420291fae1bf7ea56c9e54f335598315a0ddf8ebb5a110120
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d
@@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009
F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
F src/sqliteInt.h 0e042bd76044617ae919a294778ceacdb00a405d60de1b94c8ee4d2b81829bdb F src/sqliteInt.h 72c7bfc4831f171d25b7b03c9fb152f1a3bbf55f51d5bbcdef313ebfa5873bea
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10
F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580
F src/upsert.c 49170532578f2f30984b35e2c1e2f308b089c6814edf384d32158a53b25fc930 F src/upsert.c 4b960968084a1d2a301ddbc782f625dbb1c02e0e8d0b0d6ad4a1986ecf305729
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
@@ -1888,7 +1888,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a P a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a
R 8dc7164868d12444f67e9c3abe760e73 R 57e2c4174298e4f52a8ae7fd60045bd5
U drh U drh
Z 978ca06206ec1f9d5cc426705017394f Z 27b1defd588019eb0041967767579913

View File

@@ -1 +1 @@
a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a 64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c

View File

@@ -976,9 +976,6 @@ void sqlite3Insert(
#ifndef SQLITE_OMIT_UPSERT #ifndef SQLITE_OMIT_UPSERT
if( pUpsert ){ if( pUpsert ){
Upsert *pNx; Upsert *pNx;
int nIdx;
Index *pIdxList;
int *aReg;
if( IsVirtual(pTab) ){ if( IsVirtual(pTab) ){
sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"",
pTab->zName); pTab->zName);
@@ -1003,26 +1000,6 @@ void sqlite3Insert(
} }
pNx = pNx->pNextUpsert; pNx = pNx->pNextUpsert;
}while( pNx!=0 ); }while( pNx!=0 );
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
assert( pIdx );
assert( aRegIdx[nIdx]>0 );
}
if( nIdx==0 ){
pUpsert->pIdxList = 0;
}else{
u64 nByte = sizeof(Index)*nIdx + sizeof(int)*(nIdx+2);
pIdxList = sqlite3DbMallocRaw(db, nByte);
if( pIdxList==0 ) goto insert_cleanup;
aReg = (int*)&pIdxList[nIdx];
for(i=0, pIdx=pTab->pIndex; i<nIdx; pIdx=pIdx->pNext, i++){
memcpy(&pIdxList[i], pIdx, sizeof(Index));
pIdxList[i].pNext = 0;
if( i ) pIdxList[i-1].pNext = &pIdxList[i];
aReg[i] = aRegIdx[i];
}
aReg[i] = aRegIdx[i];
pUpsert->pIdxList = pIdxList;
}
} }
#endif #endif
@@ -1427,6 +1404,62 @@ int sqlite3ExprReferencesUpdatedColumn(
return w.eCode!=0; return w.eCode!=0;
} }
/*
** The sqlite3GenerateConstraintChecks() routine usually wants to visit
** the indexes of a table in the order provided in the Table->pIndex list.
** However, sometimes (rarely - when there is an upsert) it wants to visit
** the indexes in a different order. The following data structures accomplish
** this.
**
** The IndexIterator object is used to walk through all of the indexes
** of a table in either Index.pNext order, or in some other order established
** by an array of IndexListTerm objects.
*/
typedef struct IndexListTerm IndexListTerm;
typedef struct IndexIterator IndexIterator;
struct IndexIterator {
int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */
int i; /* Index of the current item from the list */
union {
struct { /* Use this object for eType==0: A Index.pNext list */
Index *pIdx; /* The current Index */
} lx;
struct { /* Use this object for eType==1; Array of IndexListTerm */
int nIdx; /* Size of the array */
IndexListTerm *aIdx; /* Array of IndexListTerms */
} ax;
} u;
};
/* When IndexIterator.eType==1, then each index is an array of instances
** of the following object
*/
struct IndexListTerm {
Index *p; /* The index */
int ix; /* Which entry in the original Table.pIndex list is this index*/
};
/* Return the first index on the list */
static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){
int i = pIter->i;
*pIx = i;
return pIter->eType ? pIter->u.ax.aIdx[i].p : pIter->u.lx.pIdx;
}
/* Return the next index from the list. Return NULL when out of indexes */
static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){
int i = ++pIter->i;
if( pIter->eType ){
if( i>=pIter->u.ax.nIdx ) return 0;
*pIx = pIter->u.ax.aIdx[i].ix;
return pIter->u.ax.aIdx[i].p;
}else{
*pIx = i;
pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext;
return pIter->u.lx.pIdx;
}
}
/* /*
** Generate code to do constraint checks prior to an INSERT or an UPDATE ** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab. ** on table pTab.
@@ -1557,6 +1590,7 @@ void sqlite3GenerateConstraintChecks(
int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */ int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
Trigger *pTrigger; /* List of DELETE triggers on the table pTab */ Trigger *pTrigger; /* List of DELETE triggers on the table pTab */
int nReplaceTrig = 0; /* Number of replace triggers coded */ int nReplaceTrig = 0; /* Number of replace triggers coded */
IndexIterator ixi; /* Index iterator */
isUpdate = regOldData!=0; isUpdate = regOldData!=0;
db = pParse->db; db = pParse->db;
@@ -1769,6 +1803,9 @@ void sqlite3GenerateConstraintChecks(
VdbeComment((v, "UPSERT constraint goes first")); VdbeComment((v, "UPSERT constraint goes first"));
} }
} }
ixi.eType = 0;
ixi.i = 0;
ixi.u.lx.pIdx = pTab->pIndex;
/* Determine if it is possible that triggers (either explicitly coded /* Determine if it is possible that triggers (either explicitly coded
** triggers or FK resolution actions) might run as a result of deletes ** triggers or FK resolution actions) might run as a result of deletes
@@ -1953,8 +1990,10 @@ void sqlite3GenerateConstraintChecks(
** This loop also handles the case of the PRIMARY KEY index for a ** This loop also handles the case of the PRIMARY KEY index for a
** WITHOUT ROWID table. ** WITHOUT ROWID table.
*/ */
pIdx = pUpsert ? pUpsert->pIdxList : pTab->pIndex; for(pIdx = indexIteratorFirst(&ixi, &ix);
for(ix=0; pIdx; pIdx=pIdx->pNext, ix++){ pIdx;
pIdx = indexIteratorNext(&ixi, &ix)
){
int regIdx; /* Range of registers hold conent for pIdx */ int regIdx; /* Range of registers hold conent for pIdx */
int regR; /* Range of registers holding conflicting PK */ int regR; /* Range of registers holding conflicting PK */
int iThisCur; /* Cursor for this UNIQUE index */ int iThisCur; /* Cursor for this UNIQUE index */

View File

@@ -3080,7 +3080,7 @@ struct Upsert {
Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
/* Above this point is the parse tree for the ON CONFLICT clauses. /* Above this point is the parse tree for the ON CONFLICT clauses.
** The next group of fields stores intermediate data. */ ** The next group of fields stores intermediate data. */
Index *pIdxList; void *pToFree; /* Free memory when deleting the Upsert object */
/* All fields above are owned by the Upsert object and must be freed /* All fields above are owned by the Upsert object and must be freed
** when the Upsert is destroyed. The fields below are used to transfer ** when the Upsert is destroyed. The fields below are used to transfer
** information from the INSERT processing down into the UPDATE processing ** information from the INSERT processing down into the UPDATE processing

View File

@@ -25,7 +25,7 @@ static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
sqlite3ExprDelete(db, p->pUpsertTargetWhere); sqlite3ExprDelete(db, p->pUpsertTargetWhere);
sqlite3ExprListDelete(db, p->pUpsertSet); sqlite3ExprListDelete(db, p->pUpsertSet);
sqlite3ExprDelete(db, p->pUpsertWhere); sqlite3ExprDelete(db, p->pUpsertWhere);
sqlite3DbFree(db, p->pIdxList); sqlite3DbFree(db, p->pToFree);
sqlite3DbFree(db, p); sqlite3DbFree(db, p);
p = pNext; p = pNext;
}while( p ); }while( p );