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); } }