diff options
author | drh <drh@noemail.net> | 2020-12-09 20:30:47 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2020-12-09 20:30:47 +0000 |
commit | e84ad92f17b96f1952f27645042bbf83fe6d0da5 (patch) | |
tree | 2fcba3afe39355cccb66226a626d5b5ab9e011a7 /src/insert.c | |
parent | 5602777e8fc872e638c0d9ccc116bebfd66e02e9 (diff) | |
download | sqlite-e84ad92f17b96f1952f27645042bbf83fe6d0da5.tar.gz sqlite-e84ad92f17b96f1952f27645042bbf83fe6d0da5.zip |
For upsert, the constraint check code generator uses a copy of the index list
for the target table, which can potentially be reordered.
FossilOrigin-Name: 3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a
Diffstat (limited to 'src/insert.c')
-rw-r--r-- | src/insert.c | 48 |
1 files changed, 37 insertions, 11 deletions
diff --git a/src/insert.c b/src/insert.c index 137590a5e..f10587f9f 100644 --- a/src/insert.c +++ b/src/insert.c @@ -976,6 +976,9 @@ 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); @@ -1000,6 +1003,26 @@ 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 @@ -1512,7 +1535,7 @@ void sqlite3GenerateConstraintChecks( ){ Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ - Index *pPk = 0; /* The PRIMARY KEY index */ + Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */ sqlite3 *db; /* Database connection */ int i; /* loop counter */ int ix; /* Index loop counter */ @@ -1930,7 +1953,8 @@ void sqlite3GenerateConstraintChecks( ** This loop also handles the case of the PRIMARY KEY index for a ** WITHOUT ROWID table. */ - for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ + pIdx = pUpsert ? pUpsert->pIdxList : pTab->pIndex; + for(ix=0; pIdx; pIdx=pIdx->pNext, 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 */ @@ -1938,7 +1962,7 @@ void sqlite3GenerateConstraintChecks( int addrConflictCk; /* First opcode in the conflict check logic */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ - if( pUpIdx==pIdx ){ + if( pUpIdx && pUpIdx->zName==pIdx->zName ){ addrUniqueOk = upsertJump+1; upsertBypass = sqlite3VdbeGoto(v, 0); VdbeComment((v, "Skip upsert subroutine")); @@ -1946,7 +1970,7 @@ void sqlite3GenerateConstraintChecks( }else{ addrUniqueOk = sqlite3VdbeMakeLabel(pParse); } - if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ + if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx->zName==pIdx->zName) ){ sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } @@ -1999,7 +2023,7 @@ void sqlite3GenerateConstraintChecks( ** of a WITHOUT ROWID table and there has been no change the ** primary key, then no collision is possible. The collision detection ** logic below can all be skipped. */ - if( isUpdate && pPk==pIdx && pkChng==0 ){ + if( isUpdate && pPk && pPk->zName==pIdx->zName && pkChng==0 ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } @@ -2017,7 +2041,7 @@ void sqlite3GenerateConstraintChecks( } /* Figure out if the upsert clause applies to this index */ - if( pUpIdx==pIdx ){ + if( pUpIdx && pUpIdx->zName==pIdx->zName ){ if( pUpsert->pUpsertSet==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ @@ -2037,7 +2061,7 @@ void sqlite3GenerateConstraintChecks( ** is invoked. */ #ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ - && pPk==pIdx /* Condition 2 */ + && pPk && pPk->zName==pIdx->zName /* Condition 2 */ && onError==OE_Replace /* Condition 1 */ && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ 0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) @@ -2056,7 +2080,8 @@ void sqlite3GenerateConstraintChecks( regIdx, pIdx->nKeyCol); VdbeCoverage(v); /* Generate code to handle collisions */ - regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); + regR = (pPk && pIdx->zName==pPk->zName) ? + regIdx : sqlite3GetTempRange(pParse, nPkField); if( isUpdate || onError==OE_Replace ){ if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); @@ -2071,7 +2096,7 @@ void sqlite3GenerateConstraintChecks( int x; /* Extract the PRIMARY KEY from the end of the index entry and ** store it in registers regR..regR+nPk-1 */ - if( pIdx!=pPk ){ + if( pPk && pIdx->zName!=pPk->zName ){ for(i=0; i<pPk->nKeyCol; i++){ assert( pPk->aiColumn[i]>=0 ); x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); @@ -2152,7 +2177,8 @@ void sqlite3GenerateConstraintChecks( } sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, regR, nPkField, 0, OE_Replace, - (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur); + (pPk && pIdx->zName==pPk->zName ? ONEPASS_SINGLE : ONEPASS_OFF), + iThisCur); if( pTrigger && isUpdate ){ sqlite3VdbeAddOp1(v, OP_CursorUnlock, iDataCur); } @@ -2208,7 +2234,7 @@ void sqlite3GenerateConstraintChecks( break; } } - if( pUpIdx==pIdx ){ + if( pUpIdx && pUpIdx->zName==pIdx->zName ){ sqlite3VdbeGoto(v, upsertJump+1); sqlite3VdbeJumpHere(v, upsertBypass); }else{ |