mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
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
This commit is contained in:
48
src/insert.c
48
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{
|
||||
|
||||
Reference in New Issue
Block a user