private List <string> ApplyChangesToDatabase_Insert(List <ConceptApplication> toBeInserted, List <ConceptApplication> newApplications)
        {
            var newScripts = new List <string>();

            Graph.TopologicalSort(toBeInserted, ConceptApplication.GetDependencyPairs(newApplications));

            int insertedCACount = 0;

            foreach (var ca in toBeInserted)
            {
                string[] createSqlScripts = SplitSqlScript(ca.CreateQuery);
                if (createSqlScripts.Length > 0)
                {
                    LogDatabaseChanges(ca, "Creating");
                    insertedCACount++;
                }

                newScripts.AddRange(createSqlScripts);
                newScripts.AddRange(_sqlTransactionBatches.JoinScripts(_conceptApplicationRepository.InsertMetadataSql(ca)));
                newScripts.AddRange(MaybeCommitMetadataAfterDdl(createSqlScripts));
            }

            _logger.Info($"Creating {insertedCACount} concept applications.");
            return(newScripts);
        }
        /// <summary>
        /// Compares current generated database model (from DSL scripts)
        /// and the previously created objects in database (from metadata in table Rhetos.AppliedConcept).
        /// </summary>
        public DatabaseDiff Diff()
        {
            var stopwatch = Stopwatch.StartNew();

            var oldApplications = _conceptApplicationRepository.Load();

            _performanceLogger.Write(stopwatch, "Loaded old concept applications.");

            var newApplications = ConceptApplication.FromDatabaseObjects(_databaseModel.DatabaseObjects);

            _performanceLogger.Write(stopwatch, "Got new concept applications.");

            MatchAndComputeNewApplicationIds(oldApplications, newApplications);
            _performanceLogger.Write(stopwatch, "Match new and old concept applications.");

            ConceptApplication.CheckKeyUniqueness(newApplications, "generated, after matching");
            _performanceLogger.Write(stopwatch, "Verify new concept applications' integrity.");

            newApplications = TrimEmptyApplications(newApplications);
            _performanceLogger.Write(stopwatch, "Removed unused concept applications.");

            var diff = CalculateApplicationsToBeRemovedAndInserted(oldApplications, newApplications);

            _performanceLogger.Write(stopwatch, "Analyzed differences in database structure.");
            return(diff);
        }
Beispiel #3
0
 public List <string> DeleteMetadataSql(ConceptApplication oldCA)
 {
     return(new List <string>
     {
         Sql.Format("ConceptApplicationRepository_Delete", SqlUtility.QuoteGuid(oldCA.Id))
     });
 }
        private List <SqlBatchScript> ApplyChangesToDatabase_Insert(List <ConceptApplication> toBeInserted, List <ConceptApplication> newApplications)
        {
            var newScripts = new List <SqlBatchScript>();

            Graph.TopologicalSort(toBeInserted, ConceptApplication.GetDependencyPairs(newApplications));

            int insertedCACount = 0;

            foreach (var ca in toBeInserted)
            {
                if (!string.IsNullOrWhiteSpace(ca.CreateQuery))
                {
                    LogDatabaseChanges(ca, "Creating");
                    insertedCACount++;
                }

                newScripts.Add(new SqlBatchScript {
                    Sql = ca.CreateQuery, IsBatch = true, Name = $"Creating {ca}."
                });
                newScripts.AddRange(_sqlTransactionBatches.JoinScripts(_conceptApplicationRepository.InsertMetadataSql(ca))
                                    .Select((sql, x) => new SqlBatchScript {
                    Sql = sql, IsBatch = false, Name = $"Creating {ca} metadata ({x + 1})."
                }));
                newScripts.AddRange(MaybeCommitMetadataAfterDdl(ca.CreateQuery));
            }

            _logger.Info($"Creating {insertedCACount} database objects.");
            return(newScripts);
        }
        private List <SqlBatchScript> ApplyChangesToDatabase_Remove(List <ConceptApplication> toBeRemoved, List <ConceptApplication> oldApplications)
        {
            var newScripts = new List <SqlBatchScript>();

            toBeRemoved = toBeRemoved.OrderBy(ca => ca.OldCreationOrder).ToList();                      // TopologicalSort is stable sort, so it will keep this (original) order unless current dependencies direct otherwise.
            Graph.TopologicalSort(toBeRemoved, ConceptApplication.GetDependencyPairs(oldApplications)); // Concept's dependencies might have changed, without dropping and recreating the concept. It is important to compute up-to-date remove order, otherwise FK constraint FK_AppliedConceptDependsOn_DependsOn might fail.
            toBeRemoved.Reverse();

            int removedCACount = 0;

            foreach (var ca in toBeRemoved)
            {
                if (!string.IsNullOrWhiteSpace(ca.RemoveQuery))
                {
                    LogDatabaseChanges(ca, "Removing");
                    removedCACount++;
                }

                newScripts.Add(new SqlBatchScript {
                    Sql = ca.RemoveQuery, IsBatch = true, Name = $"Removing {ca}."
                });
                newScripts.AddRange(_sqlTransactionBatches.JoinScripts(_conceptApplicationRepository.DeleteMetadataSql(ca))
                                    .Select((sql, x) => new SqlBatchScript {
                    Sql = sql, IsBatch = false, Name = $"Removing {ca} metadata ({x + 1})."
                }));
                newScripts.AddRange(MaybeCommitMetadataAfterDdl(ca.RemoveQuery));
            }

            _logger.Info($"Removing {removedCACount} database objects.");
            return(newScripts);
        }
        private List <string> ApplyChangesToDatabase_Remove(List <ConceptApplication> toBeRemoved, List <ConceptApplication> oldApplications)
        {
            var newScripts = new List <string>();

            toBeRemoved = toBeRemoved.OrderBy(ca => ca.OldCreationOrder).ToList();                      // TopologicalSort is stable sort, so it will keep this (original) order unless current dependencies direct otherwise.
            Graph.TopologicalSort(toBeRemoved, ConceptApplication.GetDependencyPairs(oldApplications)); // Concept's dependencies might have changed, without dropping and recreating the concept. It is important to compute up-to-date remove order, otherwise FK constraint FK_AppliedConceptDependsOn_DependsOn might fail.
            toBeRemoved.Reverse();

            int removedCACount = 0;

            foreach (var ca in toBeRemoved)
            {
                string[] removeSqlScripts = SplitSqlScript(ca.RemoveQuery);
                if (removeSqlScripts.Length > 0)
                {
                    LogDatabaseChanges(ca, "Removing");
                    removedCACount++;
                }

                newScripts.AddRange(removeSqlScripts);
                newScripts.AddRange(_sqlTransactionBatches.JoinScripts(_conceptApplicationRepository.DeleteMetadataSql(ca)));
                newScripts.AddRange(MaybeCommitMetadataAfterDdl(removeSqlScripts));
            }

            _logger.Info($"Removing {removedCACount} concept applications.");
            return(newScripts);
        }
 public List<string> DeleteMetadataSql(ConceptApplication ca)
 {
     return new List<string>
     {
         Sql.Format("ConceptApplicationRepository_Delete", SqlUtility.QuoteGuid(ca.Id))
     };
 }
 private void LogDatabaseChanges(ConceptApplication conceptApplication, string action, Func <string> additionalInfo = null)
 {
     _changesLogger.Trace("{0} {1}, ID={2}.{3}{4}",
                          action,
                          conceptApplication.GetConceptApplicationKey(),
                          SqlUtility.GuidToString(conceptApplication.Id),
                          additionalInfo != null ? " " : null,
                          additionalInfo != null ? additionalInfo() : null);
 }
Beispiel #9
0
        public List <ConceptApplication> Load()
        {
            var previoslyAppliedConcepts = LoadOldConceptApplicationsFromDatabase();

            ConceptApplication.CheckKeyUniqueness(previoslyAppliedConcepts, "loaded");

            var dependencies = LoadDependenciesFromDatabase();

            EvaluateDependencies(previoslyAppliedConcepts, dependencies); // Replace GUIDs with actual ConceptApplication instances.

            return(previoslyAppliedConcepts);
        }
        private List <ConceptApplication> TrimEmptyApplications(List <ConceptApplication> newApplications)
        {
            var emptyCreateQuery     = newApplications.Where(ca => string.IsNullOrWhiteSpace(ca.CreateQuery)).ToList();
            var emptyCreateHasRemove = emptyCreateQuery.FirstOrDefault(ca => !string.IsNullOrWhiteSpace(ca.RemoveQuery));

            if (emptyCreateHasRemove != null)
            {
                throw new FrameworkException("A concept that does not create database objects (CreateDatabaseStructure) cannot remove them (RemoveDatabaseStructure): "
                                             + emptyCreateHasRemove.GetConceptApplicationKey() + ".");
            }

            var removeLeaves = Graph.RemovableLeaves(emptyCreateQuery, ConceptApplication.GetDependencyPairs(newApplications));

            _logger.Trace(() => $"Removing {removeLeaves.Count} empty leaf concept applications:{string.Concat(removeLeaves.Select(l => "\r\n-" + l))}");

            return(newApplications.Except(removeLeaves).ToList());
        }
Beispiel #11
0
        public List <string> UpdateMetadataSql(NewConceptApplication ca, ConceptApplication oldApp)
        {
            var sql = new List <string>();

            if (oldApp.RemoveQuery != ca.RemoveQuery)
            {
                sql.Add(Sql.Format("ConceptApplicationRepository_Update",
                                   SqlUtility.QuoteGuid(ca.Id),
                                   SqlUtility.QuoteText(ca.ConceptInfoTypeName),
                                   SqlUtility.QuoteText(ca.ConceptInfoKey),
                                   SqlUtility.QuoteText(ca.ConceptImplementationTypeName),
                                   SqlUtility.QuoteText(_xmlUtility.SerializeToXml(ca.ConceptInfo)),
                                   SqlUtility.QuoteText(ca.CreateQuery),
                                   SqlUtility.QuoteText(ca.RemoveQuery),
                                   SqlUtility.QuoteText(ca.ConceptImplementationVersion.ToString())));
            }

            HashSet <Guid> oldDependsOn = new HashSet <Guid>(oldApp.DependsOn.Select(depOn => depOn.ConceptApplication.Id));
            HashSet <Guid> newDependsOn = new HashSet <Guid>(ca.DependsOn.Select(depOn => depOn.ConceptApplication.Id));

            foreach (var dependsOnId in ca.DependsOn.Select(d => d.ConceptApplication.Id).Distinct())
            {
                if (!oldDependsOn.Contains(dependsOnId))
                {
                    sql.Add(Sql.Format("ConceptApplicationRepository_InsertDependency",
                                       SqlUtility.QuoteGuid(ca.Id),
                                       SqlUtility.QuoteGuid(dependsOnId)));
                }
            }

            foreach (var dependsOnId in oldApp.DependsOn.Select(d => d.ConceptApplication.Id).Distinct())
            {
                if (!newDependsOn.Contains(dependsOnId))
                {
                    sql.Add(Sql.Format("ConceptApplicationRepository_DeleteDependency",
                                       SqlUtility.QuoteGuid(ca.Id),
                                       SqlUtility.QuoteGuid(dependsOnId)));
                }
            }

            return(sql);
        }
Beispiel #12
0
        public List <string> InsertMetadataSql(ConceptApplication newCA)
        {
            var sql = new List <string>();

            sql.Add(Sql.Format("ConceptApplicationRepository_Insert",
                               SqlUtility.QuoteGuid(newCA.Id),
                               SqlUtility.QuoteText(newCA.ConceptInfoTypeName),
                               SqlUtility.QuoteText(newCA.ConceptInfoKey),
                               SqlUtility.QuoteText(newCA.ConceptImplementationTypeName),
                               SqlUtility.QuoteText(newCA.CreateQuery),
                               SqlUtility.QuoteText(newCA.RemoveQuery)));

            foreach (var dependsOnId in newCA.DependsOnConceptApplications.Select(d => d.Id).Distinct())
            {
                sql.Add(Sql.Format("ConceptApplicationRepository_InsertDependency",
                                   SqlUtility.QuoteGuid(newCA.Id),
                                   SqlUtility.QuoteGuid(dependsOnId)));
            }

            return(sql);
        }
Beispiel #13
0
 protected void Log(ConceptApplication conceptApplication, string action)
 {
     _logger.Info("{0} {1}, ID={2}.", action, conceptApplication.GetConceptApplicationKey(), SqlUtility.GuidToString(conceptApplication.Id));
 }
        public List<string> UpdateMetadataSql(NewConceptApplication ca, ConceptApplication oldApp)
        {
            var sql = new List<string>();
            if (oldApp.RemoveQuery != ca.RemoveQuery)
                sql.Add(Sql.Format("ConceptApplicationRepository_Update",
                    SqlUtility.QuoteGuid(ca.Id),
                    SqlUtility.QuoteText(ca.ConceptInfoTypeName),
                    SqlUtility.QuoteText(ca.ConceptInfoKey),
                    SqlUtility.QuoteText(ca.ConceptImplementationTypeName),
                    SqlUtility.QuoteText(_xmlUtility.SerializeToXml(ca.ConceptInfo)),
                    SqlUtility.QuoteText(ca.CreateQuery),
                    SqlUtility.QuoteText(ca.RemoveQuery),
                    SqlUtility.QuoteText(ca.ConceptImplementationVersion.ToString())));

            HashSet<Guid> oldDependsOn = new HashSet<Guid>(oldApp.DependsOn.Select(depOn => depOn.ConceptApplication.Id));
            HashSet<Guid> newDependsOn = new HashSet<Guid>(ca.DependsOn.Select(depOn => depOn.ConceptApplication.Id));

            foreach (var dependsOnId in ca.DependsOn.Select(d => d.ConceptApplication.Id).Distinct())
                if (!oldDependsOn.Contains(dependsOnId))
                    sql.Add(Sql.Format("ConceptApplicationRepository_InsertDependency",
                        SqlUtility.QuoteGuid(ca.Id),
                        SqlUtility.QuoteGuid(dependsOnId)));

            foreach (var dependsOnId in oldApp.DependsOn.Select(d => d.ConceptApplication.Id).Distinct())
                if (!newDependsOn.Contains(dependsOnId))
                    sql.Add(Sql.Format("ConceptApplicationRepository_DeleteDependency",
                        SqlUtility.QuoteGuid(ca.Id),
                        SqlUtility.QuoteGuid(dependsOnId)));

            return sql;
        }
 public static string DeleteMetadataSql(ConceptApplication ca)
 {
     return Sql.Format("ConceptApplicationRepository_Delete", SqlUtility.QuoteGuid(ca.Id));
 }
Beispiel #16
0
 protected static void AddConceptApplicationSeparator(ConceptApplication ca, CodeBuilder sqlCodeBuilder)
 {
     sqlCodeBuilder.InsertCode(string.Format("{0}{1}{2}{3}",
         NextConceptApplicationSeparator, NextConceptApplicationIdPrefix, ca.Id, NextConceptApplicationIdSuffix));
 }
Beispiel #17
0
 protected void LogDatabaseChanges(ConceptApplication conceptApplication, string action, Func<string> additionalInfo = null)
 {
     _conceptsLogger.Trace("{0} {1}, ID={2}.{3}{4}",
         action,
         conceptApplication.GetConceptApplicationKey(),
         SqlUtility.GuidToString(conceptApplication.Id),
         additionalInfo != null ? " " : null,
         additionalInfo != null ? additionalInfo() : null);
 }
        private DatabaseDiff CalculateApplicationsToBeRemovedAndInserted(List <ConceptApplication> oldApplications, List <ConceptApplication> newApplications)
        {
            var oldApplicationsByKey = oldApplications.ToDictionary(a => a.GetConceptApplicationKey());
            var newApplicationsByKey = newApplications.ToDictionary(a => a.GetConceptApplicationKey());

            // Find directly inserted and removed concept applications:

            var directlyRemoved  = oldApplicationsByKey.Keys.Except(newApplicationsByKey.Keys).ToList();
            var directlyInserted = newApplicationsByKey.Keys.Except(oldApplicationsByKey.Keys).ToList();

            foreach (string ca in directlyRemoved)
            {
                _logger.Trace("Directly removed concept application: " + ca);
            }
            foreach (string ca in directlyInserted)
            {
                _logger.Trace("Directly inserted concept application: " + ca);
            }

            // Find changed concept applications (different create SQL query):

            var existingApplications = oldApplicationsByKey.Keys.Intersect(newApplicationsByKey.Keys).ToList();
            var changedApplications  = existingApplications
                                       .Where(appKey => !string.Equals(
                                                  oldApplicationsByKey[appKey].CreateQuery,
                                                  newApplicationsByKey[appKey].CreateQuery,
                                                  StringComparison.Ordinal))
                                       .ToList();

            // Find dependent concepts applications to be regenerated:

            var toBeRemovedKeys = directlyRemoved.Union(changedApplications).ToList();
            var oldDependencies = ConceptApplication.GetDependencyPairs(oldApplications).Select(dep => Tuple.Create(dep.Item1.GetConceptApplicationKey(), dep.Item2.GetConceptApplicationKey()));
            var dependentRemovedApplications = Graph.IncludeDependents(toBeRemovedKeys, oldDependencies).Except(toBeRemovedKeys);

            var toBeInsertedKeys = directlyInserted.Union(changedApplications).ToList();
            var newDependencies  = ConceptApplication.GetDependencyPairs(newApplications).Select(dep => Tuple.Create(dep.Item1.GetConceptApplicationKey(), dep.Item2.GetConceptApplicationKey()));
            var dependentInsertedApplications = Graph.IncludeDependents(toBeInsertedKeys, newDependencies).Except(toBeInsertedKeys);

            var refreshDependents = dependentRemovedApplications.Union(dependentInsertedApplications).ToList();

            toBeRemovedKeys.AddRange(refreshDependents.Intersect(oldApplicationsByKey.Keys));
            toBeInsertedKeys.AddRange(refreshDependents.Intersect(newApplicationsByKey.Keys));

            // Report dependencies for items that need to be refreshed, for logging and debugging only:

            var newDependenciesByDependent = newDependencies.GroupBy(dep => dep.Item2, dep => dep.Item1).ToDictionary(group => group.Key, group => group.ToList());
            var oldDependenciesByDependent = oldDependencies.GroupBy(dep => dep.Item2, dep => dep.Item1).ToDictionary(group => group.Key, group => group.ToList());
            var toBeInsertedIndex          = new HashSet <string>(toBeInsertedKeys);
            var toBeRemovedIndex           = new HashSet <string>(toBeRemovedKeys);
            var changedApplicationsIndex   = new HashSet <string>(changedApplications);
            var refreshes = new List <(ConceptApplication RefreshedConcept, ConceptApplication Dependency, RefreshDependencyStatus DependencyStatus)>();

            foreach (string ca in refreshDependents.Intersect(newApplicationsByKey.Keys))
            {
                var refreshedConcept  = newApplicationsByKey[ca];
                var refreshBecauseNew = new HashSet <string>(newDependenciesByDependent.GetValueOrEmpty(ca).Intersect(toBeInsertedIndex));
                var refreshBecauseOld = new HashSet <string>(oldDependenciesByDependent.GetValueOrEmpty(ca).Intersect(toBeRemovedIndex));
                var dependsOnExisting = refreshBecauseNew.Intersect(refreshBecauseOld);

                refreshes.AddRange(refreshBecauseNew.Except(refreshBecauseOld)
                                   .Select(dependency => (refreshedConcept, newApplicationsByKey[dependency], RefreshDependencyStatus.New)));
Beispiel #19
0
 protected static void AddConceptApplicationSeparator(ConceptApplication ca, CodeBuilder sqlCodeBuilder)
 {
     sqlCodeBuilder.InsertCode(string.Format("{0}{1}{2}{3}",
                                             NextConceptApplicationSeparator, NextConceptApplicationIdPrefix, ca.Id, NextConceptApplicationIdSuffix));
 }