aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2020-12-10 20:31:25 +0000
committerdrh <drh@noemail.net>2020-12-10 20:31:25 +0000
commitdaf2761c62103f7bf126f8029dcf5fb473216f5f (patch)
treedd5712fa50c7b2fd530d28b1a5a3db2e0ac74734 /src
parent91f2717f22e5a7c3feab8224a16416677ad28e47 (diff)
downloadsqlite-daf2761c62103f7bf126f8029dcf5fb473216f5f.tar.gz
sqlite-daf2761c62103f7bf126f8029dcf5fb473216f5f.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/insert.c89
-rw-r--r--src/sqliteInt.h2
-rw-r--r--src/upsert.c2
3 files changed, 66 insertions, 27 deletions
diff --git a/src/insert.c b/src/insert.c
index f10587f9f..44cd8a339 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -976,9 +976,6 @@ void sqlite3Insert(
#ifndef SQLITE_OMIT_UPSERT
if( pUpsert ){
Upsert *pNx;
- int nIdx;
- Index *pIdxList;
- int *aReg;
if( IsVirtual(pTab) ){
sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"",
pTab->zName);
@@ -1003,26 +1000,6 @@ void sqlite3Insert(
}
pNx = pNx->pNextUpsert;
}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
@@ -1428,6 +1405,62 @@ int sqlite3ExprReferencesUpdatedColumn(
}
/*
+** 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
** on table pTab.
**
@@ -1557,6 +1590,7 @@ void sqlite3GenerateConstraintChecks(
int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
Trigger *pTrigger; /* List of DELETE triggers on the table pTab */
int nReplaceTrig = 0; /* Number of replace triggers coded */
+ IndexIterator ixi; /* Index iterator */
isUpdate = regOldData!=0;
db = pParse->db;
@@ -1769,6 +1803,9 @@ void sqlite3GenerateConstraintChecks(
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
** 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
** WITHOUT ROWID table.
*/
- pIdx = pUpsert ? pUpsert->pIdxList : pTab->pIndex;
- for(ix=0; pIdx; pIdx=pIdx->pNext, ix++){
+ for(pIdx = indexIteratorFirst(&ixi, &ix);
+ pIdx;
+ pIdx = indexIteratorNext(&ixi, &ix)
+ ){
int regIdx; /* Range of registers hold conent for pIdx */
int regR; /* Range of registers holding conflicting PK */
int iThisCur; /* Cursor for this UNIQUE index */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 23df9cc71..83993f08c 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3080,7 +3080,7 @@ struct Upsert {
Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
/* Above this point is the parse tree for the ON CONFLICT clauses.
** 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
** when the Upsert is destroyed. The fields below are used to transfer
** information from the INSERT processing down into the UPDATE processing
diff --git a/src/upsert.c b/src/upsert.c
index 63b914441..6b9bff685 100644
--- a/src/upsert.c
+++ b/src/upsert.c
@@ -25,7 +25,7 @@ static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
sqlite3ExprListDelete(db, p->pUpsertSet);
sqlite3ExprDelete(db, p->pUpsertWhere);
- sqlite3DbFree(db, p->pIdxList);
+ sqlite3DbFree(db, p->pToFree);
sqlite3DbFree(db, p);
p = pNext;
}while( p );