aboutsummaryrefslogtreecommitdiff
path: root/src/update.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/update.c')
-rw-r--r--src/update.c283
1 files changed, 103 insertions, 180 deletions
diff --git a/src/update.c b/src/update.c
index dcf861213..d44aace9a 100644
--- a/src/update.c
+++ b/src/update.c
@@ -120,22 +120,17 @@ void sqlite3Update(
int isView; /* Trying to update a view */
Trigger *pTrigger; /* List of triggers on pTab, if required */
#endif
- int iBeginAfterTrigger = 0; /* Address of after trigger program */
- int iEndAfterTrigger = 0; /* Exit of after trigger program */
- int iBeginBeforeTrigger = 0; /* Address of before trigger program */
- int iEndBeforeTrigger = 0; /* Exit of before trigger program */
u32 old_col_mask = 0; /* Mask of OLD.* columns in use */
u32 new_col_mask = 0; /* Mask of NEW.* columns in use */
- int newIdx = -1; /* index of trigger "new" temp table */
- int oldIdx = -1; /* index of trigger "old" temp table */
-
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
int regOldRowid; /* The old rowid */
int regNewRowid; /* The new rowid */
- int regData; /* New data for the row */
+ int regNew;
+ int regOld;
int regRowSet = 0; /* Rowset of rows to be updated */
+ int regRec; /* Register used for new table record to insert */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
@@ -151,7 +146,7 @@ void sqlite3Update(
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
/* Figure out if we have any triggers and if the table being
- ** updated is a view
+ ** updated is a view.
*/
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0);
@@ -175,14 +170,6 @@ void sqlite3Update(
if( aXRef==0 ) goto update_cleanup;
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
- /* If there are FOR EACH ROW triggers, allocate cursors for the
- ** special OLD and NEW tables
- */
- if( pTrigger ){
- newIdx = pParse->nTab++;
- oldIdx = pParse->nTab++;
- }
-
/* Allocate a cursors for the main database table and for all indices.
** The index cursors might not be used, but if they are used they
** need to occur right after the database cursor. So go ahead and
@@ -268,24 +255,7 @@ void sqlite3Update(
aRegIdx[j] = reg;
}
- /* Allocate a block of register used to store the change record
- ** sent to sqlite3GenerateConstraintChecks(). There are either
- ** one or two registers for holding the rowid. One rowid register
- ** is used if chngRowid is false and two are used if chngRowid is
- ** true. Following these are pTab->nCol register holding column
- ** data.
- */
- regOldRowid = regNewRowid = pParse->nMem + 1;
- pParse->nMem += pTab->nCol + 1;
- if( chngRowid ){
- regNewRowid++;
- pParse->nMem++;
- }
- regData = regNewRowid+1;
-
-
- /* Begin generating code.
- */
+ /* Begin generating code. */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
@@ -302,40 +272,28 @@ void sqlite3Update(
}
#endif
- /* Start the view context
- */
+ /* Allocate required registers. */
+ regOldRowid = regNewRowid = ++pParse->nMem;
+ if( pTrigger ){
+ regOld = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ }
+ if( chngRowid || pTrigger ){
+ regNewRowid = ++pParse->nMem;
+ }
+ regNew = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ regRec = ++pParse->nMem;
+
+ /* Start the view context. */
if( isView ){
sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
}
- /* Generate the code for triggers.
- */
- if( pTrigger ){
- int iGoto;
-
- /* Create pseudo-tables for NEW and OLD
- */
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
-
- iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- addr = sqlite3VdbeMakeLabel(v);
- iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
- if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr,
- &old_col_mask, &new_col_mask) ){
- goto update_cleanup;
- }
- iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
- if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr,
- &old_col_mask, &new_col_mask) ){
- goto update_cleanup;
- }
- iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- sqlite3VdbeJumpHere(v, iGoto);
- }
+ /* If there are any triggers, set old_col_mask and new_col_mask. */
+ sqlite3TriggerUses(pParse,
+ pTrigger, TK_UPDATE, pChanges, pTab, onError, &old_col_mask, &new_col_mask
+ );
/* If we are trying to update a view, realize that view into
** a ephemeral table.
@@ -374,7 +332,7 @@ void sqlite3Update(
/* Initialize the count of updated rows
*/
- if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
+ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
@@ -407,11 +365,6 @@ void sqlite3Update(
}
}
}
-
- /* Jump back to this point if a trigger encounters an IGNORE constraint. */
- if( pTrigger ){
- sqlite3VdbeResolveLabel(v, addr);
- }
/* Top of the update loop */
if( okOnePass ){
@@ -422,139 +375,113 @@ void sqlite3Update(
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
}
- if( pTrigger ){
- int regRowid;
- int regRow;
- int regCols;
-
- /* Make cursor iCur point to the record that is being updated.
- */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
- /* Generate the OLD table
- */
- regRowid = sqlite3GetTempReg(pParse);
- regRow = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
- if( !old_col_mask ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regRow);
- }else{
- sqlite3VdbeAddOp2(v, OP_RowData, iCur, regRow);
- }
- sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, regRow, regRowid);
+ /* Make cursor iCur point to the record that is being updated. If
+ ** this record does not exist for some reason (deleted by a trigger,
+ ** for example, then jump to the next iteration of the RowSet loop. */
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
- /* Generate the NEW table
- */
- if( chngRowid ){
- sqlite3ExprCodeAndCache(pParse, pRowidExpr, regRowid);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
- }else{
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
- }
- regCols = sqlite3GetTempRange(pParse, pTab->nCol);
+ /* If there are triggers on this table, populate an array of registers
+ ** with the required old.* column data. */
+ if( pTrigger ){
for(i=0; i<pTab->nCol; i++){
- if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
- continue;
- }
- j = aXRef[i];
- if( (i<32 && (new_col_mask&((u32)1<<i))!=0) || new_col_mask==0xffffffff ){
- if( j<0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regCols+i);
- sqlite3ColumnDefault(v, pTab, i, -1);
- }else{
- sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr, regCols+i);
- }
+ if( aXRef[i]<0 || old_col_mask==0xffffffff || (old_col_mask & (1<<i)) ){
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
+ sqlite3ColumnDefault(v, pTab, i, regOld+i);
}else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
}
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRow);
- if( !isView ){
- sqlite3TableAffinityStr(v, pTab);
- sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol);
- }
- sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
- /* if( pParse->nErr ) goto update_cleanup; */
- sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRow, regRowid);
- sqlite3ReleaseTempReg(pParse, regRowid);
- sqlite3ReleaseTempReg(pParse, regRow);
-
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
- sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
}
- if( !isView ){
- /* Loop over every record that needs updating. We have to load
- ** the old data for each record to be updated because some columns
- ** might not change and we will need to copy the old value.
- ** Also, the old data is needed to delete the old index entries.
- ** So make the cursor point at the old record.
- */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
- /* If the record number will change, push the record number as it
- ** will be after the update. (The old record number is currently
- ** on top of the stack.)
- */
- if( chngRowid ){
- sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
- }
+ /* If the record number will change, set register regNewRowid to
+ ** contain the new value. If the record number is not being modified,
+ ** then regNewRowid is the same register as regOldRowid, which is
+ ** already populated. */
+ assert( chngRowid || pTrigger || regOldRowid==regNewRowid );
+ if( chngRowid ){
+ sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
+ }else if( pTrigger ){
+ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
+ }
- /* Compute new data for this record.
- */
- for(i=0; i<pTab->nCol; i++){
- if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i);
- continue;
- }
+ /* Populate the array of registers beginning at regNew with the new
+ ** row data. This array is used to check constaints, create the new
+ ** table and index records, and as the values for any new.* references
+ ** made by triggers. */
+ for(i=0; i<pTab->nCol; i++){
+ if( i==pTab->iPKey ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+ }else{
j = aXRef[i];
if( j<0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i);
- sqlite3ColumnDefault(v, pTab, i, regData+i);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
+ sqlite3ColumnDefault(v, pTab, i, regNew+i);
}else{
- sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regData+i);
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
}
}
+ }
- /* Do constraint checks
- */
+ /* If this is not a view, create the record that will be inserted into
+ ** the table (assuming no constraint checks fail). A side effect of
+ ** creating the record is applying affinity transformations to the
+ ** array of registers populated by the block above. This needs to be
+ ** done before the BEFORE triggers are fired. */
+#if 0
+ if( !isView ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regNew, pTab->nCol, regRec);
+ sqlite3TableAffinityStr(v, pTab);
+ sqlite3ExprCacheAffinityChange(pParse, regNew, pTab->nCol);
+ }
+#endif
+
+ /* Fire any BEFORE UPDATE triggers. This happens before constraints are
+ ** verified. One could argue that this is wrong. */
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+ TRIGGER_BEFORE, pTab, -1, regOldRowid, onError, addr);
+
+ if( !isView ){
+
+ /* Do constraint checks. */
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
- aRegIdx, chngRowid, 1,
- onError, addr, 0);
+ aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
- /* Delete the old indices for the current record.
- */
+ /* Delete the index entries associated with the current record. */
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
-
- /* If changing the record number, delete the old record.
- */
+
+ /* If changing the record number, delete the old record. */
if( chngRowid ){
sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
}
sqlite3VdbeJumpHere(v, j1);
+
+ /* Insert the new index entries and the new record. */
+ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, -1, 0, 0);
- /* Create the new index entries and the new record.
- */
- sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid,
- aRegIdx, 1, -1, 0, 0);
+#if 0
+ for(i=0; i<nIdx; i++){
+ if( aRegIdx[i] ){
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iCur+1+i, aRegIdx[i]);
+ }
+ }
+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, regNewRowid);
+ if( !pParse->nested ){
+ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
+ sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_ISUPDATE);
+ }
+#endif
}
/* Increment the row counter
*/
- if( db->flags & SQLITE_CountRows && !pParse->trigStack){
+ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
- /* If there are triggers, close all the cursors after each iteration
- ** through the loop. The fire the after triggers.
- */
- if( pTrigger ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
- sqlite3VdbeJumpHere(v, iEndAfterTrigger);
- }
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+ TRIGGER_AFTER, pTab, -1, regOldRowid, onError, addr);
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
@@ -569,16 +496,12 @@ void sqlite3Update(
}
}
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
- if( pTrigger ){
- sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
- sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
- }
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
*/
- if( pParse->nested==0 && pParse->trigStack==0 ){
+ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse);
}
@@ -587,7 +510,7 @@ void sqlite3Update(
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
- if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){
+ if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);