Exemple #1
0
        //==============================================================================

        /// <summary>
        /// Returns a list of nodes (a subset of 'candidates') that can be safely removed in a way
        /// that no other remaining node depends (directly or indirectly) on removed nodes.
        /// </summary>
        /// <param name="candidates">Nodes to be removed.</param>
        /// <param name="dependencies">Dependency: Item2 depends on Item1.</param>
        public static List <T> RemovableLeaves <T>(IEnumerable <T> candidates, IEnumerable <Tuple <T, T> > dependencies)
        {
            CsUtility.Materialize(ref candidates);
            CsUtility.Materialize(ref dependencies);

            dependencies = dependencies.Distinct().ToArray();
            var all = candidates.Union(dependencies.Select(d => d.Item1)).Union(dependencies.Select(d => d.Item2)).ToArray();

            var numberOfDependents = all.ToDictionary(node => node, node => 0);

            foreach (var relation in dependencies)
            {
                numberOfDependents[relation.Item1]++;
            }

            var dependsOn = all.ToDictionary(node => node, node => new List <T>());

            foreach (var relation in dependencies)
            {
                dependsOn[relation.Item2].Add(relation.Item1);
            }

            var removed         = new HashSet <T>();
            var candidatesIndex = new HashSet <T>(candidates);

            foreach (var cand in candidates)
            {
                if (numberOfDependents[cand] == 0 && !removed.Contains(cand))
                {
                    RemoveLeaf(cand, removed, numberOfDependents, dependsOn, candidatesIndex);
                }
            }
            return(removed.ToList());
        }
        public static List <SqlTransactionBatch> GroupByTransaction(IEnumerable <string> sqlScripts)
        {
            sqlScripts = sqlScripts.Where(s => !string.IsNullOrWhiteSpace(s.Replace(SqlUtility.NoTransactionTag, "")));
            var batches = CsUtility.GroupItemsKeepOrdering(sqlScripts, SqlUtility.ScriptSupportsTransaction);

            return(batches.Select(batch => new SqlTransactionBatch(batch.Items)
            {
                UseTransacion = batch.Key
            }).ToList());
        }
        /// <summary>
        /// 1. Splits the scripts by the SQL batch delimiter ("GO", for Microsoft SQL Server). See <see cref="SqlUtility.SplitBatches(string)"/>.
        /// 2. Detects and applies the transaction usage tag. See <see cref="SqlUtility.NoTransactionTag"/> and <see cref="SqlUtility.ScriptSupportsTransaction(string)"/>.
        /// 3. Reports progress (Info level) after each minute.
        /// 4. Prefixes each SQL script with a comment containing the script's name.
        /// </summary>
        public void Execute(IEnumerable <SqlScript> sqlScripts)
        {
            var scriptParts = sqlScripts
                              .SelectMany(script => script.IsBatch
                    ? SqlUtility.SplitBatches(script.Sql).Select(scriptPart => new SqlScript {
                Name = script.Name, Sql = scriptPart, IsBatch = false
            })
                    : new[] { script })
                              .Where(script => !string.IsNullOrWhiteSpace(script.Sql));

            var sqlBatches = CsUtility.GroupItemsKeepOrdering(scriptParts, script => SqlUtility.ScriptSupportsTransaction(script.Sql))
                             .Select(group => new
            {
                UseTransaction = group.Key,
                // The empty NoTransactionTag script is used by the DatabaseGenerator to split transactions.
                // This is why there scrips are removed *after* grouping
                Scripts = group.Items.Where(s => !string.Equals(s.Sql, SqlUtility.NoTransactionTag, StringComparison.Ordinal)).ToList()
            })
                             .Where(group => group.Scripts.Count() > 0) // Cleanup after removing the empty NoTransactionTag scripts.
                             .ToList();

            _logger.Trace(() => "SqlBatches: " + string.Join(", ", sqlBatches.Select(b => $"{(b.UseTransaction ? "tran" : "notran")} {b.Scripts.Count()}")));

            int totalCount           = sqlBatches.Sum(b => b.Scripts.Count);
            int previousBatchesCount = 0;
            var startTime            = DateTime.Now;
            var lastReportTime       = startTime;

            foreach (var sqlBatch in sqlBatches)
            {
                Action <int> reportProgress = currentBatchCount =>
                {
                    var now           = DateTime.Now;
                    int executedCount = previousBatchesCount + currentBatchCount + 1;

                    if (now.Subtract(lastReportTime).TotalMilliseconds > _reportDelayMs &&
                        executedCount < totalCount)    // No need to report progress if the work is done.
                    {
                        double estimatedTotalMs = now.Subtract(startTime).TotalMilliseconds / executedCount * totalCount;
                        var    remainingTime    = startTime.AddMilliseconds(estimatedTotalMs).Subtract(now);
                        _logger.Info($"Executed {executedCount} / {totalCount} SQL scripts. {(remainingTime.TotalMinutes).ToString("f2")} minutes remaining.");
                        lastReportTime = now;
                    }
                };

                var scriptsWithName = sqlBatch.Scripts
                                      .Select(script => string.IsNullOrEmpty(script.Name)
                        ? script.Sql
                        : "--Name: " + script.Name.Replace("\r", " ").Replace("\n", " ") + "\r\n" + script.Sql);

                _sqlExecuter.ExecuteSql(scriptsWithName, sqlBatch.UseTransaction, null, reportProgress);

                previousBatchesCount += sqlBatch.Scripts.Count;
            }
        }
        public static string LimitIdentifierLength(string name)
        {
            const int MaxLength = 30;

            if (name.Length > MaxLength)
            {
                var hashErasedPart = CsUtility.GetStableHashCode(name.Substring(MaxLength - 9)).ToString("X").PadLeft(8, '0');
                return(name.Substring(0, MaxLength - 9) + "_" + hashErasedPart);
            }
            return(name);
        }
Exemple #5
0
 public static string GuidToString(Guid guid)
 {
     if (DatabaseLanguageIsMsSql.Value)
     {
         return(guid.ToString().ToUpper());
     }
     else if (DatabaseLanguageIsOracle.Value)
     {
         return(CsUtility.ByteArrayToHex(guid.ToByteArray()));
     }
     else
     {
         throw new FrameworkException(UnsupportedLanguageError);
     }
 }
Exemple #6
0
 public static Guid StringToGuid(string guid)
 {
     if (DatabaseLanguageIsMsSql.Value)
     {
         return(Guid.Parse(guid));
     }
     else if (DatabaseLanguageIsOracle.Value)
     {
         return(new Guid(CsUtility.HexToByteArray(guid)));
     }
     else
     {
         throw new FrameworkException(UnsupportedLanguageError);
     }
 }
Exemple #7
0
        /// <summary>
        /// Throws an exception if 'name' is not a valid SQL database object name.
        /// Function returns given argument so it can be used as fluent interface.
        /// In some cases the function may change the identifier (limit identifier length to 30 on Oracle database, e.g.).
        /// </summary>
        public static string Identifier(string name)
        {
            string error = CsUtility.GetIdentifierError(name);

            if (error != null)
            {
                throw new FrameworkException("Invalid database object name: " + error);
            }

            if (DatabaseLanguageIsOracle.Value)
            {
                name = OracleSqlUtility.LimitIdentifierLength(name);
            }

            return(name);
        }
Exemple #8
0
        private ValueOrError <List <string> > ListCachedFiles(string fileGroupName, byte[] sourceHash, IEnumerable <string> requestedExtensions)
        {
            var cachedFilesByExt = ListCachedFiles()
                                   .GetValueOrDefault(fileGroupName)
                                   ?.ToDictionary(file => Path.GetExtension(file));

            if (cachedFilesByExt == null)
            {
                return(ValueOrError.CreateError("File group not cached."));
            }

            string cachedHashFile = cachedFilesByExt.GetValueOrDefault(".hash");

            if (cachedHashFile == null)
            {
                return(ValueOrError.CreateError("Missing hash file."));
            }

            byte[] cachedHash = CsUtility.HexToByteArray(File.ReadAllText(cachedHashFile, Encoding.Default));
            if (cachedHash == null || cachedHash.Length == 0)
            {
                return(ValueOrError.CreateError("Missing hash value."));
            }

            if (!sourceHash.SequenceEqual(cachedHash))
            {
                return(ValueOrError.CreateError("Different hash value."));
            }

            var requestedFiles = new List <string>(requestedExtensions.Count());

            foreach (var extension in requestedExtensions)
            {
                string cachedFile = cachedFilesByExt.GetValueOrDefault(extension);
                if (cachedFile == null)
                {
                    return(ValueOrError.CreateError($"Extension '{extension}' not in cache."));
                }
                requestedFiles.Add(cachedFile);
            }

            return(requestedFiles);
        }
Exemple #9
0
        /// <summary>
        /// Copies the files from cache only if all of the extensions are found in the cache,
        /// and if the sourceContent matches the corresponding sourceFile in the cache.
        /// </summary>
        /// <param name="sampleSourceFile">Any file from the cached file group, extension will be ignored.</param>
        /// <returns>List of the restored files, if the files are copied from the cache, null otherwise.</returns>
        public List <string> RestoreCachedFiles(string sampleSourceFile, byte[] sourceHash, string targetFolder, IEnumerable <string> copyExtensions)
        {
            CsUtility.Materialize(ref copyExtensions);
            var cachedFiles = ListCachedFiles(Path.GetFileNameWithoutExtension(sampleSourceFile), sourceHash, copyExtensions);

            List <string> targetFiles;
            string        report;

            if (!cachedFiles.IsError)
            {
                targetFiles = cachedFiles.Value.Select(source =>
                                                       _filesUtility.SafeCopyFileToFolder(source, targetFolder)).ToList();
                report = "Restored " + string.Join(", ", copyExtensions) + ".";
            }
            else
            {
                targetFiles = null;
                report      = cachedFiles.Error;
            }

            _logger.Trace(() => "RestoreCachedFiles for " + Path.GetFileName(sampleSourceFile) + ": " + report);
            return(targetFiles);
        }
Exemple #10
0
        /// <param name="sampleSourceFile">Any file from the cached file group, extension will be ignored.</param>
        public void SaveHash(string sampleSourceFile, byte[] hash)
        {
            string hashFile = Path.GetFullPath(Path.ChangeExtension(sampleSourceFile, ".hash"));

            File.WriteAllText(hashFile, CsUtility.ByteArrayToHex(hash), Encoding.ASCII);
        }
Exemple #11
0
        /// <param name="sampleSourceFile">Any file from the cached file group, extension will be ignored.</param>
        public byte[] LoadHash(string sampleSourceFile)
        {
            string hashFile = Path.GetFullPath(Path.ChangeExtension(sampleSourceFile, ".hash"));

            return(CsUtility.HexToByteArray(File.ReadAllText(hashFile, Encoding.Default)));
        }