Beispiel #1
0
        /// <summary>
        /// Gets the current vector value of the axis with the specified key.
        /// </summary>
        /// <param name="axisKey">The key of the axis.</param>
        /// <returns>Axis value.</returns>
        public FVector GetVectorAxisValue(FKey axisKey)
        {
            FVector result;

            Native_UInputComponent.GetVectorAxisValue(Address, ref axisKey, out result);
            return(result);
        }
Beispiel #2
0
        static string WhereForeignKeys(Parse parse, Table table)
        {
            string where_ = string.Empty;

            for (FKey p = Parse.FKReferences(table); p != null; p = p.NextTo)
            {
                where_ = WhereOrName(parse.Ctx, where_, p.From.Name);
            }
            return(where_);
        }
Beispiel #3
0
        public static void GenerateRowDelete(Parse parse, Table table, int curId, int rowid, int count, Trigger trigger, OE onconf)
        {
            // Vdbe is guaranteed to have been allocated by this stage.
            Vdbe v = parse.V;
            Debug.Assert(v != null);

            // Seek cursor iCur to the row to delete. If this row no longer exists (this can happen if a trigger program has already deleted it), do
            // not attempt to delete it or fire any DELETE triggers.
            int label = v.MakeLabel(); // Label resolved to end of generated code
            v.AddOp3(OP.NotExists, curId, label, rowid);

            // If there are any triggers to fire, allocate a range of registers to use for the old.* references in the triggers.
            if (FKey.FkRequired(parse, table, null, 0) != 0 || trigger != null)
            {
                // TODO: Could use temporary registers here. Also could attempt to avoid copying the contents of the rowid register.
                uint mask = sqlite3TriggerColmask(parse, trigger, null, 0, TRIGGER.BEFORE | TRIGGER.AFTER, table, onconf); // Mask of OLD.* columns in use
                mask |= sqlite3FkOldmask(parse, table);
                int oldId = parse.Mems + 1; // First register in OLD.* array
                parse.Mems += (1 + table.Cols.length);

                // Populate the OLD.* pseudo-table register array. These values will be used by any BEFORE and AFTER triggers that exist.
                v.AddOp2(OP.Copy, rowid, oldId);
                for (int col = 0; col < table.Cols.length; col++) // Iterator used while populating OLD.*
                    if (mask == 0xffffffff || (mask & (1 << col)) != 0)
                        Expr.CodeGetColumnOfTable(v, table, curId, col, oldId + col + 1);

                // Invoke BEFORE DELETE trigger programs.
                sqlite3CodeRowTrigger(parse, trigger, TK.DELETE, null, TRIGGER.BEFORE, table, oldId, onconf, label);

                // Seek the cursor to the row to be deleted again. It may be that the BEFORE triggers coded above have already removed the row
                // being deleted. Do not attempt to delete the row a second time, and do not fire AFTER triggers.
                v.AddOp3(OP.NotExists, curId, label, rowid);

                // Do FK processing. This call checks that any FK constraints that refer to this table (i.e. constraints attached to other tables) are not violated by deleting this row.
                FKey.FkCheck(parse, table, oldId, 0);
            }

            // Delete the index and table entries. Skip this step if table is really a view (in which case the only effect of the DELETE statement is to fire the INSTEAD OF triggers).
            if (table.Select == null)
            {
                GenerateRowIndexDelete(parse, table, curId, null);
                v.AddOp2(OP.Delete, curId, (count != 0 ? (int)OPFLAG.NCHANGE : 0));
                if (count != 0)
                    v.ChangeP4(-1, table.Name, Vdbe.P4T.TRANSIENT);
            }

            // Do any ON CASCADE, SET NULL or SET DEFAULT operations required to handle rows (possibly in other tables) that refer via a foreign key to the row just deleted.
            FKey.FkActions(parse, table, null, oldId);

            // Invoke AFTER DELETE trigger programs.
            sqlite3CodeRowTrigger(parse, trigger, TK.DELETE, null, TRIGGER.AFTER, table, oldId, onconf, label);

            // Jump here if the row had already been deleted before any BEFORE trigger programs were invoked. Or if a trigger program throws a RAISE(IGNORE) exception.
            v.ResolveLabel(label);
        }
Beispiel #4
0
        /// <summary>
        /// Binds a gesture event to a delegate function.
        /// Returned reference is only guaranteed to be valid until another gesture event is bound.
        /// </summary>
        public FInputGestureBindingHandle BindGesture(FKey gestureKey, FInputGestureHandler handler)
        {
            IntPtr  functionAddress;
            UObject obj;

            if (NativeReflection.LookupTable.GetFunctionAddress(handler, out functionAddress, out obj))
            {
                return((FInputGestureBindingHandle)Native_UInputComponent.BindGesture(
                           Address, ref gestureKey, obj.Address, functionAddress));
            }
            return(default(FInputGestureBindingHandle));
        }
Beispiel #5
0
        /// <summary>
        /// Binds a key event to a delegate function.
        /// Returned reference is only guaranteed to be valid until another input key is bound.
        /// </summary>
        public FInputKeyBindingHandle BindKey(FKey key, EInputEventType keyEvent, FInputActionHandler handler)
        {
            IntPtr  functionAddress;
            UObject obj;

            if (NativeReflection.LookupTable.GetFunctionAddress(handler, out functionAddress, out obj))
            {
                return((FInputKeyBindingHandle)Native_UInputComponent.BindKey(
                           Address, ref key, (byte)keyEvent, obj.Address, functionAddress));
            }
            return(default(FInputKeyBindingHandle));
        }
Beispiel #6
0
        /// <summary>
        /// Binds a delegate function to a vector axis key (e.g. Tilt)
        /// Returned reference is only guaranteed to be valid until another vector axis key is bound.
        /// </summary>
        public FInputVectorAxisBindingHandle BindVectorAxis(FKey axisKey, FInputVectorAxisHandler handler)
        {
            IntPtr  functionAddress;
            UObject obj;

            if (NativeReflection.LookupTable.GetFunctionAddress(handler, out functionAddress, out obj))
            {
                return((FInputVectorAxisBindingHandle)Native_UInputComponent.BindVectorAxis(
                           Address, ref axisKey, obj.Address, functionAddress));
            }
            return(default(FInputVectorAxisBindingHandle));
        }
Beispiel #7
0
        public Font GetSystemFont(string name, FontStyles style = FontStyles.Regular)
        {
            var k = new FKey()
            {
                Name = name, Style = style
            };
            Font fnt;

            if (!systemFonts.TryGetValue(k, out fnt))
            {
                fnt = Font.FromSystemFont(game.Renderer2D, name, style);
                systemFonts.Add(k, fnt);
            }
            return(fnt);
        }
Beispiel #8
0
 public void FKActions(Table table, ExprList changes, int regOld)
 {
     // If foreign-key support is enabled, iterate through all FKs that refer to table table. If there is an action associated with the FK
     // for this operation (either update or delete), invoke the associated trigger sub-program.
     if ((Ctx.Flags & Context.FLAG.ForeignKeys) != 0)
     {
         for (FKey fkey = FKReferences(table); fkey != null; fkey = fkey.NextTo)
         {
             Trigger action = fkActionTrigger(table, fkey, changes);
             if (action != null)
             {
                 CodeRowTriggerDirect(this, action, table, regOld, OE.Abort, 0);
             }
         }
     }
 }
Beispiel #9
0
        public Font GetSystemFont(string name, FontStyles style = FontStyles.Regular)
        {
            if (ren2d == null)
            {
                ren2d = game.GetService <Renderer2D>();
            }
            var k = new FKey()
            {
                Name = name, Style = style
            };
            Font fnt;

            if (!systemFonts.TryGetValue(k, out fnt))
            {
                fnt = Font.FromSystemFont(ren2d, name, style);
                systemFonts.Add(k, fnt);
            }
            return(fnt);
        }
Beispiel #10
0
        void Tick(float deltaTime)
        {
            FRotator rot     = pc.GetControlRotation();
            FVector  forward = UKismetMathLibrary.GetForwardVector(new FRotator(0, rot.Yaw, 0));
            FVector  right   = UKismetMathLibrary.GetRightVector(new FRotator(0, rot.Yaw, 0));
            FKey     key     = new FKey();

            key.KeyName = FName.FromString("W");
            if (pc.IsInputKeyDown(key))
            {
                pc.K2_GetPawn().AddMovementInput(forward, 1.0f, false);
            }
            key.KeyName = FName.FromString("S");
            if (pc.IsInputKeyDown(key))
            {
                pc.K2_GetPawn().AddMovementInput(-forward, 1.0f, false);
            }
            key.KeyName = FName.FromString("A");
            if (pc.IsInputKeyDown(key))
            {
                pc.K2_GetPawn().AddMovementInput(-right, 1.0f, false);
            }
            key.KeyName = FName.FromString("D");
            if (pc.IsInputKeyDown(key))
            {
                pc.K2_GetPawn().AddMovementInput(right, 1.0f, false);
            }

            key.KeyName = FName.FromString("J");
            if (pc.IsInputKeyDown(key))
            {
                USkeletalMeshComponent mesh = Cast <USkeletalMeshComponent>(pc.K2_GetPawn().GetComponentByClass(typeof(USkeletalMeshComponent)));
                mesh.GetAnimInstance().Montage_Play(Montage, 1);
            }

            float movex, movey;

            pc.GetInputMouseDelta(out movex, out movey);
            pc.AddYawInput(movex);
            pc.AddPitchInput(movey);
        }
Beispiel #11
0
        public static void FKDelete(Context ctx, Table table)
        {
            Debug.Assert(ctx == null || Btree.SchemaMutexHeld(ctx, 0, table.Schema));
            FKey next; // Copy of pFKey.pNextFrom

            for (FKey fkey = table.FKeys; fkey != null; fkey = next)
            {
                // Remove the FK from the fkeyHash hash table.
                //: if (!ctx || ctx->BytesFreed == 0)
                {
                    if (fkey.PrevTo != null)
                    {
                        fkey.PrevTo.NextTo = fkey.NextTo;
                    }
                    else
                    {
                        FKey   p = fkey.NextTo;
                        string z = (p != null ? fkey.NextTo.To : fkey.To);
                        table.Schema.FKeyHash.Insert(z, z.Length, p);
                    }
                    if (fkey.NextTo != null)
                    {
                        fkey.NextTo.PrevTo = fkey.PrevTo;
                    }
                }

                // EV: R-30323-21917 Each foreign key constraint in SQLite is classified as either immediate or deferred.
                Debug.Assert(fkey.IsDeferred == false || fkey.IsDeferred == true);

                // Delete any triggers created to implement actions for this FK.
#if !OMIT_TRIGGER
                FKTriggerDelete(ctx, fkey.Triggers[0]);
                FKTriggerDelete(ctx, fkey.Triggers[1]);
#endif
                next = fkey.NextFrom;
                C._tagfree(ctx, ref fkey);
            }
        }
Beispiel #12
0
        static void sqlite3CreateForeignKey(
            Parse pParse,      /* Parsing context */
            ExprList pFromCol, /* Columns in this table that point to other table */
            Token pTo,         /* Name of the other table */
            ExprList pToCol,   /* Columns in the other table */
            int flags          /* Conflict resolution algorithms. */
            )
        {
            sqlite3 db = pParse.db;

#if !SQLITE_OMIT_FOREIGN_KEY
            FKey  pFKey = null;
            FKey  pNextTo;
            Table p = pParse.pNewTable;
            int   nByte;
            int   i;
            int   nCol;
            //string z;

            Debug.Assert(pTo != null);
            if (p == null || IN_DECLARE_VTAB(pParse))
            {
                goto fk_end;
            }
            if (pFromCol == null)
            {
                int iCol = p.nCol - 1;
                if (NEVER(iCol < 0))
                {
                    goto fk_end;
                }
                if (pToCol != null && pToCol.nExpr != 1)
                {
                    sqlite3ErrorMsg(pParse, "foreign key on %s" +
                                    " should reference only one column of table %T",
                                    p.aCol[iCol].zName, pTo);
                    goto fk_end;
                }
                nCol = 1;
            }
            else if (pToCol != null && pToCol.nExpr != pFromCol.nExpr)
            {
                sqlite3ErrorMsg(pParse,
                                "number of columns in foreign key does not match the number of " +
                                "columns in the referenced table");
                goto fk_end;
            }
            else
            {
                nCol = pFromCol.nExpr;
            }
            //nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey.aCol[0]) + pTo.n + 1;
            //if( pToCol ){
            //  for(i=0; i<pToCol.nExpr; i++){
            //    nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
            //  }
            //}
            pFKey = new FKey();//sqlite3DbMallocZero(db, nByte );
            if (pFKey == null)
            {
                goto fk_end;
            }
            pFKey.pFrom     = p;
            pFKey.pNextFrom = p.pFKey;
            //z = pFKey.aCol[nCol].zCol;
            pFKey.aCol    = new FKey.sColMap[nCol];    // z;
            pFKey.aCol[0] = new FKey.sColMap();
            pFKey.zTo     = pTo.z.Substring(0, pTo.n); //memcpy( z, pTo.z, pTo.n );
            //z[pTo.n] = 0;
            sqlite3Dequote(ref pFKey.zTo);
            //z += pTo.n + 1;
            pFKey.nCol = nCol;
            if (pFromCol == null)
            {
                pFKey.aCol[0].iFrom = p.nCol - 1;
            }
            else
            {
                for (i = 0; i < nCol; i++)
                {
                    if (pFKey.aCol[i] == null)
                    {
                        pFKey.aCol[i] = new FKey.sColMap();
                    }
                    int j;
                    for (j = 0; j < p.nCol; j++)
                    {
                        if (p.aCol[j].zName.Equals(pFromCol.a[i].zName, StringComparison.InvariantCultureIgnoreCase))
                        {
                            pFKey.aCol[i].iFrom = j;
                            break;
                        }
                    }
                    if (j >= p.nCol)
                    {
                        sqlite3ErrorMsg(pParse,
                                        "unknown column \"%s\" in foreign key definition",
                                        pFromCol.a[i].zName);
                        goto fk_end;
                    }
                }
            }
            if (pToCol != null)
            {
                for (i = 0; i < nCol; i++)
                {
                    int n = sqlite3Strlen30(pToCol.a[i].zName);
                    if (pFKey.aCol[i] == null)
                    {
                        pFKey.aCol[i] = new FKey.sColMap();
                    }
                    pFKey.aCol[i].zCol = pToCol.a[i].zName;
                    //memcpy( z, pToCol.a[i].zName, n );
                    //z[n] = 0;
                    //z += n + 1;
                }
            }
            pFKey.isDeferred = 0;
            pFKey.aAction[0] = (u8)(flags & 0xff);           /* ON DELETE action */
            pFKey.aAction[1] = (u8)((flags >> 8) & 0xff);    /* ON UPDATE action */

            Debug.Assert(sqlite3SchemaMutexHeld(db, 0, p.pSchema));
            pNextTo = sqlite3HashInsert(ref p.pSchema.fkeyHash,
                                        pFKey.zTo, sqlite3Strlen30(pFKey.zTo), pFKey
                                        );
            //if( pNextTo==pFKey ){
            //  db.mallocFailed = 1;
            //  goto fk_end;
            //}
            if (pNextTo != null)
            {
                Debug.Assert(pNextTo.pPrevTo == null);
                pFKey.pNextTo   = pNextTo;
                pNextTo.pPrevTo = pFKey;
            }

            /* Link the foreign key to the table as the last step.
             */
            p.pFKey = pFKey;
            pFKey   = null;

fk_end:
            sqlite3DbFree(db, ref pFKey);
#endif // * !SQLITE_OMIT_FOREIGN_KEY) */
            sqlite3ExprListDelete(db, ref pFromCol);
            sqlite3ExprListDelete(db, ref pToCol);
        }
Beispiel #13
0
        static void sqlite3CreateForeignKey(
            Parse pParse,       /* Parsing context */
            ExprList pFromCol,  /* Columns in this table that point to other table */
            Token pTo,          /* Name of the other table */
            ExprList pToCol,    /* Columns in the other table */
            int flags           /* Conflict resolution algorithms. */
            )
        {
            sqlite3 db = pParse.db;
            #if !SQLITE_OMIT_FOREIGN_KEY
            FKey pFKey = null;
            FKey pNextTo;
            Table p = pParse.pNewTable;
            int nByte;
            int i;
            int nCol;
            //string z;

            Debug.Assert(pTo != null);
            if (p == null || IN_DECLARE_VTAB(pParse))
                goto fk_end;
            if (pFromCol == null)
            {
                int iCol = p.nCol - 1;
                if (NEVER(iCol < 0))
                    goto fk_end;
                if (pToCol != null && pToCol.nExpr != 1)
                {
                    sqlite3ErrorMsg(pParse, "foreign key on %s" +
                    " should reference only one column of table %T",
                    p.aCol[iCol].zName, pTo);
                    goto fk_end;
                }
                nCol = 1;
            }
            else if (pToCol != null && pToCol.nExpr != pFromCol.nExpr)
            {
                sqlite3ErrorMsg(pParse,
                "number of columns in foreign key does not match the number of " +
                "columns in the referenced table");
                goto fk_end;
            }
            else
            {
                nCol = pFromCol.nExpr;
            }
            //nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey.aCol[0]) + pTo.n + 1;
            //if( pToCol ){
            //  for(i=0; i<pToCol.nExpr; i++){
            //    nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
            //  }
            //}
            pFKey = new FKey();//sqlite3DbMallocZero(db, nByte );
            if (pFKey == null)
            {
                goto fk_end;
            }
            pFKey.pFrom = p;
            pFKey.pNextFrom = p.pFKey;
            //z = pFKey.aCol[nCol].zCol;
            pFKey.aCol = new FKey.sColMap[nCol];// z;
            pFKey.aCol[0] = new FKey.sColMap();
            pFKey.zTo = pTo.z.Substring(0, pTo.n);      //memcpy( z, pTo.z, pTo.n );
            //z[pTo.n] = 0;
            sqlite3Dequote(ref pFKey.zTo);
            //z += pTo.n + 1;
            pFKey.nCol = nCol;
            if (pFromCol == null)
            {
                pFKey.aCol[0].iFrom = p.nCol - 1;
            }
            else
            {
                for (i = 0; i < nCol; i++)
                {
                    if (pFKey.aCol[i] == null)
                        pFKey.aCol[i] = new FKey.sColMap();
                    int j;
                    for (j = 0; j < p.nCol; j++)
                    {
                        if (p.aCol[j].zName.Equals(pFromCol.a[i].zName, StringComparison.InvariantCultureIgnoreCase))
                        {
                            pFKey.aCol[i].iFrom = j;
                            break;
                        }
                    }
                    if (j >= p.nCol)
                    {
                        sqlite3ErrorMsg(pParse,
                        "unknown column \"%s\" in foreign key definition",
                        pFromCol.a[i].zName);
                        goto fk_end;
                    }
                }
            }
            if (pToCol != null)
            {
                for (i = 0; i < nCol; i++)
                {
                    int n = sqlite3Strlen30(pToCol.a[i].zName);
                    if (pFKey.aCol[i] == null)
                        pFKey.aCol[i] = new FKey.sColMap();
                    pFKey.aCol[i].zCol = pToCol.a[i].zName;
                    //memcpy( z, pToCol.a[i].zName, n );
                    //z[n] = 0;
                    //z += n + 1;
                }
            }
            pFKey.isDeferred = 0;
            pFKey.aAction[0] = (u8)(flags & 0xff);            /* ON DELETE action */
            pFKey.aAction[1] = (u8)((flags >> 8) & 0xff);    /* ON UPDATE action */

            Debug.Assert(sqlite3SchemaMutexHeld(db, 0, p.pSchema));
            pNextTo = sqlite3HashInsert(ref p.pSchema.fkeyHash,
                pFKey.zTo, sqlite3Strlen30(pFKey.zTo), pFKey
            );
            //if( pNextTo==pFKey ){
            //  db.mallocFailed = 1;
            //  goto fk_end;
            //}
            if (pNextTo != null)
            {
                Debug.Assert(pNextTo.pPrevTo == null);
                pFKey.pNextTo = pNextTo;
                pNextTo.pPrevTo = pFKey;
            }
            /* Link the foreign key to the table as the last step.
            */
            p.pFKey = pFKey;
            pFKey = null;

            fk_end:
            sqlite3DbFree(db, ref pFKey);
            #endif // * !SQLITE_OMIT_FOREIGN_KEY) */
            sqlite3ExprListDelete(db, ref pFromCol);
            sqlite3ExprListDelete(db, ref pToCol);
        }
Beispiel #14
0
 protected override void ReceiveActorOnClicked_Implementation(FKey ButtonPressed)
 {
     base.ReceiveActorOnClicked_Implementation(ButtonPressed);
     HandleControllerClick();
 }
Beispiel #15
0
 /// <summary>
 /// Indicates that the InputComponent is interested in knowing/consuming a vector axis key's
 /// value (via GetVectorAxisKeyValue) but does not want a delegate function called each frame.
 /// Returned reference is only guaranteed to be valid until another vector axis key is bound.
 /// </summary>
 public FInputVectorAxisBindingHandle BindVectorAxis(FKey axisKey)
 {
     return((FInputVectorAxisBindingHandle)Native_UInputComponent.BindVectorAxisKey(Address, ref axisKey));
 }
Beispiel #16
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);
        }
Beispiel #17
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);
        }
Beispiel #18
0
    /*
    ** This function is called when a row is inserted into or deleted from the 
    ** child table of foreign key constraint pFKey. If an SQL UPDATE is executed 
    ** on the child table of pFKey, this function is invoked twice for each row
    ** affected - once to "delete" the old row, and then again to "insert" the
    ** new row.
    **
    ** Each time it is called, this function generates VDBE code to locate the
    ** row in the parent table that corresponds to the row being inserted into 
    ** or deleted from the child table. If the parent row can be found, no 
    ** special action is taken. Otherwise, if the parent row can *not* be
    ** found in the parent table:
    **
    **   Operation | FK type   | Action taken
    **   --------------------------------------------------------------------------
    **   INSERT      immediate   Increment the "immediate constraint counter".
    **
    **   DELETE      immediate   Decrement the "immediate constraint counter".
    **
    **   INSERT      deferred    Increment the "deferred constraint counter".
    **
    **   DELETE      deferred    Decrement the "deferred constraint counter".
    **
    ** These operations are identified in the comment at the top of this file 
    ** (fkey.c) as "I.1" and "D.1".
    */
    static void fkLookupParent(
      Parse pParse,         /* Parse context */
      int iDb,              /* Index of database housing pTab */
      Table pTab,           /* Parent table of FK pFKey */
      Index pIdx,           /* Unique index on parent key columns in pTab */
      FKey pFKey,           /* Foreign key constraint */
      int[] aiCol,          /* Map from parent key columns to child table columns */
      int regData,          /* Address of array containing child table row */
      int nIncr,            /* Increment constraint counter by this */
      int isIgnore          /* If true, pretend pTab contains all NULL values */
    )
    {
      int i;                                    /* Iterator variable */
      Vdbe v = sqlite3GetVdbe( pParse );        /* Vdbe to add code to */
      int iCur = pParse.nTab - 1;               /* Cursor number to use */
      int iOk = sqlite3VdbeMakeLabel( v );      /* 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.  */
      if ( nIncr < 0 )
      {
        sqlite3VdbeAddOp2( v, OP_FkIfZero, pFKey.isDeferred, iOk );
      }
      for ( i = 0; i < pFKey.nCol; i++ )
      {
        int iReg = aiCol[i] + regData + 1;
        sqlite3VdbeAddOp2( v, OP_IsNull, iReg, iOk );
      }

      if ( isIgnore == 0 )
      {
        if ( pIdx == null )
        {
          /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY
          ** column of the parent table (table pTab).  */
          int iMustBeInt;               /* Address of MustBeInt instruction */
          int regTemp = sqlite3GetTempReg( pParse );

          /* 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.  */
          sqlite3VdbeAddOp2( v, OP_SCopy, aiCol[0] + 1 + regData, regTemp );
          iMustBeInt = sqlite3VdbeAddOp2( v, OP_MustBeInt, regTemp, 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 ( pTab == pFKey.pFrom && nIncr == 1 )
          {
            sqlite3VdbeAddOp3( v, OP_Eq, regData, iOk, regTemp );
          }

          sqlite3OpenTable( pParse, iCur, iDb, pTab, OP_OpenRead );
          sqlite3VdbeAddOp3( v, OP_NotExists, iCur, 0, regTemp );
          sqlite3VdbeAddOp2( v, OP_Goto, 0, iOk );
          sqlite3VdbeJumpHere( v, sqlite3VdbeCurrentAddr( v ) - 2 );
          sqlite3VdbeJumpHere( v, iMustBeInt );
          sqlite3ReleaseTempReg( pParse, regTemp );
        }
        else
        {
          int nCol = pFKey.nCol;
          int regTemp = sqlite3GetTempRange( pParse, nCol );
          int regRec = sqlite3GetTempReg( pParse );
          KeyInfo pKey = sqlite3IndexKeyinfo( pParse, pIdx );

          sqlite3VdbeAddOp3( v, OP_OpenRead, iCur, pIdx.tnum, iDb );
          sqlite3VdbeChangeP4( v, -1, pKey, P4_KEYINFO_HANDOFF );
          for ( i = 0; i < nCol; i++ )
          {
            sqlite3VdbeAddOp2( v, OP_Copy, aiCol[i] + 1 + regData, regTemp + 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 ( pTab == pFKey.pFrom && nIncr == 1 )
          {
            int iJump = sqlite3VdbeCurrentAddr( v ) + nCol + 1;
            for ( i = 0; i < nCol; i++ )
            {
              int iChild = aiCol[i] + 1 + regData;
              int iParent = pIdx.aiColumn[i] + 1 + regData;
              Debug.Assert( aiCol[i] != pTab.iPKey );
              if ( pIdx.aiColumn[i] == pTab.iPKey )
              {
                /* The parent key is a composite key that includes the IPK column */
                iParent = regData;
              }
              sqlite3VdbeAddOp3( v, OP_Ne, iChild, iJump, iParent );
              sqlite3VdbeChangeP5( v, SQLITE_JUMPIFNULL );
            }
            sqlite3VdbeAddOp2( v, OP_Goto, 0, iOk );
          }

          sqlite3VdbeAddOp3( v, OP_MakeRecord, regTemp, nCol, regRec );
          sqlite3VdbeChangeP4( v, -1, sqlite3IndexAffinityStr( v, pIdx ), P4_TRANSIENT );
          sqlite3VdbeAddOp4Int( v, OP_Found, iCur, iOk, regRec, 0 );

          sqlite3ReleaseTempReg( pParse, regRec );
          sqlite3ReleaseTempRange( pParse, regTemp, nCol );
        }
      }

      if ( 0 == pFKey.isDeferred && null == pParse.pToplevel && 0 == pParse.isMultiWrite )
      {
        /* 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( nIncr == 1 );
        sqlite3HaltConstraint(
            pParse, OE_Abort, "foreign key constraint failed", P4_STATIC
        );
      }
      else
      {
        if ( nIncr > 0 && pFKey.isDeferred == 0 )
        {
          sqlite3ParseToplevel( pParse ).mayAbort = 1;
        }
        sqlite3VdbeAddOp2( v, OP_FkCounter, pFKey.isDeferred, nIncr );
      }

      sqlite3VdbeResolveLabel( v, iOk );
      sqlite3VdbeAddOp1( v, OP_Close, iCur );
    }
Beispiel #19
0
        public static void DeleteFrom(Parse parse, SrcList tabList, Expr where_)
        {
            AuthContext sContext = new AuthContext(); // Authorization context
            Context ctx = parse.Ctx; // Main database structure
            if (parse.Errs != 0 || ctx.MallocFailed)
                goto delete_from_cleanup;
            Debug.Assert(tabList.Srcs == 1);

            // Locate the table which we want to delete.  This table has to be put in an SrcList structure because some of the subroutines we
            // will be calling are designed to work with multiple tables and expect an SrcList* parameter instead of just a Table* parameter.
            Table table = SrcList.Lookup(parse, tabList); // The table from which records will be deleted
            if (table == null) goto delete_from_cleanup;

            // Figure out if we have any triggers and if the table being deleted from is a view
#if !OMIT_TRIGGER
            int dummy;
            Trigger trigger = Triggers.Exist(parse, table, TK.DELETE, null, out dummy); // List of table triggers, if required
#if OMIT_VIEW
            const bool isView = false;
#else
            bool isView = (table.Select != null); // True if attempting to delete from a view
#endif
#else
            const Trigger trigger = null;
            bool isView = false;
#endif

            // If pTab is really a view, make sure it has been initialized.
            if (sqlite3ViewGetColumnNames(parse, table) != null || IsReadOnly(parse, table, (trigger != null)))
                goto delete_from_cleanup;
            int db = sqlite3SchemaToIndex(ctx, table.Schema); // Database number
            Debug.Assert(db < ctx.DBs.length);
            string dbName = ctx.DBs[db].Name; // Name of database holding pTab
            ARC rcauth = Auth.Check(parse, AUTH.DELETE, table.Name, 0, dbName); // Value returned by authorization callback
            Debug.Assert(rcauth == ARC.OK || rcauth == ARC.DENY || rcauth == ARC.IGNORE);
            if (rcauth == ARC.DENY)
                goto delete_from_cleanup;
            Debug.Assert(!isView || trigger != null);

            // Assign cursor number to the table and all its indices.
            Debug.Assert(tabList.Srcs == 1);
            int curId = tabList.Ids[0].Cursor = parse.Tabs++; // VDBE VdbeCursor number for pTab
            Index idx; // For looping over indices of the table
            for (idx = table.Index; idx != null; idx = idx.Next)
                parse.Tabs++;

            // Start the view context
            if (isView)
                Auth.ContextPush(parse, sContext, table.Name);

            // Begin generating code.
            Vdbe v = parse.GetVdbe(); // The virtual database engine 
            if (v == null)
                goto delete_from_cleanup;
            if (parse.Nested == 0) v.CountChanges();
            parse.BeginWriteOperation(1, db);

            // If we are trying to delete from a view, realize that view into a ephemeral table.
#if !OMIT_VIEW && !OMIT_TRIGGER
            if (isView)
                MaterializeView(parse, table, where_, curId);
#endif
            // Resolve the column names in the WHERE clause.
            NameContext sNC = new NameContext(); // Name context to resolve expressions in
            sNC.Parse = parse;
            sNC.SrcList = tabList;
            if (sqlite3ResolveExprNames(sNC, ref where_) != 0)
                goto delete_from_cleanup;

            // Initialize the counter of the number of rows deleted, if we are counting rows.
            int memCnt = -1; // Memory cell used for change counting
            if ((ctx.Flags & Context.FLAG.CountRows) != 0)
            {
                memCnt = ++parse.Mems;
                v.AddOp2(OP.Integer, 0, memCnt);
            }

#if !OMIT_TRUNCATE_OPTIMIZATION
            // Special case: A DELETE without a WHERE clause deletes everything. It is easier just to erase the whole table. Prior to version 3.6.5,
            // this optimization caused the row change count (the value returned by API function sqlite3_count_changes) to be set incorrectly.
            if (rcauth == ARC.OK && where_ == null && trigger == null && !IsVirtual(table) && !FKey.FkRequired(parse, table, null, 0))
            {
                Debug.Assert(!isView);
                v.AddOp4(OP.Clear, table.Id, db, memCnt, table.Name, Vdbe.P4T.STATIC);
                for (idx = table.Index; idx != null; idx = idx.Next)
                {
                    Debug.Assert(idx.Schema == table.Schema);
                    v.AddOp2(OP.Clear, idx.Id, db);
                }
            }
            else
#endif
            // The usual case: There is a WHERE clause so we have to scan through the table and pick which records to delete.
            {
                int rowSet = ++parse.Mems; // Register for rowset of rows to delete
                int rowid = ++parse.Mems; // Used for storing rowid values.

                // Collect rowids of every row to be deleted.
                v.AddOp2(OP.Null, 0, rowSet);
                ExprList dummy = null;
                WhereInfo winfo = Where.Begin(parse, tabList, where_, ref dummy, WHERE_DUPLICATES_OK, 0); // Information about the WHERE clause
                if (winfo == null) goto delete_from_cleanup;
                int regRowid = Expr.CodeGetColumn(parse, table, -1, curId, rowid); // Actual register containing rowids
                v.AddOp2(OP.RowSetAdd, rowSet, regRowid);
                if ((ctx.Flags & Context.FLAG.CountRows) != 0)
                    v.AddOp2(OP.AddImm, memCnt, 1);
                Where.End(winfo);

                // Delete every item whose key was written to the list during the database scan.  We have to delete items after the scan is complete
                // because deleting an item can change the scan order.
                int end = v.MakeLabel();

                // Unless this is a view, open cursors for the table we are deleting from and all its indices. If this is a view, then the
                // only effect this statement has is to fire the INSTEAD OF triggers.
                if (!isView)
                    sqlite3OpenTableAndIndices(parse, table, curId, OP.OpenWrite);
                int addr = v.AddOp3(OP.RowSetRead, rowSet, end, rowid);

                // Delete the row
#if !OMIT_VIRTUALTABLE
                if (IsVirtual(table))
                {
                    VTable vtable = VTable.GetVTable(ctx, table);
                    VTable.MakeWritable(parse, table);
                    v.AddOp4(OP.VUpdate, 0, 1, rowid, vtable, Vdbe.P4T.VTAB);
                    v.ChangeP5(OE.Abort);
                    sqlite3MayAbort(parse);
                }
                else
#endif
                {
                    int count = (parse.Nested == 0; // True to count changes
                    GenerateRowDelete(parse, table, curId, rowid, count, trigger, OE.Default);
                }

                // End of the delete loop
                v.AddOp2(OP.Goto, 0, addr);
                v.ResolveLabel(end);

                // Close the cursors open on the table and its indexes.
                if (!isView && !IsVirtual(table))
                {
                    for (int i = 1, idx = table.Index; idx != null; i++, idx = idx.Next)
                        v.AddOp2(OP.Close, curId + i, idx.Id);
                    v.AddOp1(OP.Close, curId);
                }
            }

            // Update the sqlite_sequence table by storing the content of the maximum rowid counter values recorded while inserting into
		    // autoincrement tables.
            if (parse.Nested == 0 && parse.TriggerTab == null)
                sqlite3AutoincrementEnd(parse);

            // Return the number of rows that were deleted. If this routine is generating code because of a call to sqlite3NestedParse(), do not
		    // invoke the callback function.
            if ((ctx.Flags & Context.FLAG.CountRows) != 0 && parse.Nested == 0 && parse.TriggerTab == null)
            {
                v.AddOp2(OP.ResultRow, memCnt, 1);
                v.SetNumCols(1);
                v.SetColName(0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
            }

        delete_from_cleanup:
            Auth.ContextPop(sContext);
            SrcList.Delete(ctx, ref tabList);
            Expr.Delete(ctx, ref where_);
            return;
        }
Beispiel #20
0
        int LocateFkeyIndex(Table parent, FKey fkey, out Index indexOut, out int[] colsOut)
        {
            indexOut = null; colsOut = null;

            int    colsLength = fkey.Cols.length; // Number of columns in parent key
            string key        = fkey.Cols[0].Col; // Name of left-most parent key column

            // The caller is responsible for zeroing output parameters.
            //: _assert(indexOut && *indexOut == nullptr);
            //: _assert(!colsOut || *colsOut == nullptr);

            // If this is a non-composite (single column) foreign key, check if it maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx
            // and *paiCol set to zero and return early.
            //
            // Otherwise, for a composite foreign key (more than one column), allocate space for the aiCol array (returned via output parameter *paiCol).
            // Non-composite foreign keys do not require the aiCol array.
            int[] cols = null; // Value to return via *paiCol
            if (colsLength == 1)
            {
                // The FK maps to the IPK if any of the following are true:
                //   1) There is an INTEGER PRIMARY KEY column and the FK is implicitly mapped to the primary key of table pParent, or
                //   2) The FK is explicitly mapped to a column declared as INTEGER PRIMARY KEY.
                if (parent.PKey >= 0)
                {
                    if (key == null || string.Equals(parent.Cols[parent.PKey].Name, key, StringComparison.InvariantCultureIgnoreCase))
                    {
                        return(0);
                    }
                }
            }
            else //: if (colsOut)
            {
                Debug.Assert(colsLength > 1);
                cols = new int[colsLength]; //: (int *)_tagalloc(Ctx, colsLength*sizeof(int));
                if (cols == null)
                {
                    return(1);
                }
                colsOut = cols;
            }

            Index index = null; // Value to return via *ppIdx

            for (index = parent.Index; index != null; index = index.Next)
            {
                if (index.Columns.length == colsLength && index.OnError != OE.None)
                {
                    // pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number of columns. If each indexed column corresponds to a foreign key
                    // column of pFKey, then this index is a winner.
                    if (key == null)
                    {
                        // If zKey is NULL, then this foreign key is implicitly mapped to the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
                        // identified by the test (Index.autoIndex==2).
                        if (index.AutoIndex == 2)
                        {
                            if (cols != null)
                            {
                                for (int i = 0; i < colsLength; i++)
                                {
                                    cols[i] = fkey.Cols[i].From;
                                }
                            }
                            break;
                        }
                    }
                    else
                    {
                        // If zKey is non-NULL, then this foreign key was declared to map to an explicit list of columns in table pParent. Check if this
                        // index matches those columns. Also, check that the index uses the default collation sequences for each column.
                        int i, j;
                        for (i = 0; i < colsLength; i++)
                        {
                            int col = index.Columns[i]; // Index of column in parent tbl
                            // If the index uses a collation sequence that is different from the default collation sequence for the column, this index is
                            // unusable. Bail out early in this case.
                            string dfltColl = parent.Cols[col].Coll; // Def. collation for column
                            if (string.IsNullOrEmpty(dfltColl))
                            {
                                dfltColl = "BINARY";
                            }
                            if (!string.Equals(index.CollNames[i], dfltColl, StringComparison.InvariantCultureIgnoreCase))
                            {
                                break;
                            }

                            string indexCol = parent.Cols[col].Name; // Name of indexed column
                            for (j = 0; j < colsLength; j++)
                            {
                                if (string.Equals(fkey.Cols[j].Col, indexCol, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    if (cols != null)
                                    {
                                        cols[i] = fkey.Cols[j].From;
                                    }
                                    break;
                                }
                            }
                            if (j == colsLength)
                            {
                                break;
                            }
                        }
                        if (i == colsLength)
                        {
                            break;                  // pIdx is usable
                        }
                    }
                }
            }

            if (index == null)
            {
                if (!DisableTriggers)
                {
                    ErrorMsg("foreign key mismatch - \"%w\" referencing \"%w\"", fkey.From.Name, fkey.To);
                }
                C._tagfree(Ctx, ref cols);
                return(1);
            }

            indexOut = index;
            return(0);
        }
Beispiel #21
0
        int LocateFkeyIndex(Table parent, FKey fkey, out Index indexOut, out int[] colsOut)
        {
            indexOut = null; colsOut = null;

            int colsLength = fkey.Cols.length; // Number of columns in parent key
            string key = fkey.Cols[0].Col; // Name of left-most parent key column

            // The caller is responsible for zeroing output parameters.
            //: _assert(indexOut && *indexOut == nullptr);
            //: _assert(!colsOut || *colsOut == nullptr);

            // If this is a non-composite (single column) foreign key, check if it maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx 
            // and *paiCol set to zero and return early. 
            //
            // Otherwise, for a composite foreign key (more than one column), allocate space for the aiCol array (returned via output parameter *paiCol).
            // Non-composite foreign keys do not require the aiCol array.
            int[] cols = null; // Value to return via *paiCol
            if (colsLength == 1)
            {
                // The FK maps to the IPK if any of the following are true:
                //   1) There is an INTEGER PRIMARY KEY column and the FK is implicitly mapped to the primary key of table pParent, or
                //   2) The FK is explicitly mapped to a column declared as INTEGER PRIMARY KEY.
                if (parent.PKey >= 0)
                    if (key == null || string.Equals(parent.Cols[parent.PKey].Name, key, StringComparison.InvariantCultureIgnoreCase)) return 0;
            }
            else //: if (colsOut)
            {
                Debug.Assert(colsLength > 1);
                cols = new int[colsLength]; //: (int *)_tagalloc(Ctx, colsLength*sizeof(int));
                if (cols == null) return 1;
                colsOut = cols;
            }

            Index index = null; // Value to return via *ppIdx
            for (index = parent.Index; index != null; index = index.Next)
            {
                if (index.Columns.length == colsLength && index.OnError != OE.None)
                {
                    // pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number of columns. If each indexed column corresponds to a foreign key
                    // column of pFKey, then this index is a winner.
                    if (key == null)
                    {
                        // If zKey is NULL, then this foreign key is implicitly mapped to the PRIMARY KEY of table pParent. The PRIMARY KEY index may be 
                        // identified by the test (Index.autoIndex==2).
                        if (index.AutoIndex == 2)
                        {
                            if (cols != null)
                                for (int i = 0; i < colsLength; i++) cols[i] = fkey.Cols[i].From;
                            break;
                        }
                    }
                    else
                    {
                        // If zKey is non-NULL, then this foreign key was declared to map to an explicit list of columns in table pParent. Check if this
                        // index matches those columns. Also, check that the index uses the default collation sequences for each column.
                        int i, j;
                        for (i = 0; i < colsLength; i++)
                        {
                            int col = index.Columns[i]; // Index of column in parent tbl
                            // If the index uses a collation sequence that is different from the default collation sequence for the column, this index is
                            // unusable. Bail out early in this case.
                            string dfltColl = parent.Cols[col].Coll; // Def. collation for column
                            if (string.IsNullOrEmpty(dfltColl))
                                dfltColl = "BINARY";
                            if (!string.Equals(index.CollNames[i], dfltColl, StringComparison.InvariantCultureIgnoreCase)) break;

                            string indexCol = parent.Cols[col].Name; // Name of indexed column
                            for (j = 0; j < colsLength; j++)
                            {
                                if (string.Equals(fkey.Cols[j].Col, indexCol, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    if (cols != null) cols[i] = fkey.Cols[j].From;
                                    break;
                                }
                            }
                            if (j == colsLength) break;
                        }
                        if (i == colsLength) break; // pIdx is usable
                    }
                }
            }

            if (index == null)
            {
                if (!DisableTriggers)
                    ErrorMsg("foreign key mismatch - \"%w\" referencing \"%w\"", fkey.From.Name, fkey.To);
                C._tagfree(Ctx, ref cols);
                return 1;
            }

            indexOut = index;
            return 0;
        }
Beispiel #22
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);
        }
Beispiel #23
0
 /// <summary>
 /// Gets the current value of the axis with the specified key.
 /// </summary>
 /// <param name="axisKey">The key of the axis.</param>
 /// <returns>Axis value.</returns>
 public float GetAxisKeyValue(FKey axisKey)
 {
     return(Native_UInputComponent.GetAxisKeyValue(Address, ref axisKey));
 }
Beispiel #24
0
    /*
    ** This function is called when an UPDATE or DELETE operation is being 
    ** compiled on table pTab, which is the parent table of foreign-key pFKey.
    ** If the current operation is an UPDATE, then the pChanges parameter is
    ** passed a pointer to the list of columns being modified. If it is a
    ** DELETE, pChanges is passed a NULL pointer.
    **
    ** It returns a pointer to a Trigger structure containing a trigger
    ** equivalent to the ON UPDATE or ON DELETE action specified by pFKey.
    ** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is
    ** returned (these actions require no special handling by the triggers
    ** sub-system, code for them is created by fkScanChildren()).
    **
    ** For example, if pFKey is the foreign key and pTab is table "p" in 
    ** the following schema:
    **
    **   CREATE TABLE p(pk PRIMARY KEY);
    **   CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE);
    **
    ** then the returned trigger structure is equivalent to:
    **
    **   CREATE TRIGGER ... DELETE ON p BEGIN
    **     DELETE FROM c WHERE ck = old.pk;
    **   END;
    **
    ** The returned pointer is cached as part of the foreign key object. It
    ** is eventually freed along with the rest of the foreign key object by 
    ** sqlite3FkDelete().
    */
    static Trigger fkActionTrigger(
      Parse pParse,                  /* Parse context */
      Table pTab,                    /* Table being updated or deleted from */
      FKey pFKey,                    /* Foreign key to get action for */
      ExprList pChanges              /* Change-list for UPDATE, NULL for DELETE */
    )
    {
      sqlite3 db = pParse.db;        /* Database handle */
      int action;                    /* One of OE_None, OE_Cascade etc. */
      Trigger pTrigger;              /* Trigger definition to return */
      int iAction = ( pChanges != null ) ? 1 : 0;   /* 1 for UPDATE, 0 for DELETE */

      action = pFKey.aAction[iAction];
      pTrigger = pFKey.apTrigger[iAction];

      if ( action != OE_None && null == pTrigger )
      {
        u8 enableLookaside;           /* Copy of db.lookaside.bEnabled */
        string zFrom;                 /* Name of child table */
        int nFrom;                    /* Length in bytes of zFrom */
        Index pIdx = null;            /* Parent key index for this FK */
        int[] aiCol = null;           /* child table cols . parent key cols */
        TriggerStep pStep = null;     /* First (only) step of trigger program */
        Expr pWhere = null;           /* WHERE clause of trigger step */
        ExprList pList = null;        /* Changes list if ON UPDATE CASCADE */
        Select pSelect = null;        /* If RESTRICT, "SELECT RAISE(...)" */
        int i;                        /* Iterator variable */
        Expr pWhen = null;            /* WHEN clause for the trigger */

        if ( locateFkeyIndex( pParse, pTab, pFKey, out pIdx, out aiCol ) != 0 )
          return null;
        Debug.Assert( aiCol != null || pFKey.nCol == 1 );

        for ( i = 0; i < pFKey.nCol; i++ )
        {
          Token tOld = new Token( "old", 3 );  /* Literal "old" token */
          Token tNew = new Token( "new", 3 );  /* Literal "new" token */
          Token tFromCol = new Token();        /* Name of column in child table */
          Token tToCol = new Token();          /* Name of column in parent table */
          int iFromCol;               /* Idx of column in child table */
          Expr pEq;                  /* tFromCol = OLD.tToCol */

          iFromCol = aiCol != null ? aiCol[i] : pFKey.aCol[0].iFrom;
          Debug.Assert( iFromCol >= 0 );
          tToCol.z = pIdx != null ? pTab.aCol[pIdx.aiColumn[i]].zName : "oid";
          tFromCol.z = pFKey.pFrom.aCol[iFromCol].zName;

          tToCol.n = sqlite3Strlen30( tToCol.z );
          tFromCol.n = sqlite3Strlen30( tFromCol.z );

          /* Create the expression "OLD.zToCol = zFromCol". It is important
          ** that the "OLD.zToCol" term is on the LHS of the = operator, so
          ** that the affinity and collation sequence associated with the
          ** parent table are used for the comparison. */
          pEq = sqlite3PExpr( pParse, TK_EQ,
              sqlite3PExpr( pParse, TK_DOT,
                sqlite3PExpr( pParse, TK_ID, null, null, tOld ),
                sqlite3PExpr( pParse, TK_ID, null, null, tToCol )
              , 0 ),
              sqlite3PExpr( pParse, TK_ID, null, null, tFromCol )
          , 0 );
          pWhere = sqlite3ExprAnd( db, pWhere, pEq );

          /* For ON UPDATE, construct the next term of the WHEN clause.
          ** The final WHEN clause will be like this:
          **
          **    WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN)
          */
          if ( pChanges != null )
          {
            pEq = sqlite3PExpr( pParse, TK_IS,
                sqlite3PExpr( pParse, TK_DOT,
                  sqlite3PExpr( pParse, TK_ID, null, null, tOld ),
                  sqlite3PExpr( pParse, TK_ID, null, null, tToCol ),
                  0 ),
                sqlite3PExpr( pParse, TK_DOT,
                  sqlite3PExpr( pParse, TK_ID, null, null, tNew ),
                  sqlite3PExpr( pParse, TK_ID, null, null, tToCol ),
                  0 ),
                0 );
            pWhen = sqlite3ExprAnd( db, pWhen, pEq );
          }

          if ( action != OE_Restrict && ( action != OE_Cascade || pChanges != null ) )
          {
            Expr pNew;
            if ( action == OE_Cascade )
            {
              pNew = sqlite3PExpr( pParse, TK_DOT,
                sqlite3PExpr( pParse, TK_ID, null, null, tNew ),
                sqlite3PExpr( pParse, TK_ID, null, null, tToCol )
              , 0 );
            }
            else if ( action == OE_SetDflt )
            {
              Expr pDflt = pFKey.pFrom.aCol[iFromCol].pDflt;
              if ( pDflt != null )
              {
                pNew = sqlite3ExprDup( db, pDflt, 0 );
              }
              else
              {
                pNew = sqlite3PExpr( pParse, TK_NULL, 0, 0, 0 );
              }
            }
            else
            {
              pNew = sqlite3PExpr( pParse, TK_NULL, 0, 0, 0 );
            }
            pList = sqlite3ExprListAppend( pParse, pList, pNew );
            sqlite3ExprListSetName( pParse, pList, tFromCol, 0 );
          }
        }
        sqlite3DbFree( db, ref aiCol );

        zFrom = pFKey.pFrom.zName;
        nFrom = sqlite3Strlen30( zFrom );

        if ( action == OE_Restrict )
        {
          Token tFrom = new Token();
          Expr pRaise;

          tFrom.z = zFrom;
          tFrom.n = nFrom;
          pRaise = sqlite3Expr( db, TK_RAISE, "foreign key constraint failed" );
          if ( pRaise != null )
          {
            pRaise.affinity = (char)OE_Abort;
          }
          pSelect = sqlite3SelectNew( pParse,
              sqlite3ExprListAppend( pParse, 0, pRaise ),
              sqlite3SrcListAppend( db, 0, tFrom, null ),
              pWhere,
              null, null, null, 0, null, null
          );
          pWhere = null;
        }

        /* Disable lookaside memory allocation */
        enableLookaside = db.lookaside.bEnabled;
        db.lookaside.bEnabled = 0;

        pTrigger = new Trigger();
        //(Trigger*)sqlite3DbMallocZero( db,
        //     sizeof( Trigger ) +         /* struct Trigger */
        //     sizeof( TriggerStep ) +     /* Single step in trigger program */
        //     nFrom + 1                 /* Space for pStep.target.z */
        // );
        //if ( pTrigger )
        {

          pStep = pTrigger.step_list = new TriggerStep();// = (TriggerStep)pTrigger[1];
          //pStep.target.z = pStep[1];
          pStep.target.n = nFrom;
          pStep.target.z = zFrom;// memcpy( (char*)pStep.target.z, zFrom, nFrom );

          pStep.pWhere = sqlite3ExprDup( db, pWhere, EXPRDUP_REDUCE );
          pStep.pExprList = sqlite3ExprListDup( db, pList, EXPRDUP_REDUCE );
          pStep.pSelect = sqlite3SelectDup( db, pSelect, EXPRDUP_REDUCE );
          if ( pWhen != null )
          {
            pWhen = sqlite3PExpr( pParse, TK_NOT, pWhen, 0, 0 );
            pTrigger.pWhen = sqlite3ExprDup( db, pWhen, EXPRDUP_REDUCE );
          }
        }

        /* Re-enable the lookaside buffer, if it was disabled earlier. */
        db.lookaside.bEnabled = enableLookaside;

        sqlite3ExprDelete( db, ref pWhere );
        sqlite3ExprDelete( db, ref pWhen );
        sqlite3ExprListDelete( db, ref pList );
        sqlite3SelectDelete( db, ref pSelect );
        //if ( db.mallocFailed == 1 )
        //{
        //  fkTriggerDelete( db, pTrigger );
        //  return 0;
        //}

        switch ( action )
        {
          case OE_Restrict:
            pStep.op = TK_SELECT;
            break;
          case OE_Cascade:
            if ( null == pChanges )
            {
              pStep.op = TK_DELETE;
              break;
            }
            goto default;
          default:
            pStep.op = TK_UPDATE;
            break;
        }
        pStep.pTrig = pTrigger;
        pTrigger.pSchema = pTab.pSchema;
        pTrigger.pTabSchema = pTab.pSchema;
        pFKey.apTrigger[iAction] = pTrigger;
        pTrigger.op = (byte)( pChanges != null ? TK_UPDATE : TK_DELETE );
      }

      return pTrigger;
    }
Beispiel #25
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);
            }
        }
Beispiel #26
0
    /*
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains code used by the compiler to add foreign key
    ** support to compiled SQL statements.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
    **
    *************************************************************************    */
    //#include "sqliteInt.h"

#if !SQLITE_OMIT_FOREIGN_KEY
#if !SQLITE_OMIT_TRIGGER

    /*
** Deferred and Immediate FKs
** --------------------------
**
** Foreign keys in SQLite come in two flavours: deferred and immediate.
** If an immediate foreign key constraint is violated, SQLITE_CONSTRAINT
** is returned and the current statement transaction rolled back. If a 
** deferred foreign key constraint is violated, no action is taken 
** immediately. However if the application attempts to commit the 
** transaction before fixing the constraint violation, the attempt fails.
**
** Deferred constraints are implemented using a simple counter associated
** with the database handle. The counter is set to zero each time a 
** database transaction is opened. Each time a statement is executed 
** that causes a foreign key violation, the counter is incremented. Each
** time a statement is executed that removes an existing violation from
** the database, the counter is decremented. When the transaction is
** committed, the commit fails if the current value of the counter is
** greater than zero. This scheme has two big drawbacks:
**
**   * When a commit fails due to a deferred foreign key constraint, 
**     there is no way to tell which foreign constraint is not satisfied,
**     or which row it is not satisfied for.
**
**   * If the database contains foreign key violations when the 
**     transaction is opened, this may cause the mechanism to malfunction.
**
** Despite these problems, this approach is adopted as it seems simpler
** than the alternatives.
**
** INSERT operations:
**
**   I.1) For each FK for which the table is the child table, search
**        the parent table for a match. If none is found increment the
**        constraint counter.
**
**   I.2) For each FK for which the table is the parent table, 
**        search the child table for rows that correspond to the new
**        row in the parent table. Decrement the counter for each row
**        found (as the constraint is now satisfied).
**
** DELETE operations:
**
**   D.1) For each FK for which the table is the child table, 
**        search the parent table for a row that corresponds to the 
**        deleted row in the child table. If such a row is not found, 
**        decrement the counter.
**
**   D.2) For each FK for which the table is the parent table, search 
**        the child table for rows that correspond to the deleted row 
**        in the parent table. For each found increment the counter.
**
** UPDATE operations:
**
**   An UPDATE command requires that all 4 steps above are taken, but only
**   for FK constraints for which the affected columns are actually 
**   modified (values must be compared at runtime).
**
** Note that I.1 and D.1 are very similar operations, as are I.2 and D.2.
** This simplifies the implementation a bit.
**
** For the purposes of immediate FK constraints, the OR REPLACE conflict
** resolution is considered to delete rows before the new row is inserted.
** If a delete caused by OR REPLACE violates an FK constraint, an exception
** is thrown, even if the FK constraint would be satisfied after the new 
** row is inserted.
**
** Immediate constraints are usually handled similarly. The only difference 
** is that the counter used is stored as part of each individual statement
** object (struct Vdbe). If, after the statement has run, its immediate
** constraint counter is greater than zero, it returns SQLITE_CONSTRAINT
** and the statement transaction is rolled back. An exception is an INSERT
** statement that inserts a single row only (no triggers). In this case,
** instead of using a counter, an exception is thrown immediately if the
** INSERT violates a foreign key constraint. This is necessary as such
** an INSERT does not open a statement transaction.
**
** TODO: How should dropping a table be handled? How should renaming a 
** table be handled?
**
**
** Query API Notes
** ---------------
**
** Before coding an UPDATE or DELETE row operation, the code-generator
** for those two operations needs to know whether or not the operation
** requires any FK processing and, if so, which columns of the original
** row are required by the FK processing VDBE code (i.e. if FKs were
** implemented using triggers, which of the old.* columns would be 
** accessed). No information is required by the code-generator before
** coding an INSERT operation. The functions used by the UPDATE/DELETE
** generation code to query for this information are:
**
**   sqlite3FkRequired() - Test to see if FK processing is required.
**   sqlite3FkOldmask()  - Query for the set of required old.* columns.
**
**
** Externally accessible module functions
** --------------------------------------
**
**   sqlite3FkCheck()    - Check for foreign key violations.
**   sqlite3FkActions()  - Code triggers for ON UPDATE/ON DELETE actions.
**   sqlite3FkDelete()   - Delete an FKey structure.
*/

    /*
    ** VDBE Calling Convention
    ** -----------------------
    **
    ** Example:
    **
    **   For the following INSERT statement:
    **
    **     CREATE TABLE t1(a, b INTEGER PRIMARY KEY, c);
    **     INSERT INTO t1 VALUES(1, 2, 3.1);
    **
    **   Register (x):        2    (type integer)
    **   Register (x+1):      1    (type integer)
    **   Register (x+2):      NULL (type NULL)
    **   Register (x+3):      3.1  (type real)
    */

    /*
    ** A foreign key constraint requires that the key columns in the parent
    ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
    ** Given that pParent is the parent table for foreign key constraint pFKey, 
    ** search the schema a unique index on the parent key columns. 
    **
    ** If successful, zero is returned. If the parent key is an INTEGER PRIMARY 
    ** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx 
    ** is set to point to the unique index. 
    ** 
    ** If the parent key consists of a single column (the foreign key constraint
    ** is not a composite foreign key), refput variable *paiCol is set to NULL.
    ** Otherwise, it is set to point to an allocated array of size N, where
    ** N is the number of columns in the parent key. The first element of the
    ** array is the index of the child table column that is mapped by the FK
    ** constraint to the parent table column stored in the left-most column
    ** of index *ppIdx. The second element of the array is the index of the
    ** child table column that corresponds to the second left-most column of
    ** *ppIdx, and so on.
    **
    ** If the required index cannot be found, either because:
    **
    **   1) The named parent key columns do not exist, or
    **
    **   2) The named parent key columns do exist, but are not subject to a
    **      UNIQUE or PRIMARY KEY constraint, or
    **
    **   3) No parent key columns were provided explicitly as part of the
    **      foreign key definition, and the parent table does not have a
    **      PRIMARY KEY, or
    **
    **   4) No parent key columns were provided explicitly as part of the
    **      foreign key definition, and the PRIMARY KEY of the parent table 
    **      consists of a different number of columns to the child key in 
    **      the child table.
    **
    ** then non-zero is returned, and a "foreign key mismatch" error loaded
    ** into pParse. If an OOM error occurs, non-zero is returned and the
    ** pParse.db.mallocFailed flag is set.
    */
    static int locateFkeyIndex(
      Parse pParse,                  /* Parse context to store any error in */
      Table pParent,                 /* Parent table of FK constraint pFKey */
      FKey pFKey,                    /* Foreign key to find index for */
      out Index ppIdx,               /* OUT: Unique index on parent table */
      out int[] paiCol               /* OUT: Map of index columns in pFKey */
    )
    {
      Index pIdx = null;                 /* Value to return via *ppIdx */
      ppIdx = null;
      int[] aiCol = null;                /* Value to return via *paiCol */
      paiCol = null;

      int nCol = pFKey.nCol;             /* Number of columns in parent key */
      string zKey = pFKey.aCol[0].zCol;  /* Name of left-most parent key column */

      /* The caller is responsible for zeroing output parameters. */
      //assert( ppIdx && *ppIdx==0 );
      //assert( !paiCol || *paiCol==0 );
      Debug.Assert( pParse != null );

      /* If this is a non-composite (single column) foreign key, check if it 
      ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx 
      ** and *paiCol set to zero and return early. 
      **
      ** Otherwise, for a composite foreign key (more than one column), allocate
      ** space for the aiCol array (returned via output parameter *paiCol).
      ** Non-composite foreign keys do not require the aiCol array.
      */
      if ( nCol == 1 )
      {
        /* The FK maps to the IPK if any of the following are true:
        **
        **   1) There is an INTEGER PRIMARY KEY column and the FK is implicitly 
        **      mapped to the primary key of table pParent, or
        **   2) The FK is explicitly mapped to a column declared as INTEGER
        **      PRIMARY KEY.
        */
        if ( pParent.iPKey >= 0 )
        {
          if ( null == zKey )
            return 0;
          if ( pParent.aCol[pParent.iPKey].zName.Equals( zKey ,StringComparison.OrdinalIgnoreCase ) )
            return 0;
        }
      }
      else //if( paiCol ){
      {
        Debug.Assert( nCol > 1 );
        aiCol = new int[nCol];// (int*)sqlite3DbMallocRaw( pParse.db, nCol * sizeof( int ) );
        //if( !aiCol ) return 1;
        paiCol = aiCol;
      }

      for ( pIdx = pParent.pIndex; pIdx != null; pIdx = pIdx.pNext )
      {
        if ( pIdx.nColumn == nCol && pIdx.onError != OE_None )
        {
          /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
          ** of columns. If each indexed column corresponds to a foreign key
          ** column of pFKey, then this index is a winner.  */

          if ( zKey == null )
          {
            /* If zKey is NULL, then this foreign key is implicitly mapped to 
            ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be 
            ** identified by the test (Index.autoIndex==2).  */
            if ( pIdx.autoIndex == 2 )
            {
              if ( aiCol != null )
              {
                int i;
                for ( i = 0; i < nCol; i++ )
                  aiCol[i] = pFKey.aCol[i].iFrom;
              }
              break;
            }
          }
          else
          {
            /* If zKey is non-NULL, then this foreign key was declared to
            ** map to an explicit list of columns in table pParent. Check if this
            ** index matches those columns. Also, check that the index uses
            ** the default collation sequences for each column. */
            int i, j;
            for ( i = 0; i < nCol; i++ )
            {
              int iCol = pIdx.aiColumn[i];     /* Index of column in parent tbl */
              string zDfltColl;                  /* Def. collation for column */
              string zIdxCol;                    /* Name of indexed column */

              /* If the index uses a collation sequence that is different from
              ** the default collation sequence for the column, this index is
              ** unusable. Bail out early in this case.  */
              zDfltColl = pParent.aCol[iCol].zColl;
              if ( String.IsNullOrEmpty( zDfltColl ) )
              {
                zDfltColl = "BINARY";
              }
              if ( !pIdx.azColl[i].Equals( zDfltColl ,StringComparison.OrdinalIgnoreCase )  )
                break;

              zIdxCol = pParent.aCol[iCol].zName;
              for ( j = 0; j < nCol; j++ )
              {
                if ( pFKey.aCol[j].zCol.Equals( zIdxCol ,StringComparison.OrdinalIgnoreCase )  )
                {
                  if ( aiCol != null )
                    aiCol[i] = pFKey.aCol[j].iFrom;
                  break;
                }
              }
              if ( j == nCol )
                break;
            }
            if ( i == nCol )
              break;      /* pIdx is usable */
          }
        }
      }

      if ( null == pIdx )
      {
        if ( 0 == pParse.disableTriggers )
        {
          sqlite3ErrorMsg( pParse, "foreign key mismatch" );
        }
        sqlite3DbFree( pParse.db, ref aiCol );
        return 1;
      }

      ppIdx = pIdx;
      return 0;
    }
Beispiel #27
0
        static Trigger FKActionTrigger(Parse parse, Table table, FKey fkey, ExprList changes)
        {
            Context ctx      = parse.Ctx;                 // Database handle
            int     actionId = (changes != null ? 1 : 0); // 1 for UPDATE, 0 for DELETE
            OE      action   = fkey.Actions[actionId];    // One of OE_None, OE_Cascade etc.
            Trigger trigger  = fkey.Triggers[actionId];   // Trigger definition to return

            if (action != OE.None && trigger == null)
            {
                Index index = null; // Parent key index for this FK
                int[] cols  = null; // child table cols . parent key cols
                if (LocateFkeyIndex(parse, table, fkey, out index, out cols) != 0)
                {
                    return(null);
                }
                Debug.Assert(cols != null || fkey.Cols.length == 1);

                Expr     where_ = null; // WHERE clause of trigger step
                Expr     when   = null; // WHEN clause for the trigger
                ExprList list   = null; // Changes list if ON UPDATE CASCADE
                for (int i = 0; i < fkey.Cols.length; i++)
                {
                    Token oldToken = new Token("old", 3);                         // Literal "old" token
                    Token newToken = new Token("new", 3);                         // Literal "new" token

                    int fromColId = (cols != null ? cols[i] : fkey.Cols[0].From); // Idx of column in child table
                    Debug.Assert(fromColId >= 0);
                    Token fromCol = new Token();                                  // Name of column in child table
                    Token toCol   = new Token();                                  // Name of column in parent table
                    toCol.data     = (index != null ? table.Cols[index.Columns[i]].Name : "oid");
                    fromCol.data   = fkey.From.Cols[fromColId].Name;
                    toCol.length   = (uint)toCol.data.Length;
                    fromCol.length = (uint)fromCol.data.Length;

                    // Create the expression "OLD.zToCol = zFromCol". It is important that the "OLD.zToCol" term is on the LHS of the = operator, so
                    // that the affinity and collation sequence associated with the parent table are used for the comparison.
                    Expr eq = Expr.PExpr(parse, TK.EQ,
                                         Expr.PExpr(parse, TK.DOT,
                                                    Expr.PExpr(parse, TK.ID, null, null, oldToken),
                                                    Expr.PExpr(parse, TK.ID, null, null, toCol)
                                                    , 0),
                                         Expr.PExpr(parse, TK.ID, null, null, fromCol)
                                         , 0); // tFromCol = OLD.tToCol
                    where_ = Expr.And(ctx, where_, eq);

                    // For ON UPDATE, construct the next term of the WHEN clause. The final WHEN clause will be like this:
                    //
                    //    WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN)
                    if (changes != null)
                    {
                        eq = Expr.PExpr(parse, TK.IS,
                                        Expr.PExpr(parse, TK.DOT,
                                                   Expr.PExpr(parse, TK.ID, null, null, oldToken),
                                                   Expr.PExpr(parse, TK.ID, null, null, toCol),
                                                   0),
                                        Expr.PExpr(parse, TK.DOT,
                                                   Expr.PExpr(parse, TK.ID, null, null, newToken),
                                                   Expr.PExpr(parse, TK.ID, null, null, toCol),
                                                   0),
                                        0);
                        when = Expr.And(ctx, when, eq);
                    }

                    if (action != OE.Restrict && (action != OE.Cascade || changes != null))
                    {
                        Expr newExpr;
                        if (action == OE.Cascade)
                        {
                            newExpr = Expr.PExpr(parse, TK.DOT,
                                                 Expr.PExpr(parse, TK.ID, null, null, newToken),
                                                 Expr.PExpr(parse, TK.ID, null, null, toCol)
                                                 , 0);
                        }
                        else if (action == OE.SetDflt)
                        {
                            Expr dfltExpr = fkey.From.Cols[fromColId].Dflt;
                            if (dfltExpr != null)
                            {
                                newExpr = Expr.Dup(ctx, dfltExpr, 0);
                            }
                            else
                            {
                                newExpr = Expr.PExpr(parse, TK.NULL, 0, 0, 0);
                            }
                        }
                        else
                        {
                            newExpr = Expr.PExpr(parse, TK.NULL, 0, 0, 0);
                        }
                        list = Expr.ListAppend(parse, list, newExpr);
                        Expr.ListSetName(parse, list, fromCol, 0);
                    }
                }
                C._tagfree(ctx, ref cols);

                string fromName       = fkey.From.Name;  // Name of child table
                int    fromNameLength = fromName.Length; // Length in bytes of zFrom

                Select select = null;                    // If RESTRICT, "SELECT RAISE(...)"
                if (action == OE.Restrict)
                {
                    Token from = new Token();
                    from.data   = fromName;
                    from.length = fromNameLength;
                    Expr raise = Expr.Expr(ctx, TK.RAISE, "foreign key constraint failed");
                    if (raise != null)
                    {
                        raise.Affinity = OE.Abort;
                    }
                    select = Select.New(parse,
                                        Expr.ListAppend(parse, 0, raise),
                                        SrcListAppend(ctx, 0, from, null),
                                        where_,
                                        null, null, null, 0, null, null);
                    where_ = null;
                }

                // Disable lookaside memory allocation
                bool enableLookaside = ctx.Lookaside.Enabled; // Copy of ctx->lookaside.bEnabled
                ctx.Lookaside.Enabled = false;

                trigger = new Trigger();
                //: trigger = (Trigger *)_tagalloc(ctx,
                //:    sizeof(Trigger) + // Trigger
                //:    sizeof(TriggerStep) + // Single step in trigger program
                //:    fromNameLength + 1 // Space for pStep->target.z
                //:    , true);
                TriggerStep step = null; // First (only) step of trigger program
                if (trigger != null)
                {
                    step               = trigger.StepList = new TriggerStep(); //: (TriggerStep)trigger[1];
                    step.Target.data   = fromName;                             //: (char *)&step[1];
                    step.Target.length = fromNameLength;
                    //: _memcpy((const char *)step->Target.data, fromName, fromNameLength);

                    step.Where    = Expr.Dup(ctx, where_, EXPRDUP_REDUCE);
                    step.ExprList = Expr.ListDup(ctx, list, EXPRDUP_REDUCE);
                    step.Select   = Select.Dup(ctx, select, EXPRDUP_REDUCE);
                    if (when != null)
                    {
                        when         = Expr.PExpr(parse, TK.NOT, when, 0, 0);
                        trigger.When = Expr.Dup(ctx, when, EXPRDUP_REDUCE);
                    }
                }

                // Re-enable the lookaside buffer, if it was disabled earlier.
                ctx.Lookaside.Enabled = enableLookaside;

                Expr.Delete(ctx, ref where_);
                Expr.Delete(ctx, ref when);
                Expr.ListDelete(ctx, ref list);
                Select.Delete(ctx, ref select);
                if (ctx.MallocFailed)
                {
                    FKTriggerDelete(ctx, trigger);
                    return(null);
                }

                switch (action)
                {
                case OE.Restrict:
                    step.OP = TK.SELECT;
                    break;

                case OE.Cascade:
                    if (changes == null)
                    {
                        step.OP = TK.DELETE;
                        break;
                    }
                    goto default;

                default:
                    step.OP = TK.UPDATE;
                    break;
                }
                step.Trigger            = trigger;
                trigger.Schema          = table.Schema;
                trigger.TabSchema       = table.Schema;
                fkey.Triggers[actionId] = trigger;
                trigger.OP = (TK)(changes != null ? TK.UPDATE : TK.DELETE);
            }

            return(trigger);
        }
Beispiel #28
0
    /*
    ** This function is called to generate code executed when a row is deleted
    ** from the parent table of foreign key constraint pFKey and, if pFKey is 
    ** deferred, when a row is inserted into the same table. When generating
    ** code for an SQL UPDATE operation, this function may be called twice -
    ** once to "delete" the old row and once to "insert" the new row.
    **
    ** The code generated by this function scans through the rows in the child
    ** table that correspond to the parent table row being deleted or inserted.
    ** For each child row found, one of the following actions is taken:
    **
    **   Operation | FK type   | Action taken
    **   --------------------------------------------------------------------------
    **   DELETE      immediate   Increment the "immediate constraint counter".
    **                           Or, if the ON (UPDATE|DELETE) action is RESTRICT,
    **                           throw a "foreign key constraint failed" exception.
    **
    **   INSERT      immediate   Decrement the "immediate constraint counter".
    **
    **   DELETE      deferred    Increment the "deferred constraint counter".
    **                           Or, if the ON (UPDATE|DELETE) action is RESTRICT,
    **                           throw a "foreign key constraint failed" exception.
    **
    **   INSERT      deferred    Decrement the "deferred constraint counter".
    **
    ** These operations are identified in the comment at the top of this file 
    ** (fkey.c) as "I.2" and "D.2".
    */
    static void fkScanChildren(
      Parse pParse,                   /* Parse context */
      SrcList pSrc,                   /* SrcList containing the table to scan */
      Table pTab,
      Index pIdx,                     /* Foreign key index */
      FKey pFKey,                     /* Foreign key relationship */
      int[] aiCol,                    /* Map from pIdx cols to child table cols */
      int regData,                    /* Referenced table data starts here */
      int nIncr                       /* Amount to increment deferred counter by */
    )
    {
      sqlite3 db = pParse.db;        /* Database handle */
      int i;                          /* Iterator variable */
      Expr pWhere = null;             /* WHERE clause to scan with */
      NameContext sNameContext;       /* Context used to resolve WHERE clause */
      WhereInfo pWInfo;               /* Context used by sqlite3WhereXXX() */
      int iFkIfZero = 0;              /* Address of OP_FkIfZero */
      Vdbe v = sqlite3GetVdbe( pParse );

      Debug.Assert( null == pIdx || pIdx.pTable == pTab );

      if ( nIncr < 0 )
      {
        iFkIfZero = sqlite3VdbeAddOp2( v, OP_FkIfZero, pFKey.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 ( i = 0; i < pFKey.nCol; i++ )
      {
        Expr pLeft;                  /* Value from parent table row */
        Expr pRight;                 /* Column ref to child table */
        Expr pEq;                    /* Expression (pLeft = pRight) */
        int iCol;                     /* Index of column in child table */
        string zCol;             /* Name of column in child table */

        pLeft = sqlite3Expr( db, TK_REGISTER, null );
        if ( pLeft != null )
        {
          /* Set the collation sequence and affinity of the LHS of each TK_EQ
          ** expression to the parent key column defaults.  */
          if ( pIdx != null )
          {
            Column pCol;
            iCol = pIdx.aiColumn[i];
            pCol = pTab.aCol[iCol];
            if ( pTab.iPKey == iCol )
              iCol = -1;
            pLeft.iTable = regData + iCol + 1;
            pLeft.affinity = pCol.affinity;
            pLeft.pColl = sqlite3LocateCollSeq( pParse, pCol.zColl );
          }
          else
          {
            pLeft.iTable = regData;
            pLeft.affinity = SQLITE_AFF_INTEGER;
          }
        }
        iCol = aiCol != null ? aiCol[i] : pFKey.aCol[0].iFrom;
        Debug.Assert( iCol >= 0 );
        zCol = pFKey.pFrom.aCol[iCol].zName;
        pRight = sqlite3Expr( db, TK_ID, zCol );
        pEq = sqlite3PExpr( pParse, TK_EQ, pLeft, pRight, 0 );
        pWhere = sqlite3ExprAnd( db, pWhere, pEq );
      }

      /* 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 ( pTab == pFKey.pFrom && nIncr > 0 )
      {
        Expr pEq;                    /* Expression (pLeft = pRight) */
        Expr pLeft;                  /* Value from parent table row */
        Expr pRight;                 /* Column ref to child table */
        pLeft = sqlite3Expr( db, TK_REGISTER, null );
        pRight = sqlite3Expr( db, TK_COLUMN, null );
        if ( pLeft != null && pRight != null )
        {
          pLeft.iTable = regData;
          pLeft.affinity = SQLITE_AFF_INTEGER;
          pRight.iTable = pSrc.a[0].iCursor;
          pRight.iColumn = -1;
        }
        pEq = sqlite3PExpr( pParse, TK_NE, pLeft, pRight, 0 );
        pWhere = sqlite3ExprAnd( db, pWhere, pEq );
      }

      /* Resolve the references in the WHERE clause. */
      sNameContext = new NameContext();// memset( &sNameContext, 0, sizeof( NameContext ) );
      sNameContext.pSrcList = pSrc;
      sNameContext.pParse = pParse;
      sqlite3ResolveExprNames( sNameContext, ref pWhere );

      /* Create VDBE to loop through the entries in pSrc 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 nIncr for each row selected.  */
      ExprList elDummy = null;
      pWInfo = sqlite3WhereBegin( pParse, pSrc, pWhere, ref elDummy, 0 );
      if ( nIncr > 0 && pFKey.isDeferred == 0 )
      {
        sqlite3ParseToplevel( pParse ).mayAbort = 1;
      }
      sqlite3VdbeAddOp2( v, OP_FkCounter, pFKey.isDeferred, nIncr );
      if ( pWInfo != null )
      {
        sqlite3WhereEnd( pWInfo );
      }

      /* Clean up the WHERE clause constructed above. */
      sqlite3ExprDelete( db, ref pWhere );
      if ( iFkIfZero != 0 )
      {
        sqlite3VdbeJumpHere( v, iFkIfZero );
      }
    }
Beispiel #29
0
        static Trigger FKActionTrigger(Parse parse, Table table, FKey fkey, ExprList changes)
        {
            Context ctx = parse.Ctx; // Database handle
            int actionId = (changes != null ? 1 : 0);  // 1 for UPDATE, 0 for DELETE
            OE action = fkey.Actions[actionId]; // One of OE_None, OE_Cascade etc.
            Trigger trigger = fkey.Triggers[actionId]; // Trigger definition to return

            if (action != OE.None && trigger == null)
            {

                Index index = null; // Parent key index for this FK
                int[] cols = null; // child table cols . parent key cols
                if (LocateFkeyIndex(parse, table, fkey, out index, out cols) != 0) return null;
                Debug.Assert(cols != null || fkey.Cols.length == 1);

                Expr where_ = null; // WHERE clause of trigger step
                Expr when = null; // WHEN clause for the trigger
                ExprList list = null; // Changes list if ON UPDATE CASCADE
                for (int i = 0; i < fkey.Cols.length; i++)
                {
                    Token oldToken = new Token("old", 3); // Literal "old" token
                    Token newToken = new Token("new", 3); // Literal "new" token

                    int fromColId = (cols != null ? cols[i] : fkey.Cols[0].From); // Idx of column in child table
                    Debug.Assert(fromColId >= 0);
                    Token fromCol = new Token(); // Name of column in child table
                    Token toCol = new Token(); // Name of column in parent table
                    toCol.data = (index != null ? table.Cols[index.Columns[i]].Name : "oid");
                    fromCol.data = fkey.From.Cols[fromColId].Name;
                    toCol.length = (uint)toCol.data.Length;
                    fromCol.length = (uint)fromCol.data.Length;

                    // Create the expression "OLD.zToCol = zFromCol". It is important that the "OLD.zToCol" term is on the LHS of the = operator, so
                    // that the affinity and collation sequence associated with the parent table are used for the comparison.
                    Expr eq = Expr.PExpr(parse, TK.EQ,
                        Expr.PExpr(parse, TK.DOT,
                            Expr.PExpr(parse, TK.ID, null, null, oldToken),
                            Expr.PExpr(parse, TK.ID, null, null, toCol)
                            , 0),
                        Expr.PExpr(parse, TK.ID, null, null, fromCol)
                        , 0); // tFromCol = OLD.tToCol
                    where_ = Expr.And(ctx, where_, eq);

                    // For ON UPDATE, construct the next term of the WHEN clause. The final WHEN clause will be like this:
                    //
                    //    WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN)
                    if (changes != null)
                    {
                        eq = Expr.PExpr(parse, TK.IS,
                            Expr.PExpr(parse, TK.DOT,
                            Expr.PExpr(parse, TK.ID, null, null, oldToken),
                            Expr.PExpr(parse, TK.ID, null, null, toCol),
                            0),
                            Expr.PExpr(parse, TK.DOT,
                            Expr.PExpr(parse, TK.ID, null, null, newToken),
                            Expr.PExpr(parse, TK.ID, null, null, toCol),
                            0),
                            0);
                        when = Expr.And(ctx, when, eq);
                    }

                    if (action != OE.Restrict && (action != OE.Cascade || changes != null))
                    {
                        Expr newExpr;
                        if (action == OE.Cascade)
                            newExpr = Expr.PExpr(parse, TK.DOT,
                                Expr.PExpr(parse, TK.ID, null, null, newToken),
                                Expr.PExpr(parse, TK.ID, null, null, toCol)
                                , 0);
                        else if (action == OE.SetDflt)
                        {
                            Expr dfltExpr = fkey.From.Cols[fromColId].Dflt;
                            if (dfltExpr != null)
                                newExpr = Expr.Dup(ctx, dfltExpr, 0);
                            else
                                newExpr = Expr.PExpr(parse, TK.NULL, 0, 0, 0);
                        }
                        else
                            newExpr = Expr.PExpr(parse, TK.NULL, 0, 0, 0);
                        list = Expr.ListAppend(parse, list, newExpr);
                        Expr.ListSetName(parse, list, fromCol, 0);
                    }
                }
                C._tagfree(ctx, ref cols);

                string fromName = fkey.From.Name; // Name of child table
                int fromNameLength = fromName.Length; // Length in bytes of zFrom

                Select select = null; // If RESTRICT, "SELECT RAISE(...)"
                if (action == OE.Restrict)
                {
                    Token from = new Token();
                    from.data = fromName;
                    from.length = fromNameLength;
                    Expr raise = Expr.Expr(ctx, TK.RAISE, "foreign key constraint failed");
                    if (raise != null)
                        raise.Affinity = OE.Abort;
                    select = Select.New(parse,
                        Expr.ListAppend(parse, 0, raise),
                        SrcListAppend(ctx, 0, from, null),
                        where_,
                        null, null, null, 0, null, null);
                    where_ = null;
                }

                // Disable lookaside memory allocation
                bool enableLookaside = ctx.Lookaside.Enabled; // Copy of ctx->lookaside.bEnabled
                ctx.Lookaside.Enabled = false;

                trigger = new Trigger();
                //: trigger = (Trigger *)_tagalloc(ctx, 
                //:    sizeof(Trigger) + // Trigger
                //:    sizeof(TriggerStep) + // Single step in trigger program
                //:    fromNameLength + 1 // Space for pStep->target.z
                //:    , true);
                TriggerStep step = null; // First (only) step of trigger program                
                if (trigger != null)
                {
                    step = trigger.StepList = new TriggerStep(); //: (TriggerStep)trigger[1];
                    step.Target.data = fromName; //: (char *)&step[1];
                    step.Target.length = fromNameLength;
                    //: _memcpy((const char *)step->Target.data, fromName, fromNameLength);

                    step.Where = Expr.Dup(ctx, where_, EXPRDUP_REDUCE);
                    step.ExprList = Expr.ListDup(ctx, list, EXPRDUP_REDUCE);
                    step.Select = Select.Dup(ctx, select, EXPRDUP_REDUCE);
                    if (when != null)
                    {
                        when = Expr.PExpr(parse, TK.NOT, when, 0, 0);
                        trigger.When = Expr.Dup(ctx, when, EXPRDUP_REDUCE);
                    }
                }

                // Re-enable the lookaside buffer, if it was disabled earlier.
                ctx.Lookaside.Enabled = enableLookaside;

                Expr.Delete(ctx, ref where_);
                Expr.Delete(ctx, ref when);
                Expr.ListDelete(ctx, ref list);
                Select.Delete(ctx, ref select);
                if (ctx.MallocFailed)
                {
                    FKTriggerDelete(ctx, trigger);
                    return null;
                }

                switch (action)
                {
                    case OE.Restrict:
                        step.OP = TK.SELECT;
                        break;
                    case OE.Cascade:
                        if (changes == null)
                        {
                            step.OP = TK.DELETE;
                            break;
                        }
                        goto default;
                    default:
                        step.OP = TK.UPDATE;
                        break;
                }
                step.Trigger = trigger;
                trigger.Schema = table.Schema;
                trigger.TabSchema = table.Schema;
                fkey.Triggers[actionId] = trigger;
                trigger.OP = (TK)(changes != null ? TK.UPDATE : TK.DELETE);
            }

            return trigger;
        }
Beispiel #30
0
        public static void RenameTable(Parse parse, SrcList src, Token name)
        {
            Context ctx = parse.Ctx;               // Database connection

            Context.FLAG savedDbFlags = ctx.Flags; // Saved value of db->flags
            //if (C._NEVER(ctx.MallocFailed)) goto exit_rename_table;
            Debug.Assert(src.Srcs == 1);
            Debug.Assert(Btree.HoldsAllMutexes(ctx));

            Table table = parse.LocateTableItem(false, src.Ids[0]); // Table being renamed

            if (table == null)
            {
                goto exit_rename_table;
            }
            int    db     = Prepare.SchemaToIndex(ctx, table.Schema); // Database that contains the table
            string dbName = ctx.DBs[db].Name;                         // Name of database iDb

            ctx.Flags |= Context.FLAG.PreferBuiltin;

            // Get a NULL terminated version of the new table name.
            string nameAsString = Parse.NameFromToken(ctx, name); // NULL-terminated version of pName

            if (nameAsString == null)
            {
                goto exit_rename_table;
            }

            // Check that a table or index named 'zName' does not already exist in database iDb. If so, this is an error.
            if (Parse.FindTable(ctx, nameAsString, dbName) != null || Parse.FindIndex(ctx, nameAsString, dbName) != null)
            {
                parse.ErrorMsg("there is already another table or index with this name: %s", nameAsString);
                goto exit_rename_table;
            }

            // Make sure it is not a system table being altered, or a reserved name that the table is being renamed to.
            if (IsSystemTable(parse, table.Name) || parse->CheckObjectName(nameAsString) != RC.OK)
            {
                goto exit_rename_table;
            }

#if !OMIT_VIEW
            if (table.Select != null)
            {
                parse.ErrorMsg("view %s may not be altered", table.Name);
                goto exit_rename_table;
            }
#endif

#if !OMIT_AUTHORIZATION
            // Invoke the authorization callback.
            if (Auth.Check(parse, AUTH.ALTER_TABLE, dbName, table.Name, null))
            {
                goto exit_rename_table;
            }
#endif

            VTable vtable = null; // Non-zero if this is a v-tab with an xRename()
#if !OMIT_VIRTUALTABLE
            if (parse->ViewGetColumnNames(table) != 0)
            {
                goto exit_rename_table;
            }
            if (E.IsVirtual(table))
            {
                vtable = VTable.GetVTable(ctx, table);
                if (vtable.IVTable.IModule.Rename == null)
                {
                    vtable = null;
                }
            }
#endif

            // Begin a transaction and code the VerifyCookie for database iDb. Then modify the schema cookie (since the ALTER TABLE modifies the
            // schema). Open a statement transaction if the table is a virtual table.
            Vdbe v = parse.GetVdbe();
            if (v == null)
            {
                goto exit_rename_table;
            }
            parse.BeginWriteOperation((vtable != null ? 1 : 0), db);
            parse.ChangeCookie(db);

            // If this is a virtual table, invoke the xRename() function if one is defined. The xRename() callback will modify the names
            // of any resources used by the v-table implementation (including other SQLite tables) that are identified by the name of the virtual table.
#if  !OMIT_VIRTUALTABLE
            if (vtable != null)
            {
                int i = ++parse.Mems;
                v.AddOp4(OP.String8, 0, i, 0, nameAsString, 0);
                v.AddOp4(OP.VRename, i, 0, 0, vtable, Vdbe.P4T.VTAB);
                parse.MayAbort();
            }
#endif

            // figure out how many UTF-8 characters are in zName
            string tableName       = table.Name;                       // Original name of the table
            int    tableNameLength = C._utf8charlength(tableName, -1); // Number of UTF-8 characters in zTabName

#if !OMIT_TRIGGER
            string where_ = string.Empty; // Where clause to locate temp triggers
#endif

#if !OMIT_FOREIGN_KEY && !OMIT_TRIGGER
            if ((ctx.Flags & Context.FLAG.ForeignKeys) != 0)
            {
                // If foreign-key support is enabled, rewrite the CREATE TABLE statements corresponding to all child tables of foreign key constraints
                // for which the renamed table is the parent table.
                if ((where_ = WhereForeignKeys(parse, table)) != null)
                {
                    parse.NestedParse(
                        "UPDATE \"%w\".%s SET " +
                        "sql = sqlite_rename_parent(sql, %Q, %Q) " +
                        "WHERE %s;", dbName, E.SCHEMA_TABLE(db), tableName, nameAsString, where_);
                    C._tagfree(ctx, ref where_);
                }
            }
#endif

            // Modify the sqlite_master table to use the new table name.
            parse.NestedParse(
                "UPDATE %Q.%s SET " +
#if OMIT_TRIGGER
                "sql = sqlite_rename_table(sql, %Q), " +
#else
                "sql = CASE " +
                "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" +
                "ELSE sqlite_rename_table(sql, %Q) END, " +
#endif
                "tbl_name = %Q, " +
                "name = CASE " +
                "WHEN type='table' THEN %Q " +
                "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " +
                "'sqlite_autoindex_' || %Q || substr(name,%d+18) " +
                "ELSE name END " +
                "WHERE tbl_name=%Q AND " +
                "(type='table' OR type='index' OR type='trigger');",
                dbName, SCHEMA_TABLE(db), nameAsString, nameAsString, nameAsString,
#if !OMIT_TRIGGER
                nameAsString,
#endif
                nameAsString, tableNameLength, tableName);

#if !OMIT_AUTOINCREMENT
            // If the sqlite_sequence table exists in this database, then update it with the new table name.
            if (Parse.FindTable(ctx, "sqlite_sequence", dbName) != null)
            {
                parse.NestedParse(
                    "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q",
                    dbName, nameAsString, table.Name);
            }
#endif

#if !OMIT_TRIGGER
            // If there are TEMP triggers on this table, modify the sqlite_temp_master table. Don't do this if the table being ALTERed is itself located in the temp database.
            if ((where_ = WhereTempTriggers(parse, table)) != "")
            {
                parse.NestedParse(
                    "UPDATE sqlite_temp_master SET " +
                    "sql = sqlite_rename_trigger(sql, %Q), " +
                    "tbl_name = %Q " +
                    "WHERE %s;", nameAsString, nameAsString, where_);
                C._tagfree(ctx, ref where_);
            }
#endif

#if !(OMIT_FOREIGN_KEY) && !(OMIT_TRIGGER)
            if ((ctx.Flags & Context.FLAG.ForeignKeys) != 0)
            {
                for (FKey p = Parse.FkReferences(table); p != null; p = p.NextTo)
                {
                    Table from = p.From;
                    if (from != table)
                    {
                        ReloadTableSchema(parse, p.From, from.Name);
                    }
                }
            }
#endif

            // Drop and reload the internal table schema.
            ReloadTableSchema(parse, table, nameAsString);

exit_rename_table:
            Expr.SrcListDelete(ctx, ref src);
            C._tagfree(ctx, ref nameAsString);
            ctx.Flags = savedDbFlags;
        }
Beispiel #31
0
        public void DictionaryList()
        {
            Console.Title = "Домашнее задание по теме Generic < Dictionary < TKey, TValue> >";
            //================================================================================
            //        Создаем новый Dictionary <TKey, TValue> имен
            //================================================================================
            ColorModule.Colorconsole("Имеем Dictionary <TKey, TValue> городов", ConsoleColor.Magenta);
            Console.WriteLine("");
            ColorModule.Colorconsole("Dictionary < IKey, IValue >", ConsoleColor.Green);
            Dictionary <IKey, IValue> dictionary1 = new Dictionary <IKey, IValue>();

            dictionary1.Add(new DKey()
            {
                KeyD = 1
            }, new DValue()
            {
                ValueD = "Boris"
            });
            dictionary1.Add(new DKey()
            {
                KeyD = 2
            }, new DValue()
            {
                ValueD = "Sergei"
            });
            dictionary1.Add(new DKey()
            {
                KeyD = 3
            }, new DValue()
            {
                ValueD = "Mihail"
            });
            dictionary1.Add(new DKey()
            {
                KeyD = 4
            }, new DValue()
            {
                ValueD = "Natasha"
            });
            dictionary1.Add(new DKey()
            {
                KeyD = 5
            }, new DValue()
            {
                ValueD = "Dima"
            });
            dictionary1.Add(new DKey()
            {
                KeyD = 6
            }, new DValue()
            {
                ValueD = "Vlad"
            });



            foreach (KeyValuePair <IKey, IValue> item in dictionary1)
            {
                ColorModule.Colorconsole("  " + item.Key.KeyD + " : " + item.Value.ValueD, ConsoleColor.Gray);
            }

            Console.WriteLine(Environment.NewLine);


            Dictionary <IKey, IValue> dictionary2 = new Dictionary <IKey, IValue>();


            var valueKey1 = new FKey()
            {
                KeyD = 100
            };
            var valueKey2 = new FValue()
            {
                ValueD = "Smirnov"
            };
            var valueKey3 = new FKey()
            {
                KeyD = 110
            };
            var valueKey4 = new FValue()
            {
                ValueD = "Kovalev"
            };
            var valueKey5 = new FKey()
            {
                KeyD = 120
            };
            var valueKey6 = new FValue()
            {
                ValueD = "Ivanov"
            };
            var valueKey7 = new FKey()
            {
                KeyD = 130
            };
            var valueKey8 = new FValue()
            {
                ValueD = "Smirnov"
            };
            var valueKey9 = new FKey()
            {
                KeyD = 140
            };
            var valueKey10 = new FValue()
            {
                ValueD = "Pupkin"
            };



            dictionary2.Add(valueKey1, valueKey2);
            dictionary2.Add(valueKey3, valueKey4);
            dictionary2.Add(valueKey5, valueKey6);
            dictionary2.Add(valueKey7, valueKey8);
            dictionary2.Add(valueKey9, valueKey10);

            foreach (KeyValuePair <IKey, IValue> item in dictionary2)
            {
                ColorModule.Colorconsole("  " + item.Key.KeyD + " : " + item.Value.ValueD, ConsoleColor.Gray);
            }

            Console.WriteLine(Environment.NewLine);



            Console.WriteLine("Нажмите любую клавишу для выхода.....");
            MenuModule.menu = "0";
            Console.ReadLine();
        }