xref: /sqlite-3.40.0/src/trigger.c (revision 77ad4e41)
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;
52e5f9c644Sdrh   char *zName = 0;    /* Name of the trigger */
53ed6c8671Sdrh 
54c3f9bad2Sdanielk1977   /* Check that:
559adf9ac4Sdrh   ** 1. the trigger name does not already exist.
569adf9ac4Sdrh   ** 2. the table (or view) does exist.
57d702fccbSdanielk1977   ** 3. that we are not trying to create a trigger on the sqlite_master table
58d702fccbSdanielk1977   ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
59d702fccbSdanielk1977   ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
60c3f9bad2Sdanielk1977   */
61e5f9c644Sdrh   zName = sqliteStrNDup(pName->z, pName->n);
62e5f9c644Sdrh   if( sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1) ){
63c3f9bad2Sdanielk1977     sqliteSetNString(&pParse->zErrMsg, "trigger ", -1,
64633ed08dSdanielk1977         pName->z, pName->n, " already exists", -1, 0);
65c3f9bad2Sdanielk1977     pParse->nErr++;
66c3f9bad2Sdanielk1977     goto trigger_cleanup;
67c3f9bad2Sdanielk1977   }
68c3f9bad2Sdanielk1977   {
69633ed08dSdanielk1977     char *tmp_str = sqliteStrNDup(pTableName->z, pTableName->n);
70e4697f5eSdrh     if( tmp_str==0 ) goto trigger_cleanup;
71c3f9bad2Sdanielk1977     tab = sqliteFindTable(pParse->db, tmp_str);
72c3f9bad2Sdanielk1977     sqliteFree(tmp_str);
73c3f9bad2Sdanielk1977     if( !tab ){
74c3f9bad2Sdanielk1977       sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1,
75633ed08dSdanielk1977           pTableName->z, pTableName->n, 0);
76c3f9bad2Sdanielk1977       pParse->nErr++;
77c3f9bad2Sdanielk1977       goto trigger_cleanup;
78c3f9bad2Sdanielk1977     }
791873cd50Sdrh     if( sqliteStrICmp(tab->zName, MASTER_NAME)==0 ){
801873cd50Sdrh       sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
811873cd50Sdrh          "table: " MASTER_NAME, 0);
821873cd50Sdrh       pParse->nErr++;
831873cd50Sdrh       goto trigger_cleanup;
841873cd50Sdrh     }
85e0bc4048Sdrh     if( sqliteStrICmp(tab->zName, TEMP_MASTER_NAME)==0 ){
86e0bc4048Sdrh       sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
87e0bc4048Sdrh          "table: " TEMP_MASTER_NAME, 0);
88e0bc4048Sdrh       pParse->nErr++;
89e0bc4048Sdrh       goto trigger_cleanup;
90e0bc4048Sdrh     }
91d702fccbSdanielk1977     if( tab->pSelect && tr_tm != TK_INSTEAD ){
92d702fccbSdanielk1977       sqliteSetNString(&pParse->zErrMsg, "cannot create ", -1,
93d702fccbSdanielk1977 	  (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", -1, " trigger on view: ", -1
94d702fccbSdanielk1977           , pTableName->z, pTableName->n, 0);
95d702fccbSdanielk1977       goto trigger_cleanup;
96d702fccbSdanielk1977     }
97d702fccbSdanielk1977     if( !tab->pSelect && tr_tm == TK_INSTEAD ){
98d702fccbSdanielk1977       sqliteSetNString(&pParse->zErrMsg, "cannot create INSTEAD OF", -1,
99d702fccbSdanielk1977 	  " trigger on table: ", -1, pTableName->z, pTableName->n, 0);
100d702fccbSdanielk1977       goto trigger_cleanup;
101d702fccbSdanielk1977     }
102e5f9c644Sdrh #ifndef SQLITE_OMIT_AUTHORIZATION
103e5f9c644Sdrh     {
104e5f9c644Sdrh       int code = SQLITE_CREATE_TRIGGER;
105e5f9c644Sdrh       if( tab->isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
106e5f9c644Sdrh       if( sqliteAuthCheck(pParse, code, zName, tab->zName) ){
107ed6c8671Sdrh         goto trigger_cleanup;
108ed6c8671Sdrh       }
109*77ad4e41Sdrh       if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->isTemp), 0)){
110*77ad4e41Sdrh         goto trigger_cleanup;
111*77ad4e41Sdrh       }
112d702fccbSdanielk1977     }
113e5f9c644Sdrh #endif
114e5f9c644Sdrh   }
115d702fccbSdanielk1977 
116d702fccbSdanielk1977   if (tr_tm == TK_INSTEAD){
117d702fccbSdanielk1977     tr_tm = TK_BEFORE;
118c3f9bad2Sdanielk1977   }
119c3f9bad2Sdanielk1977 
120c3f9bad2Sdanielk1977   /* Build the Trigger object */
121c3f9bad2Sdanielk1977   nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
122e4697f5eSdrh   if( nt==0 ) goto trigger_cleanup;
123e5f9c644Sdrh   nt->name = zName;
124e5f9c644Sdrh   zName = 0;
125633ed08dSdanielk1977   nt->table = sqliteStrNDup(pTableName->z, pTableName->n);
126e4697f5eSdrh   if( sqlite_malloc_failed ) goto trigger_cleanup;
127c3f9bad2Sdanielk1977   nt->op = op;
128c3f9bad2Sdanielk1977   nt->tr_tm = tr_tm;
1294b59ab5eSdrh   nt->pWhen = sqliteExprDup(pWhen);
1304b59ab5eSdrh   sqliteExprDelete(pWhen);
1314b59ab5eSdrh   nt->pColumns = sqliteIdListDup(pColumns);
1324b59ab5eSdrh   sqliteIdListDelete(pColumns);
133c3f9bad2Sdanielk1977   nt->foreach = foreach;
134633ed08dSdanielk1977   nt->step_list = pStepList;
135c3f9bad2Sdanielk1977 
136c3f9bad2Sdanielk1977   /* if we are not initializing, and this trigger is not on a TEMP table,
1379adf9ac4Sdrh   ** build the sqlite_master entry
1389adf9ac4Sdrh   */
139e0bc4048Sdrh   if( !pParse->initFlag ){
140c977f7f5Sdrh     static VdbeOp insertTrig[] = {
141c977f7f5Sdrh       { OP_NewRecno,   0, 0,  0          },
142c977f7f5Sdrh       { OP_String,     0, 0,  "trigger"  },
143e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 2: trigger name */
144e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 3: table name */
145c977f7f5Sdrh       { OP_Integer,    0, 0,  0          },
146e0bc4048Sdrh       { OP_String,     0, 0,  0          },  /* 5: SQL */
147c977f7f5Sdrh       { OP_MakeRecord, 5, 0,  0          },
148c977f7f5Sdrh       { OP_PutIntKey,  0, 0,  0          },
149c977f7f5Sdrh     };
150c977f7f5Sdrh     int addr;
151c977f7f5Sdrh     Vdbe *v;
152c3f9bad2Sdanielk1977 
153c3f9bad2Sdanielk1977     /* Make an entry in the sqlite_master table */
154c977f7f5Sdrh     v = sqliteGetVdbe(pParse);
155e4697f5eSdrh     if( v==0 ) goto trigger_cleanup;
156cabb0819Sdrh     sqliteBeginWriteOperation(pParse, 0, 0);
157e0bc4048Sdrh     sqliteOpenMasterTable(v, tab->isTemp);
158c977f7f5Sdrh     addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
159e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME,
160e0bc4048Sdrh                        P3_STATIC);
161e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
162e0bc4048Sdrh     sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
1634b59ab5eSdrh     sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
164e0bc4048Sdrh     if( !tab->isTemp ){
165e0bc4048Sdrh       sqliteChangeCookie(pParse->db, v);
166e0bc4048Sdrh     }
167e0bc4048Sdrh     sqliteVdbeAddOp(v, OP_Close, 0, 0);
168c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
169c3f9bad2Sdanielk1977   }
170c3f9bad2Sdanielk1977 
171c3f9bad2Sdanielk1977   if( !pParse->explain ){
172c3f9bad2Sdanielk1977     /* Stick it in the hash-table */
173633ed08dSdanielk1977     sqliteHashInsert(&(pParse->db->trigHash), nt->name, pName->n + 1, nt);
174c3f9bad2Sdanielk1977 
175c3f9bad2Sdanielk1977     /* Attach it to the table object */
176c3f9bad2Sdanielk1977     nt->pNext = tab->pTrigger;
177c3f9bad2Sdanielk1977     tab->pTrigger = nt;
178c3f9bad2Sdanielk1977     return;
179c3f9bad2Sdanielk1977   }else{
180c3f9bad2Sdanielk1977     sqliteFree(nt->name);
181c3f9bad2Sdanielk1977     sqliteFree(nt->table);
182c3f9bad2Sdanielk1977     sqliteFree(nt);
183c3f9bad2Sdanielk1977   }
184c3f9bad2Sdanielk1977 
185c3f9bad2Sdanielk1977 trigger_cleanup:
186c3f9bad2Sdanielk1977 
187e5f9c644Sdrh   sqliteFree(zName);
188633ed08dSdanielk1977   sqliteIdListDelete(pColumns);
189c3f9bad2Sdanielk1977   sqliteExprDelete(pWhen);
1904b59ab5eSdrh   sqliteDeleteTriggerStep(pStepList);
191c3f9bad2Sdanielk1977 }
1924b59ab5eSdrh 
1934b59ab5eSdrh /*
1944b59ab5eSdrh ** Make a copy of all components of the given trigger step.  This has
1954b59ab5eSdrh ** the effect of copying all Expr.token.z values into memory obtained
1964b59ab5eSdrh ** from sqliteMalloc().  As initially created, the Expr.token.z values
1974b59ab5eSdrh ** all point to the input string that was fed to the parser.  But that
1984b59ab5eSdrh ** string is ephemeral - it will go away as soon as the sqlite_exec()
1994b59ab5eSdrh ** call that started the parser exits.  This routine makes a persistent
2004b59ab5eSdrh ** copy of all the Expr.token.z strings so that the TriggerStep structure
2014b59ab5eSdrh ** will be valid even after the sqlite_exec() call returns.
2024b59ab5eSdrh */
2034b59ab5eSdrh static void sqlitePersistTriggerStep(TriggerStep *p){
2044b59ab5eSdrh   if( p->target.z ){
2054b59ab5eSdrh     p->target.z = sqliteStrNDup(p->target.z, p->target.n);
2064b59ab5eSdrh     p->target.dyn = 1;
2074b59ab5eSdrh   }
2084b59ab5eSdrh   if( p->pSelect ){
2094b59ab5eSdrh     Select *pNew = sqliteSelectDup(p->pSelect);
2104b59ab5eSdrh     sqliteSelectDelete(p->pSelect);
2114b59ab5eSdrh     p->pSelect = pNew;
2124b59ab5eSdrh   }
2134b59ab5eSdrh   if( p->pWhere ){
2144b59ab5eSdrh     Expr *pNew = sqliteExprDup(p->pWhere);
2154b59ab5eSdrh     sqliteExprDelete(p->pWhere);
2164b59ab5eSdrh     p->pWhere = pNew;
2174b59ab5eSdrh   }
2184b59ab5eSdrh   if( p->pExprList ){
2194b59ab5eSdrh     ExprList *pNew = sqliteExprListDup(p->pExprList);
2204b59ab5eSdrh     sqliteExprListDelete(p->pExprList);
2214b59ab5eSdrh     p->pExprList = pNew;
2224b59ab5eSdrh   }
2234b59ab5eSdrh   if( p->pIdList ){
2244b59ab5eSdrh     IdList *pNew = sqliteIdListDup(p->pIdList);
2254b59ab5eSdrh     sqliteIdListDelete(p->pIdList);
2264b59ab5eSdrh     p->pIdList = pNew;
227c3f9bad2Sdanielk1977   }
228c3f9bad2Sdanielk1977 }
229c3f9bad2Sdanielk1977 
230c977f7f5Sdrh /*
231c977f7f5Sdrh ** Turn a SELECT statement (that the pSelect parameter points to) into
232c977f7f5Sdrh ** a trigger step.  Return a pointer to a TriggerStep structure.
233c977f7f5Sdrh **
234c977f7f5Sdrh ** The parser calls this routine when it finds a SELECT statement in
235c977f7f5Sdrh ** body of a TRIGGER.
236c977f7f5Sdrh */
237c977f7f5Sdrh TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
238633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
239e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
240c3f9bad2Sdanielk1977 
241633ed08dSdanielk1977   pTriggerStep->op = TK_SELECT;
242633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
243633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
2444b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
245c3f9bad2Sdanielk1977 
246633ed08dSdanielk1977   return pTriggerStep;
247c3f9bad2Sdanielk1977 }
248c3f9bad2Sdanielk1977 
249c977f7f5Sdrh /*
250c977f7f5Sdrh ** Build a trigger step out of an INSERT statement.  Return a pointer
251c977f7f5Sdrh ** to the new trigger step.
252c977f7f5Sdrh **
253c977f7f5Sdrh ** The parser calls this routine when it sees an INSERT inside the
254c977f7f5Sdrh ** body of a trigger.
255c977f7f5Sdrh */
256633ed08dSdanielk1977 TriggerStep *sqliteTriggerInsertStep(
257c977f7f5Sdrh   Token *pTableName,  /* Name of the table into which we insert */
258c977f7f5Sdrh   IdList *pColumn,    /* List of columns in pTableName to insert into */
259c977f7f5Sdrh   ExprList *pEList,   /* The VALUE clause: a list of values to be inserted */
260c977f7f5Sdrh   Select *pSelect,    /* A SELECT statement that supplies values */
261c977f7f5Sdrh   int orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
262633ed08dSdanielk1977 ){
263633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
264e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
265c3f9bad2Sdanielk1977 
266633ed08dSdanielk1977   assert(pEList == 0 || pSelect == 0);
267633ed08dSdanielk1977   assert(pEList != 0 || pSelect != 0);
268c3f9bad2Sdanielk1977 
269633ed08dSdanielk1977   pTriggerStep->op = TK_INSERT;
270633ed08dSdanielk1977   pTriggerStep->pSelect = pSelect;
271633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
272633ed08dSdanielk1977   pTriggerStep->pIdList = pColumn;
273633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
274633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
2754b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
276c3f9bad2Sdanielk1977 
277633ed08dSdanielk1977   return pTriggerStep;
278c3f9bad2Sdanielk1977 }
279c3f9bad2Sdanielk1977 
280c977f7f5Sdrh /*
281c977f7f5Sdrh ** Construct a trigger step that implements an UPDATE statement and return
282c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
283c977f7f5Sdrh ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
284c977f7f5Sdrh */
285633ed08dSdanielk1977 TriggerStep *sqliteTriggerUpdateStep(
286c977f7f5Sdrh   Token *pTableName,   /* Name of the table to be updated */
287c977f7f5Sdrh   ExprList *pEList,    /* The SET clause: list of column and new values */
288c977f7f5Sdrh   Expr *pWhere,        /* The WHERE clause */
289c977f7f5Sdrh   int orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
290c977f7f5Sdrh ){
291633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
292e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
293c3f9bad2Sdanielk1977 
294633ed08dSdanielk1977   pTriggerStep->op = TK_UPDATE;
295633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
296633ed08dSdanielk1977   pTriggerStep->pExprList = pEList;
297633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
298633ed08dSdanielk1977   pTriggerStep->orconf = orconf;
2994b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
300c3f9bad2Sdanielk1977 
301633ed08dSdanielk1977   return pTriggerStep;
302c3f9bad2Sdanielk1977 }
303c3f9bad2Sdanielk1977 
304c977f7f5Sdrh /*
305c977f7f5Sdrh ** Construct a trigger step that implements a DELETE statement and return
306c977f7f5Sdrh ** a pointer to that trigger step.  The parser calls this routine when it
307c977f7f5Sdrh ** sees a DELETE statement inside the body of a CREATE TRIGGER.
308c977f7f5Sdrh */
309c977f7f5Sdrh TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
310633ed08dSdanielk1977   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
311e4697f5eSdrh   if( pTriggerStep==0 ) return 0;
312c3f9bad2Sdanielk1977 
313633ed08dSdanielk1977   pTriggerStep->op = TK_DELETE;
314633ed08dSdanielk1977   pTriggerStep->target  = *pTableName;
315633ed08dSdanielk1977   pTriggerStep->pWhere = pWhere;
316633ed08dSdanielk1977   pTriggerStep->orconf = OE_Default;
3174b59ab5eSdrh   sqlitePersistTriggerStep(pTriggerStep);
318c3f9bad2Sdanielk1977 
319633ed08dSdanielk1977   return pTriggerStep;
320c3f9bad2Sdanielk1977 }
321c3f9bad2Sdanielk1977 
322c3f9bad2Sdanielk1977 /*
323633ed08dSdanielk1977 ** Recursively delete a Trigger structure
324c3f9bad2Sdanielk1977 */
3251d1f3055Sdrh void sqliteDeleteTrigger(Trigger *pTrigger){
3264b59ab5eSdrh   sqliteDeleteTriggerStep(pTrigger->step_list);
327633ed08dSdanielk1977   sqliteFree(pTrigger->name);
328633ed08dSdanielk1977   sqliteFree(pTrigger->table);
329633ed08dSdanielk1977   sqliteExprDelete(pTrigger->pWhen);
330633ed08dSdanielk1977   sqliteIdListDelete(pTrigger->pColumns);
331633ed08dSdanielk1977   sqliteFree(pTrigger);
332633ed08dSdanielk1977 }
333633ed08dSdanielk1977 
334633ed08dSdanielk1977 /*
335633ed08dSdanielk1977  * This function is called to drop a trigger from the database schema.
336633ed08dSdanielk1977  *
337633ed08dSdanielk1977  * This may be called directly from the parser, or from within
338633ed08dSdanielk1977  * sqliteDropTable(). In the latter case the "nested" argument is true.
339633ed08dSdanielk1977  *
340633ed08dSdanielk1977  * Note that this function does not delete the trigger entirely. Instead it
341633ed08dSdanielk1977  * removes it from the internal schema and places it in the trigDrop hash
342633ed08dSdanielk1977  * table. This is so that the trigger can be restored into the database schema
343633ed08dSdanielk1977  * if the transaction is rolled back.
344633ed08dSdanielk1977  */
345e0bc4048Sdrh void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){
346633ed08dSdanielk1977   char *zName;
347633ed08dSdanielk1977   Trigger *pTrigger;
348633ed08dSdanielk1977   Table   *pTable;
349e0bc4048Sdrh   Vdbe *v;
350633ed08dSdanielk1977 
351633ed08dSdanielk1977   zName = sqliteStrNDup(pName->z, pName->n);
352c3f9bad2Sdanielk1977 
353c3f9bad2Sdanielk1977   /* ensure that the trigger being dropped exists */
354633ed08dSdanielk1977   pTrigger = sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1);
355633ed08dSdanielk1977   if( !pTrigger ){
356c3f9bad2Sdanielk1977     sqliteSetNString(&pParse->zErrMsg, "no such trigger: ", -1,
357633ed08dSdanielk1977         zName, -1, 0);
358633ed08dSdanielk1977     sqliteFree(zName);
359c3f9bad2Sdanielk1977     return;
360c3f9bad2Sdanielk1977   }
361ed6c8671Sdrh   pTable = sqliteFindTable(pParse->db, pTrigger->table);
362ed6c8671Sdrh   assert(pTable);
363e5f9c644Sdrh #ifndef SQLITE_OMIT_AUTHORIZATION
364e5f9c644Sdrh   {
365e5f9c644Sdrh     int code = SQLITE_DROP_TRIGGER;
366e5f9c644Sdrh     if( pTable->isTemp ) code = SQLITE_DROP_TEMP_TRIGGER;
367e5f9c644Sdrh     if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName) ||
368e5f9c644Sdrh       sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->isTemp),0) ){
369ed6c8671Sdrh       sqliteFree(zName);
370ed6c8671Sdrh       return;
371ed6c8671Sdrh     }
372e5f9c644Sdrh   }
373e5f9c644Sdrh #endif
374c3f9bad2Sdanielk1977 
375c3f9bad2Sdanielk1977   /*
376e0bc4048Sdrh    * If this is not an "explain", then delete the trigger structure.
377c3f9bad2Sdanielk1977    */
378c3f9bad2Sdanielk1977   if( !pParse->explain ){
379633ed08dSdanielk1977     if( pTable->pTrigger == pTrigger ){
380633ed08dSdanielk1977       pTable->pTrigger = pTrigger->pNext;
381633ed08dSdanielk1977     }else{
382633ed08dSdanielk1977       Trigger *cc = pTable->pTrigger;
383c3f9bad2Sdanielk1977       while( cc ){
384633ed08dSdanielk1977         if( cc->pNext == pTrigger ){
385c3f9bad2Sdanielk1977           cc->pNext = cc->pNext->pNext;
386c3f9bad2Sdanielk1977           break;
387c3f9bad2Sdanielk1977         }
388c3f9bad2Sdanielk1977         cc = cc->pNext;
389c3f9bad2Sdanielk1977       }
390c3f9bad2Sdanielk1977       assert(cc);
391c3f9bad2Sdanielk1977     }
392e0bc4048Sdrh     sqliteHashInsert(&(pParse->db->trigHash), zName, pName->n + 1, NULL);
393e0bc4048Sdrh     sqliteDeleteTrigger(pTrigger);
394c3f9bad2Sdanielk1977   }
395c3f9bad2Sdanielk1977 
396e0bc4048Sdrh   /* Generate code to destroy the database record of the trigger.
397e0bc4048Sdrh   */
398e0bc4048Sdrh   if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
399c3f9bad2Sdanielk1977     int base;
400c3f9bad2Sdanielk1977     static VdbeOp dropTrigger[] = {
401e0bc4048Sdrh       { OP_Rewind,     0, ADDR(8),  0},
402e0bc4048Sdrh       { OP_String,     0, 0,        0}, /* 1 */
403c3f9bad2Sdanielk1977       { OP_MemStore,   1, 1,        0},
404e0bc4048Sdrh       { OP_MemLoad,    1, 0,        0}, /* 3 */
405c3f9bad2Sdanielk1977       { OP_Column,     0, 1,        0},
406e0bc4048Sdrh       { OP_Ne,         0, ADDR(7),  0},
407c3f9bad2Sdanielk1977       { OP_Delete,     0, 0,        0},
408e0bc4048Sdrh       { OP_Next,       0, ADDR(3),  0}, /* 7 */
409c3f9bad2Sdanielk1977     };
410c3f9bad2Sdanielk1977 
411cabb0819Sdrh     sqliteBeginWriteOperation(pParse, 0, 0);
412e0bc4048Sdrh     sqliteOpenMasterTable(v, pTable->isTemp);
413e0bc4048Sdrh     base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
414e0bc4048Sdrh     sqliteVdbeChangeP3(v, base+1, zName, 0);
415e0bc4048Sdrh     if( !pTable->isTemp ){
416e0bc4048Sdrh       sqliteChangeCookie(pParse->db, v);
417dc379456Sdrh     }
418e0bc4048Sdrh     sqliteVdbeAddOp(v, OP_Close, 0, 0);
419c3f9bad2Sdanielk1977     sqliteEndWriteOperation(pParse);
420c3f9bad2Sdanielk1977   }
421c3f9bad2Sdanielk1977 
422633ed08dSdanielk1977   sqliteFree(zName);
423c3f9bad2Sdanielk1977 }
424c3f9bad2Sdanielk1977 
425c977f7f5Sdrh /*
426c977f7f5Sdrh ** pEList is the SET clause of an UPDATE statement.  Each entry
427c977f7f5Sdrh ** in pEList is of the format <id>=<expr>.  If any of the entries
428c977f7f5Sdrh ** in pEList have an <id> which matches an identifier in pIdList,
429c977f7f5Sdrh ** then return TRUE.  If pIdList==NULL, then it is considered a
430c977f7f5Sdrh ** wildcard that matches anything.  Likewise if pEList==NULL then
431c977f7f5Sdrh ** it matches anything so always return true.  Return false only
432c977f7f5Sdrh ** if there is no match.
433c977f7f5Sdrh */
434c977f7f5Sdrh static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
435ad2d8307Sdrh   int e;
436ad2d8307Sdrh   if( !pIdList || !pEList ) return 1;
437f29ce559Sdanielk1977   for(e=0; e<pEList->nExpr; e++){
438ad2d8307Sdrh     if( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
439f29ce559Sdanielk1977   }
440c3f9bad2Sdanielk1977   return 0;
441c3f9bad2Sdanielk1977 }
442c3f9bad2Sdanielk1977 
443c3f9bad2Sdanielk1977 /* A global variable that is TRUE if we should always set up temp tables for
444c3f9bad2Sdanielk1977  * for triggers, even if there are no triggers to code. This is used to test
445c3f9bad2Sdanielk1977  * how much overhead the triggers algorithm is causing.
446c3f9bad2Sdanielk1977  *
447c3f9bad2Sdanielk1977  * This flag can be set or cleared using the "trigger_overhead_test" pragma.
448c3f9bad2Sdanielk1977  * The pragma is not documented since it is not really part of the interface
449c3f9bad2Sdanielk1977  * to SQLite, just the test procedure.
450c3f9bad2Sdanielk1977 */
451c3f9bad2Sdanielk1977 int always_code_trigger_setup = 0;
452c3f9bad2Sdanielk1977 
453c3f9bad2Sdanielk1977 /*
454c3f9bad2Sdanielk1977  * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
455c3f9bad2Sdanielk1977  * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
456c3f9bad2Sdanielk1977  * found in the list specified as pTrigger.
457c3f9bad2Sdanielk1977  */
458c3f9bad2Sdanielk1977 int sqliteTriggersExist(
459c977f7f5Sdrh   Parse *pParse,          /* Used to check for recursive triggers */
460c977f7f5Sdrh   Trigger *pTrigger,      /* A list of triggers associated with a table */
461c3f9bad2Sdanielk1977   int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
462c3f9bad2Sdanielk1977   int tr_tm,              /* one of TK_BEFORE, TK_AFTER */
463c3f9bad2Sdanielk1977   int foreach,            /* one of TK_ROW or TK_STATEMENT */
464c977f7f5Sdrh   ExprList *pChanges      /* Columns that change in an UPDATE statement */
465c977f7f5Sdrh ){
466633ed08dSdanielk1977   Trigger * pTriggerCursor;
467c3f9bad2Sdanielk1977 
468633ed08dSdanielk1977   if( always_code_trigger_setup ){
469633ed08dSdanielk1977     return 1;
470633ed08dSdanielk1977   }
471c3f9bad2Sdanielk1977 
472633ed08dSdanielk1977   pTriggerCursor = pTrigger;
473633ed08dSdanielk1977   while( pTriggerCursor ){
474633ed08dSdanielk1977     if( pTriggerCursor->op == op &&
475633ed08dSdanielk1977 	pTriggerCursor->tr_tm == tr_tm &&
476633ed08dSdanielk1977 	pTriggerCursor->foreach == foreach &&
477633ed08dSdanielk1977 	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
478c3f9bad2Sdanielk1977       TriggerStack * ss;
479c3f9bad2Sdanielk1977       ss = pParse->trigStack;
480f29ce559Sdanielk1977       while( ss && ss->pTrigger != pTrigger ){
481f29ce559Sdanielk1977 	ss = ss->pNext;
482f29ce559Sdanielk1977       }
483c3f9bad2Sdanielk1977       if( !ss )return 1;
484c3f9bad2Sdanielk1977     }
485633ed08dSdanielk1977     pTriggerCursor = pTriggerCursor->pNext;
486c3f9bad2Sdanielk1977   }
487c3f9bad2Sdanielk1977 
488c3f9bad2Sdanielk1977   return 0;
489c3f9bad2Sdanielk1977 }
490c3f9bad2Sdanielk1977 
491c977f7f5Sdrh /*
492c977f7f5Sdrh ** Generate VDBE code for zero or more statements inside the body of a
493c977f7f5Sdrh ** trigger.
494c977f7f5Sdrh */
495c3f9bad2Sdanielk1977 static int codeTriggerProgram(
496c977f7f5Sdrh   Parse *pParse,            /* The parser context */
497c977f7f5Sdrh   TriggerStep *pStepList,   /* List of statements inside the trigger body */
498c977f7f5Sdrh   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */
499633ed08dSdanielk1977 ){
500633ed08dSdanielk1977   TriggerStep * pTriggerStep = pStepList;
501c3f9bad2Sdanielk1977   int orconf;
502c3f9bad2Sdanielk1977 
503633ed08dSdanielk1977   while( pTriggerStep ){
504c3f9bad2Sdanielk1977     int saveNTab = pParse->nTab;
505633ed08dSdanielk1977     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
506c3f9bad2Sdanielk1977     pParse->trigStack->orconf = orconf;
507633ed08dSdanielk1977     switch( pTriggerStep->op ){
508c3f9bad2Sdanielk1977       case TK_SELECT: {
5096f34903eSdanielk1977 	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
5106f34903eSdanielk1977 	assert(ss);
5116f34903eSdanielk1977 	assert(ss->pSrc);
5126f34903eSdanielk1977 	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
5136f34903eSdanielk1977 	sqliteSelectDelete(ss);
514c3f9bad2Sdanielk1977 	break;
515c3f9bad2Sdanielk1977       }
516c3f9bad2Sdanielk1977       case TK_UPDATE: {
517bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
518633ed08dSdanielk1977         sqliteUpdate(pParse, &pTriggerStep->target,
519633ed08dSdanielk1977 		sqliteExprListDup(pTriggerStep->pExprList),
520633ed08dSdanielk1977 		sqliteExprDup(pTriggerStep->pWhere), orconf);
521bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
522c3f9bad2Sdanielk1977         break;
523c3f9bad2Sdanielk1977       }
524c3f9bad2Sdanielk1977       case TK_INSERT: {
525633ed08dSdanielk1977         sqliteInsert(pParse, &pTriggerStep->target,
526633ed08dSdanielk1977         sqliteExprListDup(pTriggerStep->pExprList),
527633ed08dSdanielk1977         sqliteSelectDup(pTriggerStep->pSelect),
528633ed08dSdanielk1977         sqliteIdListDup(pTriggerStep->pIdList), orconf);
529c3f9bad2Sdanielk1977         break;
530c3f9bad2Sdanielk1977       }
531c3f9bad2Sdanielk1977       case TK_DELETE: {
532bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
533633ed08dSdanielk1977         sqliteDeleteFrom(pParse, &pTriggerStep->target,
534633ed08dSdanielk1977 	    sqliteExprDup(pTriggerStep->pWhere));
535bd5a451dSdrh         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
536c3f9bad2Sdanielk1977         break;
537c3f9bad2Sdanielk1977       }
538c3f9bad2Sdanielk1977       default:
539c3f9bad2Sdanielk1977         assert(0);
540c3f9bad2Sdanielk1977     }
541c3f9bad2Sdanielk1977     pParse->nTab = saveNTab;
542633ed08dSdanielk1977     pTriggerStep = pTriggerStep->pNext;
543c3f9bad2Sdanielk1977   }
544c3f9bad2Sdanielk1977 
545c3f9bad2Sdanielk1977   return 0;
546c3f9bad2Sdanielk1977 }
547c3f9bad2Sdanielk1977 
548633ed08dSdanielk1977 /*
549633ed08dSdanielk1977 ** This is called to code FOR EACH ROW triggers.
550633ed08dSdanielk1977 **
551633ed08dSdanielk1977 ** When the code that this function generates is executed, the following
552633ed08dSdanielk1977 ** must be true:
553c977f7f5Sdrh **
554c977f7f5Sdrh ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
555c977f7f5Sdrh **    can be indices of cursors in temporary tables.  See below.)
556c977f7f5Sdrh **
557633ed08dSdanielk1977 ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
558633ed08dSdanielk1977 **    a temporary vdbe cursor (index newIdx) must be open and pointing at
559633ed08dSdanielk1977 **    a row containing values to be substituted for new.* expressions in the
560633ed08dSdanielk1977 **    trigger program(s).
561c977f7f5Sdrh **
562633ed08dSdanielk1977 ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
563633ed08dSdanielk1977 **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
564633ed08dSdanielk1977 **    a row containing values to be substituted for old.* expressions in the
565633ed08dSdanielk1977 **    trigger program(s).
566633ed08dSdanielk1977 **
567633ed08dSdanielk1977 */
568c3f9bad2Sdanielk1977 int sqliteCodeRowTrigger(
569c3f9bad2Sdanielk1977   Parse *pParse,       /* Parse context */
570c3f9bad2Sdanielk1977   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
571633ed08dSdanielk1977   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
572c3f9bad2Sdanielk1977   int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
573633ed08dSdanielk1977   Table *pTab,         /* The table to code triggers from */
574633ed08dSdanielk1977   int newIdx,          /* The indice of the "new" row to access */
575633ed08dSdanielk1977   int oldIdx,          /* The indice of the "old" row to access */
5766f34903eSdanielk1977   int orconf,          /* ON CONFLICT policy */
5776f34903eSdanielk1977   int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
578c977f7f5Sdrh ){
579c3f9bad2Sdanielk1977   Trigger * pTrigger;
580c3f9bad2Sdanielk1977   TriggerStack * pTriggerStack;
581c3f9bad2Sdanielk1977 
582c3f9bad2Sdanielk1977   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
583c3f9bad2Sdanielk1977   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
584c3f9bad2Sdanielk1977 
585633ed08dSdanielk1977   assert(newIdx != -1 || oldIdx != -1);
586c3f9bad2Sdanielk1977 
587633ed08dSdanielk1977   pTrigger = pTab->pTrigger;
588c3f9bad2Sdanielk1977   while( pTrigger ){
589c3f9bad2Sdanielk1977     int fire_this = 0;
590c3f9bad2Sdanielk1977 
591c3f9bad2Sdanielk1977     /* determine whether we should code this trigger */
592c3f9bad2Sdanielk1977     if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
593c3f9bad2Sdanielk1977         pTrigger->foreach == TK_ROW ){
594c3f9bad2Sdanielk1977       fire_this = 1;
595c3f9bad2Sdanielk1977       pTriggerStack = pParse->trigStack;
596c3f9bad2Sdanielk1977       while( pTriggerStack ){
597f29ce559Sdanielk1977         if( pTriggerStack->pTrigger == pTrigger ){
598f29ce559Sdanielk1977 	  fire_this = 0;
599f29ce559Sdanielk1977 	}
600c3f9bad2Sdanielk1977         pTriggerStack = pTriggerStack->pNext;
601c3f9bad2Sdanielk1977       }
602c3f9bad2Sdanielk1977       if( op == TK_UPDATE && pTrigger->pColumns &&
603f29ce559Sdanielk1977           !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
604c3f9bad2Sdanielk1977         fire_this = 0;
605c3f9bad2Sdanielk1977       }
606f29ce559Sdanielk1977     }
607c3f9bad2Sdanielk1977 
608e4697f5eSdrh     if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
609c3f9bad2Sdanielk1977       int endTrigger;
610ad3cab52Sdrh       SrcList dummyTablist;
611c3f9bad2Sdanielk1977       Expr * whenExpr;
612c3f9bad2Sdanielk1977 
613ad3cab52Sdrh       dummyTablist.nSrc = 0;
614c3f9bad2Sdanielk1977       dummyTablist.a = 0;
615c3f9bad2Sdanielk1977 
616c3f9bad2Sdanielk1977       /* Push an entry on to the trigger stack */
617c3f9bad2Sdanielk1977       pTriggerStack->pTrigger = pTrigger;
618633ed08dSdanielk1977       pTriggerStack->newIdx = newIdx;
619633ed08dSdanielk1977       pTriggerStack->oldIdx = oldIdx;
620633ed08dSdanielk1977       pTriggerStack->pTab = pTab;
621c3f9bad2Sdanielk1977       pTriggerStack->pNext = pParse->trigStack;
6226f34903eSdanielk1977       pTriggerStack->ignoreJump = ignoreJump;
623c3f9bad2Sdanielk1977       pParse->trigStack = pTriggerStack;
624c3f9bad2Sdanielk1977 
625c3f9bad2Sdanielk1977       /* code the WHEN clause */
626c3f9bad2Sdanielk1977       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
627c3f9bad2Sdanielk1977       whenExpr = sqliteExprDup(pTrigger->pWhen);
628c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
629c3f9bad2Sdanielk1977         pParse->trigStack = pParse->trigStack->pNext;
630c3f9bad2Sdanielk1977         sqliteFree(pTriggerStack);
631c3f9bad2Sdanielk1977         sqliteExprDelete(whenExpr);
632c3f9bad2Sdanielk1977         return 1;
633c3f9bad2Sdanielk1977       }
634f5905aa7Sdrh       sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
635c3f9bad2Sdanielk1977       sqliteExprDelete(whenExpr);
636c3f9bad2Sdanielk1977 
637633ed08dSdanielk1977       codeTriggerProgram(pParse, pTrigger->step_list, orconf);
638c3f9bad2Sdanielk1977 
639c3f9bad2Sdanielk1977       /* Pop the entry off the trigger stack */
640c3f9bad2Sdanielk1977       pParse->trigStack = pParse->trigStack->pNext;
641c3f9bad2Sdanielk1977       sqliteFree(pTriggerStack);
642c3f9bad2Sdanielk1977 
643c3f9bad2Sdanielk1977       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
644c3f9bad2Sdanielk1977     }
645c3f9bad2Sdanielk1977     pTrigger = pTrigger->pNext;
646c3f9bad2Sdanielk1977   }
647c3f9bad2Sdanielk1977 
648c3f9bad2Sdanielk1977   return 0;
649c3f9bad2Sdanielk1977 }
650c3f9bad2Sdanielk1977 
651c3f9bad2Sdanielk1977 /*
652633ed08dSdanielk1977  * This function is called to code ON UPDATE and ON DELETE triggers on
653633ed08dSdanielk1977  * views.
654633ed08dSdanielk1977  *
655633ed08dSdanielk1977  * This function deletes the data pointed at by the pWhere and pChanges
656633ed08dSdanielk1977  * arguments before it completes.
657c3f9bad2Sdanielk1977  */
658633ed08dSdanielk1977 void sqliteViewTriggers(
659633ed08dSdanielk1977   Parse *pParse,
660633ed08dSdanielk1977   Table *pTab,         /* The view to code triggers on */
661633ed08dSdanielk1977   Expr *pWhere,        /* The WHERE clause of the statement causing triggers*/
662633ed08dSdanielk1977   int orconf,          /* The ON CONFLICT policy specified as part of the
663633ed08dSdanielk1977 			  statement causing these triggers */
664633ed08dSdanielk1977   ExprList *pChanges   /* If this is an statement causing triggers to fire
665633ed08dSdanielk1977 			  is an UPDATE, then this list holds the columns
666633ed08dSdanielk1977 			  to update and the expressions to update them to.
667633ed08dSdanielk1977 			  See comments for sqliteUpdate(). */
668633ed08dSdanielk1977 ){
669c3f9bad2Sdanielk1977   int oldIdx = -1;
670c3f9bad2Sdanielk1977   int newIdx = -1;
671c3f9bad2Sdanielk1977   int *aXRef = 0;
672c3f9bad2Sdanielk1977   Vdbe *v;
673c3f9bad2Sdanielk1977   int endOfLoop;
674c3f9bad2Sdanielk1977   int startOfLoop;
675c3f9bad2Sdanielk1977   Select theSelect;
676c3f9bad2Sdanielk1977   Token tblNameToken;
677c3f9bad2Sdanielk1977 
678c3f9bad2Sdanielk1977   assert(pTab->pSelect);
679c3f9bad2Sdanielk1977 
680c3f9bad2Sdanielk1977   tblNameToken.z = pTab->zName;
681c3f9bad2Sdanielk1977   tblNameToken.n = strlen(pTab->zName);
682c3f9bad2Sdanielk1977 
683c3f9bad2Sdanielk1977   theSelect.isDistinct = 0;
684c3f9bad2Sdanielk1977   theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
685ad3cab52Sdrh   theSelect.pSrc   = sqliteSrcListAppend(0, &tblNameToken);
686c3f9bad2Sdanielk1977   theSelect.pWhere = pWhere;    pWhere = 0;
687c3f9bad2Sdanielk1977   theSelect.pGroupBy = 0;
688c3f9bad2Sdanielk1977   theSelect.pHaving = 0;
689c3f9bad2Sdanielk1977   theSelect.pOrderBy = 0;
690c3f9bad2Sdanielk1977   theSelect.op = TK_SELECT; /* ?? */
691c3f9bad2Sdanielk1977   theSelect.pPrior = 0;
692c3f9bad2Sdanielk1977   theSelect.nLimit = -1;
693c3f9bad2Sdanielk1977   theSelect.nOffset = -1;
694c3f9bad2Sdanielk1977   theSelect.zSelect = 0;
695c3f9bad2Sdanielk1977   theSelect.base = 0;
696c3f9bad2Sdanielk1977 
697c3f9bad2Sdanielk1977   v = sqliteGetVdbe(pParse);
698c3f9bad2Sdanielk1977   assert(v);
699cabb0819Sdrh   sqliteBeginWriteOperation(pParse, 1, 0);
700c3f9bad2Sdanielk1977 
701c3f9bad2Sdanielk1977   /* Allocate temp tables */
702c3f9bad2Sdanielk1977   oldIdx = pParse->nTab++;
703c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
704c3f9bad2Sdanielk1977   if( pChanges ){
705c3f9bad2Sdanielk1977     newIdx = pParse->nTab++;
706c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0);
707c3f9bad2Sdanielk1977   }
708c3f9bad2Sdanielk1977 
709c3f9bad2Sdanielk1977   /* Snapshot the view */
710c3f9bad2Sdanielk1977   if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){
711c3f9bad2Sdanielk1977     goto trigger_cleanup;
712c3f9bad2Sdanielk1977   }
713c3f9bad2Sdanielk1977 
714c3f9bad2Sdanielk1977   /* loop thru the view snapshot, executing triggers for each row */
715c3f9bad2Sdanielk1977   endOfLoop = sqliteVdbeMakeLabel(v);
716c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);
717c3f9bad2Sdanielk1977 
718c3f9bad2Sdanielk1977   /* Loop thru the view snapshot, executing triggers for each row */
719c3f9bad2Sdanielk1977   startOfLoop = sqliteVdbeCurrentAddr(v);
720c3f9bad2Sdanielk1977 
721c3f9bad2Sdanielk1977   /* Build the updated row if required */
722c3f9bad2Sdanielk1977   if( pChanges ){
7231d1f3055Sdrh     int ii;
724c3f9bad2Sdanielk1977 
725c3f9bad2Sdanielk1977     aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
726c3f9bad2Sdanielk1977     if( aXRef==0 ) goto trigger_cleanup;
727633ed08dSdanielk1977     for(ii = 0; ii < pTab->nCol; ii++){
728c3f9bad2Sdanielk1977       aXRef[ii] = -1;
729633ed08dSdanielk1977     }
730c3f9bad2Sdanielk1977 
731c3f9bad2Sdanielk1977     for(ii=0; ii<pChanges->nExpr; ii++){
732c3f9bad2Sdanielk1977       int jj;
733c3f9bad2Sdanielk1977       if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0,
734f29ce559Sdanielk1977             pChanges->a[ii].pExpr) ){
735c3f9bad2Sdanielk1977         goto trigger_cleanup;
736f29ce559Sdanielk1977       }
737c3f9bad2Sdanielk1977 
738c3f9bad2Sdanielk1977       if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
739c3f9bad2Sdanielk1977         goto trigger_cleanup;
740c3f9bad2Sdanielk1977 
741c3f9bad2Sdanielk1977       for(jj=0; jj<pTab->nCol; jj++){
742c3f9bad2Sdanielk1977         if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
743c3f9bad2Sdanielk1977           aXRef[jj] = ii;
744c3f9bad2Sdanielk1977           break;
745c3f9bad2Sdanielk1977         }
746c3f9bad2Sdanielk1977       }
747c3f9bad2Sdanielk1977       if( jj>=pTab->nCol ){
748c3f9bad2Sdanielk1977         sqliteSetString(&pParse->zErrMsg, "no such column: ",
749c3f9bad2Sdanielk1977             pChanges->a[ii].zName, 0);
750c3f9bad2Sdanielk1977         pParse->nErr++;
751c3f9bad2Sdanielk1977         goto trigger_cleanup;
752c3f9bad2Sdanielk1977       }
753c3f9bad2Sdanielk1977     }
754c3f9bad2Sdanielk1977 
755c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Integer, 13, 0);
756c3f9bad2Sdanielk1977 
757633ed08dSdanielk1977     for(ii = 0; ii<pTab->nCol; ii++){
758633ed08dSdanielk1977       if( aXRef[ii] < 0 ){
759c3f9bad2Sdanielk1977         sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
760633ed08dSdanielk1977       }else{
761c3f9bad2Sdanielk1977         sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
762633ed08dSdanielk1977       }
763633ed08dSdanielk1977     }
764c3f9bad2Sdanielk1977 
765c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
766c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
767c3f9bad2Sdanielk1977     sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
768c3f9bad2Sdanielk1977 
769c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
7706f34903eSdanielk1977         pTab, newIdx, oldIdx, orconf, endOfLoop);
771c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
7726f34903eSdanielk1977         pTab, newIdx, oldIdx, orconf, endOfLoop);
773c3f9bad2Sdanielk1977   }else{
774c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
7756f34903eSdanielk1977         orconf, endOfLoop);
776c3f9bad2Sdanielk1977     sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
7776f34903eSdanielk1977         orconf, endOfLoop);
778c3f9bad2Sdanielk1977   }
779c3f9bad2Sdanielk1977 
780c3f9bad2Sdanielk1977   sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
781c3f9bad2Sdanielk1977 
782c3f9bad2Sdanielk1977   sqliteVdbeResolveLabel(v, endOfLoop);
783c3f9bad2Sdanielk1977   sqliteEndWriteOperation(pParse);
784c3f9bad2Sdanielk1977 
785c3f9bad2Sdanielk1977 trigger_cleanup:
786c3f9bad2Sdanielk1977   sqliteFree(aXRef);
787c3f9bad2Sdanielk1977   sqliteExprListDelete(pChanges);
788c3f9bad2Sdanielk1977   sqliteExprDelete(pWhere);
789c3f9bad2Sdanielk1977   sqliteExprListDelete(theSelect.pEList);
790ad3cab52Sdrh   sqliteSrcListDelete(theSelect.pSrc);
791c3f9bad2Sdanielk1977   sqliteExprDelete(theSelect.pWhere);
792c3f9bad2Sdanielk1977   return;
793c3f9bad2Sdanielk1977 }
794