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