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

@@ -11,8 +11,6 @@ using System.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.Extensibility
@@ -53,7 +51,6 @@ namespace Microsoft.SqlTools.Extensibility
string assemblyPath = typeof(ExtensionStore).GetTypeInfo().Assembly.Location;
string directory = Path.GetDirectoryName(assemblyPath);
AssemblyLoadContext context = new AssemblyLoader(directory);
var assemblyPaths = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly);
List<Assembly> assemblies = new List<Assembly>();
@@ -77,9 +74,7 @@ namespace Microsoft.SqlTools.Extensibility
try
{
assemblies.Add(
context.LoadFromAssemblyName(
AssemblyLoadContext.GetAssemblyName(path)));
assemblies.Add(Assembly.LoadFrom(path));
}
catch (Exception)
{
@@ -193,7 +188,7 @@ namespace Microsoft.SqlTools.Extensibility
try
{
Logger.Verbose("Loading service assembly: " + path);
assemblies.Add(AssemblyLoadContext.Default.LoadFromAssemblyPath(path));
assemblies.Add(Assembly.LoadFrom(path));
Logger.Verbose("Loaded service assembly: " + path);
}
catch (Exception ex)
@@ -281,13 +276,9 @@ namespace Microsoft.SqlTools.Extensibility
public static ContainerConfiguration WithAssembliesInPath(this ContainerConfiguration configuration, string path, AttributedModelProvider conventions, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
AssemblyLoadContext context = new AssemblyLoader(path);
var assemblyNames = Directory
.GetFiles(path, "*.dll", searchOption)
.Select(AssemblyLoadContext.GetAssemblyName);
var assemblies = assemblyNames
.Select(context.LoadFromAssemblyName)
var assemblyPaths = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly);
var assemblies = assemblyPaths
.Select(path => Assembly.LoadFrom(path))
.ToList();
configuration = configuration.WithAssemblies(assemblies, conventions);
@@ -295,37 +286,4 @@ namespace Microsoft.SqlTools.Extensibility
return configuration;
}
}
public class AssemblyLoader : AssemblyLoadContext
{
private string folderPath;
public AssemblyLoader(string folderPath)
{
this.folderPath = folderPath;
}
protected override Assembly Load(AssemblyName assemblyName)
{
var deps = DependencyContext.Default;
var res = deps.CompileLibraries.Where(d => d.Name.Equals(assemblyName.Name)).ToList();
if (res.Count > 0)
{
return Assembly.Load(new AssemblyName(res.First().Name));
}
else
{
var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
if (File.Exists(apiApplicationFileInfo.FullName))
{
// Creating a new AssemblyContext instance for the same folder puts us at risk
// of loading the same DLL in multiple contexts, which leads to some unpredictable
// behavior in the loader. See https://github.com/dotnet/coreclr/issues/19632
return LoadFromAssemblyPath(apiApplicationFileInfo.FullName);
}
}
return Assembly.Load(assemblyName);
}
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<!-- Targeting both net7.0 and net472 so that other things such as PS cmdlets can use this
which need to support a wider range of machines -->
<TargetFrameworks>net7.0;net472</TargetFrameworks>
<TargetFrameworks>net7.0;net472;netstandard2.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<Nullable>disable</Nullable>
<AssemblyName>Microsoft.SqlTools.ManagedBatchParser</AssemblyName>
@@ -37,7 +37,6 @@
<NuspecProperties>version=$(PackageVersion)</NuspecProperties>
<!-- Disable CA1852 (Seal internal types) as it depends on SrGen Tool -->
<NoWarn>$(NoWarn);CA1852</NoWarn>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" />

View File

@@ -16,6 +16,7 @@ using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlServer.Management.Smo.Agent;
using Microsoft.SqlTools.ServiceLayer.Management;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
namespace Microsoft.SqlTools.ServiceLayer.Agent
{
@@ -43,7 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
}
using (SqlConnection connection = new SqlConnection(ConnectionService.BuildConnectionString(connInfo.ConnectionDetails)))
{
connection.RetryLogicProvider = Connection.ReliableConnection.SqlRetryProviders.ServerlessDBRetryProvider();
connection.RetryLogicProvider = SqlRetryProviders.ServerlessDBRetryProvider();
connection.Open();
using (SqlCommand sqlQueryCommand = new SqlCommand(sqlQuery, connection))
{

View File

@@ -821,22 +821,6 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
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
@@ -845,30 +829,6 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
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);
}
}
public static string BackupTaskName
{
get
@@ -11651,24 +11611,9 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string SqlScriptFormatterScalarTypeMissingScale = "SqlScriptFormatterScalarTypeMissingScale";
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";
public const string BackupTaskName = "BackupTaskName";

View File

@@ -678,30 +678,10 @@
<value>Scalar column missing scale</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>
<data name="BackupTaskName" xml:space="preserve">
<value>Backup Database</value>
<comment></comment>

View File

@@ -330,19 +330,8 @@ SqlScriptFormatterScalarTypeMissingScale = Scalar column missing scale
############################################################################
# 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
############################################################################
# Backup Service
BackupTaskName = Backup Database

View File

@@ -457,21 +457,6 @@
<target state="new">Table or view requested for edit could not be found</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="QueryServiceResultSetHasNoResults">
<source>Query has no results to return</source>
<target state="new">Query has no results to return</target>
@@ -688,21 +673,6 @@
<target state="new">Scalar column missing scale</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>
<trans-unit id="EditDataMultiTableNotSupported">
<source>EditData queries targeting multiple tables are not supported</source>
<target state="new">EditData queries targeting multiple tables are not supported</target>
@@ -7273,6 +7243,11 @@ The Query Processor estimates that implementing the following index could improv
<note>.
Parameters: 0 - table (string) </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>
</body>
</file>
</xliff>

View File

@@ -6,6 +6,7 @@
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
{

View File

@@ -5,41 +5,11 @@
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.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; }
}
/// <summary>
/// Event sent to indicate a scripting operation has completed.
/// </summary>

View File

@@ -7,6 +7,7 @@
using System.Collections.Generic;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
{

View File

@@ -5,27 +5,11 @@
#nullable disable
using System.Collections.Generic;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.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; }
}
/// <summary>
/// Event sent to indicate a script operation has determined which objects will be scripted.
/// </summary>

View File

@@ -6,48 +6,10 @@
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.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; }
}
/// <summary>
/// Event to indicate the scripting operation has made progress.
/// </summary>

View File

@@ -5,83 +5,11 @@
#nullable disable
using System.Collections.Generic;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.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;
}
/// <summary>
/// Parameters returned from a script request.
/// </summary>

View File

@@ -20,14 +20,13 @@ using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
using ConnectionType = Microsoft.SqlTools.ServiceLayer.Connection.ConnectionType;
using Location = Microsoft.SqlTools.ServiceLayer.Workspace.Contracts.Location;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using System.Text;
using System.Data;
using Range = Microsoft.SqlTools.ServiceLayer.Workspace.Contracts.Range;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
@@ -540,303 +539,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
}
return false;
}
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 = ']';
}
internal static class ScriptingUtils
{
/// <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;
}
}
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,
ScriptingUtils.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,
ScriptingUtils.QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// schema
selectQuery.AppendFormat(".{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
ScriptingUtils.QuoteObjectName(urn.GetAttribute("Schema"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// object
selectQuery.AppendFormat(".{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
ScriptingUtils.QuoteObjectName(urn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
//Adding no lock in the end.
selectQuery.AppendFormat(" WITH(NOLOCK)");
script = selectQuery.ToString();
return script;
}
internal 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;
}
internal 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,
ScriptingUtils.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,
ScriptingUtils.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,
ScriptingUtils.QuoteObjectName(dbUrn.GetAttribute("Name"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
}
// schema
selectQuery.AppendFormat("{0}{1}{2}.",
ScriptingGlobals.LeftDelimiter,
ScriptingUtils.QuoteObjectName(urn.GetAttribute("Schema"), ScriptingGlobals.RightDelimiter),
ScriptingGlobals.RightDelimiter);
// object
selectQuery.AppendFormat("{0}{1}{2}",
ScriptingGlobals.LeftDelimiter,
ScriptingUtils.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") && ScriptingUtils.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();
}
#endregion
}
}

View File

@@ -10,6 +10,8 @@ using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Scripting

View File

@@ -8,7 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Scripting

View File

@@ -10,8 +10,9 @@ using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Linq;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{

View File

@@ -18,6 +18,8 @@ using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.ServiceLayer.Utility;
using static Microsoft.SqlTools.Utility.SqlConstants;
using System.Linq;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{

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

@@ -3,9 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Base class for all scripting event parameters.

View File

@@ -3,11 +3,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System;
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Class to represent a database object that can be scripted.

View File

@@ -3,7 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
namespace Microsoft.SqlTools.SqlCore.Scripting.Contracts
{
/// <summary>
/// Scripting Operation type

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

@@ -3,12 +3,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// A wrpaper of ScriptOptions to map the option name with the oen in SMO.ScriptingOptions

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -15,12 +13,13 @@ 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.Scripting.Contracts;
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.ServiceLayer.Scripting
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Class to generate script as for one smo object
@@ -47,7 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
public ScriptAsScriptingOperation(ScriptingParams parameters, string azureAccountToken) : base(parameters)
{
SqlConnection sqlConnection = new SqlConnection(this.Parameters.ConnectionString);
sqlConnection.RetryLogicProvider = Connection.ReliableConnection.SqlRetryProviders.ServerlessDBRetryProvider();
sqlConnection.RetryLogicProvider = SqlRetryProviders.ServerlessDBRetryProvider();
if (azureAccountToken != null)
{
sqlConnection.AccessToken = azureAccountToken;
@@ -176,14 +175,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
// select from service broker
if (string.Compare(typeName, "ServiceBroker", StringComparison.CurrentCultureIgnoreCase) == 0)
{
script = Scripter.SelectAllValuesFromTransmissionQueue(objectUrn);
script = ScriptingHelper.SelectAllValuesFromTransmissionQueue(objectUrn);
}
// select from queues
else if (string.Compare(typeName, "Queues", StringComparison.CurrentCultureIgnoreCase) == 0 ||
string.Compare(typeName, "SystemQueues", StringComparison.CurrentCultureIgnoreCase) == 0)
{
script = Scripter.SelectAllValues(objectUrn);
script = ScriptingHelper.SelectAllValues(objectUrn);
}
// select from table or view
@@ -191,7 +190,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
Database db = server.Databases[databaseName];
bool isDw = db.IsSqlDw;
script = new Scripter().SelectFromTableOrView(server, objectUrn, isDw);
script = ScriptingHelper.SelectFromTableOrView(server, objectUrn, isDw);
}
return script;
@@ -589,10 +588,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
// 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
object targetDatabaseEngineType;
if (Enum.TryParse(typeof(SqlScriptOptions.ScriptDatabaseEngineType), this.Parameters.ScriptOptions.TargetDatabaseEngineType, out targetDatabaseEngineType))
SqlScriptOptions.ScriptDatabaseEngineType targetDatabaseEngineType;
if (Enum.TryParse<SqlScriptOptions.ScriptDatabaseEngineType>(this.Parameters.ScriptOptions.TargetDatabaseEngineType, out targetDatabaseEngineType))
{
switch ((SqlScriptOptions.ScriptDatabaseEngineType)targetDatabaseEngineType)
switch (targetDatabaseEngineType)
{
case SqlScriptOptions.ScriptDatabaseEngineType.SingleInstance:
scriptingOptions.TargetDatabaseEngineType = DatabaseEngineType.Standalone;
@@ -603,10 +602,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
}
}
object targetDatabaseEngineEdition;
if (Enum.TryParse(typeof(SqlScriptOptions.ScriptDatabaseEngineEdition), this.Parameters.ScriptOptions.TargetDatabaseEngineEdition, out targetDatabaseEngineEdition))
SqlScriptOptions.ScriptDatabaseEngineEdition targetDatabaseEngineEdition;
if (Enum.TryParse<SqlScriptOptions.ScriptDatabaseEngineEdition>(this.Parameters.ScriptOptions.TargetDatabaseEngineEdition, out targetDatabaseEngineEdition))
{
switch ((SqlScriptOptions.ScriptDatabaseEngineEdition)targetDatabaseEngineEdition)
switch (targetDatabaseEngineEdition)
{
case SqlScriptOptions.ScriptDatabaseEngineEdition.SqlServerPersonalEdition:
scriptingOptions.TargetDatabaseEngineEdition = DatabaseEngineEdition.Personal;
@@ -650,10 +649,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
// scripting of stats is a combination of the Statistics
// and the OptimizerData flag
object scriptStatistics;
if (Enum.TryParse(typeof(SqlScriptOptions.ScriptStatisticsOptions), this.Parameters.ScriptOptions.ScriptStatistics, out scriptStatistics))
SqlScriptOptions.ScriptStatisticsOptions scriptStatistics;
if (Enum.TryParse<SqlScriptOptions.ScriptStatisticsOptions>(this.Parameters.ScriptOptions.ScriptStatistics, out scriptStatistics))
{
switch ((SqlScriptOptions.ScriptStatisticsOptions)scriptStatistics)
switch (scriptStatistics)
{
case SqlScriptOptions.ScriptStatisticsOptions.ScriptStatsAll:
scriptingOptions.Statistics = true;

View File

@@ -3,22 +3,20 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Management.SqlScriptPublish;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Extension methods used by the scripting service.
/// </summary>
internal static class ScriptingExtensionMethods
public static class ScriptingExtensionMethods
{
/// <summary>
/// Gets the status of a scripting operation for the passed scripting event.

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

@@ -3,13 +3,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System;
using System.Threading;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Base class for scripting operations. Because scripting operations can be very long

View File

@@ -3,9 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Defines the scripting options.

View File

@@ -3,19 +3,18 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System;
using System.IO;
using System.Reflection;
using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
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.ServiceLayer.Scripting
namespace Microsoft.SqlTools.SqlCore.Scripting
{
/// <summary>
/// Base class for all SMO scripting operations
@@ -78,7 +77,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
string serverName = null;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.RetryLogicProvider = Connection.ReliableConnection.SqlRetryProviders.ServerlessDBRetryProvider();
connection.RetryLogicProvider = SqlRetryProviders.ServerlessDBRetryProvider();
if (azureAccessToken != null)
{
connection.AccessToken = azureAccessToken;

View File

@@ -7,9 +7,8 @@ using System;
using System.Threading.Tasks;
using NUnit.Framework;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.CoreSql.ObjectExplorer;
using Microsoft.SqlTools.ServiceLayer.Test.Common.Extensions;
using Microsoft.SqlTools.SqlCore.ObjectExplorer;
using Microsoft.SqlTools.ServiceLayer.Test.Common.Extensions;
using System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer

View File

@@ -0,0 +1,118 @@
//
// 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.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Scripting
{
public class AsyncScriptAsScriptingOperationTests
{
public static IEnumerable<TestCaseData> ScriptAsTestCases
{
get
{
yield return new TestCaseData(
"CREATE TABLE testTable1 (c1 int)",
new ScriptingParams()
{
ScriptDestination = "ToEditor",
ScriptingObjects = new List<ScriptingObject>()
{
new ScriptingObject()
{
Name = "testTable1",
Schema = "dbo",
Type = "Table",
}
},
Operation = ScriptingOperationType.Select,
ScriptOptions = new ScriptOptions()
{
ScriptCreateDrop = "ScriptSelect"
}
},
new List<string>() { "SELECT TOP (1000) [c1]" });
yield return new TestCaseData(
"CREATE TABLE testTable1 (c1 int)",
new ScriptingParams()
{
ScriptDestination = "ToEditor",
ScriptingObjects = new List<ScriptingObject>()
{
new ScriptingObject()
{
Name = "testTable1",
Schema = "dbo",
Type = "Table"
}
},
Operation = ScriptingOperationType.Delete,
ScriptOptions = new ScriptOptions()
{
ScriptCreateDrop = "ScriptDrop"
}
},
new List<string> { "DROP TABLE [dbo].[testTable1]" }
);
yield return new TestCaseData(
@"CREATE TABLE testTable1 (c1 int)
GO
CREATE CLUSTERED INDEX [ClusteredIndex-1] ON [dbo].[testTable1]
(
[c1] ASC
)
GO
",
new ScriptingParams()
{
ScriptDestination = "ToEditor",
ScriptingObjects = new List<ScriptingObject>()
{
new ScriptingObject()
{
Name = "testTable1",
Schema = "dbo",
Type = "Table"
}
},
Operation = ScriptingOperationType.Create,
ScriptOptions = new ScriptOptions()
{
ScriptCreateDrop = "ScriptCreate"
}
},
new List<string> { "CREATE TABLE [dbo].[testTable1]", "CREATE CLUSTERED INDEX [ClusteredIndex-1] ON [dbo].[testTable1]" }
);
}
}
[Test]
[TestCaseSource("ScriptAsTestCases")]
public async Task TestCommonScenarios(
string query, ScriptingParams scriptingParams, List<string> expectedScriptContents)
{
var testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, query, "ScriptingTests");
scriptingParams.ConnectionString = testDb.ConnectionString;
var actualScript = await AsyncScriptAsScriptingOperation.GetScriptAsScript(scriptingParams, null);
foreach(var expectedStr in expectedScriptContents)
{
Assert.That(actualScript, Does.Contain(expectedStr));
}
await testDb.CleanupAsync();
}
}
}

View File

@@ -19,6 +19,8 @@ using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Scripting
{

View File

@@ -8,8 +8,9 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.PerfTests

View File

@@ -20,6 +20,7 @@ using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver;
using Microsoft.SqlTools.ServiceLayer.TestDriver.Utility;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.Test.Common

View File

@@ -12,6 +12,8 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests

View File

@@ -6,8 +6,8 @@
#nullable disable
using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlTools.ServiceLayer.Scripting;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting;
using NUnit.Framework;
using Assert = NUnit.Framework.Assert;

View File

@@ -8,7 +8,8 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.Scripting;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.SqlCore.Scripting;
using Microsoft.SqlTools.SqlCore.Scripting.Contracts;
using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Scripting
@@ -451,7 +452,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Scripting
{
for (int i = 0; i < TestObjects.Length; i++)
{
Assert.AreEqual(Scripter.ScriptingUtils.QuoteObjectName(TestObjects[i]), ExpectedObjects[i]);
Assert.AreEqual(ScriptingHelper.QuoteObjectName(TestObjects[i]), ExpectedObjects[i]);
}
}
}