internal SqlPreCommand?ImportXml(XElement element, Dictionary <string, Lite <RoleEntity> > roles, Replacements replacements) { var current = Database.RetrieveAll <RuleTypeEntity>().GroupToDictionary(a => a.Role); var xRoles = (element.Element("Types")?.Elements("Role")).EmptyIfNull(); var should = xRoles.ToDictionary(x => roles.GetOrThrow(x.Attribute("Name").Value)); Table table = Schema.Current.Table(typeof(RuleTypeEntity)); replacements.AskForReplacements( xRoles.SelectMany(x => x.Elements("Type")).Select(x => x.Attribute("Resource").Value).ToHashSet(), TypeLogic.NameToType.Where(a => !a.Value.IsEnumEntity()).Select(a => a.Key).ToHashSet(), typeReplacementKey); replacements.AskForReplacements( xRoles.SelectMany(x => x.Elements("Type")).SelectMany(t => t.Elements("Condition")).Select(x => x.Attribute("Name").Value).ToHashSet(), SymbolLogic <TypeConditionSymbol> .AllUniqueKeys(), typeConditionReplacementKey); Func <string, TypeEntity?> getResource = s => { Type?type = TypeLogic.NameToType.TryGetC(replacements.Apply(typeReplacementKey, s)); if (type == null) { return(null); } return(TypeLogic.TypeToEntity.GetOrThrow(type)); }; return(Synchronizer.SynchronizeScript(Spacing.Double, should, current, createNew: (role, x) => { var dic = (from xr in x.Elements("Type") let t = getResource(xr.Attribute("Resource").Value) where t != null select KVP.Create(t, new { Allowed = xr.Attribute("Allowed").Value.ToEnum <TypeAllowed>(), Condition = Conditions(xr, replacements) })).ToDictionaryEx("Type rules for {0}".FormatWith(role)); SqlPreCommand?restSql = dic.Select(kvp => table.InsertSqlSync(new RuleTypeEntity { Resource = kvp.Key, Role = role, Allowed = kvp.Value.Allowed, Conditions = kvp.Value.Condition ! /*CSBUG*/ }, comment: Comment(role, kvp.Key, kvp.Value.Allowed))).Combine(Spacing.Simple)?.Do(p => p.GoBefore = true);
static SqlPreCommand?SynchronizeNamespace(Replacements replacements, SyncData data) { var entities = Database.Query <NamespaceHelpEntity>().ToList(); if (entities.IsEmpty()) { return(null); } var current = entities.Select(a => a.Name).ToHashSet(); replacements.AskForReplacements(current, data.Namespaces, "namespaces"); var table = Schema.Current.Table <NamespaceHelpEntity>(); using (replacements.WithReplacedDatabaseName()) return(entities.Select(e => { e.Name = replacements.TryGetC("namespaces")?.TryGetC(e.Name) ?? e.Name; if (!data.Namespaces.Contains(e.Name)) { return table.DeleteSqlSync(e, n => n.Name == e.Name); } e.Description = SynchronizeContent(e.Description, replacements, data); return table.UpdateSqlSync(e, n => n.Name == e.Name); }).Combine(Spacing.Simple)); }
static SqlPreCommand?Schema_Synchronizing(Replacements replacements) { Table table = Schema.Current.Table <EmailModelEntity>(); Dictionary <string, EmailModelEntity> should = GenerateEmailModelEntities().ToDictionary(s => s.FullClassName); Dictionary <string, EmailModelEntity> old = Administrator.TryRetrieveAll <EmailModelEntity>(replacements).ToDictionary(c => c.FullClassName); replacements.AskForReplacements( old.Keys.ToHashSet(), should.Keys.ToHashSet(), EmailModelReplacementKey); Dictionary <string, EmailModelEntity> current = replacements.ApplyReplacementsToOld(old, EmailModelReplacementKey); using (replacements.WithReplacedDatabaseName()) return(Synchronizer.SynchronizeScript(Spacing.Double, should, current, createNew: (tn, s) => table.InsertSqlSync(s), removeOld: (tn, c) => table.DeleteSqlSync(c, se => se.FullClassName == c.FullClassName), mergeBoth: (tn, s, c) => { var oldClassName = c.FullClassName; c.FullClassName = s.FullClassName; return table.UpdateSqlSync(c, se => se.FullClassName == oldClassName); })); }
internal static void SynchronizeElements <T>(XElement loaded, XName collectionElementName, XName elementName, XName elementKeyAttribute, Dictionary <string, T> should, string replacementsKey, Action <T, XElement> update, Action <SyncAction, string> notify) where T : class { Replacements replacements = new Replacements(); var collection = loaded.Element(collectionElementName); if (collection == null) { return; } Dictionary <string, XElement> loadedDictionary = collection.Elements(elementName).ToDictionary(a => a.Attribute(elementKeyAttribute).Value); if (loadedDictionary.IsEmpty()) { return; } replacements.AskForReplacements( loadedDictionary.Keys.ToHashSet(), should.Keys.ToHashSet(), replacementsKey); var reps = replacements.TryGetC(replacementsKey); foreach (var kvp in loadedDictionary) { var key = kvp.Value.Attribute(elementKeyAttribute).Value; var newKey = reps?.TryGetC(key); if (newKey != null) { notify(SyncAction.Renamed, newKey); key = newKey; } T?val = should.TryGetC(key); if (val == null) { notify(SyncAction.Removed, key); } else { update(val, kvp.Value); } } }
public static void SynchronizeReplacing <N, O>( Replacements replacements, string replacementsKey, Dictionary <string, N> newDictionary, Dictionary <string, O> oldDictionary, Action <string, N>?createNew, Action <string, O>?removeOld, Action <string, N, O>?merge) { replacements.AskForReplacements( oldDictionary.Keys.ToHashSet(), newDictionary.Keys.ToHashSet(), replacementsKey); var repOldDictionary = replacements.ApplyReplacementsToOld(oldDictionary, replacementsKey); HashSet <string> set = new HashSet <string>(); set.UnionWith(repOldDictionary.Keys); set.UnionWith(newDictionary.Keys); foreach (var key in set) { if (repOldDictionary.TryGetValue(key, out var oldVal)) { if (newDictionary.TryGetValue(key, out var newVal)) { merge?.Invoke(key, newVal, oldVal); } else { removeOld?.Invoke(key, oldVal); } } else { if (newDictionary.TryGetValue(key, out var newVal)) { createNew?.Invoke(key, newVal); } else { throw new InvalidOperationException("Unexpected key: " + key); } } } }
public static SqlPreCommand?SynchronizeScriptReplacing <N, O>( Replacements replacements, string replacementsKey, Spacing spacing, Dictionary <string, N> newDictionary, Dictionary <string, O> oldDictionary, Func <string, N, SqlPreCommand?>?createNew, Func <string, O, SqlPreCommand?>?removeOld, Func <string, N, O, SqlPreCommand?>?mergeBoth) where O : class where N : class { replacements.AskForReplacements( oldDictionary.Keys.ToHashSet(), newDictionary.Keys.ToHashSet(), replacementsKey); var repOldDictionary = replacements.ApplyReplacementsToOld(oldDictionary, replacementsKey); return(SynchronizeScript(spacing, newDictionary, repOldDictionary, createNew, removeOld, mergeBoth)); }
private static Dictionary <string, string> AskForReplacementsWithMemory(HashSet <string> newNames, HashSet <string> oldNames, Dictionary <string, string?> memory, string replacementKey) { Dictionary <string, string> result = new Dictionary <string, string>(); foreach (var kvp in memory) { if (oldNames.Contains(kvp.Key) && kvp.Value == null) { oldNames.Remove(kvp.Key); } else if (oldNames.Contains(kvp.Key) && newNames.Contains(kvp.Value !)) { oldNames.Remove(kvp.Key); newNames.Remove(kvp.Value !); result.Add(kvp.Key, kvp.Value !); } } Replacements rep = new Replacements(); rep.AskForReplacements(oldNames, newNames, replacementKey); var answers = rep.TryGetC(replacementKey); if (answers != null) { result.AddRange(answers); memory !.SetRange(answers); } var toDelete = oldNames.Except(newNames); if (answers != null) { toDelete = toDelete.Except(answers.Keys); } memory.SetRange(toDelete.Select(n => KeyValuePair.Create(n, (string?)null))); return(result); }
public static void SynchronizeReplacing <O, N>( Replacements replacements, string replacementsKey, Dictionary <string, O> oldDictionary, Dictionary <string, N> newDictionary, Action <string, O> removeOld, Action <string, N> createNew, Action <string, O, N> merge) where O : class where N : class { replacements.AskForReplacements( oldDictionary.Keys.ToHashSet(), newDictionary.Keys.ToHashSet(), replacementsKey); var repOldDictionary = replacements.ApplyReplacementsToOld(oldDictionary, replacementsKey); HashSet <string> set = new HashSet <string>(); set.UnionWith(repOldDictionary.Keys); set.UnionWith(newDictionary.Keys); foreach (var key in set) { var oldVal = repOldDictionary.TryGetC(key); var newVal = newDictionary.TryGetC(key); if (oldVal == null) { createNew(key, newVal !); } else if (newVal == null) { removeOld(key, oldVal); } else { merge(key, oldVal, newVal); } } }
internal SqlPreCommand ImportXml(XElement element, Dictionary <string, Lite <RoleEntity> > roles, Replacements replacements) { var current = Database.RetrieveAll <RuleTypeEntity>().GroupToDictionary(a => a.Role); var xRoles = (element.Element("Types")?.Elements("Role")).EmptyIfNull(); var should = xRoles.ToDictionary(x => roles[x.Attribute("Name").Value]); Table table = Schema.Current.Table(typeof(RuleTypeEntity)); replacements.AskForReplacements( xRoles.SelectMany(x => x.Elements("Type")).Select(x => x.Attribute("Resource").Value).ToHashSet(), TypeLogic.NameToType.Where(a => !a.Value.IsEnumEntity()).Select(a => a.Key).ToHashSet(), typeReplacementKey); replacements.AskForReplacements( xRoles.SelectMany(x => x.Elements("Type")).SelectMany(t => t.Elements("Condition")).Select(x => x.Attribute("Name").Value).ToHashSet(), SymbolLogic <TypeConditionSymbol> .AllUniqueKeys(), typeConditionReplacementKey); Func <string, TypeEntity> getResource = s => { Type type = TypeLogic.NameToType.TryGetC(replacements.Apply(typeReplacementKey, s)); if (type == null) { return(null); } return(TypeLogic.TypeToEntity[type]); }; return(Synchronizer.SynchronizeScript(Spacing.Double, should, current, createNew: (role, x) => { var dic = (from xr in x.Elements("Type") let t = getResource(xr.Attribute("Resource").Value) where t != null select KVP.Create(t, new { Allowed = xr.Attribute("Allowed").Value.ToEnum <TypeAllowed>(), Condition = Conditions(xr, replacements) })).ToDictionaryEx("Type rules for {0}".FormatWith(role)); SqlPreCommand restSql = dic.Select(kvp => table.InsertSqlSync(new RuleTypeEntity { Resource = kvp.Key, Role = role, Allowed = kvp.Value.Allowed, Conditions = kvp.Value.Condition }, comment: Comment(role, kvp.Key, kvp.Value.Allowed))).Combine(Spacing.Simple)?.Do(p => p.GoBefore = true); return restSql; }, removeOld: (role, list) => list.Select(rt => table.DeleteSqlSync(rt)).Combine(Spacing.Simple)?.Do(p => p.GoBefore = true), mergeBoth: (role, x, list) => { var dic = (from xr in x.Elements("Type") let t = getResource(xr.Attribute("Resource").Value) where t != null && !t.ToType().IsEnumEntity() select KVP.Create(t, xr)).ToDictionaryEx("Type rules for {0}".FormatWith(role)); SqlPreCommand restSql = Synchronizer.SynchronizeScript( Spacing.Simple, dic, list.Where(a => a.Resource != null).ToDictionary(a => a.Resource), createNew: (r, xr) => { var a = xr.Attribute("Allowed").Value.ToEnum <TypeAllowed>(); var conditions = Conditions(xr, replacements); return table.InsertSqlSync(new RuleTypeEntity { Resource = r, Role = role, Allowed = a, Conditions = conditions }, comment: Comment(role, r, a)); }, removeOld: (r, rt) => table.DeleteSqlSync(rt, Comment(role, r, rt.Allowed)), mergeBoth: (r, xr, pr) => { var oldA = pr.Allowed; pr.Allowed = xr.Attribute("Allowed").Value.ToEnum <TypeAllowed>(); var conditions = Conditions(xr, replacements); if (!pr.Conditions.SequenceEqual(conditions)) { pr.Conditions = conditions; } return table.UpdateSqlSync(pr, comment: Comment(role, r, oldA, pr.Allowed)); })?.Do(p => p.GoBefore = true); return restSql; })); }
public static SqlPreCommand?ImportRulesScript(XDocument doc, bool interactive) { Replacements replacements = new Replacements { Interactive = interactive }; Dictionary <string, Lite <RoleEntity> > rolesDic = roles.Value.ToDictionary(a => a.ToString()); Dictionary <string, XElement> rolesXml = doc.Root.Element("Roles").Elements("Role").ToDictionary(x => x.Attribute("Name").Value); replacements.AskForReplacements(rolesXml.Keys.ToHashSet(), rolesDic.Keys.ToHashSet(), "Roles"); rolesDic = replacements.ApplyReplacementsToNew(rolesDic, "Roles"); try { var xmlOnly = rolesXml.Keys.Except(rolesDic.Keys).ToList(); if (xmlOnly.Any()) { throw new InvalidOperationException("roles {0} not found on the database".FormatWith(xmlOnly.ToString(", "))); } foreach (var kvp in rolesXml) { var r = rolesDic[kvp.Key]; var current = GetMergeStrategy(r); var should = kvp.Value.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum <MergeStrategy>()) ?? MergeStrategy.Union; if (current != should) { throw new InvalidOperationException("Merge strategy of {0} is {1} in the database but is {2} in the file".FormatWith(r, current, should)); } EnumerableExtensions.JoinStrict( roles.Value.RelatedTo(r), kvp.Value.Attribute("Contains").Value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries), sr => sr.ToString(), s => rolesDic[s].ToString(), (sr, s) => 0, "subRoles of {0}".FormatWith(r)); } } catch (InvalidOperationException ex) { throw new InvalidRoleGraphException("The role graph does not match:\r\n" + ex.Message); } var dbOnlyWarnings = rolesDic.Keys.Except(rolesXml.Keys).Select(n => new SqlPreCommandSimple("-- Alien role {0} not configured!!".FormatWith(n)) ).Combine(Spacing.Simple); SqlPreCommand?result = ImportFromXml.GetInvocationListTyped() .Select(inv => inv(doc.Root, rolesDic, replacements)).Combine(Spacing.Triple); if (replacements.Values.Any(a => a.Any())) { SafeConsole.WriteLineColor(ConsoleColor.Red, "There are renames! Remember to export after executing the script"); } if (result == null && dbOnlyWarnings == null) { return(null); } return(SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN AUTH SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), dbOnlyWarnings, result, new SqlPreCommandSimple("-- END AUTH SYNC SCRIPT"))); }
public static void SynchronizeRoles(XDocument doc) { Table table = Schema.Current.Table(typeof(RoleEntity)); TableMList relationalTable = table.TablesMList().Single(); Dictionary <string, XElement> rolesXml = doc.Root.Element("Roles").Elements("Role").ToDictionary(x => x.Attribute("Name").Value); { Dictionary <string, RoleEntity> rolesDic = Database.Query <RoleEntity>().ToDictionary(a => a.ToString()); Replacements replacements = new Replacements(); replacements.AskForReplacements(rolesDic.Keys.ToHashSet(), rolesXml.Keys.ToHashSet(), "Roles"); rolesDic = replacements.ApplyReplacementsToOld(rolesDic, "Roles"); Console.WriteLine("Part 1: Syncronize roles without relationships"); var roleInsertsDeletes = Synchronizer.SynchronizeScript(Spacing.Double, rolesXml, rolesDic, createNew: (name, xelement) => table.InsertSqlSync(new RoleEntity { Name = name }, includeCollections: false), removeOld: (name, role) => SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("DELETE {0} WHERE {1} = {2} --{3}" .FormatWith(relationalTable.Name, ((IColumn)relationalTable.Field).Name.SqlEscape(), role.Id, role.Name)), table.DeleteSqlSync(role)), mergeBoth: (name, xElement, role) => { var oldName = role.Name; role.Name = name; role.MergeStrategy = xElement.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum <MergeStrategy>()) ?? MergeStrategy.Union; return(table.UpdateSqlSync(role, includeCollections: false, comment: oldName)); }); if (roleInsertsDeletes != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleInsertsDeletes, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } { Console.WriteLine("Part 2: Syncronize roles relationships"); Dictionary <string, RoleEntity> rolesDic = Database.Query <RoleEntity>().ToDictionary(a => a.ToString()); var roleRelationships = Synchronizer.SynchronizeScript(Spacing.Double, rolesXml, rolesDic, createNew: (name, xelement) => { throw new InvalidOperationException("No new roles should be at this stage. Did you execute the script?"); }, removeOld: (name, role) => { throw new InvalidOperationException("No old roles should be at this stage. Did you execute the script?"); }, mergeBoth: (name, xElement, role) => { var should = xElement.Attribute("Contains").Value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); var current = role.Roles.Select(a => a.ToString()); if (should.OrderBy().SequenceEqual(current.OrderBy())) { return(null); } role.Roles = should.Select(rs => rolesDic.GetOrThrow(rs).ToLite()).ToMList(); return(table.UpdateSqlSync(role)); }); if (roleRelationships != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleRelationships, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } }
public static SqlPreCommand ImportRulesScript(XDocument doc, bool interactive) { Replacements replacements = new Replacements { Interactive = interactive }; Dictionary<string, Lite<RoleEntity>> rolesDic = roles.Value.ToDictionary(a => a.ToString()); Dictionary<string, XElement> rolesXml = doc.Root.Element("Roles").Elements("Role").ToDictionary(x => x.Attribute("Name").Value); replacements.AskForReplacements(rolesXml.Keys.ToHashSet(), rolesDic.Keys.ToHashSet(), "Roles"); rolesDic = replacements.ApplyReplacementsToNew(rolesDic, "Roles"); try { var xmlOnly = rolesXml.Keys.Except(rolesDic.Keys).ToList(); if (xmlOnly.Any()) throw new InvalidOperationException("roles {0} not found on the database".FormatWith(xmlOnly.ToString(", "))); foreach (var kvp in rolesXml) { var r = rolesDic[kvp.Key]; var current = GetMergeStrategy(r); var should = kvp.Value.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum<MergeStrategy>()) ?? MergeStrategy.Union; if (current != should) throw new InvalidOperationException("Merge strategy of {0} is {1} in the database but is {2} in the file".FormatWith(r, current, should)); EnumerableExtensions.JoinStrict( roles.Value.RelatedTo(r), kvp.Value.Attribute("Contains").Value.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries), sr => sr.ToString(), s => rolesDic[s].ToString(), (sr, s) => 0, "subRoles of {0}".FormatWith(r)); } } catch (InvalidOperationException ex) { throw new InvalidRoleGraphException("The role graph does not match:\r\n" + ex.Message); } var dbOnlyWarnings = rolesDic.Keys.Except(rolesXml.Keys).Select(n => new SqlPreCommandSimple("-- Alien role {0} not configured!!".FormatWith(n)) ).Combine(Spacing.Simple); SqlPreCommand result = ImportFromXml.GetInvocationListTyped() .Select(inv => inv(doc.Root, rolesDic, replacements)).Combine(Spacing.Triple); result = SqlPreCommand.Combine(Spacing.Triple, result, UpdateLastAuthRules(doc.Root.Element("Exported"))); if (replacements.Values.Any(a => a.Any())) SafeConsole.WriteLineColor(ConsoleColor.Red, "There are renames! Remember to export after executing the script"); if (result == null && dbOnlyWarnings == null) return null; return SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN AUTH SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), dbOnlyWarnings, result, new SqlPreCommandSimple("-- END AUTH SYNC SCRIPT")); }
public static void SynchronizeRoles(XDocument doc) { Table table = Schema.Current.Table(typeof(RoleEntity)); TableMList relationalTable = table.TablesMList().Single(); Dictionary<string, XElement> rolesXml = doc.Root.Element("Roles").Elements("Role").ToDictionary(x => x.Attribute("Name").Value); { Dictionary<string, RoleEntity> rolesDic = Database.Query<RoleEntity>().ToDictionary(a => a.ToString()); Replacements replacements = new Replacements(); replacements.AskForReplacements(rolesDic.Keys.ToHashSet(), rolesXml.Keys.ToHashSet(), "Roles"); rolesDic = replacements.ApplyReplacementsToOld(rolesDic, "Roles"); Console.WriteLine("Part 1: Syncronize roles without relationships"); var roleInsertsDeletes = Synchronizer.SynchronizeScript(rolesXml, rolesDic, (name, xelement) => table.InsertSqlSync(new RoleEntity { Name = name }, includeCollections: false), (name, role) => SqlPreCommand.Combine(Spacing.Simple, new SqlPreCommandSimple("DELETE {0} WHERE {1} = {2} --{3}" .FormatWith(relationalTable.Name, ((IColumn)relationalTable.Field).Name.SqlEscape(), role.Id, role.Name)), table.DeleteSqlSync(role)), (name, xElement, role) => { var oldName = role.Name; role.Name = name; role.MergeStrategy = xElement.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum<MergeStrategy>()) ?? MergeStrategy.Union; return table.UpdateSqlSync(role, includeCollections: false, comment: oldName); }, Spacing.Double); if (roleInsertsDeletes != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleInsertsDeletes, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } { Console.WriteLine("Part 2: Syncronize roles relationships"); Dictionary<string, RoleEntity> rolesDic = Database.Query<RoleEntity>().ToDictionary(a => a.ToString()); var roleRelationships = Synchronizer.SynchronizeScript(rolesXml, rolesDic, (name, xelement) => { throw new InvalidOperationException("No new roles should be at this stage. Did you execute the script?"); }, (name, role) => { throw new InvalidOperationException("No old roles should be at this stage. Did you execute the script?"); }, (name, xElement, role) => { var should = xElement.Attribute("Contains").Value.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries); var current = role.Roles.Select(a=>a.ToString()); if(should.OrderBy().SequenceEqual(current.OrderBy())) return null; role.Roles = should.Select(rs => rolesDic.GetOrThrow(rs).ToLite()).ToMList(); return table.UpdateSqlSync(role); }, Spacing.Double); if (roleRelationships != null) { SqlPreCommand.Combine(Spacing.Triple, new SqlPreCommandSimple("-- BEGIN ROLE SYNC SCRIPT"), new SqlPreCommandSimple("use {0}".FormatWith(Connector.Current.DatabaseName())), roleRelationships, new SqlPreCommandSimple("-- END ROLE SYNC SCRIPT")).OpenSqlFileRetry(); Console.WriteLine("Press [Enter] when executed..."); Console.ReadLine(); } else { SafeConsole.WriteLineColor(ConsoleColor.Green, "Already syncronized"); } } }
static SqlPreCommand SynchronizeEnumsScript(Replacements replacements) { Schema schema = Schema.Current; List<SqlPreCommand> commands = new List<SqlPreCommand>(); foreach (var table in schema.Tables.Values) { Type enumType = EnumEntity.Extract(table.Type); if (enumType != null) { IEnumerable<Entity> should = EnumEntity.GetEntities(enumType); Dictionary<string, Entity> shouldByName = should.ToDictionary(a => a.ToString()); List<Entity> current = Administrator.TryRetrieveAll(table.Type, replacements); Dictionary<string, Entity> currentByName = current.ToDictionaryEx(a => a.toStr, table.Name.Name); string key = Replacements.KeyEnumsForTable(table.Name.Name); replacements.AskForReplacements(currentByName.Keys.ToHashSet(), shouldByName.Keys.ToHashSet(), key); currentByName = replacements.ApplyReplacementsToOld(currentByName, key); var mix = shouldByName.JoinDictionary(currentByName, (n, s, c) => new { s, c }).Where(a => a.Value.s.id != a.Value.c.id).ToDictionary(); HashSet<PrimaryKey> usedIds = current.Select(a => a.Id).ToHashSet(); Dictionary<string, Entity> middleByName = mix.Where(kvp => usedIds.Contains(kvp.Value.s.Id)).ToDictionary(kvp => kvp.Key, kvp => Clone(kvp.Value.c)); if (middleByName.Any()) { var moveToAux = SyncEnums(schema, table, currentByName.Where(a => middleByName.ContainsKey(a.Key)).ToDictionary(), middleByName); if (moveToAux != null) commands.Add(moveToAux); } var com = SyncEnums(schema, table, currentByName.Where(a=>!middleByName.ContainsKey(a.Key)).ToDictionary(), shouldByName.Where(a=>!middleByName.ContainsKey(a.Key)).ToDictionary()); if (com != null) commands.Add(com); if (middleByName.Any()) { var backFromAux = SyncEnums(schema, table, middleByName, shouldByName.Where(a => middleByName.ContainsKey(a.Key)).ToDictionary()); if (backFromAux != null) commands.Add(backFromAux); } } } return SqlPreCommand.Combine(Spacing.Double, commands.ToArray()); }
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()); 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); } }
private static Dictionary<string, string> AskForReplacementsWithMemory(HashSet<string> newNames, HashSet<string> oldNames, Dictionary<string, string> memory, string replacementKey) { Dictionary<string, string> result = new Dictionary<string, string>(); foreach (var kvp in memory) { if (oldNames.Contains(kvp.Key) && kvp.Value == null) { oldNames.Remove(kvp.Key); } else if (oldNames.Contains(kvp.Key) && newNames.Contains(kvp.Value)) { oldNames.Remove(kvp.Key); newNames.Remove(kvp.Value); result.Add(kvp.Key, kvp.Value); } } Replacements rep = new Replacements(); rep.AskForReplacements(oldNames, newNames, replacementKey); var answers = rep.TryGetC(replacementKey); if (answers != null) { result.AddRange(answers); memory.SetRange(answers); } var toDelete = oldNames.Except(newNames); if(answers != null) toDelete = toDelete.Except(answers.Keys); memory.SetRange(toDelete.Select(n => KVP.Create(n, (string)null))); return result; }
public static SqlPreCommand SynchronizeTablesScript(Replacements replacements) { //Temproal HACK if (Database.View<SysIndexes>().Any(a => a.name.StartsWith("FIX")) && SafeConsole.Ask("Old index naming convention...rename first?")) { return Schema.Current.DatabaseNames().Select(db => { using (Administrator.OverrideDatabaseInViews(db)) { var indexes = (from s in Database.View<SysSchemas>() from t in s.Tables() from ix in t.Indices() where !ix.is_primary_key select new { schemaName = s.name, tableName = t.name, ix.is_unique, indexName = ix.name }).ToList(); return (from ix in indexes let newName = ix.is_unique ? Regex.Replace(ix.indexName, @"^IX_\w+?_", "UIX_") : Regex.Replace(ix.indexName, @"^F?IX_\w+?_", "IX_") where ix.indexName != newName select new SqlPreCommandSimple("EXEC SP_RENAME '{0}.{1}' , '{2}', 'INDEX' ".Formato( new ObjectName(new SchemaName(db, ix.schemaName), ix.tableName), ix.indexName, newName))).Combine(Spacing.Simple); } }).Combine(Spacing.Double); } Dictionary<string, ITable> model = Schema.Current.GetDatabaseTables().ToDictionary(a => a.Name.ToString(), "schema tables"); Dictionary<string, DiffTable> database = DefaultGetDatabaseDescription(Schema.Current.DatabaseNames()); 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().ToDictionary(a => a.IndexName, "Indexes for {0}".Formato(t.Name))); model.JoinDictionaryForeach(database, (tn, tab, diff) => { var key = Replacements.KeyColumnsForTable(tn); replacements.AskForReplacements(diff.Colums.Keys.ToHashSet(), tab.Columns.Keys.ToHashSet(), key); diff.Colums = replacements.ApplyReplacementsToOld(diff.Colums, 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).Try(a => a.Name) ?? objectName; }; Func<DiffTable, DiffIndex, Index, bool> columnsChanged = (dif, dix, mix) => { if (dix.Columns.Count != mix.Columns.Length) return true; var dixColumns = dif.Colums.Where(kvp => dix.Columns.Contains(kvp.Value.Name)); return !dixColumns.All(kvp => dif.Colums.GetOrThrow(kvp.Key).ColumnEquals(mix.Columns.SingleEx(c => c.Name == kvp.Key))); }; //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.Colums.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.Select(ix => SqlBuilder.DropIndex(dif.Name, ix)).Combine(Spacing.Simple), (tn, tab, dif) => { Dictionary<string, Index> modelIxs = modelIndices[tab]; var removedColums = dif.Colums.Keys.Except(tab.Columns.Keys).ToHashSet(); var changes = Synchronizer.SynchronizeScript(modelIxs, dif.Indices, null, (i, dix) => dix.IsControlledIndex || dix.Columns.Any(removedColums.Contains) ? SqlBuilder.DropIndex(dif.Name, dix) : null, (i, mix, dix) => (mix as UniqueIndex).Try(u => u.ViewName) != dix.ViewName || columnsChanged(dif, dix, mix) ? SqlBuilder.DropIndex(dif.Name, dix) : null, Spacing.Simple); return changes; }, Spacing.Double); SqlPreCommand dropForeignKeys = Synchronizer.SynchronizeScript( model, database, null, (tn, dif) => dif.Colums.Values.Select(c => c.ForeingKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, c.ForeingKey.Name) : null).Combine(Spacing.Simple), (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Colums, null, (cn, colDb) => colDb.ForeingKey != null ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeingKey.Name) : null, (cn, colModel, colDb) => colDb.ForeingKey == null ? null : colModel.ReferenceTable == null || !colModel.ReferenceTable.Name.Equals(ChangeName(colDb.ForeingKey.TargetTable)) ? SqlBuilder.AlterTableDropConstraint(dif.Name, colDb.ForeingKey.Name) : null, 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.Colums, (cn, tabCol) => SqlBuilder.AlterTableAddColumn(tab, tabCol), (cn, difCol) => SqlPreCommand.Combine(Spacing.Simple, difCol.DefaultConstraintName.HasText() ? SqlBuilder.AlterTableDropConstraint(tab.Name, difCol.DefaultConstraintName) : 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) ? null : SqlBuilder.AlterTableAlterColumn(tab, tabCol), UpdateByFkChange(tn, difCol, tabCol, ChangeName)), Spacing.Simple)), Spacing.Double); var tableReplacements = replacements.TryGetC(Replacements.KeyTables); if (tableReplacements != null) replacements[Replacements.KeyTablesInverse] = tableReplacements.Inverse(); SqlPreCommand syncEnums = SynchronizeEnumsScript(replacements); SqlPreCommand addForeingKeys = Synchronizer.SynchronizeScript( model, database, (tn, tab) => SqlBuilder.AlterTableForeignKeys(tab), null, (tn, tab, dif) => Synchronizer.SynchronizeScript( tab.Columns, dif.Colums, (cn, colModel) => colModel.ReferenceTable == null ? null: SqlBuilder.AlterTableAddConstraintForeignKey(tab, colModel.Name, colModel.ReferenceTable), null, (cn, colModel, coldb) => { if(colModel.ReferenceTable == null) return null; if(coldb.ForeingKey == null || !colModel.ReferenceTable.Name.Equals(ChangeName(coldb.ForeingKey.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.ForeingKey.Name? SqlBuilder.RenameForeignKey(tab.Name.Schema, coldb.ForeingKey.Name, name) : null, (coldb.ForeingKey.IsDisabled || coldb.ForeingKey.IsNotTrusted) && !ExecutionMode.IsSynchronizeSchemaOnly ? SqlBuilder.EnableForeignKey(tab.Name, name) : null); }, Spacing.Simple), Spacing.Double); bool? createMissingFreeIndexes = null; SqlPreCommand addIndices = Synchronizer.SynchronizeScript(model, database, (tn, tab) => modelIndices[tab].Values.Select(SqlBuilder.CreateIndex).Combine(Spacing.Simple), null, (tn, tab, dif) => { var columnReplacements = replacements.TryGetC(Replacements.KeyColumnsForTable(tn)); Func<IColumn, bool> isNew = c => !dif.Colums.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 too?") ? SqlBuilder.CreateIndex(mix) : null, null, (i, mix, dix) => (mix as UniqueIndex).Try(u => u.ViewName) != dix.ViewName || columnsChanged(dif, dix, 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); return SqlPreCommand.Combine(Spacing.Triple, dropStatistics, dropIndices, dropForeignKeys, tables, syncEnums, addForeingKeys, addIndices); }
public static SqlPreCommand?Schema_Synchronizing(Replacements replacements) { var schema = Schema.Current; var isPostgres = schema.Settings.IsPostgres; Dictionary <string, TypeEntity> should = GenerateSchemaTypes().ToDictionaryEx(s => s.TableName, "tableName in memory"); var currentList = Administrator.TryRetrieveAll <TypeEntity>(replacements); { //External entities are nt asked in SchemaSynchronizer replacements.AskForReplacements( currentList.Where(t => schema.IsExternalDatabase(ObjectName.Parse(t.TableName, isPostgres).Schema.Database)).Select(a => a.TableName).ToHashSet(), should.Values.Where(t => schema.IsExternalDatabase(ObjectName.Parse(t.TableName, isPostgres).Schema.Database)).Select(a => a.TableName).ToHashSet(), Replacements.KeyTables); } Dictionary <string, TypeEntity> current = ApplyReplacementsToOld(replacements, currentList.ToDictionaryEx(c => c.TableName, "tableName in database"), Replacements.KeyTables); { //Temporal solution until applications are updated var repeated = should.Keys.Select(k => ObjectName.Parse(k, isPostgres)).GroupBy(a => a.Name).Where(a => a.Count() > 1).Select(a => a.Key).Concat( current.Keys.Select(k => ObjectName.Parse(k, isPostgres)).GroupBy(a => a.Name).Where(a => a.Count() > 1).Select(a => a.Key)).ToList(); string simplify(string tn) { ObjectName name = ObjectName.Parse(tn, isPostgres); return(repeated.Contains(name.Name) ? name.ToString() : name.Name); } should = should.SelectDictionary(simplify, v => v); current = current.SelectDictionary(simplify, v => v); } Table table = schema.Table <TypeEntity>(); using (replacements.WithReplacedDatabaseName()) return(Synchronizer.SynchronizeScript( Spacing.Double, should, current, createNew: (tn, s) => table.InsertSqlSync(s), removeOld: (tn, c) => table.DeleteSqlSync(c, t => t.CleanName == c.CleanName), mergeBoth: (tn, s, c) => { var originalCleanName = c.CleanName; var originalFullName = c.FullClassName; if (c.TableName != s.TableName) { var pc = ObjectName.Parse(c.TableName, isPostgres); var ps = ObjectName.Parse(s.TableName, isPostgres); if (!EqualsIgnoringDatabasePrefix(pc, ps)) { c.TableName = ps.ToString(); } } c.CleanName = s.CleanName; c.Namespace = s.Namespace; c.ClassName = s.ClassName; return table.UpdateSqlSync(c, t => t.CleanName == originalCleanName, comment: originalFullName); })); }