Add scripting API implemented by the SqlScriptPublishModel (#316)

Update the ScriptingService to expose new scripting JSON-RPC APIs that use the SqlScriptPublishModel for script generation.

The SqlScriptPublishModel is the model behind the SSMS scripting wizard. To enable scripting for CLI tools, we've ported SqlScriptPublishModel to .NET Core. The SqlScriptPublishModel wraps the SMO scripting APIs for .sql script generation.

1) Added three new requests to the ScriptingService: ScriptingRequest, ScriptingListObjectsRequest, ScriptingCancelRequest.
2) Generating scripts are long running operations, so the ScriptingRequest and ScriptingListObjectsRequest kick off a long running scripting task and return immediately.
3) Long running scripting task reports progress and completion, and can be cancelled by a ScriptingCancelRequest request.
4) Bumped the SMO nuget package to 140.17049.0. This new version contains a signed SSMS_Rel build of SMO with the SqlScriptPublishModel.
5) For testing, adding the Northwind database schema

TODO (in later pull requests)
1) Integrate the new ScriptingService APIs with the ConnectionService
2) Integrate with the metadata support recently added
This commit is contained in:
Brian O'Neill
2017-04-24 16:10:20 -07:00
committed by GitHub
parent e65699ef75
commit 4aac4a4047
42 changed files with 7124 additions and 30 deletions

File diff suppressed because one or more lines are too long

View File

@@ -43,10 +43,12 @@ END
public static string CreateDatabaseQuery { get { return CreateDatabaseQueryInstance.Value; } }
public static string TestDbComplexSelectQueries { get { return TestDbSelectQueriesInstance.Value; } }
public static string TestDbComplexSelectQueries { get { return TestDbSelectQueriesInstance.Value; } }
public static string AdventureWorksScript { get { return AdventureWorksScriptInstance.Value; } }
public static string CreateNorthwindSchema { get { return CreateNorthwindSchemaInstance.Value; } }
private static readonly Lazy<string> CreateDatabaseObjectsQueryInstance = new Lazy<string>(() =>
{
return GetScriptFileContent(ResourceNameRefix + "CreateTestDatabaseObjects.sql");
@@ -60,13 +62,18 @@ END
private static readonly Lazy<string> TestDbSelectQueriesInstance = new Lazy<string>(() =>
{
return GetScriptFileContent(ResourceNameRefix + "TestDbTableQueries.sql");
});
});
private static readonly Lazy<string> AdventureWorksScriptInstance = new Lazy<string>(() =>
{
return GetScriptFileContent(ResourceNameRefix + "AdventureWorks.sql");
});
private static readonly Lazy<string> CreateNorthwindSchemaInstance = new Lazy<string>(() =>
{
return GetScriptFileContent(ResourceNameRefix + "CreateNorthwindSchema.sql");
});
private static string GetScriptFileContent(string fileName)
{
string fileContent = string.Empty;

View File

@@ -8,6 +8,7 @@ using System.Globalization;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Xunit;
using System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
{
@@ -24,6 +25,31 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
public bool DoNotCleanupDb { get; set; }
public string ConnectionString
{
get
{
ConnectParams connectParams = TestConnectionProfileService.Instance.GetConnectionParameters(this.ServerType, this.DatabaseName);
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder
{
DataSource = connectParams.Connection.ServerName,
InitialCatalog = connectParams.Connection.DatabaseName,
};
if (connectParams.Connection.AuthenticationType == "Integrated")
{
builder.IntegratedSecurity = true;
}
else
{
builder.UserID = connectParams.Connection.UserName;
builder.Password = connectParams.Connection.Password;
}
return builder.ToString();
}
}
/// <summary>
/// Create the test db if not already exists
/// </summary>
@@ -63,6 +89,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
return CreateNew(serverType, false, null, query);
}
/// <summary>
/// Create the test db if not already exists
/// </summary>
public static SqlTestDb CreateNew(TestServerType serverType)
{
return CreateNew(serverType, false, null, null);
}
/// <summary>
/// Returns a mangled name that unique based on Prefix + Machine + Process
/// </summary>
@@ -115,6 +149,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
return connInfo;
}
/// <summary>
/// Runs the passed query against the test db.
/// </summary>
/// <param name="query">The query to execute.</param>
/// <param name="throwOnError">If true, throw an exception if the query encounters an error executing a batch statement.</param>
public void RunQuery(string query, bool throwOnError = false)
{
TestServiceProvider.Instance.RunQuery(this.ServerType, this.DatabaseName, query, throwOnError);
}
public void Dispose()
{
Cleanup();

View File

@@ -6,10 +6,12 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver;
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
@@ -422,6 +424,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
return result;
}
public async Task<ScriptingListObjectsResult> ListScriptingObjects(ScriptingListObjectsParams parameters)
{
return await Driver.SendRequest(ScriptingListObjectsRequest.Type, parameters);
}
public async Task<ScriptingResult> Script(ScriptingParams parameters)
{
return await Driver.SendRequest(ScriptingRequest.Type, parameters);
}
public async Task<ScriptingCancelResult> CancelScript(string operationId)
{
return await Driver.SendRequest(ScriptingCancelRequest.Type, new ScriptingCancelParams { OperationId = operationId });
}
/// <summary>
/// Waits for a message to be returned by the service
/// </summary>
@@ -438,5 +455,30 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
System.IO.File.WriteAllText(ownerUri, query);
}
}
public bool TryGetEvent<T>(EventType<T> eventType, out T value)
{
value = default(T);
try
{
Task<T> t = this.Driver.WaitForEvent(eventType, TimeSpan.Zero);
value = t.Result;
return true;
}
catch (Exception)
{
return false;
}
}
public void AssertEventNotQueued<T>(EventType<T> eventType)
{
T temp;
if (TryGetEvent(eventType, out temp))
{
Assert.True(false, string.Format("Event of type {0} was found in the queue.", eventType.GetType().FullName, temp.ToString()));
}
}
}
}

View File

@@ -4,11 +4,13 @@
//
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.Credentials;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Workspace;
@@ -44,8 +46,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
{
return CredentialService.Instance;
}
}
}
public ObjectExplorerService ObjectExplorerService
{
get
@@ -74,7 +76,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
/// <summary>
/// Runs a query by calling the services directly (not using the test driver)
/// </summary>
public void RunQuery(TestServerType serverType, string databaseName, string queryText)
public void RunQuery(TestServerType serverType, string databaseName, string queryText, bool throwOnError = false)
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
@@ -82,6 +84,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
Query query = new Query(queryText, connInfo, new QueryExecutionSettings(), MemoryFileSystem.GetFileStreamFactory());
query.Execute();
query.ExecutionTask.Wait();
if (throwOnError)
{
IEnumerable<Batch> errorBatches = query.Batches.Where(b => b.HasError);
if (errorBatches.Count() > 0)
{
throw new InvalidOperationException(
string.Format(
"The query encountered and error. The batches with errors: {0}",
string.Join(Environment.NewLine, errorBatches.Select(b => b.BatchText))));
}
}
}
}

View File

@@ -6,8 +6,9 @@
"includeFiles": [
"Scripts/CreateTestDatabaseObjects.sql",
"Scripts/CreateTestDatabase.sql",
"Scripts/TestDbTableQueries.sql",
"Scripts/AdventureWorks.sql"
"Scripts/AdventureWorks.sql",
"Scripts/CreateNorthwindSchema.sql",
"Scripts/TestDbTableQueries.sql"
]
}
},
@@ -17,7 +18,7 @@
"System.Runtime.Serialization.Primitives": "4.1.1",
"System.Data.Common": "4.1.0",
"System.Data.SqlClient": "4.4.0-sqltools-24613-04",
"Microsoft.SqlServer.Smo": "14.0.17028",
"Microsoft.SqlServer.Smo": "140.17049.0",
"System.Security.SecureString": "4.0.0",
"System.Collections.Specialized": "4.0.1",
"System.ComponentModel.TypeConverter": "4.1.0",