Removing script as feature from service layer to sqlcore (#2189)

This commit is contained in:
Aasim Khan
2023-08-28 04:28:25 +00:00
committed by GitHub
parent 1cd852c061
commit 766f68551e
47 changed files with 876 additions and 683 deletions

View File

@@ -1725,6 +1725,54 @@ namespace Microsoft.SqlTools.SqlCore
}
}
public static string ScriptingParams_ConnectionString_Property_Invalid
{
get
{
return Keys.GetString(Keys.ScriptingParams_ConnectionString_Property_Invalid);
}
}
public static string ScriptingParams_FilePath_Property_Invalid
{
get
{
return Keys.GetString(Keys.ScriptingParams_FilePath_Property_Invalid);
}
}
public static string ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid
{
get
{
return Keys.GetString(Keys.ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid);
}
}
public static string StoredProcedureScriptParameterComment
{
get
{
return Keys.GetString(Keys.StoredProcedureScriptParameterComment);
}
}
public static string ScriptingGeneralError
{
get
{
return Keys.GetString(Keys.ScriptingGeneralError);
}
}
public static string ScriptingExecuteNotSupportedError
{
get
{
return Keys.GetString(Keys.ScriptingExecuteNotSupportedError);
}
}
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Keys
{
@@ -2369,6 +2417,24 @@ namespace Microsoft.SqlTools.SqlCore
public const string FilterInPrimaryKeyDescription = "FilterInPrimaryKeyDescription";
public const string ScriptingParams_ConnectionString_Property_Invalid = "ScriptingParams_ConnectionString_Property_Invalid";
public const string ScriptingParams_FilePath_Property_Invalid = "ScriptingParams_FilePath_Property_Invalid";
public const string ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid = "ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid";
public const string StoredProcedureScriptParameterComment = "StoredProcedureScriptParameterComment";
public const string ScriptingGeneralError = "ScriptingGeneralError";
public const string ScriptingExecuteNotSupportedError = "ScriptingExecuteNotSupportedError";
private Keys()
{ }

View File

@@ -965,4 +965,28 @@
<value>Include or exclude objects based on whether the column is in a primary key.</value>
<comment></comment>
</data>
<data name="ScriptingParams_ConnectionString_Property_Invalid" xml:space="preserve">
<value>Error parsing ScriptingParams.ConnectionString property.</value>
<comment></comment>
</data>
<data name="ScriptingParams_FilePath_Property_Invalid" xml:space="preserve">
<value>Invalid directory specified by the ScriptingParams.FilePath property.</value>
<comment></comment>
</data>
<data name="ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid" xml:space="preserve">
<value>Error parsing ScriptingListObjectsCompleteParams.ConnectionString property.</value>
<comment></comment>
</data>
<data name="StoredProcedureScriptParameterComment" xml:space="preserve">
<value>-- TODO: Set parameter values here.</value>
<comment></comment>
</data>
<data name="ScriptingGeneralError" xml:space="preserve">
<value>An error occurred while scripting the objects.</value>
<comment></comment>
</data>
<data name="ScriptingExecuteNotSupportedError" xml:space="preserve">
<value>Scripting as Execute is only supported for Stored Procedures</value>
<comment></comment>
</data>
</root>

View File

@@ -430,3 +430,17 @@ FilterIsNativelyCompiledDescription = Include or exclude objects based on whethe
FilterInPrimaryKey = In Primary Key
FilterInPrimaryKeyDescription = Include or exclude objects based on whether the column is in a primary key.
############################################################################
# Scripting Service
ScriptingParams_ConnectionString_Property_Invalid = Error parsing ScriptingParams.ConnectionString property.
ScriptingParams_FilePath_Property_Invalid = Invalid directory specified by the ScriptingParams.FilePath property.
ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid = Error parsing ScriptingListObjectsCompleteParams.ConnectionString property.
StoredProcedureScriptParameterComment = -- TODO: Set parameter values here.
ScriptingGeneralError = An error occurred while scripting the objects.
ScriptingExecuteNotSupportedError = Scripting as Execute is only supported for Stored Procedures

View File

@@ -1062,6 +1062,36 @@
<target state="new">Schema and Data</target>
<note></note>
</trans-unit>
<trans-unit id="ScriptingParams_ConnectionString_Property_Invalid">
<source>Error parsing ScriptingParams.ConnectionString property.</source>
<target state="new">Error parsing ScriptingParams.ConnectionString property.</target>
<note></note>
</trans-unit>
<trans-unit id="ScriptingParams_FilePath_Property_Invalid">
<source>Invalid directory specified by the ScriptingParams.FilePath property.</source>
<target state="new">Invalid directory specified by the ScriptingParams.FilePath property.</target>
<note></note>
</trans-unit>
<trans-unit id="ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid">
<source>Error parsing ScriptingListObjectsCompleteParams.ConnectionString property.</source>
<target state="new">Error parsing ScriptingListObjectsCompleteParams.ConnectionString property.</target>
<note></note>
</trans-unit>
<trans-unit id="StoredProcedureScriptParameterComment">
<source>-- TODO: Set parameter values here.</source>
<target state="new">-- TODO: Set parameter values here.</target>
<note></note>
</trans-unit>
<trans-unit id="ScriptingGeneralError">
<source>An error occurred while scripting the objects.</source>
<target state="new">An error occurred while scripting the objects.</target>
<note></note>
</trans-unit>
<trans-unit id="ScriptingExecuteNotSupportedError">
<source>Scripting as Execute is only supported for Stored Procedures</source>
<target state="new">Scripting as Execute is only supported for Stored Procedures</target>
<note></note>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -26,6 +26,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" PrivateAssets="all" />
<ProjectReference Include="../Microsoft.SqlTools.ManagedBatchParser/Microsoft.SqlTools.ManagedBatchParser.csproj" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\*.resx" />
@@ -41,7 +43,7 @@
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<Content Include="bin\$(Configuration)\**\Microsoft.SqlTools.Hosting*.dll">
<Content Include="bin\$(Configuration)\**\Microsoft.SqlTools.*.dll">
<Pack>true</Pack>
<PackagePath>lib\</PackagePath>
</Content>

View File

@@ -10,11 +10,10 @@ using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlTools.SqlCore.Connection;
using Microsoft.SqlTools.SqlCore.ObjectExplorer;
using Microsoft.SqlTools.SqlCore.ObjectExplorer.Nodes;
using Microsoft.SqlTools.SqlCore.ObjectExplorer.SmoModel;
namespace Microsoft.SqlTools.CoreSql.ObjectExplorer
namespace Microsoft.SqlTools.SqlCore.ObjectExplorer
{
/// <summary>
/// Stateless object explorer class can be used to handle object explorer requests without creating a session. It requires a connection string and a node path to query objects from the server.

View File

@@ -0,0 +1,41 @@
//
// 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.Threading.Tasks;
using Azure.Core;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
public class AsyncScriptAsScriptingOperation
{
public static async Task<string> GetScriptAsScript(ScriptingParams parameters, AccessToken? accessToken)
{
var scriptAsOperation = new ScriptAsScriptingOperation(parameters, accessToken?.Token);
TaskCompletionSource<string> scriptAsTask = new TaskCompletionSource<string>();
scriptAsOperation.CompleteNotification += (sender, args) =>
{
if (args.HasError)
{
scriptAsTask.SetException(new Exception(args.ErrorMessage));
}
scriptAsTask.SetResult(scriptAsOperation.ScriptText);
};
scriptAsOperation.ProgressNotification += (sender, args) =>
{
if(args.ErrorMessage != null)
{
scriptAsTask.SetException(new Exception(args.ErrorMessage));
}
};
scriptAsOperation.Execute();
return await scriptAsTask.Task;
}
}
}

View File

@@ -0,0 +1,38 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Parameters sent to when a scripting operation has completed.
/// </summary>
public class ScriptingCompleteParams : ScriptingEventParams
{
/// <summary>
/// Get or sets the error details for an error that occurred during the scripting operation.
/// </summary>
public string ErrorDetails { get; set; }
/// <summary>
/// Get or sets the error message for an error that occurred during the scripting operation.
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Get or sets a value to indicate an error occurred during the scripting operation.
/// </summary>
public bool HasError { get; set; }
/// <summary>
/// Get or sets a value to indicate the scripting operation was canceled.
/// </summary>
public bool Canceled { get; set; }
/// <summary>
/// Get or sets a value to indicate the scripting operation successfully completed.
/// </summary>
public bool Success { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Base class for all scripting event parameters.
/// </summary>
public abstract class ScriptingEventParams
{
/// <summary>
/// Gets or sets the operation id of the scripting operation this event is associated with.
/// </summary>
public string OperationId { get; set; }
/// <summary>
/// Gets or sets the sequence number. The sequence number starts at 1, and is incremented each time a scripting event is
/// raised for the current scripting operation.
/// </summary>
public int SequenceNumber { get; set; }
}
}

View File

@@ -0,0 +1,112 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Class to represent a database object that can be scripted.
/// </summary>
public sealed class ScriptingObject : IEquatable<ScriptingObject>
{
/// <summary>
/// Gets or sets the database object type.
/// </summary>
/// <remarks>
/// This underlying values are determined by the SqlScriptPublishModel.GetDatabaseObjectTypes() and
/// can change depending on the version of SMO used by the tools service. Values can be:
/// Table,
/// View,
/// StoredProcedure,
/// UserDefinedFunction,
/// UserDefinedDataType,
/// User,
/// Default,
/// Rule,
/// DatabaseRole,
/// ApplicationRole,
/// SqlAssembly,
/// DdlTrigger,
/// Synonym,
/// XmlSchemaCollection,
/// Schema,
/// PlanGuide,
/// UserDefinedType,
/// UserDefinedAggregate,
/// FullTextCatalog,
/// UserDefinedTableType
/// </remarks>
public string Type { get; set; }
/// <summary>
/// Gets or sets the schema of the database object.
/// </summary>
public string Schema { get; set; }
/// <summary>
/// Gets or sets the database object name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the parent object name
/// </summary>
public string ParentName { get; set; }
/// <summary>
/// Gets or sets the parent object type name, such as Table, View, etc.
/// </summary>
public string ParentTypeName { get; set; }
public override string ToString()
{
string objectName = string.Empty;
if (!string.IsNullOrEmpty(this.Schema))
{
objectName += this.Schema + ".";
}
if (!string.IsNullOrEmpty(this.ParentName))
{
objectName += this.ParentName + ".";
}
return objectName + this.Name;
}
public override int GetHashCode()
{
return
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Type ?? string.Empty) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Schema ?? string.Empty) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(this.ParentName ?? string.Empty) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(this.ParentTypeName ?? string.Empty) ^
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Name ?? string.Empty);
}
public override bool Equals(object obj)
{
return
obj != null &&
this.GetType() == obj.GetType() &&
this.Equals((ScriptingObject)obj);
}
public bool Equals(ScriptingObject other)
{
if (other == null)
{
return false;
}
return
string.Equals(this.Type, other.Type, StringComparison.OrdinalIgnoreCase) &&
string.Equals(this.Schema, other.Schema, StringComparison.OrdinalIgnoreCase) &&
string.Equals(this.ParentName, other.ParentName, StringComparison.OrdinalIgnoreCase) &&
string.Equals(this.ParentTypeName, other.ParentTypeName, StringComparison.OrdinalIgnoreCase) &&
string.Equals(this.ParentTypeName, other.ParentTypeName, StringComparison.OrdinalIgnoreCase) &&
string.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -0,0 +1,21 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Scripting Operation type
/// </summary>
public enum ScriptingOperationType
{
Select = 0,
Create = 1,
Insert = 2,
Update = 3,
Delete = 4,
Execute = 5,
Alter = 6
}
}

View File

@@ -0,0 +1,81 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Parameters for a script request.
/// </summary>
public class ScriptingParams
{
/// <summary>
/// Gets or sets the file path used when writing out the script.
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// Gets or sets whether scripting to a single file or file per object.
/// </summary>
public string ScriptDestination { get; set; }
/// <summary>
/// Gets or sets connection string of the target database the scripting operation will run against.
/// </summary>
public string ConnectionString { get; set; }
/// <summary>
/// Gets or sets a list of scripting objects to script.
/// </summary>
public List<ScriptingObject> ScriptingObjects { get; set; }
/// <summary>
/// Gets or sets a list of scripting object which specify the include criteria of objects to script.
/// </summary>
public List<ScriptingObject> IncludeObjectCriteria { get; set; }
/// <summary>
/// Gets or sets a list of scripting object which specify the exclude criteria of objects to not script.
/// </summary>
public List<ScriptingObject> ExcludeObjectCriteria { get; set; }
/// <summary>
/// Gets or sets a list of schema name of objects to script.
/// </summary>
public List<string> IncludeSchemas { get; set; }
/// <summary>
/// Gets or sets a list of schema name of objects to not script.
/// </summary>
public List<string> ExcludeSchemas { get; set; }
/// <summary>
/// Gets or sets a list of type name of objects to script.
/// </summary>
public List<string> IncludeTypes { get; set; }
/// <summary>
/// Gets or sets a list of type name of objects to not script
/// </summary>
public List<string> ExcludeTypes { get; set; }
/// <summary>
/// Gets or sets the scripting options.
/// </summary>
public ScriptOptions ScriptOptions { get; set; }
/// <summary>
/// Gets or sets the connection owner URI
/// </summary>
public string OwnerUri { get; set; }
/// <summary>
/// The script operation
/// </summary>
public ScriptingOperationType Operation { get; set; } = ScriptingOperationType.Create;
}
}

View File

@@ -0,0 +1,25 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Parameters to indicate the script operation has resolved the objects to be scripted.
/// </summary>
public class ScriptingPlanNotificationParams : ScriptingEventParams
{
/// <summary>
/// Gets or sets the list of database objects whose progress has changed.
/// </summary>
public List<ScriptingObject> ScriptingObjects { get; set; }
/// <summary>
/// Gets or sets the count of database objects whose progress has changed.
/// </summary>
public int Count { get; set; }
}
}

View File

@@ -0,0 +1,46 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Parameters sent when a scripting operation has made progress.
/// </summary>
public class ScriptingProgressNotificationParams : ScriptingEventParams
{
/// <summary>
/// Gets or sets the scripting object whose progress has changed.
/// </summary>
public ScriptingObject ScriptingObject { get; set; }
/// <summary>
/// Gets or sets the status of the scripting operation for the scripting object.
/// </summary>
/// <remarks>
/// Values can be: 'Completed', 'Progress', and 'Error'.
/// </remarks>
public string Status { get; set; }
/// <summary>
/// Gets or count of completed scripting operations.
/// </summary>
public int CompletedCount { get; set; }
/// <summary>
/// Gets this total count of objects to script.
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// Gets or sets the error details if an error occurred scripting a database object.
/// </summary>
public string ErrorDetails { get; set; }
/// <summary>
/// Get or sets the error message for an error that occurred scripting a database object.
/// </summary>
public string ErrorMessage { get; set; }
}
}

View File

@@ -0,0 +1,713 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// A wrpaper of ScriptOptions to map the option name with the oen in SMO.ScriptingOptions
/// </summary>
public class ScriptAsOptions : ScriptOptions
{
public ScriptAsOptions(ScriptOptions scriptOptions)
{
Validate.IsNotNull(nameof(scriptOptions), scriptOptions);
ScriptOptions = scriptOptions;
}
public ScriptOptions ScriptOptions { get; private set; }
/// <summary>
/// Generate ANSI padding statements
/// </summary>
public override bool? ScriptAnsiPadding
{
get
{
return ScriptOptions.ScriptAnsiPadding;
}
set
{
ScriptOptions.ScriptAnsiPadding = value;
}
}
/// <summary>
/// Append the generated script to a file
/// </summary>
public override bool? AppendToFile
{
get
{
return ScriptOptions.AppendToFile;
}
set
{
ScriptOptions.AppendToFile = value;
}
}
/// <summary>
/// Continue to script if an error occurs. Otherwise, stop.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public override bool? ContinueScriptingOnError
{
get
{
return ScriptOptions.ContinueScriptingOnError;
}
set
{
ScriptOptions.ContinueScriptingOnError = value;
}
}
/// <summary>
/// Convert user-defined data types to base types.
/// </summary>
public override bool? ConvertUDDTToBaseType
{
get
{
return ScriptOptions.ConvertUDDTToBaseType;
}
set
{
ScriptOptions.ConvertUDDTToBaseType = value;
}
}
/// <summary>
/// Generate script for dependent objects for each object scripted.
/// </summary>
/// <remarks>
/// The default is false.
/// </remarks>
public override bool? GenerateScriptForDependentObjects
{
get
{
return ScriptOptions.GenerateScriptForDependentObjects;
}
set
{
ScriptOptions.GenerateScriptForDependentObjects = value;
}
}
/// <summary>
/// Include descriptive headers for each object generated.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public override bool? IncludeDescriptiveHeaders
{
get
{
return ScriptOptions.IncludeDescriptiveHeaders;
}
set
{
ScriptOptions.IncludeDescriptiveHeaders = value;
}
}
/// <summary>
/// Check that an object with the given name exists before dropping or altering or that an object with the given name does not exist before creating.
/// </summary>
public override bool? IncludeIfNotExists
{
get
{
return ScriptOptions.IncludeIfNotExists;
}
set
{
ScriptOptions.IncludeIfNotExists = value;
}
}
/// <summary>
/// Script options to set vardecimal storage format.
/// </summary>
public override bool? IncludeVarDecimal
{
get
{
return ScriptOptions.IncludeVarDecimal;
}
set
{
ScriptOptions.IncludeVarDecimal = value;
}
}
/// <summary>
/// Include system generated constraint names to enforce declarative referential integrity.
/// </summary>
public override bool? ScriptDriIncludeSystemNames
{
get
{
return ScriptOptions.ScriptDriIncludeSystemNames;
}
set
{
ScriptOptions.ScriptDriIncludeSystemNames = value;
}
}
/// <summary>
/// Include statements in the script that are not supported on the specified SQL Server database engine type.
/// </summary>
public override bool? IncludeUnsupportedStatements
{
get
{
return ScriptOptions.IncludeUnsupportedStatements;
}
set
{
ScriptOptions.IncludeUnsupportedStatements = value;
}
}
/// <summary>
/// Prefix object names with the object schema.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public override bool? SchemaQualify
{
get
{
return ScriptOptions.SchemaQualify;
}
set
{
ScriptOptions.SchemaQualify = value;
}
}
/// <summary>
/// Script options to set bindings option.
/// </summary>
public override bool? Bindings
{
get
{
return ScriptOptions.Bindings;
}
set
{
ScriptOptions.Bindings = value;
}
}
/// <summary>
/// Script the objects that use collation.
/// </summary>
public override bool? Collation
{
get
{
return ScriptOptions.Collation;
}
set
{
ScriptOptions.Collation = value;
}
}
/// <summary>
/// Script the default values.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public override bool? Default
{
get
{
return ScriptOptions.Default;
}
set
{
ScriptOptions.Default = value;
}
}
/// <summary>
/// Script Object CREATE/DROP statements.
/// Possible values:
/// ScriptCreate
/// ScriptDrop
/// ScriptCreateDrop
/// ScriptSelect
/// </summary>
/// <remarks>
/// The default is ScriptCreate.
/// </remarks>
public override string ScriptCreateDrop
{
get
{
return ScriptOptions.ScriptCreateDrop;
}
set
{
ScriptOptions.ScriptCreateDrop = value;
}
}
/// <summary>
/// Script the Extended Properties for each object scripted.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public override bool? ScriptExtendedProperties
{
get
{
return ScriptOptions.ScriptExtendedProperties;
}
set
{
ScriptOptions.ScriptExtendedProperties = value;
}
}
/// <summary>
/// Script only features compatible with the specified version of SQL Server. Possible values:
/// Script90Compat
/// Script100Compat
/// Script105Compat
/// Script110Compat
/// Script120Compat
/// Script130Compat
/// Script140Compat
/// Script150Compat
/// Script160Compat
/// </summary>
/// <remarks>
/// The default is Script140Compat.
/// </remarks>
public override string ScriptCompatibilityOption
{
get
{
return ScriptOptions.ScriptCompatibilityOption;
}
set
{
ScriptOptions.ScriptCompatibilityOption = value;
}
}
/// <summary>
/// Script only features compatible with the specified SQL Server database engine type.
/// Possible Values:
/// SingleInstance
/// SqlAzure
/// </summary>
public override string TargetDatabaseEngineType
{
get
{
return ScriptOptions.TargetDatabaseEngineType;
}
set
{
ScriptOptions.TargetDatabaseEngineType = value;
}
}
/// <summary>
/// Script only features compatible with the specified SQL Server database engine edition.
/// Possible Values:
/// SqlServerPersonalEdition
/// SqlServerStandardEdition
/// SqlServerEnterpriseEdition
/// SqlServerExpressEdition
/// SqlAzureDatabaseEdition
/// SqlDatawarehouseEdition
/// SqlServerStretchEdition
/// </summary>
public override string TargetDatabaseEngineEdition
{
get
{
return ScriptOptions.TargetDatabaseEngineEdition;
}
set
{
ScriptOptions.TargetDatabaseEngineEdition = value;
}
}
/// <summary>
/// Script all logins available on the server. Passwords will not be scripted.
/// </summary>
public override bool? ScriptLogins
{
get
{
return ScriptOptions.ScriptLogins;
}
set
{
ScriptOptions.ScriptLogins = value;
}
}
/// <summary>
/// Generate object-level permissions.
/// </summary>
public override bool? ScriptObjectLevelPermissions
{
get
{
return ScriptOptions.ScriptObjectLevelPermissions;
}
set
{
ScriptOptions.ScriptObjectLevelPermissions = value;
}
}
/// <summary>
/// Script owner for the objects.
/// </summary>
public override bool? ScriptOwner
{
get
{
return ScriptOptions.ScriptOwner;
}
set
{
ScriptOptions.ScriptOwner = value;
}
}
/// <summary>
/// Script statistics, and optionally include histograms, for each selected table or view.
/// Possible values:
/// ScriptStatsNone
/// ScriptStatsDDL
/// ScriptStatsAll
/// </summary>
/// <remarks>
/// The default value is ScriptStatsNone.
/// </remarks>
public override string ScriptStatistics
{
get
{
return ScriptOptions.ScriptStatistics;
}
set
{
ScriptOptions.ScriptStatistics = value;
}
}
/// <summary>
/// Generate USE DATABASE statement.
/// </summary>
public override bool? ScriptUseDatabase
{
get
{
return ScriptOptions.ScriptUseDatabase;
}
set
{
ScriptOptions.ScriptUseDatabase = value;
}
}
/// <summary>
/// Generate script that contains schema only or schema and data.
/// Possible Values:
/// SchemaAndData
/// DataOnly
/// SchemaOnly
/// </summary>
/// <remarks>
/// The default value is SchemaOnly.
/// </remarks>
public override string TypeOfDataToScript
{
get
{
return ScriptOptions.TypeOfDataToScript;
}
set
{
ScriptOptions.TypeOfDataToScript = value;
}
}
/// <summary>
/// Scripts the change tracking information.
/// </summary>
public override bool? ScriptChangeTracking
{
get
{
return ScriptOptions.ScriptChangeTracking;
}
set
{
ScriptOptions.ScriptChangeTracking = value;
}
}
/// <summary>
/// Script the check constraints for each table or view scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public override bool? ScriptCheckConstraints
{
get
{
return ScriptOptions.ScriptCheckConstraints;
}
set
{
ScriptOptions.ScriptCheckConstraints = value;
}
}
/// <summary>
/// Scripts the data compression information.
/// </summary>
public override bool? ScriptDataCompressionOptions
{
get
{
return ScriptOptions.ScriptDataCompressionOptions;
}
set
{
ScriptOptions.ScriptDataCompressionOptions = value;
}
}
/// <summary>
/// Script the foreign keys for each table scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public override bool? ScriptForeignKeys
{
get
{
return ScriptOptions.ScriptForeignKeys;
}
set
{
ScriptOptions.ScriptForeignKeys = value;
}
}
/// <summary>
/// Script the full-text indexes for each table or indexed view scripted.
/// </summary>
public override bool? ScriptFullTextIndexes
{
get
{
return ScriptOptions.ScriptFullTextIndexes;
}
set
{
ScriptOptions.ScriptFullTextIndexes = value;
}
}
/// <summary>
/// Script the indexes (including XML and clustered indexes) for each table or indexed view scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public override bool? ScriptIndexes
{
get
{
return ScriptOptions.ScriptIndexes;
}
set
{
ScriptOptions.ScriptIndexes = value;
}
}
/// <summary>
/// Script the primary keys for each table or view scripted
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public override bool? ScriptPrimaryKeys
{
get
{
return ScriptOptions.ScriptPrimaryKeys;
}
set
{
ScriptOptions.ScriptPrimaryKeys = value;
}
}
/// <summary>
/// Script the triggers for each table or view scripted
/// </summary>
public override bool? ScriptTriggers
{
get
{
return ScriptOptions.ScriptTriggers;
}
set
{
ScriptOptions.ScriptTriggers = value;
}
}
/// <summary>
/// Script the unique keys for each table or view scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public override bool? UniqueKeys
{
get
{
return ScriptOptions.UniqueKeys;
}
set
{
ScriptOptions.UniqueKeys = value;
}
}
/// <summary>
/// Returns Generate ANSI padding statements
/// </summary>
public bool? AnsiPadding { get { return ScriptOptions.ScriptAnsiPadding; } }
/// <summary>
/// Returns ConvertUDDTToBaseType
/// </summary>
public bool? ConvertUserDefinedDataTypesToBaseType { get { return ScriptOptions.ConvertUDDTToBaseType; } }
/// <summary>
/// Returns IncludeDescriptiveHeaders
/// </summary>
public bool? IncludeHeaders { get { return ScriptOptions.IncludeDescriptiveHeaders; } }
/// <summary>
/// Returns ScriptDriIncludeSystemNames
/// </summary>
public bool? DriIncludeSystemNames { get { return ScriptOptions.ScriptDriIncludeSystemNames; } }
/// <summary>
/// Returns SchemaQualify
/// </summary>
public bool? SchemaQualifyForeignKeysReferences { get { return ScriptOptions.SchemaQualify; } }
/// <summary>
/// Returns false if Collation is true
/// </summary>
public bool? NoCollation { get { return !ScriptOptions.Collation; } }
/// <summary>
/// Returns the value of Default Property
/// </summary>
public bool? DriDefaults { get { return ScriptOptions.Default; } }
/// <summary>
/// Returns the value of ScriptExtendedProperties Property
/// </summary>
public bool? ExtendedProperties { get { return ScriptOptions.ScriptExtendedProperties; } }
/// <summary>
/// Returns the value of ScriptObjectLevelPermissions Property
/// </summary>
public bool? Permissions { get { return ScriptOptions.ScriptObjectLevelPermissions; } }
/// <summary>
/// Returns the value of ScriptStatistics Property
/// </summary>
public string Statistics { get { return ScriptOptions.ScriptStatistics; } }
/// <summary>
/// Returns the value of ScriptChangeTracking Property
/// </summary>
public bool? ChangeTracking { get { return ScriptOptions.ScriptChangeTracking; } }
/// <summary>
/// Returns the value of ScriptCheckConstraints Property
/// </summary>
public bool? DriChecks { get { return ScriptOptions.ScriptCheckConstraints; } }
/// <summary>
/// Returns the value of ScriptDataCompressionOptions Property
/// </summary>
public bool? ScriptDataCompression { get { return ScriptOptions.ScriptDataCompressionOptions; } }
/// <summary>
/// Returns the value of ScriptForeignKeys Property
/// </summary>
public bool? DriForeignKeys { get { return ScriptOptions.ScriptForeignKeys; } }
/// <summary>
/// Returns the value of ScriptFullTextIndexes Property
/// </summary>
public bool? FullTextIndexes { get { return ScriptOptions.ScriptFullTextIndexes; } }
/// <summary>
/// Returns the value of ScriptIndexes Property
/// </summary>
public bool? Indexes { get { return ScriptOptions.ScriptIndexes; } }
/// <summary>
/// Returns the value of ScriptPrimaryKeys Property
/// </summary>
public bool? DriPrimaryKey { get { return ScriptOptions.ScriptPrimaryKeys; } }
/// <summary>
/// Returns the value of ScriptTriggers Property
/// </summary>
public bool? Triggers { get { return ScriptOptions.ScriptTriggers; } }
/// <summary>
/// Returns the value of UniqueKeys Property
/// </summary>
public bool? DriUniqueKeys { get { return ScriptOptions.UniqueKeys; } }
}
}

View File

@@ -0,0 +1,701 @@
//
// 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.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Text;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.SqlCore.Connection;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Class to generate script as for one smo object
/// </summary>
public class ScriptAsScriptingOperation : SmoScriptingOperation
{
private static readonly Dictionary<string, SqlServerVersion> scriptCompatibilityMap = LoadScriptCompatibilityMap();
/// <summary>
/// Left delimiter for an named object
/// </summary>
public const char LeftDelimiter = '[';
/// <summary>
/// right delimiter for a named object
/// </summary>
public const char RightDelimiter = ']';
public ScriptAsScriptingOperation(ScriptingParams parameters, ServerConnection serverConnection): base(parameters)
{
Validate.IsNotNull("serverConnection", serverConnection);
ServerConnection = serverConnection;
}
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken) : base(parameters)
{
SqlConnection sqlConnection = new SqlConnection(this.Parameters.ConnectionString);
sqlConnection.RetryLogicProvider = SqlRetryProviders.ServerlessDBRetryProvider();
if (azureAccountToken != null)
{
sqlConnection.AccessToken = azureAccountToken;
}
ServerConnection = new ServerConnection(sqlConnection);
if (azureAccountToken != null)
{
ServerConnection.AccessToken = new AzureAccessToken(azureAccountToken);
}
disconnectAtDispose = true;
}
internal ServerConnection ServerConnection { get; set; }
private string serverName;
private string databaseName;
private bool disconnectAtDispose = false;
public override void Execute()
{
try
{
this.CancellationToken.ThrowIfCancellationRequested();
this.ValidateScriptDatabaseParams();
this.CancellationToken.ThrowIfCancellationRequested();
string resultScript = string.Empty;
// TODO: try to use one of the existing connections
Server server = new Server(ServerConnection);
if (!ServerConnection.IsOpen)
{
ServerConnection.Connect();
}
UrnCollection urns = CreateUrns(ServerConnection);
ScriptingOptions options = new ScriptingOptions();
SetScriptBehavior(options);
ScriptAsOptions scriptAsOptions = new ScriptAsOptions(this.Parameters.ScriptOptions);
PopulateAdvancedScriptOptions(scriptAsOptions, options);
options.WithDependencies = false;
// TODO: Not including the header by default. We have to get this option from client
options.IncludeHeaders = false;
// Scripting data is not avaialable in the scripter
options.ScriptData = false;
SetScriptingOptions(options);
switch (this.Parameters.Operation)
{
case ScriptingOperationType.Create:
case ScriptingOperationType.Alter:
case ScriptingOperationType.Delete: // Using Delete here is wrong. delete usually means delete rows from table but sqlopsstudio sending the operation name as delete instead of drop
resultScript = GenerateScriptAs(server, urns, options);
break;
case ScriptingOperationType.Select:
resultScript = GenerateScriptSelect(server, urns);
break;
case ScriptingOperationType.Execute:
resultScript = GenerareScriptAsExecute(server, urns, options);
break;
}
this.CancellationToken.ThrowIfCancellationRequested();
Logger.Verbose(
string.Format(
"Sending script complete notification event for operation {0}",
this.OperationId
));
ScriptText = resultScript;
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
{
Success = true,
});
this.SendPlanNotificationEvent(new ScriptingPlanNotificationParams
{
ScriptingObjects = this.Parameters.ScriptingObjects,
Count = 1,
});
}
catch (Exception e)
{
if (e.IsOperationCanceledException())
{
Logger.Information(string.Format("Scripting operation {0} was canceled", this.OperationId));
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
{
Canceled = true,
});
}
else
{
Logger.Error(string.Format("Scripting operation {0} failed with exception {1}", this.OperationId, e));
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
{
OperationId = OperationId,
HasError = true,
ErrorMessage = $"{SR.ScriptingGeneralError} {e.Message}",
ErrorDetails = e.ToString(),
});
}
}
finally
{
if (disconnectAtDispose && ServerConnection != null && ServerConnection.IsOpen)
{
ServerConnection.Disconnect();
}
}
}
private string GenerateScriptSelect(Server server, UrnCollection urns)
{
string script = string.Empty;
ScriptingObject scriptingObject = this.Parameters.ScriptingObjects[0];
Urn objectUrn = urns[0];
string typeName = objectUrn.GetNameForType(scriptingObject.Type);
// select from service broker
if (string.Compare(typeName, "ServiceBroker", StringComparison.CurrentCultureIgnoreCase) == 0)
{
script = ScriptingHelper.SelectAllValuesFromTransmissionQueue(objectUrn);
}
// select from queues
else if (string.Compare(typeName, "Queues", StringComparison.CurrentCultureIgnoreCase) == 0 ||
string.Compare(typeName, "SystemQueues", StringComparison.CurrentCultureIgnoreCase) == 0)
{
script = ScriptingHelper.SelectAllValues(objectUrn);
}
// select from table or view
else
{
Database db = server.Databases[databaseName];
bool isDw = db.IsSqlDw;
script = ScriptingHelper.SelectFromTableOrView(server, objectUrn, isDw);
}
return script;
}
private string GenerareScriptAsExecute(Server server, UrnCollection urns, ScriptingOptions options)
{
string script = string.Empty;
ScriptingObject scriptingObject = this.Parameters.ScriptingObjects[0];
Urn urn = urns[0];
// get the object
StoredProcedure sp = server.GetSmoObject(urn) as StoredProcedure;
Database parentObject = server.GetSmoObject(urn.Parent) as Database;
StringBuilder executeStatement = new StringBuilder();
// list of DECLARE <variable> <type>
StringBuilder declares = new StringBuilder();
// Parameters to be passed
StringBuilder parameterList = new StringBuilder();
if (sp == null || parentObject == null)
{
throw new InvalidOperationException(SR.ScriptingExecuteNotSupportedError);
}
WriteUseDatabase(parentObject, executeStatement, options);
// character string to put in front of each parameter. First one is just carriage return
// the rest will have a "," in front as well.
string newLine = Environment.NewLine;
string paramListPreChar = $"{newLine} ";
for (int i = 0; i < sp.Parameters.Count; i++)
{
StoredProcedureParameter spp = sp.Parameters[i];
declares.AppendFormat("DECLARE {0} {1}{2}"
, QuoteObjectName(spp.Name)
, GetDatatype(spp.DataType, options)
, newLine);
parameterList.AppendFormat("{0}{1}"
, paramListPreChar
, QuoteObjectName(spp.Name));
// if this is the first time through change the prefix to include a ","
if (i == 0)
{
paramListPreChar = $"{newLine} ,";
}
// mark any output parameters as such.
if (spp.IsOutputParameter)
{
parameterList.Append(" OUTPUT");
}
}
// build the execute statement
if (sp.ImplementationType == ImplementationType.TransactSql)
{
executeStatement.Append("EXECUTE @RC = ");
}
else
{
executeStatement.Append("EXECUTE ");
}
// get the object name
executeStatement.Append(GenerateSchemaQualifiedName(sp.Schema, sp.Name, options.SchemaQualify));
string formatString = sp.ImplementationType == ImplementationType.TransactSql
? "DECLARE @RC int{5}{0}{5}{1}{5}{5}{2} {3}{5}{4}"
: "{0}{5}{1}{5}{5}{2} {3}{5}{4}";
script = string.Format(CultureInfo.InvariantCulture, formatString,
declares,
SR.StoredProcedureScriptParameterComment,
executeStatement,
parameterList,
CommonConstants.DefaultBatchSeperator,
newLine);
return script;
}
/// <summary>
/// Generate a schema qualified name (e.g. [schema].[objectName]) for an object if the option for SchemaQualify is true
/// </summary>
/// <param name="schema">The schema name. May be null or empty in which case it will be ignored</param>
/// <param name="objectName">The object name.</param>
/// <param name="schemaQualify">Whether to schema qualify the object or not</param>
/// <returns>The object name, quoted as appropriate and schema-qualified if the option is set</returns>
static private string GenerateSchemaQualifiedName(string schema, string objectName, bool schemaQualify)
{
var qualifiedName = new StringBuilder();
if (schemaQualify && !String.IsNullOrEmpty(schema))
{
// schema.name
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}.{1}", GetDelimitedString(schema), GetDelimitedString(objectName));
}
else
{
// name
qualifiedName.AppendFormat(CultureInfo.InvariantCulture, "{0}", GetDelimitedString(objectName));
}
return qualifiedName.ToString();
}
/// <summary>
/// getting delimited string
/// </summary>
/// <param name="str">string</param>
/// <returns>string</returns>
static private string GetDelimitedString(string str)
{
if (string.IsNullOrEmpty(str))
{
return String.Empty;
}
else
{
StringBuilder qualifiedName = new StringBuilder();
qualifiedName.AppendFormat("{0}{1}{2}",
LeftDelimiter,
QuoteObjectName(str),
RightDelimiter);
return qualifiedName.ToString();
}
}
/// <summary>
/// turn a smo datatype object into a type that can be inserted into tsql, e.g. nvarchar(20)
/// </summary>
/// <param name="type"></param>
/// <param name="options"></param>
/// <returns></returns>
internal static string GetDatatype(DataType type, ScriptingOptions options)
{
// string we'll return.
string rv = string.Empty;
string dataType = type.Name;
switch (type.SqlDataType)
{
// char, nchar, nchar, nvarchar, varbinary, nvarbinary are all displayed as type(length)
// length of -1 is taken to be type(max). max isn't localizable.
case SqlDataType.Char:
case SqlDataType.NChar:
case SqlDataType.VarChar:
case SqlDataType.NVarChar:
case SqlDataType.Binary:
case SqlDataType.VarBinary:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1})",
dataType,
type.MaximumLength);
break;
case SqlDataType.VarCharMax:
case SqlDataType.NVarCharMax:
case SqlDataType.VarBinaryMax:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}(max)",
dataType);
break;
// numeric and decimal are displayed as type precision,scale
case SqlDataType.Numeric:
case SqlDataType.Decimal:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1},{2})",
dataType,
type.NumericPrecision,
type.NumericScale);
break;
//time, datetimeoffset and datetime2 are displayed as type scale
case SqlDataType.Time:
case SqlDataType.DateTimeOffset:
case SqlDataType.DateTime2:
rv = string.Format(CultureInfo.InvariantCulture,
"{0}({1})",
dataType,
type.NumericScale);
break;
// anything else is just type.
case SqlDataType.Xml:
if (type.Schema != null && type.Schema.Length > 0 && dataType != null && dataType.Length > 0)
{
rv = String.Format(CultureInfo.InvariantCulture
, "xml ({0}{2}{1}.{0}{3}{1})"
, LeftDelimiter
, RightDelimiter
, QuoteObjectName(type.Schema)
, QuoteObjectName(dataType));
}
else
{
rv = "xml";
}
break;
case SqlDataType.UserDefinedDataType:
case SqlDataType.UserDefinedTableType:
case SqlDataType.UserDefinedType:
//User defined types may be in a non-DBO schema so append it if necessary
rv = GenerateSchemaQualifiedName(type.Schema, dataType, options.SchemaQualify);
break;
default:
rv = dataType;
break;
}
return rv;
}
/// <summary>
/// Double quotes certain characters in object name
/// </summary>
/// <param name="sqlObject"></param>
public static string QuoteObjectName(string sqlObject)
{
int len = sqlObject.Length;
StringBuilder result = new StringBuilder(sqlObject.Length);
for (int i = 0; i < len; i++)
{
if (sqlObject[i] == ']')
{
result.Append(']');
}
result.Append(sqlObject[i]);
}
return result.ToString();
}
private static void WriteUseDatabase(Database parentObject, StringBuilder stringBuilder , ScriptingOptions options)
{
if (options.IncludeDatabaseContext)
{
string useDb = string.Format(CultureInfo.InvariantCulture, "USE {0}", CommonConstants.DefaultBatchSeperator);
if (!options.NoCommandTerminator)
{
stringBuilder.Append(useDb);
}
else
{
stringBuilder.Append(useDb);
stringBuilder.Append(Environment.NewLine);
}
}
}
private string GenerateScriptAs(Server server, UrnCollection urns, ScriptingOptions options)
{
SqlServer.Management.Smo.Scripter scripter = null;
string resultScript = string.Empty;
try
{
scripter = new SqlServer.Management.Smo.Scripter(server);
if(this.Parameters.Operation == ScriptingOperationType.Alter)
{
options.ScriptForAlter = true;
foreach (var urn in urns)
{
SqlSmoObject smoObject = server.GetSmoObject(urn);
// without calling the toch method, no alter script get generated from smo
smoObject.Touch();
}
}
scripter.Options = options;
scripter.ScriptingError += ScripterScriptingError;
var result = scripter.Script(urns);
resultScript = GetScript(options, result);
}
catch
{
throw;
}
finally
{
if (scripter != null)
{
scripter.ScriptingError -= this.ScripterScriptingError;
}
}
return resultScript;
}
private string GetScript(ScriptingOptions options, StringCollection stringCollection)
{
StringBuilder sb = new StringBuilder();
foreach (var item in stringCollection)
{
sb.Append(item);
if (options != null && !options.NoCommandTerminator)
{
//Ensure the batch separator is always on a new line (to avoid syntax errors)
//but don't write an extra if we already have one as this can affect definitions
//of objects such as Stored Procedures (see TFS#9125366)
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}{2}",
item.EndsWith(Environment.NewLine) ? string.Empty : Environment.NewLine,
CommonConstants.DefaultBatchSeperator,
Environment.NewLine);
}
else
{
sb.AppendFormat(CultureInfo.InvariantCulture, Environment.NewLine);
}
}
return sb.ToString();
}
private UrnCollection CreateUrns(ServerConnection serverConnection)
{
IEnumerable<ScriptingObject> selectedObjects = new List<ScriptingObject>(this.Parameters.ScriptingObjects);
serverName = serverConnection.TrueName;
databaseName = new SqlConnectionStringBuilder(this.Parameters.ConnectionString).InitialCatalog;
UrnCollection urnCollection = new UrnCollection();
foreach (var scriptingObject in selectedObjects)
{
if(string.IsNullOrEmpty(scriptingObject.Schema))
{
// TODO: get the default schema
scriptingObject.Schema = "dbo";
}
urnCollection.Add(scriptingObject.ToUrn(serverName, databaseName));
}
return urnCollection;
}
private void SetScriptBehavior(ScriptingOptions options)
{
// TODO: have to add Scripting behavior to Smo ScriptingOptions class
// so it would support ScriptDropAndScreate
switch (this.Parameters.ScriptOptions.ScriptCreateDrop)
{
case "ScriptCreate":
options.ScriptDrops = false;
break;
case "ScriptDrop":
options.ScriptDrops = true;
break;
default:
options.ScriptDrops = false;
break;
}
}
private static Dictionary<string, SqlServerVersion> LoadScriptCompatibilityMap()
{
return new Dictionary<string, SqlServerVersion>
{
{SqlScriptOptions.ScriptCompatibilityOptions.Script160Compat.ToString(), SqlServerVersion.Version160},
{SqlScriptOptions.ScriptCompatibilityOptions.Script150Compat.ToString(), SqlServerVersion.Version150},
{SqlScriptOptions.ScriptCompatibilityOptions.Script140Compat.ToString(), SqlServerVersion.Version140},
{SqlScriptOptions.ScriptCompatibilityOptions.Script130Compat.ToString(), SqlServerVersion.Version130},
{SqlScriptOptions.ScriptCompatibilityOptions.Script120Compat.ToString(), SqlServerVersion.Version120},
{SqlScriptOptions.ScriptCompatibilityOptions.Script110Compat.ToString(), SqlServerVersion.Version110},
{SqlScriptOptions.ScriptCompatibilityOptions.Script105Compat.ToString(), SqlServerVersion.Version105},
{SqlScriptOptions.ScriptCompatibilityOptions.Script100Compat.ToString(), SqlServerVersion.Version100},
{SqlScriptOptions.ScriptCompatibilityOptions.Script90Compat.ToString(), SqlServerVersion.Version90}
};
}
private void SetScriptingOptions(ScriptingOptions scriptingOptions)
{
scriptingOptions.AllowSystemObjects = true;
// setting this forces SMO to correctly script objects that have been renamed
scriptingOptions.EnforceScriptingOptions = true;
//We always want role memberships for users and database roles to be scripted
scriptingOptions.IncludeDatabaseRoleMemberships = true;
SqlServerVersion targetServerVersion;
if(scriptCompatibilityMap.TryGetValue(this.Parameters.ScriptOptions.ScriptCompatibilityOption, out targetServerVersion))
{
scriptingOptions.TargetServerVersion = targetServerVersion;
}
else
{
//If you are getting this assertion fail it means you are working for higher
//version of SQL Server. You need to update this part of code.
Logger.Warning("This part of the code is not updated corresponding to latest version change");
}
// for cloud scripting to work we also have to have Script Compat set to 105.
// the defaults from scripting options should take care of it
SqlScriptOptions.ScriptDatabaseEngineType targetDatabaseEngineType;
if (Enum.TryParse<SqlScriptOptions.ScriptDatabaseEngineType>(this.Parameters.ScriptOptions.TargetDatabaseEngineType, out targetDatabaseEngineType))
{
switch (targetDatabaseEngineType)
{
case SqlScriptOptions.ScriptDatabaseEngineType.SingleInstance:
scriptingOptions.TargetDatabaseEngineType = DatabaseEngineType.Standalone;
break;
case SqlScriptOptions.ScriptDatabaseEngineType.SqlAzure:
scriptingOptions.TargetDatabaseEngineType = DatabaseEngineType.SqlAzureDatabase;
break;
}
}
SqlScriptOptions.ScriptDatabaseEngineEdition targetDatabaseEngineEdition;
if (Enum.TryParse<SqlScriptOptions.ScriptDatabaseEngineEdition>(this.Parameters.ScriptOptions.TargetDatabaseEngineEdition, out targetDatabaseEngineEdition))
{
switch (targetDatabaseEngineEdition)
{
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerPersonalEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Personal;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerStandardEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Standard;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerEnterpriseEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Enterprise;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerExpressEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Express;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlAzureDatabaseEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlDatabase;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlDatawarehouseEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlDataWarehouse;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerStretchEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlStretchDatabase;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerManagedInstanceEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlManagedInstance;
break;
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerOnDemandEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.SqlOnDemand;
break;
default:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Standard;
break;
}
}
if (scriptingOptions.TargetDatabaseEngineEdition == DatabaseEngineEdition.SqlDataWarehouse)
{
scriptingOptions.Triggers = false;
}
scriptingOptions.NoVardecimal = false; //making IncludeVarDecimal true for DPW
// scripting of stats is a combination of the Statistics
// and the OptimizerData flag
SqlScriptOptions.ScriptStatisticsOptions scriptStatistics;
if (Enum.TryParse<SqlScriptOptions.ScriptStatisticsOptions>(this.Parameters.ScriptOptions.ScriptStatistics, out scriptStatistics))
{
switch (scriptStatistics)
{
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsAll:
scriptingOptions.Statistics = true;
scriptingOptions.OptimizerData = true;
break;
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsDDL:
scriptingOptions.Statistics = true;
scriptingOptions.OptimizerData = false;
break;
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsNone:
scriptingOptions.Statistics = false;
scriptingOptions.OptimizerData = false;
break;
}
}
// If Histogram and Update Statics are True then include DriIncludeSystemNames and AnsiPadding by default
if (scriptingOptions.Statistics == true && scriptingOptions.OptimizerData == true)
{
scriptingOptions.DriIncludeSystemNames = true;
scriptingOptions.AnsiPadding = true;
}
}
private void ScripterScriptingError(object sender, ScriptingErrorEventArgs e)
{
this.CancellationToken.ThrowIfCancellationRequested();
Logger.Verbose(
string.Format(
"Sending scripting error progress event, Urn={0}, OperationId={1}, Completed={2}, Error={3}",
e.Current,
this.OperationId,
false,
e?.InnerException?.ToString() ?? "null"));
this.SendProgressNotificationEvent(new ScriptingProgressNotificationParams
{
ScriptingObject = e.Current?.ToScriptingObject(),
Status = "Failed",
ErrorMessage = e?.InnerException?.Message,
ErrorDetails = e?.InnerException?.ToString(),
});
}
}
}

View File

@@ -0,0 +1,147 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Extension methods used by the scripting service.
/// </summary>
public static class ScriptingExtensionMethods
{
/// <summary>
/// Gets the status of a scripting operation for the passed scripting event.
/// </summary>
/// <param name="e">The scripting event.</param>
/// <returns>The status.</returns>
public static string GetStatus(this ScriptEventArgs e)
{
Validate.IsNotNull("e", e);
string status = null;
if (e.Error != null)
{
status = "Error";
}
else if (e.Completed)
{
status = "Completed";
}
else
{
status = "Progress";
}
return status;
}
/// <summary>
/// Returns a list of ScriptingObject instances for the passed SqlScriptPublishModel instance.
/// </summary>
/// <param name="publishModel">The sql script publish model instance.</param>
/// <returns>The list of scripting objects.</returns>
public static List<ScriptingObject> GetDatabaseObjects(this SqlScriptPublishModel publishModel)
{
Validate.IsNotNull("publishModel", publishModel);
List<ScriptingObject> databaseObjects = new List<ScriptingObject>();
IEnumerable<DatabaseObjectType> objectTypes = publishModel.GetDatabaseObjectTypes();
Logger.Verbose(
string.Format(
"Loaded SMO object type count {0}, types: {1}",
objectTypes.Count(),
string.Join(", ", objectTypes)));
foreach (DatabaseObjectType objectType in objectTypes)
{
IEnumerable<KeyValuePair<string, string>> databaseObjectsOfType = publishModel.EnumChildrenForDatabaseObjectType(objectType);
Logger.Verbose(
string.Format(
"Loaded SMO urn object count {0} for type {1}, urns: {2}",
objectType,
databaseObjectsOfType.Count(),
string.Join(", ", databaseObjectsOfType.Select(p => p.Value))));
databaseObjects.AddRange(databaseObjectsOfType.Select(d => new Urn(d.Value).ToScriptingObject()));
}
return databaseObjects;
}
/// <summary>
/// Creates a SMO Urn instance based on the passed ScriptingObject instance.
/// </summary>
/// <param name="scriptingObject">The scripting object instance.</param>
/// <param name="database">The name of the database referenced by the Urn.</param>
/// <returns>The Urn instance.</returns>
public static Urn ToUrn(this ScriptingObject scriptingObject, string server, string database)
{
Validate.IsNotNull("scriptingObject", scriptingObject);
Validate.IsNotNullOrEmptyString("server", server);
Validate.IsNotNullOrWhitespaceString("database", database);
Validate.IsNotNullOrWhitespaceString("scriptingObject.Name", scriptingObject.Name);
Validate.IsNotNullOrWhitespaceString("scriptingObject.Type", scriptingObject.Type);
// Leaving the server name blank will automatically match whatever the server SMO is running against.
StringBuilder urnBuilder = new StringBuilder();
urnBuilder.AppendFormat("Server[@Name='{0}']/", server.ToUpper(System.Globalization.CultureInfo.InvariantCulture));
urnBuilder.AppendFormat("Database[@Name='{0}']/", Urn.EscapeString(database));
bool hasParentObject = !string.IsNullOrWhiteSpace(scriptingObject.ParentName)
&& !string.IsNullOrWhiteSpace(scriptingObject.ParentTypeName);
if (hasParentObject)
{
urnBuilder.AppendFormat("{0}[@Name='{1}'", scriptingObject.ParentTypeName, Urn.EscapeString(scriptingObject.ParentName));
if (!string.IsNullOrWhiteSpace(scriptingObject.Schema))
{
urnBuilder.AppendFormat(" and @Schema = '{0}'", Urn.EscapeString(scriptingObject.Schema));
}
urnBuilder.Append("]/");
}
urnBuilder.AppendFormat("{0}[@Name='{1}'", scriptingObject.Type, Urn.EscapeString(scriptingObject.Name));
// add schema to object only if there is no parent object specified
// the parent object field is only set for objects that don't have schema themselves
// so if parent is not null then the schema filter will already be set that part of the urn above
if (!string.IsNullOrWhiteSpace(scriptingObject.Schema) && !hasParentObject)
{
urnBuilder.AppendFormat(" and @Schema = '{0}'", Urn.EscapeString(scriptingObject.Schema));
}
urnBuilder.Append("]");
return new Urn(urnBuilder.ToString());
}
/// <summary>
/// Creates a ScriptingObject instance based on the passed SMO Urn instance.
/// </summary>
/// <param name="urn">The urn instance.</param>
/// <returns>The scripting object instance.</returns>
public static ScriptingObject ToScriptingObject(this Urn urn)
{
Validate.IsNotNull("urn", urn);
return new ScriptingObject
{
Type = urn.Type,
Schema = urn.GetAttribute("Schema"),
Name = urn.GetAttribute("Name"),
};
}
}
}

View File

@@ -0,0 +1,318 @@
//
// 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.Collections.Generic;
using System.Data;
using System.Text;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Helper class for scripting
/// </summary>
public static class ScriptingHelper
{
internal static string SelectAllValuesFromTransmissionQueue(Urn urn)
{
string script = string.Empty;
StringBuilder selectQuery = new StringBuilder();
/*
SELECT TOP *, casted_message_body =
CASE MESSAGE_TYPE_NAME WHEN 'X'
THEN CAST(MESSAGE_BODY AS NVARCHAR(MAX))
ELSE MESSAGE_BODY
END
FROM [new].[sys].[transmission_queue]
*/
selectQuery.Append("SELECT TOP (1000) ");
selectQuery.Append("*, casted_message_body = \r\nCASE message_type_name WHEN 'X' \r\n THEN CAST(message_body AS NVARCHAR(MAX)) \r\n ELSE message_body \r\nEND \r\n");
// from clause
selectQuery.Append("FROM ");
Urn dbUrn = urn;
// database
while (dbUrn.Parent != null && dbUrn.Type != "Database")
{
dbUrn = dbUrn.Parent;
}
selectQuery.AppendFormat("{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
//SYS
selectQuery.AppendFormat(".{0}sys{1}",
ScriptingGlobals.LeftDelimiter,
ScriptingGlobals.RightDelimiter);
//TRANSMISSION QUEUE
selectQuery.AppendFormat(".{0}transmission_queue{1}",
ScriptingGlobals.LeftDelimiter,
ScriptingGlobals.RightDelimiter);
script = selectQuery.ToString();
return script;
}
internal static string SelectAllValues(Urn urn)
{
string script = string.Empty;
StringBuilder selectQuery = new StringBuilder();
selectQuery.Append("SELECT TOP (1000) ");
selectQuery.Append("*, casted_message_body = \r\nCASE message_type_name WHEN 'X' \r\n THEN CAST(message_body AS NVARCHAR(MAX)) \r\n ELSE message_body \r\nEND \r\n");
// from clause
selectQuery.Append("FROM ");
Urn dbUrn = urn;
// database
while (dbUrn.Parent != null && dbUrn.Type != "Database")
{
dbUrn = dbUrn.Parent;
}
selectQuery.AppendFormat("{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// schema
selectQuery.AppendFormat(".{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(urn.GetAttribute("Schema"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// object
selectQuery.AppendFormat(".{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(urn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
//Adding no lock in the end.
selectQuery.AppendFormat(" WITH(NOLOCK)");
script = selectQuery.ToString();
return script;
}
internal static class ScriptingGlobals
{
/// <summary>
/// Left delimiter for an named object
/// </summary>
public const char LeftDelimiter = '[';
/// <summary>
/// right delimiter for a named object
/// </summary>
public const char RightDelimiter = ']';
}
static DataTable GetColumnNames(Server server, Urn urn, bool isDw)
{
List<string> filterExpressions = new List<string>();
if (server.Version.Major >= 10)
{
// We don't have to include sparce columns as all the sparce columns data.
// Can be obtain from column set columns.
filterExpressions.Add("@IsSparse=0");
}
// Check if we're called for EDIT for SQL2016+/Sterling+.
// We need to omit temporal columns if such are present on this table.
if (server.Version.Major >= 13 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && server.Version.Major >= 12 && !isDw))
{
// We're called in order to generate a list of columns for EDIT TOP N rows.
// Don't return auto-generated, auto-populated, read-only temporal columns.
filterExpressions.Add("@GeneratedAlwaysType=0");
}
// Check if we're called for EDIT for SQL2022+/Sterling+.
// We need to omit dropped ledger columns if such are present
if (server.Version.Major >= 16 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && server.Version.Major >= 12 && !isDw))
{
filterExpressions.Add("@IsDroppedLedgerColumn=0");
}
// Check if we're called for SQL2017/Sterling+.
// We need to omit graph internal columns if such are present on this table.
if (server.Version.Major >= 14 || (DatabaseEngineType.SqlAzureDatabase == server.DatabaseEngineType && !isDw))
{
// from Smo.GraphType:
// 0 = None
// 1 = GraphId
// 2 = GraphIdComputed
// 3 = GraphFromId
// 4 = GraphFromObjId
// 5 = GraphFromIdComputed
// 6 = GraphToId
// 7 = GraphToObjId
// 8 = GraphToIdComputed
//
// We only want to show types 0, 2, 5, and 8:
filterExpressions.Add("(@GraphType=0 or @GraphType=2 or @GraphType=5 or @GraphType=8)");
}
Request request = new Request();
// If we have any filters on the columns, add them.
if (filterExpressions.Count > 0)
{
request.Urn = String.Format("{0}/Column[{1}]", urn.ToString(), string.Join(" and ", filterExpressions.ToArray()));
}
else
{
request.Urn = String.Format("{0}/Column", urn.ToString());
}
request.Fields = new String[] { "Name" };
// get the columns in the order they were created
OrderBy order = new OrderBy();
order.Dir = OrderBy.Direction.Asc;
order.Field = "ID";
request.OrderByList = new OrderBy[] { order };
Enumerator en = new Enumerator();
// perform the query.
DataTable? dt = null;
EnumResult result = en.Process(server.ConnectionContext, request);
if (result.Type == ResultType.DataTable)
{
dt = result;
}
else
{
dt = ((DataSet)result).Tables[0];
}
return dt;
}
public static string SelectFromTableOrView(Server server, Urn urn, bool isDw)
{
DataTable dt = GetColumnNames(server, urn, isDw);
StringBuilder selectQuery = new StringBuilder();
// build the first line
if (dt != null && dt.Rows.Count > 0)
{
selectQuery.Append("SELECT TOP (1000) ");
// first column
selectQuery.AppendFormat("{0}{1}{2}\r\n",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(dt.Rows[0][0] as string, ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// add all other columns on separate lines. Make the names align.
for (int i = 1; i < dt.Rows.Count; i++)
{
selectQuery.AppendFormat(" ,{0}{1}{2}\r\n",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(dt.Rows[i][0] as string, ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
}
}
else
{
selectQuery.Append("SELECT TOP (1000) * ");
}
// from clause
selectQuery.Append(" FROM ");
if (server.ServerType != DatabaseEngineType.SqlAzureDatabase)
{
// Azure doesn't allow qualifying object names with the DB, so only add it on if we're not in Azure database URN
Urn dbUrn = urn.Parent;
selectQuery.AppendFormat("{0}{1}{2}.",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
}
// schema
selectQuery.AppendFormat("{0}{1}{2}.",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(urn.GetAttribute("Schema"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// object
selectQuery.AppendFormat("{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
QuoteObjectName(urn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// In Hekaton M5, if it's a memory optimized table, we need to provide SNAPSHOT hint for SELECT.
if (urn.Type.Equals("Table") && IsXTPSupportedOnServer(server))
{
try
{
Table table = (Table)server.GetSmoObject(urn);
table.Refresh();
if (table.IsMemoryOptimized)
{
selectQuery.Append(" WITH (SNAPSHOT)");
}
}
catch (Exception ex)
{
// log any exceptions determining if InMemory, but don't treat as fatal exception
Logger.Error("Could not determine if is InMemory table " + ex.ToString());
}
}
return selectQuery.ToString();
}
/// <summary>
/// Quote the name of a given sql object.
/// </summary>
/// <param name="sqlObject">object</param>
/// <returns>quoted object name</returns>
internal static string QuoteObjectName(string sqlObject)
{
return QuoteObjectName(sqlObject, ']');
}
/// <summary>
/// Quotes the name of a given sql object
/// </summary>
/// <param name="sqlObject">object</param>
/// <param name="quote">quote to use</param>
/// <returns></returns>
internal static string QuoteObjectName(string sqlObject, char quote)
{
int len = sqlObject.Length;
StringBuilder result = new StringBuilder(sqlObject.Length);
for (int i = 0; i < len; i++)
{
if (sqlObject[i] == quote)
{
result.Append(quote);
}
result.Append(sqlObject[i]);
}
return result.ToString();
}
/// <summary>
/// Returns the value whether the server supports XTP or not s
internal static bool IsXTPSupportedOnServer(Server server)
{
bool isXTPSupported = false;
if (server.ConnectionContext.ExecuteScalar("SELECT SERVERPROPERTY('IsXTPSupported')") != DBNull.Value)
{
isXTPSupported = server.IsXTPSupported;
}
return isXTPSupported;
}
}
}

View File

@@ -0,0 +1,55 @@
//
// 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.Threading;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Base class for scripting operations. Because scripting operations can be very long
/// running, there my be multiple concurrent scripting operations. To distinguish events
/// between concurrent scripting operations, use the operation id.
/// </summary>
public abstract class ScriptingOperation : IDisposable
{
private CancellationTokenSource cancellation = new CancellationTokenSource();
protected ScriptingOperation()
{
this.OperationId = Guid.NewGuid().ToString();
}
protected CancellationToken CancellationToken { get { return this.cancellation.Token; } }
/// <summary>
/// Gets the unique id associated with this instance.
/// </summary>
public string OperationId { get; private set; }
/// <summary>
/// Excecutes the scripting operation.
/// </summary>
public abstract void Execute();
/// <summary>
/// Cancels the scripting operation.
/// </summary>
public virtual void Cancel()
{
if (!this.cancellation.IsCancellationRequested)
{
Logger.Verbose(string.Format("Cancel invoked for OperationId {0}", this.OperationId));
this.cancellation.Cancel();
}
}
/// <summary>
/// Disposes the scripting operation.
/// </summary>
public abstract void Dispose();
}
}

View File

@@ -0,0 +1,263 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Defines the scripting options.
/// </summary>
public class ScriptOptions
{
/// <summary>
/// Generate ANSI padding statements
/// </summary>
public virtual bool? ScriptAnsiPadding { get; set; } = false;
/// <summary>
/// Append the generated script to a file
/// </summary>
public virtual bool? AppendToFile { get; set; } = false;
/// <summary>
/// Continue to script if an error occurs. Otherwise, stop.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public virtual bool? ContinueScriptingOnError { get; set; } = true;
/// <summary>
/// Convert user-defined data types to base types.
/// </summary>
public virtual bool? ConvertUDDTToBaseType { get; set; } = false;
/// <summary>
/// Generate script for dependent objects for each object scripted.
/// </summary>
/// <remarks>
/// The default is false.
/// </remarks>
public virtual bool? GenerateScriptForDependentObjects { get; set; } = false;
/// <summary>
/// Include descriptive headers for each object generated.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public virtual bool? IncludeDescriptiveHeaders { get; set; } = true;
/// <summary>
/// Check that an object with the given name exists before dropping or altering or that an object with the given name does not exist before creating.
/// </summary>
public virtual bool? IncludeIfNotExists { get; set; } = false;
/// <summary>
/// Script options to set vardecimal storage format.
/// </summary>
public virtual bool? IncludeVarDecimal { get; set; } = true;
/// <summary>
/// Include system generated constraint names to enforce declarative referential integrity.
/// </summary>
public virtual bool? ScriptDriIncludeSystemNames { get; set; } = false;
/// <summary>
/// Include statements in the script that are not supported on the specified SQL Server database engine type.
/// </summary>
public virtual bool? IncludeUnsupportedStatements { get; set; } = true;
/// <summary>
/// Prefix object names with the object schema.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public virtual bool? SchemaQualify { get; set; } = true;
/// <summary>
/// Script options to set bindings option.
/// </summary>
public virtual bool? Bindings { get; set; } = false;
/// <summary>
/// Script the objects that use collation.
/// </summary>
public virtual bool? Collation { get; set; } = false;
/// <summary>
/// Script the default values.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public virtual bool? Default { get; set; } = true;
/// <summary>
/// Script Object CREATE/DROP statements.
/// Possible values:
/// ScriptCreate
/// ScriptDrop
/// ScriptCreateDrop
/// </summary>
/// <remarks>
/// The default is ScriptCreate.
/// </remarks>
public virtual string ScriptCreateDrop { get; set; } = "ScriptCreate";
/// <summary>
/// Script the Extended Properties for each object scripted.
/// </summary>
/// <remarks>
/// The default is true.
/// </remarks>
public virtual bool? ScriptExtendedProperties { get; set; } = true;
/// <summary>
/// Script only features compatible with the specified version of SQL Server. Possible values:
/// Script90Compat
/// Script100Compat
/// Script105Compat
/// Script110Compat
/// Script120Compat
/// Script130Compat
/// Script140Compat
/// Script150Compat
/// Script160Compat
/// </summary>
/// <remarks>
/// The default is Script140Compat.
/// </remarks>
public virtual string ScriptCompatibilityOption { get; set; } = "Script140Compat";
/// <summary>
/// Script only features compatible with the specified SQL Server database engine type.
/// Possible Values:
/// SingleInstance
/// SqlAzure
/// </summary>
public virtual string TargetDatabaseEngineType { get; set; } = "SingleInstance";
/// <summary>
/// Script only features compatible with the specified SQL Server database engine edition.
/// Possible Values:
/// SqlServerPersonalEdition
/// SqlServerStandardEdition
/// SqlServerEnterpriseEdition
/// SqlServerExpressEdition
/// SqlAzureDatabaseEdition
/// SqlDatawarehouseEdition
/// SqlServerStretchEdition
/// SqlManagedInstanceEdition
/// SqlServerOnDemandEdition
/// </summary>
public virtual string TargetDatabaseEngineEdition { get; set; } = "SqlServerEnterpriseEdition";
/// <summary>
/// Script all logins available on the server. Passwords will not be scripted.
/// </summary>
public virtual bool? ScriptLogins { get; set; } = false;
/// <summary>
/// Generate object-level permissions.
/// </summary>
public virtual bool? ScriptObjectLevelPermissions { get; set; } = false;
/// <summary>
/// Script owner for the objects.
/// </summary>
public virtual bool? ScriptOwner { get; set; } = false;
/// <summary>
/// Script statistics, and optionally include histograms, for each selected table or view.
/// Possible values:
/// ScriptStatsNone
/// ScriptStatsDDL
/// ScriptStatsAll
/// </summary>
/// <remarks>
/// The default value is ScriptStatsNone.
/// </remarks>
public virtual string ScriptStatistics { get; set; } = "ScriptStatsNone";
/// <summary>
/// Generate USE DATABASE statement.
/// </summary>
public virtual bool? ScriptUseDatabase { get; set; } = true;
/// <summary>
/// Generate script that contains schema only or schema and data.
/// Possible Values:
/// SchemaAndData
/// DataOnly
/// SchemaOnly
/// </summary>
/// <remarks>
/// The default value is SchemaOnly.
/// </remarks>
public virtual string TypeOfDataToScript { get; set; } = "SchemaOnly";
/// <summary>
/// Scripts the change tracking information.
/// </summary>
public virtual bool? ScriptChangeTracking { get; set; } = false;
/// <summary>
/// Script the check constraints for each table or view scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public virtual bool? ScriptCheckConstraints { get; set; } = true;
/// <summary>
/// Scripts the data compression information.
/// </summary>
public virtual bool? ScriptDataCompressionOptions { get; set; } = false;
/// <summary>
/// Script the foreign keys for each table scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public virtual bool? ScriptForeignKeys { get; set; } = true;
/// <summary>
/// Script the full-text indexes for each table or indexed view scripted.
/// </summary>
public virtual bool? ScriptFullTextIndexes { get; set; } = true;
/// <summary>
/// Script the indexes (including XML and clustered indexes) for each table or indexed view scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public virtual bool? ScriptIndexes { get; set; } = true;
/// <summary>
/// Script the primary keys for each table or view scripted
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public virtual bool? ScriptPrimaryKeys { get; set; } = true;
/// <summary>
/// Script the triggers for each table or view scripted
/// </summary>
public virtual bool? ScriptTriggers { get; set; } = true;
/// <summary>
/// Script the unique keys for each table or view scripted.
/// </summary>
/// <remarks>
/// The default value is true.
/// </remarks>
public virtual bool? UniqueKeys { get; set; } = true;
}
}

View File

@@ -0,0 +1,209 @@
//
// 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.IO;
using System.Reflection;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.SqlCore.Connection;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
using static Microsoft.SqlServer.Management.SqlScriptPublish.SqlScriptOptions;
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Base class for all SMO scripting operations
/// </summary>
public abstract class SmoScriptingOperation : ScriptingOperation
{
private bool disposed = false;
public SmoScriptingOperation(ScriptingParams parameters)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
}
protected ScriptingParams Parameters { get; set; }
public string ScriptText { get; protected set; }
/// <remarks>
/// An event can be completed by the following conditions: success, cancel, error.
/// </remarks>
public event EventHandler<ScriptingCompleteParams> CompleteNotification;
/// <summary>
/// Event raised when a scripting operation has made forward progress.
/// </summary>
public event EventHandler<ScriptingProgressNotificationParams> ProgressNotification;
/// <summary>
/// Event raised when a scripting operation has resolved which database objects will be scripted.
/// </summary>
public event EventHandler<ScriptingPlanNotificationParams> PlanNotification;
protected virtual void SendCompletionNotificationEvent(ScriptingCompleteParams parameters)
{
this.SetCommonEventProperties(parameters);
this.CompleteNotification?.Invoke(this, parameters);
}
protected virtual void SendProgressNotificationEvent(ScriptingProgressNotificationParams parameters)
{
this.SetCommonEventProperties(parameters);
this.ProgressNotification?.Invoke(this, parameters);
}
protected virtual void SendPlanNotificationEvent(ScriptingPlanNotificationParams parameters)
{
this.SetCommonEventProperties(parameters);
this.PlanNotification?.Invoke(this, parameters);
}
protected virtual void SetCommonEventProperties(ScriptingEventParams parameters)
{
parameters.OperationId = this.OperationId;
}
protected string GetServerNameFromLiveInstance(string connectionString, string azureAccessToken)
{
string serverName = null;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.RetryLogicProvider = SqlRetryProviders.ServerlessDBRetryProvider();
if (azureAccessToken != null)
{
connection.AccessToken = azureAccessToken;
}
connection.Open();
try
{
ServerConnection serverConnection;
if (azureAccessToken == null)
{
serverConnection = new ServerConnection(connection);
}
else
{
serverConnection = new ServerConnection(connection, new AzureAccessToken(azureAccessToken));
}
serverName = serverConnection.TrueName;
}
catch (SqlException e)
{
Logger.Verbose(
string.Format("Exception getting server name", e));
}
}
Logger.Verbose(string.Format("Resolved server name '{0}'", serverName));
return serverName;
}
protected void ValidateScriptDatabaseParams()
{
try
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(this.Parameters.ConnectionString);
}
catch (Exception e)
{
throw new ArgumentException(SR.ScriptingParams_ConnectionString_Property_Invalid, e);
}
if (this.Parameters.FilePath == null && this.Parameters.ScriptDestination != "ToEditor")
{
throw new ArgumentException(SR.ScriptingParams_FilePath_Property_Invalid);
}
else if (this.Parameters.FilePath != null && this.Parameters.ScriptDestination != "ToEditor")
{
if (!Directory.Exists(Path.GetDirectoryName(this.Parameters.FilePath)))
{
throw new ArgumentException(SR.ScriptingParams_FilePath_Property_Invalid);
}
}
}
protected static void PopulateAdvancedScriptOptions(ScriptOptions scriptOptionsParameters, object advancedOptions)
{
if (scriptOptionsParameters == null)
{
Logger.Verbose("No advanced options set, the ScriptOptions object is null.");
return;
}
foreach (PropertyInfo optionPropInfo in scriptOptionsParameters.GetType().GetProperties())
{
PropertyInfo advancedOptionPropInfo = advancedOptions.GetType().GetProperty(optionPropInfo.Name);
if (advancedOptionPropInfo == null)
{
Logger.Warning(string.Format("Invalid property info name {0} could not be mapped to a property on SqlScriptOptions.", optionPropInfo.Name));
continue;
}
object optionValue = optionPropInfo.GetValue(scriptOptionsParameters, index: null);
if (optionValue == null)
{
Logger.Verbose(string.Format("Skipping ScriptOptions.{0} since value is null", optionPropInfo.Name));
continue;
}
//
// The ScriptOptions property types from the request will be either a string or a bool?.
// The SqlScriptOptions property types from SMO will all be an Enum. Using reflection, we
// map the request ScriptOptions values to the SMO SqlScriptOptions values.
//
try
{
object smoValue = null;
if (optionPropInfo.PropertyType == typeof(bool?))
{
if (advancedOptionPropInfo.PropertyType == typeof(bool))
{
smoValue = (bool)optionValue;
}
else
{
smoValue = (bool)optionValue ? BooleanTypeOptions.True : BooleanTypeOptions.False;
}
}
else
{
smoValue = Enum.Parse(advancedOptionPropInfo.PropertyType, (string)optionValue, ignoreCase: true);
}
Logger.Verbose(string.Format("Setting ScriptOptions.{0} to value {1}", optionPropInfo.Name, smoValue));
advancedOptionPropInfo.SetValue(advancedOptions, smoValue);
}
catch (Exception e)
{
Logger.Warning(
string.Format("An exception occurred setting option {0} to value {1}: {2}", optionPropInfo.Name, optionValue, e));
}
}
}
/// <summary>
/// Disposes the scripting operation.
/// </summary>
public override void Dispose()
{
if (!disposed)
{
this.Cancel();
disposed = true;
}
}
}
}