// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Diagnostics; using System.Globalization; using System.Text; using System.Threading; using Microsoft.SqlTools.ServiceLayer.Management; using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; using Microsoft.SqlTools.ServiceLayer.TaskServices; using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment { /// /// Generates a script storing SQL Assessment results to a table. /// internal sealed class GenerateScriptOperation : ITaskOperation, IDisposable { private readonly CancellationTokenSource cancellation = new CancellationTokenSource(); private bool disposed = false; /// /// Gets the unique id associated with this instance. /// public string OperationId { get; set; } /// /// Gets the parameters containing assessment results /// to be stored in a data table. /// public GenerateScriptParams Parameters { get; } /// /// Gets or sets the error message text /// if an error occurred during task execution /// public string ErrorMessage { get; set; } /// /// Gets or sets the sql task that's executing the operation /// public SqlTask SqlTask { get; set; } public GenerateScriptOperation(GenerateScriptParams parameters) { Validate.IsNotNull(nameof(parameters), parameters); Parameters = parameters; } /// /// Execute a task /// /// Task execution mode (e.g. script or execute) /// /// The method has been called twice in parallel for the same instance. /// public void Execute(TaskExecutionMode mode) { try { var scriptText = GenerateScript(Parameters, cancellation.Token); if (scriptText != null) { SqlTask?.AddScript(SqlTaskStatus.Succeeded, scriptText); } } catch (Exception e) { ErrorMessage = e.Message; Logger.Write(TraceEventType.Error, string.Format( CultureInfo.InvariantCulture, "SQL Assessment: generate script operation failed with exception {0}", e.Message)); throw; } } public void Cancel() { cancellation.Cancel(); } #region Helpers internal static string GenerateScript(GenerateScriptParams generateScriptParams, CancellationToken cancellationToken) { const string scriptPrologue = @"IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'AssessmentResult')) BEGIN CREATE TABLE [dbo].[AssessmentResult]( [CheckName] [nvarchar](max) NOT NULL, [CheckId] [nvarchar](max) NOT NULL, [RulesetName] [nvarchar](max) NOT NULL, [RulesetVersion] [nvarchar](max) NOT NULL, [Severity] [nvarchar](max) NOT NULL, [Message] [nvarchar](max) NOT NULL, [TargetPath] [nvarchar](max) NOT NULL, [TargetType] [nvarchar](max) NOT NULL, [HelpLink] [nvarchar](max) NOT NULL, [Timestamp] [datetimeoffset](7) NOT NULL ) END GO INSERT INTO [dbo].[AssessmentResult] ([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp]) VALUES"; var sb = new StringBuilder(); if (generateScriptParams.Items != null) { sb.Append(scriptPrologue); foreach (var item in generateScriptParams.Items) { cancellationToken.ThrowIfCancellationRequested(); if (item.Kind == AssessmentResultItemKind.Note) { sb.Append( $"\r\n('{CUtils.EscapeStringSQuote(item.DisplayName)}','{CUtils.EscapeStringSQuote(item.CheckId)}','{CUtils.EscapeStringSQuote(item.RulesetName)}','{item.RulesetVersion}','{item.Level}','{CUtils.EscapeStringSQuote(item.Message)}','{CUtils.EscapeStringSQuote(item.TargetName)}','{item.TargetType}','{CUtils.EscapeStringSQuote(item.HelpLink)}','{item.Timestamp:yyyy-MM-dd hh:mm:ss.fff zzz}'),"); } } sb.Length -= 1; } return sb.ToString(); } #endregion #region IDisposable public void Dispose() { if (!disposed) { Cancel(); cancellation.Dispose(); disposed = true; } } #endregion } }