mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-06 15:49:35 +03:00
Move [7d30880114] to the trunk. Add optimizations to reduce the number of opcodes used for BEFORE UPDATE triggers.
FossilOrigin-Name: 1b7c5250ccb63182324bfc3f1ea28f17b6db357a
This commit is contained in:
63
src/update.c
63
src/update.c
@@ -111,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
|
||||
int newmask;
|
||||
|
||||
/* Register Allocations */
|
||||
int regRowCount = 0; /* A count of rows changed */
|
||||
@@ -146,11 +147,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
|
||||
@@ -160,7 +163,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 );
|
||||
@@ -388,7 +391,9 @@ void sqlite3Update(
|
||||
** with the required old.* column data. */
|
||||
if( hasFK || pTrigger ){
|
||||
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
|
||||
oldmask |= sqlite3TriggerOldmask(pParse, pTrigger, pChanges, pTab, onError);
|
||||
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 || (oldmask & (1<<i)) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
|
||||
@@ -405,24 +410,42 @@ void sqlite3Update(
|
||||
/* 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.
|
||||
*/
|
||||
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,
|
||||
@@ -432,11 +455,25 @@ void sqlite3Update(
|
||||
** 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,
|
||||
|
||||
Reference in New Issue
Block a user