Task/script refactor (#446)

* scripting working with race conditions

* new service works with no race conditions

* use new scripting service and commented out tests

* refactored peek definition to use mssql-scripter

* fixed peek definition tests

* removed auto gen comment

* fixed peek definition highlighting bug

* made scripting async and fixed event handlers

* fixed tests (without cancel and plan notifs)

* removed dead code

* added nuget package

* CR comments + select script service implementation

* minor fixes and added test

* CR comments and script select

* added unit tests

* code review comments and cleanup
This commit is contained in:
Aditya Bist
2017-10-02 12:02:43 -07:00
committed by GitHub
parent 9d898f0d0c
commit e7756b0bf1
24 changed files with 718 additions and 993 deletions

View File

@@ -101,6 +101,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
/// ScriptCreate
/// ScriptDrop
/// ScriptCreateDrop
/// ScriptSelect
/// </summary>
/// <remarks>
/// The default is ScriptCreate.

View File

@@ -67,6 +67,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
/// 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>
@@ -75,6 +81,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
public class ScriptingResult
{
public string OperationId { get; set; }
public string Script { get; set; }
}
/// <summary>

View File

@@ -1,54 +0,0 @@
//
// 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.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Metadata.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
{
/// <summary>
/// The type of scripting operation requested
/// </summary>
public enum ScriptOperation
{
Select = 0,
Create = 1,
Insert = 2,
Update = 3,
Delete = 4
}
/// <summary>
/// Script as request parameter type
/// </summary>
public class ScriptingScriptAsParams
{
public string OwnerUri { get; set; }
public ScriptOperation Operation { get; set; }
public ObjectMetadata Metadata { get; set; }
}
/// <summary>
/// Script as request result type
/// </summary>
public class ScriptingScriptAsResult
{
public string OwnerUri { get; set; }
public string Script { get; set; }
}
/// <summary>
/// Script as request message type
/// </summary>
public class ScriptingScriptAsRequest
{
public static readonly
RequestType<ScriptingScriptAsParams, ScriptingScriptAsResult> Type =
RequestType<ScriptingScriptAsParams, ScriptingScriptAsResult>.Create("scripting/scriptas");
}
}

View File

@@ -2,10 +2,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// THIS FILE IS GENERATED BY A CODEGEN TOOL. DO NOT EDIT!!!!
// IF YOU NEED TO MAKE CHANGES, EDIT THE .TT FILE!!!
// To regenerate either (1) save the .TT file from Visual Studio or (2) run the build script codegen task.
using System;
using System.IO;
@@ -14,235 +10,67 @@ using System.Collections.Specialized;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlServer.Management.Common;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
internal partial class Scripter
{
private void Initialize()
{
AddSupportedType(DeclarationType.Table, GetTableScripts, "Table", "table");
AddSupportedType(DeclarationType.View, GetViewScripts, "View", "view");
AddSupportedType(DeclarationType.StoredProcedure, GetStoredProcedureScripts, "Procedure", "stored procedure");
AddSupportedType(DeclarationType.Schema, GetSchemaScripts, "Schema", "schema");
AddSupportedType(DeclarationType.Database, GetDatabaseScripts, "Database", "database");
AddSupportedType(DeclarationType.UserDefinedDataType, GetUserDefinedDataTypeScripts, "Type", "user-defined data type");
AddSupportedType(DeclarationType.UserDefinedTableType, GetUserDefinedTableTypeScripts, "Type", "user-defined table type");
AddSupportedType(DeclarationType.Synonym, GetSynonymScripts, "Synonym", "");
AddSupportedType(DeclarationType.ScalarValuedFunction, GetScalarValuedFunctionScripts, "Function", "scalar-valued function");
AddSupportedType(DeclarationType.TableValuedFunction, GetTableValuedFunctionScripts, "Function", "table-valued function");
}
// Instantiate the mapping dictionaries
/// <summary>
/// Script a Table using SMO
/// </summary>
/// <param name="objectName">Table name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetTableScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
Table smoObject = string.IsNullOrEmpty(schemaName) ? new Table(this.Database, objectName) : new Table(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetTableScripts : " + ex.Message);
return null;
}
}
// Mapping for supported type
AddSupportedType(DeclarationType.Table, "Table", "table", typeof(Table));
AddSupportedType(DeclarationType.View, "View", "view", typeof(View));
AddSupportedType(DeclarationType.StoredProcedure, "Procedure", "stored procedure", typeof(StoredProcedure));
AddSupportedType(DeclarationType.Schema, "Schema", "schema", typeof(Schema));
AddSupportedType(DeclarationType.UserDefinedDataType, "UserDefinedDataType", "user-defined data type", typeof(UserDefinedDataType));
AddSupportedType(DeclarationType.UserDefinedTableType, "UserDefinedTableType", "user-defined table type", typeof(UserDefinedTableType));
AddSupportedType(DeclarationType.Synonym, "Synonym", "", typeof(Synonym));
AddSupportedType(DeclarationType.ScalarValuedFunction, "Function", "scalar-valued function", typeof(UserDefinedFunction));
AddSupportedType(DeclarationType.TableValuedFunction, "Function", "table-valued function", typeof(UserDefinedFunction));
/// <summary>
/// Script a View using SMO
/// </summary>
/// <param name="objectName">View name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetViewScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
View smoObject = string.IsNullOrEmpty(schemaName) ? new View(this.Database, objectName) : new View(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetViewScripts : " + ex.Message);
return null;
}
}
// Mapping for database engine edition
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Unknown, "SqlServerEnterpriseEdition"); //default case
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Personal, "SqlServerPersonalEdition");
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Standard, "SqlServerStandardEdition");
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Enterprise, "SqlServerEnterpriseEdition");
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.Express, "SqlServerExpressEdition");
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlDatabase, "SqlAzureDatabaseEdition");
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlDataWarehouse, "SqlDatawarehouseEdition");
targetDatabaseEngineEditionMap.Add(DatabaseEngineEdition.SqlStretchDatabase, "SqlServerStretchEdition");
/// <summary>
/// Script a StoredProcedure using SMO
/// </summary>
/// <param name="objectName">StoredProcedure name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetStoredProcedureScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
StoredProcedure smoObject = string.IsNullOrEmpty(schemaName) ? new StoredProcedure(this.Database, objectName) : new StoredProcedure(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetStoredProcedureScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a Schema using SMO
/// </summary>
/// <param name="objectName">Schema name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetSchemaScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
Schema smoObject = new Schema(this.Database, objectName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetSchemaScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a Database using SMO
/// </summary>
/// <param name="objectName">Database name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetDatabaseScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
Database smoObject = new Database(new Server(this.serverConnection), objectName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetDatabaseScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a UserDefinedDataType using SMO
/// </summary>
/// <param name="objectName">UserDefinedDataType name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetUserDefinedDataTypeScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
UserDefinedDataType smoObject = string.IsNullOrEmpty(schemaName) ? new UserDefinedDataType(this.Database, objectName) : new UserDefinedDataType(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetUserDefinedDataTypeScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a UserDefinedTableType using SMO
/// </summary>
/// <param name="objectName">UserDefinedTableType name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetUserDefinedTableTypeScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
UserDefinedTableType smoObject = string.IsNullOrEmpty(schemaName) ? new UserDefinedTableType(this.Database, objectName) : new UserDefinedTableType(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetUserDefinedTableTypeScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a Synonym using SMO
/// </summary>
/// <param name="objectName">Synonym name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetSynonymScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
Synonym smoObject = string.IsNullOrEmpty(schemaName) ? new Synonym(this.Database, objectName) : new Synonym(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetSynonymScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a ScalarValuedFunction using SMO
/// </summary>
/// <param name="objectName">ScalarValuedFunction name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetScalarValuedFunctionScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
UserDefinedFunction smoObject = string.IsNullOrEmpty(schemaName) ? new UserDefinedFunction(this.Database, objectName) : new UserDefinedFunction(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetScalarValuedFunctionScripts : " + ex.Message);
return null;
}
}
/// <summary>
/// Script a TableValuedFunction using SMO
/// </summary>
/// <param name="objectName">TableValuedFunction name</param>
/// <param name="schemaName">Schema name</param>
/// <returns>String collection of scripts</returns>
internal StringCollection GetTableValuedFunctionScripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)
{
try
{
UserDefinedFunction smoObject = string.IsNullOrEmpty(schemaName) ? new UserDefinedFunction(this.Database, objectName) : new UserDefinedFunction(this.Database, objectName, schemaName);
smoObject.Refresh();
return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);
}
catch (Exception ex)
{
Logger.Write(LogLevel.Error,"Exception at PeekDefinition GetTableValuedFunctionScripts : " + ex.Message);
return null;
}
}
// Mapping for database engine type
serverVersionMap.Add(9, "Script90Compat");
serverVersionMap.Add(10, "Script100Compat");
serverVersionMap.Add(11, "Script110Compat");
serverVersionMap.Add(12, "Script120Compat");
serverVersionMap.Add(13, "Script140Compat");
serverVersionMap.Add(14, "Script140Compat");
// Mapping the object types for scripting
objectScriptMap.Add("table", "Table");
objectScriptMap.Add("view", "View");
objectScriptMap.Add("storedprocedure", "Procedure");
objectScriptMap.Add("userdefinedfunction", "Function");
objectScriptMap.Add("userdefineddataType", "Type");
objectScriptMap.Add("user", "User");
objectScriptMap.Add("default", "Default");
objectScriptMap.Add("rule", "Rule");
objectScriptMap.Add("databaserole", "Role");
objectScriptMap.Add("applicationrole", "Application Role");
objectScriptMap.Add("sqlassembly", "Assembly");
objectScriptMap.Add("ddltrigger", "Trigger");
objectScriptMap.Add("synonym", "Synonym");
objectScriptMap.Add("xmlschemacollection", "Xml Schema Collection");
objectScriptMap.Add("schema", "Schema");
objectScriptMap.Add("planguide", "sp_create_plan_guide");
objectScriptMap.Add("userdefinedType", "Type");
objectScriptMap.Add("userdefinedaggregate", "Aggregate");
objectScriptMap.Add("fulltextcatalog", "Fulltext Catalog");
objectScriptMap.Add("userdefinedtabletype", "Type");
}
}
}

View File

@@ -1,157 +0,0 @@
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// THIS FILE IS GENERATED BY A CODEGEN TOOL. DO NOT EDIT!!!!
// IF YOU NEED TO MAKE CHANGES, EDIT THE .TT FILE!!!
// To regenerate either (1) save the .TT file from Visual Studio or (2) run the build script codegen task.
using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
internal partial class Scripter
{
<#
///
/// Generate Initialize method
///
var indent = " ";
var directory = Path.GetDirectoryName(Host.TemplateFile);
string xmlFile = Path.Combine(directory, "ScripterSupportedTypes.xml");
var supportedTypes = GetSupportedTypes(xmlFile);
if (supportedTypes != null && supportedTypes.Count > 0)
{
WriteLine("private void Initialize()");
PushIndent(indent);
PushIndent(indent);
WriteLine("{");
PushIndent(indent);
foreach(var typeProperty in supportedTypes)
{
string functionCall = string.Format("AddSupportedType(DeclarationType.{0}, Get{0}Scripts, \"{1}\", \"{2}\");", typeProperty["Name"], typeProperty["CreateSyntax"], typeProperty["QuickInfoType"]);
WriteLine(functionCall);
}
PopIndent();
WriteLine("}\n");
///
/// Generate scriptGetters for each type
///
foreach(var typeProperty in supportedTypes)
{
string statement;
// Write comments
WriteLine("/// <summary>");
WriteLine(string.Format("/// Script a {0} using SMO", typeProperty["Name"]));
WriteLine("/// </summary>");
WriteLine(string.Format("/// <param name=\"objectName\">{0} name</param>", typeProperty["Name"]));
WriteLine(string.Format("/// <param name=\"schemaName\">Schema name</param>"));
WriteLine("/// <returns>String collection of scripts</returns>");
WriteLine(string.Format("internal StringCollection Get{0}Scripts(string objectName, string schemaName, ScriptingOptions scriptingOptions = null)", typeProperty["Name"]));
WriteLine("{");
PushIndent(indent);
// Write try block to retrieve object and return script
WriteLine("try");
WriteLine("{");
if(typeProperty["SupportsSchemaQuery"].IndexOf("true", StringComparison.OrdinalIgnoreCase) >= 0)
{
statement = string.Format("{0} smoObject = string.IsNullOrEmpty(schemaName) ? new {0}(this.Database, objectName) : new {0}(this.Database, objectName, schemaName);", typeProperty["AccessClass"]);
}
else
{
// If it's a database
if (typeProperty["AccessClass"] == "Database")
{
statement = string.Format("{0} smoObject = new {0}(new Server(this.serverConnection), objectName);", typeProperty["Name"]);
}
else
{
statement = string.Format("{0} smoObject = new {0}(this.Database, objectName);", typeProperty["Name"]);
}
}
PushIndent(indent);
WriteLine(statement);
WriteLine("smoObject.Refresh();");
WriteLine("return (scriptingOptions == null) ? smoObject.Script() : smoObject.Script(scriptingOptions);");
PopIndent();
WriteLine("}");
// Write catch block to catch and log exceptions
WriteLine("catch (Exception ex)");
WriteLine("{");
PushIndent(indent);
statement = string.Format("LogLevel.Error,\"Exception at PeekDefinition Get{0}Scripts : \" + ex.Message", typeProperty["Name"]);
WriteLine("Logger.Write(" + statement + ");");
WriteLine("return null;");
PopIndent();
WriteLine("}");
PopIndent();
WriteLine("}\n");
}
}
PopIndent();
PopIndent();
#>
}
}
<#+
///
/// Get the supported types from the xml file
///
public static List<Dictionary<string, string>> GetSupportedTypes(string xmlFile)
{
List<Dictionary<string, string>> typeList = null;
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList supportedTypes = doc.SelectNodes("/SupportedTypes/Type");
if (supportedTypes != null)
{
typeList = new List<Dictionary<string, string>>();
foreach (var type in supportedTypes)
{
XmlElement node = type as XmlElement;
if (node != null)
{
string typeName = (node["Name"] != null) ? node["Name"].InnerText : null;
string createSyntax = (node["CreateSyntax"] != null) ? node["CreateSyntax"].InnerText : null;
string accessClass = (node["AccessClass"] != null) ? node["AccessClass"].InnerText : null;
string supportsSchemaQuery = (node["SupportsSchemaQuery"] != null) ? node["SupportsSchemaQuery"].InnerText : null;
string quickInfoType = (node["QuickInfoType"] != null) ? node["QuickInfoType"].InnerText : null;
if (typeName != null && createSyntax != null && accessClass != null && supportsSchemaQuery!= null)
{
Dictionary<string, string> typeProperties = new Dictionary<string, string>();
typeProperties.Add("Name", typeName);
typeProperties.Add("CreateSyntax", createSyntax);
typeProperties.Add("AccessClass", accessClass);
typeProperties.Add("SupportsSchemaQuery", supportsSchemaQuery);
typeProperties.Add("QuickInfoType", quickInfoType);
typeList.Add(typeProperties);
}
}
}
}
return typeList;
}
#>

View File

@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data.Common;
using System.IO;
using System.Linq;
@@ -19,10 +18,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;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
@@ -35,20 +37,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
private Database database;
private string tempPath;
internal delegate StringCollection ScriptGetter(string objectName, string schemaName, ScriptingOptions scriptingOptions);
// Dictionary that holds the script getter for each type
private Dictionary<DeclarationType, ScriptGetter> sqlScriptGetters =
new Dictionary<DeclarationType, ScriptGetter>();
private Dictionary<string, ScriptGetter> sqlScriptGettersFromQuickInfo =
new Dictionary<string, ScriptGetter>();
// Dictionary that holds the object name (as appears on the TSQL create statement)
private Dictionary<DeclarationType, string> sqlObjectTypes = new Dictionary<DeclarationType, string>();
private Dictionary<string, string> sqlObjectTypesFromQuickInfo = new Dictionary<string, string>();
private Dictionary<DatabaseEngineEdition, string> targetDatabaseEngineEditionMap = new Dictionary<DatabaseEngineEdition, string>();
private Dictionary<int, string> serverVersionMap = new Dictionary<int, string>();
private Dictionary<string, string> objectScriptMap = new Dictionary<string, string>();
internal Scripter() {}
/// <summary>
/// Initialize a Peek Definition helper object
/// </summary>
@@ -60,7 +61,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
this.tempPath = FileUtilities.GetPeekDefinitionTempFolder();
Initialize();
}
internal Database Database
{
get
@@ -114,13 +115,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
/// <summary>
/// Add the given type, scriptgetter and the typeName string to the respective dictionaries
/// </summary>
private void AddSupportedType(DeclarationType type, ScriptGetter scriptGetter, string typeName, string quickInfoType)
private void AddSupportedType(DeclarationType type, string typeName, string quickInfoType, Type smoObjectType)
{
sqlScriptGetters.Add(type, scriptGetter);
sqlObjectTypes.Add(type, typeName);
if (!string.IsNullOrEmpty(quickInfoType))
{
sqlScriptGettersFromQuickInfo.Add(quickInfoType.ToLowerInvariant(), scriptGetter);
sqlObjectTypesFromQuickInfo.Add(quickInfoType.ToLowerInvariant(), typeName);
}
}
@@ -186,7 +185,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
string tokenType = GetTokenTypeFromQuickInfo(quickInfoText, tokenText, caseSensitivity);
if (tokenType != null)
{
if (sqlScriptGettersFromQuickInfo.ContainsKey(tokenType.ToLowerInvariant()))
if (sqlObjectTypesFromQuickInfo.ContainsKey(tokenType.ToLowerInvariant()))
{
// With SqlLogin authentication, the defaultSchema property throws an Exception when accessed.
// This workaround ensures that a schema name is present by attempting
@@ -198,7 +197,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
}
Location[] locations = GetSqlObjectDefinition(
sqlScriptGettersFromQuickInfo[tokenType.ToLowerInvariant()],
tokenText,
schemaName,
sqlObjectTypesFromQuickInfo[tokenType.ToLowerInvariant()]
@@ -224,13 +222,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
/// <summary>
/// Script a object using the type extracted from declarationItem
/// </summary>
/// <param name="declarationItem">The Declarartion object that matched with the selected token</param>
/// <param name="declarationItem">The Declaration object that matched with the selected token</param>
/// <param name="tokenText">The text of the selected token</param>
/// <param name="schemaName">Schema name</param>
/// <returns></returns>
internal DefinitionResult GetDefinitionUsingDeclarationType(DeclarationType type, string databaseQualifiedName, string tokenText, string schemaName)
{
if (sqlScriptGetters.ContainsKey(type) && sqlObjectTypes.ContainsKey(type))
if (sqlObjectTypes.ContainsKey(type))
{
// With SqlLogin authentication, the defaultSchema property throws an Exception when accessed.
// This workaround ensures that a schema name is present by attempting
@@ -242,7 +240,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
schemaName = this.GetSchemaFromDatabaseQualifiedName(fullObjectName, tokenText);
}
Location[] locations = GetSqlObjectDefinition(
sqlScriptGetters[type],
tokenText,
schemaName,
sqlObjectTypes[type]
@@ -268,32 +265,43 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
/// <param name="objectType">Type of SQL object</param>
/// <returns>Location object representing URI and range of the script file</returns>
internal Location[] GetSqlObjectDefinition(
ScriptGetter sqlScriptGetter,
string objectName,
string schemaName,
string objectType)
{
StringCollection scripts = sqlScriptGetter(objectName, schemaName, null);
// script file destination
string tempFileName = (schemaName != null) ? Path.Combine(this.tempPath, string.Format("{0}.{1}.sql", schemaName, objectName))
: Path.Combine(this.tempPath, string.Format("{0}.sql", objectName));
if (scripts != null)
{
int lineNumber = 0;
using (StreamWriter scriptFile = new StreamWriter(File.Open(tempFileName, FileMode.Create, FileAccess.ReadWrite)))
{
ScriptingScriptOperation operation = InitScriptOperation(objectName, schemaName, objectType);
operation.Execute();
string script = operation.ScriptText;
foreach (string script in scripts)
bool objectFound = false;
int createStatementLineNumber = 0;
File.WriteAllText(tempFileName, script);
string[] lines = File.ReadAllLines(tempFileName);
int lineCount = 0;
string createSyntax = null;
if (objectScriptMap.ContainsKey(objectType.ToLower()))
{
createSyntax = string.Format("CREATE {0}", objectScriptMap[objectType.ToLower()]);
foreach (string line in lines)
{
if (LineContainsObject(line, objectName, createSyntax))
{
string createSyntax = string.Format("CREATE {0}", objectType);
if (script.IndexOf(createSyntax, StringComparison.OrdinalIgnoreCase) >= 0)
{
scriptFile.WriteLine(script);
lineNumber = GetStartOfCreate(script, createSyntax);
}
createStatementLineNumber = lineCount;
objectFound = true;
break;
}
lineCount++;
}
return GetLocationFromFile(tempFileName, lineNumber);
}
if (objectFound)
{
Location[] locations = GetLocationFromFile(tempFileName, createStatementLineNumber);
return locations;
}
else
{
@@ -341,33 +349,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
tempFileName = new Uri(tempFileName).AbsoluteUri;
}
// Create a location array containing the tempFile Uri, as expected by VSCode.
Location[] locations = new[] {
new Location {
Location[] locations = new[]
{
new Location
{
Uri = tempFileName,
Range = new Range {
Start = new Position { Line = lineNumber, Character = 1},
End = new Position { Line = lineNumber + 1, Character = 1}
Range = new Range
{
Start = new Position { Line = lineNumber, Character = 0},
End = new Position { Line = lineNumber + 1, Character = 0}
}
}
};
return locations;
}
/// <summary>
/// Get line number for the create statement
/// </summary>
private int GetStartOfCreate(string script, string createString)
{
string[] lines = script.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
for (int lineNumber = 0; lineNumber < lines.Length; lineNumber++)
{
if (lines[lineNumber].IndexOf(createString, StringComparison.OrdinalIgnoreCase) >= 0)
{
return lineNumber;
}
}
return 0;
}
/// <summary>
/// Helper method to create definition error result object
/// </summary>
@@ -453,7 +449,363 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
return Resolver.FindCompletions(
parseResult, parserLine, parserColumn, metadataDisplayInfoProvider);
}
#endregion
/// <summary>
/// Wrapper method that calls Resolver.FindCompletions
/// </summary>
/// <param name="objectName"></param>
/// <param name="schemaName"></param>
/// <param name="objectType"></param>
/// <param name="tempFileName"></param>
/// <returns></returns>
internal ScriptingScriptOperation InitScriptOperation(string objectName, string schemaName, string objectType)
{
// object that has to be scripted
ScriptingObject scriptingObject = new ScriptingObject
{
Name = objectName,
Schema = schemaName,
Type = objectType
};
// scripting options
ScriptOptions options = new ScriptOptions
{
ScriptCreateDrop = "ScriptCreate",
TypeOfDataToScript = "SchemaOnly",
ScriptStatistics = "ScriptStatsNone",
TargetDatabaseEngineEdition = GetTargetDatabaseEngineEdition(),
TargetDatabaseEngineType = GetTargetDatabaseEngineType(),
ScriptCompatibilityOption = GetScriptCompatibilityOption(),
IncludeIfNotExists = true
};
List<ScriptingObject> objectList = new List<ScriptingObject>();
objectList.Add(scriptingObject);
// create parameters for the scripting operation
ScriptingParams parameters = new ScriptingParams
{
ConnectionString = ConnectionService.BuildConnectionString(this.connectionInfo.ConnectionDetails),
ScriptingObjects = objectList,
ScriptOptions = options,
ScriptDestination = "ToEditor"
};
return new ScriptingScriptOperation(parameters);
}
internal string GetTargetDatabaseEngineEdition()
{
DatabaseEngineEdition dbEngineEdition = this.serverConnection.DatabaseEngineEdition;
string dbEngineEditionString = targetDatabaseEngineEditionMap[dbEngineEdition];
return (dbEngineEditionString != null) ? dbEngineEditionString : "SqlServerEnterpriseEdition";
}
internal string GetScriptCompatibilityOption()
{
int serverVersion = this.serverConnection.ServerVersion.Major;
string dbEngineTypeString = serverVersionMap[serverVersion];
return (dbEngineTypeString != null) ? dbEngineTypeString : "Script140Compat";
}
internal string GetTargetDatabaseEngineType()
{
return connectionInfo.IsAzure ? "SqlAzure" : "SingleInstance";
}
internal bool LineContainsObject(string line, string objectName, string createSyntax)
{
if (line.IndexOf(createSyntax, StringComparison.OrdinalIgnoreCase) >= 0 &&
line.IndexOf(objectName, StringComparison.OrdinalIgnoreCase) >=0)
{
return true;
}
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))
{
// 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 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)
{
string script = string.Empty;
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))
{
Table table = (Table)server.GetSmoObject(urn);
table.Refresh();
if (table.IsMemoryOptimized)
{
selectQuery.Append(" WITH (SNAPSHOT)");
}
}
script = selectQuery.ToString();
return script;
}
#endregion
}
}
}

View File

@@ -1,80 +0,0 @@
<!--
For each supported type
Name - As denoted in the Microsoft.SqlServer.Management.SqlParser.Intellisense.DeclarationType Enumeration for the object
Create Syntax - The keyword used in the create t-sql statement for the object - e.g create TABLE, create PROCEDURE etc.
AccessClass - The SMO class denoting the object e.g. Table, UserDefinedTableType etc.
QuickInfoType - The type of the object as displayed in the Babel.CodeObjectQuickInfo.Text string (optional)
SupportsSchemaQuery - Boolean denoting if the object can be queried/accessed using the schema name
-->
<SupportedTypes>
<Type>
<Name>Table</Name>
<CreateSyntax>Table</CreateSyntax>
<AccessClass>Table</AccessClass>
<QuickInfoType>table</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>View</Name>
<CreateSyntax>View</CreateSyntax>
<AccessClass>View</AccessClass>
<QuickInfoType>view</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>StoredProcedure</Name>
<CreateSyntax>Procedure</CreateSyntax>
<AccessClass>StoredProcedure</AccessClass>
<QuickInfoType>stored procedure</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>Schema</Name>
<CreateSyntax>Schema</CreateSyntax>
<AccessClass>Schema</AccessClass>
<QuickInfoType>schema</QuickInfoType>
<SupportsSchemaQuery>false</SupportsSchemaQuery>
</Type>
<Type>
<Name>Database</Name>
<CreateSyntax>Database</CreateSyntax>
<AccessClass>Database</AccessClass>
<QuickInfoType>database</QuickInfoType>
<SupportsSchemaQuery>false</SupportsSchemaQuery>
</Type>
<Type>
<Name>UserDefinedDataType</Name>
<CreateSyntax>Type</CreateSyntax>
<AccessClass>UserDefinedDataType</AccessClass>
<QuickInfoType>user-defined data type</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>UserDefinedTableType</Name>
<CreateSyntax>Type</CreateSyntax>
<AccessClass>UserDefinedTableType</AccessClass>
<QuickInfoType>user-defined table type</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>Synonym</Name>
<CreateSyntax>Synonym</CreateSyntax>
<AccessClass>Synonym</AccessClass>
<QuickInfoType></QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>ScalarValuedFunction</Name>
<CreateSyntax>Function</CreateSyntax>
<AccessClass>UserDefinedFunction</AccessClass>
<QuickInfoType>scalar-valued function</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
<Type>
<Name>TableValuedFunction</Name>
<CreateSyntax>Function</CreateSyntax>
<AccessClass>UserDefinedFunction</AccessClass>
<QuickInfoType>table-valued function</QuickInfoType>
<SupportsSchemaQuery>true</SupportsSchemaQuery>
</Type>
</SupportedTypes>

View File

@@ -39,6 +39,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
private ScriptingParams Parameters { get; set; }
public string ScriptText { get; private set; }
/// <summary>
/// Event raised when a scripting operation has resolved which database objects will be scripted.
/// </summary>
@@ -71,6 +73,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
publishModel.ScriptItemsCollected += this.OnPublishModelScriptItemsCollected;
publishModel.ScriptProgress += this.OnPublishModelScriptProgress;
publishModel.ScriptError += this.OnPublishModelScriptError;
publishModel.AllowSystemObjects = true;
ScriptDestination destination = !string.IsNullOrWhiteSpace(this.Parameters.ScriptDestination)
? (ScriptDestination)Enum.Parse(typeof(ScriptDestination), this.Parameters.ScriptDestination)
@@ -98,6 +101,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
this.eventSequenceNumber,
this.totalScriptedObjectCount,
this.scriptedObjectCount));
ScriptText = publishModel.RawScript;
this.SendCompletionNotificationEvent(new ScriptingCompleteParams
{
@@ -138,23 +143,23 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
private void SendCompletionNotificationEvent(ScriptingCompleteParams parameters)
{
this.SetCommomEventProperties(parameters);
this.SetCommonEventProperties(parameters);
this.CompleteNotification?.Invoke(this, parameters);
}
private void SendPlanNotificationEvent(ScriptingPlanNotificationParams parameters)
{
this.SetCommomEventProperties(parameters);
this.SetCommonEventProperties(parameters);
this.PlanNotification?.Invoke(this, parameters);
}
private void SendProgressNotificationEvent(ScriptingProgressNotificationParams parameters)
{
this.SetCommomEventProperties(parameters);
this.SetCommonEventProperties(parameters);
this.ProgressNotification?.Invoke(this, parameters);
}
private void SetCommomEventProperties(ScriptingEventParams parameters)
private void SetCommonEventProperties(ScriptingEventParams parameters)
{
parameters.OperationId = this.OperationId;
parameters.SequenceNumber = this.eventSequenceNumber;
@@ -234,7 +239,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
publishModel.SelectedObjects.Add(scriptingObject.ToUrn(server, database));
}
return publishModel;
}
@@ -334,11 +338,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
throw new ArgumentException(SR.ScriptingParams_ConnectionString_Property_Invalid, e);
}
if (!Directory.Exists(Path.GetDirectoryName(this.Parameters.FilePath)))
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);
}
}
}
private void OnPublishModelScriptError(object sender, ScriptEventArgs e)

View File

@@ -4,8 +4,10 @@
//
using System;
using System.IO;
using System.Collections.Concurrent;
using System.Collections.Specialized;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Text;
using System.Threading;
@@ -19,6 +21,8 @@ using Microsoft.SqlTools.ServiceLayer.Metadata.Contracts;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Sdk.Sfc;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
@@ -55,7 +59,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
}
return connectionService;
}
set
{
connectionService = value;
@@ -93,7 +96,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
/// <param name="context"></param>
public void InitializeService(ServiceHost serviceHost)
{
serviceHost.SetRequestHandler(ScriptingScriptAsRequest.Type, HandleScriptingScriptAsRequest);
serviceHost.SetRequestHandler(ScriptingRequest.Type, this.HandleScriptExecuteRequest);
serviceHost.SetRequestHandler(ScriptingCancelRequest.Type, this.HandleScriptCancelRequest);
serviceHost.SetRequestHandler(ScriptingListObjectsRequest.Type, this.HandleListObjectsRequest);
@@ -106,6 +108,61 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
});
}
/// <summary>
/// Handles request to get select script for an smo object
/// </summary>
private void HandleScriptSelectRequest(ScriptingParams parameters, RequestContext<ScriptingResult> requestContext)
{
Task.Run(() =>
{
try
{
string script = String.Empty;
ScriptingObject scriptingObject = parameters.ScriptingObjects[0];
// convert owner uri received from parameters to lookup for its
// associated connection and build a connection string out of it
SqlConnection sqlConn = new SqlConnection(parameters.ConnectionString);
ServerConnection serverConn = new ServerConnection(sqlConn);
Server server = new Server(serverConn);
server.DefaultTextMode = true;
SqlConnectionStringBuilder connStringBuilder = new SqlConnectionStringBuilder(parameters.ConnectionString);
string urnString = string.Format(
"Server[@Name='{0}']/Database[@Name='{1}']/{2}[@Name='{3}' {4}]",
server.Name.ToUpper(),
connStringBuilder.InitialCatalog,
scriptingObject.Type,
scriptingObject.Name,
scriptingObject.Schema != null ? string.Format("and @Schema = '{0}'", scriptingObject.Schema) : string.Empty);
Urn urn = new Urn(urnString);
string name = urn.GetNameForType(scriptingObject.Type);
if (string.Compare(name, "ServiceBroker", StringComparison.CurrentCultureIgnoreCase) == 0)
{
script = Scripter.SelectAllValuesFromTransmissionQueue(urn);
}
else
{
if (string.Compare(name, "Queues", StringComparison.CurrentCultureIgnoreCase) == 0 ||
string.Compare(name, "SystemQueues", StringComparison.CurrentCultureIgnoreCase) == 0)
{
script = Scripter.SelectAllValues(urn);
}
else
{
Database db = server.Databases[connStringBuilder.InitialCatalog];
bool isDw = db.IsSqlDw;
script = new Scripter().SelectFromTableOrView(server, urn, isDw);
}
}
requestContext.SendResult(new ScriptingResult { Script = script});
}
catch (Exception e)
{
requestContext.SendError(e);
}
});
}
/// <summary>
/// Handles request to execute start the list objects operation.
/// </summary>
@@ -133,14 +190,34 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
try
{
ScriptingScriptOperation operation = new ScriptingScriptOperation(parameters);
operation.PlanNotification += (sender, e) => this.SendEvent(requestContext, ScriptingPlanNotificationEvent.Type, e);
operation.ProgressNotification += (sender, e) => this.SendEvent(requestContext, ScriptingProgressNotificationEvent.Type, e);
operation.CompleteNotification += (sender, e) => this.SendEvent(requestContext, ScriptingCompleteEvent.Type, e);
// convert owner uri received from parameters to lookup for its
// associated connection and build a connection string out of it
// if a connection string doesn't already exist
if (parameters.ConnectionString == null)
{
ConnectionInfo connInfo;
ScriptingService.ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri, out connInfo);
if (connInfo != null)
{
parameters.ConnectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
}
}
RunTask(requestContext, operation);
// if the scripting operation is for select
if (parameters.ScriptOptions.ScriptCreateDrop == "ScriptSelect")
{
RunSelectTask(parameters, requestContext);
}
else
{
ScriptingScriptOperation operation = new ScriptingScriptOperation(parameters);
operation.PlanNotification += (sender, e) => this.SendEvent(requestContext, ScriptingPlanNotificationEvent.Type, e);
operation.ProgressNotification += (sender, e) => this.SendEvent(requestContext, ScriptingProgressNotificationEvent.Type, e);
operation.CompleteNotification += (sender, e) => this.SendScriptingCompleteEvent(requestContext, ScriptingCompleteEvent.Type, e, operation, parameters.ScriptDestination);
await requestContext.SendResult(new ScriptingResult { OperationId = operation.OperationId });
RunTask(requestContext, operation);
}
}
catch (Exception e)
{
@@ -173,6 +250,27 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
}
}
private async void SendScriptingCompleteEvent<TParams>(RequestContext<ScriptingResult> requestContext, EventType<TParams> eventType, TParams parameters,
ScriptingScriptOperation operation, string scriptDestination)
{
await Task.Run(async () =>
{
await requestContext.SendEvent(eventType, parameters);
if (scriptDestination == "ToEditor")
{
await requestContext.SendResult(new ScriptingResult { OperationId = operation.OperationId, Script = operation.ScriptText });
}
else if (scriptDestination == "ToSingleFile")
{
await requestContext.SendResult(new ScriptingResult { OperationId = operation.OperationId });
}
else
{
await requestContext.SendError(string.Format("Operation {0} failed", operation.ToString()));
}
});
}
/// <summary>
/// Sends a JSON-RPC event.
/// </summary>
@@ -181,6 +279,24 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
Task.Run(async () => await requestContext.SendEvent(eventType, parameters));
}
/// <summary>
/// Runs the async task that performs the scripting operation.
/// </summary>
private void RunSelectTask(ScriptingParams parameters, RequestContext<ScriptingResult> requestContext)
{
Task.Run(() =>
{
try
{
this.HandleScriptSelectRequest(parameters, requestContext);
}
catch (Exception e)
{
requestContext.SendError(e);
}
});
}
/// <summary>
/// Runs the async task that performs the scripting operation.
/// </summary>
@@ -190,7 +306,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
{
try
{
Debug.Assert(!this.ActiveOperations.ContainsKey(operation.OperationId), "Operation id must be unique");
this.ActiveOperations[operation.OperationId] = operation;
operation.Execute();
}
@@ -221,213 +336,5 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
}
}
}
/// <summary>
/// Script create statements for metadata object
/// </summary>
private static string ScriptAsCreate(
IBindingContext bindingContext,
ConnectionInfo connInfo,
ObjectMetadata metadata)
{
Scripter scripter = new Scripter(bindingContext.ServerConnection, connInfo);
StringCollection results = null;
switch(metadata.MetadataTypeName)
{
case ("Table"):
results = scripter.GetTableScripts(metadata.Name, metadata.Schema);
break;
case ("View"):
results = scripter.GetViewScripts(metadata.Name, metadata.Schema);
break;
case("StoredProcedure"):
results = scripter.GetStoredProcedureScripts(metadata.Name, metadata.Schema);
break;
case("Schema"):
results = scripter.GetSchemaScripts(metadata.Name, metadata.Schema);
break;
case("Database"):
results = scripter.GetDatabaseScripts(metadata.Name, metadata.Schema);
break;
default:
results = null;
break;
}
StringBuilder builder = null;
if (results != null)
{
builder = new StringBuilder();
foreach (var result in results)
{
builder.AppendLine(result);
builder.AppendLine();
}
}
return builder != null ? builder.ToString() : null;
}
/// <summary>
/// Not yet implemented
/// </summary>
private static string ScriptAsUpdate(
IBindingContext bindingContext,
ConnectionInfo connInfo,
ObjectMetadata metadata)
{
return null;
}
/// <summary>
/// Not yet implemented
/// </summary>
private static string ScriptAsInsert(
IBindingContext bindingContext,
ConnectionInfo connInfo,
ObjectMetadata metadata)
{
return null;
}
/// <summary>
/// Not yet implemented
/// </summary>
private static string ScriptAsDelete(
IBindingContext bindingContext,
ConnectionInfo connInfo,
ObjectMetadata metadata)
{
Scripter scripter = new Scripter(bindingContext.ServerConnection, connInfo);
StringCollection results = null;
ScriptingOptions options = new ScriptingOptions();
options.ScriptDrops = true;
switch(metadata.MetadataTypeName)
{
case ("Table"):
results = scripter.GetTableScripts(metadata.Name, metadata.Schema, options);
break;
case ("View"):
results = scripter.GetViewScripts(metadata.Name, metadata.Schema, options);
break;
case("StoredProcedure"):
results = scripter.GetStoredProcedureScripts(metadata.Name, metadata.Schema, options);
break;
case("Schema"):
results = scripter.GetSchemaScripts(metadata.Name, metadata.Schema, options);
break;
case("Database"):
results = scripter.GetDatabaseScripts(metadata.Name, metadata.Schema, options);
break;
default:
results = null;
break;
}
StringBuilder builder = null;
if (results != null)
{
builder = new StringBuilder();
foreach (var result in results)
{
builder.AppendLine(result);
builder.AppendLine();
}
}
return builder != null ? builder.ToString() : null;
}
/// <summary>
/// Handle Script As Update requests
/// </summary>
private static string QueueScriptOperation(
ScriptOperation operation,
ConnectionInfo connInfo,
ObjectMetadata metadata)
{
// get or create the current parse info object
ScriptParseInfo parseInfo = LanguageServiceInstance.GetScriptParseInfo(connInfo.OwnerUri);
if (Monitor.TryEnter(parseInfo.BuildingMetadataLock, LanguageService.BindingTimeout))
{
try
{
QueueItem queueItem = LanguageServiceInstance.BindingQueue.QueueBindingOperation(
key: parseInfo.ConnectionKey,
bindingTimeout: ScriptingService.ScriptingOperationTimeout,
bindOperation: (bindingContext, cancelToken) =>
{
if (operation == ScriptOperation.Select)
{
return string.Format(
@"SELECT TOP 1000 * " + Environment.NewLine + @"FROM {0}.{1}",
metadata.Schema, metadata.Name);
}
else if (operation == ScriptOperation.Create)
{
return ScriptAsCreate(bindingContext, connInfo, metadata);
}
else if (operation == ScriptOperation.Update)
{
return ScriptAsUpdate(bindingContext, connInfo, metadata);
}
else if (operation == ScriptOperation.Insert)
{
return ScriptAsInsert(bindingContext, connInfo, metadata);
}
else if (operation == ScriptOperation.Delete)
{
return ScriptAsDelete(bindingContext, connInfo, metadata);
}
else
{
return null;
}
});
queueItem.ItemProcessed.WaitOne();
return queueItem.GetResultAsT<string>();
}
finally
{
Monitor.Exit(parseInfo.BuildingMetadataLock);
}
}
return string.Empty;
}
/// <summary>
/// Handles script as request messages
/// </summary>
/// <param name="scriptingParams"></param>
/// <param name="requestContext"></param>
internal static async Task HandleScriptingScriptAsRequest(
ScriptingScriptAsParams scriptingParams,
RequestContext<ScriptingScriptAsResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ScriptingService.ConnectionServiceInstance.TryFindConnection(
scriptingParams.OwnerUri,
out connInfo);
ObjectMetadata metadata = scriptingParams.Metadata;
string script = string.Empty;
if (connInfo != null)
{
script = QueueScriptOperation(scriptingParams.Operation, connInfo, metadata);
}
await requestContext.SendResult(new ScriptingScriptAsResult
{
OwnerUri = scriptingParams.OwnerUri,
Script = script
});
}
catch (Exception ex)
{
await requestContext.SendError(ex.ToString());
}
}
}
}
}