/// <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); }
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_); }
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); }
/// <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)); }
/// <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)); }
/// <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)); }
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); }
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); } } } }
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); }
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); }
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); } }
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); }
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); }
protected override void ReceiveActorOnClicked_Implementation(FKey ButtonPressed) { base.ReceiveActorOnClicked_Implementation(ButtonPressed); HandleControllerClick(); }
/// <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)); }
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); }
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); }
/* ** 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 ); }
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; }
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); }
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; }
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); }
/// <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)); }
/* ** 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; }
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); } }
/* ** ** 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; }
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); }
/* ** 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 ); } }
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; }
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; }
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(); }