mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-26 01:25:42 -05:00
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:
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements matching logic to filter scripting objects based on an
|
||||
/// include/exclude criteria.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// First, objects are included by the include filter. Then, objects are removed by
|
||||
/// the exclude filter. Matches are made by comparing case insensitive strings for the
|
||||
/// ScriptingObject Type, Schema, and Name properties. Wildcards '*' are supported for
|
||||
/// the ScriptingObject Schema and Name properties. Matching on ScriptingObject Type
|
||||
/// property must be an exact match.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// Include ScriptingObject { Type = null, Schema = "dbo", Name = null }
|
||||
/// -> matches all objects in the dbo schema.
|
||||
///
|
||||
/// Include ScriptingObject { Type = "Table", Schema = "dbo", Name = null }
|
||||
/// -> matches all tables in the dbo schema.
|
||||
///
|
||||
/// Include ScriptingObject { Type = "Table", Schema = null, Name = "Emp*" }
|
||||
/// -> matches all table names that start with "Emp"
|
||||
///
|
||||
/// Include ScriptingObject { Type = "View", Schema = null, Name = "Emp*" }
|
||||
/// Include ScriptingObject { Type = "Table", Schema = null, Name = "Emp*" }
|
||||
/// -> matches all table and views with names that start with "Emp"
|
||||
///
|
||||
/// Include ScriptingObject { Type = "Table", Schema = null, Name = null }
|
||||
/// Exclude ScriptingObject { Type = null, Schema = "HumanResources", Name = null }
|
||||
/// -> matches all tables except tables in the "HumanResources" schema
|
||||
///
|
||||
/// </remarks>
|
||||
public static class ScriptingObjectMatcher
|
||||
{
|
||||
private const string Wildcard = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Given a collection of candidate scripting objects, filters the items that match
|
||||
/// based on the passed include and exclude criteria.
|
||||
/// </summary>
|
||||
/// <param name="includeCriteria">The include object criteria.</param>
|
||||
/// <param name="excludeCriteria">The exclude object criteria.</param>
|
||||
/// <param name="candidates">The candidate object to filter.</param>
|
||||
/// <returns>The matching scripting objects.</returns>
|
||||
public static IEnumerable<ScriptingObject> Match(
|
||||
ScriptingObject includeCriteria,
|
||||
ScriptingObject excludeCriteria,
|
||||
IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
return Match(
|
||||
includeCriteria == null ? new ScriptingObject[0] : new[] { includeCriteria },
|
||||
excludeCriteria == null ? new ScriptingObject[0] : new[] { excludeCriteria },
|
||||
candidates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a collection of candidate scripting objects, filters the items that match
|
||||
/// based on the passed include and exclude criteria.
|
||||
/// </summary>
|
||||
/// <param name="includeCriteria">The collection of include object criteria items.</param>
|
||||
/// <param name="excludeCriteria">The collection of exclude object criteria items.</param>
|
||||
/// <param name="candidates">The candidate object to filter.</param>
|
||||
/// <returns>The matching scripting objects.</returns>
|
||||
public static IEnumerable<ScriptingObject> Match(
|
||||
IEnumerable<ScriptingObject> includeCriteria,
|
||||
IEnumerable<ScriptingObject> excludeCriteria,
|
||||
IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
Validate.IsNotNull("candidates", candidates);
|
||||
|
||||
IEnumerable<ScriptingObject> matchedObjects = new List<ScriptingObject>();
|
||||
|
||||
if (includeCriteria != null && includeCriteria.Any())
|
||||
{
|
||||
foreach (ScriptingObject scriptingObjectCriteria in includeCriteria)
|
||||
{
|
||||
IEnumerable<ScriptingObject> matches = MatchCriteria(scriptingObjectCriteria, candidates);
|
||||
matchedObjects = matchedObjects.Union(matches);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedObjects = candidates;
|
||||
}
|
||||
|
||||
if (excludeCriteria != null)
|
||||
{
|
||||
foreach (ScriptingObject scriptingObjectCriteria in excludeCriteria)
|
||||
{
|
||||
IEnumerable<ScriptingObject> matches = MatchCriteria(scriptingObjectCriteria, candidates);
|
||||
matchedObjects = matchedObjects.Except(matches);
|
||||
}
|
||||
}
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
|
||||
private static IEnumerable<ScriptingObject> MatchCriteria(ScriptingObject criteria, IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
Validate.IsNotNull("criteria", criteria);
|
||||
Validate.IsNotNull("candidates", candidates);
|
||||
|
||||
IEnumerable<ScriptingObject> matchedObjects = candidates;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(criteria.Type))
|
||||
{
|
||||
matchedObjects = matchedObjects.Where(o => string.Equals(criteria.Type, o.Type, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
matchedObjects = MatchCriteria(criteria.Schema, (candidate) => { return candidate.Schema; }, matchedObjects);
|
||||
matchedObjects = MatchCriteria(criteria.Name, (candidate) => { return candidate.Name; }, matchedObjects);
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
|
||||
private static IEnumerable<ScriptingObject> MatchCriteria(string property, Func<ScriptingObject, string> propertySelector, IEnumerable<ScriptingObject> candidates)
|
||||
{
|
||||
IEnumerable<ScriptingObject> matchedObjects = candidates;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(property))
|
||||
{
|
||||
if (property.Equals(Wildcard, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Don't filter any objects
|
||||
}
|
||||
if (property.EndsWith(Wildcard, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
matchedObjects = candidates.Where(o => propertySelector(o).StartsWith(
|
||||
propertySelector(o).Substring(0, propertySelector(o).Length - 1),
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
matchedObjects = matchedObjects.Where(o => string.Equals(property, propertySelector(o), StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
return matchedObjects;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user