public static int DuplicateCount(UniqueIndex uniqueIndex, Replacements rep) { var primaryKey = uniqueIndex.Table.Columns.Values.Where(a => a.PrimaryKey).Only(); if (primaryKey == null) { throw new InvalidOperationException("No primary key found"); } ; var oldTableName = rep.Apply(Replacements.KeyTablesInverse, uniqueIndex.Table.Name.ToString()); var columnReplacement = rep.TryGetC(Replacements.KeyColumnsForTable(uniqueIndex.Table.Name.ToString()))?.Inverse() ?? new Dictionary <string, string>(); var oldColumns = uniqueIndex.Columns.ToString(c => (columnReplacement.TryGetC(c.Name) ?? c.Name).SqlEscape(), ", "); var oldPrimaryKey = columnReplacement.TryGetC(primaryKey.Name) ?? primaryKey.Name; return((int)Executor.ExecuteScalar( $@"SELECT Count(*) FROM {oldTableName} WHERE {oldPrimaryKey} NOT IN ( SELECT MIN({oldPrimaryKey}) FROM {oldTableName} {(string.IsNullOrWhiteSpace(uniqueIndex.Where) ? "" : "WHERE " + uniqueIndex.Where.Replace(columnReplacement))} GROUP BY {oldColumns} ){(string.IsNullOrWhiteSpace(uniqueIndex.Where) ? "" : "AND " + uniqueIndex.Where.Replace(columnReplacement))}") !); }
public static IDisposable RenameTable(Table table, Replacements replacements) { string fullName = replacements.TryGetC(Replacements.KeyTablesInverse)?.TryGetC(table.Name.ToString()); if (fullName == null) { return(null); } ObjectName realName = table.Name; table.Name = ObjectName.Parse(fullName); return(new Disposable(() => table.Name = realName)); }
public static IDisposable?UseOldTableName(Table table, Replacements replacements) { string?fullName = replacements.TryGetC(Replacements.KeyTablesInverse)?.TryGetC(table.Name.ToString()); if (fullName == null) { return(null); } ObjectName realName = table.Name; table.Name = ObjectName.Parse(fullName, Schema.Current.Settings.IsPostgres); return(new Disposable(() => table.Name = realName)); }
public static SqlPreCommand SynchronizeTablesScript(Replacements replacements) { Dictionary <string, ITable> model = Schema.Current.GetDatabaseTables().ToDictionaryEx(a => a.Name.ToString(), "schema tables"); HashSet <SchemaName> modelSchemas = Schema.Current.GetDatabaseTables().Select(a => a.Name.Schema).Where(a => !SqlBuilder.SystemSchemas.Contains(a.Name)).ToHashSet(); Dictionary <string, DiffTable> database = DefaultGetDatabaseDescription(Schema.Current.DatabaseNames()); HashSet <SchemaName> databaseSchemas = DefaultGetSchemas(Schema.Current.DatabaseNames()); if (SimplifyDiffTables != null) { SimplifyDiffTables(database); } replacements.AskForReplacements(database.Keys.ToHashSet(), model.Keys.ToHashSet(), Replacements.KeyTables); database = replacements.ApplyReplacementsToOld(database, Replacements.KeyTables); Dictionary <ITable, Dictionary <string, Index> > modelIndices = model.Values .ToDictionary(t => t, t => t.GeneratAllIndexes().ToDictionaryEx(a => a.IndexName, "Indexes for {0}".FormatWith(t.Name))); model.JoinDictionaryForeach(database, (tn, tab, diff) => { var key = Replacements.KeyColumnsForTable(tn); replacements.AskForReplacements(diff.Columns.Keys.ToHashSet(), tab.Columns.Keys.ToHashSet(), key); diff.Columns = replacements.ApplyReplacementsToOld(diff.Columns, key); diff.Indices = ApplyIndexAutoReplacements(diff, tab, modelIndices[tab]); }); Func <ObjectName, ObjectName> ChangeName = (ObjectName objectName) => { string name = replacements.Apply(Replacements.KeyTables, objectName.ToString()); return(model.TryGetC(name)?.Name ?? objectName); }; Func <ObjectName, SqlPreCommand> DeleteAllForeignKey = tableName => { var dropFks = (from t in database.Values from c in t.Columns.Values where c.ForeignKey != null && c.ForeignKey.TargetTable.Equals(tableName) select SqlBuilder.AlterTableDropConstraint(t.Name, c.ForeignKey.Name)).Combine(Spacing.Simple); if (dropFks == null) { return(null); } return(SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("---In order to remove the PK of " + tableName.Name), dropFks)); }; using (replacements.WithReplacedDatabaseName()) { SqlPreCommand createSchemas = Synchronizer.SynchronizeScriptReplacing(replacements, "Schemas", modelSchemas.ToDictionary(a => a.ToString()), databaseSchemas.ToDictionary(a => a.ToString()), (_, newSN) => SqlBuilder.CreateSchema(newSN), null, (_, newSN, oldSN) => newSN.Equals(oldSN) ? null : SqlBuilder.CreateSchema(newSN), Spacing.Double); //use database without replacements to just remove indexes SqlPreCommand dropStatistics = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => SqlBuilder.DropStatistics(tn, dif.Stats), (tn, tab, dif) => { var removedColums = dif.Columns.Keys.Except(tab.Columns.Keys).ToHashSet(); return(SqlBuilder.DropStatistics(tn, dif.Stats.Where(a => a.Columns.Any(removedColums.Contains)).ToList())); }, Spacing.Double); SqlPreCommand dropIndices = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => dif.Indices.Values.Where(ix => !ix.IsPrimary).Select(ix => SqlBuilder.DropIndex(dif.Name, ix)).Combine(Spacing.Simple), (tn, tab, dif) => { Dictionary <string, Index> modelIxs = modelIndices[tab]; var removedColums = dif.Columns.Keys.Except(tab.Columns.Keys).ToHashSet(); var changes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, null, (i, dix) => dix.Columns.Any(removedColums.Contains) || dix.IsControlledIndex ? SqlBuilder.DropIndex(dif.Name, dix) : null, (i, mix, dix) => !dix.IndexEquals(dif, mix) ? SqlPreCommand.Combine(Spacing.Double, dix.IsPrimary ? DeleteAllForeignKey(dif.Name) : null, SqlBuilder.DropIndex(dif.Name, dix)) : null, Spacing.Simple); return(changes); }, Spacing.Double); SqlPreCommand dropForeignKeys = Synchronizer.SynchronizeScript( model, database, null, (tn, dif) => dif.Columns.Values.Select(c => c.ForeignKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, c.ForeignKey.Name) : null) .Concat(dif.MultiForeignKeys.Select(fk => SqlBuilder.AlterTableDropConstraint(dif.Name, fk.Name))).Combine(Spacing.Simple), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, null, (cn, colDb) => colDb.ForeignKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeignKey.Name) : null, (cn, colModel, colDb) => colDb.ForeignKey == null ? null : colModel.ReferenceTable == null || colModel.AvoidForeignKey || !colModel.ReferenceTable.Name.Equals(ChangeName(colDb.ForeignKey.TargetTable)) ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeignKey.Name) : null, Spacing.Simple), dif.MultiForeignKeys.Select(fk => SqlBuilder.AlterTableDropConstraint(dif.Name, fk.Name)).Combine(Spacing.Simple)), Spacing.Double); SqlPreCommand tables = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.CreateTableSql(tab), (tn, dif) => SqlBuilder.DropTable(dif.Name), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, !object.Equals(dif.Name, tab.Name) ? SqlBuilder.RenameOrMove(dif, tab) : null, Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, (cn, tabCol) => SqlPreCommandSimple.Combine(Spacing.Simple, tabCol.PrimaryKey && dif.PrimaryKeyName != null ? SqlBuilder.DropPrimaryKeyConstraint(tab.Name) : null, AlterTableAddColumnDefault(tab, tabCol, replacements)), (cn, difCol) => SqlPreCommandSimple.Combine(Spacing.Simple, difCol.Default != null ? SqlBuilder.DropDefaultConstraint(tab.Name, difCol.Name) : null, SqlBuilder.AlterTableDropColumn(tab, cn)), (cn, tabCol, difCol) => SqlPreCommand.Combine(Spacing.Simple, difCol.Name == tabCol.Name ? null : SqlBuilder.RenameColumn(tab, difCol.Name, tabCol.Name), difCol.ColumnEquals(tabCol, ignorePrimaryKey: true) ? null : SqlPreCommand.Combine(Spacing.Simple, tabCol.PrimaryKey && !difCol.PrimaryKey && dif.PrimaryKeyName != null ? SqlBuilder.DropPrimaryKeyConstraint(tab.Name) : null, SqlBuilder.AlterTableAlterColumn(tab, tabCol), tabCol.SqlDbType == SqlDbType.NVarChar && difCol.SqlDbType == SqlDbType.NChar ? SqlBuilder.UpdateTrim(tab, tabCol) : null), difCol.DefaultEquals(tabCol) ? null : SqlPreCommand.Combine(Spacing.Simple, difCol.Default != null ? SqlBuilder.DropDefaultConstraint(tab.Name, tabCol.Name) : null, tabCol.Default != null ? SqlBuilder.AddDefaultConstraint(tab.Name, tabCol.Name, tabCol.Default) : null), UpdateByFkChange(tn, difCol, tabCol, ChangeName)), Spacing.Simple)), Spacing.Double); if (tables != null) { tables.GoAfter = true; } var tableReplacements = replacements.TryGetC(Replacements.KeyTables); if (tableReplacements != null) { replacements[Replacements.KeyTablesInverse] = tableReplacements.Inverse(); } SqlPreCommand syncEnums; try { syncEnums = SynchronizeEnumsScript(replacements); } catch (Exception e) { syncEnums = new SqlPreCommandSimple("-- Exception synchronizing enums: " + e.Message); } SqlPreCommand addForeingKeys = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.AlterTableForeignKeys(tab), null, (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, (cn, colModel) => colModel.ReferenceTable == null || colModel.AvoidForeignKey ? null : SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable), null, (cn, colModel, coldb) => { if (colModel.ReferenceTable == null || colModel.AvoidForeignKey) { return(null); } if (coldb.ForeignKey == null || !colModel.ReferenceTable.Name.Equals(ChangeName(coldb.ForeignKey.TargetTable))) { return(SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable)); } var name = SqlBuilder.ForeignKeyName(tab.Name.Name, colModel.Name); return(SqlPreCommand.Combine(Spacing.Simple, name != coldb.ForeignKey.Name.Name ? SqlBuilder.RenameForeignKey(coldb.ForeignKey.Name, name) : null, (coldb.ForeignKey.IsDisabled || coldb.ForeignKey.IsNotTrusted) && !replacements.SchemaOnly ? SqlBuilder.EnableForeignKey(tab.Name, name) : null)); }, Spacing.Simple), Spacing.Double); bool?createMissingFreeIndexes = null; SqlPreCommand addIndices = Synchronizer.SynchronizeScript(model, database, (tn, tab) => modelIndices[tab].Values.Where(a => !(a is PrimaryClusteredIndex)).Select(SqlBuilder.CreateIndex).Combine(Spacing.Simple), null, (tn, tab, dif) => { var columnReplacements = replacements.TryGetC(Replacements.KeyColumnsForTable(tn)); Func <IColumn, bool> isNew = c => !dif.Columns.ContainsKey(columnReplacements?.TryGetC(c.Name) ?? c.Name); Dictionary <string, Index> modelIxs = modelIndices[tab]; var controlledIndexes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, (i, mix) => mix is UniqueIndex || mix.Columns.Any(isNew) || SafeConsole.Ask(ref createMissingFreeIndexes, "Create missing non-unique index {0} in {1}?".FormatWith(mix.IndexName, tab.Name)) ? SqlBuilder.CreateIndex(mix) : null, null, (i, mix, dix) => !dix.IndexEquals(dif, mix) ? SqlBuilder.CreateIndex(mix) : mix.IndexName != dix.IndexName ? SqlBuilder.RenameIndex(tab, dix.IndexName, mix.IndexName) : null, Spacing.Simple); return(SqlPreCommand.Combine(Spacing.Simple, controlledIndexes)); }, Spacing.Double); SqlPreCommand dropSchemas = Synchronizer.SynchronizeScriptReplacing(replacements, "Schemas", modelSchemas.ToDictionary(a => a.ToString()), databaseSchemas.ToDictionary(a => a.ToString()), null, (_, oldSN) => DropSchema(oldSN) ? SqlBuilder.DropSchema(oldSN) : null, (_, newSN, oldSN) => newSN.Equals(oldSN) ? null : SqlBuilder.DropSchema(oldSN), Spacing.Double); return(SqlPreCommand.Combine(Spacing.Triple, createSchemas, dropStatistics, dropIndices, dropForeignKeys, tables, syncEnums, addForeingKeys, addIndices, dropSchemas)); } }
public static SqlPreCommand SynchronizeTablesScript(Replacements replacements) { Schema s = Schema.Current; Dictionary <string, ITable> model = s.GetDatabaseTables().Where(t => !s.IsExternalDatabase(t.Name.Schema.Database)).ToDictionaryEx(a => a.Name.ToString(), "schema tables"); HashSet <SchemaName> modelSchemas = model.Values.Select(a => a.Name.Schema).Where(a => !SqlBuilder.SystemSchemas.Contains(a.Name)).ToHashSet(); Dictionary <string, DiffTable> database = DefaultGetDatabaseDescription(s.DatabaseNames()); HashSet <SchemaName> databaseSchemas = DefaultGetSchemas(s.DatabaseNames()); SimplifyDiffTables?.Invoke(database); replacements.AskForReplacements(database.Keys.ToHashSet(), model.Keys.ToHashSet(), Replacements.KeyTables); database = replacements.ApplyReplacementsToOld(database, Replacements.KeyTables); Dictionary <ITable, Dictionary <string, Index> > modelIndices = model.Values .ToDictionary(t => t, t => t.GeneratAllIndexes().ToDictionaryEx(a => a.IndexName, "Indexes for {0}".FormatWith(t.Name))); //To --> From Dictionary <ObjectName, ObjectName> copyDataFrom = new Dictionary <ObjectName, ObjectName>(); //A -> A_temp Dictionary <ObjectName, ObjectName> preRenames = new Dictionary <ObjectName, ObjectName>(); model.JoinDictionaryForeach(database, (tn, tab, diff) => { var key = Replacements.KeyColumnsForTable(tn); replacements.AskForReplacements(diff.Columns.Keys.ToHashSet(), tab.Columns.Keys.ToHashSet(), key); diff.Columns = replacements.ApplyReplacementsToOld(diff.Columns, key); diff.Indices = ApplyIndexAutoReplacements(diff, tab, modelIndices[tab]); var diffPk = diff.Columns.TryGetC(tab.PrimaryKey.Name); if (diffPk != null && tab.PrimaryKey.Identity != diffPk.Identity) { if (tab.Name.Equals(diff.Name)) { var tempName = new ObjectName(diff.Name.Schema, diff.Name.Name + "_old"); preRenames.Add(diff.Name, tempName); copyDataFrom.Add(tab.Name, tempName); if (replacements.Interactive) { SafeConsole.WriteLineColor(ConsoleColor.Yellow, $@"Column {diffPk.Name} in {diff.Name} is now Identity={tab.PrimaryKey.Identity}."); Console.WriteLine($@"Changing a Primary Key is not supported by SQL Server so the script will...: 1. Rename {diff.Name} table to {tempName} 2. Create a new table {diff.Name} 3. Copy data from {tempName} to {tab.Name}. 4. Drop {tempName} "); } } else { copyDataFrom.Add(tab.Name, diff.Name); if (replacements.Interactive) { SafeConsole.WriteLineColor(ConsoleColor.Yellow, $@"Column {diffPk.Name} in {diff.Name} is now Identity={tab.PrimaryKey.Identity}."); Console.WriteLine($@"Changing a Primary Key is not supported by SQL Server so the script will...: 1. Create a new table {tab.Name} 2. Copy data from {diff.Name} to {tab.Name}. 3. Drop {diff.Name} "); } } } }); var columnsByFKTarget = database.Values.SelectMany(a => a.Columns.Values).Where(a => a.ForeignKey != null).GroupToDictionary(a => a.ForeignKey.TargetTable); foreach (var pr in preRenames) { var diff = database[pr.Key.ToString()]; diff.Name = pr.Value; foreach (var col in columnsByFKTarget.TryGetC(pr.Key).EmptyIfNull()) { col.ForeignKey.TargetTable = pr.Value; } database.Add(pr.Value.ToString(), diff); database.Remove(pr.Key.ToString()); } Func <ObjectName, ObjectName> ChangeName = (ObjectName objectName) => { string name = replacements.Apply(Replacements.KeyTables, objectName.ToString()); return(model.TryGetC(name)?.Name ?? objectName); }; Func <ObjectName, SqlPreCommand> DeleteAllForeignKey = tableName => { var dropFks = (from t in database.Values from c in t.Columns.Values where c.ForeignKey != null && c.ForeignKey.TargetTable.Equals(tableName) select SqlBuilder.AlterTableDropConstraint(t.Name, c.ForeignKey.Name)).Combine(Spacing.Simple); if (dropFks == null) { return(null); } return(SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("---In order to remove the PK of " + tableName.Name), dropFks)); }; using (replacements.WithReplacedDatabaseName()) { SqlPreCommand preRenameTables = preRenames.Select(a => SqlBuilder.RenameTable(a.Key, a.Value.Name)).Combine(Spacing.Double); if (preRenameTables != null) { preRenameTables.GoAfter = true; } SqlPreCommand createSchemas = Synchronizer.SynchronizeScriptReplacing(replacements, "Schemas", modelSchemas.ToDictionary(a => a.ToString()), databaseSchemas.ToDictionary(a => a.ToString()), (_, newSN) => SqlBuilder.CreateSchema(newSN), null, (_, newSN, oldSN) => newSN.Equals(oldSN) ? null : SqlBuilder.CreateSchema(newSN), Spacing.Double); //use database without replacements to just remove indexes SqlPreCommand dropStatistics = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => SqlBuilder.DropStatistics(tn, dif.Stats), (tn, tab, dif) => { var removedColums = dif.Columns.Keys.Except(tab.Columns.Keys).ToHashSet(); return(SqlBuilder.DropStatistics(tn, dif.Stats.Where(a => a.Columns.Any(removedColums.Contains)).ToList())); }, Spacing.Double); SqlPreCommand dropIndices = Synchronizer.SynchronizeScript(model, database, null, (tn, dif) => dif.Indices.Values.Where(ix => !ix.IsPrimary).Select(ix => SqlBuilder.DropIndex(dif.Name, ix)).Combine(Spacing.Simple), (tn, tab, dif) => { Dictionary <string, Index> modelIxs = modelIndices[tab]; var removedColums = dif.Columns.Keys.Except(tab.Columns.Keys).ToHashSet(); var changes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, null, (i, dix) => dix.Columns.Any(removedColums.Contains) || dix.IsControlledIndex ? SqlBuilder.DropIndex(dif.Name, dix) : null, (i, mix, dix) => !dix.IndexEquals(dif, mix) ? SqlPreCommand.Combine(Spacing.Double, dix.IsPrimary ? DeleteAllForeignKey(dif.Name) : null, SqlBuilder.DropIndex(dif.Name, dix)) : null, Spacing.Simple); return(changes); }, Spacing.Double); SqlPreCommand dropForeignKeys = Synchronizer.SynchronizeScript( model, database, null, (tn, dif) => dif.Columns.Values.Select(c => c.ForeignKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, c.ForeignKey.Name) : null) .Concat(dif.MultiForeignKeys.Select(fk => SqlBuilder.AlterTableDropConstraint(dif.Name, fk.Name))).Combine(Spacing.Simple), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, null, (cn, colDb) => colDb.ForeignKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeignKey.Name) : null, (cn, colModel, colDb) => colDb.ForeignKey == null ? null : colModel.ReferenceTable == null || colModel.AvoidForeignKey || !colModel.ReferenceTable.Name.Equals(ChangeName(colDb.ForeignKey.TargetTable)) ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeignKey.Name) : null, Spacing.Simple), dif.MultiForeignKeys.Select(fk => SqlBuilder.AlterTableDropConstraint(dif.Name, fk.Name)).Combine(Spacing.Simple)), Spacing.Double); SqlPreCommand preRenamePks = preRenames.Select(a => SqlBuilder.DropPrimaryKeyConstraint(a.Value)).Combine(Spacing.Double); SqlPreCommand tables = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlPreCommand.Combine(Spacing.Double, SqlBuilder.CreateTableSql(tab), copyDataFrom.ContainsKey(tab.Name) ? CopyData(tab, database.GetOrThrow(copyDataFrom.GetOrThrow(tab.Name).ToString()), replacements).Do(a => a.GoBefore = true) : null ), (tn, dif) => SqlBuilder.DropTable(dif.Name), (tn, tab, dif) => SqlPreCommand.Combine(Spacing.Simple, !object.Equals(dif.Name, tab.Name) ? SqlBuilder.RenameOrMove(dif, tab) : null, Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, (cn, tabCol) => SqlPreCommand.Combine(Spacing.Simple, tabCol.PrimaryKey && dif.PrimaryKeyName != null ? SqlBuilder.DropPrimaryKeyConstraint(tab.Name) : null, AlterTableAddColumnDefault(tab, tabCol, replacements)), (cn, difCol) => SqlPreCommand.Combine(Spacing.Simple, difCol.Default != null ? SqlBuilder.DropDefaultConstraint(tab.Name, difCol.Name) : null, SqlBuilder.AlterTableDropColumn(tab, cn)), (cn, tabCol, difCol) => SqlPreCommand.Combine(Spacing.Simple, difCol.Name == tabCol.Name ? null : SqlBuilder.RenameColumn(tab, difCol.Name, tabCol.Name), difCol.ColumnEquals(tabCol, ignorePrimaryKey: true) ? null : SqlPreCommand.Combine(Spacing.Simple, tabCol.PrimaryKey && !difCol.PrimaryKey && dif.PrimaryKeyName != null ? SqlBuilder.DropPrimaryKeyConstraint(tab.Name) : null, SqlBuilder.AlterTableAlterColumn(tab, tabCol), tabCol.SqlDbType == SqlDbType.NVarChar && difCol.SqlDbType == SqlDbType.NChar ? SqlBuilder.UpdateTrim(tab, tabCol) : null), difCol.DefaultEquals(tabCol) ? null : SqlPreCommand.Combine(Spacing.Simple, difCol.Default != null ? SqlBuilder.DropDefaultConstraint(tab.Name, tabCol.Name) : null, tabCol.Default != null ? SqlBuilder.AddDefaultConstraint(tab.Name, tabCol.Name, tabCol.Default, tabCol.SqlDbType) : null), UpdateByFkChange(tn, difCol, tabCol, ChangeName, copyDataFrom)), Spacing.Simple)), Spacing.Double); if (tables != null) { tables.GoAfter = true; } var tableReplacements = replacements.TryGetC(Replacements.KeyTables); if (tableReplacements != null) { replacements[Replacements.KeyTablesInverse] = tableReplacements.Inverse(); } SqlPreCommand syncEnums; try { syncEnums = SynchronizeEnumsScript(replacements); } catch (Exception e) { syncEnums = new SqlPreCommandSimple("-- Exception synchronizing enums: " + e.Message); } SqlPreCommand addForeingKeys = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.AlterTableForeignKeys(tab), null, (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Columns, (cn, colModel) => colModel.ReferenceTable == null || colModel.AvoidForeignKey ? null : SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable), null, (cn, colModel, coldb) => { if (colModel.ReferenceTable == null || colModel.AvoidForeignKey) { return(null); } if (coldb.ForeignKey == null || !colModel.ReferenceTable.Name.Equals(ChangeName(coldb.ForeignKey.TargetTable))) { return(SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable)); } var name = SqlBuilder.ForeignKeyName(tab.Name.Name, colModel.Name); return(SqlPreCommand.Combine(Spacing.Simple, name != coldb.ForeignKey.Name.Name ? SqlBuilder.RenameForeignKey(coldb.ForeignKey.Name, name) : null, (coldb.ForeignKey.IsDisabled || coldb.ForeignKey.IsNotTrusted) && !replacements.SchemaOnly ? SqlBuilder.EnableForeignKey(tab.Name, name) : null)); }, Spacing.Simple), Spacing.Double); bool?createMissingFreeIndexes = null; SqlPreCommand addIndices = Synchronizer.SynchronizeScript(model, database, (tn, tab) => modelIndices[tab].Values.Where(a => !(a is PrimaryClusteredIndex)).Select(SqlBuilder.CreateIndex).Combine(Spacing.Simple), null, (tn, tab, dif) => { var columnReplacements = replacements.TryGetC(Replacements.KeyColumnsForTable(tn)); Func <IColumn, bool> isNew = c => !dif.Columns.ContainsKey(columnReplacements?.TryGetC(c.Name) ?? c.Name); Dictionary <string, Index> modelIxs = modelIndices[tab]; var controlledIndexes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, (i, mix) => mix is UniqueIndex || mix.Columns.Any(isNew) || SafeConsole.Ask(ref createMissingFreeIndexes, "Create missing non-unique index {0} in {1}?".FormatWith(mix.IndexName, tab.Name)) ? SqlBuilder.CreateIndex(mix) : null, null, (i, mix, dix) => !dix.IndexEquals(dif, mix) ? SqlBuilder.CreateIndex(mix) : mix.IndexName != dix.IndexName ? SqlBuilder.RenameIndex(tab, dix.IndexName, mix.IndexName) : null, Spacing.Simple); return(SqlPreCommand.Combine(Spacing.Simple, controlledIndexes)); }, Spacing.Double); SqlPreCommand dropSchemas = Synchronizer.SynchronizeScriptReplacing(replacements, "Schemas", modelSchemas.ToDictionary(a => a.ToString()), databaseSchemas.ToDictionary(a => a.ToString()), null, (_, oldSN) => DropSchema(oldSN) ? SqlBuilder.DropSchema(oldSN) : null, (_, newSN, oldSN) => newSN.Equals(oldSN) ? null : SqlBuilder.DropSchema(oldSN), Spacing.Double); return(SqlPreCommand.Combine(Spacing.Triple, preRenameTables, createSchemas, dropStatistics, dropIndices, dropForeignKeys, preRenamePks, tables, syncEnums, addForeingKeys, addIndices, dropSchemas)); } }