Пример #1
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);
        }
Пример #2
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);
        }