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)); } } }
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 override void Perform(SchemaChanges changes, IOutput output) { var pendingReorders = PendingReorderTableType.AllFrom(changes.Current); foreach (var desired in TableType.AllFrom(changes.Desired)) { if (changes.Current.Contains(desired)) { continue; } var fields = FieldType.ChildrenFrom(changes.Desired, desired.Identifier); var fieldCreation = from field in fields orderby field.State.OrdinalPosition select new FieldCreation(field.Name, field.State.DataType, field.State.IsNullable, field.State.IsSequencedPkey, field.State.SequenceName); changes.Put(changes.SchemaDriver.GetCreateTableSql(desired.Name, fieldCreation), new ObjectState[] { desired }.Concat(fields)); } }
private void createTempTables(SchemaChanges changes, IOutput output) { // For any tables with order-sensitivity and fields in different order between current and desired (ignoring fields that only exist in current), // Create a new table "_nrdo_reorder" using "select (columns in correct order followed by columns to be dropped) from original table into newname" // Put the table, the fields (with nullability, datatype and identityness as current but in new order) and a PendingReorderTable into current // For any tables that need their fields reordered, create a copy of the table with the fields in the right order. foreach (var origTable in TableType.AllFrom(changes.Current)) { // Tables that don't exist in desired state are to be dropped, they certainly don't need to be reordered! if (!changes.Desired.Contains(origTable)) { continue; } // Figure out whether a reorder is needed if (!FieldType.IsFieldReorderNeeded(changes, origTable.Identifier)) { continue; } // Put the fields in order. // At this point there may be extra fields that are to be dropped but haven't been yet. They need to stick around until // it's time to drop them, because before statements may rely on them. But we put those ones last (in the order they're currently in). var fieldsInOrder = from field in FieldType.ChildrenFrom(changes.Current, origTable.Identifier) let desired = changes.Desired.Get(field) orderby desired != null ? desired.State.OrdinalPosition : int.MaxValue, field.State.OrdinalPosition select field; // Construct the state representation that will be used for the new table. // It inherits all before statements from the original table so that when it's renamed they'll still be there. // FIXME: This hardcodes the assumption that CreateTableAsSelect preserves field type, nullability and identity-ness, which may or may not be true on other DBs // FIXME: any other aspects of field state that we add support for (Default values, check constraints...) need to get dropped here. var tempTable = TableType.Create(origTable.Name + "_nrdo_reorder"); var pendingReorder = PendingReorderTableType.Create(tempTable.Identifier, origTable.Name); var tempTableFields = from fi in fieldsInOrder.Select((field, index) => new { field, index }) select fi.field .WithParent(tempTable.Identifier) .With(s => s.WithOrdinalPosition(fi.index)); var tempTableBefores = from before in BeforeStatementType.ChildrenFrom(changes.Current, origTable.Identifier) select before.WithParent(tempTable.Identifier); // Gather all temporary objects into one list to put them into the database together var tempTableObjects = new ObjectState[] { tempTable, pendingReorder } .Concat(tempTableFields) .Concat(tempTableBefores); var sql = changes.SchemaDriver.GetCreateTableAsSelectSql(tempTable.Name, string.Join(", ", from field in fieldsInOrder select changes.DbDriver.QuoteIdentifier(field.Name)), changes.DbDriver.QuoteSchemaIdentifier(origTable.Name)); changes.Put(sql, tempTableObjects); } }