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

@@ -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
@@ -1427,6 +1404,62 @@ int sqlite3ExprReferencesUpdatedColumn(
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
** 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 */