Beispiel #1
0
        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);
Beispiel #2
0
        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);
            }));
    }
Beispiel #4
0
        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);
                }
            }
        }
Beispiel #5
0
    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);
                }
            }
        }
    }
Beispiel #6
0
    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));
    }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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);
                }
            }
        }
Beispiel #9
0
        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;
            }));
        }
Beispiel #10
0
        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")));
        }
Beispiel #11
0
        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");
                }
            }
        }
Beispiel #12
0
        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"));
        }
Beispiel #13
0
        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;
        }
Beispiel #17
0
        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);
        }
Beispiel #18
0
        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);
                }));
        }