diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alter.c | 6 | ||||
-rw-r--r-- | src/analyze.c | 4 | ||||
-rw-r--r-- | src/build.c | 4 | ||||
-rw-r--r-- | src/delete.c | 36 | ||||
-rw-r--r-- | src/insert.c | 39 | ||||
-rw-r--r-- | src/sqliteInt.h | 13 | ||||
-rw-r--r-- | src/trigger.c | 125 | ||||
-rw-r--r-- | src/update.c | 32 |
8 files changed, 140 insertions, 119 deletions
diff --git a/src/alter.c b/src/alter.c index 497415833..2a1b18ea1 100644 --- a/src/alter.c +++ b/src/alter.c @@ -12,7 +12,7 @@ ** This file contains C code routines that used to generate VDBE code ** that implements the ALTER TABLE command. ** -** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $ +** $Id: alter.c,v 1.54 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -193,7 +193,7 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){ */ if( pTab->pSchema!=pTempSchema ){ sqlite3 *db = pParse->db; - for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ + for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ if( !zWhere ){ zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name); @@ -232,7 +232,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ #ifndef SQLITE_OMIT_TRIGGER /* Drop any table triggers from the internal schema. */ - for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){ + for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); assert( iTrigDb==iDb || iTrigDb==1 ); sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0); diff --git a/src/analyze.c b/src/analyze.c index 6b1a33628..d002805b6 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code associated with the ANALYZE command. ** -** @(#) $Id: analyze.c,v 1.50 2009/02/20 10:58:42 danielk1977 Exp $ +** @(#) $Id: analyze.c,v 1.51 2009/02/28 10:47:42 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_ANALYZE #include "sqliteInt.h" @@ -188,7 +188,7 @@ static void analyzeOneTable( ** The result is a single row of the sqlite_stat1 table. The first ** two columns are the names of the table and index. The third column ** is a string composed of a list of integer statistics about the - ** index. The first integer in the list is the total number of entires + ** index. The first integer in the list is the total number of entries ** in the index. There is one additional integer in the list for each ** column of the table. This additional integer is a guess of how many ** rows of the table the index will select. If D is the count of distinct diff --git a/src/build.c b/src/build.c index 459539bfe..446a6903f 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.520 2009/02/20 10:58:42 danielk1977 Exp $ +** $Id: build.c,v 1.521 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -2058,7 +2058,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ ** is generated to remove entries from sqlite_master and/or ** sqlite_temp_master if required. */ - pTrigger = pTab->pTrigger; + pTrigger = sqlite3TriggerList(pParse, pTab); while( pTrigger ){ assert( pTrigger->pSchema==pTab->pSchema || pTrigger->pSchema==db->aDb[1].pSchema ); diff --git a/src/delete.c b/src/delete.c index 62449a63d..a9988044d 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.195 2009/02/24 10:14:40 danielk1977 Exp $ +** $Id: delete.c,v 1.196 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -238,7 +238,7 @@ void sqlite3DeleteFrom( #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ - int triggers_exist = 0; /* True if any triggers exist */ + Trigger *pTrigger; /* List of table triggers, if required */ #endif int iBeginAfterTrigger = 0; /* Address of after trigger program */ int iEndAfterTrigger = 0; /* Exit of after trigger program */ @@ -265,10 +265,10 @@ void sqlite3DeleteFrom( ** deleted from is a view */ #ifndef SQLITE_OMIT_TRIGGER - triggers_exist = sqlite3TriggersExist(pTab, TK_DELETE, 0); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); isView = pTab->pSelect!=0; #else -# define triggers_exist 0 +# define pTrigger 0 # define isView 0 #endif #ifdef SQLITE_OMIT_VIEW @@ -276,7 +276,7 @@ void sqlite3DeleteFrom( # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto delete_from_cleanup; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -287,7 +287,7 @@ void sqlite3DeleteFrom( if( rcauth==SQLITE_DENY ){ goto delete_from_cleanup; } - assert(!isView || triggers_exist); + assert(!isView || pTrigger); /* If pTab is really a view, make sure it has been initialized. */ @@ -297,7 +297,7 @@ void sqlite3DeleteFrom( /* Allocate a cursor used to store the old.* data for a trigger. */ - if( triggers_exist ){ + if( pTrigger ){ oldIdx = pParse->nTab++; } @@ -322,21 +322,21 @@ void sqlite3DeleteFrom( goto delete_from_cleanup; } if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, triggers_exist, iDb); + sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb); - if( triggers_exist ){ + if( pTrigger ){ int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default); int iGoto = sqlite3VdbeAddOp0(v, OP_Goto); addr = sqlite3VdbeMakeLabel(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); - (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab, - -1, oldIdx, orconf, addr, &old_col_mask, 0); + (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, + TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto); iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); - (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, - oldIdx, orconf, addr, &old_col_mask, 0); + (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, + TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, iGoto); @@ -373,7 +373,7 @@ void sqlite3DeleteFrom( ** It is easier just to erase the whole table. Note, however, that ** this means that the row change count will be incorrect. */ - if( rcauth==SQLITE_OK && pWhere==0 && !triggers_exist && !IsVirtual(pTab) ){ + if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){ assert( !isView ); sqlite3VdbeAddOp3(v, OP_Clear, pTab->tnum, iDb, memCnt); if( !pParse->nested ){ @@ -405,7 +405,7 @@ void sqlite3DeleteFrom( /* Open the pseudo-table used to store OLD if there are triggers. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol); } @@ -425,12 +425,12 @@ void sqlite3DeleteFrom( /* This is the beginning of the delete loop. If a trigger encounters ** an IGNORE constraint, it jumps back to here. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeResolveLabel(v, addr); } addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - if( triggers_exist ){ + if( pTrigger ){ int iData = ++pParse->nMem; /* For storing row data of OLD table */ /* If the record is no longer present in the table, jump to the @@ -468,7 +468,7 @@ void sqlite3DeleteFrom( /* If there are row triggers, close all cursors then invoke ** the AFTER triggers */ - if( triggers_exist ){ + if( pTrigger ){ /* Jump back and run the AFTER triggers */ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger); diff --git a/src/insert.c b/src/insert.c index 72078c554..9b23c8666 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.259 2009/02/20 10:58:42 danielk1977 Exp $ +** $Id: insert.c,v 1.260 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -401,7 +401,8 @@ void sqlite3Insert( #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ - int triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ + Trigger *pTrigger; /* List of triggers on pTab, if required */ + int tmask; /* Mask of trigger times */ #endif db = pParse->db; @@ -431,22 +432,24 @@ void sqlite3Insert( ** inserted into is a view */ #ifndef SQLITE_OMIT_TRIGGER - triggers_exist = sqlite3TriggersExist(pTab, TK_INSERT, 0); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask); isView = pTab->pSelect!=0; #else -# define triggers_exist 0 +# define pTrigger 0 +# define tmask 0 # define isView 0 #endif #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif + assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ - if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto insert_cleanup; } assert( pTab!=0 ); @@ -464,10 +467,10 @@ void sqlite3Insert( v = sqlite3GetVdbe(pParse); if( v==0 ) goto insert_cleanup; if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb); + sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb); /* if there are row triggers, allocate a temp table for new.* references. */ - if( triggers_exist ){ + if( pTrigger ){ newIdx = pParse->nTab++; } @@ -482,7 +485,7 @@ void sqlite3Insert( ** This is the 2nd template. */ if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){ - assert( !triggers_exist ); + assert( !pTrigger ); assert( pList==0 ); goto insert_cleanup; } @@ -557,7 +560,7 @@ void sqlite3Insert( ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ - if( triggers_exist || readsTable(v, addrSelect, iDb, pTab) ){ + if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){ useTempTable = 1; } @@ -676,7 +679,7 @@ void sqlite3Insert( /* Open the temp table for FOR EACH ROW triggers */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol); } @@ -744,7 +747,7 @@ void sqlite3Insert( /* Run the BEFORE and INSTEAD OF triggers, if there are any */ endOfLoop = sqlite3VdbeMakeLabel(v); - if( triggers_exist & TRIGGER_BEFORE ){ + if( tmask & TRIGGER_BEFORE ){ int regTrigRowid; int regCols; int regRec; @@ -812,8 +815,8 @@ void sqlite3Insert( sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); /* Fire BEFORE or INSTEAD OF triggers */ - if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab, - newIdx, -1, onError, endOfLoop, 0, 0) ){ + if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, + pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ goto insert_cleanup; } } @@ -935,7 +938,7 @@ void sqlite3Insert( regIns, aRegIdx, 0, - (triggers_exist & TRIGGER_AFTER)!=0 ? newIdx : -1, + (tmask&TRIGGER_AFTER) ? newIdx : -1, appendFlag ); } @@ -947,10 +950,10 @@ void sqlite3Insert( sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } - if( triggers_exist ){ + if( pTrigger ){ /* Code AFTER triggers */ - if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab, - newIdx, -1, onError, endOfLoop, 0, 0) ){ + if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, + pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ goto insert_cleanup; } } @@ -1530,7 +1533,7 @@ static int xferOptimization( if( pSelect==0 ){ return 0; /* Must be of the form INSERT INTO ... SELECT ... */ } - if( pDest->pTrigger ){ + if( sqlite3TriggerList(pParse, pDest) ){ return 0; /* tab1 must not have triggers */ } #ifndef SQLITE_OMIT_VIRTUALTABLE diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 429346d29..19fb9b1f4 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.838 2009/02/24 10:14:40 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.839 2009/02/28 10:47:42 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1093,7 +1093,6 @@ struct Table { u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ - Trigger *pTrigger; /* List of SQL triggers on this table */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK @@ -1108,6 +1107,7 @@ struct Table { int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* Text of all module args. [0] is module name */ #endif + Trigger *pTrigger; /* List of triggers stored in pSchema */ Schema *pSchema; /* Schema that contains this table */ Table *pNextZombie; /* Next on the Parse.pZombieTab list */ }; @@ -2473,9 +2473,10 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); void sqlite3DropTrigger(Parse*, SrcList*, int); void sqlite3DropTriggerPtr(Parse*, Trigger*); - int sqlite3TriggersExist(Table*, int, ExprList*); - int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, - int, int, u32*, u32*); + Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask); + Trigger *sqlite3TriggerList(Parse *, Table *); + int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, + int, int, int, int, u32*, u32*); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); @@ -2490,7 +2491,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); # define sqlite3DeleteTrigger(A,B) # define sqlite3DropTriggerPtr(A,B) # define sqlite3UnlinkAndDeleteTrigger(A,B,C) -# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K) 0 +# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0 #endif int sqlite3JoinType(Parse*, Token*, Token*, Token*); diff --git a/src/trigger.c b/src/trigger.c index 98f8d835d..34b3f099d 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -10,7 +10,7 @@ ************************************************************************* ** ** -** $Id: trigger.c,v 1.134 2009/02/19 14:39:25 danielk1977 Exp $ +** $Id: trigger.c,v 1.135 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -34,6 +34,30 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){ } /* +** Given table pTab, return a list of all the triggers attached to +** the table. The list is connected by Trigger.pNext pointers. +*/ +Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ + Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; + Trigger *pList = 0; /* List of triggers to return */ + + if( pTmpSchema!=pTab->pSchema ){ + HashElem *p; + for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ + Trigger *pTrig = (Trigger *)sqliteHashData(p); + if( pTrig->pTabSchema==pTab->pSchema + && 0==sqlite3StrICmp(pTrig->table, pTab->zName) + ){ + pTrig->pNext = (pList ? pList : pTab->pTrigger); + pList = pTrig; + } + } + } + + return (pList ? pList : pTab->pTrigger); +} + +/* ** This is called by the parser when it sees a CREATE TRIGGER statement ** up to the point of the BEGIN before the trigger actions. A Trigger ** structure is generated based on the information available and stored @@ -209,14 +233,16 @@ void sqlite3FinishTrigger( TriggerStep *pStepList, /* The triggered program */ Token *pAll /* Token that describes the complete CREATE TRIGGER */ ){ - Trigger *pTrig = 0; /* The trigger whose construction is finishing up */ - sqlite3 *db = pParse->db; /* The database */ + Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ + char *zName; /* Name of trigger */ + sqlite3 *db = pParse->db; /* The database */ DbFixer sFix; - int iDb; /* Database containing the trigger */ + int iDb; /* Database containing the trigger */ pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup; + zName = pTrig->name; iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); pTrig->step_list = pStepList; while( pStepList ){ @@ -242,32 +268,29 @@ void sqlite3FinishTrigger( z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTrig->name, + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, pTrig->table, z); sqlite3DbFree(db, z); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf( - db, "type='trigger' AND name='%q'", pTrig->name), P4_DYNAMIC + db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC ); } if( db->init.busy ){ - int n; - Table *pTab; - Trigger *pDel; - pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash, - pTrig->name, sqlite3Strlen30(pTrig->name), pTrig); - if( pDel ){ - assert( pDel==pTrig ); + Trigger *pLink = pTrig; + Hash *pHash = &db->aDb[iDb].pSchema->trigHash; + pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig); + if( pTrig ){ db->mallocFailed = 1; - goto triggerfinish_cleanup; + }else if( pLink->pSchema==pLink->pTabSchema ){ + Table *pTab; + int n = sqlite3Strlen30(pLink->table) + 1; + pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n); + assert( pTab!=0 ); + pLink->pNext = pTab->pTrigger; + pTab->pTrigger = pLink; } - n = sqlite3Strlen30(pTrig->table) + 1; - pTab = sqlite3HashFind(&pTrig->pTabSchema->tblHash, pTrig->table, n); - assert( pTab!=0 ); - pTrig->pNext = pTab->pTrigger; - pTab->pTrigger = pTrig; - pTrig = 0; } triggerfinish_cleanup: @@ -556,25 +579,15 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ ** Remove a trigger from the hash tables of the sqlite* pointer. */ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ + Hash *pHash = &(db->aDb[iDb].pSchema->trigHash); Trigger *pTrigger; - int nName = sqlite3Strlen30(zName); - pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash), - zName, nName, 0); + pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0); if( pTrigger ){ - Table *pTable = tableOfTrigger(pTrigger); - assert( pTable!=0 ); - if( pTable->pTrigger == pTrigger ){ - pTable->pTrigger = pTrigger->pNext; - }else{ - Trigger *cc = pTable->pTrigger; - while( cc ){ - if( cc->pNext == pTrigger ){ - cc->pNext = cc->pNext->pNext; - break; - } - cc = cc->pNext; - } - assert(cc); + if( pTrigger->pSchema==pTrigger->pTabSchema ){ + Table *pTab = tableOfTrigger(pTrigger); + Trigger **pp; + for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext)); + *pp = (*pp)->pNext; } sqlite3DeleteTrigger(db, pTrigger); db->flags |= SQLITE_InternChanges; @@ -600,30 +613,31 @@ static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){ } /* -** Return a bit vector to indicate what kind of triggers exist for operation -** "op" on table pTab. If pChanges is not NULL then it is a list of columns -** that are being updated. Triggers only match if the ON clause of the -** trigger definition overlaps the set of columns being updated. -** -** The returned bit vector is some combination of TRIGGER_BEFORE and -** TRIGGER_AFTER. +** Return a list of all triggers on table pTab if there exists at least +** one trigger that must be fired when an operation of type 'op' is +** performed on the table, and, if that operation is an UPDATE, if at +** least one of the columns in pChanges is being modified. */ -int sqlite3TriggersExist( +Trigger *sqlite3TriggersExist( + Parse *pParse, /* Parse context */ Table *pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ - ExprList *pChanges /* Columns that change in an UPDATE statement */ + ExprList *pChanges, /* Columns that change in an UPDATE statement */ + int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ){ - Trigger *pTrigger; int mask = 0; - - pTrigger = IsVirtual(pTab) ? 0 : pTab->pTrigger; - while( pTrigger ){ - if( pTrigger->op==op && checkColumnOverLap(pTrigger->pColumns, pChanges) ){ - mask |= pTrigger->tr_tm; + Trigger *pList = sqlite3TriggerList(pParse, pTab); + Trigger *p; + assert( pList==0 || IsVirtual(pTab)==0 ); + for(p=pList; p; p=p->pNext){ + if( p->op==op && checkColumnOverLap(p->pColumns, pChanges) ){ + mask |= p->tr_tm; } - pTrigger = pTrigger->pNext; } - return mask; + if( pMask ){ + *pMask = mask; + } + return (mask ? pList : 0); } /* @@ -760,6 +774,7 @@ static int codeTriggerProgram( */ int sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ + Trigger *pTrigger, /* List of triggers on table pTab */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ @@ -783,7 +798,7 @@ int sqlite3CodeRowTrigger( assert(newIdx != -1 || oldIdx != -1); - for(p=pTab->pTrigger; p; p=p->pNext){ + for(p=pTrigger; p; p=p->pNext){ int fire_this = 0; /* Determine whether we should code this trigger */ diff --git a/src/update.c b/src/update.c index b377ee98c..81831831e 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.195 2009/02/24 10:14:40 danielk1977 Exp $ +** $Id: update.c,v 1.196 2009/02/28 10:47:42 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -107,7 +107,7 @@ void sqlite3Update( #ifndef SQLITE_OMIT_TRIGGER int isView; /* Trying to update a view */ - int triggers_exist = 0; /* True if any row triggers exist */ + 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 */ @@ -143,10 +143,10 @@ void sqlite3Update( ** updated is a view */ #ifndef SQLITE_OMIT_TRIGGER - triggers_exist = sqlite3TriggersExist(pTab, TK_UPDATE, pChanges); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0); isView = pTab->pSelect!=0; #else -# define triggers_exist 0 +# define pTrigger 0 # define isView 0 #endif #ifdef SQLITE_OMIT_VIEW @@ -154,7 +154,7 @@ void sqlite3Update( # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto update_cleanup; } if( sqlite3ViewGetColumnNames(pParse, pTab) ){ @@ -167,7 +167,7 @@ void sqlite3Update( /* If there are FOR EACH ROW triggers, allocate cursors for the ** special OLD and NEW tables */ - if( triggers_exist ){ + if( pTrigger ){ newIdx = pParse->nTab++; oldIdx = pParse->nTab++; } @@ -299,7 +299,7 @@ void sqlite3Update( /* Generate the code for triggers. */ - if( triggers_exist ){ + if( pTrigger ){ int iGoto; /* Create pseudo-tables for NEW and OLD @@ -310,14 +310,16 @@ void sqlite3Update( iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); addr = sqlite3VdbeMakeLabel(v); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); - if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, - newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){ + 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, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, - newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){ + 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); @@ -397,7 +399,7 @@ void sqlite3Update( } /* Jump back to this point if a trigger encounters an IGNORE constraint. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeResolveLabel(v, addr); } @@ -410,7 +412,7 @@ void sqlite3Update( addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); } - if( triggers_exist ){ + if( pTrigger ){ int regRowid; int regRow; int regCols; @@ -539,7 +541,7 @@ void sqlite3Update( /* If there are triggers, close all the cursors after each iteration ** through the loop. The fire the after triggers. */ - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); sqlite3VdbeJumpHere(v, iEndAfterTrigger); } @@ -557,7 +559,7 @@ void sqlite3Update( } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); - if( triggers_exist ){ + if( pTrigger ){ sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0); sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); } |