xref: /sqlite-3.40.0/src/trigger.c (revision d702fccb)
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 /*
16633ed08dSdanielk1977 ** This is called by the parser when it sees a CREATE TRIGGER statement. See
17633ed08dSdanielk1977 ** comments surrounding struct Trigger in sqliteInt.h for a description of
18633ed08dSdanielk1977 ** how triggers are stored.
19c3f9bad2Sdanielk1977 */
209adf9ac4Sdrh void sqliteCreateTrigger(
21c3f9bad2Sdanielk1977   Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
22633ed08dSdanielk1977   Token *pName,       /* The name of the trigger */
23*d702fccbSdanielk1977   int tr_tm,          /* One of TK_BEFORE, TK_AFTER , TK_INSTEAD */
24c3f9bad2Sdanielk1977   int op,             /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
25633ed08dSdanielk1977   IdList *pColumns,   /* column list if this is an UPDATE OF trigger */
26633ed08dSdanielk1977   Token *pTableName,  /* The name of the table/view the trigger applies to */
27c3f9bad2Sdanielk1977   int foreach,        /* One of TK_ROW or TK_STATEMENT */
28c3f9bad2Sdanielk1977   Expr *pWhen,        /* WHEN clause */
29633ed08dSdanielk1977   TriggerStep *pStepList, /* The triggered program */
30633ed08dSdanielk1977   char const *zData,  /* The string data to make persistent */
31633ed08dSdanielk1977   int zDataLen
329adf9ac4Sdrh ){
33c3f9bad2Sdanielk1977   Trigger *nt;
34c3f9bad2Sdanielk1977   Table   *tab;
35c3f9bad2Sdanielk1977   int offset;
36c3f9bad2Sdanielk1977   TriggerStep *ss;
37c3f9bad2Sdanielk1977 
38c3f9bad2Sdanielk1977   /* Check that:
399adf9ac4Sdrh   ** 1. the trigger name does not already exist.
409adf9ac4Sdrh   ** 2. the table (or view) does exist.
41*d702fccbSdanielk1977   ** 3. that we are not trying to create a trigger on the sqlite_master table
42*d702fccbSdanielk1977   ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
43*d702fccbSdanielk1977   ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
44c3f9bad2Sdanielk1977   */
45c3f9bad2Sdanielk1977   {
46633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pName->z, pName->n);
47633ed08dSdanielk1977     if( sqliteHashFind(&(pParse->db->trigHash), tmp_str, pName->n + 1) ){
48c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "trigger ", -1,
49633ed08dSdanielk1977           pName->z, pName->n, " already exists", -1, 0);
50c3f9bad2Sdanielk1977       sqliteFree(tmp_str);
51c3f9bad2Sdanielk1977       pParse->nErr++;
52c3f9bad2Sdanielk1977       goto trigger_cleanup;
53c3f9bad2Sdanielk1977     }
54c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
55c3f9bad2Sdanielk1977   }
56c3f9bad2Sdanielk1977   {
57633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pTableName->z, pTableName->n);
58e4697f5eSdrh     if( tmp_str==0 ) goto trigger_cleanup;
59c3f9bad2Sdanielk1977     tab = sqliteFindTable(pParse->db, tmp_str);
60c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
61c3f9bad2Sdanielk1977     if( !tab ){
62c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1,
63633ed08dSdanielk1977           pTableName->z, pTableName->n, 0);
64c3f9bad2Sdanielk1977       pParse->nErr++;
65c3f9bad2Sdanielk1977       goto trigger_cleanup;
66c3f9bad2Sdanielk1977     }
671873cd50Sdrh     if( sqliteStrICmp(tab->zName, MASTER_NAME)==0 ){
681873cd50Sdrh       sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
691873cd50Sdrh          "table: " MASTER_NAME, 0);
701873cd50Sdrh       pParse->nErr++;
711873cd50Sdrh       goto trigger_cleanup;
721873cd50Sdrh     }
73*d702fccbSdanielk1977     if( tab->pSelect && tr_tm != TK_INSTEAD ){
74*d702fccbSdanielk1977       sqliteSetNString(&pParse->zErrMsg, "cannot create ", -1,
75*d702fccbSdanielk1977 	  (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", -1, " trigger on view: ", -1
76*d702fccbSdanielk1977           , pTableName->z, pTableName->n, 0);
77*d702fccbSdanielk1977       goto trigger_cleanup;
78*d702fccbSdanielk1977     }
79*d702fccbSdanielk1977     if( !tab->pSelect && tr_tm == TK_INSTEAD ){
80*d702fccbSdanielk1977       sqliteSetNString(&pParse->zErrMsg, "cannot create INSTEAD OF", -1,
81*d702fccbSdanielk1977 	  " trigger on table: ", -1, pTableName->z, pTableName->n, 0);
82*d702fccbSdanielk1977       goto trigger_cleanup;
83*d702fccbSdanielk1977     }
84*d702fccbSdanielk1977   }
85*d702fccbSdanielk1977 
86*d702fccbSdanielk1977   if (tr_tm == TK_INSTEAD){
87*d702fccbSdanielk1977     tr_tm = TK_BEFORE;
88c3f9bad2Sdanielk1977   }
89c3f9bad2Sdanielk1977 
90c3f9bad2Sdanielk1977   /* Build the Trigger object */
91c3f9bad2Sdanielk1977   nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
92e4697f5eSdrh   if( nt==0 ) goto trigger_cleanup;
93633ed08dSdanielk1977   nt->name = sqliteStrNDup(pName->z, pName->n);
94633ed08dSdanielk1977   nt->table = sqliteStrNDup(pTableName->z, pTableName->n);
95e4697f5eSdrh   nt->strings = sqliteStrNDup(zData, zDataLen);
96e4697f5eSdrh   if( sqlite_malloc_failed ) goto trigger_cleanup;
97c3f9bad2Sdanielk1977   nt->op = op;
98c3f9bad2Sdanielk1977   nt->tr_tm = tr_tm;
99c3f9bad2Sdanielk1977   nt->pWhen = pWhen;
100633ed08dSdanielk1977   nt->pColumns = pColumns;
101c3f9bad2Sdanielk1977   nt->foreach = foreach;
102633ed08dSdanielk1977   nt->step_list = pStepList;
103c3f9bad2Sdanielk1977   nt->isCommit = 0;
104633ed08dSdanielk1977   offset = (int)(nt->strings - zData);
105c3f9bad2Sdanielk1977   sqliteExprMoveStrings(nt->pWhen, offset);
106c3f9bad2Sdanielk1977 
107c3f9bad2Sdanielk1977   ss = nt->step_list;
108c3f9bad2Sdanielk1977   while( ss ){
109c3f9bad2Sdanielk1977     sqliteSelectMoveStrings(ss->pSelect, offset);
110f29ce559Sdanielk1977     if( ss->target.z ){
111f29ce559Sdanielk1977       ss->target.z += offset;
112f29ce559Sdanielk1977     }
113c3f9bad2Sdanielk1977     sqliteExprMoveStrings(ss->pWhere, offset);
114c3f9bad2Sdanielk1977     sqliteExprListMoveStrings(ss->pExprList, offset);
115c3f9bad2Sdanielk1977 
116c3f9bad2Sdanielk1977     ss = ss->pNext;
117c3f9bad2Sdanielk1977   }
118c3f9bad2Sdanielk1977 
119c3f9bad2Sdanielk1977   /* if we are not initializing, and this trigger is not on a TEMP table,
1209adf9ac4Sdrh   ** build the sqlite_master entry
1219adf9ac4Sdrh   */
122c3f9bad2Sdanielk1977   if( !pParse->initFlag && !tab->isTemp ){
123c977f7f5Sdrh     static VdbeOp insertTrig[] = {
124c977f7f5Sdrh       { OP_OpenWrite,  0, 2,  MASTER_NAME},
125c977f7f5Sdrh       { OP_NewRecno,   0, 0,  0          },
126c977f7f5Sdrh       { OP_String,     0, 0,  "trigger"  },
127c977f7f5Sdrh       { OP_String,     0, 0,  0          },  /* 3: trigger name */
128c977f7f5Sdrh       { OP_String,     0, 0,  0          },  /* 4: table name */
129c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },
130c977f7f5Sdrh       { OP_String,     0, 0,  0          },  /* 6: SQL */
131c977f7f5Sdrh       { OP_MakeRecord, 5, 0,  0          },
132c977f7f5Sdrh       { OP_PutIntKey,  0, 0,  0          },
133c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },  /* 9: Next cookie */
134c977f7f5Sdrh       { OP_SetCookie,  0, 0,  0          },
135c977f7f5Sdrh       { OP_Close,      0, 0,  0          },
136c977f7f5Sdrh     };
137c977f7f5Sdrh     int addr;
138c977f7f5Sdrh     Vdbe *v;
139c3f9bad2Sdanielk1977 
140c3f9bad2Sdanielk1977     /* Make an entry in the sqlite_master table */
141c977f7f5Sdrh     v = sqliteGetVdbe(pParse);
142e4697f5eSdrh     if( v==0 ) goto trigger_cleanup;
143c977f7f5Sdrh     sqliteBeginWriteOperation(pParse, 0);
144c977f7f5Sdrh     addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
145c977f7f5Sdrh     sqliteVdbeChangeP3(v, addr+3, nt->name, 0);
146c977f7f5Sdrh     sqliteVdbeChangeP3(v, addr+4, nt->table, 0);
147c977f7f5Sdrh     sqliteVdbeChangeP3(v, addr+6, nt->strings, 0);
148dc379456Sdrh     sqliteChangeCookie(pParse->db);
149c977f7f5Sdrh     sqliteVdbeChangeP1(v, addr+9, pParse->db->next_cookie);
150c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
151c3f9bad2Sdanielk1977   }
152c3f9bad2Sdanielk1977 
153c3f9bad2Sdanielk1977   if( !pParse->explain ){
154c3f9bad2Sdanielk1977     /* Stick it in the hash-table */
155633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigHash), nt->name, pName->n + 1, nt);
156c3f9bad2Sdanielk1977 
157c3f9bad2Sdanielk1977     /* Attach it to the table object */
158c3f9bad2Sdanielk1977     nt->pNext = tab->pTrigger;
159c3f9bad2Sdanielk1977     tab->pTrigger = nt;
160c3f9bad2Sdanielk1977     return;
161c3f9bad2Sdanielk1977   }else{
162c3f9bad2Sdanielk1977     sqliteFree(nt->strings);
163c3f9bad2Sdanielk1977     sqliteFree(nt->name);
164c3f9bad2Sdanielk1977     sqliteFree(nt->table);
165c3f9bad2Sdanielk1977     sqliteFree(nt);
166c3f9bad2Sdanielk1977   }
167c3f9bad2Sdanielk1977 
168c3f9bad2Sdanielk1977 trigger_cleanup:
169c3f9bad2Sdanielk1977 
170633ed08dSdanielk1977   sqliteIdListDelete(pColumns);
171c3f9bad2Sdanielk1977   sqliteExprDelete(pWhen);
172c3f9bad2Sdanielk1977   {
173c3f9bad2Sdanielk1977     TriggerStep * pp;
174c3f9bad2Sdanielk1977     TriggerStep * nn;
175c3f9bad2Sdanielk1977 
176633ed08dSdanielk1977     pp = pStepList;
177c3f9bad2Sdanielk1977     while( pp ){
178c3f9bad2Sdanielk1977       nn = pp->pNext;
179c3f9bad2Sdanielk1977       sqliteExprDelete(pp->pWhere);
180c3f9bad2Sdanielk1977       sqliteExprListDelete(pp->pExprList);
181c3f9bad2Sdanielk1977       sqliteSelectDelete(pp->pSelect);
182c3f9bad2Sdanielk1977       sqliteIdListDelete(pp->pIdList);
183c3f9bad2Sdanielk1977       sqliteFree(pp);
184c3f9bad2Sdanielk1977       pp = nn;
185c3f9bad2Sdanielk1977     }
186c3f9bad2Sdanielk1977   }
187c3f9bad2Sdanielk1977 }
188c3f9bad2Sdanielk1977 
189c977f7f5Sdrh /*
190c977f7f5Sdrh ** Turn a SELECT statement (that the pSelect parameter points to) into
191c977f7f5Sdrh ** a trigger step.  Return a pointer to a TriggerStep structure.
192c977f7f5Sdrh **
193c977f7f5Sdrh ** The parser calls this routine when it finds a SELECT statement in
194c977f7f5Sdrh ** body of a TRIGGER.
195c977f7f5Sdrh */
196c977f7f5Sdrh TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
197633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
198e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
199c3f9bad2Sdanielk1977 
200633ed08dSdanielk1977   pTriggerStep->op = TK_SELECT;
201633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
202633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
203c3f9bad2Sdanielk1977 
204633ed08dSdanielk1977   return pTriggerStep;
205c3f9bad2Sdanielk1977 }
206c3f9bad2Sdanielk1977 
207c977f7f5Sdrh /*
208c977f7f5Sdrh ** Build a trigger step out of an INSERT statement.  Return a pointer
209c977f7f5Sdrh ** to the new trigger step.
210c977f7f5Sdrh **
211c977f7f5Sdrh ** The parser calls this routine when it sees an INSERT inside the
212c977f7f5Sdrh ** body of a trigger.
213c977f7f5Sdrh */
214633ed08dSdanielk1977 TriggerStep *sqliteTriggerInsertStep(
215c977f7f5Sdrh   Token *pTableName,  /* Name of the table into which we insert */
216c977f7f5Sdrh   IdList *pColumn,    /* List of columns in pTableName to insert into */
217c977f7f5Sdrh   ExprList *pEList,   /* The VALUE clause: a list of values to be inserted */
218c977f7f5Sdrh   Select *pSelect,    /* A SELECT statement that supplies values */
219c977f7f5Sdrh   int orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
220633ed08dSdanielk1977 ){
221633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
222e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
223c3f9bad2Sdanielk1977 
224633ed08dSdanielk1977   assert(pEList == 0 || pSelect == 0);
225633ed08dSdanielk1977   assert(pEList != 0 || pSelect != 0);
226c3f9bad2Sdanielk1977 
227633ed08dSdanielk1977   pTriggerStep->op = TK_INSERT;
228633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
229633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
230633ed08dSdanielk1977   pTriggerStep->pIdList = pColumn;
231633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
232633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
233c3f9bad2Sdanielk1977 
234633ed08dSdanielk1977   return pTriggerStep;
235c3f9bad2Sdanielk1977 }
236c3f9bad2Sdanielk1977 
237c977f7f5Sdrh /*
238c977f7f5Sdrh ** Construct a trigger step that implements an UPDATE statement and return
239c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
240c977f7f5Sdrh ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
241c977f7f5Sdrh */
242633ed08dSdanielk1977 TriggerStep *sqliteTriggerUpdateStep(
243c977f7f5Sdrh   Token *pTableName,   /* Name of the table to be updated */
244c977f7f5Sdrh   ExprList *pEList,    /* The SET clause: list of column and new values */
245c977f7f5Sdrh   Expr *pWhere,        /* The WHERE clause */
246c977f7f5Sdrh   int orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
247c977f7f5Sdrh ){
248633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
249e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
250c3f9bad2Sdanielk1977 
251633ed08dSdanielk1977   pTriggerStep->op = TK_UPDATE;
252633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
253633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
254633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
255633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
256c3f9bad2Sdanielk1977 
257633ed08dSdanielk1977   return pTriggerStep;
258c3f9bad2Sdanielk1977 }
259c3f9bad2Sdanielk1977 
260c977f7f5Sdrh /*
261c977f7f5Sdrh ** Construct a trigger step that implements a DELETE statement and return
262c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
263c977f7f5Sdrh ** sees a DELETE statement inside the body of a CREATE TRIGGER.
264c977f7f5Sdrh */
265c977f7f5Sdrh TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
266633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
267e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
268c3f9bad2Sdanielk1977 
269633ed08dSdanielk1977   pTriggerStep->op = TK_DELETE;
270633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
271633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
272633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
273c3f9bad2Sdanielk1977 
274633ed08dSdanielk1977   return pTriggerStep;
275c3f9bad2Sdanielk1977 }
276c3f9bad2Sdanielk1977 
277c3f9bad2Sdanielk1977 /*
278633ed08dSdanielk1977 ** Recursively delete a Trigger structure
279c3f9bad2Sdanielk1977 */
2801d1f3055Sdrh void sqliteDeleteTrigger(Trigger *pTrigger){
281633ed08dSdanielk1977   TriggerStep *pTriggerStep;
282c3f9bad2Sdanielk1977 
283633ed08dSdanielk1977   pTriggerStep = pTrigger->step_list;
284633ed08dSdanielk1977   while( pTriggerStep ){
285633ed08dSdanielk1977     TriggerStep * pTmp = pTriggerStep;
286633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
287633ed08dSdanielk1977 
288633ed08dSdanielk1977     sqliteExprDelete(pTmp->pWhere);
289633ed08dSdanielk1977     sqliteExprListDelete(pTmp->pExprList);
290633ed08dSdanielk1977     sqliteSelectDelete(pTmp->pSelect);
291633ed08dSdanielk1977     sqliteIdListDelete(pTmp->pIdList);
292633ed08dSdanielk1977 
293633ed08dSdanielk1977     sqliteFree(pTmp);
294633ed08dSdanielk1977   }
295633ed08dSdanielk1977 
296633ed08dSdanielk1977   sqliteFree(pTrigger->name);
297633ed08dSdanielk1977   sqliteFree(pTrigger->table);
298633ed08dSdanielk1977   sqliteExprDelete(pTrigger->pWhen);
299633ed08dSdanielk1977   sqliteIdListDelete(pTrigger->pColumns);
300633ed08dSdanielk1977   sqliteFree(pTrigger->strings);
301633ed08dSdanielk1977   sqliteFree(pTrigger);
302633ed08dSdanielk1977 }
303633ed08dSdanielk1977 
304633ed08dSdanielk1977 /*
305633ed08dSdanielk1977  * This function is called to drop a trigger from the database schema.
306633ed08dSdanielk1977  *
307633ed08dSdanielk1977  * This may be called directly from the parser, or from within
308633ed08dSdanielk1977  * sqliteDropTable(). In the latter case the "nested" argument is true.
309633ed08dSdanielk1977  *
310633ed08dSdanielk1977  * Note that this function does not delete the trigger entirely. Instead it
311633ed08dSdanielk1977  * removes it from the internal schema and places it in the trigDrop hash
312633ed08dSdanielk1977  * table. This is so that the trigger can be restored into the database schema
313633ed08dSdanielk1977  * if the transaction is rolled back.
314633ed08dSdanielk1977  */
315633ed08dSdanielk1977 void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
316633ed08dSdanielk1977 {
317633ed08dSdanielk1977   char *zName;
318633ed08dSdanielk1977   Trigger *pTrigger;
319633ed08dSdanielk1977   Table   *pTable;
320633ed08dSdanielk1977 
321633ed08dSdanielk1977   zName = sqliteStrNDup(pName->z, pName->n);
322c3f9bad2Sdanielk1977 
323c3f9bad2Sdanielk1977   /* ensure that the trigger being dropped exists */
324633ed08dSdanielk1977   pTrigger = sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1);
325633ed08dSdanielk1977   if( !pTrigger ){
326c3f9bad2Sdanielk1977     sqliteSetNString(&pParse->zErrMsg, "no such trigger: ", -1,
327633ed08dSdanielk1977         zName, -1, 0);
328633ed08dSdanielk1977     sqliteFree(zName);
329c3f9bad2Sdanielk1977     return;
330c3f9bad2Sdanielk1977   }
331c3f9bad2Sdanielk1977 
332c3f9bad2Sdanielk1977   /*
333c3f9bad2Sdanielk1977    * If this is not an "explain", do the following:
334c3f9bad2Sdanielk1977    * 1. Remove the trigger from its associated table structure
335c3f9bad2Sdanielk1977    * 2. Move the trigger from the trigHash hash to trigDrop
336c3f9bad2Sdanielk1977    */
337c3f9bad2Sdanielk1977   if( !pParse->explain ){
338c3f9bad2Sdanielk1977     /* 1 */
339633ed08dSdanielk1977     pTable = sqliteFindTable(pParse->db, pTrigger->table);
340633ed08dSdanielk1977     assert(pTable);
341633ed08dSdanielk1977     if( pTable->pTrigger == pTrigger ){
342633ed08dSdanielk1977       pTable->pTrigger = pTrigger->pNext;
343633ed08dSdanielk1977     }else{
344633ed08dSdanielk1977       Trigger *cc = pTable->pTrigger;
345c3f9bad2Sdanielk1977       while( cc ){
346633ed08dSdanielk1977         if( cc->pNext == pTrigger ){
347c3f9bad2Sdanielk1977           cc->pNext = cc->pNext->pNext;
348c3f9bad2Sdanielk1977           break;
349c3f9bad2Sdanielk1977         }
350c3f9bad2Sdanielk1977         cc = cc->pNext;
351c3f9bad2Sdanielk1977       }
352c3f9bad2Sdanielk1977       assert(cc);
353c3f9bad2Sdanielk1977     }
354c3f9bad2Sdanielk1977 
355c3f9bad2Sdanielk1977     /* 2 */
356633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigHash), zName,
357633ed08dSdanielk1977         pName->n + 1, NULL);
358633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigDrop), pTrigger->name,
359633ed08dSdanielk1977         pName->n + 1, pTrigger);
360c3f9bad2Sdanielk1977   }
361c3f9bad2Sdanielk1977 
362c3f9bad2Sdanielk1977   /* Unless this is a trigger on a TEMP TABLE, generate code to destroy the
363c3f9bad2Sdanielk1977    * database record of the trigger */
364633ed08dSdanielk1977   if( !pTable->isTemp ){
365c3f9bad2Sdanielk1977     int base;
366c3f9bad2Sdanielk1977     static VdbeOp dropTrigger[] = {
367c3f9bad2Sdanielk1977       { OP_OpenWrite,  0, 2,        MASTER_NAME},
368c3f9bad2Sdanielk1977       { OP_Rewind,     0, ADDR(9),  0},
369c3f9bad2Sdanielk1977       { OP_String,     0, 0,        0}, /* 2 */
370c3f9bad2Sdanielk1977       { OP_MemStore,   1, 1,        0},
371c3f9bad2Sdanielk1977       { OP_MemLoad,    1, 0,        0}, /* 4 */
372c3f9bad2Sdanielk1977       { OP_Column,     0, 1,        0},
373c3f9bad2Sdanielk1977       { OP_Ne,         0, ADDR(8),  0},
374c3f9bad2Sdanielk1977       { OP_Delete,     0, 0,        0},
375c3f9bad2Sdanielk1977       { OP_Next,       0, ADDR(4),  0}, /* 8 */
376c3f9bad2Sdanielk1977       { OP_Integer,    0, 0,        0}, /* 9 */
377c3f9bad2Sdanielk1977       { OP_SetCookie,  0, 0,        0},
378c3f9bad2Sdanielk1977       { OP_Close,      0, 0,        0},
379c3f9bad2Sdanielk1977     };
380c3f9bad2Sdanielk1977 
381dc379456Sdrh     if( !nested ){
382c977f7f5Sdrh       sqliteBeginWriteOperation(pParse, 0);
383dc379456Sdrh     }
384c3f9bad2Sdanielk1977     base = sqliteVdbeAddOpList(pParse->pVdbe,
385c3f9bad2Sdanielk1977         ArraySize(dropTrigger), dropTrigger);
386633ed08dSdanielk1977     sqliteVdbeChangeP3(pParse->pVdbe, base+2, zName, 0);
387dc379456Sdrh     if( !nested ){
388dc379456Sdrh       sqliteChangeCookie(pParse->db);
389dc379456Sdrh     }
390c3f9bad2Sdanielk1977     sqliteVdbeChangeP1(pParse->pVdbe, base+9, pParse->db->next_cookie);
391dc379456Sdrh     if( !nested ){
392c3f9bad2Sdanielk1977       sqliteEndWriteOperation(pParse);
393c3f9bad2Sdanielk1977     }
394dc379456Sdrh   }
395c3f9bad2Sdanielk1977 
396633ed08dSdanielk1977   sqliteFree(zName);
397c3f9bad2Sdanielk1977 }
398c3f9bad2Sdanielk1977 
399c977f7f5Sdrh /*
400c977f7f5Sdrh ** pEList is the SET clause of an UPDATE statement.  Each entry
401c977f7f5Sdrh ** in pEList is of the format <id>=<expr>.  If any of the entries
402c977f7f5Sdrh ** in pEList have an <id> which matches an identifier in pIdList,
403c977f7f5Sdrh ** then return TRUE.  If pIdList==NULL, then it is considered a
404c977f7f5Sdrh ** wildcard that matches anything.  Likewise if pEList==NULL then
405c977f7f5Sdrh ** it matches anything so always return true.  Return false only
406c977f7f5Sdrh ** if there is no match.
407c977f7f5Sdrh */
408c977f7f5Sdrh static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
409ad2d8307Sdrh   int e;
410ad2d8307Sdrh   if( !pIdList || !pEList ) return 1;
411f29ce559Sdanielk1977   for(e=0; e<pEList->nExpr; e++){
412ad2d8307Sdrh     if( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
413f29ce559Sdanielk1977   }
414c3f9bad2Sdanielk1977   return 0;
415c3f9bad2Sdanielk1977 }
416c3f9bad2Sdanielk1977 
417c3f9bad2Sdanielk1977 /* A global variable that is TRUE if we should always set up temp tables for
418c3f9bad2Sdanielk1977  * for triggers, even if there are no triggers to code. This is used to test
419c3f9bad2Sdanielk1977  * how much overhead the triggers algorithm is causing.
420c3f9bad2Sdanielk1977  *
421c3f9bad2Sdanielk1977  * This flag can be set or cleared using the "trigger_overhead_test" pragma.
422c3f9bad2Sdanielk1977  * The pragma is not documented since it is not really part of the interface
423c3f9bad2Sdanielk1977  * to SQLite, just the test procedure.
424c3f9bad2Sdanielk1977 */
425c3f9bad2Sdanielk1977 int always_code_trigger_setup = 0;
426c3f9bad2Sdanielk1977 
427c3f9bad2Sdanielk1977 /*
428c3f9bad2Sdanielk1977  * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
429c3f9bad2Sdanielk1977  * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
430c3f9bad2Sdanielk1977  * found in the list specified as pTrigger.
431c3f9bad2Sdanielk1977  */
432c3f9bad2Sdanielk1977 int sqliteTriggersExist(
433c977f7f5Sdrh   Parse *pParse,          /* Used to check for recursive triggers */
434c977f7f5Sdrh   Trigger *pTrigger,      /* A list of triggers associated with a table */
435c3f9bad2Sdanielk1977   int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
436c3f9bad2Sdanielk1977   int tr_tm,              /* one of TK_BEFORE, TK_AFTER */
437c3f9bad2Sdanielk1977   int foreach,            /* one of TK_ROW or TK_STATEMENT */
438c977f7f5Sdrh   ExprList *pChanges      /* Columns that change in an UPDATE statement */
439c977f7f5Sdrh ){
440633ed08dSdanielk1977   Trigger * pTriggerCursor;
441c3f9bad2Sdanielk1977 
442633ed08dSdanielk1977   if( always_code_trigger_setup ){
443633ed08dSdanielk1977     return 1;
444633ed08dSdanielk1977   }
445c3f9bad2Sdanielk1977 
446633ed08dSdanielk1977   pTriggerCursor = pTrigger;
447633ed08dSdanielk1977   while( pTriggerCursor ){
448633ed08dSdanielk1977     if( pTriggerCursor->op == op &&
449633ed08dSdanielk1977 	pTriggerCursor->tr_tm == tr_tm &&
450633ed08dSdanielk1977 	pTriggerCursor->foreach == foreach &&
451633ed08dSdanielk1977 	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
452c3f9bad2Sdanielk1977       TriggerStack * ss;
453c3f9bad2Sdanielk1977       ss = pParse->trigStack;
454f29ce559Sdanielk1977       while( ss && ss->pTrigger != pTrigger ){
455f29ce559Sdanielk1977 	ss = ss->pNext;
456f29ce559Sdanielk1977       }
457c3f9bad2Sdanielk1977       if( !ss )return 1;
458c3f9bad2Sdanielk1977     }
459633ed08dSdanielk1977     pTriggerCursor = pTriggerCursor->pNext;
460c3f9bad2Sdanielk1977   }
461c3f9bad2Sdanielk1977 
462c3f9bad2Sdanielk1977   return 0;
463c3f9bad2Sdanielk1977 }
464c3f9bad2Sdanielk1977 
465c977f7f5Sdrh /*
466c977f7f5Sdrh ** Generate VDBE code for zero or more statements inside the body of a
467c977f7f5Sdrh ** trigger.
468c977f7f5Sdrh */
469c3f9bad2Sdanielk1977 static int codeTriggerProgram(
470c977f7f5Sdrh   Parse *pParse,            /* The parser context */
471c977f7f5Sdrh   TriggerStep *pStepList,   /* List of statements inside the trigger body */
472c977f7f5Sdrh   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */
473633ed08dSdanielk1977 ){
474633ed08dSdanielk1977   TriggerStep * pTriggerStep = pStepList;
475c3f9bad2Sdanielk1977   int orconf;
476c3f9bad2Sdanielk1977 
477633ed08dSdanielk1977   while( pTriggerStep ){
478c3f9bad2Sdanielk1977     int saveNTab = pParse->nTab;
479633ed08dSdanielk1977     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
480c3f9bad2Sdanielk1977     pParse->trigStack->orconf = orconf;
481633ed08dSdanielk1977     switch( pTriggerStep->op ){
482c3f9bad2Sdanielk1977       case TK_SELECT: {
483c3f9bad2Sdanielk1977         int tmp_tbl = pParse->nTab++;
484c3f9bad2Sdanielk1977 	sqliteVdbeAddOp(pParse->pVdbe, OP_OpenTemp, tmp_tbl, 0);
485c3f9bad2Sdanielk1977 	sqliteVdbeAddOp(pParse->pVdbe, OP_KeyAsData, tmp_tbl, 1);
486633ed08dSdanielk1977 	sqliteSelect(pParse, pTriggerStep->pSelect, SRT_Union,
487633ed08dSdanielk1977 	    tmp_tbl, 0, 0, 0);
488c3f9bad2Sdanielk1977 	sqliteVdbeAddOp(pParse->pVdbe, OP_Close, tmp_tbl, 0);
489c3f9bad2Sdanielk1977 	pParse->nTab--;
490c3f9bad2Sdanielk1977 	break;
491c3f9bad2Sdanielk1977       }
492c3f9bad2Sdanielk1977       case TK_UPDATE: {
493bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
494633ed08dSdanielk1977         sqliteUpdate(pParse, &pTriggerStep->target,
495633ed08dSdanielk1977         sqliteExprListDup(pTriggerStep->pExprList),
496633ed08dSdanielk1977         sqliteExprDup(pTriggerStep->pWhere), orconf);
497bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
498c3f9bad2Sdanielk1977         break;
499c3f9bad2Sdanielk1977       }
500c3f9bad2Sdanielk1977       case TK_INSERT: {
501633ed08dSdanielk1977         sqliteInsert(pParse, &pTriggerStep->target,
502633ed08dSdanielk1977         sqliteExprListDup(pTriggerStep->pExprList),
503633ed08dSdanielk1977         sqliteSelectDup(pTriggerStep->pSelect),
504633ed08dSdanielk1977         sqliteIdListDup(pTriggerStep->pIdList), orconf);
505c3f9bad2Sdanielk1977         break;
506c3f9bad2Sdanielk1977       }
507c3f9bad2Sdanielk1977       case TK_DELETE: {
508bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
509633ed08dSdanielk1977         sqliteDeleteFrom(pParse, &pTriggerStep->target,
510633ed08dSdanielk1977 	    sqliteExprDup(pTriggerStep->pWhere));
511bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
512c3f9bad2Sdanielk1977         break;
513c3f9bad2Sdanielk1977       }
514c3f9bad2Sdanielk1977       default:
515c3f9bad2Sdanielk1977         assert(0);
516c3f9bad2Sdanielk1977     }
517c3f9bad2Sdanielk1977     pParse->nTab = saveNTab;
518633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
519c3f9bad2Sdanielk1977   }
520c3f9bad2Sdanielk1977 
521c3f9bad2Sdanielk1977   return 0;
522c3f9bad2Sdanielk1977 }
523c3f9bad2Sdanielk1977 
524633ed08dSdanielk1977 /*
525633ed08dSdanielk1977 ** This is called to code FOR EACH ROW triggers.
526633ed08dSdanielk1977 **
527633ed08dSdanielk1977 ** When the code that this function generates is executed, the following
528633ed08dSdanielk1977 ** must be true:
529c977f7f5Sdrh **
530c977f7f5Sdrh ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
531c977f7f5Sdrh **    can be indices of cursors in temporary tables.  See below.)
532c977f7f5Sdrh **
533633ed08dSdanielk1977 ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
534633ed08dSdanielk1977 **    a temporary vdbe cursor (index newIdx) must be open and pointing at
535633ed08dSdanielk1977 **    a row containing values to be substituted for new.* expressions in the
536633ed08dSdanielk1977 **    trigger program(s).
537c977f7f5Sdrh **
538633ed08dSdanielk1977 ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
539633ed08dSdanielk1977 **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
540633ed08dSdanielk1977 **    a row containing values to be substituted for old.* expressions in the
541633ed08dSdanielk1977 **    trigger program(s).
542633ed08dSdanielk1977 **
543633ed08dSdanielk1977 */
544c3f9bad2Sdanielk1977 int sqliteCodeRowTrigger(
545c3f9bad2Sdanielk1977   Parse *pParse,       /* Parse context */
546c3f9bad2Sdanielk1977   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
547633ed08dSdanielk1977   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
548c3f9bad2Sdanielk1977   int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
549633ed08dSdanielk1977   Table *pTab,         /* The table to code triggers from */
550633ed08dSdanielk1977   int newIdx,          /* The indice of the "new" row to access */
551633ed08dSdanielk1977   int oldIdx,          /* The indice of the "old" row to access */
552c977f7f5Sdrh   int orconf           /* ON CONFLICT policy */
553c977f7f5Sdrh ){
554c3f9bad2Sdanielk1977   Trigger * pTrigger;
555c3f9bad2Sdanielk1977   TriggerStack * pTriggerStack;
556c3f9bad2Sdanielk1977 
557c3f9bad2Sdanielk1977   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
558c3f9bad2Sdanielk1977   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
559c3f9bad2Sdanielk1977 
560633ed08dSdanielk1977   assert(newIdx != -1 || oldIdx != -1);
561c3f9bad2Sdanielk1977 
562633ed08dSdanielk1977   pTrigger = pTab->pTrigger;
563c3f9bad2Sdanielk1977   while( pTrigger ){
564c3f9bad2Sdanielk1977     int fire_this = 0;
565c3f9bad2Sdanielk1977 
566c3f9bad2Sdanielk1977     /* determine whether we should code this trigger */
567c3f9bad2Sdanielk1977     if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
568c3f9bad2Sdanielk1977         pTrigger->foreach == TK_ROW ){
569c3f9bad2Sdanielk1977       fire_this = 1;
570c3f9bad2Sdanielk1977       pTriggerStack = pParse->trigStack;
571c3f9bad2Sdanielk1977       while( pTriggerStack ){
572f29ce559Sdanielk1977         if( pTriggerStack->pTrigger == pTrigger ){
573f29ce559Sdanielk1977 	  fire_this = 0;
574f29ce559Sdanielk1977 	}
575c3f9bad2Sdanielk1977         pTriggerStack = pTriggerStack->pNext;
576c3f9bad2Sdanielk1977       }
577c3f9bad2Sdanielk1977       if( op == TK_UPDATE && pTrigger->pColumns &&
578f29ce559Sdanielk1977           !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
579c3f9bad2Sdanielk1977         fire_this = 0;
580c3f9bad2Sdanielk1977       }
581f29ce559Sdanielk1977     }
582c3f9bad2Sdanielk1977 
583e4697f5eSdrh     if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
584c3f9bad2Sdanielk1977       int endTrigger;
585ad3cab52Sdrh       SrcList dummyTablist;
586c3f9bad2Sdanielk1977       Expr * whenExpr;
587c3f9bad2Sdanielk1977 
588ad3cab52Sdrh       dummyTablist.nSrc = 0;
589c3f9bad2Sdanielk1977       dummyTablist.a = 0;
590c3f9bad2Sdanielk1977 
591c3f9bad2Sdanielk1977       /* Push an entry on to the trigger stack */
592c3f9bad2Sdanielk1977       pTriggerStack->pTrigger = pTrigger;
593633ed08dSdanielk1977       pTriggerStack->newIdx = newIdx;
594633ed08dSdanielk1977       pTriggerStack->oldIdx = oldIdx;
595633ed08dSdanielk1977       pTriggerStack->pTab = pTab;
596c3f9bad2Sdanielk1977       pTriggerStack->pNext = pParse->trigStack;
597c3f9bad2Sdanielk1977       pParse->trigStack = pTriggerStack;
598c3f9bad2Sdanielk1977 
599c3f9bad2Sdanielk1977       /* code the WHEN clause */
600c3f9bad2Sdanielk1977       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
601c3f9bad2Sdanielk1977       whenExpr = sqliteExprDup(pTrigger->pWhen);
602c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
603c3f9bad2Sdanielk1977         pParse->trigStack = pParse->trigStack->pNext;
604c3f9bad2Sdanielk1977         sqliteFree(pTriggerStack);
605c3f9bad2Sdanielk1977         sqliteExprDelete(whenExpr);
606c3f9bad2Sdanielk1977         return 1;
607c3f9bad2Sdanielk1977       }
608f5905aa7Sdrh       sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
609c3f9bad2Sdanielk1977       sqliteExprDelete(whenExpr);
610c3f9bad2Sdanielk1977 
611633ed08dSdanielk1977       codeTriggerProgram(pParse, pTrigger->step_list, orconf);
612c3f9bad2Sdanielk1977 
613c3f9bad2Sdanielk1977       /* Pop the entry off the trigger stack */
614c3f9bad2Sdanielk1977       pParse->trigStack = pParse->trigStack->pNext;
615c3f9bad2Sdanielk1977       sqliteFree(pTriggerStack);
616c3f9bad2Sdanielk1977 
617c3f9bad2Sdanielk1977       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
618c3f9bad2Sdanielk1977     }
619c3f9bad2Sdanielk1977     pTrigger = pTrigger->pNext;
620c3f9bad2Sdanielk1977   }
621c3f9bad2Sdanielk1977 
622c3f9bad2Sdanielk1977   return 0;
623c3f9bad2Sdanielk1977 }
624c3f9bad2Sdanielk1977 
625c3f9bad2Sdanielk1977 /*
626633ed08dSdanielk1977  * This function is called to code ON UPDATE and ON DELETE triggers on
627633ed08dSdanielk1977  * views.
628633ed08dSdanielk1977  *
629633ed08dSdanielk1977  * This function deletes the data pointed at by the pWhere and pChanges
630633ed08dSdanielk1977  * arguments before it completes.
631c3f9bad2Sdanielk1977  */
632633ed08dSdanielk1977 void sqliteViewTriggers(
633633ed08dSdanielk1977   Parse *pParse,
634633ed08dSdanielk1977   Table *pTab,         /* The view to code triggers on */
635633ed08dSdanielk1977   Expr *pWhere,        /* The WHERE clause of the statement causing triggers*/
636633ed08dSdanielk1977   int orconf,          /* The ON CONFLICT policy specified as part of the
637633ed08dSdanielk1977 			  statement causing these triggers */
638633ed08dSdanielk1977   ExprList *pChanges   /* If this is an statement causing triggers to fire
639633ed08dSdanielk1977 			  is an UPDATE, then this list holds the columns
640633ed08dSdanielk1977 			  to update and the expressions to update them to.
641633ed08dSdanielk1977 			  See comments for sqliteUpdate(). */
642633ed08dSdanielk1977 ){
643c3f9bad2Sdanielk1977   int oldIdx = -1;
644c3f9bad2Sdanielk1977   int newIdx = -1;
645c3f9bad2Sdanielk1977   int *aXRef = 0;
646c3f9bad2Sdanielk1977   Vdbe *v;
647c3f9bad2Sdanielk1977   int endOfLoop;
648c3f9bad2Sdanielk1977   int startOfLoop;
649c3f9bad2Sdanielk1977   Select theSelect;
650c3f9bad2Sdanielk1977   Token tblNameToken;
651c3f9bad2Sdanielk1977 
652c3f9bad2Sdanielk1977   assert(pTab->pSelect);
653c3f9bad2Sdanielk1977 
654c3f9bad2Sdanielk1977   tblNameToken.z = pTab->zName;
655c3f9bad2Sdanielk1977   tblNameToken.n = strlen(pTab->zName);
656c3f9bad2Sdanielk1977 
657c3f9bad2Sdanielk1977   theSelect.isDistinct = 0;
658c3f9bad2Sdanielk1977   theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
659ad3cab52Sdrh   theSelect.pSrc   = sqliteSrcListAppend(0, &tblNameToken);
660c3f9bad2Sdanielk1977   theSelect.pWhere = pWhere;    pWhere = 0;
661c3f9bad2Sdanielk1977   theSelect.pGroupBy = 0;
662c3f9bad2Sdanielk1977   theSelect.pHaving = 0;
663c3f9bad2Sdanielk1977   theSelect.pOrderBy = 0;
664c3f9bad2Sdanielk1977   theSelect.op = TK_SELECT; /* ?? */
665c3f9bad2Sdanielk1977   theSelect.pPrior = 0;
666c3f9bad2Sdanielk1977   theSelect.nLimit = -1;
667c3f9bad2Sdanielk1977   theSelect.nOffset = -1;
668c3f9bad2Sdanielk1977   theSelect.zSelect = 0;
669c3f9bad2Sdanielk1977   theSelect.base = 0;
670c3f9bad2Sdanielk1977 
671c3f9bad2Sdanielk1977   v = sqliteGetVdbe(pParse);
672c3f9bad2Sdanielk1977   assert(v);
673c977f7f5Sdrh   sqliteBeginWriteOperation(pParse, 1);
674c3f9bad2Sdanielk1977 
675c3f9bad2Sdanielk1977   /* Allocate temp tables */
676c3f9bad2Sdanielk1977   oldIdx = pParse->nTab++;
677c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
678c3f9bad2Sdanielk1977   if( pChanges ){
679c3f9bad2Sdanielk1977     newIdx = pParse->nTab++;
680c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
681c3f9bad2Sdanielk1977   }
682c3f9bad2Sdanielk1977 
683c3f9bad2Sdanielk1977   /* Snapshot the view */
684c3f9bad2Sdanielk1977   if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){
685c3f9bad2Sdanielk1977     goto trigger_cleanup;
686c3f9bad2Sdanielk1977   }
687c3f9bad2Sdanielk1977 
688c3f9bad2Sdanielk1977   /* loop thru the view snapshot, executing triggers for each row */
689c3f9bad2Sdanielk1977   endOfLoop = sqliteVdbeMakeLabel(v);
690c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);
691c3f9bad2Sdanielk1977 
692c3f9bad2Sdanielk1977   /* Loop thru the view snapshot, executing triggers for each row */
693c3f9bad2Sdanielk1977   startOfLoop = sqliteVdbeCurrentAddr(v);
694c3f9bad2Sdanielk1977 
695c3f9bad2Sdanielk1977   /* Build the updated row if required */
696c3f9bad2Sdanielk1977   if( pChanges ){
6971d1f3055Sdrh     int ii;
698c3f9bad2Sdanielk1977 
699c3f9bad2Sdanielk1977     aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
700c3f9bad2Sdanielk1977     if( aXRef==0 ) goto trigger_cleanup;
701633ed08dSdanielk1977     for(ii = 0; ii < pTab->nCol; ii++){
702c3f9bad2Sdanielk1977       aXRef[ii] = -1;
703633ed08dSdanielk1977     }
704c3f9bad2Sdanielk1977 
705c3f9bad2Sdanielk1977     for(ii=0; ii<pChanges->nExpr; ii++){
706c3f9bad2Sdanielk1977       int jj;
707c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0,
708f29ce559Sdanielk1977             pChanges->a[ii].pExpr) ){
709c3f9bad2Sdanielk1977         goto trigger_cleanup;
710f29ce559Sdanielk1977       }
711c3f9bad2Sdanielk1977 
712c3f9bad2Sdanielk1977       if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
713c3f9bad2Sdanielk1977         goto trigger_cleanup;
714c3f9bad2Sdanielk1977 
715c3f9bad2Sdanielk1977       for(jj=0; jj<pTab->nCol; jj++){
716c3f9bad2Sdanielk1977         if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
717c3f9bad2Sdanielk1977           aXRef[jj] = ii;
718c3f9bad2Sdanielk1977           break;
719c3f9bad2Sdanielk1977         }
720c3f9bad2Sdanielk1977       }
721c3f9bad2Sdanielk1977       if( jj>=pTab->nCol ){
722c3f9bad2Sdanielk1977         sqliteSetString(&pParse->zErrMsg, "no such column: ",
723c3f9bad2Sdanielk1977             pChanges->a[ii].zName, 0);
724c3f9bad2Sdanielk1977         pParse->nErr++;
725c3f9bad2Sdanielk1977         goto trigger_cleanup;
726c3f9bad2Sdanielk1977       }
727c3f9bad2Sdanielk1977     }
728c3f9bad2Sdanielk1977 
729c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Integer, 13, 0);
730c3f9bad2Sdanielk1977 
731633ed08dSdanielk1977     for(ii = 0; ii<pTab->nCol; ii++){
732633ed08dSdanielk1977       if( aXRef[ii] < 0 ){
733c3f9bad2Sdanielk1977         sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
734633ed08dSdanielk1977       }else{
735c3f9bad2Sdanielk1977         sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
736633ed08dSdanielk1977       }
737633ed08dSdanielk1977     }
738c3f9bad2Sdanielk1977 
739c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
740c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
741c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
742c3f9bad2Sdanielk1977 
743c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
744633ed08dSdanielk1977         pTab, newIdx, oldIdx, orconf);
745c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
746633ed08dSdanielk1977         pTab, newIdx, oldIdx, orconf);
747c3f9bad2Sdanielk1977   }else{
748c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
749633ed08dSdanielk1977         orconf);
750c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
751633ed08dSdanielk1977         orconf);
752c3f9bad2Sdanielk1977   }
753c3f9bad2Sdanielk1977 
754c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
755c3f9bad2Sdanielk1977 
756c3f9bad2Sdanielk1977   sqliteVdbeResolveLabel(v, endOfLoop);
757c3f9bad2Sdanielk1977   sqliteEndWriteOperation(pParse);
758c3f9bad2Sdanielk1977 
759c3f9bad2Sdanielk1977 trigger_cleanup:
760c3f9bad2Sdanielk1977   sqliteFree(aXRef);
761c3f9bad2Sdanielk1977   sqliteExprListDelete(pChanges);
762c3f9bad2Sdanielk1977   sqliteExprDelete(pWhere);
763c3f9bad2Sdanielk1977   sqliteExprListDelete(theSelect.pEList);
764ad3cab52Sdrh   sqliteSrcListDelete(theSelect.pSrc);
765c3f9bad2Sdanielk1977   sqliteExprDelete(theSelect.pWhere);
766c3f9bad2Sdanielk1977   return;
767c3f9bad2Sdanielk1977 }
768