Ejemplo n.º 1
0
        public static void DropTriggerPtr(Parse parse, Trigger trigger)
        {
            Context ctx = parse.Ctx;
            int     db  = Prepare.SchemaToIndex(ctx, trigger.Schema);

            Debug.Assert(db >= 0 && db < ctx.DBs.length);
            Table table = TableOfTrigger(trigger);

            Debug.Assert(table != null);
            Debug.Assert(table.Schema == trigger.Schema || db == 1);
#if !OMIT_AUTHORIZATION
            {
                AUTH   code      = AUTH.DROP_TRIGGER;
                string dbName    = ctx.DBs[db].Name;
                string tableName = E.SCHEMA_TABLE(db);
                if (db == 1)
                {
                    code = AUTH.DROP_TEMP_TRIGGER;
                }
                if (Auth.Check(parse, code, trigger.Name, table.Name, dbName) || Auth.Check(parse, AUTH.DELETE, tableName, null, dbName))
                {
                    return;
                }
            }
#endif

            // Generate code to destroy the database record of the trigger.
            Debug.Assert(table != null);
            Vdbe v = parse.GetVdbe();
            if (v != null)
            {
                parse.BeginWriteOperation(0, db);
                parse.OpenMasterTable(db);
                int base_ = v.AddOpList(_dropTrigger.Length, _dropTrigger);
                v.ChangeP4(base_ + 1, trigger.Name, Vdbe.P4T.TRANSIENT);
                v.ChangeP4(base_ + 4, "trigger", Vdbe.P4T.STATIC);
                parse.ChangeCookie(db);
                v.AddOp2(Core.OP.Close, 0, 0);
                v.AddOp4(Core.OP.DropTrigger, db, 0, 0, trigger.Name, 0);
                if (parse.Mems < 3)
                {
                    parse.Mems = 3;
                }
            }
        }
Ejemplo n.º 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));
            }
        }
Ejemplo n.º 3
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);
        }
Ejemplo n.º 4
0
        public static void FinishTrigger(Parse parse, TriggerStep stepList, Token all)
        {
            Trigger trig      = parse.NewTrigger; // Trigger being finished
            Context ctx       = parse.Ctx;        // The database
            Token   nameToken = new Token();      // Trigger name for error reporting

            parse.NewTrigger = null;
            if (C._NEVER(parse.Errs != 0) || trig == null)
            {
                goto triggerfinish_cleanup;
            }
            string name = trig.Name;                                     // Name of trigger
            int    db   = Prepare.SchemaToIndex(parse.Ctx, trig.Schema); // Database containing the trigger

            trig.StepList = stepList;
            while (stepList != null)
            {
                stepList.Trig = trig;
                stepList      = stepList.Next;
            }
            nameToken.data   = trig.Name;
            nameToken.length = (uint)nameToken.data.Length;
            DbFixer sFix = new DbFixer(); // Fixer object

            if (sFix.FixInit(parse, db, "trigger", nameToken) && sFix.FixTriggerStep(trig.StepList))
            {
                goto triggerfinish_cleanup;
            }

            // if we are not initializing, build the sqlite_master entry
            if (ctx.Init.Busy)
            {
                // Make an entry in the sqlite_master table
                Vdbe v = parse.GetVdbe();
                if (v == null)
                {
                    goto triggerfinish_cleanup;
                }
                parse.BeginWriteOperation(0, db);
                string z = all.data.Substring(0, (int)all.length); //: _tagstrndup(ctx, (char *)all->data, all->length);
                parse.NestedParse("INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", ctx.DBs[db].Name, E.SCHEMA_TABLE(db), name, trig.Table, z);
                C._tagfree(ctx, ref z);
                parse.ChangeCookie(db);
                v.AddParseSchemaOp(db, C._mtagprintf(ctx, "type='trigger' AND name='%q'", name));
            }

            if (!ctx.Init.Busy)
            {
                Trigger link = trig;
                Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null));
                trig = ctx.DBs[db].Schema.TriggerHash.Insert(name, name.Length, trig);
                if (trig != null)
                {
                    ctx.MallocFailed = true;
                }
                else if (link.Schema == link.TabSchema)
                {
                    int   tableLength = link.Table.Length;
                    Table table       = (Table)link.TabSchema.TableHash.Find(link.Table, tableLength, (Table)null);
                    Debug.Assert(table != null);
                    link.Next      = table.Triggers;
                    table.Triggers = link;
                }
            }

triggerfinish_cleanup:
            DeleteTrigger(ctx, ref trig);
            Debug.Assert(parse.NewTrigger == null);
            DeleteTriggerStep(ctx, ref stepList);
        }
Ejemplo n.º 5
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;
        }
Ejemplo n.º 6
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));
            }
        }
Ejemplo n.º 7
0
        public static void FinishTrigger(Parse parse, TriggerStep stepList, Token all)
        {
            Trigger trig = parse.NewTrigger; // Trigger being finished
            Context ctx = parse.Ctx; // The database
            Token nameToken = new Token(); // Trigger name for error reporting

            parse.NewTrigger = null;
            if (C._NEVER(parse.Errs != 0) || trig == null) goto triggerfinish_cleanup;
            string name = trig.Name; // Name of trigger
            int db = Prepare.SchemaToIndex(parse.Ctx, trig.Schema); // Database containing the trigger
            trig.StepList = stepList;
            while (stepList != null)
            {
                stepList.Trig = trig;
                stepList = stepList.Next;
            }
            nameToken.data = trig.Name;
            nameToken.length = (uint)nameToken.data.Length;
            DbFixer sFix = new DbFixer(); // Fixer object
            if (sFix.FixInit(parse, db, "trigger", nameToken) && sFix.FixTriggerStep(trig.StepList))
                goto triggerfinish_cleanup;

            // if we are not initializing, build the sqlite_master entry
            if (ctx.Init.Busy)
            {
                // Make an entry in the sqlite_master table
                Vdbe v = parse.GetVdbe();
                if (v == null) goto triggerfinish_cleanup;
                parse.BeginWriteOperation(0, db);
                string z = all.data.Substring(0, (int)all.length); //: _tagstrndup(ctx, (char *)all->data, all->length);
                parse.NestedParse("INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", ctx.DBs[db].Name, E.SCHEMA_TABLE(db), name, trig.Table, z);
                C._tagfree(ctx, ref z);
                parse.ChangeCookie(db);
                v.AddParseSchemaOp(db, C._mtagprintf(ctx, "type='trigger' AND name='%q'", name));
            }

            if (!ctx.Init.Busy)
            {
                Trigger link = trig;
                Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null));
                trig = ctx.DBs[db].Schema.TriggerHash.Insert(name, name.Length, trig);
                if (trig != null)
                    ctx.MallocFailed = true;
                else if (link.Schema == link.TabSchema)
                {
                    int tableLength = link.Table.Length;
                    Table table = (Table)link.TabSchema.TableHash.Find(link.Table, tableLength, (Table)null);
                    Debug.Assert(table != null);
                    link.Next = table.Triggers;
                    table.Triggers = link;
                }
            }

        triggerfinish_cleanup:
            DeleteTrigger(ctx, ref trig);
            Debug.Assert(parse.NewTrigger == null);
            DeleteTriggerStep(ctx, ref stepList);
        }
Ejemplo n.º 8
0
        public static void DropTriggerPtr(Parse parse, Trigger trigger)
        {
            Context ctx = parse.Ctx;
            int db = Prepare.SchemaToIndex(ctx, trigger.Schema);
            Debug.Assert(db >= 0 && db < ctx.DBs.length);
            Table table = TableOfTrigger(trigger);
            Debug.Assert(table != null);
            Debug.Assert(table.Schema == trigger.Schema || db == 1);
#if !OMIT_AUTHORIZATION
            {
                AUTH code = AUTH.DROP_TRIGGER;
                string dbName = ctx.DBs[db].Name;
                string tableName = E.SCHEMA_TABLE(db);
                if (db == 1) code = AUTH.DROP_TEMP_TRIGGER;
                if (Auth.Check(parse, code, trigger.Name, table.Name, dbName) || Auth.Check(parse, AUTH.DELETE, tableName, null, dbName))
                    return;
            }
#endif

            // Generate code to destroy the database record of the trigger.
            Debug.Assert(table != null);
            Vdbe v = parse.GetVdbe();
            if (v != null)
            {
                parse.BeginWriteOperation(0, db);
                parse.OpenMasterTable(db);
                int base_ = v.AddOpList(_dropTrigger.Length, _dropTrigger);
                v.ChangeP4(base_ + 1, trigger.Name, Vdbe.P4T.TRANSIENT);
                v.ChangeP4(base_ + 4, "trigger", Vdbe.P4T.STATIC);
                parse.ChangeCookie(db);
                v.AddOp2(Core.OP.Close, 0, 0);
                v.AddOp4(Core.OP.DropTrigger, db, 0, 0, trigger.Name, 0);
                if (parse.Mems < 3)
                    parse.Mems = 3;
            }
        }
Ejemplo n.º 9
0
        static void FKScanChildren(Parse parse, SrcList src, Table table, Index index, FKey fkey, int[] cols, int regDataId, int incr)
        {
            Context ctx    = parse.Ctx; // Database handle
            Vdbe    v      = parse.GetVdbe();
            Expr    where_ = null;      // WHERE clause to scan with

            Debug.Assert(index == null || index.Table == table);
            int fkIfZero = 0; // Address of OP_FkIfZero

            if (incr < 0)
            {
                fkIfZero = v.AddOp2(OP.FkIfZero, fkey.IsDeferred, 0);
            }

            // Create an Expr object representing an SQL expression like:
            //
            //   <parent-key1> = <child-key1> AND <parent-key2> = <child-key2> ...
            //
            // The collation sequence used for the comparison should be that of the parent key columns. The affinity of the parent key column should
            // be applied to each child key value before the comparison takes place.
            for (int i = 0; i < fkey.Cols.length; i++)
            {
                int  col;                                      // Index of column in child table
                Expr left = Expr.Expr(ctx, TK.REGISTER, null); // Value from parent table row
                if (left != null)
                {
                    // Set the collation sequence and affinity of the LHS of each TK_EQ expression to the parent key column defaults.
                    if (index != null)
                    {
                        col = index.Columns[i];
                        Column colObj = table.Cols[col];
                        if (table.PKey == col)
                        {
                            col = -1;
                        }
                        left.TableId = regDataId + col + 1;
                        left.Aff     = colObj.Affinity;
                        string collName = colObj.Coll;
                        if (collName == null)
                        {
                            collName = ctx.DefaultColl.Name;
                        }
                        left = Expr.AddCollateString(parse, left, collName);
                    }
                    else
                    {
                        left.TableId = regDataId;
                        left.Aff     = AFF.INTEGER;
                    }
                }
                col = (cols != null ? cols[i] : fkey.Cols[0].From);
                Debug.Assert(col >= 0);
                string colName = fkey.From.Cols[col].Name;                 // Name of column in child table
                Expr   right   = Expr.Expr(ctx, TK.ID, colName);           // Column ref to child table
                Expr   eq      = Expr.PExpr(parse, TK.EQ, left, right, 0); // Expression (pLeft = pRight)
                where_ = Expr.And(ctx, where_, eq);
            }

            // If the child table is the same as the parent table, and this scan is taking place as part of a DELETE operation (operation D.2), omit the
            // row being deleted from the scan by adding ($rowid != rowid) to the WHERE clause, where $rowid is the rowid of the row being deleted.
            if (table == fkey.From && incr > 0)
            {
                Expr left  = Expr.Expr(ctx, TK.REGISTER, null); // Value from parent table row
                Expr right = Expr.Expr(ctx, TK.COLUMN, null);   // Column ref to child table
                if (left != null && right != null)
                {
                    left.TableId   = regDataId;
                    left.Aff       = AFF.INTEGER;
                    right.TableId  = src.Ids[0].Cursor;
                    right.ColumnId = -1;
                }
                Expr eq = Expr.PExpr(parse, TK.NE, left, right, 0); // Expression (pLeft = pRight)
                where_ = Expr.And(ctx, where_, eq);
            }

            // Resolve the references in the WHERE clause.
            NameContext nameContext;                 // Context used to resolve WHERE clause

            nameContext         = new NameContext(); // memset( &sNameContext, 0, sizeof( NameContext ) );
            nameContext.SrcList = src;
            nameContext.Parse   = parse;
            ResolveExprNames(nameContext, ref where_);

            // Create VDBE to loop through the entries in src that match the WHERE clause. If the constraint is not deferred, throw an exception for
            // each row found. Otherwise, for deferred constraints, increment the deferred constraint counter by incr for each row selected.
            ExprList  dummy     = null;
            WhereInfo whereInfo = Where.Begin(parse, src, where_, ref dummy, 0); // Context used by sqlite3WhereXXX()

            if (incr > 0 && !fkey.IsDeferred)
            {
                E.Parse_Toplevel(parse).MayAbort = true;
            }
            v.AddOp2(OP.FkCounter, fkey.IsDeferred, incr);
            if (whereInfo != null)
            {
                Where.End(whereInfo);
            }

            // Clean up the WHERE clause constructed above.
            Expr.Delete(ctx, ref where_);
            if (fkIfZero != 0)
            {
                v.JumpHere(fkIfZero);
            }
        }
Ejemplo n.º 10
0
        static void FKLookupParent(Parse parse, int db, Table table, Index index, FKey fkey, int[] cols, int regDataId, int incr, bool isIgnore)
        {
            Vdbe v     = parse.GetVdbe(); // Vdbe to add code to
            int  curId = parse.Tabs - 1;  // Cursor number to use
            int  okId  = v.MakeLabel();   // jump here if parent key found

            // If nIncr is less than zero, then check at runtime if there are any outstanding constraints to resolve. If there are not, there is no need
            // to check if deleting this row resolves any outstanding violations.
            //
            // Check if any of the key columns in the child table row are NULL. If any are, then the constraint is considered satisfied. No need to
            // search for a matching row in the parent table.
            int i;

            if (incr < 0)
            {
                v.AddOp2(OP.FkIfZero, fkey.IsDeferred, okId);
            }
            for (i = 0; i < fkey.Cols.length; i++)
            {
                int regId = cols[i] + regDataId + 1;
                v.AddOp2(OP.IsNull, regId, okId);
            }

            if (!isIgnore)
            {
                if (index == null)
                {
                    // If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY column of the parent table (table pTab).
                    int mustBeIntId; // Address of MustBeInt instruction
                    int regTempId = parse.GetTempReg();

                    // Invoke MustBeInt to coerce the child key value to an integer (i.e. apply the affinity of the parent key). If this fails, then there
                    // is no matching parent key. Before using MustBeInt, make a copy of the value. Otherwise, the value inserted into the child key column
                    // will have INTEGER affinity applied to it, which may not be correct.
                    v.AddOp2(OP.SCopy, cols[0] + 1 + regDataId, regTempId);
                    mustBeIntId = v.AddOp2(OP.MustBeInt, regTempId, 0);

                    // If the parent table is the same as the child table, and we are about to increment the constraint-counter (i.e. this is an INSERT operation),
                    // then check if the row being inserted matches itself. If so, do not increment the constraint-counter.
                    if (table == fkey.From && incr == 1)
                    {
                        v.AddOp3(OP.Eq, regDataId, okId, regTempId);
                    }

                    parse.OpenTable(parse, curId, db, table, OP.OpenRead);
                    v.AddOp3(OP.NotExists, curId, 0, regTempId);
                    v.AddOp2(OP.Goto, 0, okId);
                    v.JumpHere(v.CurrentAddr() - 2);
                    v.JumpHere(mustBeIntId);
                    parse.ReleaseTempReg(regTempId);
                }
                else
                {
                    int     colsLength = fkey.Cols.length;
                    int     regTempId  = parse.GetTempRange(colsLength);
                    int     regRecId   = parse.GetTempReg();
                    KeyInfo key        = IndexKeyinfo(parse, index);

                    v.AddOp3(OP.OpenRead, curId, index.Id, db);
                    v.ChangeP4(v, -1, key, Vdbe.P4T.KEYINFO_HANDOFF);
                    for (i = 0; i < colsLength; i++)
                    {
                        v.AddOp2(OP.Copy, cols[i] + 1 + regDataId, regTempId + i);
                    }

                    // If the parent table is the same as the child table, and we are about to increment the constraint-counter (i.e. this is an INSERT operation),
                    // then check if the row being inserted matches itself. If so, do not increment the constraint-counter.

                    // If any of the parent-key values are NULL, then the row cannot match itself. So set JUMPIFNULL to make sure we do the OP_Found if any
                    // of the parent-key values are NULL (at this point it is known that none of the child key values are).
                    if (table == fkey.From && incr == 1)
                    {
                        int jumpId = v.CurrentAddr() + colsLength + 1;
                        for (i = 0; i < colsLength; i++)
                        {
                            int childId  = cols[i] + 1 + regDataId;
                            int parentId = index.Columns[i] + 1 + regDataId;
                            Debug.Assert(cols[i] != table.PKey);
                            if (index.Columns[i] == table.PKey)
                            {
                                parentId = regDataId;  // The parent key is a composite key that includes the IPK column
                            }
                            v.AddOp3(OP.Ne, childId, jumpId, parentId);
                            v.ChangeP5(SQLITE_JUMPIFNULL);
                        }
                        v.AddOp2(OP.Goto, 0, okId);
                    }

                    v.AddOp3(OP.MakeRecord, regTempId, colsLength, regRecId);
                    v.ChangeP4(-1, IndexAffinityStr(v, index), Vdbe.P4T.TRANSIENT);
                    v.AddOp4Int(OP.Found, curId, okId, regRecId, 0);

                    parse.ReleaseTempReg(regRecId);
                    parse.ReleaseTempRange(regTempId, colsLength);
                }
            }

            if (!fkey.IsDeferred && parse.Toplevel == null && parse.IsMultiWrite == 0)
            {
                // Special case: If this is an INSERT statement that will insert exactly one row into the table, raise a constraint immediately instead of
                // incrementing a counter. This is necessary as the VM code is being generated for will not open a statement transaction.
                Debug.Assert(incr == 1);
                HaltConstraint(parse, OE.Abort, "foreign key constraint failed", Vdbe.P4T.STATIC);
            }
            else
            {
                if (incr > 0 && !fkey.IsDeferred)
                {
                    E.Parse_Toplevel(parse).MayAbort = true;
                }
                v.AddOp2(OP.FkCounter, fkey.IsDeferred, incr);
            }

            v.ResolveLabel(v, okId);
            v.AddOp1(OP.Close, curId);
        }
Ejemplo n.º 11
0
        public static void FinishParse(Parse parse, Token end)
        {
            Table   table = parse.NewTable; // The table being constructed
            Context ctx   = parse.Ctx;      // The database connection

            if (table == null)
            {
                return;
            }
            AddArgumentToVtab(parse);
            parse.Arg.data = null;
            if (table.ModuleArgs.length < 1)
            {
                return;
            }

            // If the CREATE VIRTUAL TABLE statement is being entered for the first time (in other words if the virtual table is actually being
            // created now instead of just being read out of sqlite_master) then do additional initialization work and store the statement text
            // in the sqlite_master table.
            if (!ctx.Init.Busy)
            {
                // Compute the complete text of the CREATE VIRTUAL TABLE statement
                if (end != null)
                {
                    parse.NameToken.length = (uint)parse.NameToken.data.Length;               //: (int)(end->data - parse->NameToken) + end->length;
                }
                string stmt = C._mtagprintf(ctx, "CREATE VIRTUAL TABLE %T", parse.NameToken); //.Z.Substring(0, parse.NameToken.length));

                // A slot for the record has already been allocated in the SQLITE_MASTER table.  We just need to update that slot with all
                // the information we've collected.
                //
                // The VM register number pParse->regRowid holds the rowid of an entry in the sqlite_master table tht was created for this vtab
                // by sqlite3StartTable().
                int db = Prepare.SchemaToIndex(ctx, table.Schema);
                parse.NestedParse("UPDATE %Q.%s SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q WHERE rowid=#%d",
                                  ctx.DBs[db].Name, E.SCHEMA_TABLE(db),
                                  table.Name, table.Name,
                                  stmt,
                                  parse.RegRowid
                                  );
                C._tagfree(ctx, ref stmt);
                Vdbe v = parse.GetVdbe();
                parse.ChangeCookie(db);

                v.AddOp2(OP.Expire, 0, 0);
                string where_ = C._mtagprintf(ctx, "name='%q' AND type='table'", table.Name);
                v.AddParseSchemaOp(db, where_);
                v.AddOp4(OP.VCreate, db, 0, 0, table.Name, (Vdbe.P4T)table.Name.Length + 1);
            }

            // If we are rereading the sqlite_master table create the in-memory record of the table. The xConnect() method is not called until
            // the first time the virtual table is used in an SQL statement. This allows a schema that contains virtual tables to be loaded before
            // the required virtual table implementations are registered.
            else
            {
                Schema schema     = table.Schema;
                string name       = table.Name;
                int    nameLength = name.Length;
                Debug.Assert(Btree.SchemaMutexHeld(ctx, 0, schema));
                Table oldTable = schema.TableHash.Insert(name, nameLength, table);
                if (oldTable != null)
                {
                    ctx.MallocFailed = true;
                    Debug.Assert(table == oldTable); // Malloc must have failed inside HashInsert()
                    return;
                }
                parse.NewTable = null;
            }
        }
Ejemplo n.º 12
0
        static void FKScanChildren(Parse parse, SrcList src, Table table, Index index, FKey fkey, int[] cols, int regDataId, int incr)
        {
            Context ctx = parse.Ctx; // Database handle
            Vdbe v = parse.GetVdbe();
            Expr where_ = null; // WHERE clause to scan with

            Debug.Assert(index == null || index.Table == table);
            int fkIfZero = 0; // Address of OP_FkIfZero
            if (incr < 0)
                fkIfZero = v.AddOp2(OP.FkIfZero, fkey.IsDeferred, 0);

            // Create an Expr object representing an SQL expression like:
            //
            //   <parent-key1> = <child-key1> AND <parent-key2> = <child-key2> ...
            //
            // The collation sequence used for the comparison should be that of the parent key columns. The affinity of the parent key column should
            // be applied to each child key value before the comparison takes place.
            for (int i = 0; i < fkey.Cols.length; i++)
            {
                int col; // Index of column in child table
                Expr left = Expr.Expr(ctx, TK.REGISTER, null); // Value from parent table row
                if (left != null)
                {
                    // Set the collation sequence and affinity of the LHS of each TK_EQ expression to the parent key column defaults.
                    if (index != null)
                    {
                        col = index.Columns[i];
                        Column colObj = table.Cols[col];
                        if (table.PKey == col) col = -1;
                        left.TableId = regDataId + col + 1;
                        left.Aff = colObj.Affinity;
                        string collName = colObj.Coll;
                        if (collName == null) collName = ctx.DefaultColl.Name;
                        left = Expr.AddCollateString(parse, left, collName);
                    }
                    else
                    {
                        left.TableId = regDataId;
                        left.Aff = AFF.INTEGER;
                    }
                }
                col = (cols != null ? cols[i] : fkey.Cols[0].From);
                Debug.Assert(col >= 0);
                string colName = fkey.From.Cols[col].Name; // Name of column in child table
                Expr right = Expr.Expr(ctx, TK.ID, colName); // Column ref to child table
                Expr eq = Expr.PExpr(parse, TK.EQ, left, right, 0); // Expression (pLeft = pRight)
                where_ = Expr.And(ctx, where_, eq);
            }

            // If the child table is the same as the parent table, and this scan is taking place as part of a DELETE operation (operation D.2), omit the
            // row being deleted from the scan by adding ($rowid != rowid) to the WHERE clause, where $rowid is the rowid of the row being deleted.
            if (table == fkey.From && incr > 0)
            {
                Expr left = Expr.Expr(ctx, TK.REGISTER, null); // Value from parent table row
                Expr right = Expr.Expr(ctx, TK.COLUMN, null); // Column ref to child table
                if (left != null && right != null)
                {
                    left.TableId = regDataId;
                    left.Aff = AFF.INTEGER;
                    right.TableId = src.Ids[0].Cursor;
                    right.ColumnIdx = -1;
                }
                Expr eq = Expr.PExpr(parse, TK.NE, left, right, 0); // Expression (pLeft = pRight)
                where_ = Expr.And(ctx, where_, eq);
            }

            // Resolve the references in the WHERE clause.
            NameContext nameContext; // Context used to resolve WHERE clause
            nameContext = new NameContext(); // memset( &sNameContext, 0, sizeof( NameContext ) );
            nameContext.SrcList = src;
            nameContext.Parse = parse;
            ResolveExprNames(nameContext, ref where_);

            // Create VDBE to loop through the entries in src that match the WHERE clause. If the constraint is not deferred, throw an exception for
            // each row found. Otherwise, for deferred constraints, increment the deferred constraint counter by incr for each row selected.
            ExprList dummy = null;
            WhereInfo whereInfo = Where.Begin(parse, src, where_, ref dummy, 0); // Context used by sqlite3WhereXXX()
            if (incr > 0 && !fkey.IsDeferred)
                E.Parse_Toplevel(parse).MayAbort = true;
            v.AddOp2(OP.FkCounter, fkey.IsDeferred, incr);
            if (whereInfo != null)
                Where.End(whereInfo);

            // Clean up the WHERE clause constructed above.
            Expr.Delete(ctx, ref where_);
            if (fkIfZero != 0)
                v.JumpHere(fkIfZero);
        }
Ejemplo n.º 13
0
        static void FKLookupParent(Parse parse, int db, Table table, Index index, FKey fkey, int[] cols, int regDataId, int incr, bool isIgnore)
        {
            Vdbe v = parse.GetVdbe(); // Vdbe to add code to
            int curId = parse.Tabs - 1; // Cursor number to use
            int okId = v.MakeLabel(); // jump here if parent key found

            // If nIncr is less than zero, then check at runtime if there are any outstanding constraints to resolve. If there are not, there is no need
            // to check if deleting this row resolves any outstanding violations.
            //
            // Check if any of the key columns in the child table row are NULL. If any are, then the constraint is considered satisfied. No need to 
            // search for a matching row in the parent table.
            int i;
            if (incr < 0)
                v.AddOp2(OP.FkIfZero, fkey.IsDeferred, okId);
            for (i = 0; i < fkey.Cols.length; i++)
            {
                int regId = cols[i] + regDataId + 1;
                v.AddOp2(OP.IsNull, regId, okId);
            }

            if (!isIgnore)
            {
                if (index == null)
                {
                    // If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY column of the parent table (table pTab).
                    int mustBeIntId; // Address of MustBeInt instruction
                    int regTempId = parse.GetTempReg();

                    // Invoke MustBeInt to coerce the child key value to an integer (i.e. apply the affinity of the parent key). If this fails, then there
                    // is no matching parent key. Before using MustBeInt, make a copy of the value. Otherwise, the value inserted into the child key column
                    // will have INTEGER affinity applied to it, which may not be correct.
                    v.AddOp2(OP.SCopy, cols[0] + 1 + regDataId, regTempId);
                    mustBeIntId = v.AddOp2(OP.MustBeInt, regTempId, 0);

                    // If the parent table is the same as the child table, and we are about to increment the constraint-counter (i.e. this is an INSERT operation),
                    // then check if the row being inserted matches itself. If so, do not increment the constraint-counter.
                    if (table == fkey.From && incr == 1)
                        v.AddOp3(OP.Eq, regDataId, okId, regTempId);

                    parse.OpenTable(parse, curId, db, table, OP.OpenRead);
                    v.AddOp3(OP.NotExists, curId, 0, regTempId);
                    v.AddOp2(OP.Goto, 0, okId);
                    v.JumpHere(v.CurrentAddr() - 2);
                    v.JumpHere(mustBeIntId);
                    parse.ReleaseTempReg(regTempId);
                }
                else
                {
                    int colsLength = fkey.Cols.length;
                    int regTempId = parse.GetTempRange(colsLength);
                    int regRecId = parse.GetTempReg();
                    KeyInfo key = IndexKeyinfo(parse, index);

                    v.AddOp3(OP.OpenRead, curId, index.Id, db);
                    v.ChangeP4(v, -1, key, Vdbe.P4T.KEYINFO_HANDOFF);
                    for (i = 0; i < colsLength; i++)
                        v.AddOp2(OP.Copy, cols[i] + 1 + regDataId, regTempId + i);

                    // If the parent table is the same as the child table, and we are about to increment the constraint-counter (i.e. this is an INSERT operation),
                    // then check if the row being inserted matches itself. If so, do not increment the constraint-counter. 

                    // If any of the parent-key values are NULL, then the row cannot match itself. So set JUMPIFNULL to make sure we do the OP_Found if any
                    // of the parent-key values are NULL (at this point it is known that none of the child key values are).
                    if (table == fkey.From && incr == 1)
                    {
                        int jumpId = v.CurrentAddr() + colsLength + 1;
                        for (i = 0; i < colsLength; i++)
                        {
                            int childId = cols[i] + 1 + regDataId;
                            int parentId = index.Columns[i] + 1 + regDataId;
                            Debug.Assert(cols[i] != table.PKey);
                            if (index.Columns[i] == table.PKey)
                                parentId = regDataId;  // The parent key is a composite key that includes the IPK column
                            v.AddOp3(OP.Ne, childId, jumpId, parentId);
                            v.ChangeP5(SQLITE_JUMPIFNULL);
                        }
                        v.AddOp2(OP.Goto, 0, okId);
                    }

                    v.AddOp3(OP.MakeRecord, regTempId, colsLength, regRecId);
                    v.ChangeP4(-1, IndexAffinityStr(v, index), Vdbe.P4T.TRANSIENT);
                    v.AddOp4Int(OP.Found, curId, okId, regRecId, 0);

                    parse.ReleaseTempReg(regRecId);
                    parse.ReleaseTempRange(regTempId, colsLength);
                }
            }

            if (!fkey.IsDeferred && parse.Toplevel == null && parse.IsMultiWrite == 0)
            {
                // Special case: If this is an INSERT statement that will insert exactly one row into the table, raise a constraint immediately instead of
                // incrementing a counter. This is necessary as the VM code is being generated for will not open a statement transaction.
                Debug.Assert(incr == 1);
                HaltConstraint(parse, OE.Abort, "foreign key constraint failed", Vdbe.P4T.STATIC);
            }
            else
            {
                if (incr > 0 && !fkey.IsDeferred)
                    E.Parse_Toplevel(parse).MayAbort = true;
                v.AddOp2(OP.FkCounter, fkey.IsDeferred, incr);
            }

            v.ResolveLabel(v, okId);
            v.AddOp1(OP.Close, curId);
        }