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 f0880de407c9788b75a436e241ba05a4ea1f4e17..315034d86f5e5638c17a56dc707eb4cbb8add95f 100644 |
--- a/third_party/sqlite/src/src/update.c |
+++ b/third_party/sqlite/src/src/update.c |
@@ -11,8 +11,6 @@ |
************************************************************************* |
** This file contains C code routines that are called by the parser |
** to handle UPDATE statements. |
-** |
-** $Id: update.c,v 1.207 2009/08/08 18:01:08 drh Exp $ |
*/ |
#include "sqliteInt.h" |
@@ -113,14 +111,15 @@ 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 j1; /* Addresses of jump instructions */ |
int okOnePass; /* True for one-pass algorithm without the FIFO */ |
+ int hasFK; /* True if foreign key processing is required */ |
#ifndef SQLITE_OMIT_TRIGGER |
- int isView; /* Trying to update a view */ |
- Trigger *pTrigger; /* List of triggers on pTab, if required */ |
+ int isView; /* True when updating a view (INSTEAD OF trigger) */ |
+ Trigger *pTrigger; /* List of triggers on pTab, if required */ |
+ int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ |
#endif |
- u32 oldmask = 0; /* Mask of OLD.* columns in use */ |
+ int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ |
/* Register Allocations */ |
int regRowCount = 0; /* A count of rows changed */ |
@@ -129,7 +128,6 @@ void sqlite3Update( |
int regNew; |
int regOld = 0; |
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; |
@@ -148,11 +146,13 @@ void sqlite3Update( |
** updated is a view. |
*/ |
#ifndef SQLITE_OMIT_TRIGGER |
- pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0); |
+ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); |
isView = pTab->pSelect!=0; |
+ assert( pTrigger || tmask==0 ); |
#else |
# define pTrigger 0 |
# define isView 0 |
+# define tmask 0 |
#endif |
#ifdef SQLITE_OMIT_VIEW |
# undef isView |
@@ -162,7 +162,7 @@ void sqlite3Update( |
if( sqlite3ViewGetColumnNames(pParse, pTab) ){ |
goto update_cleanup; |
} |
- if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ |
+ if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ |
goto update_cleanup; |
} |
aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); |
@@ -211,6 +211,7 @@ void sqlite3Update( |
pRowidExpr = pChanges->a[i].pExpr; |
}else{ |
sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName); |
+ pParse->checkSchema = 1; |
goto update_cleanup; |
} |
} |
@@ -228,6 +229,8 @@ void sqlite3Update( |
#endif |
} |
+ hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid); |
+ |
/* Allocate memory for the array aRegIdx[]. There is one entry in the |
** array for each index associated with table being updated. Fill in |
** the value with a register number for indices that are to be used |
@@ -273,26 +276,21 @@ void sqlite3Update( |
/* Allocate required registers. */ |
regOldRowid = regNewRowid = ++pParse->nMem; |
- if( pTrigger ){ |
+ if( pTrigger || hasFK ){ |
regOld = pParse->nMem + 1; |
pParse->nMem += pTab->nCol; |
} |
- if( chngRowid || pTrigger ){ |
+ if( chngRowid || pTrigger || hasFK ){ |
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); |
} |
- /* If there are any triggers, set oldmask and new_col_mask. */ |
- oldmask = sqlite3TriggerOldmask( |
- pParse, pTrigger, TK_UPDATE, pChanges, pTab, onError); |
- |
/* If we are trying to update a view, realize that view into |
** a ephemeral table. |
*/ |
@@ -378,83 +376,137 @@ void sqlite3Update( |
** for example, then jump to the next iteration of the RowSet loop. */ |
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); |
- /* 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( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){ |
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i); |
- sqlite3ColumnDefault(v, pTab, i, regOld+i); |
- }else{ |
- sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i); |
- } |
- } |
- } |
- |
/* 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 ); |
+ assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid ); |
if( chngRowid ){ |
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); |
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); |
- }else if( pTrigger ){ |
- sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); |
+ } |
+ |
+ /* If there are triggers on this table, populate an array of registers |
+ ** with the required old.* column data. */ |
+ if( hasFK || pTrigger ){ |
+ u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); |
+ oldmask |= sqlite3TriggerColmask(pParse, |
+ pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError |
+ ); |
+ for(i=0; i<pTab->nCol; i++){ |
+ if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){ |
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i); |
+ }else{ |
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i); |
+ } |
+ } |
+ if( chngRowid==0 ){ |
+ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); |
+ } |
} |
/* 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. */ |
+ ** made by triggers. |
+ ** |
+ ** If there are one or more BEFORE triggers, then do not populate the |
+ ** registers associated with columns that are (a) not modified by |
+ ** this UPDATE statement and (b) not accessed by new.* references. The |
+ ** values for registers not modified by the UPDATE must be reloaded from |
+ ** the database after the BEFORE triggers are fired anyway (as the trigger |
+ ** may have modified them). So not loading those that are not going to |
+ ** be used eliminates some redundant opcodes. |
+ */ |
+ newmask = sqlite3TriggerColmask( |
+ pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError |
+ ); |
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 ){ |
+ if( j>=0 ){ |
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
+ }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){ |
+ /* This branch loads the value of a column that will not be changed |
+ ** into a register. This is done if there are no BEFORE triggers, or |
+ ** if there are one or more BEFORE triggers that use this value via |
+ ** a new.* reference in a trigger program. |
+ */ |
+ testcase( i==31 ); |
+ testcase( i==32 ); |
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); |
sqlite3ColumnDefault(v, pTab, i, regNew+i); |
- }else{ |
- sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); |
} |
} |
} |
/* Fire any BEFORE UPDATE triggers. This happens before constraints are |
- ** verified. One could argue that this is wrong. */ |
- if( pTrigger ){ |
+ ** verified. One could argue that this is wrong. |
+ */ |
+ if( tmask&TRIGGER_BEFORE ){ |
sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol); |
sqlite3TableAffinityStr(v, pTab); |
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, |
- TRIGGER_BEFORE, pTab, -1, regOldRowid, onError, addr); |
+ TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); |
/* The row-trigger may have deleted the row being updated. In this |
** case, jump to the next row. No updates or AFTER triggers are |
** required. This behaviour - what happens when the row being updated |
** is deleted or renamed by a BEFORE trigger - is left undefined in the |
- ** documentation. */ |
+ ** documentation. |
+ */ |
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); |
+ |
+ /* If it did not delete it, the row-trigger may still have modified |
+ ** some of the columns of the row being updated. Load the values for |
+ ** all columns not modified by the update statement into their |
+ ** registers in case this has happened. |
+ */ |
+ for(i=0; i<pTab->nCol; i++){ |
+ if( aXRef[i]<0 && i!=pTab->iPKey ){ |
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); |
+ sqlite3ColumnDefault(v, pTab, i, regNew+i); |
+ } |
+ } |
} |
if( !isView ){ |
+ int j1; /* Address of jump instruction */ |
/* Do constraint checks. */ |
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, |
aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); |
+ /* Do FK constraint checks. */ |
+ if( hasFK ){ |
+ sqlite3FkCheck(pParse, pTab, regOldRowid, 0); |
+ } |
+ |
/* 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( chngRowid ){ |
+ if( hasFK || chngRowid ){ |
sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); |
} |
sqlite3VdbeJumpHere(v, j1); |
+ |
+ if( hasFK ){ |
+ sqlite3FkCheck(pParse, pTab, 0, regNewRowid); |
+ } |
/* Insert the new index entries and the new record. */ |
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 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 |
+ ** to the row just updated. */ |
+ if( hasFK ){ |
+ sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); |
+ } |
} |
/* Increment the row counter |
@@ -464,7 +516,7 @@ void sqlite3Update( |
} |
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, |
- TRIGGER_AFTER, pTab, -1, regOldRowid, onError, addr); |
+ TRIGGER_AFTER, pTab, regOldRowid, onError, addr); |
/* Repeat the above with the next record to be updated, until |
** all record selected by the WHERE clause have been updated. |
@@ -508,6 +560,15 @@ update_cleanup: |
sqlite3ExprDelete(db, pWhere); |
return; |
} |
+/* Make sure "isView" and other macros defined above are undefined. Otherwise |
+** thely may interfere with compilation of other functions in this file |
+** (or in another file, if this file becomes part of the amalgamation). */ |
+#ifdef isView |
+ #undef isView |
+#endif |
+#ifdef pTrigger |
+ #undef pTrigger |
+#endif |
#ifndef SQLITE_OMIT_VIRTUALTABLE |
/* |
@@ -553,8 +614,7 @@ static void updateVirtualTable( |
/* Construct the SELECT statement that will find the new values for |
** all updated rows. |
*/ |
- pEList = sqlite3ExprListAppend(pParse, 0, |
- sqlite3CreateIdExpr(pParse, "_rowid_")); |
+ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_")); |
if( pRowid ){ |
pEList = sqlite3ExprListAppend(pParse, pEList, |
sqlite3ExprDup(db, pRowid, 0)); |
@@ -564,7 +624,7 @@ static void updateVirtualTable( |
if( aXRef[i]>=0 ){ |
pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0); |
}else{ |
- pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName); |
+ pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName); |
} |
pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); |
} |
@@ -576,6 +636,7 @@ static void updateVirtualTable( |
assert( v ); |
ephemTab = pParse->nTab++; |
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); |
+ sqlite3VdbeChangeP5(v, BTREE_UNORDERED); |
/* fill the ephemeral table |
*/ |
@@ -602,8 +663,3 @@ static void updateVirtualTable( |
sqlite3SelectDelete(db, pSelect); |
} |
#endif /* SQLITE_OMIT_VIRTUALTABLE */ |
- |
-/* Make sure "isView" gets undefined in case this file becomes part of |
-** the amalgamation - so that subsequent files do not see isView as a |
-** macro. */ |
-#undef isView |