aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordanielk1977 <danielk1977@noemail.net>2009-02-28 10:47:41 +0000
committerdanielk1977 <danielk1977@noemail.net>2009-02-28 10:47:41 +0000
commit2f886d1d534ae2fabca3e3bcb5af259c38b3cb23 (patch)
treea0e6b164c0bf7f829558d41b51758909f68032a8 /src
parent02b4e3b34e1544fe7da63bb81afc827a86e0926b (diff)
downloadsqlite-2f886d1d534ae2fabca3e3bcb5af259c38b3cb23.tar.gz
sqlite-2f886d1d534ae2fabca3e3bcb5af259c38b3cb23.zip
Instead of linking temporary triggers on non-temporary tables into the Table.pTrigger list, search the temp schema for them on demand. Fix for #3688. (CVS 6329)
FossilOrigin-Name: 3befe1ef7e6ebddedfa69579553a1b85b411ee98
Diffstat (limited to 'src')
-rw-r--r--src/alter.c6
-rw-r--r--src/analyze.c4
-rw-r--r--src/build.c4
-rw-r--r--src/delete.c36
-rw-r--r--src/insert.c39
-rw-r--r--src/sqliteInt.h13
-rw-r--r--src/trigger.c125
-rw-r--r--src/update.c32
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);
}