xref: /sqlite-3.40.0/src/trigger.c (revision cabb0819)
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 */
44633ed08dSdanielk1977   Token *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;
52c3f9bad2Sdanielk1977 
53c3f9bad2Sdanielk1977   /* Check that:
549adf9ac4Sdrh   ** 1. the trigger name does not already exist.
559adf9ac4Sdrh   ** 2. the table (or view) does exist.
56d702fccbSdanielk1977   ** 3. that we are not trying to create a trigger on the sqlite_master table
57d702fccbSdanielk1977   ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
58d702fccbSdanielk1977   ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
59c3f9bad2Sdanielk1977   */
60c3f9bad2Sdanielk1977   {
61633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pName->z, pName->n);
62633ed08dSdanielk1977     if( sqliteHashFind(&(pParse->db->trigHash), tmp_str, pName->n + 1) ){
63c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "trigger ", -1,
64633ed08dSdanielk1977           pName->z, pName->n, " already exists", -1, 0);
65c3f9bad2Sdanielk1977       sqliteFree(tmp_str);
66c3f9bad2Sdanielk1977       pParse->nErr++;
67c3f9bad2Sdanielk1977       goto trigger_cleanup;
68c3f9bad2Sdanielk1977     }
69c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
70c3f9bad2Sdanielk1977   }
71c3f9bad2Sdanielk1977   {
72633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pTableName->z, pTableName->n);
73e4697f5eSdrh     if( tmp_str==0 ) goto trigger_cleanup;
74c3f9bad2Sdanielk1977     tab = sqliteFindTable(pParse->db, tmp_str);
75c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
76c3f9bad2Sdanielk1977     if( !tab ){
77c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1,
78633ed08dSdanielk1977           pTableName->z, pTableName->n, 0);
79c3f9bad2Sdanielk1977       pParse->nErr++;
80c3f9bad2Sdanielk1977       goto trigger_cleanup;
81c3f9bad2Sdanielk1977     }
821873cd50Sdrh     if( sqliteStrICmp(tab->zName, MASTER_NAME)==0 ){
831873cd50Sdrh       sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
841873cd50Sdrh          "table: " MASTER_NAME, 0);
851873cd50Sdrh       pParse->nErr++;
861873cd50Sdrh       goto trigger_cleanup;
871873cd50Sdrh     }
88e0bc4048Sdrh     if( sqliteStrICmp(tab->zName, TEMP_MASTER_NAME)==0 ){
89e0bc4048Sdrh       sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
90e0bc4048Sdrh          "table: " TEMP_MASTER_NAME, 0);
91e0bc4048Sdrh       pParse->nErr++;
92e0bc4048Sdrh       goto trigger_cleanup;
93e0bc4048Sdrh     }
94d702fccbSdanielk1977     if( tab->pSelect && tr_tm != TK_INSTEAD ){
95d702fccbSdanielk1977       sqliteSetNString(&pParse->zErrMsg, "cannot create ", -1,
96d702fccbSdanielk1977 	  (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", -1, " trigger on view: ", -1
97d702fccbSdanielk1977           , pTableName->z, pTableName->n, 0);
98d702fccbSdanielk1977       goto trigger_cleanup;
99d702fccbSdanielk1977     }
100d702fccbSdanielk1977     if( !tab->pSelect && tr_tm == TK_INSTEAD ){
101d702fccbSdanielk1977       sqliteSetNString(&pParse->zErrMsg, "cannot create INSTEAD OF", -1,
102d702fccbSdanielk1977 	  " trigger on table: ", -1, pTableName->z, pTableName->n, 0);
103d702fccbSdanielk1977       goto trigger_cleanup;
104d702fccbSdanielk1977     }
105d702fccbSdanielk1977   }
106d702fccbSdanielk1977 
107d702fccbSdanielk1977   if (tr_tm == TK_INSTEAD){
108d702fccbSdanielk1977     tr_tm = TK_BEFORE;
109c3f9bad2Sdanielk1977   }
110c3f9bad2Sdanielk1977 
111c3f9bad2Sdanielk1977   /* Build the Trigger object */
112c3f9bad2Sdanielk1977   nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
113e4697f5eSdrh   if( nt==0 ) goto trigger_cleanup;
114633ed08dSdanielk1977   nt->name = sqliteStrNDup(pName->z, pName->n);
115633ed08dSdanielk1977   nt->table = sqliteStrNDup(pTableName->z, pTableName->n);
116e4697f5eSdrh   if( sqlite_malloc_failed ) goto trigger_cleanup;
117c3f9bad2Sdanielk1977   nt->op = op;
118c3f9bad2Sdanielk1977   nt->tr_tm = tr_tm;
1194b59ab5eSdrh   nt->pWhen = sqliteExprDup(pWhen);
1204b59ab5eSdrh   sqliteExprDelete(pWhen);
1214b59ab5eSdrh   nt->pColumns = sqliteIdListDup(pColumns);
1224b59ab5eSdrh   sqliteIdListDelete(pColumns);
123c3f9bad2Sdanielk1977   nt->foreach = foreach;
124633ed08dSdanielk1977   nt->step_list = pStepList;
125c3f9bad2Sdanielk1977 
126c3f9bad2Sdanielk1977   /* if we are not initializing, and this trigger is not on a TEMP table,
1279adf9ac4Sdrh   ** build the sqlite_master entry
1289adf9ac4Sdrh   */
129e0bc4048Sdrh   if( !pParse->initFlag ){
130c977f7f5Sdrh     static VdbeOp insertTrig[] = {
131c977f7f5Sdrh       { OP_NewRecno,   0, 0,  0          },
132c977f7f5Sdrh       { OP_String,     0, 0,  "trigger"  },
133e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 2: trigger name */
134e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 3: table name */
135c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },
136e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 5: SQL */
137c977f7f5Sdrh       { OP_MakeRecord, 5, 0,  0          },
138c977f7f5Sdrh       { OP_PutIntKey,  0, 0,  0          },
139c977f7f5Sdrh     };
140c977f7f5Sdrh     int addr;
141c977f7f5Sdrh     Vdbe *v;
142c3f9bad2Sdanielk1977 
143c3f9bad2Sdanielk1977     /* Make an entry in the sqlite_master table */
144c977f7f5Sdrh     v = sqliteGetVdbe(pParse);
145e4697f5eSdrh     if( v==0 ) goto trigger_cleanup;
146*cabb0819Sdrh     sqliteBeginWriteOperation(pParse, 0, 0);
147e0bc4048Sdrh     sqliteOpenMasterTable(v, tab->isTemp);
148c977f7f5Sdrh     addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
149e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME,
150e0bc4048Sdrh                        P3_STATIC);
151e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
152e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
1534b59ab5eSdrh     sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
154e0bc4048Sdrh     if( !tab->isTemp ){
155e0bc4048Sdrh       sqliteChangeCookie(pParse->db, v);
156e0bc4048Sdrh     }
157e0bc4048Sdrh     sqliteVdbeAddOp(v, OP_Close, 0, 0);
158c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
159c3f9bad2Sdanielk1977   }
160c3f9bad2Sdanielk1977 
161c3f9bad2Sdanielk1977   if( !pParse->explain ){
162c3f9bad2Sdanielk1977     /* Stick it in the hash-table */
163633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigHash), nt->name, pName->n + 1, nt);
164c3f9bad2Sdanielk1977 
165c3f9bad2Sdanielk1977     /* Attach it to the table object */
166c3f9bad2Sdanielk1977     nt->pNext = tab->pTrigger;
167c3f9bad2Sdanielk1977     tab->pTrigger = nt;
168c3f9bad2Sdanielk1977     return;
169c3f9bad2Sdanielk1977   }else{
170c3f9bad2Sdanielk1977     sqliteFree(nt->name);
171c3f9bad2Sdanielk1977     sqliteFree(nt->table);
172c3f9bad2Sdanielk1977     sqliteFree(nt);
173c3f9bad2Sdanielk1977   }
174c3f9bad2Sdanielk1977 
175c3f9bad2Sdanielk1977 trigger_cleanup:
176c3f9bad2Sdanielk1977 
177633ed08dSdanielk1977   sqliteIdListDelete(pColumns);
178c3f9bad2Sdanielk1977   sqliteExprDelete(pWhen);
1794b59ab5eSdrh   sqliteDeleteTriggerStep(pStepList);
180c3f9bad2Sdanielk1977 }
1814b59ab5eSdrh 
1824b59ab5eSdrh /*
1834b59ab5eSdrh ** Make a copy of all components of the given trigger step.  This has
1844b59ab5eSdrh ** the effect of copying all Expr.token.z values into memory obtained
1854b59ab5eSdrh ** from sqliteMalloc().  As initially created, the Expr.token.z values
1864b59ab5eSdrh ** all point to the input string that was fed to the parser.  But that
1874b59ab5eSdrh ** string is ephemeral - it will go away as soon as the sqlite_exec()
1884b59ab5eSdrh ** call that started the parser exits.  This routine makes a persistent
1894b59ab5eSdrh ** copy of all the Expr.token.z strings so that the TriggerStep structure
1904b59ab5eSdrh ** will be valid even after the sqlite_exec() call returns.
1914b59ab5eSdrh */
1924b59ab5eSdrh static void sqlitePersistTriggerStep(TriggerStep *p){
1934b59ab5eSdrh   if( p->target.z ){
1944b59ab5eSdrh     p->target.z = sqliteStrNDup(p->target.z, p->target.n);
1954b59ab5eSdrh     p->target.dyn = 1;
1964b59ab5eSdrh   }
1974b59ab5eSdrh   if( p->pSelect ){
1984b59ab5eSdrh     Select *pNew = sqliteSelectDup(p->pSelect);
1994b59ab5eSdrh     sqliteSelectDelete(p->pSelect);
2004b59ab5eSdrh     p->pSelect = pNew;
2014b59ab5eSdrh   }
2024b59ab5eSdrh   if( p->pWhere ){
2034b59ab5eSdrh     Expr *pNew = sqliteExprDup(p->pWhere);
2044b59ab5eSdrh     sqliteExprDelete(p->pWhere);
2054b59ab5eSdrh     p->pWhere = pNew;
2064b59ab5eSdrh   }
2074b59ab5eSdrh   if( p->pExprList ){
2084b59ab5eSdrh     ExprList *pNew = sqliteExprListDup(p->pExprList);
2094b59ab5eSdrh     sqliteExprListDelete(p->pExprList);
2104b59ab5eSdrh     p->pExprList = pNew;
2114b59ab5eSdrh   }
2124b59ab5eSdrh   if( p->pIdList ){
2134b59ab5eSdrh     IdList *pNew = sqliteIdListDup(p->pIdList);
2144b59ab5eSdrh     sqliteIdListDelete(p->pIdList);
2154b59ab5eSdrh     p->pIdList = pNew;
216c3f9bad2Sdanielk1977   }
217c3f9bad2Sdanielk1977 }
218c3f9bad2Sdanielk1977 
219c977f7f5Sdrh /*
220c977f7f5Sdrh ** Turn a SELECT statement (that the pSelect parameter points to) into
221c977f7f5Sdrh ** a trigger step.  Return a pointer to a TriggerStep structure.
222c977f7f5Sdrh **
223c977f7f5Sdrh ** The parser calls this routine when it finds a SELECT statement in
224c977f7f5Sdrh ** body of a TRIGGER.
225c977f7f5Sdrh */
226c977f7f5Sdrh TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
227633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
228e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
229c3f9bad2Sdanielk1977 
230633ed08dSdanielk1977   pTriggerStep->op = TK_SELECT;
231633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
232633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
2334b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
234c3f9bad2Sdanielk1977 
235633ed08dSdanielk1977   return pTriggerStep;
236c3f9bad2Sdanielk1977 }
237c3f9bad2Sdanielk1977 
238c977f7f5Sdrh /*
239c977f7f5Sdrh ** Build a trigger step out of an INSERT statement.  Return a pointer
240c977f7f5Sdrh ** to the new trigger step.
241c977f7f5Sdrh **
242c977f7f5Sdrh ** The parser calls this routine when it sees an INSERT inside the
243c977f7f5Sdrh ** body of a trigger.
244c977f7f5Sdrh */
245633ed08dSdanielk1977 TriggerStep *sqliteTriggerInsertStep(
246c977f7f5Sdrh   Token *pTableName,  /* Name of the table into which we insert */
247c977f7f5Sdrh   IdList *pColumn,    /* List of columns in pTableName to insert into */
248c977f7f5Sdrh   ExprList *pEList,   /* The VALUE clause: a list of values to be inserted */
249c977f7f5Sdrh   Select *pSelect,    /* A SELECT statement that supplies values */
250c977f7f5Sdrh   int orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
251633ed08dSdanielk1977 ){
252633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
253e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
254c3f9bad2Sdanielk1977 
255633ed08dSdanielk1977   assert(pEList == 0 || pSelect == 0);
256633ed08dSdanielk1977   assert(pEList != 0 || pSelect != 0);
257c3f9bad2Sdanielk1977 
258633ed08dSdanielk1977   pTriggerStep->op = TK_INSERT;
259633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
260633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
261633ed08dSdanielk1977   pTriggerStep->pIdList = pColumn;
262633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
263633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
2644b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
265c3f9bad2Sdanielk1977 
266633ed08dSdanielk1977   return pTriggerStep;
267c3f9bad2Sdanielk1977 }
268c3f9bad2Sdanielk1977 
269c977f7f5Sdrh /*
270c977f7f5Sdrh ** Construct a trigger step that implements an UPDATE statement and return
271c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
272c977f7f5Sdrh ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
273c977f7f5Sdrh */
274633ed08dSdanielk1977 TriggerStep *sqliteTriggerUpdateStep(
275c977f7f5Sdrh   Token *pTableName,   /* Name of the table to be updated */
276c977f7f5Sdrh   ExprList *pEList,    /* The SET clause: list of column and new values */
277c977f7f5Sdrh   Expr *pWhere,        /* The WHERE clause */
278c977f7f5Sdrh   int orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
279c977f7f5Sdrh ){
280633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
281e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
282c3f9bad2Sdanielk1977 
283633ed08dSdanielk1977   pTriggerStep->op = TK_UPDATE;
284633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
285633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
286633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
287633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
2884b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
289c3f9bad2Sdanielk1977 
290633ed08dSdanielk1977   return pTriggerStep;
291c3f9bad2Sdanielk1977 }
292c3f9bad2Sdanielk1977 
293c977f7f5Sdrh /*
294c977f7f5Sdrh ** Construct a trigger step that implements a DELETE statement and return
295c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
296c977f7f5Sdrh ** sees a DELETE statement inside the body of a CREATE TRIGGER.
297c977f7f5Sdrh */
298c977f7f5Sdrh TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
299633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
300e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
301c3f9bad2Sdanielk1977 
302633ed08dSdanielk1977   pTriggerStep->op = TK_DELETE;
303633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
304633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
305633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
3064b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
307c3f9bad2Sdanielk1977 
308633ed08dSdanielk1977   return pTriggerStep;
309c3f9bad2Sdanielk1977 }
310c3f9bad2Sdanielk1977 
311c3f9bad2Sdanielk1977 /*
312633ed08dSdanielk1977 ** Recursively delete a Trigger structure
313c3f9bad2Sdanielk1977 */
3141d1f3055Sdrh void sqliteDeleteTrigger(Trigger *pTrigger){
3154b59ab5eSdrh   sqliteDeleteTriggerStep(pTrigger->step_list);
316633ed08dSdanielk1977   sqliteFree(pTrigger->name);
317633ed08dSdanielk1977   sqliteFree(pTrigger->table);
318633ed08dSdanielk1977   sqliteExprDelete(pTrigger->pWhen);
319633ed08dSdanielk1977   sqliteIdListDelete(pTrigger->pColumns);
320633ed08dSdanielk1977   sqliteFree(pTrigger);
321633ed08dSdanielk1977 }
322633ed08dSdanielk1977 
323633ed08dSdanielk1977 /*
324633ed08dSdanielk1977  * This function is called to drop a trigger from the database schema.
325633ed08dSdanielk1977  *
326633ed08dSdanielk1977  * This may be called directly from the parser, or from within
327633ed08dSdanielk1977  * sqliteDropTable(). In the latter case the "nested" argument is true.
328633ed08dSdanielk1977  *
329633ed08dSdanielk1977  * Note that this function does not delete the trigger entirely. Instead it
330633ed08dSdanielk1977  * removes it from the internal schema and places it in the trigDrop hash
331633ed08dSdanielk1977  * table. This is so that the trigger can be restored into the database schema
332633ed08dSdanielk1977  * if the transaction is rolled back.
333633ed08dSdanielk1977  */
334e0bc4048Sdrh void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){
335633ed08dSdanielk1977   char *zName;
336633ed08dSdanielk1977   Trigger *pTrigger;
337633ed08dSdanielk1977   Table   *pTable;
338e0bc4048Sdrh   Vdbe *v;
339633ed08dSdanielk1977 
340633ed08dSdanielk1977   zName = sqliteStrNDup(pName->z, pName->n);
341c3f9bad2Sdanielk1977 
342c3f9bad2Sdanielk1977   /* ensure that the trigger being dropped exists */
343633ed08dSdanielk1977   pTrigger = sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1);
344633ed08dSdanielk1977   if( !pTrigger ){
345c3f9bad2Sdanielk1977     sqliteSetNString(&pParse->zErrMsg, "no such trigger: ", -1,
346633ed08dSdanielk1977         zName, -1, 0);
347633ed08dSdanielk1977     sqliteFree(zName);
348c3f9bad2Sdanielk1977     return;
349c3f9bad2Sdanielk1977   }
350c3f9bad2Sdanielk1977 
351c3f9bad2Sdanielk1977   /*
352e0bc4048Sdrh    * If this is not an "explain", then delete the trigger structure.
353c3f9bad2Sdanielk1977    */
354c3f9bad2Sdanielk1977   if( !pParse->explain ){
355633ed08dSdanielk1977     pTable = sqliteFindTable(pParse->db, pTrigger->table);
356633ed08dSdanielk1977     assert(pTable);
357633ed08dSdanielk1977     if( pTable->pTrigger == pTrigger ){
358633ed08dSdanielk1977       pTable->pTrigger = pTrigger->pNext;
359633ed08dSdanielk1977     }else{
360633ed08dSdanielk1977       Trigger *cc = pTable->pTrigger;
361c3f9bad2Sdanielk1977       while( cc ){
362633ed08dSdanielk1977         if( cc->pNext == pTrigger ){
363c3f9bad2Sdanielk1977           cc->pNext = cc->pNext->pNext;
364c3f9bad2Sdanielk1977           break;
365c3f9bad2Sdanielk1977         }
366c3f9bad2Sdanielk1977         cc = cc->pNext;
367c3f9bad2Sdanielk1977       }
368c3f9bad2Sdanielk1977       assert(cc);
369c3f9bad2Sdanielk1977     }
370e0bc4048Sdrh     sqliteHashInsert(&(pParse->db->trigHash), zName, pName->n + 1, NULL);
371e0bc4048Sdrh     sqliteDeleteTrigger(pTrigger);
372c3f9bad2Sdanielk1977   }
373c3f9bad2Sdanielk1977 
374e0bc4048Sdrh   /* Generate code to destroy the database record of the trigger.
375e0bc4048Sdrh   */
376e0bc4048Sdrh   if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
377c3f9bad2Sdanielk1977     int base;
378c3f9bad2Sdanielk1977     static VdbeOp dropTrigger[] = {
379e0bc4048Sdrh       { OP_Rewind,     0, ADDR(8),  0},
380e0bc4048Sdrh       { OP_String,     0, 0,        0}, /* 1 */
381c3f9bad2Sdanielk1977       { OP_MemStore,   1, 1,        0},
382e0bc4048Sdrh       { OP_MemLoad,    1, 0,        0}, /* 3 */
383c3f9bad2Sdanielk1977       { OP_Column,     0, 1,        0},
384e0bc4048Sdrh       { OP_Ne,         0, ADDR(7),  0},
385c3f9bad2Sdanielk1977       { OP_Delete,     0, 0,        0},
386e0bc4048Sdrh       { OP_Next,       0, ADDR(3),  0}, /* 7 */
387c3f9bad2Sdanielk1977     };
388c3f9bad2Sdanielk1977 
389*cabb0819Sdrh     sqliteBeginWriteOperation(pParse, 0, 0);
390e0bc4048Sdrh     sqliteOpenMasterTable(v, pTable->isTemp);
391e0bc4048Sdrh     base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
392e0bc4048Sdrh     sqliteVdbeChangeP3(v, base+1, zName, 0);
393e0bc4048Sdrh     if( !pTable->isTemp ){
394e0bc4048Sdrh       sqliteChangeCookie(pParse->db, v);
395dc379456Sdrh     }
396e0bc4048Sdrh     sqliteVdbeAddOp(v, OP_Close, 0, 0);
397c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
398c3f9bad2Sdanielk1977   }
399c3f9bad2Sdanielk1977 
400633ed08dSdanielk1977   sqliteFree(zName);
401c3f9bad2Sdanielk1977 }
402c3f9bad2Sdanielk1977 
403c977f7f5Sdrh /*
404c977f7f5Sdrh ** pEList is the SET clause of an UPDATE statement.  Each entry
405c977f7f5Sdrh ** in pEList is of the format <id>=<expr>.  If any of the entries
406c977f7f5Sdrh ** in pEList have an <id> which matches an identifier in pIdList,
407c977f7f5Sdrh ** then return TRUE.  If pIdList==NULL, then it is considered a
408c977f7f5Sdrh ** wildcard that matches anything.  Likewise if pEList==NULL then
409c977f7f5Sdrh ** it matches anything so always return true.  Return false only
410c977f7f5Sdrh ** if there is no match.
411c977f7f5Sdrh */
412c977f7f5Sdrh static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
413ad2d8307Sdrh   int e;
414ad2d8307Sdrh   if( !pIdList || !pEList ) return 1;
415f29ce559Sdanielk1977   for(e=0; e<pEList->nExpr; e++){
416ad2d8307Sdrh     if( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
417f29ce559Sdanielk1977   }
418c3f9bad2Sdanielk1977   return 0;
419c3f9bad2Sdanielk1977 }
420c3f9bad2Sdanielk1977 
421c3f9bad2Sdanielk1977 /* A global variable that is TRUE if we should always set up temp tables for
422c3f9bad2Sdanielk1977  * for triggers, even if there are no triggers to code. This is used to test
423c3f9bad2Sdanielk1977  * how much overhead the triggers algorithm is causing.
424c3f9bad2Sdanielk1977  *
425c3f9bad2Sdanielk1977  * This flag can be set or cleared using the "trigger_overhead_test" pragma.
426c3f9bad2Sdanielk1977  * The pragma is not documented since it is not really part of the interface
427c3f9bad2Sdanielk1977  * to SQLite, just the test procedure.
428c3f9bad2Sdanielk1977 */
429c3f9bad2Sdanielk1977 int always_code_trigger_setup = 0;
430c3f9bad2Sdanielk1977 
431c3f9bad2Sdanielk1977 /*
432c3f9bad2Sdanielk1977  * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
433c3f9bad2Sdanielk1977  * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
434c3f9bad2Sdanielk1977  * found in the list specified as pTrigger.
435c3f9bad2Sdanielk1977  */
436c3f9bad2Sdanielk1977 int sqliteTriggersExist(
437c977f7f5Sdrh   Parse *pParse,          /* Used to check for recursive triggers */
438c977f7f5Sdrh   Trigger *pTrigger,      /* A list of triggers associated with a table */
439c3f9bad2Sdanielk1977   int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
440c3f9bad2Sdanielk1977   int tr_tm,              /* one of TK_BEFORE, TK_AFTER */
441c3f9bad2Sdanielk1977   int foreach,            /* one of TK_ROW or TK_STATEMENT */
442c977f7f5Sdrh   ExprList *pChanges      /* Columns that change in an UPDATE statement */
443c977f7f5Sdrh ){
444633ed08dSdanielk1977   Trigger * pTriggerCursor;
445c3f9bad2Sdanielk1977 
446633ed08dSdanielk1977   if( always_code_trigger_setup ){
447633ed08dSdanielk1977     return 1;
448633ed08dSdanielk1977   }
449c3f9bad2Sdanielk1977 
450633ed08dSdanielk1977   pTriggerCursor = pTrigger;
451633ed08dSdanielk1977   while( pTriggerCursor ){
452633ed08dSdanielk1977     if( pTriggerCursor->op == op &&
453633ed08dSdanielk1977 	pTriggerCursor->tr_tm == tr_tm &&
454633ed08dSdanielk1977 	pTriggerCursor->foreach == foreach &&
455633ed08dSdanielk1977 	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
456c3f9bad2Sdanielk1977       TriggerStack * ss;
457c3f9bad2Sdanielk1977       ss = pParse->trigStack;
458f29ce559Sdanielk1977       while( ss && ss->pTrigger != pTrigger ){
459f29ce559Sdanielk1977 	ss = ss->pNext;
460f29ce559Sdanielk1977       }
461c3f9bad2Sdanielk1977       if( !ss )return 1;
462c3f9bad2Sdanielk1977     }
463633ed08dSdanielk1977     pTriggerCursor = pTriggerCursor->pNext;
464c3f9bad2Sdanielk1977   }
465c3f9bad2Sdanielk1977 
466c3f9bad2Sdanielk1977   return 0;
467c3f9bad2Sdanielk1977 }
468c3f9bad2Sdanielk1977 
469c977f7f5Sdrh /*
470c977f7f5Sdrh ** Generate VDBE code for zero or more statements inside the body of a
471c977f7f5Sdrh ** trigger.
472c977f7f5Sdrh */
473c3f9bad2Sdanielk1977 static int codeTriggerProgram(
474c977f7f5Sdrh   Parse *pParse,            /* The parser context */
475c977f7f5Sdrh   TriggerStep *pStepList,   /* List of statements inside the trigger body */
476c977f7f5Sdrh   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */
477633ed08dSdanielk1977 ){
478633ed08dSdanielk1977   TriggerStep * pTriggerStep = pStepList;
479c3f9bad2Sdanielk1977   int orconf;
480c3f9bad2Sdanielk1977 
481633ed08dSdanielk1977   while( pTriggerStep ){
482c3f9bad2Sdanielk1977     int saveNTab = pParse->nTab;
483633ed08dSdanielk1977     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
484c3f9bad2Sdanielk1977     pParse->trigStack->orconf = orconf;
485633ed08dSdanielk1977     switch( pTriggerStep->op ){
486c3f9bad2Sdanielk1977       case TK_SELECT: {
4876f34903eSdanielk1977 	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
4886f34903eSdanielk1977 	assert(ss);
4896f34903eSdanielk1977 	assert(ss->pSrc);
4906f34903eSdanielk1977 	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
4916f34903eSdanielk1977 	sqliteSelectDelete(ss);
492c3f9bad2Sdanielk1977 	break;
493c3f9bad2Sdanielk1977       }
494c3f9bad2Sdanielk1977       case TK_UPDATE: {
495bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
496633ed08dSdanielk1977         sqliteUpdate(pParse, &pTriggerStep->target,
497633ed08dSdanielk1977 		sqliteExprListDup(pTriggerStep->pExprList),
498633ed08dSdanielk1977 		sqliteExprDup(pTriggerStep->pWhere), orconf);
499bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
500c3f9bad2Sdanielk1977         break;
501c3f9bad2Sdanielk1977       }
502c3f9bad2Sdanielk1977       case TK_INSERT: {
503633ed08dSdanielk1977         sqliteInsert(pParse, &pTriggerStep->target,
504633ed08dSdanielk1977         sqliteExprListDup(pTriggerStep->pExprList),
505633ed08dSdanielk1977         sqliteSelectDup(pTriggerStep->pSelect),
506633ed08dSdanielk1977         sqliteIdListDup(pTriggerStep->pIdList), orconf);
507c3f9bad2Sdanielk1977         break;
508c3f9bad2Sdanielk1977       }
509c3f9bad2Sdanielk1977       case TK_DELETE: {
510bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
511633ed08dSdanielk1977         sqliteDeleteFrom(pParse, &pTriggerStep->target,
512633ed08dSdanielk1977 	    sqliteExprDup(pTriggerStep->pWhere));
513bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
514c3f9bad2Sdanielk1977         break;
515c3f9bad2Sdanielk1977       }
516c3f9bad2Sdanielk1977       default:
517c3f9bad2Sdanielk1977         assert(0);
518c3f9bad2Sdanielk1977     }
519c3f9bad2Sdanielk1977     pParse->nTab = saveNTab;
520633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
521c3f9bad2Sdanielk1977   }
522c3f9bad2Sdanielk1977 
523c3f9bad2Sdanielk1977   return 0;
524c3f9bad2Sdanielk1977 }
525c3f9bad2Sdanielk1977 
526633ed08dSdanielk1977 /*
527633ed08dSdanielk1977 ** This is called to code FOR EACH ROW triggers.
528633ed08dSdanielk1977 **
529633ed08dSdanielk1977 ** When the code that this function generates is executed, the following
530633ed08dSdanielk1977 ** must be true:
531c977f7f5Sdrh **
532c977f7f5Sdrh ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
533c977f7f5Sdrh **    can be indices of cursors in temporary tables.  See below.)
534c977f7f5Sdrh **
535633ed08dSdanielk1977 ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
536633ed08dSdanielk1977 **    a temporary vdbe cursor (index newIdx) must be open and pointing at
537633ed08dSdanielk1977 **    a row containing values to be substituted for new.* expressions in the
538633ed08dSdanielk1977 **    trigger program(s).
539c977f7f5Sdrh **
540633ed08dSdanielk1977 ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
541633ed08dSdanielk1977 **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
542633ed08dSdanielk1977 **    a row containing values to be substituted for old.* expressions in the
543633ed08dSdanielk1977 **    trigger program(s).
544633ed08dSdanielk1977 **
545633ed08dSdanielk1977 */
546c3f9bad2Sdanielk1977 int sqliteCodeRowTrigger(
547c3f9bad2Sdanielk1977   Parse *pParse,       /* Parse context */
548c3f9bad2Sdanielk1977   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
549633ed08dSdanielk1977   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
550c3f9bad2Sdanielk1977   int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
551633ed08dSdanielk1977   Table *pTab,         /* The table to code triggers from */
552633ed08dSdanielk1977   int newIdx,          /* The indice of the "new" row to access */
553633ed08dSdanielk1977   int oldIdx,          /* The indice of the "old" row to access */
5546f34903eSdanielk1977   int orconf,          /* ON CONFLICT policy */
5556f34903eSdanielk1977   int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
556c977f7f5Sdrh ){
557c3f9bad2Sdanielk1977   Trigger * pTrigger;
558c3f9bad2Sdanielk1977   TriggerStack * pTriggerStack;
559c3f9bad2Sdanielk1977 
560c3f9bad2Sdanielk1977   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
561c3f9bad2Sdanielk1977   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
562c3f9bad2Sdanielk1977 
563633ed08dSdanielk1977   assert(newIdx != -1 || oldIdx != -1);
564c3f9bad2Sdanielk1977 
565633ed08dSdanielk1977   pTrigger = pTab->pTrigger;
566c3f9bad2Sdanielk1977   while( pTrigger ){
567c3f9bad2Sdanielk1977     int fire_this = 0;
568c3f9bad2Sdanielk1977 
569c3f9bad2Sdanielk1977     /* determine whether we should code this trigger */
570c3f9bad2Sdanielk1977     if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
571c3f9bad2Sdanielk1977         pTrigger->foreach == TK_ROW ){
572c3f9bad2Sdanielk1977       fire_this = 1;
573c3f9bad2Sdanielk1977       pTriggerStack = pParse->trigStack;
574c3f9bad2Sdanielk1977       while( pTriggerStack ){
575f29ce559Sdanielk1977         if( pTriggerStack->pTrigger == pTrigger ){
576f29ce559Sdanielk1977 	  fire_this = 0;
577f29ce559Sdanielk1977 	}
578c3f9bad2Sdanielk1977         pTriggerStack = pTriggerStack->pNext;
579c3f9bad2Sdanielk1977       }
580c3f9bad2Sdanielk1977       if( op == TK_UPDATE && pTrigger->pColumns &&
581f29ce559Sdanielk1977           !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
582c3f9bad2Sdanielk1977         fire_this = 0;
583c3f9bad2Sdanielk1977       }
584f29ce559Sdanielk1977     }
585c3f9bad2Sdanielk1977 
586e4697f5eSdrh     if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
587c3f9bad2Sdanielk1977       int endTrigger;
588ad3cab52Sdrh       SrcList dummyTablist;
589c3f9bad2Sdanielk1977       Expr * whenExpr;
590c3f9bad2Sdanielk1977 
591ad3cab52Sdrh       dummyTablist.nSrc = 0;
592c3f9bad2Sdanielk1977       dummyTablist.a = 0;
593c3f9bad2Sdanielk1977 
594c3f9bad2Sdanielk1977       /* Push an entry on to the trigger stack */
595c3f9bad2Sdanielk1977       pTriggerStack->pTrigger = pTrigger;
596633ed08dSdanielk1977       pTriggerStack->newIdx = newIdx;
597633ed08dSdanielk1977       pTriggerStack->oldIdx = oldIdx;
598633ed08dSdanielk1977       pTriggerStack->pTab = pTab;
599c3f9bad2Sdanielk1977       pTriggerStack->pNext = pParse->trigStack;
6006f34903eSdanielk1977       pTriggerStack->ignoreJump = ignoreJump;
601c3f9bad2Sdanielk1977       pParse->trigStack = pTriggerStack;
602c3f9bad2Sdanielk1977 
603c3f9bad2Sdanielk1977       /* code the WHEN clause */
604c3f9bad2Sdanielk1977       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
605c3f9bad2Sdanielk1977       whenExpr = sqliteExprDup(pTrigger->pWhen);
606c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
607c3f9bad2Sdanielk1977         pParse->trigStack = pParse->trigStack->pNext;
608c3f9bad2Sdanielk1977         sqliteFree(pTriggerStack);
609c3f9bad2Sdanielk1977         sqliteExprDelete(whenExpr);
610c3f9bad2Sdanielk1977         return 1;
611c3f9bad2Sdanielk1977       }
612f5905aa7Sdrh       sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
613c3f9bad2Sdanielk1977       sqliteExprDelete(whenExpr);
614c3f9bad2Sdanielk1977 
615633ed08dSdanielk1977       codeTriggerProgram(pParse, pTrigger->step_list, orconf);
616c3f9bad2Sdanielk1977 
617c3f9bad2Sdanielk1977       /* Pop the entry off the trigger stack */
618c3f9bad2Sdanielk1977       pParse->trigStack = pParse->trigStack->pNext;
619c3f9bad2Sdanielk1977       sqliteFree(pTriggerStack);
620c3f9bad2Sdanielk1977 
621c3f9bad2Sdanielk1977       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
622c3f9bad2Sdanielk1977     }
623c3f9bad2Sdanielk1977     pTrigger = pTrigger->pNext;
624c3f9bad2Sdanielk1977   }
625c3f9bad2Sdanielk1977 
626c3f9bad2Sdanielk1977   return 0;
627c3f9bad2Sdanielk1977 }
628c3f9bad2Sdanielk1977 
629c3f9bad2Sdanielk1977 /*
630633ed08dSdanielk1977  * This function is called to code ON UPDATE and ON DELETE triggers on
631633ed08dSdanielk1977  * views.
632633ed08dSdanielk1977  *
633633ed08dSdanielk1977  * This function deletes the data pointed at by the pWhere and pChanges
634633ed08dSdanielk1977  * arguments before it completes.
635c3f9bad2Sdanielk1977  */
636633ed08dSdanielk1977 void sqliteViewTriggers(
637633ed08dSdanielk1977   Parse *pParse,
638633ed08dSdanielk1977   Table *pTab,         /* The view to code triggers on */
639633ed08dSdanielk1977   Expr *pWhere,        /* The WHERE clause of the statement causing triggers*/
640633ed08dSdanielk1977   int orconf,          /* The ON CONFLICT policy specified as part of the
641633ed08dSdanielk1977 			  statement causing these triggers */
642633ed08dSdanielk1977   ExprList *pChanges   /* If this is an statement causing triggers to fire
643633ed08dSdanielk1977 			  is an UPDATE, then this list holds the columns
644633ed08dSdanielk1977 			  to update and the expressions to update them to.
645633ed08dSdanielk1977 			  See comments for sqliteUpdate(). */
646633ed08dSdanielk1977 ){
647c3f9bad2Sdanielk1977   int oldIdx = -1;
648c3f9bad2Sdanielk1977   int newIdx = -1;
649c3f9bad2Sdanielk1977   int *aXRef = 0;
650c3f9bad2Sdanielk1977   Vdbe *v;
651c3f9bad2Sdanielk1977   int endOfLoop;
652c3f9bad2Sdanielk1977   int startOfLoop;
653c3f9bad2Sdanielk1977   Select theSelect;
654c3f9bad2Sdanielk1977   Token tblNameToken;
655c3f9bad2Sdanielk1977 
656c3f9bad2Sdanielk1977   assert(pTab->pSelect);
657c3f9bad2Sdanielk1977 
658c3f9bad2Sdanielk1977   tblNameToken.z = pTab->zName;
659c3f9bad2Sdanielk1977   tblNameToken.n = strlen(pTab->zName);
660c3f9bad2Sdanielk1977 
661c3f9bad2Sdanielk1977   theSelect.isDistinct = 0;
662c3f9bad2Sdanielk1977   theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
663ad3cab52Sdrh   theSelect.pSrc   = sqliteSrcListAppend(0, &tblNameToken);
664c3f9bad2Sdanielk1977   theSelect.pWhere = pWhere;    pWhere = 0;
665c3f9bad2Sdanielk1977   theSelect.pGroupBy = 0;
666c3f9bad2Sdanielk1977   theSelect.pHaving = 0;
667c3f9bad2Sdanielk1977   theSelect.pOrderBy = 0;
668c3f9bad2Sdanielk1977   theSelect.op = TK_SELECT; /* ?? */
669c3f9bad2Sdanielk1977   theSelect.pPrior = 0;
670c3f9bad2Sdanielk1977   theSelect.nLimit = -1;
671c3f9bad2Sdanielk1977   theSelect.nOffset = -1;
672c3f9bad2Sdanielk1977   theSelect.zSelect = 0;
673c3f9bad2Sdanielk1977   theSelect.base = 0;
674c3f9bad2Sdanielk1977 
675c3f9bad2Sdanielk1977   v = sqliteGetVdbe(pParse);
676c3f9bad2Sdanielk1977   assert(v);
677*cabb0819Sdrh   sqliteBeginWriteOperation(pParse, 1, 0);
678c3f9bad2Sdanielk1977 
679c3f9bad2Sdanielk1977   /* Allocate temp tables */
680c3f9bad2Sdanielk1977   oldIdx = pParse->nTab++;
681c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
682c3f9bad2Sdanielk1977   if( pChanges ){
683c3f9bad2Sdanielk1977     newIdx = pParse->nTab++;
684c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
685c3f9bad2Sdanielk1977   }
686c3f9bad2Sdanielk1977 
687c3f9bad2Sdanielk1977   /* Snapshot the view */
688c3f9bad2Sdanielk1977   if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){
689c3f9bad2Sdanielk1977     goto trigger_cleanup;
690c3f9bad2Sdanielk1977   }
691c3f9bad2Sdanielk1977 
692c3f9bad2Sdanielk1977   /* loop thru the view snapshot, executing triggers for each row */
693c3f9bad2Sdanielk1977   endOfLoop = sqliteVdbeMakeLabel(v);
694c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);
695c3f9bad2Sdanielk1977 
696c3f9bad2Sdanielk1977   /* Loop thru the view snapshot, executing triggers for each row */
697c3f9bad2Sdanielk1977   startOfLoop = sqliteVdbeCurrentAddr(v);
698c3f9bad2Sdanielk1977 
699c3f9bad2Sdanielk1977   /* Build the updated row if required */
700c3f9bad2Sdanielk1977   if( pChanges ){
7011d1f3055Sdrh     int ii;
702c3f9bad2Sdanielk1977 
703c3f9bad2Sdanielk1977     aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
704c3f9bad2Sdanielk1977     if( aXRef==0 ) goto trigger_cleanup;
705633ed08dSdanielk1977     for(ii = 0; ii < pTab->nCol; ii++){
706c3f9bad2Sdanielk1977       aXRef[ii] = -1;
707633ed08dSdanielk1977     }
708c3f9bad2Sdanielk1977 
709c3f9bad2Sdanielk1977     for(ii=0; ii<pChanges->nExpr; ii++){
710c3f9bad2Sdanielk1977       int jj;
711c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0,
712f29ce559Sdanielk1977             pChanges->a[ii].pExpr) ){
713c3f9bad2Sdanielk1977         goto trigger_cleanup;
714f29ce559Sdanielk1977       }
715c3f9bad2Sdanielk1977 
716c3f9bad2Sdanielk1977       if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
717c3f9bad2Sdanielk1977         goto trigger_cleanup;
718c3f9bad2Sdanielk1977 
719c3f9bad2Sdanielk1977       for(jj=0; jj<pTab->nCol; jj++){
720c3f9bad2Sdanielk1977         if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
721c3f9bad2Sdanielk1977           aXRef[jj] = ii;
722c3f9bad2Sdanielk1977           break;
723c3f9bad2Sdanielk1977         }
724c3f9bad2Sdanielk1977       }
725c3f9bad2Sdanielk1977       if( jj>=pTab->nCol ){
726c3f9bad2Sdanielk1977         sqliteSetString(&pParse->zErrMsg, "no such column: ",
727c3f9bad2Sdanielk1977             pChanges->a[ii].zName, 0);
728c3f9bad2Sdanielk1977         pParse->nErr++;
729c3f9bad2Sdanielk1977         goto trigger_cleanup;
730c3f9bad2Sdanielk1977       }
731c3f9bad2Sdanielk1977     }
732c3f9bad2Sdanielk1977 
733c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Integer, 13, 0);
734c3f9bad2Sdanielk1977 
735633ed08dSdanielk1977     for(ii = 0; ii<pTab->nCol; ii++){
736633ed08dSdanielk1977       if( aXRef[ii] < 0 ){
737c3f9bad2Sdanielk1977         sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
738633ed08dSdanielk1977       }else{
739c3f9bad2Sdanielk1977         sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
740633ed08dSdanielk1977       }
741633ed08dSdanielk1977     }
742c3f9bad2Sdanielk1977 
743c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
744c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
745c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
746c3f9bad2Sdanielk1977 
747c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
7486f34903eSdanielk1977         pTab, newIdx, oldIdx, orconf, endOfLoop);
749c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
7506f34903eSdanielk1977         pTab, newIdx, oldIdx, orconf, endOfLoop);
751c3f9bad2Sdanielk1977   }else{
752c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
7536f34903eSdanielk1977         orconf, endOfLoop);
754c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
7556f34903eSdanielk1977         orconf, endOfLoop);
756c3f9bad2Sdanielk1977   }
757c3f9bad2Sdanielk1977 
758c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
759c3f9bad2Sdanielk1977 
760c3f9bad2Sdanielk1977   sqliteVdbeResolveLabel(v, endOfLoop);
761c3f9bad2Sdanielk1977   sqliteEndWriteOperation(pParse);
762c3f9bad2Sdanielk1977 
763c3f9bad2Sdanielk1977 trigger_cleanup:
764c3f9bad2Sdanielk1977   sqliteFree(aXRef);
765c3f9bad2Sdanielk1977   sqliteExprListDelete(pChanges);
766c3f9bad2Sdanielk1977   sqliteExprDelete(pWhere);
767c3f9bad2Sdanielk1977   sqliteExprListDelete(theSelect.pEList);
768ad3cab52Sdrh   sqliteSrcListDelete(theSelect.pSrc);
769c3f9bad2Sdanielk1977   sqliteExprDelete(theSelect.pWhere);
770c3f9bad2Sdanielk1977   return;
771c3f9bad2Sdanielk1977 }
772