public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in FieldType.AllFrom(changes.Current)) { var desired = FieldType.GetDesired(changes, current); if (desired == null) { continue; } // If we can't alter sequenced keys then no point trying! if (!changes.SchemaDriver.IsAlterSequencedFieldSupported && (current.State.IsSequencedPkey || desired.State.IsSequencedPkey)) { continue; } // If the fields are the same by datatype and sequenced-ness - or the sequenced-ness is irrelevant at the field level - // then there's nothing to change if (changes.SchemaDriver.DbDriver.StringEquals(current.State.DataType, desired.State.DataType) && (current.State.IsSequencedPkey == desired.State.IsSequencedPkey || !changes.SchemaDriver.IsSequencedPartOfFieldDeclaration)) { continue; } // Try the alter statement. If it doesn't work, the field will be dropped and added. var altered = current.With(s => s.WithTypeChange(desired.State.DataType, desired.State.IsSequencedPkey, desired.State.SequenceName)); var alterSql = changes.SchemaDriver.GetAlterFieldTypeSql(altered.ParentName, altered.Name, altered.State.DataType, altered.State.IsNullable, altered.State.IsSequencedPkey, altered.State.SequenceName); changes.Put(alterSql, ErrorResponse.Ignore, altered); } }
public override void Perform(SchemaChanges changes, IOutput output) { // This isn't really anything to do with pre-upgrade-hooks but there's no other good place to give this warning! foreach (var query in QueryType.AllFrom(changes.Current)) { if (query.State != null && query.State.Body.StartsWith("<FAIL>")) { output.Warning(query.State.Body.Substring(6)); } } foreach (var hook in PreUpgradeHookType.AllFrom(changes.Current)) { var proc = QueryType.GetFrom(changes.Current, hook.ParentIdentifier); if (proc.State == null || proc.State is QueryType.FunctionState || proc.State.Parameters.Any()) { output.Warning("Cannot execute pre-upgrade hook " + proc.Name + ": stored procedure does not exist or has parameters."); } changes.Put(changes.SchemaDriver.GetExecuteProcSql(hook.ParentName)); if (changes.Desired.Get(hook) == null) { changes.Remove(null, hook); } } }
public override void Perform(SchemaChanges changes, IOutput output) { var fieldsToBeAdded = from field in FieldType.AllFrom(changes.Desired) where !changes.Current.Contains(field) orderby field.State.OrdinalPosition select field; // FIXME It's a little weird to be ordering by OrdinalPosition independent of table, because it means we jump from table to table adding all // fifth columns, then all sixth columns, etc, but it doesn't hurt anything. foreach (var desired in fieldsToBeAdded) { // We create all fields as initially nullable... var fieldToCreate = desired.With(s => s.WithNullable(true)); // ... except for sequenced fields on databases that don't support altering sequenced fields to notnull later. if (desired.State.IsSequencedPkey && !changes.SchemaDriver.IsAlterSequencedFieldSupported) { fieldToCreate = desired; } // Add the field changes.Put(changes.SchemaDriver.GetAddFieldSql(desired.ParentName, desired.Name, desired.State.DataType, desired.State.IsSequencedPkey, desired.State.SequenceName), fieldToCreate); } }
public static bool TriggerHasChanged(SchemaChanges changes, SubObjectState <TriggerType, State> current) { if (current == null) { return(true); } var desired = changes.Desired.Get(current); // The trigger doesn't exist in the desired schema at all if (desired == null) { return(true); } // The trigger's contents are different if (current.State.Timing != desired.State.Timing || current.State.Events != desired.State.Events || !Nstring.DBEquivalentComparer.Equals(current.State.Body, desired.State.Body)) { return(true); } // The table might need to be dropped entirely to reorder the columns if (FieldType.IsFieldReorderPossiblyNeeded(changes, current.ParentIdentifier)) { return(true); } return(false); }
public static bool FkeyHasChanged(SchemaChanges changes, SubObjectState <FkeyType, FkeyType.State> current) { if (current == null) { return(true); } var desired = changes.Desired.Get(current); // The foreign key doesn't exist in the desired schema at all if (desired == null) { return(true); } // The destination table has changed if (desired.State.ToTable != current.State.ToTable) { return(true); } // Either the from or to table might need to be dropped entirely to reorder fields if (FieldType.IsFieldReorderPossiblyNeeded(changes, current.ParentIdentifier) || FieldType.IsFieldReorderPossiblyNeeded(changes, current.State.ToTable)) { return(true); } // It's changed from cascading to not or vice versa if (desired.State.IsCascadeDelete != current.State.IsCascadeDelete) { return(true); } // The sequence of field name pairs don't match if (!Enumerable.SequenceEqual(desired.State.Joins, current.State.Joins, FieldPair.GetComparer(changes.DbDriver))) { return(true); } // Any of the fields in either table are changing if (current.State.Joins.Any(field => FieldType.FieldHasChanged(changes, current.ParentIdentifier, FieldType.Identifier(field.FromFieldName)) || FieldType.FieldHasChanged(changes, current.State.ToTable, FieldType.Identifier(field.ToFieldName)))) { return(true); } // The unique index on the destination table that corresponds to the destination fields is changing var ukey = UniqueIndexType.ChildrenFrom(changes.Current, current.State.ToTable) .SingleOrDefault(key => new HashSet <Identifier>(key.State.IndexState.Fields).SetEquals(from field in current.State.Joins select FieldType.Identifier(field.ToFieldName))); if (ukey != null && UniqueIndexType.IndexHasChanged(changes, ukey)) { return(true); } return(false); }
internal static bool IndexStateHasChanged(SchemaChanges changes, Identifier table, IndexState current, IndexState desired) { // The table might need to be dropped entirely to reorder the columns if (FieldType.IsFieldReorderPossiblyNeeded(changes, table)) { return(true); } // The sequence of field names don't match if (!Enumerable.SequenceEqual(desired.FieldNames, current.FieldNames, changes.DbDriver.DbStringComparer)) { return(true); } // Something custom about the index has changed if (!changes.SchemaDriver.IsIndexCustomStateEqual(current.CustomState, desired.CustomState)) { return(true); } // Any of the fields in the index are changing if (current.Fields.Any(field => FieldType.FieldHasChanged(changes, table, field))) { return(true); } return(false); }
public static bool IndexHasChanged(SchemaChanges changes, SubObjectState <UniqueIndexType, UniqueIndexType.State> current) { if (current == null) { return(true); } var desired = changes.Desired.Get(current); // The index doesn't exist in the desired schema at all if (desired == null) { return(true); } // It's changed from primary key to not or vice versa if (desired.State.IsPrimaryKey != current.State.IsPrimaryKey) { return(true); } // The index fields are different if (IndexState.IndexStateHasChanged(changes, current.ParentIdentifier, current.State.IndexState, desired.State.IndexState)) { return(true); } return(false); }
public override void Perform(SchemaChanges changes, IOutput output) { // This step covers both unique keys (including primary keys) and non-unique indexes foreach (var desired in UniqueIndexType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } var sql = changes.SchemaDriver.GetCreateUniqueConstraintSql(desired.ParentName, desired.State.IsPrimaryKey, desired.Name, desired.State.IndexState.FieldNames, desired.State.IndexState.CustomState); changes.Put(sql, desired); } foreach (var desired in NonUniqueIndexType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } var sql = changes.SchemaDriver.GetCreateIndexSql(desired.ParentName, desired.Name, desired.State.FieldNames, desired.State.CustomState); changes.Put(sql, desired); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var pending in PendingReorderTableType.AllFrom(changes.Current)) { var originalTable = TableType.Identifier(pending.Name); var tempTable = pending.ParentIdentifier; if (object.Equals(originalTable, tempTable)) { // If the database fails at precisely the wrong time, the rename can be succeed without dropping the PendingReorder, // so the table thinks it's to be renamed to itself. In that case we need to drop the PendingReorder, NOT drop the table! output.Verbose("Cleaning up after completed reorder of columns in " + originalTable); changes.Remove(null, pending); } else if (changes.Current.ContainsRoot(originalTable)) { // If the original table still exists, we drop the temp table so we can start over (in case the data in the table has changed since the temp table // was created, or the reordering has somehow become unnecessary). output.Verbose("Clearing pending reorder of columns in " + originalTable); changes.Remove(changes.SchemaDriver.GetDropTableSql(pending.ParentName), changes.Current.GetRoot(pending.ParentIdentifier)); } else { // The original table has been dropped so we need to recreate it by renaming the temp table. output.Verbose("Completing pending reordering of columns in " + originalTable); changes.Rename(changes.SchemaDriver.GetRenameTableSql(tempTable.Name, originalTable.Name), tempTable, originalTable); // And forget about the pending rename. changes.Remove(null, pending.WithParent(originalTable)); } } }
public override void Perform(SchemaChanges changes, IOutput output) { if (changes.Options.FieldDropBehavior == DropBehavior.SkipDrop) { return; } var errorResponse = changes.Options.FieldDropBehavior == DropBehavior.TryDrop ? ErrorResponse.Ignore : ErrorResponse.Fail; foreach (var current in FieldType.AllFrom(changes.Current)) { if (changes.Desired.Contains(current)) { continue; } // If we're going to drop the whole table then don't bother dropping the field if (!changes.Desired.ContainsRoot(current.ParentIdentifier)) { continue; } if (!changes.AllowDropWithPossibleDataLoss(current, changes.Options.FieldDropBehavior)) { continue; } changes.Remove(changes.SchemaDriver.GetDropFieldSql(current.ParentName, current.Name), current); } }
public static bool FieldHasChanged(SchemaChanges changes, Identifier table, Identifier field) { var current = FieldType.GetFrom(changes.Current, table, field); var desired = FieldType.GetFrom(changes.Desired, table, field); return(current == null || desired == null || !FieldType.IsEqual(current.State, desired.State, changes.SchemaDriver.IsSequencedPartOfFieldDeclaration)); }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in FieldType.AllFrom(changes.Current)) { // Don't need to set null if it's already null! if (current.State.IsNullable) { continue; } // Don't need to set null if nullability is already correct var desired = FieldType.GetDesired(changes, current); if (desired != null && !desired.State.IsNullable) { continue; } // Sequenced pkey fields can't be made nullable if (current.State.IsSequencedPkey && !changes.SchemaDriver.IsAlterSequencedFieldSupported) { continue; } // The field is either being dropped (desired == null) or set to nullable // Either way we set it to null now // We know to specify "false, null" for sequenced key field because the desired state can't be nullable if it's sequenced var setNullSql = changes.SchemaDriver.GetSetFieldNullSql(current.ParentName, current.Name, current.State.DataType, false, null); changes.Put(setNullSql, current.With(state => state.WithNullable(true))); } }
public override void Perform(SchemaChanges changes, IOutput output) { var completion = CompletionType.AllFrom(changes.Desired).Single(); if (!changes.Current.Contains(completion)) { changes.Put(null, completion); } }
private bool isSequencedPkeyToBeDropped(SchemaChanges changes, SubObjectState <FieldType, FieldType.State> current) { if (current.State.IsSequencedPkey) { var desiredTable = TableType.GetDesiredIdentifier(changes, current.ParentIdentifier); return(desiredTable != null && FieldType.ChildrenFrom(changes.Desired, desiredTable).Any(field => field.State.IsSequencedPkey)); } return(false); }
public static SubObjectState <FieldType, State> GetDesired(SchemaChanges changes, SubObjectState <FieldType, State> current) { var desiredTable = TableType.GetDesiredIdentifier(changes, current.ParentIdentifier); if (desiredTable == null) { return(null); } return(GetFrom(changes.Desired, desiredTable, current.Identifier)); }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in FulltextCatalogType.AllFrom(changes.Current)) { if (changes.Desired.Contains(current)) { continue; } changes.RemoveWithoutTransaction(changes.SchemaDriver.GetDropFulltextCatalogSql(current.Name), current); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in SequenceType.AllFrom(changes.Current)) { if (changes.Desired.Contains(current)) { continue; } changes.Remove(changes.SchemaDriver.GetDropSequenceSql(current.Name), current); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in TriggerType.AllFrom(changes.Current)) { if (!TriggerType.TriggerHasChanged(changes, current)) { continue; } changes.Remove(changes.SchemaDriver.GetDropTriggerSql(current.ParentName, current.Name), current); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in FulltextIndexType.AllFrom(changes.Current)) { if (!FulltextIndexType.FulltextIndexHasChanged(changes, current)) { continue; } changes.RemoveWithoutTransaction(changes.SchemaDriver.GetDropFulltextIndexSql(current.Name), current); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var desired in SequenceType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } changes.Put(changes.SchemaDriver.GetCreateSequenceSql(desired.Name), desired); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var desired in FkeyType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } changes.Put(changes.SchemaDriver.GetAddFkeySql(desired.ParentName, desired.State.ToTableName, desired.Name, desired.State.IsCascadeDelete, desired.State.Joins), desired); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in ViewType.AllFrom(changes.Current)) { var desired = changes.Desired.Get(current); if (desired != null && ViewType.IsEqual(current.State, desired.State, changes.DbDriver)) { continue; } changes.Remove(changes.SchemaDriver.GetDropViewSql(current.Name), current); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var desired in FulltextIndexType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } changes.PutWithoutTransaction(changes.SchemaDriver.GetCreateFulltextIndexSql(desired.Name, desired.ParentName, desired.State.KeyName, desired.State.Columns), desired); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var desired in TriggerType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } var table = desired.ParentIdentifier; changes.Put(changes.SchemaDriver.GetCreateTriggerSql(table.Name, desired.Name, desired.State.Timing, desired.State.Events, desired.State.Body), desired); } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var desired in QueryType.AllFrom(changes.Desired)) { var current = changes.Current.Get(desired); if (current != null && QueryType.IsEqual(current.State, desired.State, changes.DbDriver)) { continue; } if (desired.State != null) { var body = desired.State.Body; var parameters = desired.State.Parameters; string createSql; var functionState = desired.State as QueryType.FunctionState; if (functionState != null) { createSql = changes.SchemaDriver.GetCreateFunctionSql(desired.Name, parameters, functionState.ReturnType, body); } else { createSql = changes.SchemaDriver.GetCreateProcSql(desired.Name, parameters, body); } changes.Put(createSql, desired); } else { if (current == null) { changes.Put(null, desired); } } } // Restore all pre-upgrade-hooks corresponding to procedures that have been successfully updated. foreach (var upgradeHook in PreUpgradeHookType.AllFrom(changes.Desired)) { var desiredProc = QueryType.GetFrom(changes.Desired, upgradeHook.ParentIdentifier); var currentProc = changes.Current.Get(desiredProc); if (currentProc != null && QueryType.IsEqual(currentProc.State, desiredProc.State, changes.DbDriver)) { changes.Put(null, upgradeHook); } } }
public override void Perform(SchemaChanges changes, IOutput output) { foreach (var current in QueryType.AllFrom(changes.Current)) { // Queries that are neither stored proc or stored function in the current schema don't need to be dropped! if (current.State == null) { continue; } var desired = changes.Desired.Get(current); if (desired != null) { if (changes.SchemaDriver.IsCreateOrReplaceProcSupported) { // If CREATE OR REPLACE PROCEDURE is supported in the current database, there's no need to drop it unless // it's changed from function to procedure or vice versa. if (QueryType.IsTypeEqual(current.State, desired.State)) { continue; } } else { // Even if CREATE OR REPLACE PROCEDURE is not supported, we don't need to drop it if it's // not changed at all. if (QueryType.IsEqual(current.State, desired.State, changes.DbDriver)) { continue; } } } var dropSql = current.State is QueryType.FunctionState ? changes.SchemaDriver.GetDropFunctionSql(current.Name) : changes.SchemaDriver.GetDropProcSql(current.Name); if (desired != null) { // The query isn't going away entirely so we don't want to lose its associated before statements changes.Put(dropSql, QueryType.CreateUnstored(current.Name)); } else { changes.Remove(dropSql, current); } } }
private void renameTempTables(SchemaChanges changes, IOutput output) { // For any PendingReorderTables where the original table doesn't exist, rename the table to the target name and remove the pending rename. foreach (var pending in PendingReorderTableType.AllFrom(changes.Current)) { var originalTable = TableType.Identifier(pending.Name); var tempTable = pending.ParentIdentifier; if (changes.Current.ContainsRoot(originalTable)) { continue; } changes.Rename(changes.SchemaDriver.GetRenameTableSql(tempTable.Name, originalTable.Name), tempTable, originalTable); changes.Remove(null, pending.WithParent(originalTable)); } }
private void dropOriginalTables(SchemaChanges changes, IOutput output) { // For any tables that are the target of existing PendingReorderTables, drop them (without prompting). foreach (var pending in PendingReorderTableType.AllFrom(changes.Current)) { var originalTable = TableType.Identifier(pending.Name); var tempTable = pending.ParentIdentifier; if (!changes.Current.ContainsRoot(originalTable)) { continue; } // FIXME: (Consider sanity checks before doing this - anything from "confirm all the right column names exist" to "confirm the right number of rows exist" to // "confirm all the data is identical in all the rows") changes.Remove(changes.SchemaDriver.GetDropTableSql(originalTable.Name), changes.Current.GetRoot(originalTable)); } }
public static bool IndexHasChanged(SchemaChanges changes, SubObjectState <NonUniqueIndexType, IndexState> current) { var desired = changes.Desired.Get(current); // The index doesn't exist in the desired schema at all if (desired == null) { return(true); } // The index fields are different if (IndexState.IndexStateHasChanged(changes, current.ParentIdentifier, current.State, desired.State)) { return(true); } return(false); }
public static Identifier GetDesiredIdentifier(SchemaChanges changes, Identifier ident) { CheckType(ident); if (changes.Desired.ContainsRoot(ident)) { return(ident); } var rename = TableRenameType.GetFrom(changes.Desired, TableRenameType.Identifier(ident.Name)); if (rename != null) { return(rename.State.ToTable); } return(null); }
internal SchemaChange(SchemaChanges change, string name, string dbDataType) { Change = change; Name = name; DbDataType = dbDataType; }