Example #1
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.
        **
        */
        static 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) */
            ref u32 piOldColMask, /* OUT: Mask of columns used from the OLD.* table */
            ref u32 piNewColMask  /* OUT: Mask of columns used from the NEW.* table */
            )
        {
            Trigger      p;
            sqlite3      db             = pParse.db;
            TriggerStack trigStackEntry = new TriggerStack();

            trigStackEntry.oldColMask = 0;
            trigStackEntry.newColMask = 0;

            Debug.Assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
            Debug.Assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER);

            Debug.Assert(newIdx != -1 || oldIdx != -1);

            for (p = pTrigger; p != null; p = p.pNext)
            {
                bool fire_this = false;

                /* 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. */
                Debug.Assert(p.pSchema != null);
                Debug.Assert(p.pTabSchema != null);
                Debug.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) != 0)
                {
                    TriggerStack pS; /* Pointer to trigger-stack entry */
                    for (pS = pParse.trigStack; pS != null && p != pS.pTrigger; pS = pS.pNext)
                    {
                    }
                    if (pS == null)
                    {
                        fire_this = true;
                    }
#if FALSE   // * 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;

#if !SQLITE_OMIT_TRACE
                    sqlite3VdbeAddOp4(pParse.pVdbe, OP_Trace, 0, 0, 0,
                                      sqlite3MPrintf(db, "-- TRIGGER %s", p.name),
                                      P4_DYNAMIC);
#endif
                    sNC        = new NameContext();// 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;
#if !SQLITE_OMIT_AUTHORIZATION
                    sqlite3AuthContextPush(pParse, sContext, p.name);
#endif

                    /* code the WHEN clause */
                    endTrigger = sqlite3VdbeMakeLabel(pParse.pVdbe);
                    whenExpr   = sqlite3ExprDup(db, p.pWhen, 0);
                    if (/* db.mallocFailed != 0 || */ sqlite3ResolveExprNames(sNC, ref whenExpr) != 0)
                    {
                        pParse.trigStack = trigStackEntry.pNext;
                        sqlite3ExprDelete(db, ref whenExpr);
                        return(1);
                    }
                    sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL);
                    sqlite3ExprDelete(db, ref whenExpr);

                    codeTriggerProgram(pParse, p.step_list, orconf);

                    /* Pop the entry off the trigger stack */
                    pParse.trigStack = trigStackEntry.pNext;
#if !SQLITE_OMIT_AUTHORIZATION
                    sqlite3AuthContextPop(sContext);
#endif
                    sqlite3VdbeResolveLabel(pParse.pVdbe, endTrigger);
                }
            }
            piOldColMask |= trigStackEntry.oldColMask; // if ( piOldColMask != 0 ) piOldColMask |= trigStackEntry.oldColMask;
            piNewColMask |= trigStackEntry.newColMask; // if ( piNewColMask != 0 ) piNewColMask |= trigStackEntry.newColMask;
            return(0);
        }
Example #2
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.
    **
    */
    static 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) */
    ref u32 piOldColMask,/* OUT: Mask of columns used from the OLD.* table */
    ref u32 piNewColMask /* OUT: Mask of columns used from the NEW.* table */
    )
    {
      Trigger p;
      sqlite3 db = pParse.db;
      TriggerStack trigStackEntry = new TriggerStack();

      trigStackEntry.oldColMask = 0;
      trigStackEntry.newColMask = 0;

      Debug.Assert( op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE );
      Debug.Assert( tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );

      Debug.Assert( newIdx != -1 || oldIdx != -1 );

      for ( p = pTrigger ; p != null ; p = p.pNext )
      {
        bool fire_this = false;

        /* 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. */
        Debug.Assert( p.pSchema != null );
        Debug.Assert( p.pTabSchema != null );
        Debug.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 ) != 0 )
        {
          TriggerStack pS;      /* Pointer to trigger-stack entry */
          for ( pS = pParse.trigStack ; pS != null && p != pS.pTrigger ; pS = pS.pNext ) { }
          if ( pS == null )
          {
            fire_this = true;
          }
#if FALSE   // * 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;

#if !SQLITE_OMIT_TRACE
          sqlite3VdbeAddOp4( pParse.pVdbe, OP_Trace, 0, 0, 0,
          sqlite3MPrintf( db, "-- TRIGGER %s", p.name ),
          P4_DYNAMIC );
#endif
          sNC = new NameContext();// 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;
#if !SQLITE_OMIT_AUTHORIZATION
sqlite3AuthContextPush( pParse, sContext, p.name );
#endif

          /* code the WHEN clause */
          endTrigger = sqlite3VdbeMakeLabel( pParse.pVdbe );
          whenExpr = sqlite3ExprDup( db, p.pWhen, 0 );
          if ( /* db.mallocFailed != 0 || */ sqlite3ResolveExprNames( sNC, ref whenExpr ) != 0 )
          {
            pParse.trigStack = trigStackEntry.pNext;
            sqlite3ExprDelete( db, ref whenExpr );
            return 1;
          }
          sqlite3ExprIfFalse( pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL );
          sqlite3ExprDelete( db, ref whenExpr );

          codeTriggerProgram( pParse, p.step_list, orconf );

          /* Pop the entry off the trigger stack */
          pParse.trigStack = trigStackEntry.pNext;
#if !SQLITE_OMIT_AUTHORIZATION
sqlite3AuthContextPop( sContext );
#endif
          sqlite3VdbeResolveLabel( pParse.pVdbe, endTrigger );
        }
      }
      piOldColMask |= trigStackEntry.oldColMask; // if ( piOldColMask != 0 ) piOldColMask |= trigStackEntry.oldColMask;
      piNewColMask |= trigStackEntry.newColMask; // if ( piNewColMask != 0 ) piNewColMask |= trigStackEntry.newColMask;
      return 0;
    }