/// <summary>
        ///
        /// </summary>
        /// <param name="backupItem"></param>
        /// <param name="restore"></param>
        /// <remarks>
        ///     <paramref name="restore"/> must have backup device already added
        /// </remarks>
        private void AddMoveFileInstructionsIfNeeded(BackupItem backupItem, Restore restore)
        {
            Check.DoRequireArgumentNotNull(restore, "restore");
            Check.DoCheckArgument(restore.Devices.Count > 0, "Restore instance must have backup device already added");

            if (backupItem.BackupType == BackupType.Full)
            {
                var databaseFilesInBackup = ReadDatabaseFileList(restore);
                foreach (var dbFile in databaseFilesInBackup)
                {
                    var targetDir        = dbFile.IsLog ? DefaultLogPath : DefaultDataPath;
                    var physicalFilePath = FindExistingDatabaseFile(dbFile.LogicalName, dbFile.IsLog);

                    if (string.IsNullOrEmpty(physicalFilePath))
                    {
                        // no such file in the existing database; maybe it was dropped
                        physicalFilePath = Path.Combine(targetDir, Path.GetFileName(dbFile.PhysicalName));
                        physicalFilePath = PickNewFileName(physicalFilePath);
                    }

                    Log.InfoFormat("Moving {0} to {1}", dbFile.LogicalName, physicalFilePath);

                    restore.RelocateFiles.Add(new RelocateFile(dbFile.LogicalName, physicalFilePath));
                }
            }
        }
예제 #2
0
        private bool CheckSequence(BackupItem first, BackupItem next, bool raiseException)
        {
            bool result;

            if (first.BackupType == BackupType.Full)
            {
                if (next.BackupType == BackupType.DifferentialDatabase)
                {
                    result = Verify(next.DatabaseBackupLsn == first.CheckpointLsn, raiseException
                                    , $"The differential backup {next} cannot be applied after the full backup {first} due to LSN mismatch: {next.DatabaseBackupLsn}" +
                                    $" vs {first.CheckpointLsn}; another full backup was likely taken between the 2.");
                }
                else
                {
                    result =
                        Verify(next.BackupType == BackupType.Log, raiseException
                               , $"Next backup {next} is of unsupported type {next.BackupType} after full {first}") &&
                        Verify(next.FirstLsn <= first.LastLsn && next.LastLsn >= first.LastLsn, raiseException
                               , $"The log backup {next} is out of sequence with the full backup {first}");
                }
            }
            else if (first.BackupType == BackupType.DifferentialDatabase)
            {
                result =
                    Verify(next.BackupType == BackupType.Log, raiseException
                           , $"Cannot apply {next.BackupType} type backup after differential") &&
                    Verify(next.FirstLsn <= first.LastLsn && next.LastLsn >= first.LastLsn, raiseException
                           , $"The log backup {next} is out of sequence with the diff backup {first}") &&
                    Verify(next.DatabaseBackupLsn == first.DatabaseBackupLsn, raiseException
                           , $"The log backup {next} has a different base with the diff backup {first}");
            }
            else
            {
                result =
                    Verify(first.BackupType == BackupType.Log && next.BackupType == BackupType.Log, raiseException
                           , $"Pair {first.BackupType} - {next.BackupType} is not supported") &&
                    Verify(first.LastLsn == next.FirstLsn, raiseException
                           , $"Log backups {first.FileInfo.Name}#{first.Position} and {next.FileInfo.Name}#{next.Position} are out of sequence");
            }
            return(result);
        }
예제 #3
0
        /// <summary>
        ///     Verified sequence of backup items to restore to achieve configured outcome.
        ///     Never returns null.
        /// </summary>
        /// <param name="lastDatabaseBackupItem"></param>
        /// <returns></returns>
        private List <BackupItem> GetLogBackupItems(BackupItem lastDatabaseBackupItem, DateTime?targetTime)
        {
            Check.DoRequireArgumentNotNull(lastDatabaseBackupItem, "lastDatabaseBackupItem");

            var result = new List <BackupItem>(1000);

            var logFiles = BackupFileCatalog.GetLogBackupsSequence(lastDatabaseBackupItem.BackupStartTime);

            // need only first log backup after target time; flag indicates that file is already processed
            var targetReached = false;
            var fileIterator  = logFiles.GetEnumerator();

            var lastBackupItem = lastDatabaseBackupItem;

            while (!targetReached && fileIterator.MoveNext())
            {
                var header = ParseBackupHeader(fileIterator.Current.FileInfo);

                if (header != null)
                {
                    var itemIterator = header.ValidItems.GetEnumerator();

                    while (!targetReached && itemIterator.MoveNext())
                    {
                        var item = itemIterator.Current;

                        Check.DoAssertLambda(CheckSequence(lastBackupItem, item, false), () =>
                                             $"Item #{item.Position} in file {item.FileInfo.Name} cannot be applied after item #{lastBackupItem.Position} in" +
                                             $" file {lastBackupItem.FileInfo.Name}");
                        lastBackupItem = item;

                        result.Add(item);

                        targetReached = (targetTime.HasValue && item.BackupStartTime > targetTime.Value);
                    }
                }
            }

            return(result);
        }
예제 #4
0
        private BackupItem GetBackupItem(DataRow row, FileInfo backupFileInfo)
        {
            var result = new BackupItem()
            {
                DatabaseName         = (string)row["DatabaseName"],
                BackupType           = (BackupType)Convert.ToInt32(row["BackupType"]),
                Position             = Convert.ToInt32(row["Position"]),
                FirstLsn             = (decimal)row["FirstLSN"],    // numeric(25,0)
                LastLsn              = (decimal)row["LastLSN"],
                DatabaseBackupLsn    = (decimal)row["DatabaseBackupLSN"],
                DifferentialBaseLsn  = (decimal?)(row["DifferentialBaseLSN"] != DBNull.Value ? row["DifferentialBaseLSN"] : null),
                CheckpointLsn        = Convert.ToDecimal(row["CheckpointLSN"]),
                DifferentialBaseGuid = (Guid?)(row["DifferentialBaseGUID"] != DBNull.Value ? row["DifferentialBaseGUID"] : null),
                FileInfo             = backupFileInfo,
                BackupStartTime      = (DateTime)row["BackupStartDate"],
                BackupEndTime        = (DateTime)row["BackupFinishDate"],
                IsCopyOnly           = (bool)row["IsCopyOnly"],
                RecoveryModel        = (RecoveryModel)Enum.Parse(typeof(RecoveryModel), (string)row["RecoveryModel"], true)
            };

            return(result);
        }
        private void Restore(BackupItem item)
        {
            Check.DoRequireArgumentNotNull(item, "item");

            Log.InfoFormat("{0}: restoring {1} from {2}", DatabaseName, item, item.FileInfo.FullName);

            var actionType = item.BackupType == BackupType.Log ? RestoreActionType.Log : RestoreActionType.Database;

            var restore = CreateRestoreInstance();

            restore.Action = actionType;
            restore.Devices.AddDevice(item.FileInfo.FullName, DeviceType.File);
            restore.FileNumber = item.Position;

            if (TargetTime.HasValue && item.BackupType == BackupType.Log)
            {
                // note that another format, yyyy-MM-ddTHH:mm:ss.fff failed with error saying "already restored past the point"
                restore.ToPointInTime = TargetTime.Value.ToString("MMM dd, yyyy hh:mm:ss tt");
                Log.InfoFormat("Setting Point In Time {0} on {1}", restore.ToPointInTime, item);
            }

            AddMoveFileInstructionsIfNeeded(item, restore);

            restore.PercentCompleteNotification = 10;
            restore.PercentComplete            += RestoreOnPercentComplete;

            try
            {
                Server.ConnectionContext.ExecuteNonQuery("use master;");
                restore.SqlRestore(Server);
            }
            finally
            {
                restore.PercentComplete -= RestoreOnPercentComplete;
            }
        }