/*
 ** Return a list of all triggers on table pTab if there exists at least
 ** one trigger that must be fired when an operation of type 'op' is
 ** performed on the table, and, if that operation is an UPDATE, if at
 ** least one of the columns in pChanges is being modified.
 */
SQLITE_PRIVATE Trigger *sqlite3TriggersExist(
                                             Parse *pParse,          /* Parse context */
                                             Table *pTab,            /* The table the contains the triggers */
                                             int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
                                             ExprList *pChanges,     /* Columns that change in an UPDATE statement */
                                             int *pMask              /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
){
    int mask = 0;
    Trigger *pList = 0;
    Trigger *p;
    
    if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
        pList = sqlite3TriggerList(pParse, pTab);
    }
    assert( pList==0 || IsVirtual(pTab)==0 );
    for(p=pList; p; p=p->pNext){
        if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
            mask |= p->tr_tm;
        }
    }
    if( pMask ){
        *pMask = mask;
    }
    return (mask ? pList : 0);
}
/*
 ** Triggers may access values stored in the old.* or new.* pseudo-table. 
 ** This function returns a 32-bit bitmask indicating which columns of the 
 ** old.* or new.* tables actually are used by triggers. This information 
 ** may be used by the caller, for example, to avoid having to load the entire
 ** old.* record into memory when executing an UPDATE or DELETE command.
 **
 ** Bit 0 of the returned mask is set if the left-most column of the
 ** table may be accessed using an [old|new].<col> reference. Bit 1 is set if
 ** the second leftmost column value is required, and so on. If there
 ** are more than 32 columns in the table, and at least one of the columns
 ** with an index greater than 32 may be accessed, 0xffffffff is returned.
 **
 ** It is not possible to determine if the old.rowid or new.rowid column is 
 ** accessed by triggers. The caller must always assume that it is.
 **
 ** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned
 ** applies to the old.* table. If 1, the new.* table.
 **
 ** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE
 ** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only
 ** included in the returned mask if the TRIGGER_BEFORE bit is set in the
 ** tr_tm parameter. Similarly, values accessed by AFTER triggers are only
 ** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm.
 */
SQLITE_PRIVATE u32 sqlite3TriggerColmask(
                                         Parse *pParse,       /* Parse context */
                                         Trigger *pTrigger,   /* List of triggers on table pTab */
                                         ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
                                         int isNew,           /* 1 for new.* ref mask, 0 for old.* ref mask */
                                         int tr_tm,           /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
                                         Table *pTab,         /* The table to code triggers from */
                                         int orconf           /* Default ON CONFLICT policy for trigger steps */
){
    const int op = pChanges ? TK_UPDATE : TK_DELETE;
    u32 mask = 0;
    Trigger *p;
    
    assert( isNew==1 || isNew==0 );
    for(p=pTrigger; p; p=p->pNext){
        if( p->op==op && (tr_tm&p->tr_tm)
           && checkColumnOverlap(p->pColumns,pChanges)
           ){
            TriggerPrg *pPrg;
            pPrg = getRowTrigger(pParse, p, pTab, orconf);
            if( pPrg ){
                mask |= pPrg->aColmask[isNew];
            }
        }
    }
    
    return mask;
}
/*
 ** This is called to code the required FOR EACH ROW triggers for an operation
 ** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
 ** is given by the op parameter. The tr_tm parameter determines whether the
 ** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then
 ** parameter pChanges is passed the list of columns being modified.
 **
 ** If there are no triggers that fire at the specified time for the specified
 ** operation on pTab, this function is a no-op.
 **
 ** The reg argument is the address of the first in an array of registers
 ** that contain the values substituted for the new.* and old.* references
 ** in the trigger program. If N is the number of columns in table pTab
 ** (a copy of pTab->nCol), then registers are populated as follows:
 **
 **   Register       Contains
 **   ------------------------------------------------------
 **   reg+0          OLD.rowid
 **   reg+1          OLD.* value of left-most column of pTab
 **   ...            ...
 **   reg+N          OLD.* value of right-most column of pTab
 **   reg+N+1        NEW.rowid
 **   reg+N+2        OLD.* value of left-most column of pTab
 **   ...            ...
 **   reg+N+N+1      NEW.* value of right-most column of pTab
 **
 ** For ON DELETE triggers, the registers containing the NEW.* values will
 ** never be accessed by the trigger program, so they are not allocated or 
 ** populated by the caller (there is no data to populate them with anyway). 
 ** Similarly, for ON INSERT triggers the values stored in the OLD.* registers
 ** are never accessed, and so are not allocated by the caller. So, for an
 ** ON INSERT trigger, the value passed to this function as parameter reg
 ** is not a readable register, although registers (reg+N) through 
 ** (reg+N+N+1) are.
 **
 ** Parameter orconf is the default conflict resolution algorithm for the
 ** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump
 ** is the instruction that control should jump to if a trigger program
 ** raises an IGNORE exception.
 */
SQLITE_PRIVATE void 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 reg,             /* The first in an array of registers (see above) */
                                          int orconf,          /* ON CONFLICT policy */
                                          int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
){
    Trigger *p;          /* Used to iterate through pTrigger list */
    
    assert( op==TK_UPDATE || op==TK_INSERT || op==TK_DELETE );
    assert( tr_tm==TRIGGER_BEFORE || tr_tm==TRIGGER_AFTER );
    assert( (op==TK_UPDATE)==(pChanges!=0) );
    
    for(p=pTrigger; p; p=p->pNext){
        
        /* 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==pParse->db->aDb[1].pSchema );
        
        /* Determine whether we should code this trigger */
        if( p->op==op 
           && p->tr_tm==tr_tm 
           && checkColumnOverlap(p->pColumns, pChanges)
           ){
            sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
        }
    }
}
Exemple #4
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).
**
** 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;
}