From 1ef70ef259a70657507a232a8ef3aad711ed8063 Mon Sep 17 00:00:00 2001 From: Arvind Ranasaria Date: Fri, 19 Oct 2018 13:25:18 -0700 Subject: [PATCH] Bug fix for https://github.com/Microsoft/azuredatastudio/issues/2923 and misc other fixes (#711) --- .gitignore | 1 + src/Microsoft.SqlTools.Credentials/Program.cs | 9 +- .../Utility/CommandOptions.cs | 39 +---- .../Utility/Logger.cs | 18 ++- .../Utility/CommandOptions.cs | 39 +---- .../Utility/Logger.cs | 18 ++- .../Program.cs | 6 +- .../SmoModel/SmoColumnCustomNode.cs | 138 +++++++++-------- .../SmoModel/SmoKeyCustomNode.cs | 39 +++-- .../SmoModel/SmoLoginCustomNode.cs | 21 ++- .../SmoModel/SmoTriggerCustomNode.cs | 17 +-- .../SmoModel/SmoUserCustomNode.cs | 19 +-- .../SmoModel/TreeNodeGenerator.cs | 141 ++++++++---------- .../Program.cs | 11 +- .../TestLogger.cs | 30 ++-- .../ServiceHost/LoggerTests.cs | 28 ++++ .../Utility/CommandOptionsTests.cs | 70 +++++---- .../Properties/Resources.Designer.cs | 17 ++- 18 files changed, 323 insertions(+), 338 deletions(-) diff --git a/.gitignore b/.gitignore index c7a5b1b7..0c4dc1bf 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ project.lock.json *.sln.docstates *.exe scratch.txt +launchSettings.json # mergetool conflict files *.orig diff --git a/src/Microsoft.SqlTools.Credentials/Program.cs b/src/Microsoft.SqlTools.Credentials/Program.cs index 2a72f3bf..359f09f0 100644 --- a/src/Microsoft.SqlTools.Credentials/Program.cs +++ b/src/Microsoft.SqlTools.Credentials/Program.cs @@ -36,15 +36,12 @@ namespace Microsoft.SqlTools.Credentials string logFilePath = commandOptions.LogFilePath; if (string.IsNullOrWhiteSpace(logFilePath)) { - logFilePath = "credentials"; - } - if (!string.IsNullOrWhiteSpace(commandOptions.LoggingDirectory)) - { - logFilePath = Logger.GenerateLogFilePath(Path.Combine(commandOptions.LoggingDirectory, logFilePath)); + logFilePath = Logger.GenerateLogFilePath("credentials"); } + Logger.AutoFlush = commandOptions.AutoFlushLog; + Logger.Initialize(tracingLevel: commandOptions.TracingLevel, logFilePath: logFilePath, traceSource: "credentials"); - Logger.Write(TraceEventType.Information, "Starting SqlTools Credentials Provider"); // set up the host details and profile paths var hostDetails = new HostDetails( diff --git a/src/Microsoft.SqlTools.Hosting.v2/Utility/CommandOptions.cs b/src/Microsoft.SqlTools.Hosting.v2/Utility/CommandOptions.cs index 364f0c6a..cdfdb568 100644 --- a/src/Microsoft.SqlTools.Hosting.v2/Utility/CommandOptions.cs +++ b/src/Microsoft.SqlTools.Hosting.v2/Utility/CommandOptions.cs @@ -28,19 +28,12 @@ namespace Microsoft.SqlTools.Hosting.Utility ErrorMessage = string.Empty; Locale = string.Empty; - //set default log directory - LoggingDirectory = DefaultLogRoot; - if (!string.IsNullOrWhiteSpace(ServiceName)) - { - LoggingDirectory = Path.Combine(LoggingDirectory, ServiceName); - } - try { for (int i = 0; i < args.Length; ++i) { string arg = args[i]; - if (arg.StartsWith("--") || arg.StartsWith("-")) + if (arg != null && (arg.StartsWith("--") || arg.StartsWith("-"))) { // Extracting arguments and properties arg = arg.Substring(1).ToLowerInvariant(); @@ -48,17 +41,15 @@ namespace Microsoft.SqlTools.Hosting.Utility switch (argName) { - case "-enable-logging": - break; //ignore this old option for now for backward compat with older opsstudio code - to be removed in a future checkin + case "-autoflush-log": + AutoFlushLog = true; + break; case "-tracing-level": TracingLevel = args[++i]; break; case "-log-file": LogFilePath = args[++i]; break; - case "-log-dir": - SetLoggingDirectory(args[++i]); - break; case "-locale": SetLocale(args[++i]); break; @@ -93,11 +84,6 @@ namespace Microsoft.SqlTools.Hosting.Utility /// public string ErrorMessage { get; private set; } - /// - /// Gets the directory where log files are output. - /// - public string LoggingDirectory { get; private set; } - /// /// Whether the program should exit immediately. Set to true when the usage is printed. /// @@ -123,11 +109,10 @@ namespace Microsoft.SqlTools.Hosting.Utility var str = string.Format("{0}" + Environment.NewLine + ServiceName + " " + Environment.NewLine + " Options:" + Environment.NewLine + - " [--enable-logging ] (obsolete - present for backward compat. Logging is always on, except -tracing-level Off has the effect of disabling logging)" + Environment.NewLine + - " [--tracing-level **] (** can be any of: All, Off, Critical, Error, Warning, Information, Verbose. Default is Critical)" + Environment.NewLine + - " [--log-file **]" + Environment.NewLine + - " [--log-dir **] (default: %APPDATA%\\)" + Environment.NewLine + + " [--autoflush-log] (If passed in auto flushing of log files is enabled., Verbose. Default is to not auto-flush log files)" + Environment.NewLine + " [--locale **] (default: 'en')" + Environment.NewLine, + " [--log-file **]" + Environment.NewLine + + " [--tracing-level **] (** can be any of: All, Off, Critical, Error, Warning, Information, Verbose. Default is Critical)" + Environment.NewLine + " [--help]" + Environment.NewLine + ErrorMessage); return str; @@ -138,15 +123,7 @@ namespace Microsoft.SqlTools.Hosting.Utility public string LogFilePath { get; private set; } - private void SetLoggingDirectory(string loggingDirectory) - { - if (string.IsNullOrWhiteSpace(loggingDirectory)) - { - return; - } - - this.LoggingDirectory = Path.GetFullPath(loggingDirectory); - } + public bool AutoFlushLog { get; private set; } = false; public virtual void SetLocale(string locale) { diff --git a/src/Microsoft.SqlTools.Hosting.v2/Utility/Logger.cs b/src/Microsoft.SqlTools.Hosting.v2/Utility/Logger.cs index 47ba6877..8480f283 100644 --- a/src/Microsoft.SqlTools.Hosting.v2/Utility/Logger.cs +++ b/src/Microsoft.SqlTools.Hosting.v2/Utility/Logger.cs @@ -86,7 +86,7 @@ namespace Microsoft.SqlTools.Hosting.Utility get => tracingLevel; set { - // configures the source level filter. This alone is not enough for tracing that is done via "Trace" object instead of "TraceSource" object + // configures the source level filter. This alone is not enough for tracing that is done via "Trace" class instead of "TraceSource" object TraceSource.Switch = new SourceSwitch(TraceSource.Name, value.ToString()); // configure the listener level filter tracingLevel = value; @@ -94,6 +94,8 @@ namespace Microsoft.SqlTools.Hosting.Utility } } + public static bool AutoFlush { get; set; } = false; + /// /// Initializes the Logger for the current process. /// @@ -108,9 +110,11 @@ namespace Microsoft.SqlTools.Hosting.Utility public static void Initialize( SourceLevels tracingLevel = defaultTracingLevel, string logFilePath = null, - string traceSource = defaultTraceSource) + string traceSource = defaultTraceSource, + bool autoFlush = false) { Logger.tracingLevel = tracingLevel; + Logger.AutoFlush = autoFlush; TraceSource = new TraceSource(traceSource, Logger.tracingLevel); if (string.IsNullOrWhiteSpace(logFilePath)) { @@ -118,7 +122,7 @@ namespace Microsoft.SqlTools.Hosting.Utility } LogFileFullPath = logFilePath; - Write(TraceEventType.Information, $"Initialized the {traceSource} logger"); + Write(TraceEventType.Information, $"Initialized the {traceSource} logger. Log file is: {LogFileFullPath}"); } /// @@ -185,7 +189,7 @@ namespace Microsoft.SqlTools.Hosting.Utility } // make the log path unique - return $"{logFilePrefix}_{DateTime.Now.Year,4:D4}{DateTime.Now.Month,2:D2}{DateTime.Now.Day,2:D2}{DateTime.Now.Hour,2:D2}{DateTime.Now.Minute,2:D2}{DateTime.Now.Second,2:D2}{uniqueId}.log"; + return $"{logFilePrefix}_{DateTime.Now.Year,4:D4}{DateTime.Now.Month,2:D2}{DateTime.Now.Day,2:D2}{DateTime.Now.Hour,2:D2}{DateTime.Now.Minute,2:D2}{DateTime.Now.Second,2:D2}_{uniqueId}.log"; } private static void ConfigureListener() @@ -290,6 +294,10 @@ namespace Microsoft.SqlTools.Hosting.Utility break; } } + if (AutoFlush) + { + Flush(); + } } } @@ -431,7 +439,7 @@ namespace Microsoft.SqlTools.Hosting.Utility return message; } - return $"{(IsEnabled(TraceOptions.DateTime) ? string.Format(CultureInfo.InvariantCulture, "{0} ", eventCache.DateTime.ToLocalTime().ToString("u", CultureInfo.InvariantCulture)) : string.Empty)}" + return $"{(IsEnabled(TraceOptions.DateTime) ? string.Format(CultureInfo.InvariantCulture, "{0} ", eventCache.DateTime.ToLocalTime().ToString("yy-MM-dd H:mm:ss.fffffff", CultureInfo.InvariantCulture)) : string.Empty)}" + $"{(IsEnabled(TraceOptions.ProcessId) ? string.Format(CultureInfo.InvariantCulture, "pid:{0} ", eventCache.ProcessId.ToString(CultureInfo.InvariantCulture)) : string.Empty)}" + $"{(IsEnabled(TraceOptions.ThreadId) ? string.Format(CultureInfo.InvariantCulture, "tid:{0} ", eventCache.ThreadId.ToString(CultureInfo.InvariantCulture)) : string.Empty)}" + message; diff --git a/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs b/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs index 364f0c6a..cdfdb568 100644 --- a/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs +++ b/src/Microsoft.SqlTools.Hosting/Utility/CommandOptions.cs @@ -28,19 +28,12 @@ namespace Microsoft.SqlTools.Hosting.Utility ErrorMessage = string.Empty; Locale = string.Empty; - //set default log directory - LoggingDirectory = DefaultLogRoot; - if (!string.IsNullOrWhiteSpace(ServiceName)) - { - LoggingDirectory = Path.Combine(LoggingDirectory, ServiceName); - } - try { for (int i = 0; i < args.Length; ++i) { string arg = args[i]; - if (arg.StartsWith("--") || arg.StartsWith("-")) + if (arg != null && (arg.StartsWith("--") || arg.StartsWith("-"))) { // Extracting arguments and properties arg = arg.Substring(1).ToLowerInvariant(); @@ -48,17 +41,15 @@ namespace Microsoft.SqlTools.Hosting.Utility switch (argName) { - case "-enable-logging": - break; //ignore this old option for now for backward compat with older opsstudio code - to be removed in a future checkin + case "-autoflush-log": + AutoFlushLog = true; + break; case "-tracing-level": TracingLevel = args[++i]; break; case "-log-file": LogFilePath = args[++i]; break; - case "-log-dir": - SetLoggingDirectory(args[++i]); - break; case "-locale": SetLocale(args[++i]); break; @@ -93,11 +84,6 @@ namespace Microsoft.SqlTools.Hosting.Utility /// public string ErrorMessage { get; private set; } - /// - /// Gets the directory where log files are output. - /// - public string LoggingDirectory { get; private set; } - /// /// Whether the program should exit immediately. Set to true when the usage is printed. /// @@ -123,11 +109,10 @@ namespace Microsoft.SqlTools.Hosting.Utility var str = string.Format("{0}" + Environment.NewLine + ServiceName + " " + Environment.NewLine + " Options:" + Environment.NewLine + - " [--enable-logging ] (obsolete - present for backward compat. Logging is always on, except -tracing-level Off has the effect of disabling logging)" + Environment.NewLine + - " [--tracing-level **] (** can be any of: All, Off, Critical, Error, Warning, Information, Verbose. Default is Critical)" + Environment.NewLine + - " [--log-file **]" + Environment.NewLine + - " [--log-dir **] (default: %APPDATA%\\)" + Environment.NewLine + + " [--autoflush-log] (If passed in auto flushing of log files is enabled., Verbose. Default is to not auto-flush log files)" + Environment.NewLine + " [--locale **] (default: 'en')" + Environment.NewLine, + " [--log-file **]" + Environment.NewLine + + " [--tracing-level **] (** can be any of: All, Off, Critical, Error, Warning, Information, Verbose. Default is Critical)" + Environment.NewLine + " [--help]" + Environment.NewLine + ErrorMessage); return str; @@ -138,15 +123,7 @@ namespace Microsoft.SqlTools.Hosting.Utility public string LogFilePath { get; private set; } - private void SetLoggingDirectory(string loggingDirectory) - { - if (string.IsNullOrWhiteSpace(loggingDirectory)) - { - return; - } - - this.LoggingDirectory = Path.GetFullPath(loggingDirectory); - } + public bool AutoFlushLog { get; private set; } = false; public virtual void SetLocale(string locale) { diff --git a/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs b/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs index 843e8436..69d7e34e 100644 --- a/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs +++ b/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs @@ -86,7 +86,7 @@ namespace Microsoft.SqlTools.Utility get => tracingLevel; set { - // configures the source level filter. This alone is not enough for tracing that is done via "Trace" object instead of "TraceSource" object + // configures the source level filter. This alone is not enough for tracing that is done via "Trace" class instead of "TraceSource" object TraceSource.Switch = new SourceSwitch(TraceSource.Name, value.ToString()); // configure the listener level filter tracingLevel = value; @@ -94,6 +94,8 @@ namespace Microsoft.SqlTools.Utility } } + public static bool AutoFlush { get; set; } = false; + /// /// Initializes the Logger for the current process. /// @@ -108,9 +110,11 @@ namespace Microsoft.SqlTools.Utility public static void Initialize( SourceLevels tracingLevel = defaultTracingLevel, string logFilePath = null, - string traceSource = defaultTraceSource) + string traceSource = defaultTraceSource, + bool autoFlush = false) { Logger.tracingLevel = tracingLevel; + Logger.AutoFlush = autoFlush; TraceSource = new TraceSource(traceSource, Logger.tracingLevel); if (string.IsNullOrWhiteSpace(logFilePath)) { @@ -118,7 +122,7 @@ namespace Microsoft.SqlTools.Utility } LogFileFullPath = logFilePath; - Write(TraceEventType.Information, $"Initialized the {traceSource} logger"); + Write(TraceEventType.Information, $"Initialized the {traceSource} logger. Log file is: {LogFileFullPath}"); } /// @@ -185,7 +189,7 @@ namespace Microsoft.SqlTools.Utility } // make the log path unique - return $"{logFilePrefix}_{DateTime.Now.Year,4:D4}{DateTime.Now.Month,2:D2}{DateTime.Now.Day,2:D2}{DateTime.Now.Hour,2:D2}{DateTime.Now.Minute,2:D2}{DateTime.Now.Second,2:D2}{uniqueId}.log"; + return $"{logFilePrefix}_{DateTime.Now.Year,4:D4}{DateTime.Now.Month,2:D2}{DateTime.Now.Day,2:D2}{DateTime.Now.Hour,2:D2}{DateTime.Now.Minute,2:D2}{DateTime.Now.Second,2:D2}_{uniqueId}.log"; } private static void ConfigureListener() @@ -290,6 +294,10 @@ namespace Microsoft.SqlTools.Utility break; } } + if (AutoFlush) + { + Flush(); + } } } @@ -431,7 +439,7 @@ namespace Microsoft.SqlTools.Utility return message; } - return $"{(IsEnabled(TraceOptions.DateTime) ? string.Format(CultureInfo.InvariantCulture, "{0} ", eventCache.DateTime.ToLocalTime().ToString("u", CultureInfo.InvariantCulture)) : string.Empty)}" + return $"{(IsEnabled(TraceOptions.DateTime) ? string.Format(CultureInfo.InvariantCulture, "{0} ", eventCache.DateTime.ToLocalTime().ToString("yy-MM-dd H:mm:ss.fffffff", CultureInfo.InvariantCulture)) : string.Empty)}" + $"{(IsEnabled(TraceOptions.ProcessId) ? string.Format(CultureInfo.InvariantCulture, "pid:{0} ", eventCache.ProcessId.ToString(CultureInfo.InvariantCulture)) : string.Empty)}" + $"{(IsEnabled(TraceOptions.ThreadId) ? string.Format(CultureInfo.InvariantCulture, "tid:{0} ", eventCache.ThreadId.ToString(CultureInfo.InvariantCulture)) : string.Empty)}" + message; diff --git a/src/Microsoft.SqlTools.ResourceProvider/Program.cs b/src/Microsoft.SqlTools.ResourceProvider/Program.cs index d0ab5f05..7d71f6aa 100644 --- a/src/Microsoft.SqlTools.ResourceProvider/Program.cs +++ b/src/Microsoft.SqlTools.ResourceProvider/Program.cs @@ -35,11 +35,7 @@ namespace Microsoft.SqlTools.ResourceProvider string logFilePath = commandOptions.LogFilePath; if (string.IsNullOrWhiteSpace(logFilePath)) { - logFilePath = "SqlToolsResourceProviderService"; - } - if (!string.IsNullOrWhiteSpace(commandOptions.LoggingDirectory)) - { - logFilePath = Path.Combine(commandOptions.LoggingDirectory, logFilePath); + logFilePath = Logger.GenerateLogFilePath("SqlToolsResourceProviderService"); } // turn on Verbose logging during early development diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoColumnCustomNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoColumnCustomNode.cs index 0daa4ff2..e23f643c 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoColumnCustomNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoColumnCustomNode.cs @@ -23,80 +23,76 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel return SmoColumnCustomNodeHelper.CalculateCustomLabel(smoObject, smoContext); } - public override IEnumerable SmoProperties + private readonly Lazy> smoPropertiesLazy = new Lazy>(() => new List { - get + new NodeSmoProperty { - return new List - { - new NodeSmoProperty - { - Name = "Computed", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "IsColumnSet", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "Nullable", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "DataType", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "InPrimaryKey", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "IsForeignKey", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "SystemType", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "Length", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "NumericPrecision", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "NumericScale", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "XmlSchemaNamespaceSchema", - ValidFor = ValidForFlag.NotSqlDw - }, - new NodeSmoProperty - { - Name = "XmlSchemaNamespace", - ValidFor = ValidForFlag.NotSqlDw - }, - new NodeSmoProperty - { - Name = "XmlDocumentConstraint", - ValidFor = ValidForFlag.NotSqlDw - } - }; + Name = "Computed", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "IsColumnSet", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "Nullable", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "DataType", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "InPrimaryKey", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "IsForeignKey", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "SystemType", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "Length", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "NumericPrecision", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "NumericScale", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "XmlSchemaNamespaceSchema", + ValidFor = ValidForFlag.NotSqlDw + }, + new NodeSmoProperty + { + Name = "XmlSchemaNamespace", + ValidFor = ValidForFlag.NotSqlDw + }, + new NodeSmoProperty + { + Name = "XmlDocumentConstraint", + ValidFor = ValidForFlag.NotSqlDw } - } + }); + + public override IEnumerable SmoProperties => smoPropertiesLazy.Value; } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoKeyCustomNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoKeyCustomNode.cs index 34ad3f02..88806197 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoKeyCustomNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoKeyCustomNode.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; using System.Collections.Generic; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; @@ -25,30 +26,26 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel /// internal partial class IndexesChildFactory : SmoChildFactoryBase { - public override IEnumerable SmoProperties + private readonly Lazy> smoPropertiesLazy = new Lazy>(() => new List { - get + new NodeSmoProperty { - return new List - { - new NodeSmoProperty - { - Name = "IsUnique", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "IsClustered", - ValidFor = ValidForFlag.All - }, - new NodeSmoProperty - { - Name = "IndexKeyType", - ValidFor = ValidForFlag.All - } - }; + Name = "IsUnique", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "IsClustered", + ValidFor = ValidForFlag.All + }, + new NodeSmoProperty + { + Name = "IndexKeyType", + ValidFor = ValidForFlag.All } - } + }); + + public override IEnumerable SmoProperties => smoPropertiesLazy.Value; public override string GetNodeSubType(object smoObject, SmoQueryContext smoContext) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoLoginCustomNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoLoginCustomNode.cs index a11a9519..cffb14f6 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoLoginCustomNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoLoginCustomNode.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; using System.Collections.Generic; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; @@ -18,21 +19,17 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { return LoginCustomNodeHelper.GetStatus(smoObject); } - - public override IEnumerable SmoProperties + + private readonly Lazy> smoPropertiesLazy = new Lazy>(() => new List { - get + new NodeSmoProperty { - return new List - { - new NodeSmoProperty - { - Name = "IsDisabled", - ValidFor = ValidForFlag.All - } - }; + Name = "IsDisabled", + ValidFor = ValidForFlag.All } - } + }); + + public override IEnumerable SmoProperties => smoPropertiesLazy.Value; } internal static class LoginCustomNodeHelper diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoTriggerCustomNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoTriggerCustomNode.cs index 0e2c382f..ec10615f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoTriggerCustomNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoTriggerCustomNode.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; using System.Collections.Generic; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; @@ -14,27 +15,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel /// internal partial class TriggersChildFactory : SmoChildFactoryBase { - public static readonly List SmoPropertyList = new List + public static readonly Lazy> SmoPropertiesLazy = new Lazy>(() => new List { new NodeSmoProperty { Name = "IsEnabled", ValidFor = ValidForFlag.All } - }; + }); public override string GetNodeStatus(object smoObject, SmoQueryContext smoContext) { return TriggersCustomeNodeHelper.GetStatus(smoObject); } - public override IEnumerable SmoProperties - { - get - { - return TriggersChildFactory.SmoPropertyList; - } - } + public override IEnumerable SmoProperties => SmoPropertiesLazy.Value; } internal partial class ServerLevelServerTriggersChildFactory : SmoChildFactoryBase @@ -48,7 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { get { - return TriggersChildFactory.SmoPropertyList; + return TriggersChildFactory.SmoPropertiesLazy.Value; } } } @@ -64,7 +59,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { get { - return TriggersChildFactory.SmoPropertyList; + return TriggersChildFactory.SmoPropertiesLazy.Value; } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoUserCustomNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoUserCustomNode.cs index 10794e4d..1da7db12 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoUserCustomNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoUserCustomNode.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System; using System.Collections.Generic; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; @@ -19,20 +20,16 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel return UserCustomeNodeHelper.GetStatus(smoObject); } - public override IEnumerable SmoProperties + private readonly Lazy> smoPropertiesLazy = new Lazy>(() => new List { - get + new NodeSmoProperty { - return new List - { - new NodeSmoProperty - { - Name = "HasDBAccess", - ValidFor = ValidForFlag.All - } - }; + Name = "HasDBAccess", + ValidFor = ValidForFlag.All } - } + }); + + public override IEnumerable SmoProperties => smoPropertiesLazy.Value; } internal static class UserCustomeNodeHelper diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs index 039aad41..9d3614a2 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs @@ -180,34 +180,29 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { public override IEnumerable ApplicableParents() { return new[] { "Databases" }; } - public override IEnumerable Filters - { - get - { - var filters = new List(); - filters.Add(new NodeFilter - { - Property = "IsSystemObject", - Type = typeof(bool), - Values = new List { 0 }, - }); - return filters; - } - } - public override IEnumerable SmoProperties + private readonly Lazy> filtersLazy = new Lazy>(() => new List { - get - { - var properties = new List(); - properties.Add(new NodeSmoProperty - { - Name = "Status", - ValidFor = ValidForFlag.All - }); - return properties; - } - } + new NodeFilter + { + Property = "IsSystemObject", + Type = typeof(bool), + Values = new List { 0 }, + } + }); + + private readonly Lazy> smoPropertiesLazy = new Lazy>(() => new List + { + new NodeSmoProperty + { + Name = "Status", + ValidFor = ValidForFlag.All + } + }); + + public override IEnumerable Filters => filtersLazy.Value; + + public override IEnumerable SmoProperties => smoPropertiesLazy.Value; protected override void OnExpandPopulateFolders(IList currentChildren, TreeNode parent) { @@ -753,60 +748,54 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { public override IEnumerable ApplicableParents() { return new[] { "Tables" }; } - public override IEnumerable Filters + private readonly Lazy> filtersLazy = new Lazy>(() => new List { - get - { - var filters = new List(); - filters.Add(new NodeFilter + new NodeFilter + { + Property = "IsSystemObject", + Type = typeof(bool), + Values = new List { 0 }, + }, + new NodeFilter + { + Property = "TemporalType", + Type = typeof(Enum), + ValidFor = ValidForFlag.Sql2016 | ValidForFlag.Sql2017 | ValidForFlag.AzureV12, + Values = new List { - Property = "IsSystemObject", - Type = typeof(bool), - Values = new List { 0 }, - }); - filters.Add(new NodeFilter - { - Property = "TemporalType", - Type = typeof(Enum), - ValidFor = ValidForFlag.Sql2016|ValidForFlag.Sql2017|ValidForFlag.AzureV12, - Values = new List - { - { TableTemporalType.None }, - { TableTemporalType.SystemVersioned } - } - }); - return filters; - } - } + { TableTemporalType.None }, + { TableTemporalType.SystemVersioned } + } + } + }); - public override IEnumerable SmoProperties + private readonly Lazy> smoPropertiesLazy = new Lazy>(() => new List { - get - { - var properties = new List(); - properties.Add(new NodeSmoProperty - { - Name = "IsFileTable", - ValidFor = ValidForFlag.Sql2012|ValidForFlag.Sql2014|ValidForFlag.Sql2016|ValidForFlag.Sql2017 - }); - properties.Add(new NodeSmoProperty - { - Name = "IsSystemVersioned", - ValidFor = ValidForFlag.Sql2016|ValidForFlag.Sql2017|ValidForFlag.AzureV12 - }); - properties.Add(new NodeSmoProperty - { - Name = "TemporalType", - ValidFor = ValidForFlag.Sql2016|ValidForFlag.Sql2017|ValidForFlag.AzureV12 - }); - properties.Add(new NodeSmoProperty - { - Name = "IsExternal", - ValidFor = ValidForFlag.Sql2016|ValidForFlag.Sql2017|ValidForFlag.AzureV12 - }); - return properties; - } - } + new NodeSmoProperty + { + Name = "IsFileTable", + ValidFor = ValidForFlag.Sql2012 | ValidForFlag.Sql2014 | ValidForFlag.Sql2016 | ValidForFlag.Sql2017 + }, + new NodeSmoProperty + { + Name = "IsSystemVersioned", + ValidFor = ValidForFlag.Sql2016 | ValidForFlag.Sql2017 | ValidForFlag.AzureV12 + }, + new NodeSmoProperty + { + Name = "TemporalType", + ValidFor = ValidForFlag.Sql2016 | ValidForFlag.Sql2017 | ValidForFlag.AzureV12 + }, + new NodeSmoProperty + { + Name = "IsExternal", + ValidFor = ValidForFlag.Sql2016 | ValidForFlag.Sql2017 | ValidForFlag.AzureV12 + } + }); + + public override IEnumerable Filters => filtersLazy.Value; + + public override IEnumerable SmoProperties => smoPropertiesLazy.Value; protected override void OnExpandPopulateFolders(IList currentChildren, TreeNode parent) { diff --git a/src/Microsoft.SqlTools.ServiceLayer/Program.cs b/src/Microsoft.SqlTools.ServiceLayer/Program.cs index a6adb8a8..59f0582e 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Program.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Program.cs @@ -33,17 +33,12 @@ namespace Microsoft.SqlTools.ServiceLayer string logFilePath = commandOptions.LogFilePath; if (string.IsNullOrWhiteSpace(logFilePath)) { - logFilePath = "sqltools"; - } - if (!string.IsNullOrWhiteSpace(commandOptions.LoggingDirectory)) - { - logFilePath = Path.Combine(commandOptions.LoggingDirectory, logFilePath); + logFilePath = Logger.GenerateLogFilePath("sqltools"); } - // turn on Verbose logging during early development - // we need to switch to Information when preparing for public preview + Logger.AutoFlush = commandOptions.AutoFlushLog; + Logger.Initialize(tracingLevel: commandOptions.TracingLevel, logFilePath: logFilePath, traceSource: "sqltools"); - Logger.Write(TraceEventType.Information, "Starting SQL Tools Service Layer"); // set up the host details and profile paths var hostDetails = new HostDetails(version: new Version(1, 0)); diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestLogger.cs b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestLogger.cs index 6e7bebaf..904ba892 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestLogger.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestLogger.cs @@ -30,21 +30,25 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common public SourceLevels TracingLevel { get; set; } = SourceLevels.Critical; public bool DoNotUseTraceSource { get; set; } = false; + public bool AutoFlush { get; set; } = false; + private List pendingVerifications; private string testName; public string CallstackMessage { get => $"Callstack=\\s*{TopFrame}"; } + public void Close() => Logger.Close(); + public string LogFileName { get => logFileName ?? Logger.LogFileFullPath; set => logFileName = value; } public void Initialize() => - Logger.Initialize(TracingLevel, LogFilePath, TraceSource); // initialize the logger + Logger.Initialize(TracingLevel, LogFilePath, TraceSource, AutoFlush); // initialize the logger public string LogContents { get { if (logContents == null) { - Logger.Close(); + Close(); Assert.True(!string.IsNullOrWhiteSpace(LogFileName)); Assert.True(LogFileName.Length > "{TraceSource}_.log".Length); Assert.True(File.Exists(LogFileName)); @@ -70,24 +74,30 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common set => pendingVerifications = value; } - public void Write() + public void Write() => Write(LogMessage); + + public void Write(string logMessage) { // write test log if (DoNotUseTraceSource) { TraceSource savedTraceSource = Logger.TraceSource; Logger.TraceSource = null; - Logger.Write(EventType, LogMessage); + Logger.Write(EventType, logMessage); Logger.TraceSource = savedTraceSource; } else - Logger.Write(EventType, LogMessage); + { + Logger.Write(EventType, logMessage); + } } - public void WriteWithCallstack() + public void WriteWithCallstack() => WriteWithCallstack(LogMessage); + + public void WriteWithCallstack(string logMessage) { // write test log with callstack - Logger.WriteWithCallstack(EventType, LogMessage); + Logger.WriteWithCallstack(EventType, logMessage); ShouldVerifyCallstack = true; } @@ -97,7 +107,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common public void Verify(TraceEventType eventType, string message, string callstackMessage, bool shouldVerifyCallstack = false, bool expectLogMessage = true) { - Logger.Flush(); + if (!AutoFlush) + { + Logger.Flush(); + } // The Regex uses .* between the severity and the message to allow SMO to vary the content. 140 SMO has nothing there, 150 has a timestamp if (expectLogMessage) { @@ -138,7 +151,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common } } - public static void VerifyInitialization(SourceLevels expectedTracingLevel, string expectedTraceSource, string logFilePath, bool isLogFileExpectedToExist, int? testNo = null) { string FailureMessagePrefix = testNo.HasValue ? $"For Test No:{testNo.Value.ToString()}," : string.Empty; diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ServiceHost/LoggerTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ServiceHost/LoggerTests.cs index d11ce268..6a87ae78 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ServiceHost/LoggerTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ServiceHost/LoggerTests.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; +using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Test.Common; using Microsoft.SqlTools.Utility; using Xunit; @@ -333,6 +334,33 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ServiceHost TestTracingLevelChangeFromWarningToError(test); } + /// + /// Tests out AutoFlush funcitonality. The verification is two fold. + /// 1st is to verify that the setting is persistent. + /// 2nd that after a lot of log entries are written with AutoFlush on, explicitly flushing and closing the Tracing does not increase the file size + /// thereby verifying that there was no leftover log entries being left behind to be flushed. + /// + [Fact] + public void LoggerAutolFlush() + { + // setup the test object + TestLogger test = new TestLogger() + { + TraceSource = MethodInfo.GetCurrentMethod().Name, + AutoFlush = true, + TracingLevel = SourceLevels.All + }; + test.Initialize(); + // Write 10000 lines of log + Parallel.For(0, 100, (i) => test.Write($"Message Number:{i}, Message:{test.LogMessage}")); + long logContentsSizeBeforeExplicitFlush = (new FileInfo(test.LogFileName)).Length; + // Please note that Logger.Close() first flushes the logs before closing them out. + Logger.Flush(); + long logContentsSizeAfterExplicitFlush = (new FileInfo(test.LogFileName)).Length; + Assert.True(logContentsSizeBeforeExplicitFlush == logContentsSizeAfterExplicitFlush, "The length of log file with autoflush before and after explicit flush must be same"); + test.Cleanup(); + } + /// /// When not use TraceSource, test to verify that upon changing TracingLevel from Error To Warning, /// after the change, messages of Warning as well as of Error type are present in the log. diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/CommandOptionsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/CommandOptionsTests.cs index 29c45eef..f75309d3 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/CommandOptionsTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/CommandOptionsTests.cs @@ -47,33 +47,45 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); VerifyCommandOptions(options, testNo++); } - // Test 2: All defaults, -logDir as null + // Test 2: All defaults, -autoflush-log as null { - var args = new string[] { "--log-dir", null }; + var args = new string[] { "--autoflush-log", null }; ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); - VerifyCommandOptions(options, testNo++); + VerifyCommandOptions(options, testNo++, autoFlushLog: true); } - // Test 3: All defaults, -logDir as empty string + // Test 3: All defaults, -autoflush-log as empty string { - var args = new string[] { "--log-dir", string.Empty }; + var args = new string[] { "--autoflush-log", string.Empty }; ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); - VerifyCommandOptions(options, testNo++); + VerifyCommandOptions(options, testNo++, autoFlushLog: true); } - // Test 4: All defaults, -log-file as null + // Test 4: All defaults, -tracing-level as empty string { - var args = new string[] { "--log-file", null }; + var args = new string[] { "--tracing-level", string.Empty }; ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); - VerifyCommandOptions(options, testNo++, logFilePath: null); + VerifyCommandOptions(options, testNo++, tracingLevel: string.Empty); } - // Test 5: All defaults, -log-file as empty string + // Test 5: All defaults, -tracing-level as null + { + var args = new string[] { "--tracing-level", null }; + ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); + VerifyCommandOptions(options, testNo++, tracingLevel: null); + } + // Test 6: All defaults, -log-file as empty string { var args = new string[] { "--log-file", string.Empty }; ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); VerifyCommandOptions(options, testNo++, logFilePath: string.Empty); } + // Test 6: All defaults, -log-file as null + { + var args = new string[] { "--log-file", null }; + ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); + VerifyCommandOptions(options, testNo++, logFilePath: null); + } } - private static void VerifyCommandOptions(ServiceLayerCommandOptions options, int? testNo = null, string errorMessage = "", string tracingLevel = null, string logFilePath = null, bool shouldExit = false, string locale = "", string logDirectory = null) + private static void VerifyCommandOptions(ServiceLayerCommandOptions options, int? testNo = null, string errorMessage = "", string tracingLevel = null, string logFilePath = null, bool shouldExit = false, string locale = "", bool autoFlushLog = false) { Assert.NotNull(options); string MsgPrefix = testNo != null ? $"TestNo:{testNo} ::" : string.Empty; @@ -81,12 +93,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility Assert.True(tracingLevel == options.TracingLevel, $"{MsgPrefix} options:{nameof(tracingLevel)} should be '{tracingLevel}'"); Assert.True(logFilePath == options.LogFilePath, $"{MsgPrefix} options:{nameof(logFilePath)} should be '{logFilePath}'"); Assert.True(shouldExit == options.ShouldExit, $"{MsgPrefix} options:{nameof(shouldExit)} should be '{shouldExit}'"); - Assert.False(string.IsNullOrWhiteSpace(options.LoggingDirectory)); - if (string.IsNullOrWhiteSpace(logDirectory)) - { - logDirectory = Path.Combine(options.DefaultLogRoot, options.ServiceName); - } - Assert.True(logDirectory == options.LoggingDirectory, $"{MsgPrefix} options:{nameof(logDirectory)} should be '{logDirectory}'"); + Assert.True(autoFlushLog == options.AutoFlushLog, $"{MsgPrefix} options:{nameof(autoFlushLog)} should be '{autoFlushLog}'"); Assert.True(options.Locale == locale, $"{MsgPrefix} options:{nameof(locale)} should be '{locale}'"); } @@ -128,24 +135,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility Assert.Equal(options.Locale, string.Empty); } - [Fact] - public void LoggingDirectorySet() - { - string logDir = Directory.GetCurrentDirectory(); - var args = new string[] { "--log-dir", logDir }; - ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); - - // Asserting all options were properly set - Assert.NotNull(options); - Assert.False(options.ShouldExit); - Assert.Equal(options.LoggingDirectory, logDir); - } - [Fact] public void TracingLevelSet() { - string expectedLevel = "Critical"; + string expectedLevel = "Information"; var args = new string[] { "--tracing-level", expectedLevel }; ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); @@ -155,11 +149,23 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility Assert.Equal(options.TracingLevel, expectedLevel); } + [Fact] + public void AutoFlushLogSet() + { + bool expectedAutoFlush = true; + var args = new string[] { "--autoflush-log"}; + ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); + + // Asserting all options were properly set + Assert.NotNull(options); + Assert.False(options.ShouldExit); + Assert.Equal(options.AutoFlushLog, expectedAutoFlush); + } [Fact] public void LogFilePathSet() { - string expectedFilePath = Path.GetRandomFileName(); + string expectedFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); var args = new string[] { "--log-file", expectedFilePath }; ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args); diff --git a/test/ScriptGenerator/Properties/Resources.Designer.cs b/test/ScriptGenerator/Properties/Resources.Designer.cs index b39eda2f..6763a1a4 100644 --- a/test/ScriptGenerator/Properties/Resources.Designer.cs +++ b/test/ScriptGenerator/Properties/Resources.Designer.cs @@ -66,10 +66,19 @@ namespace ScriptGenerator.Properties { ////****** Object: Database [AdventureWorks] Script Date: 9/6/2018 12:11:50 PM ******/ ///CREATE DATABASE [AdventureWorks] /// CONTAINMENT = NONE - /// ON PRIMARY - ///( NAME = N'AdventureWorks_Data', FILENAME = N'D:\sql\14.0.1000.169\sqlservr\data\AdventureWorks_Data.mdf' , SIZE = 174080KB , MAXSIZE = UNLIMITED, FILEGROWTH = 16384KB ) - /// LOG ON - ///( NAME = N'AdventureWorks_Log', FILENAME = N'D:\sql\14.0.1000.169\sqlservr\data\AdventureWorks_Log.ldf' , SIZE = 18432KB , MAXSIZE = 2048GB , FILEGROWTH = [rest of string was truncated]";. + ///GO + ///ALTER DATABASE [AdventureWorks] SET COMPATIBILITY_LEVEL = 100 + ///GO + ///IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled')) + ///begin + ///EXEC [AdventureWorks].[dbo].[sp_fulltext_database] @action = 'enable' + ///end + ///GO + ///ALTER DATABASE [AdventureWorks] SET ANSI_NULL_DEFAULT OFF + ///GO + ///ALTER DATABASE [AdventureWorks] SET ANSI_NULLS ON + ///GO + ///ALTER DATABASE [rest of string was truncated]";. /// internal static string AdventureWorks { get {