/* ** This is called to code FOR EACH ROW triggers. ** ** When the code that this function generates is executed, the following ** must be true: ** ** 1. No cursors may be open in the main database. (But newIdx and oldIdx ** can be indices of cursors in temporary tables. See below.) ** ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then ** a temporary vdbe cursor (index newIdx) must be open and pointing at ** a row containing values to be substituted for new.* expressions in the ** trigger program(s). ** ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** a temporary vdbe cursor (index oldIdx) must be open and pointing at ** a row containing values to be substituted for old.* expressions in the ** trigger program(s). ** */ int sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TK_BEFORE, TK_AFTER */ Table *pTab, /* The table to code triggers from */ int newIdx, /* The indice of the "new" row to access */ int oldIdx, /* The indice of the "old" row to access */ int orconf, /* ON CONFLICT policy */ int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ){ Trigger * pTrigger; TriggerStack * pTriggerStack; assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER ); assert(newIdx != -1 || oldIdx != -1); pTrigger = pTab->pTrigger; while( pTrigger ){ int fire_this = 0; /* determine whether we should code this trigger */ if( pTrigger->op == op && pTrigger->tr_tm == tr_tm && pTrigger->foreach == TK_ROW ){ fire_this = 1; pTriggerStack = pParse->trigStack; while( pTriggerStack ){ if( pTriggerStack->pTrigger == pTrigger ){ fire_this = 0; } pTriggerStack = pTriggerStack->pNext; } if( op == TK_UPDATE && pTrigger->pColumns && !checkColumnOverLap(pTrigger->pColumns, pChanges) ){ fire_this = 0; } } if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){ int endTrigger; SrcList dummyTablist; Expr * whenExpr; AuthContext sContext; dummyTablist.nSrc = 0; /* Push an entry on to the trigger stack */ pTriggerStack->pTrigger = pTrigger; pTriggerStack->newIdx = newIdx; pTriggerStack->oldIdx = oldIdx; pTriggerStack->pTab = pTab; pTriggerStack->pNext = pParse->trigStack; pTriggerStack->ignoreJump = ignoreJump; pParse->trigStack = pTriggerStack; sqlite3AuthContextPush(pParse, &sContext, pTrigger->name); /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); whenExpr = sqlite3ExprDup(pTrigger->pWhen); if( sqlite3ExprResolveIds(pParse, &dummyTablist, 0, whenExpr) ){ pParse->trigStack = pParse->trigStack->pNext; sqliteFree(pTriggerStack); sqlite3ExprDelete(whenExpr); return 1; } sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1); sqlite3ExprDelete(whenExpr); sqlite3VdbeAddOp(pParse->pVdbe, OP_ContextPush, 0, 0); codeTriggerProgram(pParse, pTrigger->step_list, orconf); sqlite3VdbeAddOp(pParse->pVdbe, OP_ContextPop, 0, 0); /* Pop the entry off the trigger stack */ pParse->trigStack = pParse->trigStack->pNext; sqlite3AuthContextPop(&sContext); sqliteFree(pTriggerStack); sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); } pTrigger = pTrigger->pNext; } return 0; }
/* ** Create and populate a new TriggerPrg object with a sub-program ** implementing trigger pTrigger with ON CONFLICT policy orconf. */ static TriggerPrg *codeRowTrigger( Parse *pParse, /* Current parse context */ Trigger *pTrigger, /* Trigger to code */ Table *pTab, /* The table pTrigger is attached to */ int orconf /* ON CONFLICT policy to code trigger program with */ ){ Parse *pTop = sqlite3ParseToplevel(pParse); sqlite3 *db = pParse->db; /* Database handle */ TriggerPrg *pPrg; /* Value to return */ Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ Vdbe *v; /* Temporary VM */ NameContext sNC; /* Name context for sub-vdbe */ SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ Parse *pSubParse; /* Parse context for sub-vdbe */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); assert( pTop->pVdbe ); /* Allocate the TriggerPrg and SubProgram objects. To ensure that they ** are freed if an error occurs, link them into the Parse.pTriggerPrg ** list of the top-level Parse object sooner rather than later. */ pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg)); if( !pPrg ) return 0; pPrg->pNext = pTop->pTriggerPrg; pTop->pTriggerPrg = pPrg; pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram)); if( !pProgram ) return 0; sqlite3VdbeLinkSubProgram(pTop->pVdbe, pProgram); pPrg->pTrigger = pTrigger; pPrg->orconf = orconf; pPrg->aColmask[0] = 0xffffffff; pPrg->aColmask[1] = 0xffffffff; /* Allocate and populate a new Parse context to use for coding the ** trigger sub-program. */ pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); if( !pSubParse ) return 0; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pSubParse; pSubParse->db = db; pSubParse->pTriggerTab = pTab; pSubParse->pToplevel = pTop; pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; pSubParse->nQueryLoop = pParse->nQueryLoop; v = sqlite3GetVdbe(pSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), (pTrigger->op==TK_INSERT ? "INSERT" : ""), (pTrigger->op==TK_DELETE ? "DELETE" : ""), pTab->zName )); #ifndef SQLITE_OMIT_TRACE sqlite3VdbeChangeP4(v, -1, sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC ); #endif /* If one was specified, code the WHEN clause. If it evaluates to false ** (or NULL) the sub-vdbe is immediately halted by jumping to the ** OP_Halt inserted at the end of the program. */ if( pTrigger->pWhen ){ pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) && db->mallocFailed==0 ){ iEndTrigger = sqlite3VdbeMakeLabel(v); sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); } sqlite3ExprDelete(db, pWhen); } /* Code the trigger program into the sub-vdbe. */ codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); /* Insert an OP_Halt at the end of the sub-program. */ if( iEndTrigger ){ sqlite3VdbeResolveLabel(v, iEndTrigger); } sqlite3VdbeAddOp0(v, OP_Halt); VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); transferParseError(pParse, pSubParse); if( db->mallocFailed==0 ){ pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } pProgram->nMem = pSubParse->nMem; pProgram->nCsr = pSubParse->nTab; pProgram->nOnce = pSubParse->nOnce; pProgram->token = (void *)pTrigger; pPrg->aColmask[0] = pSubParse->oldmask; pPrg->aColmask[1] = pSubParse->newmask; sqlite3VdbeDelete(v); } assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); sqlite3StackFree(db, pSubParse); return pPrg; }
/* ** This is called to code FOR EACH ROW triggers. ** ** When the code that this function generates is executed, the following ** must be true: ** ** 1. No cursors may be open in the main database. (But newIdx and oldIdx ** can be indices of cursors in temporary tables. See below.) ** ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then ** a temporary vdbe cursor (index newIdx) must be open and pointing at ** a row containing values to be substituted for new.* expressions in the ** trigger program(s). ** ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** a temporary vdbe cursor (index oldIdx) must be open and pointing at ** a row containing values to be substituted for old.* expressions in the ** trigger program(s). ** ** If they are not NULL, the piOldColMask and piNewColMask output variables ** are set to values that describe the columns used by the trigger program ** in the OLD.* and NEW.* tables respectively. If column N of the ** pseudo-table is read at least once, the corresponding bit of the output ** mask is set. If a column with an index greater than 32 is read, the ** output mask is set to the special value 0xffffffff. ** */ int sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ Trigger *pTrigger, /* List of triggers on table pTab */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Table *pTab, /* The table to code triggers from */ int newIdx, /* The indice of the "new" row to access */ int oldIdx, /* The indice of the "old" row to access */ int orconf, /* ON CONFLICT policy */ int ignoreJump, /* Instruction to jump to for RAISE(IGNORE) */ u32 *piOldColMask, /* OUT: Mask of columns used from the OLD.* table */ u32 *piNewColMask /* OUT: Mask of columns used from the NEW.* table */ ){ Trigger *p; sqlite3 *db = pParse->db; TriggerStack trigStackEntry; trigStackEntry.oldColMask = 0; trigStackEntry.newColMask = 0; assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); assert(newIdx != -1 || oldIdx != -1); for(p=pTrigger; p; p=p->pNext){ int fire_this = 0; /* Sanity checking: The schema for the trigger and for the table are ** always defined. The trigger must be in the same schema as the table ** or else it must be a TEMP trigger. */ assert( p->pSchema!=0 ); assert( p->pTabSchema!=0 ); assert( p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema ); /* Determine whether we should code this trigger */ if( p->op==op && p->tr_tm==tr_tm && checkColumnOverlap(p->pColumns,pChanges) ){ TriggerStack *pS; /* Pointer to trigger-stack entry */ for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){} if( !pS ){ fire_this = 1; } #if 0 /* Give no warning for recursive triggers. Just do not do them */ else{ sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)", p->name); return SQLITE_ERROR; } #endif } if( fire_this ){ int endTrigger; Expr * whenExpr; AuthContext sContext; NameContext sNC; #ifndef SQLITE_OMIT_TRACE sqlite3VdbeAddOp4(pParse->pVdbe, OP_Trace, 0, 0, 0, sqlite3MPrintf(db, "-- TRIGGER %s", p->name), P4_DYNAMIC); #endif memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; /* Push an entry on to the trigger stack */ trigStackEntry.pTrigger = p; trigStackEntry.newIdx = newIdx; trigStackEntry.oldIdx = oldIdx; trigStackEntry.pTab = pTab; trigStackEntry.pNext = pParse->trigStack; trigStackEntry.ignoreJump = ignoreJump; pParse->trigStack = &trigStackEntry; sqlite3AuthContextPush(pParse, &sContext, p->name); /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); whenExpr = sqlite3ExprDup(db, p->pWhen, 0); if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){ pParse->trigStack = trigStackEntry.pNext; sqlite3ExprDelete(db, whenExpr); return 1; } sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL); sqlite3ExprDelete(db, whenExpr); codeTriggerProgram(pParse, p->step_list, orconf); /* Pop the entry off the trigger stack */ pParse->trigStack = trigStackEntry.pNext; sqlite3AuthContextPop(&sContext); sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); } } if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask; if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask; return 0; }
/* ** This is called to code FOR EACH ROW triggers. ** ** When the code that this function generates is executed, the following ** must be true: ** ** 1. No cursors may be open in the main database. (But newIdx and oldIdx ** can be indices of cursors in temporary tables. See below.) ** ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then ** a temporary vdbe cursor (index newIdx) must be open and pointing at ** a row containing values to be substituted for new.* expressions in the ** trigger program(s). ** ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** a temporary vdbe cursor (index oldIdx) must be open and pointing at ** a row containing values to be substituted for old.* expressions in the ** trigger program(s). ** */ int sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Table *pTab, /* The table to code triggers from */ int newIdx, /* The indice of the "new" row to access */ int oldIdx, /* The indice of the "old" row to access */ int orconf, /* ON CONFLICT policy */ int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ){ Trigger *p; TriggerStack trigStackEntry; assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); assert(newIdx != -1 || oldIdx != -1); for(p=pTab->pTrigger; p; p=p->pNext){ int fire_this = 0; sqlite3 *db = pParse->db; /* Determine whether we should code this trigger */ if( p->op==op && p->tr_tm==tr_tm && (p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema) && (op!=TK_UPDATE||!p->pColumns||checkColumnOverLap(p->pColumns,pChanges)) ){ TriggerStack *pS; /* Pointer to trigger-stack entry */ for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){} if( !pS ){ fire_this = 1; } #if 0 /* Give no warning for recursive triggers. Just do not do them */ else{ sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)", p->name); return SQLITE_ERROR; } #endif } if( fire_this ){ int endTrigger; Expr * whenExpr; AuthContext sContext; NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; /* Push an entry on to the trigger stack */ trigStackEntry.pTrigger = p; trigStackEntry.newIdx = newIdx; trigStackEntry.oldIdx = oldIdx; trigStackEntry.pTab = pTab; trigStackEntry.pNext = pParse->trigStack; trigStackEntry.ignoreJump = ignoreJump; pParse->trigStack = &trigStackEntry; sqlite3AuthContextPush(pParse, &sContext, p->name); /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); whenExpr = sqlite3ExprDup(db, p->pWhen); if( db->mallocFailed || sqlite3ExprResolveNames(&sNC, whenExpr) ){ pParse->trigStack = trigStackEntry.pNext; sqlite3ExprDelete(whenExpr); return 1; } sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1); sqlite3ExprDelete(whenExpr); codeTriggerProgram(pParse, p->step_list, orconf); /* Pop the entry off the trigger stack */ pParse->trigStack = trigStackEntry.pNext; sqlite3AuthContextPop(&sContext); sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); } } return 0; }
/* ** This is called to code FOR EACH ROW triggers. ** ** When the code that this function generates is executed, the following ** must be true: ** ** 1. No cursors may be open in the main database. (But newIdx and oldIdx ** can be indices of cursors in temporary tables. See below.) ** ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then ** a temporary vdbe cursor (index newIdx) must be open and pointing at ** a row containing values to be substituted for new.* expressions in the ** trigger program(s). ** ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** a temporary vdbe cursor (index oldIdx) must be open and pointing at ** a row containing values to be substituted for old.* expressions in the ** trigger program(s). ** */ int sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Table *pTab, /* The table to code triggers from */ int newIdx, /* The indice of the "new" row to access */ int oldIdx, /* The indice of the "old" row to access */ int orconf, /* ON CONFLICT policy */ int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ){ Trigger *pTrigger; TriggerStack *pStack; TriggerStack trigStackEntry; assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); assert(newIdx != -1 || oldIdx != -1); pTrigger = pTab->pTrigger; while( pTrigger ){ int fire_this = 0; /* determine whether we should code this trigger */ if( pTrigger->op == op && pTrigger->tr_tm == tr_tm ){ fire_this = 1; for(pStack=pParse->trigStack; pStack; pStack=pStack->pNext){ if( pStack->pTrigger==pTrigger ){ fire_this = 0; } } if( op == TK_UPDATE && pTrigger->pColumns && !checkColumnOverLap(pTrigger->pColumns, pChanges) ){ fire_this = 0; } } if( fire_this ){ int endTrigger; Expr * whenExpr; AuthContext sContext; NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; /* Push an entry on to the trigger stack */ trigStackEntry.pTrigger = pTrigger; trigStackEntry.newIdx = newIdx; trigStackEntry.oldIdx = oldIdx; trigStackEntry.pTab = pTab; trigStackEntry.pNext = pParse->trigStack; trigStackEntry.ignoreJump = ignoreJump; pParse->trigStack = &trigStackEntry; sqlite3AuthContextPush(pParse, &sContext, pTrigger->name); /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); whenExpr = sqlite3ExprDup(pTrigger->pWhen); if( sqlite3ExprResolveNames(&sNC, whenExpr) ){ pParse->trigStack = trigStackEntry.pNext; sqlite3ExprDelete(whenExpr); return 1; } sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1); sqlite3ExprDelete(whenExpr); codeTriggerProgram(pParse, pTrigger->step_list, orconf); /* Pop the entry off the trigger stack */ pParse->trigStack = trigStackEntry.pNext; sqlite3AuthContextPop(&sContext); sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); } pTrigger = pTrigger->pNext; } return 0; }