Example #1
0
        public uint TriggerColmask(Parse parse, ExprList changes, bool isNew, TRIGGER trtm, Table table, OE orconf)
        {
            TK   op      = (changes != null ? TK.UPDATE : TK.DELETE);
            int  isNewId = (isNew ? 1 : 0);
            uint mask    = 0;

            for (Trigger p = this; p != null; p = p.Next)
            {
                if (p.OP == op && (trtm & p.TRtm) != 0 && CheckColumnOverlap(p.Columns, changes))
                {
                    TriggerPrg prg = GetRowTrigger(parse, p, table, orconf);
                    if (prg != null)
                    {
                        mask |= prg.Colmasks[isNewId];
                    }
                }
            }
            return(mask);
        }
Example #2
0
        public void CodeRowTriggerDirect(Parse parse, Table table, int reg, OE orconf, int ignoreJump)
        {
            Vdbe       v   = parse.GetVdbe(); // Main VM
            TriggerPrg prg = GetRowTrigger(parse, this, table, orconf);

            Debug.Assert(prg != null || parse.Errs != 0 || parse.Ctx.MallocFailed);

            // Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program is a pointer to the sub-vdbe containing the trigger program.
            if (prg != null)
            {
                bool recursive = (Name != null && (parse.Ctx.Flags & Context.FLAG.RecTriggers) == 0);
                v.AddOp3(Core.OP.Program, reg, ignoreJump, ++parse.Mems);
                v.ChangeP4(-1, prg.Program, Vdbe.P4T.SUBPROGRAM);
#if DEBUG
                v.Comment("Call: %s.%s", (!string.IsNullOrEmpty(p.Name) ? p.Name : "fkey"), OnErrorText(orconf));
#endif
                // Set the P5 operand of the OP_Program instruction to non-zero if recursive invocation of this trigger program is disallowed. Recursive
                // invocation is disallowed if (a) the sub-program is really a trigger, not a foreign key action, and (b) the flag to enable recursive triggers is clear.
                v.ChangeP5((int)(recursive ? 1 : 0));
            }
        }
Example #3
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 = null;          /* Duplicate of trigger WHEN expression */
      Vdbe v;                     /* Temporary VM */
      NameContext sNC;            /* Name context for sub-vdbe */
      SubProgram pProgram = null; /* Sub-vdbe for trigger program */
      Parse pSubParse;            /* Parse context for sub-vdbe */
      int iEndTrigger = 0;        /* Label to jump to if WHEN is false */

      Debug.Assert( pTrigger.zName == null || pTab == tableOfTrigger( pTrigger ) );
      Debug.Assert( pTop.pVdbe != null );

      /* 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 = new TriggerPrg();// sqlite3DbMallocZero( db, sizeof( TriggerPrg ) );
      //if ( null == pPrg ) return 0;
      pPrg.pNext = pTop.pTriggerPrg;
      pTop.pTriggerPrg = pPrg;
      pPrg.pProgram = pProgram = new SubProgram();// sqlite3DbMallocZero( db, sizeof( SubProgram ) );
      //if( null==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 = new Parse();// sqlite3StackAllocZero( db, sizeof( Parse ) );
      //if ( null == pSubParse ) return null;
      sNC = new NameContext();// 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 != null )
      {
#if SQLITE_DEBUG
        VdbeComment( v, "Start: %s.%s (%s %s%s%s ON %s)",
          pTrigger.zName != null ? 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
        );
#endif
#if !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 != null )
        {
          pWhen = sqlite3ExprDup( db, pTrigger.pWhen, 0 );
          if ( SQLITE_OK == sqlite3ResolveExprNames( sNC, ref pWhen )
            //&& db.mallocFailed==0 
          )
          {
            iEndTrigger = sqlite3VdbeMakeLabel( v );
            sqlite3ExprIfFalse( pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL );
          }
          sqlite3ExprDelete( db, ref 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 != 0 )
        {
          sqlite3VdbeResolveLabel( v, iEndTrigger );
        }
        sqlite3VdbeAddOp0( v, OP_Halt );
#if SQLITE_DEBUG
        VdbeComment( v, "End: %s.%s", pTrigger.zName, onErrorText( orconf ) );
#endif
        transferParseError( pParse, pSubParse );
        //if( db.mallocFailed==0 ){
        pProgram.aOp = sqlite3VdbeTakeOpArray( v, ref pProgram.nOp, ref pTop.nMaxArg );
        //}
        pProgram.nMem = pSubParse.nMem;
        pProgram.nCsr = pSubParse.nTab;
        pProgram.token = pTrigger.GetHashCode();
        pPrg.aColmask[0] = pSubParse.oldmask;
        pPrg.aColmask[1] = pSubParse.newmask;
        sqlite3VdbeDelete( ref v );
      }

      Debug.Assert( null == pSubParse.pAinc && null == pSubParse.pZombieTab );
      Debug.Assert( null == pSubParse.pTriggerPrg && 0 == pSubParse.nMaxArg );
      //sqlite3StackFree(db, pSubParse);

      return pPrg;
    }
Example #4
0
        public static RC Prepare_(Context ctx, string sql, int bytes, bool isPrepareV2, Vdbe reprepare, ref Vdbe stmtOut, ref string tailOut)
        {
            stmtOut = null;
            tailOut = null;
            string errMsg = null; // Error message
            RC     rc     = RC.OK;
            int    i;

            // Allocate the parsing context
            Parse parse = new Parse(); // Parsing context

            if (parse == null)
            {
                rc = RC.NOMEM;
                goto end_prepare;
            }
            parse.Reprepare      = reprepare;
            parse.LastToken.data = null; //: C#?
            Debug.Assert(tailOut == null);
            Debug.Assert(!ctx.MallocFailed);
            Debug.Assert(MutexEx.Held(ctx.Mutex));

            // Check to verify that it is possible to get a read lock on all database schemas.  The inability to get a read lock indicates that
            // some other database connection is holding a write-lock, which in turn means that the other connection has made uncommitted changes
            // to the schema.
            //
            // Were we to proceed and prepare the statement against the uncommitted schema changes and if those schema changes are subsequently rolled
            // back and different changes are made in their place, then when this prepared statement goes to run the schema cookie would fail to detect
            // the schema change.  Disaster would follow.
            //
            // This thread is currently holding mutexes on all Btrees (because of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it
            // is not possible for another thread to start a new schema change while this routine is running.  Hence, we do not need to hold
            // locks on the schema, we just need to make sure nobody else is holding them.
            //
            // Note that setting READ_UNCOMMITTED overrides most lock detection, but it does *not* override schema lock detection, so this all still
            // works even if READ_UNCOMMITTED is set.
            for (i = 0; i < ctx.DBs.length; i++)
            {
                Btree bt = ctx.DBs[i].Bt;
                if (bt != null)
                {
                    Debug.Assert(bt.HoldsMutex());
                    rc = bt.SchemaLocked();
                    if (rc != 0)
                    {
                        string dbName = ctx.DBs[i].Name;
                        sqlite3Error(ctx, rc, "database schema is locked: %s", dbName);
                        C.ASSERTCOVERAGE((ctx.Flags & Context.FLAG.ReadUncommitted) != 0);
                        goto end_prepare;
                    }
                }
            }

            VTable.UnlockList(ctx);

            parse.Ctx        = ctx;
            parse.QueryLoops = (double)1;
            if (bytes >= 0 && (bytes == 0 || sql[bytes - 1] != 0))
            {
                int maxLen = ctx.aLimit[SQLITE_LIMIT_SQL_LENGTH];
                C.ASSERTCOVERAGE(bytes == maxLen);
                C.ASSERTCOVERAGE(bytes == maxLen + 1);
                if (bytes > maxLen)
                {
                    sqlite3Error(ctx, RC.TOOBIG, "statement too long");
                    rc = SysEx.ApiExit(ctx, RC.TOOBIG);
                    goto end_prepare;
                }
                string sqlCopy = sql.Substring(0, bytes);
                if (sqlCopy != null)
                {
                    parse.RunParser(sqlCopy, ref errMsg);
                    C._tagfree(ctx, ref sqlCopy);
                    parse.Tail = null; //: &sql[parse->Tail - sqlCopy];
                }
                else
                {
                    parse.Tail = null; //: &sql[bytes];
                }
            }
            else
            {
                parse.RunParser(sql, ref errMsg);
            }
            Debug.Assert((int)parse.QueryLoops == 1);

            if (ctx.MallocFailed)
            {
                parse.RC = RC.NOMEM;
            }
            if (parse.RC == RC.DONE)
            {
                parse.RC = RC.OK;
            }
            if (parse.CheckSchema != 0)
            {
                SchemaIsValid(parse);
            }
            if (ctx.MallocFailed)
            {
                parse.RC = RC.NOMEM;
            }
            tailOut = (parse.Tail == null ? null : parse.Tail.ToString());
            rc      = parse.RC;

            Vdbe v = parse.V;

#if !OMIT_EXPLAIN
            if (rc == RC.OK && parse.V != null && parse.Explain != 0)
            {
                int first, max;
                if (parse.Explain == 2)
                {
                    v.SetNumCols(4);
                    first = 8;
                    max   = 12;
                }
                else
                {
                    v.SetNumCols(8);
                    first = 0;
                    max   = 8;
                }
                for (i = first; i < max; i++)
                {
                    v.SetColName(i - first, COLNAME_NAME, _colName[i], C.DESTRUCTOR_STATIC);
                }
            }
#endif

            Debug.Assert(!ctx.Init.Busy || !isPrepareV2);
            if (!ctx.Init.Busy)
            {
                Vdbe.SetSql(v, sql, (int)(sql.Length - (parse.Tail == null ? 0 : parse.Tail.Length)), isPrepareV2);
            }
            if (v != null && (rc != RC.OK || ctx.MallocFailed))
            {
                v.Finalize();
                Debug.Assert(stmtOut == null);
            }
            else
            {
                stmtOut = v;
            }

            if (errMsg != null)
            {
                sqlite3Error(ctx, rc, "%s", errMsg);
                C._tagfree(ctx, ref errMsg);
            }
            else
            {
                sqlite3Error(ctx, rc, null);
            }

            // Delete any TriggerPrg structures allocated while parsing this statement.
            while (parse.TriggerPrg != null)
            {
                TriggerPrg t = parse.TriggerPrg;
                parse.TriggerPrg = t.Next;
                C._tagfree(ctx, ref t);
            }

end_prepare:
            //sqlite3StackFree( db, pParse );
            rc = SysEx.ApiExit(ctx, rc);
            Debug.Assert((RC)((int)rc & ctx.ErrMask) == rc);
            return(rc);
        }
Example #5
0
        /*
        ** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
        */
        static int sqlite3Prepare(
            sqlite3 db,              /* Database handle. */
            string zSql,             /* UTF-8 encoded SQL statement. */
            int nBytes,              /* Length of zSql in bytes. */
            int saveSqlFlag,         /* True to copy SQL text into the sqlite3_stmt */
            Vdbe pReprepare,         /* VM being reprepared */
            ref sqlite3_stmt ppStmt, /* OUT: A pointer to the prepared statement */
            ref string pzTail        /* OUT: End of parsed string */
            )
        {
            Parse  pParse;              /* Parsing context */
            string zErrMsg = "";        /* Error message */
            int    rc      = SQLITE_OK; /* Result code */
            int    i;                   /* Loop counter */

            ppStmt = null;
            pzTail = null;

            /* Allocate the parsing context */
            pParse = new Parse();//sqlite3StackAllocZero(db, sizeof(*pParse));
            //if ( pParse == null )
            //{
            //  rc = SQLITE_NOMEM;
            //  goto end_prepare;
            //}
            pParse.pReprepare   = pReprepare;
            pParse.sLastToken.z = "";

            //  assert( ppStmt && *ppStmt==0 );
            //Debug.Assert( 0 == db.mallocFailed );
            Debug.Assert(sqlite3_mutex_held(db.mutex));

            /* Check to verify that it is possible to get a read lock on all
            ** database schemas.  The inability to get a read lock indicates that
            ** some other database connection is holding a write-lock, which in
            ** turn means that the other connection has made uncommitted changes
            ** to the schema.
            **
            ** Were we to proceed and prepare the statement against the uncommitted
            ** schema changes and if those schema changes are subsequently rolled
            ** back and different changes are made in their place, then when this
            ** prepared statement goes to run the schema cookie would fail to detect
            ** the schema change.  Disaster would follow.
            **
            ** This thread is currently holding mutexes on all Btrees (because
            ** of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it
            ** is not possible for another thread to start a new schema change
            ** while this routine is running.  Hence, we do not need to hold
            ** locks on the schema, we just need to make sure nobody else is
            ** holding them.
            **
            ** Note that setting READ_UNCOMMITTED overrides most lock detection,
            ** but it does *not* override schema lock detection, so this all still
            ** works even if READ_UNCOMMITTED is set.
            */
            for (i = 0; i < db.nDb; i++)
            {
                Btree pBt = db.aDb[i].pBt;
                if (pBt != null)
                {
                    Debug.Assert(sqlite3BtreeHoldsMutex(pBt));
                    rc = sqlite3BtreeSchemaLocked(pBt);
                    if (rc != 0)
                    {
                        string zDb = db.aDb[i].zName;
                        sqlite3Error(db, rc, "database schema is locked: %s", zDb);
                        testcase(db.flags & SQLITE_ReadUncommitted);
                        goto end_prepare;
                    }
                }
            }

            sqlite3VtabUnlockList(db);

            pParse.db         = db;
            pParse.nQueryLoop = (double)1;
            if (nBytes >= 0 && (nBytes == 0 || zSql[nBytes - 1] != 0))
            {
                string zSqlCopy;
                int    mxLen = db.aLimit[SQLITE_LIMIT_SQL_LENGTH];
                testcase(nBytes == mxLen);
                testcase(nBytes == mxLen + 1);
                if (nBytes > mxLen)
                {
                    sqlite3Error(db, SQLITE_TOOBIG, "statement too long");
                    rc = sqlite3ApiExit(db, SQLITE_TOOBIG);
                    goto end_prepare;
                }
                zSqlCopy = zSql.Substring(0, nBytes);// sqlite3DbStrNDup(db, zSql, nBytes);
                if (zSqlCopy != null)
                {
                    sqlite3RunParser(pParse, zSqlCopy, ref zErrMsg);
                    sqlite3DbFree(db, ref zSqlCopy);
                    //pParse->zTail = &zSql[pParse->zTail-zSqlCopy];
                }
                else
                {
                    //pParse->zTail = &zSql[nBytes];
                }
            }
            else
            {
                sqlite3RunParser(pParse, zSql, ref zErrMsg);
            }
            Debug.Assert(1 == (int)pParse.nQueryLoop);

            //if ( db.mallocFailed != 0 )
            //{
            //  pParse.rc = SQLITE_NOMEM;
            //}
            if (pParse.rc == SQLITE_DONE)
            {
                pParse.rc = SQLITE_OK;
            }
            if (pParse.checkSchema != 0)
            {
                schemaIsValid(pParse);
            }
            //if ( db.mallocFailed != 0 )
            //{
            //  pParse.rc = SQLITE_NOMEM;
            //}
            //if (pzTail != null)
            {
                pzTail = pParse.zTail == null ? "" : pParse.zTail.ToString();
            }
            rc = pParse.rc;
#if !SQLITE_OMIT_EXPLAIN
            if (rc == SQLITE_OK && pParse.pVdbe != null && pParse.explain != 0)
            {
                string[] azColName = new string[] {
                    "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
                    "selectid", "order", "from", "detail"
                };
                int iFirst, mx;
                if (pParse.explain == 2)
                {
                    sqlite3VdbeSetNumCols(pParse.pVdbe, 4);
                    iFirst = 8;
                    mx     = 12;
                }
                else
                {
                    sqlite3VdbeSetNumCols(pParse.pVdbe, 8);
                    iFirst = 0;
                    mx     = 8;
                }
                for (i = iFirst; i < mx; i++)
                {
                    sqlite3VdbeSetColName(pParse.pVdbe, i - iFirst, COLNAME_NAME,
                                          azColName[i], SQLITE_STATIC);
                }
            }
#endif

            Debug.Assert(db.init.busy == 0 || saveSqlFlag == 0);
            if (db.init.busy == 0)
            {
                Vdbe pVdbe = pParse.pVdbe;
                sqlite3VdbeSetSql(pVdbe, zSql, (int)(zSql.Length - (pParse.zTail == null ? 0 : pParse.zTail.Length)), saveSqlFlag);
            }
            if (pParse.pVdbe != null && (rc != SQLITE_OK /*|| db.mallocFailed != 0 */))
            {
                sqlite3VdbeFinalize(ref pParse.pVdbe);
                //Debug.Assert( ppStmt == null );
            }
            else
            {
                ppStmt = pParse.pVdbe;
            }

            if (zErrMsg != "")
            {
                sqlite3Error(db, rc, "%s", zErrMsg);
                sqlite3DbFree(db, ref zErrMsg);
            }
            else
            {
                sqlite3Error(db, rc, 0);
            }

            /* Delete any TriggerPrg structures allocated while parsing this statement. */
            while (pParse.pTriggerPrg != null)
            {
                TriggerPrg pT = pParse.pTriggerPrg;
                pParse.pTriggerPrg = pT.pNext;
                sqlite3DbFree(db, ref pT);
            }

end_prepare:

            //sqlite3StackFree( db, pParse );
            rc = sqlite3ApiExit(db, rc);
            Debug.Assert((rc & db.errMask) == rc);
            return(rc);
        }
Example #6
0
        static TriggerPrg CodeRowTrigger(Parse parse, Trigger trigger, Table table, OE orconf)
        {
            Parse   top = E.Parse_Toplevel(parse);
            Context ctx = parse.Ctx; // Database handle

            Debug.Assert(trigger.Name == null || table == TableOfTrigger(trigger));
            Debug.Assert(top.V != null);

            // 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.
            TriggerPrg prg = new TriggerPrg(); // Value to return //: _tagalloc(ctx, sizeof(TriggerPrg), true);

            if (prg == null)
            {
                return(null);
            }
            prg.Next       = top.TriggerPrg;
            top.TriggerPrg = prg;
            Vdbe.SubProgram program;                       // Sub-vdbe for trigger program
            prg.Program = program = new Vdbe.SubProgram(); // sqlite3DbMallocZero( db, sizeof( SubProgram ) );
            if (program == null)
            {
                return(null);
            }
            top.V.LinkSubProgram(program);
            prg.Trigger     = trigger;
            prg.Orconf      = orconf;
            prg.Colmasks[0] = 0xffffffff;
            prg.Colmasks[1] = 0xffffffff;

            // Allocate and populate a new Parse context to use for coding the trigger sub-program.
            Parse subParse = new Parse(); // Parse context for sub-vdbe //: _stackalloc(ctx, sizeof(Parse), true);

            if (subParse == null)
            {
                return(null);
            }
            NameContext sNC = new NameContext(); // Name context for sub-vdbe

            sNC.Parse            = subParse;
            subParse.Ctx         = ctx;
            subParse.TriggerTab  = table;
            subParse.Toplevel    = top;
            subParse.AuthContext = trigger.Name;
            subParse.TriggerOp   = trigger.OP;
            subParse.QueryLoops  = parse.QueryLoops;

            int  endTrigger = 0;                  // Label to jump to if WHEN is false
            Vdbe v          = subParse.GetVdbe(); // Temporary VM

            if (v != null)
            {
#if DEBUG
                v.Comment("Start: %s.%s (%s %s%s%s ON %s)",
                          trigger.Name, OnErrorText(orconf),
                          (trigger.TRtm == TRIGGER.BEFORE ? "BEFORE" : "AFTER"),
                          (trigger.OP == TK.UPDATE ? "UPDATE" : string.Empty),
                          (trigger.OP == TK.INSERT ? "INSERT" : string.Empty),
                          (trigger.OP == TK.DELETE ? "DELETE" : string.Empty),
                          table.Name);
#endif
#if !OMIT_TRACE
                v.ChangeP4(-1, C._mtagprintf(ctx, "-- TRIGGER %s", trigger.Name), Vdbe.P4T.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 (trigger.When != null)
                {
                    Expr when = Expr.Dup(ctx, trigger.When, 0); // Duplicate of trigger WHEN expression
                    if (ResolveExprNames(sNC, ref when) == RC.OK && !ctx.MallocFailed)
                    {
                        endTrigger = v.MakeLabel();
                        subParse.IfFalse(when, endTrigger, RC_JUMPIFNULL);
                    }
                    Expr.Delete(ctx, ref when);
                }

                // Code the trigger program into the sub-vdbe.
                CodeTriggerProgram(subParse, trigger.StepList, orconf);

                // Insert an OP_Halt at the end of the sub-program.
                if (endTrigger != 0)
                {
                    v.ResolveLabel(endTrigger);
                }
                v.AddOp0(Core.OP.Halt);
#if DEBUG
                v.Comment("End: %s.%s", trigger.Name, OnErrorText(orconf));
#endif
                TransferParseError(parse, subParse);
                if (!ctx.MallocFailed)
                {
                    program.Ops.data = v.TakeOpArray(ref program.Ops.length, ref top.MaxArgs);
                }
                program.Mems    = subParse.Mems;
                program.Csrs    = subParse.Tabs;
                program.Token   = trigger.GetHashCode();
                prg.Colmasks[0] = subParse.Oldmask;
                prg.Colmasks[1] = subParse.Newmask;
                Vdbe.Delete(v);
            }

            Debug.Assert(subParse.Ainc == null && subParse.ZombieTab == null);
            Debug.Assert(subParse.TriggerPrg == null && subParse.MaxArgs == 0);
            C._stackfree(ctx, ref subParse);

            return(prg);
        }
Example #7
0
        static TriggerPrg CodeRowTrigger(Parse parse, Trigger trigger, Table table, OE orconf)
        {
            Parse top = E.Parse_Toplevel(parse);
            Context ctx = parse.Ctx; // Database handle

            Debug.Assert(trigger.Name == null || table == TableOfTrigger(trigger));
            Debug.Assert(top.V != null);

            // 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.
            TriggerPrg prg = new TriggerPrg(); // Value to return //: _tagalloc(ctx, sizeof(TriggerPrg), true);
            if (prg == null) return null;
            prg.Next = top.TriggerPrg;
            top.TriggerPrg = prg;
            Vdbe.SubProgram program; // Sub-vdbe for trigger program
            prg.Program = program = new Vdbe.SubProgram();// sqlite3DbMallocZero( db, sizeof( SubProgram ) );
            if (program == null) return null;
            top.V.LinkSubProgram(program);
            prg.Trigger = trigger;
            prg.Orconf = orconf;
            prg.Colmasks[0] = 0xffffffff;
            prg.Colmasks[1] = 0xffffffff;

            // Allocate and populate a new Parse context to use for coding the trigger sub-program.
            Parse subParse = new Parse(); // Parse context for sub-vdbe //: _stackalloc(ctx, sizeof(Parse), true);
            if (subParse == null) return null;
            NameContext sNC = new NameContext(); // Name context for sub-vdbe
            sNC.Parse = subParse;
            subParse.Ctx = ctx;
            subParse.TriggerTab = table;
            subParse.Toplevel = top;
            subParse.AuthContext = trigger.Name;
            subParse.TriggerOp = trigger.OP;
            subParse.QueryLoops = parse.QueryLoops;

            int endTrigger = 0; // Label to jump to if WHEN is false
            Vdbe v = subParse.GetVdbe(); // Temporary VM
            if (v != null)
            {
#if DEBUG
                v.Comment("Start: %s.%s (%s %s%s%s ON %s)",
                    trigger.Name, OnErrorText(orconf),
                    (trigger.TRtm == TRIGGER.BEFORE ? "BEFORE" : "AFTER"),
                    (trigger.OP == TK.UPDATE ? "UPDATE" : string.Empty),
                    (trigger.OP == TK.INSERT ? "INSERT" : string.Empty),
                    (trigger.OP == TK.DELETE ? "DELETE" : string.Empty),
                    table.Name);
#endif
#if !OMIT_TRACE
                v.ChangeP4(-1, C._mtagprintf(ctx, "-- TRIGGER %s", trigger.Name), Vdbe.P4T.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 (trigger.When != null)
                {
                    Expr when = Expr.Dup(ctx, trigger.When, 0); // Duplicate of trigger WHEN expression
                    if (ResolveExprNames(sNC, ref when) == RC.OK && !ctx.MallocFailed)
                    {
                        endTrigger = v.MakeLabel();
                        subParse.IfFalse(when, endTrigger, RC_JUMPIFNULL);
                    }
                    Expr.Delete(ctx, ref when);
                }

                // Code the trigger program into the sub-vdbe.
                CodeTriggerProgram(subParse, trigger.StepList, orconf);

                // Insert an OP_Halt at the end of the sub-program.
                if (endTrigger != 0)
                    v.ResolveLabel(endTrigger);
                v.AddOp0(Core.OP.Halt);
#if DEBUG
                v.Comment("End: %s.%s", trigger.Name, OnErrorText(orconf));
#endif
                TransferParseError(parse, subParse);
                if (!ctx.MallocFailed)
                    program.Ops.data = v.TakeOpArray(ref program.Ops.length, ref top.MaxArgs);
                program.Mems = subParse.Mems;
                program.Csrs = subParse.Tabs;
                program.Token = trigger.GetHashCode();
                prg.Colmasks[0] = subParse.Oldmask;
                prg.Colmasks[1] = subParse.Newmask;
                Vdbe.Delete(v);
            }

            Debug.Assert(subParse.Ainc == null && subParse.ZombieTab == null);
            Debug.Assert(subParse.TriggerPrg == null && subParse.MaxArgs == 0);
            C._stackfree(ctx, ref subParse);

            return prg;
        }