/// <summary> /// Sets restore plan properties /// </summary> private void SetRestorePlanProperties(RestorePlan rp) { if (rp == null || rp.RestoreOperations.Count < 1) { return; } rp.SetRestoreOptions(this.RestoreOptions); rp.CloseExistingConnections = this.CloseExistingConnections; if (this.targetDbName != null && !this.targetDbName.Equals(string.Empty)) { rp.DatabaseName = targetDbName; } rp.RestoreOperations[0].RelocateFiles.Clear(); foreach (DbFile dbFile in this.DbFiles) { // For XStore path, we don't want to try the getFullPath. string newPhysicalPath; Uri pathUri; bool uriCreated = Uri.TryCreate(dbFile.PhysicalNameRelocate, UriKind.Absolute, out pathUri); if (uriCreated && pathUri.Scheme == "https") { newPhysicalPath = dbFile.PhysicalNameRelocate; } else { newPhysicalPath = Path.GetFullPath(dbFile.PhysicalNameRelocate); } if (!dbFile.PhysicalName.Equals(newPhysicalPath)) { RelocateFile relocFile = new RelocateFile(dbFile.LogicalName, dbFile.PhysicalNameRelocate); rp.RestoreOperations[0].RelocateFiles.Add(relocFile); } } }
internal RestorePlan CreateRestorePlan(DatabaseRestorePlanner planner, RestoreOptions restoreOptions) { this.CreateOrUpdateRestorePlanException = null; RestorePlan ret = null; try { ret = planner.CreateRestorePlan(restoreOptions); if (ret == null || ret.RestoreOperations.Count == 0) { this.ActiveException = planner.GetBackupDeviceReadErrors(); } } catch (Exception ex) { this.ActiveException = ex; this.CreateOrUpdateRestorePlanException = this.ActiveException; } finally { } return(ret); }
public void CreateNewRestorePlan() { this.ActiveException = null; //Clear any existing exceptions as the plan is getting recreated. //Clear any existing exceptions as new plan is getting recreated. this.CreateOrUpdateRestorePlanException = null; this.DbFiles.Clear(); this.PlanUpdateRequired = false; this.restorePlan = null; if (String.IsNullOrEmpty(this.RestorePlanner.DatabaseName)) { this.RestorePlan = new RestorePlan(this.Server); this.Util.AddCredentialNameForUrlBackupSet(this.RestorePlan, this.CredentialName); } else { this.RestorePlan = this.CreateRestorePlan(this.RestorePlanner, this.RestoreOptions); this.Util.AddCredentialNameForUrlBackupSet(this.restorePlan, this.CredentialName); if (this.ActiveException == null) { this.dbFiles = this.GetDbFiles(); UpdateDBFilesPhysicalRelocate(); } } if (this.restorePlan == null) { this.RestorePlan = new RestorePlan(this.Server); this.Util.AddCredentialNameForUrlBackupSet(this.RestorePlan, this.CredentialName); } }
/// <summary> /// Gets RestorePlan to perform restore and to script /// </summary> public RestorePlan GetRestorePlanForExecutionAndScript() { this.ActiveException = null; //Clear any existing exceptions as the plan is getting recreated. //Clear any existing exceptions as new plan is getting recreated. this.CreateOrUpdateRestorePlanException = null; bool tailLogBackup = this.RestorePlanner.BackupTailLog; if (this.planUpdateRequired) { this.RestorePlan = this.RestorePlanner.CreateRestorePlan(this.RestoreOptions); this.UpdateRestoreSelected(); this.Util.AddCredentialNameForUrlBackupSet(this.RestorePlan, this.CredentialName); } RestorePlan rp = new RestorePlan(this.Server); rp.RestoreAction = RestoreActionType.Database; if (this.RestorePlan != null) { if (this.RestorePlan.TailLogBackupOperation != null && tailLogBackup) { rp.TailLogBackupOperation = this.RestorePlan.TailLogBackupOperation; } int i = 0; foreach (Restore res in this.RestorePlan.RestoreOperations) { if (this.RestoreSelected[i] == true) { rp.RestoreOperations.Add(res); } i++; } } this.SetRestorePlanProperties(rp); return(rp); }
/// <summary> /// Set credential name in the restore objects which have a backup set in Microsoft Azure /// From sql16, default credential is SAS credential so no explict credential needed for restore object. /// </summary> /// <param name="restorePlan">Restore plan created for the restore operation</param> /// <param name="credentialName">Sql server credential name</param> public void AddCredentialNameForUrlBackupSet(RestorePlan restorePlan, string credentialName) { if (string.IsNullOrEmpty(credentialName) || restorePlan == null || restorePlan.RestoreOperations == null) { return; } if (restorePlan.Server.VersionMajor >= 13) // for sql16, default backup/restore URL will use SAS { return; } // If any of the backup media in the restore object is in URL, we assign the credential name to the CredentialName property of the Restore object foreach (Restore res in restorePlan.RestoreOperations) { if (res.BackupSet != null && res.BackupSet.BackupMediaSet != null && res.BackupSet.BackupMediaSet.BackupMediaList != null) { foreach (BackupMedia bkMedia in res.BackupSet.BackupMediaSet.BackupMediaList) { if (bkMedia != null && bkMedia.MediaType == DeviceType.Url) { res.CredentialName = credentialName; break; } } } if (res.Devices != null) { foreach (BackupDeviceItem bkDevice in res.Devices) { if (bkDevice.DeviceType == DeviceType.Url) { res.CredentialName = credentialName; break; } } } } // If the backup file to which the tail log is going to be backed up is a file in Microsoft Azure, // we assign the credential name to the Credential Name property of the Backup object if (restorePlan.TailLogBackupOperation != null && restorePlan.TailLogBackupOperation.Devices != null) { foreach (BackupDeviceItem bkdevItem in restorePlan.TailLogBackupOperation.Devices) { if (bkdevItem != null && bkdevItem.DeviceType == DeviceType.Url) { restorePlan.TailLogBackupOperation.CredentialName = credentialName; break; } } } }
/// <summary> /// Returns a default location for tail log backup /// If the first backup media is from Microsoft Azure, a Microsoft Azure url for the Tail log backup file is returned /// </summary> internal string GetDefaultTailLogbackupFile(string databaseName, RestorePlan restorePlan) { if (string.IsNullOrEmpty(databaseName) || restorePlan == null) { return(string.Empty); } if (restorePlan.TailLogBackupOperation != null && restorePlan.TailLogBackupOperation.Devices != null) { restorePlan.TailLogBackupOperation.Devices.Clear(); } string folderpath = string.Empty; BackupMedia firstBackupMedia = this.GetFirstBackupMedia(restorePlan); string filename = this.SanitizeFileName(databaseName) + "_LogBackup_" + this.GetServerCurrentDateTime().ToString("yyyy-MM-dd_HH-mm-ss") + ".bak"; if (firstBackupMedia != null && firstBackupMedia.MediaType == DeviceType.Url) { // the uri will use the same container as the container of the first backup media Uri uri; if (Uri.TryCreate(firstBackupMedia.MediaName, UriKind.Absolute, out uri)) { UriBuilder uriBuilder = new UriBuilder(); uriBuilder.Scheme = uri.Scheme; uriBuilder.Host = uri.Host; if (uri.AbsolutePath.Length > 0) { string[] parts = uri.AbsolutePath.Split('/'); string newPath = string.Join("/", parts, 0, parts.Length - 1); if (newPath.EndsWith("/")) { newPath = newPath.Substring(0, newPath.Length - 1); } uriBuilder.Host = uriBuilder.Host + newPath; } uriBuilder.Path = filename; string urlFilename = uriBuilder.Uri.AbsoluteUri; if (restorePlan.TailLogBackupOperation != null && restorePlan.TailLogBackupOperation.Devices != null) { restorePlan.TailLogBackupOperation.Devices.Add(new BackupDeviceItem(urlFilename, DeviceType.Url)); } return(urlFilename); } } folderpath = this.GetDefaultBackupFolder(); if (restorePlan.TailLogBackupOperation != null && restorePlan.TailLogBackupOperation.Devices != null) { restorePlan.TailLogBackupOperation.Devices.Add(new BackupDeviceItem(PathWrapper.Combine(folderpath, filename), DeviceType.File)); } return(PathWrapper.Combine(folderpath, filename)); }
//TODO: the code is moved from ssms and used for other typs of restore operation //Uncomment when restore operation for those types are supported /* * internal void MarkDuplicateSuspectPages(List<SuspectPageTaskDataObject> suspectPageObjList) * { * List<SuspectPageTaskDataObject> newList = new List<SuspectPageTaskDataObject>(suspectPageObjList); * newList.Sort(); * newList[0].IsDuplicate = false; * for (int i = 1; i < newList.Count; i++) * { * if (newList[i].CompareTo(newList[i - 1]) == 0) * { * newList[i].IsDuplicate = true; * newList[i - 1].IsDuplicate = true; * } * else * { * newList[i].IsDuplicate = false; * } * } * } */ /* * internal void VerifyChecksumWorker(RestorePlan plan, IBackgroundOperationContext backgroundContext, EventHandler cancelEventHandler) * { * if (plan == null || plan.RestoreOperations.Count() == 0) * { * return; * } * backgroundContext.IsCancelable = true; * backgroundContext.CancelRequested += cancelEventHandler; * try * { * foreach (Restore res in plan.RestoreOperations) * { * if (!backgroundContext.IsCancelRequested && res.backupSet != null) * { * StringBuilder bkMediaNames = new StringBuilder(); * foreach (BackupDeviceItem item in res.Devices) * { * backgroundContext.Status = SR.Verifying + ":" + item.Name; * try * { * // Use the Restore public API to do the Restore VerifyOnly query * Restore restore = new Restore(); * restore.CredentialName = res.CredentialName; * restore.Devices.Add(item); * if (!res.SqlVerify(this.server)) * { * throw new Exception(SR.BackupDeviceItemVerificationFailed(item.Name)); * } * } * catch (Exception ex) * { * throw new Exception(SR.BackupDeviceItemVerificationFailed(item.Name), ex); * } * } * } * } * } * finally * { * backgroundContext.CancelRequested -= cancelEventHandler; * } * } */ private BackupMedia GetFirstBackupMedia(RestorePlan restorePlan) { /* * if (restorePlan == null || restorePlan.RestoreOperations == null || restorePlan.RestoreOperations.Count == 0) * { * return null; * } * Restore res = restorePlan.RestoreOperations[0]; * if (res == null || res.backupSet == null || res.backupSet.backupMediaSet == null || res.backupSet.backupMediaSet.BackupMediaList == null || res.backupSet.backupMediaSet.BackupMediaList.ToList().Count == 0) * { * return null; * } * return res.backupSet.backupMediaSet.BackupMediaList.ToList()[0]; */ return(null); }
/// <summary> /// Updates restore plan /// </summary> public void UpdateRestorePlan(bool relocateAllFiles = false) { this.ActiveException = null; //Clear any existing exceptions as the plan is getting recreated. //Clear any existing exceptions as new plan is getting recreated. this.CreateOrUpdateRestorePlanException = null; this.DbFiles.Clear(); this.planUpdateRequired = false; this.restorePlan = null; if (String.IsNullOrEmpty(this.RestorePlanner.DatabaseName)) { this.RestorePlan = new RestorePlan(this.Server); this.Util.AddCredentialNameForUrlBackupSet(this.RestorePlan, this.CredentialName); } else { this.RestorePlan = this.CreateRestorePlan(this.RestorePlanner, this.RestoreOptions); this.Util.AddCredentialNameForUrlBackupSet(this.restorePlan, this.CredentialName); if (this.ActiveException == null) { this.dbFiles = this.GetDbFiles(); UpdateDBFilesPhysicalRelocate(); if (relocateAllFiles) { UpdateDbFiles(); } this.SetRestorePlanProperties(this.restorePlan); } } if (this.restorePlan != null) { this.RestoreSelected = new bool[this.restorePlan.RestoreOperations.Count]; for (int i = 0; i < this.restorePlan.RestoreOperations.Count; i++) { this.RestoreSelected[i] = true; } } else { this.RestorePlan = new RestorePlan(this.Server); this.Util.AddCredentialNameForUrlBackupSet(this.RestorePlan, this.CredentialName); this.RestoreSelected = new bool[0]; } }
/// <summary> /// Gets RestorePlan to perform restore and to script /// </summary> public RestorePlan GetRestorePlanForExecutionAndScript() { // One of the reasons the existing plan is not used and a new plan is created to execute is the order of the backupsets and // the last backupset is important. User can choose specific backupset in the plan to restore and // if the last backup set is not selected from the list, it means the item before that should be the real last item // with simply removing the item from the list, the order won't work correctlly. Smo considered the last item removed and // doesn't consider the previous item as the last item. Creating a new list and adding backpsets is the only way to make it work // and the reason the last backup set is important is that it has to restore with recovery mode while the others are restored with no recovery this.ActiveException = null; //Clear any existing exceptions as the plan is getting recreated. //Clear any existing exceptions as new plan is getting recreated. this.CreateOrUpdateRestorePlanException = null; bool tailLogBackup = this.RestorePlanner.BackupTailLog; if (this.PlanUpdateRequired) { this.RestorePlan = this.RestorePlanner.CreateRestorePlan(this.RestoreOptions); this.Util.AddCredentialNameForUrlBackupSet(this.RestorePlan, this.CredentialName); } RestorePlan rp = new RestorePlan(this.Server); rp.RestoreAction = RestoreActionType.Database; if (this.RestorePlan != null) { if (this.RestorePlan.TailLogBackupOperation != null && tailLogBackup) { rp.TailLogBackupOperation = this.RestorePlan.TailLogBackupOperation; } int i = 0; foreach (Restore res in this.RestorePlan.RestoreOperations) { if (this.backupSetsFilterInfo.IsBackupSetSelected(res.BackupSet)) { rp.RestoreOperations.Add(res); } i++; } } this.SetRestorePlanProperties(rp); RestorePlanToExecute = rp; return(rp); }
/// <summary> /// Executes the restore operations /// </summary> public void Execute() { RestorePlan restorePlan = GetRestorePlanForExecutionAndScript(); // ssms creates a new restore plan by calling GetRestorePlanForExecutionAndScript and // Doens't use the plan already created here. not sure why, using the existing restore plan doesn't make // any issue so far so keeping in it for now but we might want to double check later if (restorePlan != null && restorePlan.RestoreOperations.Count > 0) { restorePlan.PercentComplete += (object sender, PercentCompleteEventArgs e) => { if (SqlTask != null) { SqlTask.AddMessage($"{e.Percent}%", SqlTaskStatus.InProgress); } }; restorePlan.Execute(); } }
/// <summary> /// Executes the restore operations /// </summary> public override void Execute() { if (IsValid && RestorePlan.RestoreOperations != null && RestorePlan.RestoreOperations.Any()) { // Restore Plan should be already created and updated at this point RestorePlan restorePlan = GetRestorePlanForExecutionAndScript(); if (restorePlan != null && restorePlan.RestoreOperations.Count > 0) { restorePlan.PercentComplete += (object sender, PercentCompleteEventArgs e) => { OnMessageAdded(new TaskMessage { Description = $"{e.Percent}%", Status = SqlTaskStatus.InProgress }); }; restorePlan.Execute(); } } else { throw new InvalidOperationException(SR.RestoreNotSupported); } }
public void ToggleSelectRestore(int index) { RestorePlan rp = this.restorePlan; if (rp == null || rp.RestoreOperations.Count <= index) { return; } //the last index - this will include tail-Log restore operation if present if (index == rp.RestoreOperations.Count - 1) { if (this.RestoreSelected[index]) { this.RestoreSelected[index] = false; } else { for (int i = 0; i <= index; i++) { this.RestoreSelected[i] = true; } } return; } if (index == 0) { if (!this.RestoreSelected[index]) { this.RestoreSelected[index] = true; } else { for (int i = index; i < rp.RestoreOperations.Count; i++) { this.RestoreSelected[i] = false; } } return; } if (index == 1 && rp.RestoreOperations[index].BackupSet.BackupSetType == BackupSetType.Differential) { if (!this.RestoreSelected[index]) { this.RestoreSelected[0] = true; this.RestoreSelected[index] = true; } else if (rp.RestoreOperations[2].BackupSet == null) { this.RestoreSelected[index] = false; this.RestoreSelected[2] = false; } else if (this.Server.Version.Major < 9 || BackupSet.IsBackupSetsInSequence(rp.RestoreOperations[0].BackupSet, rp.RestoreOperations[2].BackupSet)) { this.RestoreSelected[index] = false; } else { for (int i = index; i < rp.RestoreOperations.Count; i++) { this.RestoreSelected[i] = false; } } return; } if (rp.RestoreOperations[index].BackupSet.BackupSetType == BackupSetType.Log) { if (this.RestoreSelected[index]) { for (int i = index; i < rp.RestoreOperations.Count; i++) { this.RestoreSelected[i] = false; } return; } else { for (int i = 0; i <= index; i++) { this.RestoreSelected[i] = true; } return; } } }
public DefaultRestoreScanner(RestorePlan plan, CancellationToken cancellationToken) { CancellationToken = cancellationToken; Plan = plan; }