Index: third_party/sqlite/src/src/update.c |
diff --git a/third_party/sqlite/src/src/update.c b/third_party/sqlite/src/src/update.c |
index a9735cadcad390c274f55f08482a3802f79ad0e2..8fca7eb9882e2e980b5b886c639e51516d4e1041 100644 |
--- a/third_party/sqlite/src/src/update.c |
+++ b/third_party/sqlite/src/src/update.c |
@@ -69,14 +69,14 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ |
sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc, |
pCol->affinity, &pValue); |
if( pValue ){ |
- sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); |
+ sqlite3VdbeAppendP4(v, pValue, P4_MEM); |
} |
+ } |
#ifndef SQLITE_OMIT_FLOATING_POINT |
- if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
- sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
- } |
-#endif |
+ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ |
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); |
} |
+#endif |
} |
/* |
@@ -105,7 +105,7 @@ void sqlite3Update( |
int iDataCur; /* Cursor for the canonical data btree */ |
int iIdxCur; /* Cursor for the first index */ |
sqlite3 *db; /* The database structure */ |
- int *aRegIdx = 0; /* One register assigned to each index to be updated */ |
+ int *aRegIdx = 0; /* First register in array assigned to each index */ |
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the |
** an expression for the i-th column of the table. |
** aXRef[i]==-1 if the i-th column is not changed. */ |
@@ -117,10 +117,11 @@ void sqlite3Update( |
AuthContext sContext; /* The authorization context */ |
NameContext sNC; /* The name-context to resolve expressions in */ |
int iDb; /* Database containing the table being updated */ |
- int okOnePass; /* True for one-pass algorithm without the FIFO */ |
+ int eOnePass; /* ONEPASS_XXX value from where.c */ |
int hasFK; /* True if foreign key processing is required */ |
int labelBreak; /* Jump here to break out of UPDATE loop */ |
int labelContinue; /* Jump here to continue next step of UPDATE loop */ |
+ int flags; /* Flags for sqlite3WhereBegin() */ |
#ifndef SQLITE_OMIT_TRIGGER |
int isView; /* True when updating a view (INSTEAD OF trigger) */ |
@@ -131,6 +132,10 @@ void sqlite3Update( |
int iEph = 0; /* Ephemeral table holding all primary key values */ |
int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */ |
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */ |
+ int addrOpen = 0; /* Address of OP_OpenEphemeral */ |
+ int iPk = 0; /* First of nPk cells holding PRIMARY KEY value */ |
+ i16 nPk = 0; /* Number of components of the PRIMARY KEY */ |
+ int bReplace = 0; /* True if REPLACE conflict resolution might happen */ |
/* Register Allocations */ |
int regRowCount = 0; /* A count of rows changed */ |
@@ -197,7 +202,7 @@ void sqlite3Update( |
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[]. |
** Initialize aXRef[] and aToOpen[] to their default values. |
*/ |
- aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 ); |
+ aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 ); |
if( aXRef==0 ) goto update_cleanup; |
aRegIdx = aXRef+pTab->nCol; |
aToOpen = (u8*)(aRegIdx+nIdx); |
@@ -249,7 +254,7 @@ void sqlite3Update( |
int rc; |
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, |
j<0 ? "ROWID" : pTab->aCol[j].zName, |
- db->aDb[iDb].zName); |
+ db->aDb[iDb].zDbSName); |
if( rc==SQLITE_DENY ){ |
goto update_cleanup; |
}else if( rc==SQLITE_IGNORE ){ |
@@ -268,7 +273,7 @@ void sqlite3Update( |
** case, set all bits of the colUsed mask (to ensure that the virtual |
** table implementation makes all columns available). |
*/ |
- pTabList->a[0].colUsed = IsVirtual(pTab) ? (Bitmask)-1 : 0; |
+ pTabList->a[0].colUsed = IsVirtual(pTab) ? ALLBITS : 0; |
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey); |
@@ -282,12 +287,19 @@ void sqlite3Update( |
int reg; |
if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){ |
reg = ++pParse->nMem; |
+ pParse->nMem += pIdx->nColumn; |
}else{ |
reg = 0; |
for(i=0; i<pIdx->nKeyCol; i++){ |
i16 iIdxCol = pIdx->aiColumn[i]; |
if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){ |
reg = ++pParse->nMem; |
+ pParse->nMem += pIdx->nColumn; |
+ if( (onError==OE_Replace) |
+ || (onError==OE_Default && pIdx->onError==OE_Replace) |
+ ){ |
+ bReplace = 1; |
+ } |
break; |
} |
} |
@@ -295,6 +307,11 @@ void sqlite3Update( |
if( reg==0 ) aToOpen[j+1] = 0; |
aRegIdx[j] = reg; |
} |
+ if( bReplace ){ |
+ /* If REPLACE conflict resolution might be invoked, open cursors on all |
+ ** indexes in case they are needed to delete records. */ |
+ memset(aToOpen, 1, nIdx+1); |
+ } |
/* Begin generating code. */ |
v = sqlite3GetVdbe(pParse); |
@@ -347,109 +364,130 @@ void sqlite3Update( |
} |
#endif |
- /* Begin the database scan |
- */ |
+ /* Initialize the count of updated rows */ |
+ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ |
+ regRowCount = ++pParse->nMem; |
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
+ } |
+ |
if( HasRowid(pTab) ){ |
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
- pWInfo = sqlite3WhereBegin( |
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur |
- ); |
- if( pWInfo==0 ) goto update_cleanup; |
- okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); |
- |
- /* Remember the rowid of every item to be updated. |
- */ |
- sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
- if( !okOnePass ){ |
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); |
- } |
- |
- /* End the database scan loop. |
- */ |
- sqlite3WhereEnd(pWInfo); |
}else{ |
- int iPk; /* First of nPk memory cells holding PRIMARY KEY value */ |
- i16 nPk; /* Number of components of the PRIMARY KEY */ |
- int addrOpen; /* Address of the OpenEphemeral instruction */ |
- |
assert( pPk!=0 ); |
nPk = pPk->nKeyCol; |
iPk = pParse->nMem+1; |
pParse->nMem += nPk; |
regKey = ++pParse->nMem; |
iEph = pParse->nTab++; |
+ |
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk); |
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); |
sqlite3VdbeSetP4KeyInfo(pParse, pPk); |
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, |
- WHERE_ONEPASS_DESIRED, iIdxCur); |
- if( pWInfo==0 ) goto update_cleanup; |
- okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); |
+ } |
+ |
+ /* Begin the database scan. |
+ ** |
+ ** Do not consider a single-pass strategy for a multi-row update if |
+ ** there are any triggers or foreign keys to process, or rows may |
+ ** be deleted as a result of REPLACE conflict handling. Any of these |
+ ** things might disturb a cursor being used to scan through the table |
+ ** or index, causing a single-pass approach to malfunction. */ |
+ flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE; |
+ if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){ |
+ flags |= WHERE_ONEPASS_MULTIROW; |
+ } |
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur); |
+ if( pWInfo==0 ) goto update_cleanup; |
+ |
+ /* A one-pass strategy that might update more than one row may not |
+ ** be used if any column of the index used for the scan is being |
+ ** updated. Otherwise, if there is an index on "b", statements like |
+ ** the following could create an infinite loop: |
+ ** |
+ ** UPDATE t1 SET b=b+1 WHERE b>? |
+ ** |
+ ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI |
+ ** strategy that uses an index for which one or more columns are being |
+ ** updated. */ |
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); |
+ if( eOnePass==ONEPASS_MULTI ){ |
+ int iCur = aiCurOnePass[1]; |
+ if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){ |
+ eOnePass = ONEPASS_OFF; |
+ } |
+ assert( iCur!=iDataCur || !HasRowid(pTab) ); |
+ } |
+ |
+ if( HasRowid(pTab) ){ |
+ /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
+ ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
+ ** leave it in register regOldRowid. */ |
+ sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
+ if( eOnePass==ONEPASS_OFF ){ |
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); |
+ } |
+ }else{ |
+ /* Read the PK of the current row into an array of registers. In |
+ ** ONEPASS_OFF mode, serialize the array into a record and store it in |
+ ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
+ ** the OP_OpenEphemeral instruction to a Noop (the ephemeral table |
+ ** is not required) and leave the PK fields in the array of registers. */ |
for(i=0; i<nPk; i++){ |
assert( pPk->aiColumn[i]>=0 ); |
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i], |
- iPk+i); |
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i); |
} |
- if( okOnePass ){ |
+ if( eOnePass ){ |
sqlite3VdbeChangeToNoop(v, addrOpen); |
nKey = nPk; |
regKey = iPk; |
}else{ |
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey, |
sqlite3IndexAffinityStr(db, pPk), nPk); |
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey); |
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk); |
} |
- sqlite3WhereEnd(pWInfo); |
} |
- /* Initialize the count of updated rows |
- */ |
- if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ |
- regRowCount = ++pParse->nMem; |
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
+ if( eOnePass!=ONEPASS_MULTI ){ |
+ sqlite3WhereEnd(pWInfo); |
} |
labelBreak = sqlite3VdbeMakeLabel(v); |
if( !isView ){ |
- /* |
- ** Open every index that needs updating. Note that if any |
- ** index could potentially invoke a REPLACE conflict resolution |
- ** action, then we need to open all indices because we might need |
- ** to be deleting some records. |
- */ |
- if( onError==OE_Replace ){ |
- memset(aToOpen, 1, nIdx+1); |
- }else{ |
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
- if( pIdx->onError==OE_Replace ){ |
- memset(aToOpen, 1, nIdx+1); |
- break; |
- } |
- } |
- } |
- if( okOnePass ){ |
+ int addrOnce = 0; |
+ |
+ /* Open every index that needs updating. */ |
+ if( eOnePass!=ONEPASS_OFF ){ |
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0; |
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0; |
} |
+ |
+ if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){ |
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
+ } |
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen, |
0, 0); |
+ if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); |
} |
/* Top of the update loop */ |
- if( okOnePass ){ |
- if( aToOpen[iDataCur-iBaseCur] && !isView ){ |
+ if( eOnePass!=ONEPASS_OFF ){ |
+ if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){ |
assert( pPk ); |
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); |
VdbeCoverageNeverTaken(v); |
} |
- labelContinue = labelBreak; |
+ if( eOnePass==ONEPASS_SINGLE ){ |
+ labelContinue = labelBreak; |
+ }else{ |
+ labelContinue = sqlite3VdbeMakeLabel(v); |
+ } |
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); |
VdbeCoverageIf(v, pPk==0); |
VdbeCoverageIf(v, pPk!=0); |
}else if( pPk ){ |
labelContinue = sqlite3VdbeMakeLabel(v); |
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); |
- addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey); |
+ addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); |
VdbeCoverage(v); |
}else{ |
@@ -567,12 +605,12 @@ void sqlite3Update( |
if( !isView ){ |
int addr1 = 0; /* Address of jump instruction */ |
- int bReplace = 0; /* True if REPLACE conflict resolution might happen */ |
/* Do constraint checks. */ |
assert( regOldRowid>0 ); |
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, |
- regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace); |
+ regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace, |
+ aXRef); |
/* Do FK constraint checks. */ |
if( hasFK ){ |
@@ -589,11 +627,34 @@ void sqlite3Update( |
VdbeCoverageNeverTaken(v); |
} |
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1); |
- |
- /* If changing the record number, delete the old record. */ |
- if( hasFK || chngKey || pPk!=0 ){ |
+ |
+ /* If changing the rowid value, or if there are foreign key constraints |
+ ** to process, delete the old record. Otherwise, add a noop OP_Delete |
+ ** to invoke the pre-update hook. |
+ ** |
+ ** That (regNew==regnewRowid+1) is true is also important for the |
+ ** pre-update hook. If the caller invokes preupdate_new(), the returned |
+ ** value is copied from memory cell (regNewRowid+1+iCol), where iCol |
+ ** is the column index supplied by the user. |
+ */ |
+ assert( regNew==regNewRowid+1 ); |
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
+ sqlite3VdbeAddOp3(v, OP_Delete, iDataCur, |
+ OPFLAG_ISUPDATE | ((hasFK || chngKey) ? 0 : OPFLAG_ISNOOP), |
+ regNewRowid |
+ ); |
+ if( eOnePass==ONEPASS_MULTI ){ |
+ assert( hasFK==0 && chngKey==0 ); |
+ sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); |
+ } |
+ if( !pParse->nested ){ |
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE); |
+ } |
+#else |
+ if( hasFK || chngKey ){ |
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0); |
} |
+#endif |
if( bReplace || chngKey ){ |
sqlite3VdbeJumpHere(v, addr1); |
} |
@@ -603,8 +664,11 @@ void sqlite3Update( |
} |
/* Insert the new index entries and the new record. */ |
- sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur, |
- regNewRowid, aRegIdx, 1, 0, 0); |
+ sqlite3CompleteInsertion( |
+ pParse, pTab, iDataCur, iIdxCur, regNewRowid, aRegIdx, |
+ OPFLAG_ISUPDATE | (eOnePass==ONEPASS_MULTI ? OPFLAG_SAVEPOSITION : 0), |
+ 0, 0 |
+ ); |
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to |
** handle rows (possibly in other tables) that refer via a foreign key |
@@ -626,8 +690,11 @@ void sqlite3Update( |
/* Repeat the above with the next record to be updated, until |
** all record selected by the WHERE clause have been updated. |
*/ |
- if( okOnePass ){ |
+ if( eOnePass==ONEPASS_SINGLE ){ |
/* Nothing to do at end-of-loop for a single-pass */ |
+ }else if( eOnePass==ONEPASS_MULTI ){ |
+ sqlite3VdbeResolveLabel(v, labelContinue); |
+ sqlite3WhereEnd(pWInfo); |
}else if( pPk ){ |
sqlite3VdbeResolveLabel(v, labelContinue); |
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
@@ -636,15 +703,6 @@ void sqlite3Update( |
} |
sqlite3VdbeResolveLabel(v, labelBreak); |
- /* Close all tables */ |
- for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ |
- assert( aRegIdx ); |
- if( aToOpen[i+1] ){ |
- sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0); |
- } |
- } |
- if( iDataCur<iIdxCur ) sqlite3VdbeAddOp2(v, OP_Close, iDataCur, 0); |
- |
/* Update the sqlite_sequence table by storing the content of the |
** maximum rowid counter values recorded while inserting into |
** autoincrement tables. |