aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/insert.c48
-rw-r--r--src/sqliteInt.h12
-rw-r--r--src/upsert.c4
3 files changed, 47 insertions, 17 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{
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 06ddb3a61..8661f9058 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3078,10 +3078,14 @@ struct Upsert {
ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
- /* The fields above comprise the parse tree for the upsert clause.
- ** The fields below are used to transfer information from the INSERT
- ** processing down into the UPDATE processing while generating code.
- ** Upsert owns the memory allocated above, but not the memory below. */
+ /* Above this point is the parse tree for the ON CONFLICT clauses.
+ ** The next group of fields stores intermediate data. */
+ Index *pIdxList;
+ /* 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
+ ** while generating code. The fields below are owned by the INSERT
+ ** statement and will be freed by INSERT processing. */
Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */
SrcList *pUpsertSrc; /* Table to be updated */
int regData; /* First register holding array of VALUES */
diff --git a/src/upsert.c b/src/upsert.c
index ddd7c1842..0f1f6d8a3 100644
--- a/src/upsert.c
+++ b/src/upsert.c
@@ -25,6 +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);
p = pNext;
}while( p );
@@ -60,7 +61,7 @@ Upsert *sqlite3UpsertNew(
Upsert *pNext /* Next ON CONFLICT clause in the list */
){
Upsert *pNew;
- pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
+ pNew = sqlite3DbMallocZero(db, sizeof(Upsert));
if( pNew==0 ){
sqlite3ExprListDelete(db, pTarget);
sqlite3ExprDelete(db, pTargetWhere);
@@ -73,7 +74,6 @@ Upsert *sqlite3UpsertNew(
pNew->pUpsertTargetWhere = pTargetWhere;
pNew->pUpsertSet = pSet;
pNew->pUpsertWhere = pWhere;
- pNew->pUpsertIdx = 0;
pNew->pNextUpsert = pNext;
}
return pNew;