diff --git a/src/SqlBackupTools/Notification/SlackSend.cs b/src/SqlBackupTools/Notification/SlackSend.cs index 44252a8..05d7398 100644 --- a/src/SqlBackupTools/Notification/SlackSend.cs +++ b/src/SqlBackupTools/Notification/SlackSend.cs @@ -212,6 +212,22 @@ public async Task ReportAsync(ReportState state, string slackChannel, string sla }); } + if (state.IntegrityErrors.Any()) + { + subMsg.Attachments.Add(new Attachment + { + Color = AlertLevel.Error.ToSlackColor(), + Title = $"DBCC CHECKDB Error :", + Fields = state.IntegrityErrors + .Select(i => new Field + { + Title = i.Key + " :", + Value = string.Join(Environment.NewLine, i.Value), + Short = false + }).ToArray() + }); + } + await _slackClient.SendSlackMessageAsync(subMsg, slackSecret); } diff --git a/src/SqlBackupTools/Restore/DbccCheckDbResult.cs b/src/SqlBackupTools/Restore/DbccCheckDbResult.cs new file mode 100644 index 0000000..e11af97 --- /dev/null +++ b/src/SqlBackupTools/Restore/DbccCheckDbResult.cs @@ -0,0 +1,29 @@ +namespace SqlBackupTools.Restore +{ + public class DbccCheckDbResult + { + public int Error { get; set; } + public int Level { get; set; } + public int State { get; set; } + public string MessageText { get; set; } + public int RepairLevel { get; set; } + public int Status { get; set; } + public int DbId { get; set; } + public int DbFragId { get; set; } + public int ObjectId { get; set; } + public int IndexId { get; set; } + public int PartitionID { get; set; } + public int AllocUnitID { get; set; } + public int RidDbId { get; set; } + public int RidPruId { get; set; } + public int File { get; set; } + public int Page { get; set; } + public int Slot { get; set; } + public int RefDbId { get; set; } + public int RefPruId { get; set; } + public int RefFile { get; set; } + public int RefPage { get; set; } + public int RefSlot { get; set; } + public int Allocation { get; set; } + } +} diff --git a/src/SqlBackupTools/Restore/RestoreCommand.cs b/src/SqlBackupTools/Restore/RestoreCommand.cs index bacacca..00f667e 100644 --- a/src/SqlBackupTools/Restore/RestoreCommand.cs +++ b/src/SqlBackupTools/Restore/RestoreCommand.cs @@ -111,5 +111,7 @@ public bool IsUncheckedModeEnable } } + [Option("checkDb", HelpText = "runs DBCC CHECKDB")] + public bool CheckDb { get; set; } } } diff --git a/src/SqlBackupTools/Restore/RestoreRunner.cs b/src/SqlBackupTools/Restore/RestoreRunner.cs index db04469..4f46396 100644 --- a/src/SqlBackupTools/Restore/RestoreRunner.cs +++ b/src/SqlBackupTools/Restore/RestoreRunner.cs @@ -98,6 +98,8 @@ private static async Task RestoreBackupAsync(IRestoreMethod restore try { + Exception scriptException = null; + if (restoreCommand.RunRecovery) { var recoveryException = await restoreMethod.RunRecoveryAsync(item); @@ -107,13 +109,10 @@ private static async Task RestoreBackupAsync(IRestoreMethod restore item.SetError(recoveryException); throw recoveryException; } - } - Exception scriptException = null; - if (restoreCommand.RunRecovery) - { - await using var sqlConnectionOnDatabase = restoreCommand.CreateConnectionMars(item.Name); - scriptException = await PostScriptExecuteAsync(state, sqlConnectionOnDatabase, item); + scriptException = await PostScriptExecuteAsync(state, item); + + await RunDbccCheckDb(state, item); } if (scriptException != null) @@ -159,6 +158,33 @@ private static async Task RestoreBackupAsync(IRestoreMethod restore return item; } + private static async Task RunDbccCheckDb(RestoreState state, RestoreItem item) + { + if (state.RestoreCommand.CheckDb == false) + { + return; + } + + await using var sqlConnectionOnDatabase = state.RestoreCommand.CreateConnectionMars(item.Name); + try + { + + var checkdbResults = await sqlConnectionOnDatabase.QueryAsync("DBCC CHECKDB with TABLERESULTS", commandTimeout: state.RestoreCommand.Timeout); + + foreach (var r in checkdbResults.Where(i => i.Level >= 17)) + { + state.Loggger.Fatal($"[{item.Name}] Error found while DBCC CHECKDB : " + r.MessageText); + var errors = state.IntegrityErrors.GetValueOrDefault(item.Name, new List()); + errors.Add(r.MessageText); + } + } + catch (Exception e) + { + state.Loggger.Error(e, $"[{item.Name}] Error while DBCC CHECKDB"); + return; + } + } + private static bool StartFromFull(RestoreState state, RestoreItem item) { if (!state.ActualDbs.ContainsKey(item.Name)) @@ -178,7 +204,7 @@ private static bool StartFromFull(RestoreState state, RestoreItem item) return false; } - private static async Task PostScriptExecuteAsync(RestoreState state, SqlConnection sqlConnection, RestoreItem restoreItem) + private static async Task PostScriptExecuteAsync(RestoreState state, RestoreItem restoreItem) { if (state.RestoreCommand.PostScripts == null) { @@ -203,9 +229,11 @@ private static async Task PostScriptExecuteAsync(RestoreState state, { var scripts = LoadScripts(state.RestoreCommand); state.Loggger.Debug("Applying " + scripts.Count + " sql scripts"); + await using var sqlConnectionOnDatabase = state.RestoreCommand.CreateConnectionMars(restoreItem.Name); + foreach (var script in scripts) { - await sqlConnection.ExecuteAsync(script, commandTimeout: state.RestoreCommand.Timeout); + await sqlConnectionOnDatabase.ExecuteAsync(script, commandTimeout: state.RestoreCommand.Timeout); } } catch (Exception e) diff --git a/src/SqlBackupTools/Restore/RestoreState.cs b/src/SqlBackupTools/Restore/RestoreState.cs index 12de916..84024a1 100644 --- a/src/SqlBackupTools/Restore/RestoreState.cs +++ b/src/SqlBackupTools/Restore/RestoreState.cs @@ -32,6 +32,7 @@ public class ReportState public ReportStatus Status { get; internal set; } = ReportStatus.Ok; public ServerInfos Info { get; internal set; } public string Mode { get; internal set; } + public Dictionary> IntegrityErrors { get; } = new Dictionary>(); } [Flags] @@ -70,7 +71,7 @@ public RestoreState(ILogger logger, RestoreCommand restoreCommand, SqlConnection public TimeSpan Accumulated { get; set; } public ServerInfos ServerInfos { get; set; } public List<(string name, int count, List excluded)> DuplicatesExcluded { get; } = new List<(string name, int count, List excluded)>(); - + public Dictionary> IntegrityErrors { get; } = new Dictionary>(); private readonly Stopwatch _sw = new Stopwatch(); private int _increment = 0; @@ -187,6 +188,12 @@ public async Task GetReportStateAsync() } } } + + foreach(var i in IntegrityErrors) + { + reportState.IntegrityErrors.Add(i.Key,i.Value); + } + return reportState; }