xref: /illumos-gate/usr/src/lib/libsqlite/src/trigger.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate **
37c478bd9Sstevel@tonic-gate ** The author disclaims copyright to this source code.  In place of
47c478bd9Sstevel@tonic-gate ** a legal notice, here is a blessing:
57c478bd9Sstevel@tonic-gate **
67c478bd9Sstevel@tonic-gate **    May you do good and not evil.
77c478bd9Sstevel@tonic-gate **    May you find forgiveness for yourself and forgive others.
87c478bd9Sstevel@tonic-gate **    May you share freely, never taking more than you give.
97c478bd9Sstevel@tonic-gate **
107c478bd9Sstevel@tonic-gate *************************************************************************
117c478bd9Sstevel@tonic-gate *
127c478bd9Sstevel@tonic-gate */
137c478bd9Sstevel@tonic-gate #include "sqliteInt.h"
147c478bd9Sstevel@tonic-gate 
157c478bd9Sstevel@tonic-gate /*
167c478bd9Sstevel@tonic-gate ** Delete a linked list of TriggerStep structures.
177c478bd9Sstevel@tonic-gate */
sqliteDeleteTriggerStep(TriggerStep * pTriggerStep)187c478bd9Sstevel@tonic-gate void sqliteDeleteTriggerStep(TriggerStep *pTriggerStep){
197c478bd9Sstevel@tonic-gate   while( pTriggerStep ){
207c478bd9Sstevel@tonic-gate     TriggerStep * pTmp = pTriggerStep;
217c478bd9Sstevel@tonic-gate     pTriggerStep = pTriggerStep->pNext;
227c478bd9Sstevel@tonic-gate 
237c478bd9Sstevel@tonic-gate     if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z);
247c478bd9Sstevel@tonic-gate     sqliteExprDelete(pTmp->pWhere);
257c478bd9Sstevel@tonic-gate     sqliteExprListDelete(pTmp->pExprList);
267c478bd9Sstevel@tonic-gate     sqliteSelectDelete(pTmp->pSelect);
277c478bd9Sstevel@tonic-gate     sqliteIdListDelete(pTmp->pIdList);
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate     sqliteFree(pTmp);
307c478bd9Sstevel@tonic-gate   }
317c478bd9Sstevel@tonic-gate }
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate ** This is called by the parser when it sees a CREATE TRIGGER statement
357c478bd9Sstevel@tonic-gate ** up to the point of the BEGIN before the trigger actions.  A Trigger
367c478bd9Sstevel@tonic-gate ** structure is generated based on the information available and stored
377c478bd9Sstevel@tonic-gate ** in pParse->pNewTrigger.  After the trigger actions have been parsed, the
387c478bd9Sstevel@tonic-gate ** sqliteFinishTrigger() function is called to complete the trigger
397c478bd9Sstevel@tonic-gate ** construction process.
407c478bd9Sstevel@tonic-gate */
sqliteBeginTrigger(Parse * pParse,Token * pName,int tr_tm,int op,IdList * pColumns,SrcList * pTableName,int foreach,Expr * pWhen,int isTemp)417c478bd9Sstevel@tonic-gate void sqliteBeginTrigger(
427c478bd9Sstevel@tonic-gate   Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
437c478bd9Sstevel@tonic-gate   Token *pName,       /* The name of the trigger */
447c478bd9Sstevel@tonic-gate   int tr_tm,          /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
457c478bd9Sstevel@tonic-gate   int op,             /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
467c478bd9Sstevel@tonic-gate   IdList *pColumns,   /* column list if this is an UPDATE OF trigger */
477c478bd9Sstevel@tonic-gate   SrcList *pTableName,/* The name of the table/view the trigger applies to */
487c478bd9Sstevel@tonic-gate   int foreach,        /* One of TK_ROW or TK_STATEMENT */
497c478bd9Sstevel@tonic-gate   Expr *pWhen,        /* WHEN clause */
507c478bd9Sstevel@tonic-gate   int isTemp          /* True if the TEMPORARY keyword is present */
517c478bd9Sstevel@tonic-gate ){
527c478bd9Sstevel@tonic-gate   Trigger *nt;
537c478bd9Sstevel@tonic-gate   Table   *tab;
547c478bd9Sstevel@tonic-gate   char *zName = 0;        /* Name of the trigger */
557c478bd9Sstevel@tonic-gate   sqlite *db = pParse->db;
567c478bd9Sstevel@tonic-gate   int iDb;                /* When database to store the trigger in */
577c478bd9Sstevel@tonic-gate   DbFixer sFix;
587c478bd9Sstevel@tonic-gate 
59*1da57d55SToomas Soome   /* Check that:
607c478bd9Sstevel@tonic-gate   ** 1. the trigger name does not already exist.
617c478bd9Sstevel@tonic-gate   ** 2. the table (or view) does exist in the same database as the trigger.
627c478bd9Sstevel@tonic-gate   ** 3. that we are not trying to create a trigger on the sqlite_master table
637c478bd9Sstevel@tonic-gate   ** 4. That we are not trying to create an INSTEAD OF trigger on a table.
647c478bd9Sstevel@tonic-gate   ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
657c478bd9Sstevel@tonic-gate   */
667c478bd9Sstevel@tonic-gate   if( sqlite_malloc_failed ) goto trigger_cleanup;
677c478bd9Sstevel@tonic-gate   assert( pTableName->nSrc==1 );
687c478bd9Sstevel@tonic-gate   if( db->init.busy
697c478bd9Sstevel@tonic-gate    && sqliteFixInit(&sFix, pParse, db->init.iDb, "trigger", pName)
707c478bd9Sstevel@tonic-gate    && sqliteFixSrcList(&sFix, pTableName)
717c478bd9Sstevel@tonic-gate   ){
727c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
737c478bd9Sstevel@tonic-gate   }
747c478bd9Sstevel@tonic-gate   tab = sqliteSrcListLookup(pParse, pTableName);
757c478bd9Sstevel@tonic-gate   if( !tab ){
767c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
777c478bd9Sstevel@tonic-gate   }
787c478bd9Sstevel@tonic-gate   iDb = isTemp ? 1 : tab->iDb;
797c478bd9Sstevel@tonic-gate   if( iDb>=2 && !db->init.busy ){
807c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
817c478bd9Sstevel@tonic-gate        "database %s", db->aDb[tab->iDb].zName);
827c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
837c478bd9Sstevel@tonic-gate   }
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate   zName = sqliteStrNDup(pName->z, pName->n);
867c478bd9Sstevel@tonic-gate   sqliteDequote(zName);
877c478bd9Sstevel@tonic-gate   if( sqliteHashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
887c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "trigger %T already exists", pName);
897c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
907c478bd9Sstevel@tonic-gate   }
917c478bd9Sstevel@tonic-gate   if( sqliteStrNICmp(tab->zName, "sqlite_", 7)==0 ){
927c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "cannot create trigger on system table");
937c478bd9Sstevel@tonic-gate     pParse->nErr++;
947c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
957c478bd9Sstevel@tonic-gate   }
967c478bd9Sstevel@tonic-gate   if( tab->pSelect && tr_tm != TK_INSTEAD ){
97*1da57d55SToomas Soome     sqliteErrorMsg(pParse, "cannot create %s trigger on view: %S",
987c478bd9Sstevel@tonic-gate         (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
997c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
1007c478bd9Sstevel@tonic-gate   }
1017c478bd9Sstevel@tonic-gate   if( !tab->pSelect && tr_tm == TK_INSTEAD ){
1027c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "cannot create INSTEAD OF"
1037c478bd9Sstevel@tonic-gate         " trigger on table: %S", pTableName, 0);
1047c478bd9Sstevel@tonic-gate     goto trigger_cleanup;
1057c478bd9Sstevel@tonic-gate   }
1067c478bd9Sstevel@tonic-gate #ifndef SQLITE_OMIT_AUTHORIZATION
1077c478bd9Sstevel@tonic-gate   {
1087c478bd9Sstevel@tonic-gate     int code = SQLITE_CREATE_TRIGGER;
1097c478bd9Sstevel@tonic-gate     const char *zDb = db->aDb[tab->iDb].zName;
1107c478bd9Sstevel@tonic-gate     const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
1117c478bd9Sstevel@tonic-gate     if( tab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
1127c478bd9Sstevel@tonic-gate     if( sqliteAuthCheck(pParse, code, zName, tab->zName, zDbTrig) ){
1137c478bd9Sstevel@tonic-gate       goto trigger_cleanup;
1147c478bd9Sstevel@tonic-gate     }
1157c478bd9Sstevel@tonic-gate     if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){
1167c478bd9Sstevel@tonic-gate       goto trigger_cleanup;
1177c478bd9Sstevel@tonic-gate     }
1187c478bd9Sstevel@tonic-gate   }
1197c478bd9Sstevel@tonic-gate #endif
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate   /* INSTEAD OF triggers can only appear on views and BEGIN triggers
1227c478bd9Sstevel@tonic-gate   ** cannot appear on views.  So we might as well translate every
1237c478bd9Sstevel@tonic-gate   ** INSTEAD OF trigger into a BEFORE trigger.  It simplifies code
1247c478bd9Sstevel@tonic-gate   ** elsewhere.
1257c478bd9Sstevel@tonic-gate   */
1267c478bd9Sstevel@tonic-gate   if (tr_tm == TK_INSTEAD){
1277c478bd9Sstevel@tonic-gate     tr_tm = TK_BEFORE;
1287c478bd9Sstevel@tonic-gate   }
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate   /* Build the Trigger object */
1317c478bd9Sstevel@tonic-gate   nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
1327c478bd9Sstevel@tonic-gate   if( nt==0 ) goto trigger_cleanup;
1337c478bd9Sstevel@tonic-gate   nt->name = zName;
1347c478bd9Sstevel@tonic-gate   zName = 0;
1357c478bd9Sstevel@tonic-gate   nt->table = sqliteStrDup(pTableName->a[0].zName);
1367c478bd9Sstevel@tonic-gate   if( sqlite_malloc_failed ) goto trigger_cleanup;
1377c478bd9Sstevel@tonic-gate   nt->iDb = iDb;
1387c478bd9Sstevel@tonic-gate   nt->iTabDb = tab->iDb;
1397c478bd9Sstevel@tonic-gate   nt->op = op;
1407c478bd9Sstevel@tonic-gate   nt->tr_tm = tr_tm;
1417c478bd9Sstevel@tonic-gate   nt->pWhen = sqliteExprDup(pWhen);
1427c478bd9Sstevel@tonic-gate   nt->pColumns = sqliteIdListDup(pColumns);
1437c478bd9Sstevel@tonic-gate   nt->foreach = foreach;
1447c478bd9Sstevel@tonic-gate   sqliteTokenCopy(&nt->nameToken,pName);
1457c478bd9Sstevel@tonic-gate   assert( pParse->pNewTrigger==0 );
1467c478bd9Sstevel@tonic-gate   pParse->pNewTrigger = nt;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate trigger_cleanup:
1497c478bd9Sstevel@tonic-gate   sqliteFree(zName);
1507c478bd9Sstevel@tonic-gate   sqliteSrcListDelete(pTableName);
1517c478bd9Sstevel@tonic-gate   sqliteIdListDelete(pColumns);
1527c478bd9Sstevel@tonic-gate   sqliteExprDelete(pWhen);
1537c478bd9Sstevel@tonic-gate }
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate /*
1567c478bd9Sstevel@tonic-gate ** This routine is called after all of the trigger actions have been parsed
1577c478bd9Sstevel@tonic-gate ** in order to complete the process of building the trigger.
1587c478bd9Sstevel@tonic-gate */
sqliteFinishTrigger(Parse * pParse,TriggerStep * pStepList,Token * pAll)1597c478bd9Sstevel@tonic-gate void sqliteFinishTrigger(
1607c478bd9Sstevel@tonic-gate   Parse *pParse,          /* Parser context */
1617c478bd9Sstevel@tonic-gate   TriggerStep *pStepList, /* The triggered program */
1627c478bd9Sstevel@tonic-gate   Token *pAll             /* Token that describes the complete CREATE TRIGGER */
1637c478bd9Sstevel@tonic-gate ){
1647c478bd9Sstevel@tonic-gate   Trigger *nt = 0;          /* The trigger whose construction is finishing up */
1657c478bd9Sstevel@tonic-gate   sqlite *db = pParse->db;  /* The database */
1667c478bd9Sstevel@tonic-gate   DbFixer sFix;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate   if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
1697c478bd9Sstevel@tonic-gate   nt = pParse->pNewTrigger;
1707c478bd9Sstevel@tonic-gate   pParse->pNewTrigger = 0;
1717c478bd9Sstevel@tonic-gate   nt->step_list = pStepList;
1727c478bd9Sstevel@tonic-gate   while( pStepList ){
1737c478bd9Sstevel@tonic-gate     pStepList->pTrig = nt;
1747c478bd9Sstevel@tonic-gate     pStepList = pStepList->pNext;
1757c478bd9Sstevel@tonic-gate   }
176*1da57d55SToomas Soome   if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken)
1777c478bd9Sstevel@tonic-gate           && sqliteFixTriggerStep(&sFix, nt->step_list) ){
1787c478bd9Sstevel@tonic-gate     goto triggerfinish_cleanup;
1797c478bd9Sstevel@tonic-gate   }
1807c478bd9Sstevel@tonic-gate 
181*1da57d55SToomas Soome   /* if we are not initializing, and this trigger is not on a TEMP table,
1827c478bd9Sstevel@tonic-gate   ** build the sqlite_master entry
1837c478bd9Sstevel@tonic-gate   */
1847c478bd9Sstevel@tonic-gate   if( !db->init.busy ){
1857c478bd9Sstevel@tonic-gate     static VdbeOpList insertTrig[] = {
1867c478bd9Sstevel@tonic-gate       { OP_NewRecno,   0, 0,  0          },
1877c478bd9Sstevel@tonic-gate       { OP_String,     0, 0,  "trigger"  },
1887c478bd9Sstevel@tonic-gate       { OP_String,     0, 0,  0          },  /* 2: trigger name */
1897c478bd9Sstevel@tonic-gate       { OP_String,     0, 0,  0          },  /* 3: table name */
1907c478bd9Sstevel@tonic-gate       { OP_Integer,    0, 0,  0          },
1917c478bd9Sstevel@tonic-gate       { OP_String,     0, 0,  0          },  /* 5: SQL */
1927c478bd9Sstevel@tonic-gate       { OP_MakeRecord, 5, 0,  0          },
1937c478bd9Sstevel@tonic-gate       { OP_PutIntKey,  0, 0,  0          },
1947c478bd9Sstevel@tonic-gate     };
1957c478bd9Sstevel@tonic-gate     int addr;
1967c478bd9Sstevel@tonic-gate     Vdbe *v;
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate     /* Make an entry in the sqlite_master table */
1997c478bd9Sstevel@tonic-gate     v = sqliteGetVdbe(pParse);
2007c478bd9Sstevel@tonic-gate     if( v==0 ) goto triggerfinish_cleanup;
2017c478bd9Sstevel@tonic-gate     sqliteBeginWriteOperation(pParse, 0, 0);
2027c478bd9Sstevel@tonic-gate     sqliteOpenMasterTable(v, nt->iDb);
2037c478bd9Sstevel@tonic-gate     addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
204*1da57d55SToomas Soome     sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
205*1da57d55SToomas Soome     sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
2067c478bd9Sstevel@tonic-gate     sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
2077c478bd9Sstevel@tonic-gate     if( nt->iDb==0 ){
2087c478bd9Sstevel@tonic-gate       sqliteChangeCookie(db, v);
2097c478bd9Sstevel@tonic-gate     }
2107c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_Close, 0, 0);
2117c478bd9Sstevel@tonic-gate     sqliteEndWriteOperation(pParse);
2127c478bd9Sstevel@tonic-gate   }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate   if( !pParse->explain ){
2157c478bd9Sstevel@tonic-gate     Table *pTab;
216*1da57d55SToomas Soome     sqliteHashInsert(&db->aDb[nt->iDb].trigHash,
2177c478bd9Sstevel@tonic-gate                      nt->name, strlen(nt->name)+1, nt);
2187c478bd9Sstevel@tonic-gate     pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
2197c478bd9Sstevel@tonic-gate     assert( pTab!=0 );
2207c478bd9Sstevel@tonic-gate     nt->pNext = pTab->pTrigger;
2217c478bd9Sstevel@tonic-gate     pTab->pTrigger = nt;
2227c478bd9Sstevel@tonic-gate     nt = 0;
2237c478bd9Sstevel@tonic-gate   }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate triggerfinish_cleanup:
2267c478bd9Sstevel@tonic-gate   sqliteDeleteTrigger(nt);
2277c478bd9Sstevel@tonic-gate   sqliteDeleteTrigger(pParse->pNewTrigger);
2287c478bd9Sstevel@tonic-gate   pParse->pNewTrigger = 0;
2297c478bd9Sstevel@tonic-gate   sqliteDeleteTriggerStep(pStepList);
2307c478bd9Sstevel@tonic-gate }
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate /*
2337c478bd9Sstevel@tonic-gate ** Make a copy of all components of the given trigger step.  This has
2347c478bd9Sstevel@tonic-gate ** the effect of copying all Expr.token.z values into memory obtained
2357c478bd9Sstevel@tonic-gate ** from sqliteMalloc().  As initially created, the Expr.token.z values
2367c478bd9Sstevel@tonic-gate ** all point to the input string that was fed to the parser.  But that
2377c478bd9Sstevel@tonic-gate ** string is ephemeral - it will go away as soon as the sqlite_exec()
2387c478bd9Sstevel@tonic-gate ** call that started the parser exits.  This routine makes a persistent
2397c478bd9Sstevel@tonic-gate ** copy of all the Expr.token.z strings so that the TriggerStep structure
2407c478bd9Sstevel@tonic-gate ** will be valid even after the sqlite_exec() call returns.
2417c478bd9Sstevel@tonic-gate */
sqlitePersistTriggerStep(TriggerStep * p)2427c478bd9Sstevel@tonic-gate static void sqlitePersistTriggerStep(TriggerStep *p){
2437c478bd9Sstevel@tonic-gate   if( p->target.z ){
2447c478bd9Sstevel@tonic-gate     p->target.z = sqliteStrNDup(p->target.z, p->target.n);
2457c478bd9Sstevel@tonic-gate     p->target.dyn = 1;
2467c478bd9Sstevel@tonic-gate   }
2477c478bd9Sstevel@tonic-gate   if( p->pSelect ){
2487c478bd9Sstevel@tonic-gate     Select *pNew = sqliteSelectDup(p->pSelect);
2497c478bd9Sstevel@tonic-gate     sqliteSelectDelete(p->pSelect);
2507c478bd9Sstevel@tonic-gate     p->pSelect = pNew;
2517c478bd9Sstevel@tonic-gate   }
2527c478bd9Sstevel@tonic-gate   if( p->pWhere ){
2537c478bd9Sstevel@tonic-gate     Expr *pNew = sqliteExprDup(p->pWhere);
2547c478bd9Sstevel@tonic-gate     sqliteExprDelete(p->pWhere);
2557c478bd9Sstevel@tonic-gate     p->pWhere = pNew;
2567c478bd9Sstevel@tonic-gate   }
2577c478bd9Sstevel@tonic-gate   if( p->pExprList ){
2587c478bd9Sstevel@tonic-gate     ExprList *pNew = sqliteExprListDup(p->pExprList);
2597c478bd9Sstevel@tonic-gate     sqliteExprListDelete(p->pExprList);
2607c478bd9Sstevel@tonic-gate     p->pExprList = pNew;
2617c478bd9Sstevel@tonic-gate   }
2627c478bd9Sstevel@tonic-gate   if( p->pIdList ){
2637c478bd9Sstevel@tonic-gate     IdList *pNew = sqliteIdListDup(p->pIdList);
2647c478bd9Sstevel@tonic-gate     sqliteIdListDelete(p->pIdList);
2657c478bd9Sstevel@tonic-gate     p->pIdList = pNew;
2667c478bd9Sstevel@tonic-gate   }
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate /*
2707c478bd9Sstevel@tonic-gate ** Turn a SELECT statement (that the pSelect parameter points to) into
2717c478bd9Sstevel@tonic-gate ** a trigger step.  Return a pointer to a TriggerStep structure.
2727c478bd9Sstevel@tonic-gate **
2737c478bd9Sstevel@tonic-gate ** The parser calls this routine when it finds a SELECT statement in
274*1da57d55SToomas Soome ** body of a TRIGGER.
2757c478bd9Sstevel@tonic-gate */
sqliteTriggerSelectStep(Select * pSelect)2767c478bd9Sstevel@tonic-gate TriggerStep *sqliteTriggerSelectStep(Select *pSelect){
2777c478bd9Sstevel@tonic-gate   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
2787c478bd9Sstevel@tonic-gate   if( pTriggerStep==0 ) return 0;
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate   pTriggerStep->op = TK_SELECT;
2817c478bd9Sstevel@tonic-gate   pTriggerStep->pSelect = pSelect;
2827c478bd9Sstevel@tonic-gate   pTriggerStep->orconf = OE_Default;
2837c478bd9Sstevel@tonic-gate   sqlitePersistTriggerStep(pTriggerStep);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate   return pTriggerStep;
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate /*
2897c478bd9Sstevel@tonic-gate ** Build a trigger step out of an INSERT statement.  Return a pointer
2907c478bd9Sstevel@tonic-gate ** to the new trigger step.
2917c478bd9Sstevel@tonic-gate **
2927c478bd9Sstevel@tonic-gate ** The parser calls this routine when it sees an INSERT inside the
2937c478bd9Sstevel@tonic-gate ** body of a trigger.
2947c478bd9Sstevel@tonic-gate */
sqliteTriggerInsertStep(Token * pTableName,IdList * pColumn,ExprList * pEList,Select * pSelect,int orconf)2957c478bd9Sstevel@tonic-gate TriggerStep *sqliteTriggerInsertStep(
2967c478bd9Sstevel@tonic-gate   Token *pTableName,  /* Name of the table into which we insert */
2977c478bd9Sstevel@tonic-gate   IdList *pColumn,    /* List of columns in pTableName to insert into */
2987c478bd9Sstevel@tonic-gate   ExprList *pEList,   /* The VALUE clause: a list of values to be inserted */
2997c478bd9Sstevel@tonic-gate   Select *pSelect,    /* A SELECT statement that supplies values */
3007c478bd9Sstevel@tonic-gate   int orconf          /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
3017c478bd9Sstevel@tonic-gate ){
3027c478bd9Sstevel@tonic-gate   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
3037c478bd9Sstevel@tonic-gate   if( pTriggerStep==0 ) return 0;
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate   assert(pEList == 0 || pSelect == 0);
3067c478bd9Sstevel@tonic-gate   assert(pEList != 0 || pSelect != 0);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate   pTriggerStep->op = TK_INSERT;
3097c478bd9Sstevel@tonic-gate   pTriggerStep->pSelect = pSelect;
3107c478bd9Sstevel@tonic-gate   pTriggerStep->target  = *pTableName;
3117c478bd9Sstevel@tonic-gate   pTriggerStep->pIdList = pColumn;
3127c478bd9Sstevel@tonic-gate   pTriggerStep->pExprList = pEList;
3137c478bd9Sstevel@tonic-gate   pTriggerStep->orconf = orconf;
3147c478bd9Sstevel@tonic-gate   sqlitePersistTriggerStep(pTriggerStep);
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate   return pTriggerStep;
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate /*
3207c478bd9Sstevel@tonic-gate ** Construct a trigger step that implements an UPDATE statement and return
3217c478bd9Sstevel@tonic-gate ** a pointer to that trigger step.  The parser calls this routine when it
3227c478bd9Sstevel@tonic-gate ** sees an UPDATE statement inside the body of a CREATE TRIGGER.
3237c478bd9Sstevel@tonic-gate */
sqliteTriggerUpdateStep(Token * pTableName,ExprList * pEList,Expr * pWhere,int orconf)3247c478bd9Sstevel@tonic-gate TriggerStep *sqliteTriggerUpdateStep(
3257c478bd9Sstevel@tonic-gate   Token *pTableName,   /* Name of the table to be updated */
3267c478bd9Sstevel@tonic-gate   ExprList *pEList,    /* The SET clause: list of column and new values */
3277c478bd9Sstevel@tonic-gate   Expr *pWhere,        /* The WHERE clause */
3287c478bd9Sstevel@tonic-gate   int orconf           /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
3297c478bd9Sstevel@tonic-gate ){
3307c478bd9Sstevel@tonic-gate   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
3317c478bd9Sstevel@tonic-gate   if( pTriggerStep==0 ) return 0;
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate   pTriggerStep->op = TK_UPDATE;
3347c478bd9Sstevel@tonic-gate   pTriggerStep->target  = *pTableName;
3357c478bd9Sstevel@tonic-gate   pTriggerStep->pExprList = pEList;
3367c478bd9Sstevel@tonic-gate   pTriggerStep->pWhere = pWhere;
3377c478bd9Sstevel@tonic-gate   pTriggerStep->orconf = orconf;
3387c478bd9Sstevel@tonic-gate   sqlitePersistTriggerStep(pTriggerStep);
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate   return pTriggerStep;
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate /*
3447c478bd9Sstevel@tonic-gate ** Construct a trigger step that implements a DELETE statement and return
3457c478bd9Sstevel@tonic-gate ** a pointer to that trigger step.  The parser calls this routine when it
3467c478bd9Sstevel@tonic-gate ** sees a DELETE statement inside the body of a CREATE TRIGGER.
3477c478bd9Sstevel@tonic-gate */
sqliteTriggerDeleteStep(Token * pTableName,Expr * pWhere)3487c478bd9Sstevel@tonic-gate TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){
3497c478bd9Sstevel@tonic-gate   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
3507c478bd9Sstevel@tonic-gate   if( pTriggerStep==0 ) return 0;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate   pTriggerStep->op = TK_DELETE;
3537c478bd9Sstevel@tonic-gate   pTriggerStep->target  = *pTableName;
3547c478bd9Sstevel@tonic-gate   pTriggerStep->pWhere = pWhere;
3557c478bd9Sstevel@tonic-gate   pTriggerStep->orconf = OE_Default;
3567c478bd9Sstevel@tonic-gate   sqlitePersistTriggerStep(pTriggerStep);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate   return pTriggerStep;
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate 
361*1da57d55SToomas Soome /*
3627c478bd9Sstevel@tonic-gate ** Recursively delete a Trigger structure
3637c478bd9Sstevel@tonic-gate */
sqliteDeleteTrigger(Trigger * pTrigger)3647c478bd9Sstevel@tonic-gate void sqliteDeleteTrigger(Trigger *pTrigger){
3657c478bd9Sstevel@tonic-gate   if( pTrigger==0 ) return;
3667c478bd9Sstevel@tonic-gate   sqliteDeleteTriggerStep(pTrigger->step_list);
3677c478bd9Sstevel@tonic-gate   sqliteFree(pTrigger->name);
3687c478bd9Sstevel@tonic-gate   sqliteFree(pTrigger->table);
3697c478bd9Sstevel@tonic-gate   sqliteExprDelete(pTrigger->pWhen);
3707c478bd9Sstevel@tonic-gate   sqliteIdListDelete(pTrigger->pColumns);
3717c478bd9Sstevel@tonic-gate   if( pTrigger->nameToken.dyn ) sqliteFree((char*)pTrigger->nameToken.z);
3727c478bd9Sstevel@tonic-gate   sqliteFree(pTrigger);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate /*
376*1da57d55SToomas Soome  * This function is called to drop a trigger from the database schema.
3777c478bd9Sstevel@tonic-gate  *
3787c478bd9Sstevel@tonic-gate  * This may be called directly from the parser and therefore identifies
3797c478bd9Sstevel@tonic-gate  * the trigger by name.  The sqliteDropTriggerPtr() routine does the
3807c478bd9Sstevel@tonic-gate  * same job as this routine except it take a spointer to the trigger
3817c478bd9Sstevel@tonic-gate  * instead of the trigger name.
3827c478bd9Sstevel@tonic-gate  *
3837c478bd9Sstevel@tonic-gate  * Note that this function does not delete the trigger entirely. Instead it
384*1da57d55SToomas Soome  * removes it from the internal schema and places it in the trigDrop hash
3857c478bd9Sstevel@tonic-gate  * table. This is so that the trigger can be restored into the database schema
3867c478bd9Sstevel@tonic-gate  * if the transaction is rolled back.
3877c478bd9Sstevel@tonic-gate  */
sqliteDropTrigger(Parse * pParse,SrcList * pName)3887c478bd9Sstevel@tonic-gate void sqliteDropTrigger(Parse *pParse, SrcList *pName){
3897c478bd9Sstevel@tonic-gate   Trigger *pTrigger;
3907c478bd9Sstevel@tonic-gate   int i;
3917c478bd9Sstevel@tonic-gate   const char *zDb;
3927c478bd9Sstevel@tonic-gate   const char *zName;
3937c478bd9Sstevel@tonic-gate   int nName;
3947c478bd9Sstevel@tonic-gate   sqlite *db = pParse->db;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate   if( sqlite_malloc_failed ) goto drop_trigger_cleanup;
3977c478bd9Sstevel@tonic-gate   assert( pName->nSrc==1 );
3987c478bd9Sstevel@tonic-gate   zDb = pName->a[0].zDatabase;
3997c478bd9Sstevel@tonic-gate   zName = pName->a[0].zName;
4007c478bd9Sstevel@tonic-gate   nName = strlen(zName);
4017c478bd9Sstevel@tonic-gate   for(i=0; i<db->nDb; i++){
4027c478bd9Sstevel@tonic-gate     int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */
4037c478bd9Sstevel@tonic-gate     if( zDb && sqliteStrICmp(db->aDb[j].zName, zDb) ) continue;
4047c478bd9Sstevel@tonic-gate     pTrigger = sqliteHashFind(&(db->aDb[j].trigHash), zName, nName+1);
4057c478bd9Sstevel@tonic-gate     if( pTrigger ) break;
4067c478bd9Sstevel@tonic-gate   }
4077c478bd9Sstevel@tonic-gate   if( !pTrigger ){
4087c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "no such trigger: %S", pName, 0);
4097c478bd9Sstevel@tonic-gate     goto drop_trigger_cleanup;
4107c478bd9Sstevel@tonic-gate   }
4117c478bd9Sstevel@tonic-gate   sqliteDropTriggerPtr(pParse, pTrigger, 0);
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate drop_trigger_cleanup:
4147c478bd9Sstevel@tonic-gate   sqliteSrcListDelete(pName);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate ** Drop a trigger given a pointer to that trigger.  If nested is false,
4197c478bd9Sstevel@tonic-gate ** then also generate code to remove the trigger from the SQLITE_MASTER
4207c478bd9Sstevel@tonic-gate ** table.
4217c478bd9Sstevel@tonic-gate */
sqliteDropTriggerPtr(Parse * pParse,Trigger * pTrigger,int nested)4227c478bd9Sstevel@tonic-gate void sqliteDropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
4237c478bd9Sstevel@tonic-gate   Table   *pTable;
4247c478bd9Sstevel@tonic-gate   Vdbe *v;
4257c478bd9Sstevel@tonic-gate   sqlite *db = pParse->db;
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate   assert( pTrigger->iDb<db->nDb );
4287c478bd9Sstevel@tonic-gate   if( pTrigger->iDb>=2 ){
4297c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "triggers may not be removed from "
4307c478bd9Sstevel@tonic-gate        "auxiliary database %s", db->aDb[pTrigger->iDb].zName);
4317c478bd9Sstevel@tonic-gate     return;
4327c478bd9Sstevel@tonic-gate   }
4337c478bd9Sstevel@tonic-gate   pTable = sqliteFindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
4347c478bd9Sstevel@tonic-gate   assert(pTable);
4357c478bd9Sstevel@tonic-gate   assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
4367c478bd9Sstevel@tonic-gate #ifndef SQLITE_OMIT_AUTHORIZATION
4377c478bd9Sstevel@tonic-gate   {
4387c478bd9Sstevel@tonic-gate     int code = SQLITE_DROP_TRIGGER;
4397c478bd9Sstevel@tonic-gate     const char *zDb = db->aDb[pTrigger->iDb].zName;
4407c478bd9Sstevel@tonic-gate     const char *zTab = SCHEMA_TABLE(pTrigger->iDb);
4417c478bd9Sstevel@tonic-gate     if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;
4427c478bd9Sstevel@tonic-gate     if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
4437c478bd9Sstevel@tonic-gate       sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
4447c478bd9Sstevel@tonic-gate       return;
4457c478bd9Sstevel@tonic-gate     }
4467c478bd9Sstevel@tonic-gate   }
4477c478bd9Sstevel@tonic-gate #endif
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate   /* Generate code to destroy the database record of the trigger.
4507c478bd9Sstevel@tonic-gate   */
4517c478bd9Sstevel@tonic-gate   if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
4527c478bd9Sstevel@tonic-gate     int base;
4537c478bd9Sstevel@tonic-gate     static VdbeOpList dropTrigger[] = {
4547c478bd9Sstevel@tonic-gate       { OP_Rewind,     0, ADDR(9),  0},
4557c478bd9Sstevel@tonic-gate       { OP_String,     0, 0,        0}, /* 1 */
4567c478bd9Sstevel@tonic-gate       { OP_Column,     0, 1,        0},
4577c478bd9Sstevel@tonic-gate       { OP_Ne,         0, ADDR(8),  0},
4587c478bd9Sstevel@tonic-gate       { OP_String,     0, 0,        "trigger"},
4597c478bd9Sstevel@tonic-gate       { OP_Column,     0, 0,        0},
4607c478bd9Sstevel@tonic-gate       { OP_Ne,         0, ADDR(8),  0},
4617c478bd9Sstevel@tonic-gate       { OP_Delete,     0, 0,        0},
4627c478bd9Sstevel@tonic-gate       { OP_Next,       0, ADDR(1),  0}, /* 8 */
4637c478bd9Sstevel@tonic-gate     };
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate     sqliteBeginWriteOperation(pParse, 0, 0);
4667c478bd9Sstevel@tonic-gate     sqliteOpenMasterTable(v, pTrigger->iDb);
4677c478bd9Sstevel@tonic-gate     base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
4687c478bd9Sstevel@tonic-gate     sqliteVdbeChangeP3(v, base+1, pTrigger->name, 0);
4697c478bd9Sstevel@tonic-gate     if( pTrigger->iDb==0 ){
4707c478bd9Sstevel@tonic-gate       sqliteChangeCookie(db, v);
4717c478bd9Sstevel@tonic-gate     }
4727c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_Close, 0, 0);
4737c478bd9Sstevel@tonic-gate     sqliteEndWriteOperation(pParse);
4747c478bd9Sstevel@tonic-gate   }
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate   /*
4777c478bd9Sstevel@tonic-gate    * If this is not an "explain", then delete the trigger structure.
4787c478bd9Sstevel@tonic-gate    */
4797c478bd9Sstevel@tonic-gate   if( !pParse->explain ){
4807c478bd9Sstevel@tonic-gate     const char *zName = pTrigger->name;
4817c478bd9Sstevel@tonic-gate     int nName = strlen(zName);
4827c478bd9Sstevel@tonic-gate     if( pTable->pTrigger == pTrigger ){
4837c478bd9Sstevel@tonic-gate       pTable->pTrigger = pTrigger->pNext;
4847c478bd9Sstevel@tonic-gate     }else{
4857c478bd9Sstevel@tonic-gate       Trigger *cc = pTable->pTrigger;
486*1da57d55SToomas Soome       while( cc ){
4877c478bd9Sstevel@tonic-gate         if( cc->pNext == pTrigger ){
4887c478bd9Sstevel@tonic-gate           cc->pNext = cc->pNext->pNext;
4897c478bd9Sstevel@tonic-gate           break;
4907c478bd9Sstevel@tonic-gate         }
4917c478bd9Sstevel@tonic-gate         cc = cc->pNext;
4927c478bd9Sstevel@tonic-gate       }
4937c478bd9Sstevel@tonic-gate       assert(cc);
4947c478bd9Sstevel@tonic-gate     }
4957c478bd9Sstevel@tonic-gate     sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);
4967c478bd9Sstevel@tonic-gate     sqliteDeleteTrigger(pTrigger);
4977c478bd9Sstevel@tonic-gate   }
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate /*
5017c478bd9Sstevel@tonic-gate ** pEList is the SET clause of an UPDATE statement.  Each entry
5027c478bd9Sstevel@tonic-gate ** in pEList is of the format <id>=<expr>.  If any of the entries
5037c478bd9Sstevel@tonic-gate ** in pEList have an <id> which matches an identifier in pIdList,
5047c478bd9Sstevel@tonic-gate ** then return TRUE.  If pIdList==NULL, then it is considered a
5057c478bd9Sstevel@tonic-gate ** wildcard that matches anything.  Likewise if pEList==NULL then
5067c478bd9Sstevel@tonic-gate ** it matches anything so always return true.  Return false only
5077c478bd9Sstevel@tonic-gate ** if there is no match.
5087c478bd9Sstevel@tonic-gate */
checkColumnOverLap(IdList * pIdList,ExprList * pEList)5097c478bd9Sstevel@tonic-gate static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
5107c478bd9Sstevel@tonic-gate   int e;
5117c478bd9Sstevel@tonic-gate   if( !pIdList || !pEList ) return 1;
5127c478bd9Sstevel@tonic-gate   for(e=0; e<pEList->nExpr; e++){
5137c478bd9Sstevel@tonic-gate     if( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
5147c478bd9Sstevel@tonic-gate   }
515*1da57d55SToomas Soome   return 0;
5167c478bd9Sstevel@tonic-gate }
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate /* A global variable that is TRUE if we should always set up temp tables for
519*1da57d55SToomas Soome  * for triggers, even if there are no triggers to code. This is used to test
5207c478bd9Sstevel@tonic-gate  * how much overhead the triggers algorithm is causing.
5217c478bd9Sstevel@tonic-gate  *
5227c478bd9Sstevel@tonic-gate  * This flag can be set or cleared using the "trigger_overhead_test" pragma.
5237c478bd9Sstevel@tonic-gate  * The pragma is not documented since it is not really part of the interface
5247c478bd9Sstevel@tonic-gate  * to SQLite, just the test procedure.
5257c478bd9Sstevel@tonic-gate */
5267c478bd9Sstevel@tonic-gate int always_code_trigger_setup = 0;
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate /*
5297c478bd9Sstevel@tonic-gate  * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
5307c478bd9Sstevel@tonic-gate  * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
5317c478bd9Sstevel@tonic-gate  * found in the list specified as pTrigger.
5327c478bd9Sstevel@tonic-gate  */
sqliteTriggersExist(Parse * pParse,Trigger * pTrigger,int op,int tr_tm,int foreach,ExprList * pChanges)5337c478bd9Sstevel@tonic-gate int sqliteTriggersExist(
5347c478bd9Sstevel@tonic-gate   Parse *pParse,          /* Used to check for recursive triggers */
5357c478bd9Sstevel@tonic-gate   Trigger *pTrigger,      /* A list of triggers associated with a table */
5367c478bd9Sstevel@tonic-gate   int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
5377c478bd9Sstevel@tonic-gate   int tr_tm,              /* one of TK_BEFORE, TK_AFTER */
5387c478bd9Sstevel@tonic-gate   int foreach,            /* one of TK_ROW or TK_STATEMENT */
5397c478bd9Sstevel@tonic-gate   ExprList *pChanges      /* Columns that change in an UPDATE statement */
5407c478bd9Sstevel@tonic-gate ){
5417c478bd9Sstevel@tonic-gate   Trigger * pTriggerCursor;
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate   if( always_code_trigger_setup ){
5447c478bd9Sstevel@tonic-gate     return 1;
5457c478bd9Sstevel@tonic-gate   }
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate   pTriggerCursor = pTrigger;
5487c478bd9Sstevel@tonic-gate   while( pTriggerCursor ){
549*1da57d55SToomas Soome     if( pTriggerCursor->op == op &&
550*1da57d55SToomas Soome 	pTriggerCursor->tr_tm == tr_tm &&
5517c478bd9Sstevel@tonic-gate 	pTriggerCursor->foreach == foreach &&
5527c478bd9Sstevel@tonic-gate 	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
5537c478bd9Sstevel@tonic-gate       TriggerStack * ss;
5547c478bd9Sstevel@tonic-gate       ss = pParse->trigStack;
5557c478bd9Sstevel@tonic-gate       while( ss && ss->pTrigger != pTrigger ){
5567c478bd9Sstevel@tonic-gate 	ss = ss->pNext;
5577c478bd9Sstevel@tonic-gate       }
5587c478bd9Sstevel@tonic-gate       if( !ss )return 1;
5597c478bd9Sstevel@tonic-gate     }
5607c478bd9Sstevel@tonic-gate     pTriggerCursor = pTriggerCursor->pNext;
5617c478bd9Sstevel@tonic-gate   }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate   return 0;
5647c478bd9Sstevel@tonic-gate }
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate ** Convert the pStep->target token into a SrcList and return a pointer
5687c478bd9Sstevel@tonic-gate ** to that SrcList.
5697c478bd9Sstevel@tonic-gate **
5707c478bd9Sstevel@tonic-gate ** This routine adds a specific database name, if needed, to the target when
5717c478bd9Sstevel@tonic-gate ** forming the SrcList.  This prevents a trigger in one database from
5727c478bd9Sstevel@tonic-gate ** referring to a target in another database.  An exception is when the
5737c478bd9Sstevel@tonic-gate ** trigger is in TEMP in which case it can refer to any other database it
5747c478bd9Sstevel@tonic-gate ** wants.
5757c478bd9Sstevel@tonic-gate */
targetSrcList(Parse * pParse,TriggerStep * pStep)5767c478bd9Sstevel@tonic-gate static SrcList *targetSrcList(
5777c478bd9Sstevel@tonic-gate   Parse *pParse,       /* The parsing context */
5787c478bd9Sstevel@tonic-gate   TriggerStep *pStep   /* The trigger containing the target token */
5797c478bd9Sstevel@tonic-gate ){
5807c478bd9Sstevel@tonic-gate   Token sDb;           /* Dummy database name token */
5817c478bd9Sstevel@tonic-gate   int iDb;             /* Index of the database to use */
5827c478bd9Sstevel@tonic-gate   SrcList *pSrc;       /* SrcList to be returned */
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate   iDb = pStep->pTrig->iDb;
5857c478bd9Sstevel@tonic-gate   if( iDb==0 || iDb>=2 ){
5867c478bd9Sstevel@tonic-gate     assert( iDb<pParse->db->nDb );
5877c478bd9Sstevel@tonic-gate     sDb.z = pParse->db->aDb[iDb].zName;
5887c478bd9Sstevel@tonic-gate     sDb.n = strlen(sDb.z);
5897c478bd9Sstevel@tonic-gate     pSrc = sqliteSrcListAppend(0, &sDb, &pStep->target);
5907c478bd9Sstevel@tonic-gate   } else {
5917c478bd9Sstevel@tonic-gate     pSrc = sqliteSrcListAppend(0, &pStep->target, 0);
5927c478bd9Sstevel@tonic-gate   }
5937c478bd9Sstevel@tonic-gate   return pSrc;
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate /*
5977c478bd9Sstevel@tonic-gate ** Generate VDBE code for zero or more statements inside the body of a
598*1da57d55SToomas Soome ** trigger.
5997c478bd9Sstevel@tonic-gate */
codeTriggerProgram(Parse * pParse,TriggerStep * pStepList,int orconfin)6007c478bd9Sstevel@tonic-gate static int codeTriggerProgram(
6017c478bd9Sstevel@tonic-gate   Parse *pParse,            /* The parser context */
6027c478bd9Sstevel@tonic-gate   TriggerStep *pStepList,   /* List of statements inside the trigger body */
603*1da57d55SToomas Soome   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */
6047c478bd9Sstevel@tonic-gate ){
6057c478bd9Sstevel@tonic-gate   TriggerStep * pTriggerStep = pStepList;
6067c478bd9Sstevel@tonic-gate   int orconf;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate   while( pTriggerStep ){
6097c478bd9Sstevel@tonic-gate     int saveNTab = pParse->nTab;
610*1da57d55SToomas Soome 
6117c478bd9Sstevel@tonic-gate     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
6127c478bd9Sstevel@tonic-gate     pParse->trigStack->orconf = orconf;
6137c478bd9Sstevel@tonic-gate     switch( pTriggerStep->op ){
6147c478bd9Sstevel@tonic-gate       case TK_SELECT: {
615*1da57d55SToomas Soome 	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
6167c478bd9Sstevel@tonic-gate 	assert(ss);
6177c478bd9Sstevel@tonic-gate 	assert(ss->pSrc);
6187c478bd9Sstevel@tonic-gate 	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
6197c478bd9Sstevel@tonic-gate 	sqliteSelectDelete(ss);
6207c478bd9Sstevel@tonic-gate 	break;
6217c478bd9Sstevel@tonic-gate       }
6227c478bd9Sstevel@tonic-gate       case TK_UPDATE: {
6237c478bd9Sstevel@tonic-gate         SrcList *pSrc;
6247c478bd9Sstevel@tonic-gate         pSrc = targetSrcList(pParse, pTriggerStep);
6257c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
6267c478bd9Sstevel@tonic-gate         sqliteUpdate(pParse, pSrc,
627*1da57d55SToomas Soome 		sqliteExprListDup(pTriggerStep->pExprList),
6287c478bd9Sstevel@tonic-gate 		sqliteExprDup(pTriggerStep->pWhere), orconf);
6297c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
6307c478bd9Sstevel@tonic-gate         break;
6317c478bd9Sstevel@tonic-gate       }
6327c478bd9Sstevel@tonic-gate       case TK_INSERT: {
6337c478bd9Sstevel@tonic-gate         SrcList *pSrc;
6347c478bd9Sstevel@tonic-gate         pSrc = targetSrcList(pParse, pTriggerStep);
6357c478bd9Sstevel@tonic-gate         sqliteInsert(pParse, pSrc,
636*1da57d55SToomas Soome           sqliteExprListDup(pTriggerStep->pExprList),
637*1da57d55SToomas Soome           sqliteSelectDup(pTriggerStep->pSelect),
6387c478bd9Sstevel@tonic-gate           sqliteIdListDup(pTriggerStep->pIdList), orconf);
6397c478bd9Sstevel@tonic-gate         break;
6407c478bd9Sstevel@tonic-gate       }
6417c478bd9Sstevel@tonic-gate       case TK_DELETE: {
6427c478bd9Sstevel@tonic-gate         SrcList *pSrc;
6437c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
6447c478bd9Sstevel@tonic-gate         pSrc = targetSrcList(pParse, pTriggerStep);
6457c478bd9Sstevel@tonic-gate         sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
6467c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
6477c478bd9Sstevel@tonic-gate         break;
6487c478bd9Sstevel@tonic-gate       }
6497c478bd9Sstevel@tonic-gate       default:
6507c478bd9Sstevel@tonic-gate         assert(0);
651*1da57d55SToomas Soome     }
6527c478bd9Sstevel@tonic-gate     pParse->nTab = saveNTab;
6537c478bd9Sstevel@tonic-gate     pTriggerStep = pTriggerStep->pNext;
6547c478bd9Sstevel@tonic-gate   }
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate   return 0;
6577c478bd9Sstevel@tonic-gate }
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate /*
6607c478bd9Sstevel@tonic-gate ** This is called to code FOR EACH ROW triggers.
6617c478bd9Sstevel@tonic-gate **
662*1da57d55SToomas Soome ** When the code that this function generates is executed, the following
6637c478bd9Sstevel@tonic-gate ** must be true:
6647c478bd9Sstevel@tonic-gate **
6657c478bd9Sstevel@tonic-gate ** 1. No cursors may be open in the main database.  (But newIdx and oldIdx
6667c478bd9Sstevel@tonic-gate **    can be indices of cursors in temporary tables.  See below.)
6677c478bd9Sstevel@tonic-gate **
6687c478bd9Sstevel@tonic-gate ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
6697c478bd9Sstevel@tonic-gate **    a temporary vdbe cursor (index newIdx) must be open and pointing at
6707c478bd9Sstevel@tonic-gate **    a row containing values to be substituted for new.* expressions in the
6717c478bd9Sstevel@tonic-gate **    trigger program(s).
6727c478bd9Sstevel@tonic-gate **
6737c478bd9Sstevel@tonic-gate ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
6747c478bd9Sstevel@tonic-gate **    a temporary vdbe cursor (index oldIdx) must be open and pointing at
6757c478bd9Sstevel@tonic-gate **    a row containing values to be substituted for old.* expressions in the
6767c478bd9Sstevel@tonic-gate **    trigger program(s).
6777c478bd9Sstevel@tonic-gate **
6787c478bd9Sstevel@tonic-gate */
sqliteCodeRowTrigger(Parse * pParse,int op,ExprList * pChanges,int tr_tm,Table * pTab,int newIdx,int oldIdx,int orconf,int ignoreJump)6797c478bd9Sstevel@tonic-gate int sqliteCodeRowTrigger(
6807c478bd9Sstevel@tonic-gate   Parse *pParse,       /* Parse context */
6817c478bd9Sstevel@tonic-gate   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
6827c478bd9Sstevel@tonic-gate   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
6837c478bd9Sstevel@tonic-gate   int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
6847c478bd9Sstevel@tonic-gate   Table *pTab,         /* The table to code triggers from */
6857c478bd9Sstevel@tonic-gate   int newIdx,          /* The indice of the "new" row to access */
6867c478bd9Sstevel@tonic-gate   int oldIdx,          /* The indice of the "old" row to access */
6877c478bd9Sstevel@tonic-gate   int orconf,          /* ON CONFLICT policy */
6887c478bd9Sstevel@tonic-gate   int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
6897c478bd9Sstevel@tonic-gate ){
6907c478bd9Sstevel@tonic-gate   Trigger * pTrigger;
6917c478bd9Sstevel@tonic-gate   TriggerStack * pTriggerStack;
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
6947c478bd9Sstevel@tonic-gate   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate   assert(newIdx != -1 || oldIdx != -1);
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate   pTrigger = pTab->pTrigger;
6997c478bd9Sstevel@tonic-gate   while( pTrigger ){
7007c478bd9Sstevel@tonic-gate     int fire_this = 0;
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate     /* determine whether we should code this trigger */
703*1da57d55SToomas Soome     if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
7047c478bd9Sstevel@tonic-gate         pTrigger->foreach == TK_ROW ){
7057c478bd9Sstevel@tonic-gate       fire_this = 1;
7067c478bd9Sstevel@tonic-gate       pTriggerStack = pParse->trigStack;
7077c478bd9Sstevel@tonic-gate       while( pTriggerStack ){
7087c478bd9Sstevel@tonic-gate         if( pTriggerStack->pTrigger == pTrigger ){
7097c478bd9Sstevel@tonic-gate 	  fire_this = 0;
7107c478bd9Sstevel@tonic-gate 	}
7117c478bd9Sstevel@tonic-gate         pTriggerStack = pTriggerStack->pNext;
7127c478bd9Sstevel@tonic-gate       }
7137c478bd9Sstevel@tonic-gate       if( op == TK_UPDATE && pTrigger->pColumns &&
7147c478bd9Sstevel@tonic-gate           !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
7157c478bd9Sstevel@tonic-gate         fire_this = 0;
7167c478bd9Sstevel@tonic-gate       }
7177c478bd9Sstevel@tonic-gate     }
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate     if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){
7207c478bd9Sstevel@tonic-gate       int endTrigger;
7217c478bd9Sstevel@tonic-gate       SrcList dummyTablist;
7227c478bd9Sstevel@tonic-gate       Expr * whenExpr;
7237c478bd9Sstevel@tonic-gate       AuthContext sContext;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate       dummyTablist.nSrc = 0;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate       /* Push an entry on to the trigger stack */
7287c478bd9Sstevel@tonic-gate       pTriggerStack->pTrigger = pTrigger;
7297c478bd9Sstevel@tonic-gate       pTriggerStack->newIdx = newIdx;
7307c478bd9Sstevel@tonic-gate       pTriggerStack->oldIdx = oldIdx;
7317c478bd9Sstevel@tonic-gate       pTriggerStack->pTab = pTab;
7327c478bd9Sstevel@tonic-gate       pTriggerStack->pNext = pParse->trigStack;
7337c478bd9Sstevel@tonic-gate       pTriggerStack->ignoreJump = ignoreJump;
7347c478bd9Sstevel@tonic-gate       pParse->trigStack = pTriggerStack;
7357c478bd9Sstevel@tonic-gate       sqliteAuthContextPush(pParse, &sContext, pTrigger->name);
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate       /* code the WHEN clause */
7387c478bd9Sstevel@tonic-gate       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
7397c478bd9Sstevel@tonic-gate       whenExpr = sqliteExprDup(pTrigger->pWhen);
7407c478bd9Sstevel@tonic-gate       if( sqliteExprResolveIds(pParse, &dummyTablist, 0, whenExpr) ){
7417c478bd9Sstevel@tonic-gate         pParse->trigStack = pParse->trigStack->pNext;
7427c478bd9Sstevel@tonic-gate         sqliteFree(pTriggerStack);
7437c478bd9Sstevel@tonic-gate         sqliteExprDelete(whenExpr);
7447c478bd9Sstevel@tonic-gate         return 1;
7457c478bd9Sstevel@tonic-gate       }
7467c478bd9Sstevel@tonic-gate       sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
7477c478bd9Sstevel@tonic-gate       sqliteExprDelete(whenExpr);
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPush, 0, 0);
750*1da57d55SToomas Soome       codeTriggerProgram(pParse, pTrigger->step_list, orconf);
7517c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPop, 0, 0);
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate       /* Pop the entry off the trigger stack */
7547c478bd9Sstevel@tonic-gate       pParse->trigStack = pParse->trigStack->pNext;
7557c478bd9Sstevel@tonic-gate       sqliteAuthContextPop(&sContext);
7567c478bd9Sstevel@tonic-gate       sqliteFree(pTriggerStack);
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
7597c478bd9Sstevel@tonic-gate     }
7607c478bd9Sstevel@tonic-gate     pTrigger = pTrigger->pNext;
7617c478bd9Sstevel@tonic-gate   }
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate   return 0;
7647c478bd9Sstevel@tonic-gate }
765