xref: /sqlite-3.40.0/src/trigger.c (revision 0951d703)
1c3f9bad2Sdanielk1977 /*
2633ed08dSdanielk1977 **
3633ed08dSdanielk1977 ** The author disclaims copyright to this source code.  In place of
4633ed08dSdanielk1977 ** a legal notice, here is a blessing:
5633ed08dSdanielk1977 **
6633ed08dSdanielk1977 **    May you do good and not evil.
7633ed08dSdanielk1977 **    May you find forgiveness for yourself and forgive others.
8633ed08dSdanielk1977 **    May you share freely, never taking more than you give.
9633ed08dSdanielk1977 **
10633ed08dSdanielk1977 *************************************************************************
11633ed08dSdanielk1977 *
12c3f9bad2Sdanielk1977 */
13c3f9bad2Sdanielk1977 #include "sqliteInt.h"
149adf9ac4Sdrh 
15c3f9bad2Sdanielk1977 /*
164b59ab5eSdrh ** Delete a linked list of TriggerStep structures.
174b59ab5eSdrh */
184b59ab5eSdrh static void sqliteDeleteTriggerStep(TriggerStep *pTriggerStep){
194b59ab5eSdrh   while( pTriggerStep ){
204b59ab5eSdrh     TriggerStep * pTmp = pTriggerStep;
214b59ab5eSdrh     pTriggerStep = pTriggerStep->pNext;
224b59ab5eSdrh 
238c74a8caSdrh     if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z);
244b59ab5eSdrh     sqliteExprDelete(pTmp->pWhere);
254b59ab5eSdrh     sqliteExprListDelete(pTmp->pExprList);
264b59ab5eSdrh     sqliteSelectDelete(pTmp->pSelect);
274b59ab5eSdrh     sqliteIdListDelete(pTmp->pIdList);
284b59ab5eSdrh 
294b59ab5eSdrh     sqliteFree(pTmp);
304b59ab5eSdrh   }
314b59ab5eSdrh }
324b59ab5eSdrh 
334b59ab5eSdrh /*
34633ed08dSdanielk1977 ** This is called by the parser when it sees a CREATE TRIGGER statement. See
35633ed08dSdanielk1977 ** comments surrounding struct Trigger in sqliteInt.h for a description of
36633ed08dSdanielk1977 ** how triggers are stored.
37c3f9bad2Sdanielk1977 */
389adf9ac4Sdrh void sqliteCreateTrigger(
39c3f9bad2Sdanielk1977   Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
40633ed08dSdanielk1977   Token *pName,       /* The name of the trigger */
41d702fccbSdanielk1977   int tr_tm,          /* One of TK_BEFORE, TK_AFTER , TK_INSTEAD */
42c3f9bad2Sdanielk1977   int op,             /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
43633ed08dSdanielk1977   IdList *pColumns,   /* column list if this is an UPDATE OF trigger */
44d24cc427Sdrh   SrcList *pTableName,/* The name of the table/view the trigger applies to */
45c3f9bad2Sdanielk1977   int foreach,        /* One of TK_ROW or TK_STATEMENT */
46c3f9bad2Sdanielk1977   Expr *pWhen,        /* WHEN clause */
47633ed08dSdanielk1977   TriggerStep *pStepList, /* The triggered program */
484b59ab5eSdrh   Token *pAll             /* Token that describes the complete CREATE TRIGGER */
499adf9ac4Sdrh ){
50c3f9bad2Sdanielk1977   Trigger *nt;
51c3f9bad2Sdanielk1977   Table   *tab;
52e5f9c644Sdrh   char *zName = 0;    /* Name of the trigger */
53d24cc427Sdrh   sqlite *db = pParse->db;
54ed6c8671Sdrh 
55c3f9bad2Sdanielk1977   /* Check that:
569adf9ac4Sdrh   ** 1. the trigger name does not already exist.
579adf9ac4Sdrh   ** 2. the table (or view) does exist.
58d702fccbSdanielk1977   ** 3. that we are not trying to create a trigger on the sqlite_master table
59d702fccbSdanielk1977   ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
60d702fccbSdanielk1977   ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
61c3f9bad2Sdanielk1977   */
62d24cc427Sdrh   if( sqlite_malloc_failed ) goto trigger_cleanup;
63d24cc427Sdrh   assert( pTableName->nSrc==1 );
64d24cc427Sdrh   tab = sqliteTableNameToTable(pParse, pTableName->a[0].zName,
65d24cc427Sdrh                                 pTableName->a[0].zDatabase);
66d24cc427Sdrh   if( !tab ){
67d24cc427Sdrh     goto trigger_cleanup;
68d24cc427Sdrh   }
69d24cc427Sdrh   if( tab->iDb>=2 ){
70d24cc427Sdrh     sqliteSetString(&pParse->zErrMsg, "triggers may not be added to "
71d24cc427Sdrh        "auxiliary database \"", db->aDb[tab->iDb].zName, "\"", 0);
72d24cc427Sdrh     pParse->nErr++;
73d24cc427Sdrh     goto trigger_cleanup;
74d24cc427Sdrh   }
75d24cc427Sdrh 
76e5f9c644Sdrh   zName = sqliteStrNDup(pName->z, pName->n);
77d24cc427Sdrh   if( sqliteHashFind(&(db->aDb[tab->iDb].trigHash), zName,pName->n+1) ){
78c3f9bad2Sdanielk1977     sqliteSetNString(&pParse->zErrMsg, "trigger ", -1,
79633ed08dSdanielk1977         pName->z, pName->n, " already exists", -1, 0);
80c3f9bad2Sdanielk1977     pParse->nErr++;
81c3f9bad2Sdanielk1977     goto trigger_cleanup;
82c3f9bad2Sdanielk1977   }
83d24cc427Sdrh   if( sqliteStrNICmp(tab->zName, "sqlite_", 7)==0 ){
84d24cc427Sdrh     sqliteSetString(&pParse->zErrMsg,"cannot create trigger on system table",0);
85e0bc4048Sdrh     pParse->nErr++;
86e0bc4048Sdrh     goto trigger_cleanup;
87e0bc4048Sdrh   }
88d702fccbSdanielk1977   if( tab->pSelect && tr_tm != TK_INSTEAD ){
89*0951d703Sdrh     sqliteSetString(&pParse->zErrMsg, "cannot create ",
90d24cc427Sdrh         (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", " trigger on view: ",
91d24cc427Sdrh         pTableName->a[0].zName, 0);
92d702fccbSdanielk1977     goto trigger_cleanup;
93d702fccbSdanielk1977   }
94d702fccbSdanielk1977   if( !tab->pSelect && tr_tm == TK_INSTEAD ){
95*0951d703Sdrh     sqliteSetString(&pParse->zErrMsg, "cannot create INSTEAD OF",
96*0951d703Sdrh         " trigger on table: ", pTableName->a[0].zName, 0);
97d702fccbSdanielk1977     goto trigger_cleanup;
98d702fccbSdanielk1977   }
99e5f9c644Sdrh #ifndef SQLITE_OMIT_AUTHORIZATION
100e5f9c644Sdrh   {
101e5f9c644Sdrh     int code = SQLITE_CREATE_TRIGGER;
102d24cc427Sdrh     if( tab->iDb==1 ) code = SQLITE_CREATE_TEMP_TRIGGER;
103e5f9c644Sdrh     if( sqliteAuthCheck(pParse, code, zName, tab->zName) ){
104ed6c8671Sdrh       goto trigger_cleanup;
105ed6c8671Sdrh     }
106d24cc427Sdrh     if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0)){
10777ad4e41Sdrh       goto trigger_cleanup;
10877ad4e41Sdrh     }
109d702fccbSdanielk1977   }
110e5f9c644Sdrh #endif
111d702fccbSdanielk1977 
112d702fccbSdanielk1977   if (tr_tm == TK_INSTEAD){
113d702fccbSdanielk1977     tr_tm = TK_BEFORE;
114c3f9bad2Sdanielk1977   }
115c3f9bad2Sdanielk1977 
116c3f9bad2Sdanielk1977   /* Build the Trigger object */
117c3f9bad2Sdanielk1977   nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
118e4697f5eSdrh   if( nt==0 ) goto trigger_cleanup;
119e5f9c644Sdrh   nt->name = zName;
120e5f9c644Sdrh   zName = 0;
121d24cc427Sdrh   nt->table = sqliteStrDup(pTableName->a[0].zName);
122e4697f5eSdrh   if( sqlite_malloc_failed ) goto trigger_cleanup;
123d24cc427Sdrh   nt->iDb = tab->iDb;
124c3f9bad2Sdanielk1977   nt->op = op;
125c3f9bad2Sdanielk1977   nt->tr_tm = tr_tm;
1264b59ab5eSdrh   nt->pWhen = sqliteExprDup(pWhen);
1274b59ab5eSdrh   sqliteExprDelete(pWhen);
1284b59ab5eSdrh   nt->pColumns = sqliteIdListDup(pColumns);
1294b59ab5eSdrh   sqliteIdListDelete(pColumns);
130c3f9bad2Sdanielk1977   nt->foreach = foreach;
131633ed08dSdanielk1977   nt->step_list = pStepList;
132c3f9bad2Sdanielk1977 
133c3f9bad2Sdanielk1977   /* if we are not initializing, and this trigger is not on a TEMP table,
1349adf9ac4Sdrh   ** build the sqlite_master entry
1359adf9ac4Sdrh   */
136e0bc4048Sdrh   if( !pParse->initFlag ){
137c977f7f5Sdrh     static VdbeOp insertTrig[] = {
138c977f7f5Sdrh       { OP_NewRecno,   0, 0,  0          },
139c977f7f5Sdrh       { OP_String,     0, 0,  "trigger"  },
140e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 2: trigger name */
141e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 3: table name */
142c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },
143e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 5: SQL */
144c977f7f5Sdrh       { OP_MakeRecord, 5, 0,  0          },
145c977f7f5Sdrh       { OP_PutIntKey,  0, 0,  0          },
146c977f7f5Sdrh     };
147c977f7f5Sdrh     int addr;
148c977f7f5Sdrh     Vdbe *v;
149c3f9bad2Sdanielk1977 
150c3f9bad2Sdanielk1977     /* Make an entry in the sqlite_master table */
151c977f7f5Sdrh     v = sqliteGetVdbe(pParse);
152e4697f5eSdrh     if( v==0 ) goto trigger_cleanup;
153cabb0819Sdrh     sqliteBeginWriteOperation(pParse, 0, 0);
154d24cc427Sdrh     sqliteOpenMasterTable(v, tab->iDb);
155c977f7f5Sdrh     addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
156d24cc427Sdrh     sqliteVdbeChangeP3(v, addr, tab->iDb ? TEMP_MASTER_NAME : MASTER_NAME,
157e0bc4048Sdrh                        P3_STATIC);
158e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
159e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
1604b59ab5eSdrh     sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
161d24cc427Sdrh     if( tab->iDb==0 ){
162d24cc427Sdrh       sqliteChangeCookie(db, v);
163e0bc4048Sdrh     }
164e0bc4048Sdrh     sqliteVdbeAddOp(v, OP_Close, 0, 0);
165c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
166c3f9bad2Sdanielk1977   }
167c3f9bad2Sdanielk1977 
168c3f9bad2Sdanielk1977   if( !pParse->explain ){
169c3f9bad2Sdanielk1977     /* Stick it in the hash-table */
170d24cc427Sdrh     sqliteHashInsert(&(db->aDb[nt->iDb].trigHash), nt->name, pName->n + 1, nt);
171c3f9bad2Sdanielk1977 
172c3f9bad2Sdanielk1977     /* Attach it to the table object */
173c3f9bad2Sdanielk1977     nt->pNext = tab->pTrigger;
174c3f9bad2Sdanielk1977     tab->pTrigger = nt;
175c3f9bad2Sdanielk1977     return;
176c3f9bad2Sdanielk1977   }else{
177c3f9bad2Sdanielk1977     sqliteFree(nt->name);
178c3f9bad2Sdanielk1977     sqliteFree(nt->table);
179c3f9bad2Sdanielk1977     sqliteFree(nt);
180c3f9bad2Sdanielk1977   }
181c3f9bad2Sdanielk1977 
182c3f9bad2Sdanielk1977 trigger_cleanup:
183c3f9bad2Sdanielk1977 
184e5f9c644Sdrh   sqliteFree(zName);
185d24cc427Sdrh   sqliteSrcListDelete(pTableName);
186633ed08dSdanielk1977   sqliteIdListDelete(pColumns);
187c3f9bad2Sdanielk1977   sqliteExprDelete(pWhen);
1884b59ab5eSdrh   sqliteDeleteTriggerStep(pStepList);
189c3f9bad2Sdanielk1977 }
1904b59ab5eSdrh 
1914b59ab5eSdrh /*
1924b59ab5eSdrh ** Make a copy of all components of the given trigger step.  This has
1934b59ab5eSdrh ** the effect of copying all Expr.token.z values into memory obtained
1944b59ab5eSdrh ** from sqliteMalloc().  As initially created, the Expr.token.z values
1954b59ab5eSdrh ** all point to the input string that was fed to the parser.  But that
1964b59ab5eSdrh ** string is ephemeral - it will go away as soon as the sqlite_exec()
1974b59ab5eSdrh ** call that started the parser exits.  This routine makes a persistent
1984b59ab5eSdrh ** copy of all the Expr.token.z strings so that the TriggerStep structure
1994b59ab5eSdrh ** will be valid even after the sqlite_exec() call returns.
2004b59ab5eSdrh */
2014b59ab5eSdrh static void sqlitePersistTriggerStep(TriggerStep *p){
2024b59ab5eSdrh   if( p->target.z ){
2034b59ab5eSdrh     p->target.z = sqliteStrNDup(p->target.z, p->target.n);
2044b59ab5eSdrh     p->target.dyn = 1;
2054b59ab5eSdrh   }
2064b59ab5eSdrh   if( p->pSelect ){
2074b59ab5eSdrh     Select *pNew = sqliteSelectDup(p->pSelect);
2084b59ab5eSdrh     sqliteSelectDelete(p->pSelect);
2094b59ab5eSdrh     p->pSelect = pNew;
2104b59ab5eSdrh   }
2114b59ab5eSdrh   if( p->pWhere ){
2124b59ab5eSdrh     Expr *pNew = sqliteExprDup(p->pWhere);
2134b59ab5eSdrh     sqliteExprDelete(p->pWhere);
2144b59ab5eSdrh     p->pWhere = pNew;
2154b59ab5eSdrh   }
2164b59ab5eSdrh   if( p->pExprList ){
2174b59ab5eSdrh     ExprList *pNew = sqliteExprListDup(p->pExprList);
2184b59ab5eSdrh     sqliteExprListDelete(p->pExprList);
2194b59ab5eSdrh     p->pExprList = pNew;
2204b59ab5eSdrh   }
2214b59ab5eSdrh   if( p->pIdList ){
2224b59ab5eSdrh     IdList *pNew = sqliteIdListDup(p->pIdList);
2234b59ab5eSdrh     sqliteIdListDelete(p->pIdList);
2244b59ab5eSdrh     p->pIdList = pNew;
225c3f9bad2Sdanielk1977   }
226c3f9bad2Sdanielk1977 }
227c3f9bad2Sdanielk1977 
228c977f7f5Sdrh /*
229c977f7f5Sdrh ** Turn a SELECT statement (that the pSelect parameter points to) into
230c977f7f5Sdrh ** a trigger step.  Return a pointer to a TriggerStep structure.
231c977f7f5Sdrh **
232c977f7f5Sdrh ** The parser calls this routine when it finds a SELECT statement in
233c977f7f5Sdrh ** body of a TRIGGER.
234c977f7f5Sdrh */
235c977f7f5Sdrh TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
236633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
237e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
238c3f9bad2Sdanielk1977 
239633ed08dSdanielk1977   pTriggerStep->op = TK_SELECT;
240633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
241633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
2424b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
243c3f9bad2Sdanielk1977 
244633ed08dSdanielk1977   return pTriggerStep;
245c3f9bad2Sdanielk1977 }
246c3f9bad2Sdanielk1977 
247c977f7f5Sdrh /*
248c977f7f5Sdrh ** Build a trigger step out of an INSERT statement.  Return a pointer
249c977f7f5Sdrh ** to the new trigger step.
250c977f7f5Sdrh **
251c977f7f5Sdrh ** The parser calls this routine when it sees an INSERT inside the
252c977f7f5Sdrh ** body of a trigger.
253c977f7f5Sdrh */
254633ed08dSdanielk1977 TriggerStep *sqliteTriggerInsertStep(
255c977f7f5Sdrh   Token *pTableName,  /* Name of the table into which we insert */
256c977f7f5Sdrh   IdList *pColumn,    /* List of columns in pTableName to insert into */
257c977f7f5Sdrh   ExprList *pEList,   /* The VALUE clause: a list of values to be inserted */
258c977f7f5Sdrh   Select *pSelect,    /* A SELECT statement that supplies values */
259c977f7f5Sdrh   int orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
260633ed08dSdanielk1977 ){
261633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
262e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
263c3f9bad2Sdanielk1977 
264633ed08dSdanielk1977   assert(pEList == 0 || pSelect == 0);
265633ed08dSdanielk1977   assert(pEList != 0 || pSelect != 0);
266c3f9bad2Sdanielk1977 
267633ed08dSdanielk1977   pTriggerStep->op = TK_INSERT;
268633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
269633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
270633ed08dSdanielk1977   pTriggerStep->pIdList = pColumn;
271633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
272633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
2734b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
274c3f9bad2Sdanielk1977 
275633ed08dSdanielk1977   return pTriggerStep;
276c3f9bad2Sdanielk1977 }
277c3f9bad2Sdanielk1977 
278c977f7f5Sdrh /*
279c977f7f5Sdrh ** Construct a trigger step that implements an UPDATE statement and return
280c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
281c977f7f5Sdrh ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
282c977f7f5Sdrh */
283633ed08dSdanielk1977 TriggerStep *sqliteTriggerUpdateStep(
284c977f7f5Sdrh   Token *pTableName,   /* Name of the table to be updated */
285c977f7f5Sdrh   ExprList *pEList,    /* The SET clause: list of column and new values */
286c977f7f5Sdrh   Expr *pWhere,        /* The WHERE clause */
287c977f7f5Sdrh   int orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
288c977f7f5Sdrh ){
289633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
290e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
291c3f9bad2Sdanielk1977 
292633ed08dSdanielk1977   pTriggerStep->op = TK_UPDATE;
293633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
294633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
295633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
296633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
2974b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
298c3f9bad2Sdanielk1977 
299633ed08dSdanielk1977   return pTriggerStep;
300c3f9bad2Sdanielk1977 }
301c3f9bad2Sdanielk1977 
302c977f7f5Sdrh /*
303c977f7f5Sdrh ** Construct a trigger step that implements a DELETE statement and return
304c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
305c977f7f5Sdrh ** sees a DELETE statement inside the body of a CREATE TRIGGER.
306c977f7f5Sdrh */
307c977f7f5Sdrh TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
308633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
309e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
310c3f9bad2Sdanielk1977 
311633ed08dSdanielk1977   pTriggerStep->op = TK_DELETE;
312633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
313633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
314633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
3154b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
316c3f9bad2Sdanielk1977 
317633ed08dSdanielk1977   return pTriggerStep;
318c3f9bad2Sdanielk1977 }
319c3f9bad2Sdanielk1977 
320c3f9bad2Sdanielk1977 /*
321633ed08dSdanielk1977 ** Recursively delete a Trigger structure
322c3f9bad2Sdanielk1977 */
3231d1f3055Sdrh void sqliteDeleteTrigger(Trigger *pTrigger){
3244b59ab5eSdrh   sqliteDeleteTriggerStep(pTrigger->step_list);
325633ed08dSdanielk1977   sqliteFree(pTrigger->name);
326633ed08dSdanielk1977   sqliteFree(pTrigger->table);
327633ed08dSdanielk1977   sqliteExprDelete(pTrigger->pWhen);
328633ed08dSdanielk1977   sqliteIdListDelete(pTrigger->pColumns);
329633ed08dSdanielk1977   sqliteFree(pTrigger);
330633ed08dSdanielk1977 }
331633ed08dSdanielk1977 
332633ed08dSdanielk1977 /*
333633ed08dSdanielk1977  * This function is called to drop a trigger from the database schema.
334633ed08dSdanielk1977  *
335633ed08dSdanielk1977  * This may be called directly from the parser, or from within
336633ed08dSdanielk1977  * sqliteDropTable(). In the latter case the "nested" argument is true.
337633ed08dSdanielk1977  *
338633ed08dSdanielk1977  * Note that this function does not delete the trigger entirely. Instead it
339633ed08dSdanielk1977  * removes it from the internal schema and places it in the trigDrop hash
340633ed08dSdanielk1977  * table. This is so that the trigger can be restored into the database schema
341633ed08dSdanielk1977  * if the transaction is rolled back.
342633ed08dSdanielk1977  */
343d24cc427Sdrh void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
344633ed08dSdanielk1977   Trigger *pTrigger;
345633ed08dSdanielk1977   Table   *pTable;
346e0bc4048Sdrh   Vdbe *v;
347d24cc427Sdrh   int i;
348d24cc427Sdrh   const char *zDb;
349d24cc427Sdrh   const char *zName;
350d24cc427Sdrh   int nName;
351d24cc427Sdrh   sqlite *db = pParse->db;
352633ed08dSdanielk1977 
353d24cc427Sdrh   if( sqlite_malloc_failed ) goto drop_trigger_cleanup;
354d24cc427Sdrh   assert( pName->nSrc==1 );
355d24cc427Sdrh   zDb = pName->a[0].zDatabase;
356d24cc427Sdrh   zName = pName->a[0].zName;
357d24cc427Sdrh   nName = strlen(zName);
358d24cc427Sdrh   for(i=0; i<db->nDb; i++){
359d24cc427Sdrh     if( zDb && sqliteStrICmp(db->aDb[i].zName, zDb) ) continue;
360d24cc427Sdrh     pTrigger = sqliteHashFind(&(db->aDb[i].trigHash), zName, nName+1);
361d24cc427Sdrh     if( pTrigger ) break;
362c3f9bad2Sdanielk1977   }
363d24cc427Sdrh   if( !pTrigger ){
364d24cc427Sdrh     sqliteSetString(&pParse->zErrMsg, "no such trigger: ", zName, 0);
365d24cc427Sdrh     goto drop_trigger_cleanup;
366d24cc427Sdrh   }
367d24cc427Sdrh   assert( pTrigger->iDb>=0 && pTrigger->iDb<db->nDb );
368d24cc427Sdrh   if( pTrigger->iDb>=2 ){
369d24cc427Sdrh     sqliteSetString(&pParse->zErrMsg, "triggers may not be removed from "
370d24cc427Sdrh        "auxiliary database \"", db->aDb[pTrigger->iDb].zName, "\"", 0);
371d24cc427Sdrh     pParse->nErr++;
372d24cc427Sdrh     goto drop_trigger_cleanup;
373d24cc427Sdrh   }
374d24cc427Sdrh   pTable = sqliteFindTable(db, pTrigger->table, db->aDb[pTrigger->iDb].zName);
375ed6c8671Sdrh   assert(pTable);
376d24cc427Sdrh   assert( pTable->iDb==pTrigger->iDb );
377e5f9c644Sdrh #ifndef SQLITE_OMIT_AUTHORIZATION
378e5f9c644Sdrh   {
379e5f9c644Sdrh     int code = SQLITE_DROP_TRIGGER;
380d24cc427Sdrh     if( pTable->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;
381e5f9c644Sdrh     if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName) ||
382d24cc427Sdrh       sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->iDb),0) ){
383ed6c8671Sdrh       return;
384ed6c8671Sdrh     }
385e5f9c644Sdrh   }
386e5f9c644Sdrh #endif
387c3f9bad2Sdanielk1977 
388c3f9bad2Sdanielk1977   /*
389e0bc4048Sdrh    * If this is not an "explain", then delete the trigger structure.
390c3f9bad2Sdanielk1977    */
391c3f9bad2Sdanielk1977   if( !pParse->explain ){
392633ed08dSdanielk1977     if( pTable->pTrigger == pTrigger ){
393633ed08dSdanielk1977       pTable->pTrigger = pTrigger->pNext;
394633ed08dSdanielk1977     }else{
395633ed08dSdanielk1977       Trigger *cc = pTable->pTrigger;
396c3f9bad2Sdanielk1977       while( cc ){
397633ed08dSdanielk1977         if( cc->pNext == pTrigger ){
398c3f9bad2Sdanielk1977           cc->pNext = cc->pNext->pNext;
399c3f9bad2Sdanielk1977           break;
400c3f9bad2Sdanielk1977         }
401c3f9bad2Sdanielk1977         cc = cc->pNext;
402c3f9bad2Sdanielk1977       }
403c3f9bad2Sdanielk1977       assert(cc);
404c3f9bad2Sdanielk1977     }
405d24cc427Sdrh     sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);
406e0bc4048Sdrh     sqliteDeleteTrigger(pTrigger);
407c3f9bad2Sdanielk1977   }
408c3f9bad2Sdanielk1977 
409e0bc4048Sdrh   /* Generate code to destroy the database record of the trigger.
410e0bc4048Sdrh   */
411e0bc4048Sdrh   if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
412c3f9bad2Sdanielk1977     int base;
413c3f9bad2Sdanielk1977     static VdbeOp dropTrigger[] = {
414e0bc4048Sdrh       { OP_Rewind,     0, ADDR(8),  0},
415e0bc4048Sdrh       { OP_String,     0, 0,        0}, /* 1 */
416c3f9bad2Sdanielk1977       { OP_MemStore,   1, 1,        0},
417e0bc4048Sdrh       { OP_MemLoad,    1, 0,        0}, /* 3 */
418c3f9bad2Sdanielk1977       { OP_Column,     0, 1,        0},
419e0bc4048Sdrh       { OP_Ne,         0, ADDR(7),  0},
420c3f9bad2Sdanielk1977       { OP_Delete,     0, 0,        0},
421e0bc4048Sdrh       { OP_Next,       0, ADDR(3),  0}, /* 7 */
422c3f9bad2Sdanielk1977     };
423c3f9bad2Sdanielk1977 
424cabb0819Sdrh     sqliteBeginWriteOperation(pParse, 0, 0);
425d24cc427Sdrh     sqliteOpenMasterTable(v, pTable->iDb);
426e0bc4048Sdrh     base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
427e0bc4048Sdrh     sqliteVdbeChangeP3(v, base+1, zName, 0);
428d24cc427Sdrh     if( pTable->iDb==0 ){
429d24cc427Sdrh       sqliteChangeCookie(db, v);
430dc379456Sdrh     }
431e0bc4048Sdrh     sqliteVdbeAddOp(v, OP_Close, 0, 0);
432c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
433c3f9bad2Sdanielk1977   }
434c3f9bad2Sdanielk1977 
435d24cc427Sdrh drop_trigger_cleanup:
436d24cc427Sdrh   sqliteSrcListDelete(pName);
437c3f9bad2Sdanielk1977 }
438c3f9bad2Sdanielk1977 
439c977f7f5Sdrh /*
440c977f7f5Sdrh ** pEList is the SET clause of an UPDATE statement.  Each entry
441c977f7f5Sdrh ** in pEList is of the format <id>=<expr>.  If any of the entries
442c977f7f5Sdrh ** in pEList have an <id> which matches an identifier in pIdList,
443c977f7f5Sdrh ** then return TRUE.  If pIdList==NULL, then it is considered a
444c977f7f5Sdrh ** wildcard that matches anything.  Likewise if pEList==NULL then
445c977f7f5Sdrh ** it matches anything so always return true.  Return false only
446c977f7f5Sdrh ** if there is no match.
447c977f7f5Sdrh */
448c977f7f5Sdrh static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
449ad2d8307Sdrh   int e;
450ad2d8307Sdrh   if( !pIdList || !pEList ) return 1;
451f29ce559Sdanielk1977   for(e=0; e<pEList->nExpr; e++){
452ad2d8307Sdrh     if( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
453f29ce559Sdanielk1977   }
454c3f9bad2Sdanielk1977   return 0;
455c3f9bad2Sdanielk1977 }
456c3f9bad2Sdanielk1977 
457c3f9bad2Sdanielk1977 /* A global variable that is TRUE if we should always set up temp tables for
458c3f9bad2Sdanielk1977  * for triggers, even if there are no triggers to code. This is used to test
459c3f9bad2Sdanielk1977  * how much overhead the triggers algorithm is causing.
460c3f9bad2Sdanielk1977  *
461c3f9bad2Sdanielk1977  * This flag can be set or cleared using the "trigger_overhead_test" pragma.
462c3f9bad2Sdanielk1977  * The pragma is not documented since it is not really part of the interface
463c3f9bad2Sdanielk1977  * to SQLite, just the test procedure.
464c3f9bad2Sdanielk1977 */
465c3f9bad2Sdanielk1977 int always_code_trigger_setup = 0;
466c3f9bad2Sdanielk1977 
467c3f9bad2Sdanielk1977 /*
468c3f9bad2Sdanielk1977  * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
469c3f9bad2Sdanielk1977  * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
470c3f9bad2Sdanielk1977  * found in the list specified as pTrigger.
471c3f9bad2Sdanielk1977  */
472c3f9bad2Sdanielk1977 int sqliteTriggersExist(
473c977f7f5Sdrh   Parse *pParse,          /* Used to check for recursive triggers */
474c977f7f5Sdrh   Trigger *pTrigger,      /* A list of triggers associated with a table */
475c3f9bad2Sdanielk1977   int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
476c3f9bad2Sdanielk1977   int tr_tm,              /* one of TK_BEFORE, TK_AFTER */
477c3f9bad2Sdanielk1977   int foreach,            /* one of TK_ROW or TK_STATEMENT */
478c977f7f5Sdrh   ExprList *pChanges      /* Columns that change in an UPDATE statement */
479c977f7f5Sdrh ){
480633ed08dSdanielk1977   Trigger * pTriggerCursor;
481c3f9bad2Sdanielk1977 
482633ed08dSdanielk1977   if( always_code_trigger_setup ){
483633ed08dSdanielk1977     return 1;
484633ed08dSdanielk1977   }
485c3f9bad2Sdanielk1977 
486633ed08dSdanielk1977   pTriggerCursor = pTrigger;
487633ed08dSdanielk1977   while( pTriggerCursor ){
488633ed08dSdanielk1977     if( pTriggerCursor->op == op &&
489633ed08dSdanielk1977 	pTriggerCursor->tr_tm == tr_tm &&
490633ed08dSdanielk1977 	pTriggerCursor->foreach == foreach &&
491633ed08dSdanielk1977 	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
492c3f9bad2Sdanielk1977       TriggerStack * ss;
493c3f9bad2Sdanielk1977       ss = pParse->trigStack;
494f29ce559Sdanielk1977       while( ss && ss->pTrigger != pTrigger ){
495f29ce559Sdanielk1977 	ss = ss->pNext;
496f29ce559Sdanielk1977       }
497c3f9bad2Sdanielk1977       if( !ss )return 1;
498c3f9bad2Sdanielk1977     }
499633ed08dSdanielk1977     pTriggerCursor = pTriggerCursor->pNext;
500c3f9bad2Sdanielk1977   }
501c3f9bad2Sdanielk1977 
502c3f9bad2Sdanielk1977   return 0;
503c3f9bad2Sdanielk1977 }
504c3f9bad2Sdanielk1977 
505c977f7f5Sdrh /*
506c977f7f5Sdrh ** Generate VDBE code for zero or more statements inside the body of a
507c977f7f5Sdrh ** trigger.
508c977f7f5Sdrh */
509c3f9bad2Sdanielk1977 static int codeTriggerProgram(
510c977f7f5Sdrh   Parse *pParse,            /* The parser context */
511c977f7f5Sdrh   TriggerStep *pStepList,   /* List of statements inside the trigger body */
512c977f7f5Sdrh   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */
513633ed08dSdanielk1977 ){
514633ed08dSdanielk1977   TriggerStep * pTriggerStep = pStepList;
515c3f9bad2Sdanielk1977   int orconf;
516c3f9bad2Sdanielk1977 
517633ed08dSdanielk1977   while( pTriggerStep ){
518c3f9bad2Sdanielk1977     int saveNTab = pParse->nTab;
519633ed08dSdanielk1977     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
520c3f9bad2Sdanielk1977     pParse->trigStack->orconf = orconf;
521633ed08dSdanielk1977     switch( pTriggerStep->op ){
522c3f9bad2Sdanielk1977       case TK_SELECT: {
5236f34903eSdanielk1977 	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
5246f34903eSdanielk1977 	assert(ss);
5256f34903eSdanielk1977 	assert(ss->pSrc);
5266f34903eSdanielk1977 	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
5276f34903eSdanielk1977 	sqliteSelectDelete(ss);
528c3f9bad2Sdanielk1977 	break;
529c3f9bad2Sdanielk1977       }
530c3f9bad2Sdanielk1977       case TK_UPDATE: {
531113088ecSdrh         SrcList *pSrc;
532113088ecSdrh         pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
533bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
534113088ecSdrh         sqliteUpdate(pParse, pSrc,
535633ed08dSdanielk1977 		sqliteExprListDup(pTriggerStep->pExprList),
536633ed08dSdanielk1977 		sqliteExprDup(pTriggerStep->pWhere), orconf);
537bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
538c3f9bad2Sdanielk1977         break;
539c3f9bad2Sdanielk1977       }
540c3f9bad2Sdanielk1977       case TK_INSERT: {
541113088ecSdrh         SrcList *pSrc;
542113088ecSdrh         pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
543113088ecSdrh         sqliteInsert(pParse, pSrc,
544633ed08dSdanielk1977           sqliteExprListDup(pTriggerStep->pExprList),
545633ed08dSdanielk1977           sqliteSelectDup(pTriggerStep->pSelect),
546633ed08dSdanielk1977           sqliteIdListDup(pTriggerStep->pIdList), orconf);
547c3f9bad2Sdanielk1977         break;
548c3f9bad2Sdanielk1977       }
549c3f9bad2Sdanielk1977       case TK_DELETE: {
550113088ecSdrh         SrcList *pSrc;
551bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
552113088ecSdrh         pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
553113088ecSdrh         sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
554bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
555c3f9bad2Sdanielk1977         break;
556c3f9bad2Sdanielk1977       }
557c3f9bad2Sdanielk1977       default:
558c3f9bad2Sdanielk1977         assert(0);
559c3f9bad2Sdanielk1977     }
560c3f9bad2Sdanielk1977     pParse->nTab = saveNTab;
561633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
562c3f9bad2Sdanielk1977   }
563c3f9bad2Sdanielk1977 
564c3f9bad2Sdanielk1977   return 0;
565c3f9bad2Sdanielk1977 }
566c3f9bad2Sdanielk1977 
567633ed08dSdanielk1977 /*
568633ed08dSdanielk1977 ** This is called to code FOR EACH ROW triggers.
569633ed08dSdanielk1977 **
570633ed08dSdanielk1977 ** When the code that this function generates is executed, the following
571633ed08dSdanielk1977 ** must be true:
572c977f7f5Sdrh **
573c977f7f5Sdrh ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
574c977f7f5Sdrh **    can be indices of cursors in temporary tables.  See below.)
575c977f7f5Sdrh **
576633ed08dSdanielk1977 ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
577633ed08dSdanielk1977 **    a temporary vdbe cursor (index newIdx) must be open and pointing at
578633ed08dSdanielk1977 **    a row containing values to be substituted for new.* expressions in the
579633ed08dSdanielk1977 **    trigger program(s).
580c977f7f5Sdrh **
581633ed08dSdanielk1977 ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
582633ed08dSdanielk1977 **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
583633ed08dSdanielk1977 **    a row containing values to be substituted for old.* expressions in the
584633ed08dSdanielk1977 **    trigger program(s).
585633ed08dSdanielk1977 **
586633ed08dSdanielk1977 */
587c3f9bad2Sdanielk1977 int sqliteCodeRowTrigger(
588c3f9bad2Sdanielk1977   Parse *pParse,       /* Parse context */
589c3f9bad2Sdanielk1977   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
590633ed08dSdanielk1977   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
591c3f9bad2Sdanielk1977   int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
592633ed08dSdanielk1977   Table *pTab,         /* The table to code triggers from */
593633ed08dSdanielk1977   int newIdx,          /* The indice of the "new" row to access */
594633ed08dSdanielk1977   int oldIdx,          /* The indice of the "old" row to access */
5956f34903eSdanielk1977   int orconf,          /* ON CONFLICT policy */
5966f34903eSdanielk1977   int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
597c977f7f5Sdrh ){
598c3f9bad2Sdanielk1977   Trigger * pTrigger;
599c3f9bad2Sdanielk1977   TriggerStack * pTriggerStack;
600c3f9bad2Sdanielk1977 
601c3f9bad2Sdanielk1977   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
602c3f9bad2Sdanielk1977   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
603c3f9bad2Sdanielk1977 
604633ed08dSdanielk1977   assert(newIdx != -1 || oldIdx != -1);
605c3f9bad2Sdanielk1977 
606633ed08dSdanielk1977   pTrigger = pTab->pTrigger;
607c3f9bad2Sdanielk1977   while( pTrigger ){
608c3f9bad2Sdanielk1977     int fire_this = 0;
609c3f9bad2Sdanielk1977 
610c3f9bad2Sdanielk1977     /* determine whether we should code this trigger */
611c3f9bad2Sdanielk1977     if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
612c3f9bad2Sdanielk1977         pTrigger->foreach == TK_ROW ){
613c3f9bad2Sdanielk1977       fire_this = 1;
614c3f9bad2Sdanielk1977       pTriggerStack = pParse->trigStack;
615c3f9bad2Sdanielk1977       while( pTriggerStack ){
616f29ce559Sdanielk1977         if( pTriggerStack->pTrigger == pTrigger ){
617f29ce559Sdanielk1977 	  fire_this = 0;
618f29ce559Sdanielk1977 	}
619c3f9bad2Sdanielk1977         pTriggerStack = pTriggerStack->pNext;
620c3f9bad2Sdanielk1977       }
621c3f9bad2Sdanielk1977       if( op == TK_UPDATE && pTrigger->pColumns &&
622f29ce559Sdanielk1977           !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
623c3f9bad2Sdanielk1977         fire_this = 0;
624c3f9bad2Sdanielk1977       }
625f29ce559Sdanielk1977     }
626c3f9bad2Sdanielk1977 
627e4697f5eSdrh     if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
628c3f9bad2Sdanielk1977       int endTrigger;
629ad3cab52Sdrh       SrcList dummyTablist;
630c3f9bad2Sdanielk1977       Expr * whenExpr;
631c3f9bad2Sdanielk1977 
632ad3cab52Sdrh       dummyTablist.nSrc = 0;
633c3f9bad2Sdanielk1977 
634c3f9bad2Sdanielk1977       /* Push an entry on to the trigger stack */
635c3f9bad2Sdanielk1977       pTriggerStack->pTrigger = pTrigger;
636633ed08dSdanielk1977       pTriggerStack->newIdx = newIdx;
637633ed08dSdanielk1977       pTriggerStack->oldIdx = oldIdx;
638633ed08dSdanielk1977       pTriggerStack->pTab = pTab;
639c3f9bad2Sdanielk1977       pTriggerStack->pNext = pParse->trigStack;
6406f34903eSdanielk1977       pTriggerStack->ignoreJump = ignoreJump;
641c3f9bad2Sdanielk1977       pParse->trigStack = pTriggerStack;
642c3f9bad2Sdanielk1977 
643c3f9bad2Sdanielk1977       /* code the WHEN clause */
644c3f9bad2Sdanielk1977       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
645c3f9bad2Sdanielk1977       whenExpr = sqliteExprDup(pTrigger->pWhen);
646c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
647c3f9bad2Sdanielk1977         pParse->trigStack = pParse->trigStack->pNext;
648c3f9bad2Sdanielk1977         sqliteFree(pTriggerStack);
649c3f9bad2Sdanielk1977         sqliteExprDelete(whenExpr);
650c3f9bad2Sdanielk1977         return 1;
651c3f9bad2Sdanielk1977       }
652f5905aa7Sdrh       sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
653c3f9bad2Sdanielk1977       sqliteExprDelete(whenExpr);
654c3f9bad2Sdanielk1977 
655633ed08dSdanielk1977       codeTriggerProgram(pParse, pTrigger->step_list, orconf);
656c3f9bad2Sdanielk1977 
657c3f9bad2Sdanielk1977       /* Pop the entry off the trigger stack */
658c3f9bad2Sdanielk1977       pParse->trigStack = pParse->trigStack->pNext;
659c3f9bad2Sdanielk1977       sqliteFree(pTriggerStack);
660c3f9bad2Sdanielk1977 
661c3f9bad2Sdanielk1977       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
662c3f9bad2Sdanielk1977     }
663c3f9bad2Sdanielk1977     pTrigger = pTrigger->pNext;
664c3f9bad2Sdanielk1977   }
665c3f9bad2Sdanielk1977 
666c3f9bad2Sdanielk1977   return 0;
667c3f9bad2Sdanielk1977 }
668c3f9bad2Sdanielk1977 
669c3f9bad2Sdanielk1977 /*
670633ed08dSdanielk1977  * This function is called to code ON UPDATE and ON DELETE triggers on
671633ed08dSdanielk1977  * views.
672633ed08dSdanielk1977  *
673633ed08dSdanielk1977  * This function deletes the data pointed at by the pWhere and pChanges
674633ed08dSdanielk1977  * arguments before it completes.
675c3f9bad2Sdanielk1977  */
676633ed08dSdanielk1977 void sqliteViewTriggers(
677633ed08dSdanielk1977   Parse *pParse,
678633ed08dSdanielk1977   Table *pTab,         /* The view to code triggers on */
679633ed08dSdanielk1977   Expr *pWhere,        /* The WHERE clause of the statement causing triggers*/
680633ed08dSdanielk1977   int orconf,          /* The ON CONFLICT policy specified as part of the
681633ed08dSdanielk1977 			  statement causing these triggers */
682633ed08dSdanielk1977   ExprList *pChanges   /* If this is an statement causing triggers to fire
683633ed08dSdanielk1977 			  is an UPDATE, then this list holds the columns
684633ed08dSdanielk1977 			  to update and the expressions to update them to.
685633ed08dSdanielk1977 			  See comments for sqliteUpdate(). */
686633ed08dSdanielk1977 ){
687c3f9bad2Sdanielk1977   int oldIdx = -1;
688c3f9bad2Sdanielk1977   int newIdx = -1;
689c3f9bad2Sdanielk1977   int *aXRef = 0;
690c3f9bad2Sdanielk1977   Vdbe *v;
691c3f9bad2Sdanielk1977   int endOfLoop;
692c3f9bad2Sdanielk1977   int startOfLoop;
693c3f9bad2Sdanielk1977   Select theSelect;
694c3f9bad2Sdanielk1977   Token tblNameToken;
695c3f9bad2Sdanielk1977 
696c3f9bad2Sdanielk1977   assert(pTab->pSelect);
697c3f9bad2Sdanielk1977 
698c3f9bad2Sdanielk1977   tblNameToken.z = pTab->zName;
699c3f9bad2Sdanielk1977   tblNameToken.n = strlen(pTab->zName);
700c3f9bad2Sdanielk1977 
701c3f9bad2Sdanielk1977   theSelect.isDistinct = 0;
702c3f9bad2Sdanielk1977   theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
703113088ecSdrh   theSelect.pSrc   = sqliteSrcListAppend(0, &tblNameToken, 0);
704c3f9bad2Sdanielk1977   theSelect.pWhere = pWhere;    pWhere = 0;
705c3f9bad2Sdanielk1977   theSelect.pGroupBy = 0;
706c3f9bad2Sdanielk1977   theSelect.pHaving = 0;
707c3f9bad2Sdanielk1977   theSelect.pOrderBy = 0;
708c3f9bad2Sdanielk1977   theSelect.op = TK_SELECT; /* ?? */
709c3f9bad2Sdanielk1977   theSelect.pPrior = 0;
710c3f9bad2Sdanielk1977   theSelect.nLimit = -1;
711c3f9bad2Sdanielk1977   theSelect.nOffset = -1;
712c3f9bad2Sdanielk1977   theSelect.zSelect = 0;
713c3f9bad2Sdanielk1977   theSelect.base = 0;
714c3f9bad2Sdanielk1977 
715c3f9bad2Sdanielk1977   v = sqliteGetVdbe(pParse);
716c3f9bad2Sdanielk1977   assert(v);
717cabb0819Sdrh   sqliteBeginWriteOperation(pParse, 1, 0);
718c3f9bad2Sdanielk1977 
719c3f9bad2Sdanielk1977   /* Allocate temp tables */
720c3f9bad2Sdanielk1977   oldIdx = pParse->nTab++;
721c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
722c3f9bad2Sdanielk1977   if( pChanges ){
723c3f9bad2Sdanielk1977     newIdx = pParse->nTab++;
724c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
725c3f9bad2Sdanielk1977   }
726c3f9bad2Sdanielk1977 
727c3f9bad2Sdanielk1977   /* Snapshot the view */
728c3f9bad2Sdanielk1977   if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){
729c3f9bad2Sdanielk1977     goto trigger_cleanup;
730c3f9bad2Sdanielk1977   }
731c3f9bad2Sdanielk1977 
732c3f9bad2Sdanielk1977   /* loop thru the view snapshot, executing triggers for each row */
733c3f9bad2Sdanielk1977   endOfLoop = sqliteVdbeMakeLabel(v);
734c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);
735c3f9bad2Sdanielk1977 
736c3f9bad2Sdanielk1977   /* Loop thru the view snapshot, executing triggers for each row */
737c3f9bad2Sdanielk1977   startOfLoop = sqliteVdbeCurrentAddr(v);
738c3f9bad2Sdanielk1977 
739c3f9bad2Sdanielk1977   /* Build the updated row if required */
740c3f9bad2Sdanielk1977   if( pChanges ){
7411d1f3055Sdrh     int ii;
742c3f9bad2Sdanielk1977 
743c3f9bad2Sdanielk1977     aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
744c3f9bad2Sdanielk1977     if( aXRef==0 ) goto trigger_cleanup;
745633ed08dSdanielk1977     for(ii = 0; ii < pTab->nCol; ii++){
746c3f9bad2Sdanielk1977       aXRef[ii] = -1;
747633ed08dSdanielk1977     }
748c3f9bad2Sdanielk1977 
749c3f9bad2Sdanielk1977     for(ii=0; ii<pChanges->nExpr; ii++){
750c3f9bad2Sdanielk1977       int jj;
751c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0,
752f29ce559Sdanielk1977             pChanges->a[ii].pExpr) ){
753c3f9bad2Sdanielk1977         goto trigger_cleanup;
754f29ce559Sdanielk1977       }
755c3f9bad2Sdanielk1977 
756c3f9bad2Sdanielk1977       if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
757c3f9bad2Sdanielk1977         goto trigger_cleanup;
758c3f9bad2Sdanielk1977 
759c3f9bad2Sdanielk1977       for(jj=0; jj<pTab->nCol; jj++){
760c3f9bad2Sdanielk1977         if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
761c3f9bad2Sdanielk1977           aXRef[jj] = ii;
762c3f9bad2Sdanielk1977           break;
763c3f9bad2Sdanielk1977         }
764c3f9bad2Sdanielk1977       }
765c3f9bad2Sdanielk1977       if( jj>=pTab->nCol ){
766c3f9bad2Sdanielk1977         sqliteSetString(&pParse->zErrMsg, "no such column: ",
767c3f9bad2Sdanielk1977             pChanges->a[ii].zName, 0);
768c3f9bad2Sdanielk1977         pParse->nErr++;
769c3f9bad2Sdanielk1977         goto trigger_cleanup;
770c3f9bad2Sdanielk1977       }
771c3f9bad2Sdanielk1977     }
772c3f9bad2Sdanielk1977 
773c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Integer, 13, 0);
774c3f9bad2Sdanielk1977 
775633ed08dSdanielk1977     for(ii = 0; ii<pTab->nCol; ii++){
776633ed08dSdanielk1977       if( aXRef[ii] < 0 ){
777c3f9bad2Sdanielk1977         sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
778633ed08dSdanielk1977       }else{
779c3f9bad2Sdanielk1977         sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
780633ed08dSdanielk1977       }
781633ed08dSdanielk1977     }
782c3f9bad2Sdanielk1977 
783c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
784c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
785c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
786c3f9bad2Sdanielk1977 
787c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
7886f34903eSdanielk1977         pTab, newIdx, oldIdx, orconf, endOfLoop);
789c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
7906f34903eSdanielk1977         pTab, newIdx, oldIdx, orconf, endOfLoop);
791c3f9bad2Sdanielk1977   }else{
792c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
7936f34903eSdanielk1977         orconf, endOfLoop);
794c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
7956f34903eSdanielk1977         orconf, endOfLoop);
796c3f9bad2Sdanielk1977   }
797c3f9bad2Sdanielk1977 
798c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
799c3f9bad2Sdanielk1977 
800c3f9bad2Sdanielk1977   sqliteVdbeResolveLabel(v, endOfLoop);
801c3f9bad2Sdanielk1977   sqliteEndWriteOperation(pParse);
802c3f9bad2Sdanielk1977 
803c3f9bad2Sdanielk1977 trigger_cleanup:
804c3f9bad2Sdanielk1977   sqliteFree(aXRef);
805c3f9bad2Sdanielk1977   sqliteExprListDelete(pChanges);
806c3f9bad2Sdanielk1977   sqliteExprDelete(pWhere);
807c3f9bad2Sdanielk1977   sqliteExprListDelete(theSelect.pEList);
808ad3cab52Sdrh   sqliteSrcListDelete(theSelect.pSrc);
809c3f9bad2Sdanielk1977   sqliteExprDelete(theSelect.pWhere);
810c3f9bad2Sdanielk1977   return;
811c3f9bad2Sdanielk1977 }
812