xref: /sqlite-3.40.0/src/trigger.c (revision e4697f5e)
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 */
23c3f9bad2Sdanielk1977   int tr_tm,          /* One of TK_BEFORE, TK_AFTER */
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.
41c3f9bad2Sdanielk1977   */
42c3f9bad2Sdanielk1977   {
43633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pName->z, pName->n);
44633ed08dSdanielk1977     if( sqliteHashFind(&(pParse->db->trigHash), tmp_str, pName->n + 1) ){
45c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "trigger ", -1,
46633ed08dSdanielk1977           pName->z, pName->n, " already exists", -1, 0);
47c3f9bad2Sdanielk1977       sqliteFree(tmp_str);
48c3f9bad2Sdanielk1977       pParse->nErr++;
49c3f9bad2Sdanielk1977       goto trigger_cleanup;
50c3f9bad2Sdanielk1977     }
51c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
52c3f9bad2Sdanielk1977   }
53c3f9bad2Sdanielk1977   {
54633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pTableName->z, pTableName->n);
55*e4697f5eSdrh     if( tmp_str==0 ) goto trigger_cleanup;
56c3f9bad2Sdanielk1977     tab = sqliteFindTable(pParse->db, tmp_str);
57c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
58c3f9bad2Sdanielk1977     if( !tab ){
59c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1,
60633ed08dSdanielk1977           pTableName->z, pTableName->n, 0);
61c3f9bad2Sdanielk1977       pParse->nErr++;
62c3f9bad2Sdanielk1977       goto trigger_cleanup;
63c3f9bad2Sdanielk1977     }
641873cd50Sdrh     if( sqliteStrICmp(tab->zName, MASTER_NAME)==0 ){
651873cd50Sdrh       sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
661873cd50Sdrh          "table: " MASTER_NAME, 0);
671873cd50Sdrh       pParse->nErr++;
681873cd50Sdrh       goto trigger_cleanup;
691873cd50Sdrh     }
70c3f9bad2Sdanielk1977   }
71c3f9bad2Sdanielk1977 
72c3f9bad2Sdanielk1977   /* Build the Trigger object */
73c3f9bad2Sdanielk1977   nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
74*e4697f5eSdrh   if( nt==0 ) goto trigger_cleanup;
75633ed08dSdanielk1977   nt->name = sqliteStrNDup(pName->z, pName->n);
76633ed08dSdanielk1977   nt->table = sqliteStrNDup(pTableName->z, pTableName->n);
77*e4697f5eSdrh   nt->strings = sqliteStrNDup(zData, zDataLen);
78*e4697f5eSdrh   if( sqlite_malloc_failed ) goto trigger_cleanup;
79c3f9bad2Sdanielk1977   nt->op = op;
80c3f9bad2Sdanielk1977   nt->tr_tm = tr_tm;
81c3f9bad2Sdanielk1977   nt->pWhen = pWhen;
82633ed08dSdanielk1977   nt->pColumns = pColumns;
83c3f9bad2Sdanielk1977   nt->foreach = foreach;
84633ed08dSdanielk1977   nt->step_list = pStepList;
85c3f9bad2Sdanielk1977   nt->isCommit = 0;
86633ed08dSdanielk1977   offset = (int)(nt->strings - zData);
87c3f9bad2Sdanielk1977   sqliteExprMoveStrings(nt->pWhen, offset);
88c3f9bad2Sdanielk1977 
89c3f9bad2Sdanielk1977   ss = nt->step_list;
90c3f9bad2Sdanielk1977   while( ss ){
91c3f9bad2Sdanielk1977     sqliteSelectMoveStrings(ss->pSelect, offset);
92f29ce559Sdanielk1977     if( ss->target.z ){
93f29ce559Sdanielk1977       ss->target.z += offset;
94f29ce559Sdanielk1977     }
95c3f9bad2Sdanielk1977     sqliteExprMoveStrings(ss->pWhere, offset);
96c3f9bad2Sdanielk1977     sqliteExprListMoveStrings(ss->pExprList, offset);
97c3f9bad2Sdanielk1977 
98c3f9bad2Sdanielk1977     ss = ss->pNext;
99c3f9bad2Sdanielk1977   }
100c3f9bad2Sdanielk1977 
101c3f9bad2Sdanielk1977   /* if we are not initializing, and this trigger is not on a TEMP table,
1029adf9ac4Sdrh   ** build the sqlite_master entry
1039adf9ac4Sdrh   */
104c3f9bad2Sdanielk1977   if( !pParse->initFlag && !tab->isTemp ){
105c977f7f5Sdrh     static VdbeOp insertTrig[] = {
106c977f7f5Sdrh       { OP_OpenWrite,  0, 2,  MASTER_NAME},
107c977f7f5Sdrh       { OP_NewRecno,   0, 0,  0          },
108c977f7f5Sdrh       { OP_String,     0, 0,  "trigger"  },
109c977f7f5Sdrh       { OP_String,     0, 0,  0          },  /* 3: trigger name */
110c977f7f5Sdrh       { OP_String,     0, 0,  0          },  /* 4: table name */
111c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },
112c977f7f5Sdrh       { OP_String,     0, 0,  0          },  /* 6: SQL */
113c977f7f5Sdrh       { OP_MakeRecord, 5, 0,  0          },
114c977f7f5Sdrh       { OP_PutIntKey,  0, 0,  0          },
115c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },  /* 9: Next cookie */
116c977f7f5Sdrh       { OP_SetCookie,  0, 0,  0          },
117c977f7f5Sdrh       { OP_Close,      0, 0,  0          },
118c977f7f5Sdrh     };
119c977f7f5Sdrh     int addr;
120c977f7f5Sdrh     Vdbe *v;
121c3f9bad2Sdanielk1977 
122c3f9bad2Sdanielk1977     /* Make an entry in the sqlite_master table */
123c977f7f5Sdrh     v = sqliteGetVdbe(pParse);
124*e4697f5eSdrh     if( v==0 ) goto trigger_cleanup;
125c977f7f5Sdrh     sqliteBeginWriteOperation(pParse, 0);
126c977f7f5Sdrh     addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
127c977f7f5Sdrh     sqliteVdbeChangeP3(v, addr+3, nt->name, 0);
128c977f7f5Sdrh     sqliteVdbeChangeP3(v, addr+4, nt->table, 0);
129c977f7f5Sdrh     sqliteVdbeChangeP3(v, addr+6, nt->strings, 0);
130dc379456Sdrh     sqliteChangeCookie(pParse->db);
131c977f7f5Sdrh     sqliteVdbeChangeP1(v, addr+9, pParse->db->next_cookie);
132c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
133c3f9bad2Sdanielk1977   }
134c3f9bad2Sdanielk1977 
135c3f9bad2Sdanielk1977   if( !pParse->explain ){
136c3f9bad2Sdanielk1977     /* Stick it in the hash-table */
137633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigHash), nt->name, pName->n + 1, nt);
138c3f9bad2Sdanielk1977 
139c3f9bad2Sdanielk1977     /* Attach it to the table object */
140c3f9bad2Sdanielk1977     nt->pNext = tab->pTrigger;
141c3f9bad2Sdanielk1977     tab->pTrigger = nt;
142c3f9bad2Sdanielk1977     return;
143c3f9bad2Sdanielk1977   }else{
144c3f9bad2Sdanielk1977     sqliteFree(nt->strings);
145c3f9bad2Sdanielk1977     sqliteFree(nt->name);
146c3f9bad2Sdanielk1977     sqliteFree(nt->table);
147c3f9bad2Sdanielk1977     sqliteFree(nt);
148c3f9bad2Sdanielk1977   }
149c3f9bad2Sdanielk1977 
150c3f9bad2Sdanielk1977 trigger_cleanup:
151c3f9bad2Sdanielk1977 
152633ed08dSdanielk1977   sqliteIdListDelete(pColumns);
153c3f9bad2Sdanielk1977   sqliteExprDelete(pWhen);
154c3f9bad2Sdanielk1977   {
155c3f9bad2Sdanielk1977     TriggerStep * pp;
156c3f9bad2Sdanielk1977     TriggerStep * nn;
157c3f9bad2Sdanielk1977 
158633ed08dSdanielk1977     pp = pStepList;
159c3f9bad2Sdanielk1977     while( pp ){
160c3f9bad2Sdanielk1977       nn = pp->pNext;
161c3f9bad2Sdanielk1977       sqliteExprDelete(pp->pWhere);
162c3f9bad2Sdanielk1977       sqliteExprListDelete(pp->pExprList);
163c3f9bad2Sdanielk1977       sqliteSelectDelete(pp->pSelect);
164c3f9bad2Sdanielk1977       sqliteIdListDelete(pp->pIdList);
165c3f9bad2Sdanielk1977       sqliteFree(pp);
166c3f9bad2Sdanielk1977       pp = nn;
167c3f9bad2Sdanielk1977     }
168c3f9bad2Sdanielk1977   }
169c3f9bad2Sdanielk1977 }
170c3f9bad2Sdanielk1977 
171c977f7f5Sdrh /*
172c977f7f5Sdrh ** Turn a SELECT statement (that the pSelect parameter points to) into
173c977f7f5Sdrh ** a trigger step.  Return a pointer to a TriggerStep structure.
174c977f7f5Sdrh **
175c977f7f5Sdrh ** The parser calls this routine when it finds a SELECT statement in
176c977f7f5Sdrh ** body of a TRIGGER.
177c977f7f5Sdrh */
178c977f7f5Sdrh TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
179633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
180*e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
181c3f9bad2Sdanielk1977 
182633ed08dSdanielk1977   pTriggerStep->op = TK_SELECT;
183633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
184633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
185c3f9bad2Sdanielk1977 
186633ed08dSdanielk1977   return pTriggerStep;
187c3f9bad2Sdanielk1977 }
188c3f9bad2Sdanielk1977 
189c977f7f5Sdrh /*
190c977f7f5Sdrh ** Build a trigger step out of an INSERT statement.  Return a pointer
191c977f7f5Sdrh ** to the new trigger step.
192c977f7f5Sdrh **
193c977f7f5Sdrh ** The parser calls this routine when it sees an INSERT inside the
194c977f7f5Sdrh ** body of a trigger.
195c977f7f5Sdrh */
196633ed08dSdanielk1977 TriggerStep *sqliteTriggerInsertStep(
197c977f7f5Sdrh   Token *pTableName,  /* Name of the table into which we insert */
198c977f7f5Sdrh   IdList *pColumn,    /* List of columns in pTableName to insert into */
199c977f7f5Sdrh   ExprList *pEList,   /* The VALUE clause: a list of values to be inserted */
200c977f7f5Sdrh   Select *pSelect,    /* A SELECT statement that supplies values */
201c977f7f5Sdrh   int orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
202633ed08dSdanielk1977 ){
203633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
204*e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
205c3f9bad2Sdanielk1977 
206633ed08dSdanielk1977   assert(pEList == 0 || pSelect == 0);
207633ed08dSdanielk1977   assert(pEList != 0 || pSelect != 0);
208c3f9bad2Sdanielk1977 
209633ed08dSdanielk1977   pTriggerStep->op = TK_INSERT;
210633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
211633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
212633ed08dSdanielk1977   pTriggerStep->pIdList = pColumn;
213633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
214633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
215c3f9bad2Sdanielk1977 
216633ed08dSdanielk1977   return pTriggerStep;
217c3f9bad2Sdanielk1977 }
218c3f9bad2Sdanielk1977 
219c977f7f5Sdrh /*
220c977f7f5Sdrh ** Construct a trigger step that implements an UPDATE statement and return
221c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
222c977f7f5Sdrh ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
223c977f7f5Sdrh */
224633ed08dSdanielk1977 TriggerStep *sqliteTriggerUpdateStep(
225c977f7f5Sdrh   Token *pTableName,   /* Name of the table to be updated */
226c977f7f5Sdrh   ExprList *pEList,    /* The SET clause: list of column and new values */
227c977f7f5Sdrh   Expr *pWhere,        /* The WHERE clause */
228c977f7f5Sdrh   int orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
229c977f7f5Sdrh ){
230633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
231*e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
232c3f9bad2Sdanielk1977 
233633ed08dSdanielk1977   pTriggerStep->op = TK_UPDATE;
234633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
235633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
236633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
237633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
238c3f9bad2Sdanielk1977 
239633ed08dSdanielk1977   return pTriggerStep;
240c3f9bad2Sdanielk1977 }
241c3f9bad2Sdanielk1977 
242c977f7f5Sdrh /*
243c977f7f5Sdrh ** Construct a trigger step that implements a DELETE statement and return
244c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
245c977f7f5Sdrh ** sees a DELETE statement inside the body of a CREATE TRIGGER.
246c977f7f5Sdrh */
247c977f7f5Sdrh TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
248633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
249*e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
250c3f9bad2Sdanielk1977 
251633ed08dSdanielk1977   pTriggerStep->op = TK_DELETE;
252633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
253633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
254633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
255c3f9bad2Sdanielk1977 
256633ed08dSdanielk1977   return pTriggerStep;
257c3f9bad2Sdanielk1977 }
258c3f9bad2Sdanielk1977 
259c3f9bad2Sdanielk1977 /*
260633ed08dSdanielk1977 ** Recursively delete a Trigger structure
261c3f9bad2Sdanielk1977 */
2621d1f3055Sdrh void sqliteDeleteTrigger(Trigger *pTrigger){
263633ed08dSdanielk1977   TriggerStep *pTriggerStep;
264c3f9bad2Sdanielk1977 
265633ed08dSdanielk1977   pTriggerStep = pTrigger->step_list;
266633ed08dSdanielk1977   while( pTriggerStep ){
267633ed08dSdanielk1977     TriggerStep * pTmp = pTriggerStep;
268633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
269633ed08dSdanielk1977 
270633ed08dSdanielk1977     sqliteExprDelete(pTmp->pWhere);
271633ed08dSdanielk1977     sqliteExprListDelete(pTmp->pExprList);
272633ed08dSdanielk1977     sqliteSelectDelete(pTmp->pSelect);
273633ed08dSdanielk1977     sqliteIdListDelete(pTmp->pIdList);
274633ed08dSdanielk1977 
275633ed08dSdanielk1977     sqliteFree(pTmp);
276633ed08dSdanielk1977   }
277633ed08dSdanielk1977 
278633ed08dSdanielk1977   sqliteFree(pTrigger->name);
279633ed08dSdanielk1977   sqliteFree(pTrigger->table);
280633ed08dSdanielk1977   sqliteExprDelete(pTrigger->pWhen);
281633ed08dSdanielk1977   sqliteIdListDelete(pTrigger->pColumns);
282633ed08dSdanielk1977   sqliteFree(pTrigger->strings);
283633ed08dSdanielk1977   sqliteFree(pTrigger);
284633ed08dSdanielk1977 }
285633ed08dSdanielk1977 
286633ed08dSdanielk1977 /*
287633ed08dSdanielk1977  * This function is called to drop a trigger from the database schema.
288633ed08dSdanielk1977  *
289633ed08dSdanielk1977  * This may be called directly from the parser, or from within
290633ed08dSdanielk1977  * sqliteDropTable(). In the latter case the "nested" argument is true.
291633ed08dSdanielk1977  *
292633ed08dSdanielk1977  * Note that this function does not delete the trigger entirely. Instead it
293633ed08dSdanielk1977  * removes it from the internal schema and places it in the trigDrop hash
294633ed08dSdanielk1977  * table. This is so that the trigger can be restored into the database schema
295633ed08dSdanielk1977  * if the transaction is rolled back.
296633ed08dSdanielk1977  */
297633ed08dSdanielk1977 void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
298633ed08dSdanielk1977 {
299633ed08dSdanielk1977   char *zName;
300633ed08dSdanielk1977   Trigger *pTrigger;
301633ed08dSdanielk1977   Table   *pTable;
302633ed08dSdanielk1977 
303633ed08dSdanielk1977   zName = sqliteStrNDup(pName->z, pName->n);
304c3f9bad2Sdanielk1977 
305c3f9bad2Sdanielk1977   /* ensure that the trigger being dropped exists */
306633ed08dSdanielk1977   pTrigger = sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1);
307633ed08dSdanielk1977   if( !pTrigger ){
308c3f9bad2Sdanielk1977     sqliteSetNString(&pParse->zErrMsg, "no such trigger: ", -1,
309633ed08dSdanielk1977         zName, -1, 0);
310633ed08dSdanielk1977     sqliteFree(zName);
311c3f9bad2Sdanielk1977     return;
312c3f9bad2Sdanielk1977   }
313c3f9bad2Sdanielk1977 
314c3f9bad2Sdanielk1977   /*
315c3f9bad2Sdanielk1977    * If this is not an "explain", do the following:
316c3f9bad2Sdanielk1977    * 1. Remove the trigger from its associated table structure
317c3f9bad2Sdanielk1977    * 2. Move the trigger from the trigHash hash to trigDrop
318c3f9bad2Sdanielk1977    */
319c3f9bad2Sdanielk1977   if( !pParse->explain ){
320c3f9bad2Sdanielk1977     /* 1 */
321633ed08dSdanielk1977     pTable = sqliteFindTable(pParse->db, pTrigger->table);
322633ed08dSdanielk1977     assert(pTable);
323633ed08dSdanielk1977     if( pTable->pTrigger == pTrigger ){
324633ed08dSdanielk1977       pTable->pTrigger = pTrigger->pNext;
325633ed08dSdanielk1977     }else{
326633ed08dSdanielk1977       Trigger *cc = pTable->pTrigger;
327c3f9bad2Sdanielk1977       while( cc ){
328633ed08dSdanielk1977         if( cc->pNext == pTrigger ){
329c3f9bad2Sdanielk1977           cc->pNext = cc->pNext->pNext;
330c3f9bad2Sdanielk1977           break;
331c3f9bad2Sdanielk1977         }
332c3f9bad2Sdanielk1977         cc = cc->pNext;
333c3f9bad2Sdanielk1977       }
334c3f9bad2Sdanielk1977       assert(cc);
335c3f9bad2Sdanielk1977     }
336c3f9bad2Sdanielk1977 
337c3f9bad2Sdanielk1977     /* 2 */
338633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigHash), zName,
339633ed08dSdanielk1977         pName->n + 1, NULL);
340633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigDrop), pTrigger->name,
341633ed08dSdanielk1977         pName->n + 1, pTrigger);
342c3f9bad2Sdanielk1977   }
343c3f9bad2Sdanielk1977 
344c3f9bad2Sdanielk1977   /* Unless this is a trigger on a TEMP TABLE, generate code to destroy the
345c3f9bad2Sdanielk1977    * database record of the trigger */
346633ed08dSdanielk1977   if( !pTable->isTemp ){
347c3f9bad2Sdanielk1977     int base;
348c3f9bad2Sdanielk1977     static VdbeOp dropTrigger[] = {
349c3f9bad2Sdanielk1977       { OP_OpenWrite,  0, 2,        MASTER_NAME},
350c3f9bad2Sdanielk1977       { OP_Rewind,     0, ADDR(9),  0},
351c3f9bad2Sdanielk1977       { OP_String,     0, 0,        0}, /* 2 */
352c3f9bad2Sdanielk1977       { OP_MemStore,   1, 1,        0},
353c3f9bad2Sdanielk1977       { OP_MemLoad,    1, 0,        0}, /* 4 */
354c3f9bad2Sdanielk1977       { OP_Column,     0, 1,        0},
355c3f9bad2Sdanielk1977       { OP_Ne,         0, ADDR(8),  0},
356c3f9bad2Sdanielk1977       { OP_Delete,     0, 0,        0},
357c3f9bad2Sdanielk1977       { OP_Next,       0, ADDR(4),  0}, /* 8 */
358c3f9bad2Sdanielk1977       { OP_Integer,    0, 0,        0}, /* 9 */
359c3f9bad2Sdanielk1977       { OP_SetCookie,  0, 0,        0},
360c3f9bad2Sdanielk1977       { OP_Close,      0, 0,        0},
361c3f9bad2Sdanielk1977     };
362c3f9bad2Sdanielk1977 
363dc379456Sdrh     if( !nested ){
364c977f7f5Sdrh       sqliteBeginWriteOperation(pParse, 0);
365dc379456Sdrh     }
366c3f9bad2Sdanielk1977     base = sqliteVdbeAddOpList(pParse->pVdbe,
367c3f9bad2Sdanielk1977         ArraySize(dropTrigger), dropTrigger);
368633ed08dSdanielk1977     sqliteVdbeChangeP3(pParse->pVdbe, base+2, zName, 0);
369dc379456Sdrh     if( !nested ){
370dc379456Sdrh       sqliteChangeCookie(pParse->db);
371dc379456Sdrh     }
372c3f9bad2Sdanielk1977     sqliteVdbeChangeP1(pParse->pVdbe, base+9, pParse->db->next_cookie);
373dc379456Sdrh     if( !nested ){
374c3f9bad2Sdanielk1977       sqliteEndWriteOperation(pParse);
375c3f9bad2Sdanielk1977     }
376dc379456Sdrh   }
377c3f9bad2Sdanielk1977 
378633ed08dSdanielk1977   sqliteFree(zName);
379c3f9bad2Sdanielk1977 }
380c3f9bad2Sdanielk1977 
381c977f7f5Sdrh /*
382c977f7f5Sdrh ** pEList is the SET clause of an UPDATE statement.  Each entry
383c977f7f5Sdrh ** in pEList is of the format <id>=<expr>.  If any of the entries
384c977f7f5Sdrh ** in pEList have an <id> which matches an identifier in pIdList,
385c977f7f5Sdrh ** then return TRUE.  If pIdList==NULL, then it is considered a
386c977f7f5Sdrh ** wildcard that matches anything.  Likewise if pEList==NULL then
387c977f7f5Sdrh ** it matches anything so always return true.  Return false only
388c977f7f5Sdrh ** if there is no match.
389c977f7f5Sdrh */
390c977f7f5Sdrh static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
391c3f9bad2Sdanielk1977   int i, e;
392633ed08dSdanielk1977   if( !pIdList )return 1;
393633ed08dSdanielk1977   if( !pEList )return 1;
394c3f9bad2Sdanielk1977 
395f29ce559Sdanielk1977   for(i = 0; i < pIdList->nId; i++){
396f29ce559Sdanielk1977     for(e = 0; e < pEList->nExpr; e++){
397f29ce559Sdanielk1977       if( !sqliteStrICmp(pIdList->a[i].zName, pEList->a[e].zName) ){
398c3f9bad2Sdanielk1977         return 1;
399f29ce559Sdanielk1977       }
400f29ce559Sdanielk1977     }
401f29ce559Sdanielk1977   }
402c3f9bad2Sdanielk1977 
403c3f9bad2Sdanielk1977   return 0;
404c3f9bad2Sdanielk1977 }
405c3f9bad2Sdanielk1977 
406c3f9bad2Sdanielk1977 /* A global variable that is TRUE if we should always set up temp tables for
407c3f9bad2Sdanielk1977  * for triggers, even if there are no triggers to code. This is used to test
408c3f9bad2Sdanielk1977  * how much overhead the triggers algorithm is causing.
409c3f9bad2Sdanielk1977  *
410c3f9bad2Sdanielk1977  * This flag can be set or cleared using the "trigger_overhead_test" pragma.
411c3f9bad2Sdanielk1977  * The pragma is not documented since it is not really part of the interface
412c3f9bad2Sdanielk1977  * to SQLite, just the test procedure.
413c3f9bad2Sdanielk1977 */
414c3f9bad2Sdanielk1977 int always_code_trigger_setup = 0;
415c3f9bad2Sdanielk1977 
416c3f9bad2Sdanielk1977 /*
417c3f9bad2Sdanielk1977  * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
418c3f9bad2Sdanielk1977  * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
419c3f9bad2Sdanielk1977  * found in the list specified as pTrigger.
420c3f9bad2Sdanielk1977  */
421c3f9bad2Sdanielk1977 int sqliteTriggersExist(
422c977f7f5Sdrh   Parse *pParse,          /* Used to check for recursive triggers */
423c977f7f5Sdrh   Trigger *pTrigger,      /* A list of triggers associated with a table */
424c3f9bad2Sdanielk1977   int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
425c3f9bad2Sdanielk1977   int tr_tm,              /* one of TK_BEFORE, TK_AFTER */
426c3f9bad2Sdanielk1977   int foreach,            /* one of TK_ROW or TK_STATEMENT */
427c977f7f5Sdrh   ExprList *pChanges      /* Columns that change in an UPDATE statement */
428c977f7f5Sdrh ){
429633ed08dSdanielk1977   Trigger * pTriggerCursor;
430c3f9bad2Sdanielk1977 
431633ed08dSdanielk1977   if( always_code_trigger_setup ){
432633ed08dSdanielk1977     return 1;
433633ed08dSdanielk1977   }
434c3f9bad2Sdanielk1977 
435633ed08dSdanielk1977   pTriggerCursor = pTrigger;
436633ed08dSdanielk1977   while( pTriggerCursor ){
437633ed08dSdanielk1977     if( pTriggerCursor->op == op &&
438633ed08dSdanielk1977 	pTriggerCursor->tr_tm == tr_tm &&
439633ed08dSdanielk1977 	pTriggerCursor->foreach == foreach &&
440633ed08dSdanielk1977 	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
441c3f9bad2Sdanielk1977       TriggerStack * ss;
442c3f9bad2Sdanielk1977       ss = pParse->trigStack;
443f29ce559Sdanielk1977       while( ss && ss->pTrigger != pTrigger ){
444f29ce559Sdanielk1977 	ss = ss->pNext;
445f29ce559Sdanielk1977       }
446c3f9bad2Sdanielk1977       if( !ss )return 1;
447c3f9bad2Sdanielk1977     }
448633ed08dSdanielk1977     pTriggerCursor = pTriggerCursor->pNext;
449c3f9bad2Sdanielk1977   }
450c3f9bad2Sdanielk1977 
451c3f9bad2Sdanielk1977   return 0;
452c3f9bad2Sdanielk1977 }
453c3f9bad2Sdanielk1977 
454c977f7f5Sdrh /*
455c977f7f5Sdrh ** Generate VDBE code for zero or more statements inside the body of a
456c977f7f5Sdrh ** trigger.
457c977f7f5Sdrh */
458c3f9bad2Sdanielk1977 static int codeTriggerProgram(
459c977f7f5Sdrh   Parse *pParse,            /* The parser context */
460c977f7f5Sdrh   TriggerStep *pStepList,   /* List of statements inside the trigger body */
461c977f7f5Sdrh   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */
462633ed08dSdanielk1977 ){
463633ed08dSdanielk1977   TriggerStep * pTriggerStep = pStepList;
464c3f9bad2Sdanielk1977   int orconf;
465c3f9bad2Sdanielk1977 
466633ed08dSdanielk1977   while( pTriggerStep ){
467c3f9bad2Sdanielk1977     int saveNTab = pParse->nTab;
468633ed08dSdanielk1977     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
469c3f9bad2Sdanielk1977     pParse->trigStack->orconf = orconf;
470633ed08dSdanielk1977     switch( pTriggerStep->op ){
471c3f9bad2Sdanielk1977       case TK_SELECT: {
472c3f9bad2Sdanielk1977         int tmp_tbl = pParse->nTab++;
473c3f9bad2Sdanielk1977 	sqliteVdbeAddOp(pParse->pVdbe, OP_OpenTemp, tmp_tbl, 0);
474c3f9bad2Sdanielk1977 	sqliteVdbeAddOp(pParse->pVdbe, OP_KeyAsData, tmp_tbl, 1);
475633ed08dSdanielk1977 	sqliteSelect(pParse, pTriggerStep->pSelect, SRT_Union,
476633ed08dSdanielk1977 	    tmp_tbl, 0, 0, 0);
477c3f9bad2Sdanielk1977 	sqliteVdbeAddOp(pParse->pVdbe, OP_Close, tmp_tbl, 0);
478c3f9bad2Sdanielk1977 	pParse->nTab--;
479c3f9bad2Sdanielk1977 	break;
480c3f9bad2Sdanielk1977       }
481c3f9bad2Sdanielk1977       case TK_UPDATE: {
482c3f9bad2Sdanielk1977         sqliteVdbeAddOp(pParse->pVdbe, OP_PushList, 0, 0);
483633ed08dSdanielk1977         sqliteUpdate(pParse, &pTriggerStep->target,
484633ed08dSdanielk1977         sqliteExprListDup(pTriggerStep->pExprList),
485633ed08dSdanielk1977         sqliteExprDup(pTriggerStep->pWhere), orconf);
486c3f9bad2Sdanielk1977         sqliteVdbeAddOp(pParse->pVdbe, OP_PopList, 0, 0);
487c3f9bad2Sdanielk1977         break;
488c3f9bad2Sdanielk1977       }
489c3f9bad2Sdanielk1977       case TK_INSERT: {
490633ed08dSdanielk1977         sqliteInsert(pParse, &pTriggerStep->target,
491633ed08dSdanielk1977         sqliteExprListDup(pTriggerStep->pExprList),
492633ed08dSdanielk1977         sqliteSelectDup(pTriggerStep->pSelect),
493633ed08dSdanielk1977         sqliteIdListDup(pTriggerStep->pIdList), orconf);
494c3f9bad2Sdanielk1977         break;
495c3f9bad2Sdanielk1977       }
496c3f9bad2Sdanielk1977       case TK_DELETE: {
497c3f9bad2Sdanielk1977         sqliteVdbeAddOp(pParse->pVdbe, OP_PushList, 0, 0);
498633ed08dSdanielk1977         sqliteDeleteFrom(pParse, &pTriggerStep->target,
499633ed08dSdanielk1977 	    sqliteExprDup(pTriggerStep->pWhere));
500c3f9bad2Sdanielk1977         sqliteVdbeAddOp(pParse->pVdbe, OP_PopList, 0, 0);
501c3f9bad2Sdanielk1977         break;
502c3f9bad2Sdanielk1977       }
503c3f9bad2Sdanielk1977       default:
504c3f9bad2Sdanielk1977         assert(0);
505c3f9bad2Sdanielk1977     }
506c3f9bad2Sdanielk1977     pParse->nTab = saveNTab;
507633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
508c3f9bad2Sdanielk1977   }
509c3f9bad2Sdanielk1977 
510c3f9bad2Sdanielk1977   return 0;
511c3f9bad2Sdanielk1977 }
512c3f9bad2Sdanielk1977 
513633ed08dSdanielk1977 /*
514633ed08dSdanielk1977 ** This is called to code FOR EACH ROW triggers.
515633ed08dSdanielk1977 **
516633ed08dSdanielk1977 ** When the code that this function generates is executed, the following
517633ed08dSdanielk1977 ** must be true:
518c977f7f5Sdrh **
519c977f7f5Sdrh ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
520c977f7f5Sdrh **    can be indices of cursors in temporary tables.  See below.)
521c977f7f5Sdrh **
522633ed08dSdanielk1977 ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
523633ed08dSdanielk1977 **    a temporary vdbe cursor (index newIdx) must be open and pointing at
524633ed08dSdanielk1977 **    a row containing values to be substituted for new.* expressions in the
525633ed08dSdanielk1977 **    trigger program(s).
526c977f7f5Sdrh **
527633ed08dSdanielk1977 ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
528633ed08dSdanielk1977 **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
529633ed08dSdanielk1977 **    a row containing values to be substituted for old.* expressions in the
530633ed08dSdanielk1977 **    trigger program(s).
531633ed08dSdanielk1977 **
532633ed08dSdanielk1977 */
533c3f9bad2Sdanielk1977 int sqliteCodeRowTrigger(
534c3f9bad2Sdanielk1977   Parse *pParse,       /* Parse context */
535c3f9bad2Sdanielk1977   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
536633ed08dSdanielk1977   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
537c3f9bad2Sdanielk1977   int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
538633ed08dSdanielk1977   Table *pTab,         /* The table to code triggers from */
539633ed08dSdanielk1977   int newIdx,          /* The indice of the "new" row to access */
540633ed08dSdanielk1977   int oldIdx,          /* The indice of the "old" row to access */
541c977f7f5Sdrh   int orconf           /* ON CONFLICT policy */
542c977f7f5Sdrh ){
543c3f9bad2Sdanielk1977   Trigger * pTrigger;
544c3f9bad2Sdanielk1977   TriggerStack * pTriggerStack;
545c3f9bad2Sdanielk1977 
546c3f9bad2Sdanielk1977   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
547c3f9bad2Sdanielk1977   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
548c3f9bad2Sdanielk1977 
549633ed08dSdanielk1977   assert(newIdx != -1 || oldIdx != -1);
550c3f9bad2Sdanielk1977 
551633ed08dSdanielk1977   pTrigger = pTab->pTrigger;
552c3f9bad2Sdanielk1977   while( pTrigger ){
553c3f9bad2Sdanielk1977     int fire_this = 0;
554c3f9bad2Sdanielk1977 
555c3f9bad2Sdanielk1977     /* determine whether we should code this trigger */
556c3f9bad2Sdanielk1977     if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
557c3f9bad2Sdanielk1977         pTrigger->foreach == TK_ROW ){
558c3f9bad2Sdanielk1977       fire_this = 1;
559c3f9bad2Sdanielk1977       pTriggerStack = pParse->trigStack;
560c3f9bad2Sdanielk1977       while( pTriggerStack ){
561f29ce559Sdanielk1977         if( pTriggerStack->pTrigger == pTrigger ){
562f29ce559Sdanielk1977 	  fire_this = 0;
563f29ce559Sdanielk1977 	}
564c3f9bad2Sdanielk1977         pTriggerStack = pTriggerStack->pNext;
565c3f9bad2Sdanielk1977       }
566c3f9bad2Sdanielk1977       if( op == TK_UPDATE && pTrigger->pColumns &&
567f29ce559Sdanielk1977           !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
568c3f9bad2Sdanielk1977         fire_this = 0;
569c3f9bad2Sdanielk1977       }
570f29ce559Sdanielk1977     }
571c3f9bad2Sdanielk1977 
572*e4697f5eSdrh     if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
573c3f9bad2Sdanielk1977       int endTrigger;
574c3f9bad2Sdanielk1977       IdList dummyTablist;
575c3f9bad2Sdanielk1977       Expr * whenExpr;
576c3f9bad2Sdanielk1977 
577c3f9bad2Sdanielk1977       dummyTablist.nId = 0;
578c3f9bad2Sdanielk1977       dummyTablist.a = 0;
579c3f9bad2Sdanielk1977 
580c3f9bad2Sdanielk1977       /* Push an entry on to the trigger stack */
581c3f9bad2Sdanielk1977       pTriggerStack->pTrigger = pTrigger;
582633ed08dSdanielk1977       pTriggerStack->newIdx = newIdx;
583633ed08dSdanielk1977       pTriggerStack->oldIdx = oldIdx;
584633ed08dSdanielk1977       pTriggerStack->pTab = pTab;
585c3f9bad2Sdanielk1977       pTriggerStack->pNext = pParse->trigStack;
586c3f9bad2Sdanielk1977       pParse->trigStack = pTriggerStack;
587c3f9bad2Sdanielk1977 
588c3f9bad2Sdanielk1977       /* code the WHEN clause */
589c3f9bad2Sdanielk1977       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
590c3f9bad2Sdanielk1977       whenExpr = sqliteExprDup(pTrigger->pWhen);
591c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
592c3f9bad2Sdanielk1977         pParse->trigStack = pParse->trigStack->pNext;
593c3f9bad2Sdanielk1977         sqliteFree(pTriggerStack);
594c3f9bad2Sdanielk1977         sqliteExprDelete(whenExpr);
595c3f9bad2Sdanielk1977         return 1;
596c3f9bad2Sdanielk1977       }
597c3f9bad2Sdanielk1977       sqliteExprIfFalse(pParse, whenExpr, endTrigger);
598c3f9bad2Sdanielk1977       sqliteExprDelete(whenExpr);
599c3f9bad2Sdanielk1977 
600633ed08dSdanielk1977       codeTriggerProgram(pParse, pTrigger->step_list, orconf);
601c3f9bad2Sdanielk1977 
602c3f9bad2Sdanielk1977       /* Pop the entry off the trigger stack */
603c3f9bad2Sdanielk1977       pParse->trigStack = pParse->trigStack->pNext;
604c3f9bad2Sdanielk1977       sqliteFree(pTriggerStack);
605c3f9bad2Sdanielk1977 
606c3f9bad2Sdanielk1977       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
607c3f9bad2Sdanielk1977     }
608c3f9bad2Sdanielk1977     pTrigger = pTrigger->pNext;
609c3f9bad2Sdanielk1977   }
610c3f9bad2Sdanielk1977 
611c3f9bad2Sdanielk1977   return 0;
612c3f9bad2Sdanielk1977 }
613c3f9bad2Sdanielk1977 
614c3f9bad2Sdanielk1977 /*
615633ed08dSdanielk1977  * This function is called to code ON UPDATE and ON DELETE triggers on
616633ed08dSdanielk1977  * views.
617633ed08dSdanielk1977  *
618633ed08dSdanielk1977  * This function deletes the data pointed at by the pWhere and pChanges
619633ed08dSdanielk1977  * arguments before it completes.
620c3f9bad2Sdanielk1977  */
621633ed08dSdanielk1977 void sqliteViewTriggers(
622633ed08dSdanielk1977   Parse *pParse,
623633ed08dSdanielk1977   Table *pTab,         /* The view to code triggers on */
624633ed08dSdanielk1977   Expr *pWhere,        /* The WHERE clause of the statement causing triggers*/
625633ed08dSdanielk1977   int orconf,          /* The ON CONFLICT policy specified as part of the
626633ed08dSdanielk1977 			  statement causing these triggers */
627633ed08dSdanielk1977   ExprList *pChanges   /* If this is an statement causing triggers to fire
628633ed08dSdanielk1977 			  is an UPDATE, then this list holds the columns
629633ed08dSdanielk1977 			  to update and the expressions to update them to.
630633ed08dSdanielk1977 			  See comments for sqliteUpdate(). */
631633ed08dSdanielk1977 ){
632c3f9bad2Sdanielk1977   int oldIdx = -1;
633c3f9bad2Sdanielk1977   int newIdx = -1;
634c3f9bad2Sdanielk1977   int *aXRef = 0;
635c3f9bad2Sdanielk1977   Vdbe *v;
636c3f9bad2Sdanielk1977   int endOfLoop;
637c3f9bad2Sdanielk1977   int startOfLoop;
638c3f9bad2Sdanielk1977   Select theSelect;
639c3f9bad2Sdanielk1977   Token tblNameToken;
640c3f9bad2Sdanielk1977 
641c3f9bad2Sdanielk1977   assert(pTab->pSelect);
642c3f9bad2Sdanielk1977 
643c3f9bad2Sdanielk1977   tblNameToken.z = pTab->zName;
644c3f9bad2Sdanielk1977   tblNameToken.n = strlen(pTab->zName);
645c3f9bad2Sdanielk1977 
646c3f9bad2Sdanielk1977   theSelect.isDistinct = 0;
647c3f9bad2Sdanielk1977   theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
648c3f9bad2Sdanielk1977   theSelect.pSrc   = sqliteIdListAppend(0, &tblNameToken);
649c3f9bad2Sdanielk1977   theSelect.pWhere = pWhere;    pWhere = 0;
650c3f9bad2Sdanielk1977   theSelect.pGroupBy = 0;
651c3f9bad2Sdanielk1977   theSelect.pHaving = 0;
652c3f9bad2Sdanielk1977   theSelect.pOrderBy = 0;
653c3f9bad2Sdanielk1977   theSelect.op = TK_SELECT; /* ?? */
654c3f9bad2Sdanielk1977   theSelect.pPrior = 0;
655c3f9bad2Sdanielk1977   theSelect.nLimit = -1;
656c3f9bad2Sdanielk1977   theSelect.nOffset = -1;
657c3f9bad2Sdanielk1977   theSelect.zSelect = 0;
658c3f9bad2Sdanielk1977   theSelect.base = 0;
659c3f9bad2Sdanielk1977 
660c3f9bad2Sdanielk1977   v = sqliteGetVdbe(pParse);
661c3f9bad2Sdanielk1977   assert(v);
662c977f7f5Sdrh   sqliteBeginWriteOperation(pParse, 1);
663c3f9bad2Sdanielk1977 
664c3f9bad2Sdanielk1977   /* Allocate temp tables */
665c3f9bad2Sdanielk1977   oldIdx = pParse->nTab++;
666c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
667c3f9bad2Sdanielk1977   if( pChanges ){
668c3f9bad2Sdanielk1977     newIdx = pParse->nTab++;
669c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
670c3f9bad2Sdanielk1977   }
671c3f9bad2Sdanielk1977 
672c3f9bad2Sdanielk1977   /* Snapshot the view */
673c3f9bad2Sdanielk1977   if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){
674c3f9bad2Sdanielk1977     goto trigger_cleanup;
675c3f9bad2Sdanielk1977   }
676c3f9bad2Sdanielk1977 
677c3f9bad2Sdanielk1977   /* loop thru the view snapshot, executing triggers for each row */
678c3f9bad2Sdanielk1977   endOfLoop = sqliteVdbeMakeLabel(v);
679c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);
680c3f9bad2Sdanielk1977 
681c3f9bad2Sdanielk1977   /* Loop thru the view snapshot, executing triggers for each row */
682c3f9bad2Sdanielk1977   startOfLoop = sqliteVdbeCurrentAddr(v);
683c3f9bad2Sdanielk1977 
684c3f9bad2Sdanielk1977   /* Build the updated row if required */
685c3f9bad2Sdanielk1977   if( pChanges ){
6861d1f3055Sdrh     int ii;
687c3f9bad2Sdanielk1977 
688c3f9bad2Sdanielk1977     aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
689c3f9bad2Sdanielk1977     if( aXRef==0 ) goto trigger_cleanup;
690633ed08dSdanielk1977     for(ii = 0; ii < pTab->nCol; ii++){
691c3f9bad2Sdanielk1977       aXRef[ii] = -1;
692633ed08dSdanielk1977     }
693c3f9bad2Sdanielk1977 
694c3f9bad2Sdanielk1977     for(ii=0; ii<pChanges->nExpr; ii++){
695c3f9bad2Sdanielk1977       int jj;
696c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0,
697f29ce559Sdanielk1977             pChanges->a[ii].pExpr) ){
698c3f9bad2Sdanielk1977         goto trigger_cleanup;
699f29ce559Sdanielk1977       }
700c3f9bad2Sdanielk1977 
701c3f9bad2Sdanielk1977       if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
702c3f9bad2Sdanielk1977         goto trigger_cleanup;
703c3f9bad2Sdanielk1977 
704c3f9bad2Sdanielk1977       for(jj=0; jj<pTab->nCol; jj++){
705c3f9bad2Sdanielk1977         if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
706c3f9bad2Sdanielk1977           aXRef[jj] = ii;
707c3f9bad2Sdanielk1977           break;
708c3f9bad2Sdanielk1977         }
709c3f9bad2Sdanielk1977       }
710c3f9bad2Sdanielk1977       if( jj>=pTab->nCol ){
711c3f9bad2Sdanielk1977         sqliteSetString(&pParse->zErrMsg, "no such column: ",
712c3f9bad2Sdanielk1977             pChanges->a[ii].zName, 0);
713c3f9bad2Sdanielk1977         pParse->nErr++;
714c3f9bad2Sdanielk1977         goto trigger_cleanup;
715c3f9bad2Sdanielk1977       }
716c3f9bad2Sdanielk1977     }
717c3f9bad2Sdanielk1977 
718c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Integer, 13, 0);
719c3f9bad2Sdanielk1977 
720633ed08dSdanielk1977     for(ii = 0; ii<pTab->nCol; ii++){
721633ed08dSdanielk1977       if( aXRef[ii] < 0 ){
722c3f9bad2Sdanielk1977         sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
723633ed08dSdanielk1977       }else{
724c3f9bad2Sdanielk1977         sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
725633ed08dSdanielk1977       }
726633ed08dSdanielk1977     }
727c3f9bad2Sdanielk1977 
728c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
729c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
730c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
731c3f9bad2Sdanielk1977 
732c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
733633ed08dSdanielk1977         pTab, newIdx, oldIdx, orconf);
734c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
735633ed08dSdanielk1977         pTab, newIdx, oldIdx, orconf);
736c3f9bad2Sdanielk1977   }else{
737c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
738633ed08dSdanielk1977         orconf);
739c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
740633ed08dSdanielk1977         orconf);
741c3f9bad2Sdanielk1977   }
742c3f9bad2Sdanielk1977 
743c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
744c3f9bad2Sdanielk1977 
745c3f9bad2Sdanielk1977   sqliteVdbeResolveLabel(v, endOfLoop);
746c3f9bad2Sdanielk1977   sqliteEndWriteOperation(pParse);
747c3f9bad2Sdanielk1977 
748c3f9bad2Sdanielk1977 trigger_cleanup:
749c3f9bad2Sdanielk1977   sqliteFree(aXRef);
750c3f9bad2Sdanielk1977   sqliteExprListDelete(pChanges);
751c3f9bad2Sdanielk1977   sqliteExprDelete(pWhere);
752c3f9bad2Sdanielk1977   sqliteExprListDelete(theSelect.pEList);
753c3f9bad2Sdanielk1977   sqliteIdListDelete(theSelect.pSrc);
754c3f9bad2Sdanielk1977   sqliteExprDelete(theSelect.pWhere);
755c3f9bad2Sdanielk1977   return;
756c3f9bad2Sdanielk1977 }
757