fixed db trigger, system types and oe tests (#331)

* fixed db trigger, system types and oe tests
This commit is contained in:
Leila Lali
2017-04-28 09:44:45 -07:00
committed by GitHub
parent c4227c2e24
commit c46032c71f
13 changed files with 1082 additions and 565 deletions

View File

@@ -4,7 +4,9 @@
// //
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
{ {
@@ -76,6 +78,22 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
filter = $"{filter} {orPrefix} @{Property} = {proeprtyValue}"; filter = $"{filter} {orPrefix} @{Property} = {proeprtyValue}";
} }
} }
filter = $"({filter})";
return filter;
}
public static string ConcatProperties(IEnumerable<NodeFilter> filters)
{
string filter = "";
var list = filters.ToList();
for (int i = 0; i < list.Count; i++)
{
var value = list[i];
string orPrefix = i == 0 ? "" : "and";
filter = $"{filter} {orPrefix} {value.ToPropertyFilterString()}";
}
filter = $"[{filter}]"; filter = $"[{filter}]";
return filter; return filter;

View File

@@ -1,353 +1,363 @@
// //
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
// //
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Composition; using System.Composition;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlTools.Extensibility; using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting; using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel;
using Microsoft.SqlTools.Utility; using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
{ {
/// <summary> /// <summary>
/// A Service to support querying server and database information as an Object Explorer tree. /// A Service to support querying server and database information as an Object Explorer tree.
/// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API. /// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API.
/// </summary> /// </summary>
[Export(typeof(IHostedService))] [Export(typeof(IHostedService))]
public class ObjectExplorerService : HostedService<ObjectExplorerService>, IComposableService public class ObjectExplorerService : HostedService<ObjectExplorerService>, IComposableService
{ {
internal const string uriPrefix = "objectexplorer://"; internal const string uriPrefix = "objectexplorer://";
// Instance of the connection service, used to get the connection info for a given owner URI // Instance of the connection service, used to get the connection info for a given owner URI
private ConnectionService connectionService; private ConnectionService connectionService;
private IProtocolEndpoint serviceHost; private IProtocolEndpoint serviceHost;
private Dictionary<string, ObjectExplorerSession> sessionMap; private Dictionary<string, ObjectExplorerSession> sessionMap;
private readonly Lazy<Dictionary<string, HashSet<ChildFactory>>> applicableNodeChildFactories; private readonly Lazy<Dictionary<string, HashSet<ChildFactory>>> applicableNodeChildFactories;
private IMultiServiceProvider serviceProvider; private IMultiServiceProvider serviceProvider;
/// <summary> /// <summary>
/// Singleton constructor /// Singleton constructor
/// </summary> /// </summary>
public ObjectExplorerService() public ObjectExplorerService()
{ {
sessionMap = new Dictionary<string, ObjectExplorerSession>(); sessionMap = new Dictionary<string, ObjectExplorerSession>();
applicableNodeChildFactories = new Lazy<Dictionary<string, HashSet<ChildFactory>>>(() => PopulateFactories()); applicableNodeChildFactories = new Lazy<Dictionary<string, HashSet<ChildFactory>>>(() => PopulateFactories());
} }
/// <summary> /// <summary>
/// Internal for testing only /// Internal for testing only
/// </summary> /// </summary>
internal ObjectExplorerService(ExtensionServiceProvider serviceProvider) internal ObjectExplorerService(ExtensionServiceProvider serviceProvider)
: this() : this()
{ {
SetServiceProvider(serviceProvider); SetServiceProvider(serviceProvider);
} }
private Dictionary<string, HashSet<ChildFactory>> ApplicableNodeChildFactories private Dictionary<string, HashSet<ChildFactory>> ApplicableNodeChildFactories
{ {
get get
{ {
return applicableNodeChildFactories.Value; return applicableNodeChildFactories.Value;
} }
} }
/// <summary> /// <summary>
/// As an <see cref="IComposableService"/>, this will be set whenever the service is initialized /// As an <see cref="IComposableService"/>, this will be set whenever the service is initialized
/// via an <see cref="IMultiServiceProvider"/> /// via an <see cref="IMultiServiceProvider"/>
/// </summary> /// </summary>
/// <param name="provider"></param> /// <param name="provider"></param>
public override void SetServiceProvider(IMultiServiceProvider provider) public override void SetServiceProvider(IMultiServiceProvider provider)
{ {
Validate.IsNotNull(nameof(provider), provider); Validate.IsNotNull(nameof(provider), provider);
serviceProvider = provider; serviceProvider = provider;
connectionService = provider.GetService<ConnectionService>(); connectionService = provider.GetService<ConnectionService>();
} }
/// <summary> /// <summary>
/// Initializes the service with the service host and registers request handlers. /// Initializes the service with the service host and registers request handlers.
/// </summary> /// </summary>
/// <param name="serviceHost">The service host instance to register with</param> /// <param name="serviceHost">The service host instance to register with</param>
public override void InitializeService(IProtocolEndpoint serviceHost) public override void InitializeService(IProtocolEndpoint serviceHost)
{ {
Logger.Write(LogLevel.Verbose, "ObjectExplorer service initialized"); Logger.Write(LogLevel.Verbose, "ObjectExplorer service initialized");
this.serviceHost = serviceHost; this.serviceHost = serviceHost;
// Register handlers for requests // Register handlers for requests
serviceHost.SetRequestHandler(CreateSessionRequest.Type, HandleCreateSessionRequest); serviceHost.SetRequestHandler(CreateSessionRequest.Type, HandleCreateSessionRequest);
serviceHost.SetRequestHandler(ExpandRequest.Type, HandleExpandRequest); serviceHost.SetRequestHandler(ExpandRequest.Type, HandleExpandRequest);
} }
internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext<CreateSessionResponse> context) public void CloseSession(string uri)
{ {
Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest"); ObjectExplorerSession session;
Func<Task<CreateSessionResponse>> doCreateSession = async () => if (sessionMap.TryGetValue(uri, out session))
{ {
Validate.IsNotNull(nameof(connectionDetails), connectionDetails); // Establish a connection to the specified server/database
Validate.IsNotNull(nameof(context), context); sessionMap.Remove(session.Uri);
}
string uri = GenerateUri(connectionDetails); }
ObjectExplorerSession session; internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext<CreateSessionResponse> context)
if (!sessionMap.TryGetValue(uri, out session)) {
{ Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest");
// Establish a connection to the specified server/database Func<Task<CreateSessionResponse>> doCreateSession = async () =>
session = await DoCreateSession(connectionDetails, uri); {
} Validate.IsNotNull(nameof(connectionDetails), connectionDetails);
Validate.IsNotNull(nameof(context), context);
CreateSessionResponse response;
if (session == null) string uri = GenerateUri(connectionDetails);
{
response = new CreateSessionResponse() { Success = false }; ObjectExplorerSession session;
} if (!sessionMap.TryGetValue(uri, out session))
else {
{ // Establish a connection to the specified server/database
// Else we have a session available, response with existing session information session = await DoCreateSession(connectionDetails, uri);
response = new CreateSessionResponse() }
{
Success = true, CreateSessionResponse response;
RootNode = session.Root.ToNodeInfo(), if (session == null)
SessionId = session.Uri {
}; response = new CreateSessionResponse() { Success = false };
} }
return response; else
}; {
// Else we have a session available, response with existing session information
await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest"); response = new CreateSessionResponse()
} {
Success = true,
internal async Task<NodeInfo[]> ExpandNode(ObjectExplorerSession session, string nodePath) RootNode = session.Root.ToNodeInfo(),
{ SessionId = session.Uri
return await Task.Factory.StartNew(() => };
{ }
NodeInfo[] nodes = null; return response;
TreeNode node = session.Root.FindNodeByPath(nodePath); };
if(node != null)
{ await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest");
nodes = node.Expand().Select(x => x.ToNodeInfo()).ToArray(); }
}
return nodes; internal async Task<NodeInfo[]> ExpandNode(ObjectExplorerSession session, string nodePath)
}); {
} return await Task.Factory.StartNew(() =>
{
/// <summary> NodeInfo[] nodes = null;
/// Establishes a new session and stores its information TreeNode node = session.Root.FindNodeByPath(nodePath);
/// </summary> if(node != null)
/// <returns><see cref="ObjectExplorerSession"/> object if successful, null if unsuccessful</returns> {
internal async Task<ObjectExplorerSession> DoCreateSession(ConnectionDetails connectionDetails, string uri) nodes = node.Expand().Select(x => x.ToNodeInfo()).ToArray();
{ }
ObjectExplorerSession session; return nodes;
});
ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails }; }
ConnectionCompleteParams connectionResult = await Connect(connectParams); /// <summary>
if (connectionResult == null) /// Establishes a new session and stores its information
{ /// </summary>
return null; /// <returns><see cref="ObjectExplorerSession"/> object if successful, null if unsuccessful</returns>
} internal async Task<ObjectExplorerSession> DoCreateSession(ConnectionDetails connectionDetails, string uri)
{
session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider); ObjectExplorerSession session;
sessionMap[uri] = session;
return session; ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails };
}
ConnectionCompleteParams connectionResult = await Connect(connectParams);
if (connectionResult == null)
private async Task<ConnectionCompleteParams> Connect(ConnectParams connectParams) {
{ return null;
try }
{
// open connection based on request details session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider);
ConnectionCompleteParams result = await connectionService.Connect(connectParams); sessionMap[uri] = session;
if(result != null && !string.IsNullOrEmpty(result.ConnectionId)) return session;
{ }
return result;
}
else private async Task<ConnectionCompleteParams> Connect(ConnectParams connectParams)
{ {
await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result); try
return null; {
} // open connection based on request details
ConnectionCompleteParams result = await connectionService.Connect(connectParams);
} if(result != null && !string.IsNullOrEmpty(result.ConnectionId))
catch (Exception ex) {
{ return result;
// Send a connection failed error message in this case. }
ConnectionCompleteParams result = new ConnectionCompleteParams() else
{ {
Messages = ex.ToString() await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
}; return null;
await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result); }
return null;
} }
} catch (Exception ex)
{
internal async Task HandleExpandRequest(ExpandParams expandParams, RequestContext<ExpandResponse> context) // Send a connection failed error message in this case.
{ ConnectionCompleteParams result = new ConnectionCompleteParams()
Logger.Write(LogLevel.Verbose, "HandleExpandRequest"); {
Func<Task<ExpandResponse>> expandNode = async () => Messages = ex.ToString()
{ };
Validate.IsNotNull(nameof(expandParams), expandParams); await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result);
Validate.IsNotNull(nameof(context), context); return null;
}
string uri = expandParams.SessionId; }
ObjectExplorerSession session = null;
NodeInfo[] nodes = null; internal async Task HandleExpandRequest(ExpandParams expandParams, RequestContext<ExpandResponse> context)
if (sessionMap.ContainsKey(uri)) {
{ Logger.Write(LogLevel.Verbose, "HandleExpandRequest");
session = sessionMap[uri]; Func<Task<ExpandResponse>> expandNode = async () =>
} {
else Validate.IsNotNull(nameof(expandParams), expandParams);
{ Validate.IsNotNull(nameof(context), context);
//TODO: error
} string uri = expandParams.SessionId;
ObjectExplorerSession session = null;
NodeInfo[] nodes = null;
if (session != null) if (sessionMap.ContainsKey(uri))
{ {
// Establish a connection to the specified server/database session = sessionMap[uri];
nodes = await ExpandNode(session, expandParams.NodePath); }
} else
{
ExpandResponse response; //TODO: error
response = new ExpandResponse() { Nodes = nodes, SessionId = uri }; }
return response;
};
if (session != null)
await HandleRequestAsync(expandNode, context, "HandleExpandRequest"); {
} // Establish a connection to the specified server/database
nodes = await ExpandNode(session, expandParams.NodePath);
private async Task HandleRequestAsync<T>(Func<Task<T>> handler, RequestContext<T> requestContext, string requestType) }
{
Logger.Write(LogLevel.Verbose, requestType); ExpandResponse response;
response = new ExpandResponse() { Nodes = nodes, SessionId = uri };
try return response;
{ };
T result = await handler();
await requestContext.SendResult(result); await HandleRequestAsync(expandNode, context, "HandleExpandRequest");
} }
catch (Exception ex)
{ private async Task HandleRequestAsync<T>(Func<Task<T>> handler, RequestContext<T> requestContext, string requestType)
await requestContext.SendError(ex.ToString()); {
} Logger.Write(LogLevel.Verbose, requestType);
}
try
/// <summary> {
/// Generates a URI for object explorer using a similar pattern to Mongo DB (which has URI-based database definition) T result = await handler();
/// as this should ensure uniqueness await requestContext.SendResult(result);
/// </summary> }
/// <param name="details"></param> catch (Exception ex)
/// <returns>string representing a URI</returns> {
/// <remarks>Internal for testing purposes only</remarks> await requestContext.SendError(ex.ToString());
internal static string GenerateUri(ConnectionDetails details) }
{ }
Validate.IsNotNull("details", details);
string uri = string.Format(CultureInfo.InvariantCulture, "{0}{1}", uriPrefix, Uri.EscapeUriString(details.ServerName)); /// <summary>
uri = AppendIfExists(uri, "databaseName", details.DatabaseName); /// Generates a URI for object explorer using a similar pattern to Mongo DB (which has URI-based database definition)
uri = AppendIfExists(uri, "user", details.UserName); /// as this should ensure uniqueness
return uri; /// </summary>
} /// <param name="details"></param>
/// <returns>string representing a URI</returns>
private static string AppendIfExists(string uri, string propertyName, string propertyValue) /// <remarks>Internal for testing purposes only</remarks>
{ internal static string GenerateUri(ConnectionDetails details)
if (!string.IsNullOrEmpty(propertyValue)) {
{ Validate.IsNotNull("details", details);
uri += string.Format(CultureInfo.InvariantCulture, ";{0}={1}", propertyName, Uri.EscapeUriString(propertyValue)); string uri = string.Format(CultureInfo.InvariantCulture, "{0}{1}", uriPrefix, Uri.EscapeUriString(details.ServerName));
} uri = AppendIfExists(uri, "databaseName", details.DatabaseName);
return uri; uri = AppendIfExists(uri, "user", details.UserName);
} return uri;
}
public IEnumerable<ChildFactory> GetApplicableChildFactories(TreeNode item)
{ private static string AppendIfExists(string uri, string propertyName, string propertyValue)
if (ApplicableNodeChildFactories != null) {
{ if (!string.IsNullOrEmpty(propertyValue))
HashSet<ChildFactory> applicableFactories; {
if (ApplicableNodeChildFactories.TryGetValue(item.NodeTypeId.ToString(), out applicableFactories)) uri += string.Format(CultureInfo.InvariantCulture, ";{0}={1}", propertyName, Uri.EscapeUriString(propertyValue));
{ }
return applicableFactories; return uri;
} }
}
return null; public IEnumerable<ChildFactory> GetApplicableChildFactories(TreeNode item)
} {
if (ApplicableNodeChildFactories != null)
internal Dictionary<string, HashSet<ChildFactory>> PopulateFactories() {
{ HashSet<ChildFactory> applicableFactories;
VerifyServicesInitialized(); if (ApplicableNodeChildFactories.TryGetValue(item.NodeTypeId.ToString(), out applicableFactories))
{
var childFactories = new Dictionary<string, HashSet<ChildFactory>>(); return applicableFactories;
// Create our list of all NodeType to ChildFactory objects so we can expand appropriately }
foreach (var factory in serviceProvider.GetServices<ChildFactory>()) }
{ return null;
var parents = factory.ApplicableParents(); }
if (parents != null)
{ internal Dictionary<string, HashSet<ChildFactory>> PopulateFactories()
foreach (var parent in parents) {
{ VerifyServicesInitialized();
AddToApplicableChildFactories(childFactories, factory, parent);
} var childFactories = new Dictionary<string, HashSet<ChildFactory>>();
} // Create our list of all NodeType to ChildFactory objects so we can expand appropriately
} foreach (var factory in serviceProvider.GetServices<ChildFactory>())
return childFactories; {
} var parents = factory.ApplicableParents();
if (parents != null)
private void VerifyServicesInitialized() {
{ foreach (var parent in parents)
if (serviceProvider == null) {
{ AddToApplicableChildFactories(childFactories, factory, parent);
throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet); }
} }
if (connectionService == null) }
{ return childFactories;
throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet); }
}
} private void VerifyServicesInitialized()
{
private static void AddToApplicableChildFactories(Dictionary<string, HashSet<ChildFactory>> childFactories, ChildFactory factory, string parent) if (serviceProvider == null)
{ {
HashSet<ChildFactory> applicableFactories; throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet);
if (!childFactories.TryGetValue(parent, out applicableFactories)) }
{ if (connectionService == null)
applicableFactories = new HashSet<ChildFactory>(); {
childFactories[parent] = applicableFactories; throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet);
} }
applicableFactories.Add(factory); }
}
private static void AddToApplicableChildFactories(Dictionary<string, HashSet<ChildFactory>> childFactories, ChildFactory factory, string parent)
internal class ObjectExplorerSession {
{ HashSet<ChildFactory> applicableFactories;
private ConnectionService connectionService; if (!childFactories.TryGetValue(parent, out applicableFactories))
private IMultiServiceProvider serviceProvider; {
applicableFactories = new HashSet<ChildFactory>();
// TODO decide whether a cache is needed to handle lookups in elements with a large # children childFactories[parent] = applicableFactories;
//private const int Cachesize = 10000; }
//private Cache<string, NodeMapping> cache; applicableFactories.Add(factory);
}
public ObjectExplorerSession(string uri, TreeNode root, IMultiServiceProvider serviceProvider, ConnectionService connectionService)
{ internal class ObjectExplorerSession
Validate.IsNotNullOrEmptyString("uri", uri); {
Validate.IsNotNull("root", root); private ConnectionService connectionService;
Uri = uri; private IMultiServiceProvider serviceProvider;
Root = root;
this.serviceProvider = serviceProvider; // TODO decide whether a cache is needed to handle lookups in elements with a large # children
this.connectionService = connectionService; //private const int Cachesize = 10000;
} //private Cache<string, NodeMapping> cache;
public string Uri { get; private set; } public ObjectExplorerSession(string uri, TreeNode root, IMultiServiceProvider serviceProvider, ConnectionService connectionService)
public TreeNode Root { get; private set; } {
Validate.IsNotNullOrEmptyString("uri", uri);
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider) Validate.IsNotNull("root", root);
{ Uri = uri;
TreeNode rootNode = new ServerNode(response, serviceProvider); Root = root;
var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService<ConnectionService>()); this.serviceProvider = serviceProvider;
this.connectionService = connectionService;
}
public string Uri { get; private set; }
public TreeNode Root { get; private set; }
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider)
{
TreeNode rootNode = new ServerNode(response, serviceProvider);
var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService<ConnectionService>());
if (!ObjectExplorerUtils.IsSystemDatabaseConnection(response.ConnectionSummary.DatabaseName)) if (!ObjectExplorerUtils.IsSystemDatabaseConnection(response.ConnectionSummary.DatabaseName))
{ {
// Assuming the databases are in a folder under server node // Assuming the databases are in a folder under server node
@@ -358,11 +368,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
var databaseNode = databases.FirstOrDefault(d => d.Label == response.ConnectionSummary.DatabaseName); var databaseNode = databases.FirstOrDefault(d => d.Label == response.ConnectionSummary.DatabaseName);
databaseNode.Label = rootNode.Label; databaseNode.Label = rootNode.Label;
session.Root = databaseNode; session.Root = databaseNode;
} }
return session; return session;
} }
} }
} }
} }

View File

@@ -23,7 +23,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
public override IEnumerable<TreeNode> Expand(TreeNode parent) public override IEnumerable<TreeNode> Expand(TreeNode parent)
{ {
//parent.BeginChildrenInit();
try try
{ {
List<TreeNode> allChildren = new List<TreeNode>(); List<TreeNode> allChildren = new List<TreeNode>();
@@ -35,7 +34,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
} }
finally finally
{ {
//parent.EndChildrenInit();
} }
} }
@@ -62,10 +60,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
} }
SmoQueryContext context = parent.GetContextAs<SmoQueryContext>(); SmoQueryContext context = parent.GetContextAs<SmoQueryContext>();
Validate.IsNotNull(nameof(context), context); Validate.IsNotNull(nameof(context), context);
var validForFlag = ServerVersionHelper.GetValidForFlag(context.SqlServerType);
if (ShouldFilterNode(parent, validForFlag))
{
return;
}
IEnumerable<SmoQuerier> queriers = context.ServiceProvider.GetServices<SmoQuerier>(q => IsCompatibleQuerier(q)); IEnumerable<SmoQuerier> queriers = context.ServiceProvider.GetServices<SmoQuerier>(q => IsCompatibleQuerier(q));
var filters = this.Filters; var filters = this.Filters;
var validForFlag = ServerVersionHelper.GetValidForFlag(context.SqlServerType);
foreach (var querier in queriers) foreach (var querier in queriers)
{ {
string propertyFilter = GetProperyFilter(filters, querier.GetType(), validForFlag); string propertyFilter = GetProperyFilter(filters, querier.GetType(), validForFlag);
@@ -77,7 +80,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
Console.WriteLine("smoObject should not be null"); Console.WriteLine("smoObject should not be null");
} }
TreeNode childNode = CreateChild(parent, smoObject); TreeNode childNode = CreateChild(parent, smoObject);
if (childNode != null && !ShouldFilterNode(childNode, validForFlag)) if (childNode != null && PassesFinalFilters(childNode, smoObject) && !ShouldFilterNode(childNode, validForFlag))
{ {
allChildren.Add(childNode); allChildren.Add(childNode);
} }
@@ -102,15 +105,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
private string GetProperyFilter(IEnumerable<NodeFilter> filters, Type querierType, ValidForFlag validForFlag) private string GetProperyFilter(IEnumerable<NodeFilter> filters, Type querierType, ValidForFlag validForFlag)
{ {
string filter = ""; string filter = string.Empty;
if (filters != null) if (filters != null)
{ {
var filterToApply = filters.FirstOrDefault(f => f.CanApplyFilter(querierType, validForFlag)); var filtersToApply = filters.Where(f => f.CanApplyFilter(querierType, validForFlag)).ToList();
filter = ""; filter = string.Empty;
if (filtersToApply.Any())
if (filterToApply != null)
{ {
filter = filterToApply.ToPropertyFilterString(); filter = NodeFilter.ConcatProperties(filtersToApply);
} }
} }

View File

@@ -923,7 +923,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
[Export(typeof(SmoQuerier))] [Export(typeof(SmoQuerier))]
internal partial class SqlDatabaseDdlTriggerQuerier: SmoQuerier internal partial class SqlDatabaseDdlTriggerQuerier: SmoQuerier
{ {
Type[] supportedTypes = new Type[] { typeof(Trigger) }; Type[] supportedTypes = new Type[] { typeof(DatabaseDdlTrigger) };
public override Type[] SupportedObjectTypes { get { return supportedTypes; } } public override Type[] SupportedObjectTypes { get { return supportedTypes; } }
@@ -937,7 +937,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
HashSet<string> urns = null; HashSet<string> urns = null;
if (hasFilter) if (hasFilter)
{ {
string urn = $"{parentDatabase.Urn.ToString()}/Trigger" + filter; string urn = $"{parentDatabase.Urn.ToString()}/DatabaseDdlTrigger" + filter;
Enumerator en = new Enumerator(); Enumerator en = new Enumerator();
Request request = new Request(new Urn(urn)); Request request = new Request(new Urn(urn));
ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject); ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject);
@@ -948,11 +948,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
{ {
if (hasFilter && urns != null) if (hasFilter && urns != null)
{ {
return new SmoCollectionWrapper<Trigger>(retValue).Where(c => urns.Contains(c.Urn)); return new SmoCollectionWrapper<DatabaseDdlTrigger>(retValue).Where(c => urns.Contains(c.Urn));
} }
else else
{ {
return new SmoCollectionWrapper<Trigger>(retValue); return new SmoCollectionWrapper<DatabaseDdlTrigger>(retValue);
} }
} }
} }
@@ -1120,46 +1120,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
} }
} }
[Export(typeof(SmoQuerier))]
internal partial class SqlSystemDataTypeQuerier: SmoQuerier
{
Type[] supportedTypes = new Type[] { typeof(SystemDataType) };
public override Type[] SupportedObjectTypes { get { return supportedTypes; } }
public override IEnumerable<SqlSmoObject> Query(SmoQueryContext context, string filter)
{
Server parentServer = context.Parent as Server;
if (parentServer != null)
{
var retValue = parentServer.SystemDataTypes;
bool hasFilter = !string.IsNullOrEmpty(filter);
HashSet<string> urns = null;
if (hasFilter)
{
string urn = $"{parentServer.Urn.ToString()}/SystemDataType" + filter;
Enumerator en = new Enumerator();
Request request = new Request(new Urn(urn));
ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject);
EnumResult result = en.Process(serverConnection, request);
urns = GetUrns(result);
}
if (retValue != null)
{
if (hasFilter && urns != null)
{
return new SmoCollectionWrapper<SystemDataType>(retValue).Where(c => urns.Contains(c.Urn));
}
else
{
return new SmoCollectionWrapper<SystemDataType>(retValue);
}
}
}
return Enumerable.Empty<SqlSmoObject>();
}
}
[Export(typeof(SmoQuerier))] [Export(typeof(SmoQuerier))]
internal partial class SqlUserDefinedDataTypeQuerier: SmoQuerier internal partial class SqlUserDefinedDataTypeQuerier: SmoQuerier
{ {
@@ -2802,15 +2762,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
public override IEnumerable<SqlSmoObject> Query(SmoQueryContext context, string filter) public override IEnumerable<SqlSmoObject> Query(SmoQueryContext context, string filter)
{ {
Server parentServer = context.Parent as Server; Database parentDatabase = context.Parent as Database;
if (parentServer != null) if (parentDatabase != null)
{ {
var retValue = parentServer.SystemDataTypes; var retValue = parentDatabase.Parent.SystemDataTypes;
bool hasFilter = !string.IsNullOrEmpty(filter); bool hasFilter = !string.IsNullOrEmpty(filter);
HashSet<string> urns = null; HashSet<string> urns = null;
if (hasFilter) if (hasFilter)
{ {
string urn = $"{parentServer.Urn.ToString()}/SystemDataType" + filter; string urn = $"{parentDatabase.Urn.ToString()}/SystemDataType" + filter;
Enumerator en = new Enumerator(); Enumerator en = new Enumerator();
Request request = new Request(new Urn(urn)); Request request = new Request(new Urn(urn));
ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject); ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject);

View File

@@ -52,7 +52,9 @@
<Node Name="SqlFullTextIndex" Parent="Table" Collection="False" /> <Node Name="SqlFullTextIndex" Parent="Table" Collection="False" />
<Node Name="SqlStatistic" Parent="TableViewBase"/> <Node Name="SqlStatistic" Parent="TableViewBase"/>
<Node Name="SqlDatabaseDdlTrigger" Type="Trigger" Parent="Database"/> <Node Name="SqlDatabaseDdlTrigger" Type="DatabaseDdlTrigger" Parent="Database">
<NavigationPath Parent="Database" Field="Triggers" />
</Node>
<Node Name="SqlAssembly" Type="SqlAssembly" Parent="Database" > <Node Name="SqlAssembly" Type="SqlAssembly" Parent="Database" >
<NavigationPath Parent="Database" Field="Assemblies" /> <NavigationPath Parent="Database" Field="Assemblies" />
</Node> </Node>
@@ -61,7 +63,6 @@
<Node Name="SqlDefault" Parent="Database" /> <Node Name="SqlDefault" Parent="Database" />
<Node Name="SqlSequence" Parent="Database" /> <Node Name="SqlSequence" Parent="Database" />
<Node Name="SqlSystemDataType" Parent="Server" />
<Node Name="SqlUserDefinedDataType" Parent="Database" /> <Node Name="SqlUserDefinedDataType" Parent="Database" />
<Node Name="SqlUserDefinedTableType" Parent="Database" /> <Node Name="SqlUserDefinedTableType" Parent="Database" />
@@ -129,7 +130,9 @@
<Node Name="SqlPartitionFunctionParameter" Parent="PartitionFunction" /> <Node Name="SqlPartitionFunctionParameter" Parent="PartitionFunction" />
<Node Name="SqlBuiltInType" Type="SystemDataType" Parent="Server"/> <Node Name="SqlBuiltInType" Type="SystemDataType" Parent="Database">
<NavigationPath Parent="Database" Field="Parent.SystemDataTypes" />
</Node>
<!-- TODO Enable all types <!-- TODO Enable all types
<Node Name="SqlRoute"/> <Node Name="SqlRoute"/>
--> -->

View File

@@ -216,6 +216,7 @@
<Child Name="ScalarValuedFunctions"/> <Child Name="ScalarValuedFunctions"/>
<Child Name="AggregateFunctions"/> <Child Name="AggregateFunctions"/>
</Node> </Node>
<Node Name="DatabaseTriggers" LocLabel="SR.SchemaHierarchy_DatabaseTriggers" BaseClass="ModelBased" NodeType="DatabaseTrigger" Strategy="MultipleElementsOfType" ChildQuerierTypes="SqlDatabaseDdlTrigger"/> <Node Name="DatabaseTriggers" LocLabel="SR.SchemaHierarchy_DatabaseTriggers" BaseClass="ModelBased" NodeType="DatabaseTrigger" Strategy="MultipleElementsOfType" ChildQuerierTypes="SqlDatabaseDdlTrigger"/>
<Node Name="Assemblies" LocLabel="SR.SchemaHierarchy_Assemblies" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="Assembly" ChildQuerierTypes="SqlAssembly" ValidFor="Sql2005|Sql2008|Sql2012|Sql2014|Sql2016|SqlvNext|AzureV12"/> <Node Name="Assemblies" LocLabel="SR.SchemaHierarchy_Assemblies" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="Assembly" ChildQuerierTypes="SqlAssembly" ValidFor="Sql2005|Sql2008|Sql2012|Sql2014|Sql2016|SqlvNext|AzureV12"/>
<Node Name="Types" LocLabel="SR.SchemaHierarchy_Types" BaseClass="ModelBased" > <Node Name="Types" LocLabel="SR.SchemaHierarchy_Types" BaseClass="ModelBased" >
@@ -268,8 +269,8 @@
<Node Name="SystemUnicodeCharacterStrings" LocLabel="SR.SchemaHierarchy_SystemUnicodeCharacterStrings" BaseClass="ModelBased" NodeType="SystemUnicodeCharacterString" Strategy="MultipleElementsOfType" ChildQuerierTypes="SqlBuiltInType"/> <Node Name="SystemUnicodeCharacterStrings" LocLabel="SR.SchemaHierarchy_SystemUnicodeCharacterStrings" BaseClass="ModelBased" NodeType="SystemUnicodeCharacterString" Strategy="MultipleElementsOfType" ChildQuerierTypes="SqlBuiltInType"/>
<Node Name="SystemBinaryStrings" LocLabel="SR.SchemaHierarchy_SystemBinaryStrings" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemBinaryString" ChildQuerierTypes="SqlBuiltInType"/> <Node Name="SystemBinaryStrings" LocLabel="SR.SchemaHierarchy_SystemBinaryStrings" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemBinaryString" ChildQuerierTypes="SqlBuiltInType"/>
<Node Name="SystemOtherDataTypes" LocLabel="SR.SchemaHierarchy_SystemOtherDataTypes" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemOtherDataType" ChildQuerierTypes="SqlBuiltInType"/> <Node Name="SystemOtherDataTypes" LocLabel="SR.SchemaHierarchy_SystemOtherDataTypes" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemOtherDataType" ChildQuerierTypes="SqlBuiltInType"/>
<Node Name="SystemClrDataTypes" LocLabel="SR.SchemaHierarchy_SystemCLRDataTypes" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemClrDataType" ChildQuerierTypes="SqlUserDefinedType" ValidFor="Sql2005|Sql2008|Sql2012|Sql2014|Sql2016|SqlvNext|AzureV11|AzureV12"/> <Node Name="SystemClrDataTypes" LocLabel="SR.SchemaHierarchy_SystemCLRDataTypes" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemClrDataType" ChildQuerierTypes="SqlBuiltInType" ValidFor="Sql2005|Sql2008|Sql2012|Sql2014|Sql2016|SqlvNext|AzureV11|AzureV12"/>
<Node Name="SystemSpatialDataTypes" LocLabel="SR.SchemaHierarchy_SystemSpatialDataTypes" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemSpatialDataType" ChildQuerierTypes="SqlUserDefinedType" ValidFor="Sql2008|Sql2012|Sql2014|Sql2016|SqlvNext|AzureV11|AzureV12"/> <Node Name="SystemSpatialDataTypes" LocLabel="SR.SchemaHierarchy_SystemSpatialDataTypes" BaseClass="ModelBased" Strategy="MultipleElementsOfType" NodeType="SystemSpatialDataType" ChildQuerierTypes="SqlBuiltInType" ValidFor="Sql2008|Sql2012|Sql2014|Sql2016|SqlvNext|AzureV11|AzureV12"/>
<!-- Childs of ExternalResources --> <!-- Childs of ExternalResources -->
<Node Name="ExternalDataSources" LocLabel="SR.SchemaHierarchy_ExternalDataSources" BaseClass="ModelBased" NodeType="ExternalDataSource" Strategy="MultipleElementsOfType" ChildQuerierTypes="SqlExternalDataSource" ValidFor="Sql2016|SqlvNext|AzureV12"/> <Node Name="ExternalDataSources" LocLabel="SR.SchemaHierarchy_ExternalDataSources" BaseClass="ModelBased" NodeType="ExternalDataSource" Strategy="MultipleElementsOfType" ChildQuerierTypes="SqlExternalDataSource" ValidFor="Sql2016|SqlvNext|AzureV12"/>

View File

@@ -2393,7 +2393,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
{ {
get get
{ {
return new [] { typeof(SqlUserDefinedTypeQuerier), }; return new [] { typeof(SqlBuiltInTypeQuerier), };
} }
} }
@@ -2417,7 +2417,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
{ {
get get
{ {
return new [] { typeof(SqlUserDefinedTypeQuerier), }; return new [] { typeof(SqlBuiltInTypeQuerier), };
} }
} }

View File

@@ -4,9 +4,12 @@
// //
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
@@ -14,6 +17,7 @@ using Microsoft.SqlTools.ServiceLayer.ObjectExplorer;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes;
using Microsoft.SqlTools.ServiceLayer.Test.Common; using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined;
using Xunit; using Xunit;
using static Microsoft.SqlTools.ServiceLayer.ObjectExplorer.ObjectExplorerService; using static Microsoft.SqlTools.ServiceLayer.ObjectExplorer.ObjectExplorerService;
@@ -33,8 +37,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
{ {
var session = await CreateSession(null, uri); var session = await CreateSession(null, uri);
await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session);
DisconnectConnection(uri);
} }
CancelConnection(uri);
} }
[Fact] [Fact]
@@ -47,9 +51,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
{ {
var session = await CreateSession("tempdb", uri); var session = await CreateSession("tempdb", uri);
await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session);
DisconnectConnection(uri);
} }
CancelConnection(uri);
} }
[Fact] [Fact]
@@ -62,15 +65,35 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
{ {
var session = await CreateSession(testDb.DatabaseName, uri); var session = await CreateSession(testDb.DatabaseName, uri);
ExpandAndVerifyDatabaseNode(testDb.DatabaseName, session); ExpandAndVerifyDatabaseNode(testDb.DatabaseName, session);
DisconnectConnection(uri);
} }
CancelConnection(uri); }
[Fact]
public async void VerifyAllSqlObjects()
{
var queryFileName = "AllSqlObjects.sql";
string uri = "AllSqlObjects";
string baselineFileName = "AllSqlObjects.txt";
string databaseName = null;
await TestServiceProvider.CalculateRunTime(() => VerifyObjectExplorerTest(databaseName, queryFileName, uri, baselineFileName), true);
}
//[Fact]
//This takes take long to run so not a good test for CI builds
public async void VerifySystemObjects()
{
string queryFileName = null;
string uri = "SystemObjects";
string baselineFileName = null;
string databaseName = null;
await TestServiceProvider.CalculateRunTime(() => VerifyObjectExplorerTest(databaseName, queryFileName, uri, baselineFileName, true), true);
} }
private async Task<ObjectExplorerSession> CreateSession(string databaseName, string uri) private async Task<ObjectExplorerSession> CreateSession(string databaseName, string uri)
{ {
ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName); ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName);
connectParams.Connection.Pooling = false; //connectParams.Connection.Pooling = false;
ConnectionDetails details = connectParams.Connection; ConnectionDetails details = connectParams.Connection;
var session = await _service.DoCreateSession(details, uri); var session = await _service.DoCreateSession(details, uri);
@@ -136,23 +159,37 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
Assert.NotNull(tablesRoot); Assert.NotNull(tablesRoot);
} }
private void CancelConnection(string uri) private void DisconnectConnection(string uri)
{ {
ConnectionService.Instance.CancelConnect(new CancelConnectParams ConnectionService.Instance.Disconnect(new DisconnectParams
{ {
OwnerUri = uri, OwnerUri = uri,
Type = ConnectionType.Default Type = ConnectionType.Default
}); });
} }
private async Task ExpandTree(NodeInfo node, ObjectExplorerSession session) private async Task ExpandTree(NodeInfo node, ObjectExplorerSession session, StringBuilder stringBuilder = null, bool verifySystemObjects = false)
{ {
if (node != null && !node.IsLeaf) if (node != null && !node.IsLeaf)
{ {
var children = await _service.ExpandNode(session, node.NodePath); var children = await _service.ExpandNode(session, node.NodePath);
foreach (var child in children) foreach (var child in children)
{ {
await _service.ExpandNode(session, child.NodePath); if (stringBuilder != null && child.NodeType != "Folder" && child.NodeType != "FileGroupFile")
{
stringBuilder.AppendLine($"NodeType: {child.NodeType} Label: {child.Label}");
}
if (!verifySystemObjects && (child.Label == SR.SchemaHierarchy_SystemStoredProcedures ||
child.Label == SR.SchemaHierarchy_SystemViews ||
child.Label == SR.SchemaHierarchy_SystemFunctions ||
child.Label == SR.SchemaHierarchy_SystemDataTypes))
{
// don't expand the system folders because then the test will take for ever
}
else
{
await ExpandTree(child, session, stringBuilder, verifySystemObjects);
}
} }
} }
} }
@@ -160,7 +197,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
/// <summary> /// <summary>
/// Returns the children of a node with the given label /// Returns the children of a node with the given label
/// </summary> /// </summary>
private async Task<NodeInfo[]> FindNodeByLabel(NodeInfo node, ObjectExplorerSession session, string nodeType, bool nodeFound = false) private async Task<IList<NodeInfo>> FindNodeByLabel(NodeInfo node, ObjectExplorerSession session, string nodeType, bool nodeFound = false)
{ {
if (node != null && !node.IsLeaf) if (node != null && !node.IsLeaf)
{ {
@@ -211,117 +248,25 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
} }
} }
private async Task<bool> VerifyObjectExplorerTest(string databaseName, string queryFileName, string uri, string baselineFileName, bool verifySystemObjects = false)
[Fact]
public async void VerifyAdventureWorksDatabaseObjects()
{ {
var query = Scripts.AdventureWorksScript; var query = string.IsNullOrEmpty(queryFileName) ? string.Empty : LoadScript(queryFileName);
string uri = "VerifyAdventureWorksDatabaseObjects"; StringBuilder stringBuilder = new StringBuilder();
string databaseName = null; SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri);
var session = await CreateSession(testDb.DatabaseName, uri);
await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session, false);
await ExpandTree(session.Root.ToNodeInfo(), session, stringBuilder, verifySystemObjects);
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) string baseline = string.IsNullOrEmpty(baselineFileName) ? string.Empty : LoadBaseLine(baselineFileName);
using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, true, databaseName, query, uri)) if (!string.IsNullOrEmpty(baseline))
{ {
var session = await CreateSession(testDb.DatabaseName, queryTempFile.FilePath); string actual = stringBuilder.ToString();
var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session, false); BaselinedTest.CompareActualWithBaseline(actual, baseline);
await ExpandTree(session.Root.ToNodeInfo(), session);
var tablesChildren = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables);
var systemTables = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_SystemTables);
Assert.True(!systemTables.Any());
var externalTables = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_ExternalTables);
Assert.True(!externalTables.Any());
var fileTables = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_FileTables);
Assert.True(!fileTables.Any());
var allTables = tablesChildren.Where(x => x.NodeType != NodeTypes.Folder.ToString());
Assert.True(allTables.Any());
var storedProcedures = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_StoredProcedures);
Assert.True(storedProcedures.Any());
var views = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Views);
Assert.True(views.Any());
var userDefinedDataTypes = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_UserDefinedDataTypes);
Assert.True(userDefinedDataTypes.Any());
var scalarValuedFunctions = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_ScalarValuedFunctions);
Assert.True(scalarValuedFunctions.Any());
} }
CancelConnection(uri); _service.CloseSession(session.Uri);
Thread.Sleep(3000);
} testDb.Cleanup();
return true;
// [Fact]
public async void VerifySql2016Objects()
{
var query = LoadScript("Sql_2016_Additions.sql");
string uri = "VerifySql2016Objects";
string databaseName = null;
using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri))
{
var session = await CreateSession(testDb.DatabaseName, uri);
var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session);
await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables);
}
CancelConnection(uri);
}
// [Fact]
public async void VerifySqlObjects()
{
var query = LoadScript("Sql_Additions.sql");
string uri = "VerifySqlObjects";
string databaseName = null;
using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri))
{
var session = await CreateSession(testDb.DatabaseName, uri);
var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session);
await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables);
}
CancelConnection(uri);
}
// [Fact]
public async void VerifyFileTableTest()
{
var query = LoadScript("FileTableTest.sql");
string uri = "VerifyFileTableTest";
string databaseName = null;
using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri))
{
var session = await CreateSession(testDb.DatabaseName, uri);
var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session);
await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables);
}
CancelConnection(uri);
}
//[Fact]
public async void VerifyColumnstoreindexSql16()
{
var query = LoadScript("ColumnstoreindexSql16.sql");
string uri = "VerifyColumnstoreindexSql16";
string databaseName = null;
using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri))
{
var session = await CreateSession(testDb.DatabaseName, uri);
var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session);
await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables);
}
CancelConnection(uri);
} }
private static string TestLocationDirectory private static string TestLocationDirectory
@@ -341,15 +286,36 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer
} }
} }
public DirectoryInfo BaselineFileDirectory
{
get
{
string d = Path.Combine(TestLocationDirectory, "Baselines");
return new DirectoryInfo(d);
}
}
public FileInfo GetInputFile(string fileName) public FileInfo GetInputFile(string fileName)
{ {
return new FileInfo(Path.Combine(InputFileDirectory.FullName, fileName)); return new FileInfo(Path.Combine(InputFileDirectory.FullName, fileName));
} }
public FileInfo GetBaseLineFile(string fileName)
{
return new FileInfo(Path.Combine(BaselineFileDirectory.FullName, fileName));
}
private string LoadScript(string fileName) private string LoadScript(string fileName)
{ {
FileInfo inputFile = GetInputFile(fileName); FileInfo inputFile = GetInputFile(fileName);
return TestUtilities.ReadTextAndNormalizeLineEndings(inputFile.FullName); return TestUtilities.ReadTextAndNormalizeLineEndings(inputFile.FullName);
} }
private string LoadBaseLine(string fileName)
{
FileInfo inputFile = GetBaseLineFile(fileName);
return TestUtilities.ReadTextAndNormalizeLineEndings(inputFile.FullName);
}
} }
} }

View File

@@ -272,7 +272,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined
/// <param name="actualContent">Actual string</param> /// <param name="actualContent">Actual string</param>
/// <param name="baselineContent">Expected string</param> /// <param name="baselineContent">Expected string</param>
/// <remarks>Fails test if strings do not match; comparison is done using an InvariantCulture StringComparer</remarks> /// <remarks>Fails test if strings do not match; comparison is done using an InvariantCulture StringComparer</remarks>
public void CompareActualWithBaseline(string actualContent, string baselineContent) public static void CompareActualWithBaseline(string actualContent, string baselineContent)
{ {
int _compareResult = string.Compare(actualContent, baselineContent, StringComparison.OrdinalIgnoreCase); int _compareResult = string.Compare(actualContent, baselineContent, StringComparison.OrdinalIgnoreCase);

View File

@@ -54,29 +54,26 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
/// Create the test db if not already exists /// Create the test db if not already exists
/// </summary> /// </summary>
public static SqlTestDb CreateNew( public static SqlTestDb CreateNew(
TestServerType serverType, TestServerType serverType,
bool doNotCleanupDb = false, bool doNotCleanupDb = false,
string databaseName = null, string databaseName = null,
string query = null, string query = null,
string dbNamePrefix = null) string dbNamePrefix = null)
{ {
SqlTestDb testDb = new SqlTestDb(); SqlTestDb testDb = new SqlTestDb();
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) databaseName = databaseName ?? GetUniqueDBName(dbNamePrefix);
string createDatabaseQuery = Scripts.CreateDatabaseQuery.Replace("#DatabaseName#", databaseName);
TestServiceProvider.Instance.RunQuery(serverType, MasterDatabaseName, createDatabaseQuery);
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' is created", databaseName));
if (!string.IsNullOrEmpty(query))
{ {
databaseName = databaseName ?? GetUniqueDBName(dbNamePrefix); TestServiceProvider.Instance.RunQuery(serverType, databaseName, query);
string createDatabaseQuery = Scripts.CreateDatabaseQuery.Replace("#DatabaseName#", databaseName); Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' SQL types are created", databaseName));
TestServiceProvider.Instance.RunQuery(serverType, MasterDatabaseName, createDatabaseQuery);
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' is created", databaseName));
if (!string.IsNullOrEmpty(query))
{
TestServiceProvider.Instance.RunQuery(serverType, databaseName, query);
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' SQL types are created", databaseName));
}
testDb.DatabaseName = databaseName;
testDb.ServerType = serverType;
testDb.DoNotCleanupDb = doNotCleanupDb;
} }
testDb.DatabaseName = databaseName;
testDb.ServerType = serverType;
testDb.DoNotCleanupDb = doNotCleanupDb;
return testDb; return testDb;
} }
@@ -111,13 +108,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
public void Cleanup() public void Cleanup()
{ {
if (!DoNotCleanupDb) try
{ {
string dropDatabaseQuery = string.Format(CultureInfo.InvariantCulture, if (!DoNotCleanupDb)
(ServerType == TestServerType.Azure ? Scripts.DropDatabaseIfExistAzure : Scripts.DropDatabaseIfExist), DatabaseName); {
string dropDatabaseQuery = string.Format(CultureInfo.InvariantCulture,
(ServerType == TestServerType.Azure ? Scripts.DropDatabaseIfExistAzure : Scripts.DropDatabaseIfExist), DatabaseName);
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Cleaning up database {0}", DatabaseName)); Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Cleaning up database {0}", DatabaseName));
TestServiceProvider.Instance.RunQuery(ServerType, MasterDatabaseName, dropDatabaseQuery); TestServiceProvider.Instance.RunQuery(ServerType, MasterDatabaseName, dropDatabaseQuery);
}
}
catch (Exception ex)
{
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Failed to cleanup database: {0} error:{1}", DatabaseName, ex.Message));
} }
} }

View File

@@ -0,0 +1,136 @@
NodeType: Table Label: HumanResources.Employee
NodeType: Column Label: BusinessEntityID
NodeType: Column Label: NationalIDNumber
NodeType: Column Label: LoginID
NodeType: Column Label: OrganizationNode
NodeType: Column Label: OrganizationLevel
NodeType: Column Label: JobTitle
NodeType: Column Label: BirthDate
NodeType: Column Label: MaritalStatus
NodeType: Column Label: Gender
NodeType: Column Label: HireDate
NodeType: Column Label: SalariedFlag
NodeType: Column Label: VacationHours
NodeType: Column Label: SickLeaveHours
NodeType: Column Label: CurrentFlag
NodeType: Column Label: rowguid
NodeType: Column Label: ModifiedDate
NodeType: Key Label: FK_Employee_Person_BusinessEntityID
NodeType: Key Label: PK_Employee_BusinessEntityID
NodeType: Constraint Label: CK_Employee_BirthDate
NodeType: Constraint Label: CK_Employee_Gender
NodeType: Constraint Label: CK_Employee_HireDate
NodeType: Constraint Label: CK_Employee_MaritalStatus
NodeType: Constraint Label: CK_Employee_SickLeaveHours
NodeType: Constraint Label: CK_Employee_VacationHours
NodeType: Index Label: NonClusteredIndex-Login
NodeType: Statistic Label: NonClusteredIndex-Login
NodeType: Statistic Label: PK_Employee_BusinessEntityID
NodeType: Table Label: HumanResources.Employee_Temporal
NodeType: Column Label: BusinessEntityID
NodeType: Column Label: NationalIDNumber
NodeType: Column Label: LoginID
NodeType: Column Label: OrganizationNode
NodeType: Column Label: OrganizationLevel
NodeType: Column Label: JobTitle
NodeType: Column Label: BirthDate
NodeType: Column Label: MaritalStatus
NodeType: Column Label: Gender
NodeType: Column Label: HireDate
NodeType: Column Label: VacationHours
NodeType: Column Label: SickLeaveHours
NodeType: Column Label: ValidFrom
NodeType: Column Label: ValidTo
NodeType: Key Label: PK_Employee_History_BusinessEntityID
NodeType: Statistic Label: PK_Employee_History_BusinessEntityID
NodeType: Table Label: Person.Person
NodeType: Column Label: BusinessEntityID
NodeType: Column Label: PersonType
NodeType: Column Label: NameStyle
NodeType: Column Label: Title
NodeType: Column Label: FirstName
NodeType: Column Label: MiddleName
NodeType: Column Label: LastName
NodeType: Column Label: Suffix
NodeType: Column Label: EmailPromotion
NodeType: Column Label: AdditionalContactInfo
NodeType: Column Label: rowguid
NodeType: Column Label: ModifiedDate
NodeType: Key Label: PK_Person_BusinessEntityID
NodeType: Constraint Label: CK_Person_EmailPromotion
NodeType: Constraint Label: CK_Person_PersonType
NodeType: Trigger Label: TableTrigger
NodeType: Statistic Label: PK_Person_BusinessEntityID
NodeType: View Label: HumanResources.vEmployee
NodeType: Column Label: BusinessEntityID
NodeType: Column Label: Title
NodeType: Column Label: FirstName
NodeType: Column Label: MiddleName
NodeType: Column Label: LastName
NodeType: Column Label: Suffix
NodeType: Column Label: JobTitle
NodeType: Column Label: AdditionalContactInfo
NodeType: Synonym Label: dbo.MyProduct
NodeType: StoredProcedure Label: HumanResources.sp_GetEmployee_Person_Info_AsOf
NodeType: StoredProcedureParameter Label: @asOf
NodeType: TableValuedFunction Label: dbo.ufnGetContactInformation
NodeType: TableValuedFunctionParameter Label: @PersonID
NodeType: ScalarValuedFunction Label: dbo.fun1
NodeType: ScalarValuedFunction Label: dbo.ufnGetInventoryStock
NodeType: ScalarValuedFunctionParameter Label: @ProductID
NodeType: DatabaseTrigger Label: Trigger_2
NodeType: Assembly Label: Microsoft.SqlServer.Types
NodeType: UserDefinedDataType Label: dbo.AccountNumber
NodeType: UserDefinedDataType Label: dbo.Flag
NodeType: UserDefinedDataType Label: dbo.Name
NodeType: UserDefinedDataType Label: dbo.NameStyle
NodeType: UserDefinedDataType Label: dbo.OrderNumber
NodeType: UserDefinedDataType Label: dbo.Phone
NodeType: UserDefinedTableType Label: Demo.SalesOrderDetailType_inmem
NodeType: UserDefinedTableTypeColumn Label: OrderQty
NodeType: UserDefinedTableTypeColumn Label: ProductID
NodeType: UserDefinedTableTypeColumn Label: SpecialOfferID
NodeType: UserDefinedTableType Label: Demo.SalesOrderDetailType_ondisk
NodeType: UserDefinedTableTypeColumn Label: OrderQty
NodeType: UserDefinedTableTypeColumn Label: ProductID
NodeType: UserDefinedTableTypeColumn Label: SpecialOfferID
NodeType: XmlSchemaCollection Label: Person.AdditionalContactInfoSchemaCollection
NodeType: Rule Label: dbo.list_rule
NodeType: Default Label: dbo.phonedflt
NodeType: Sequence Label: Demo.ID_Seq
NodeType: FileGroup Label: PRIMARY
NodeType: FullTextCatalog Label: AW2014FullTextCatalog
NodeType: User Label: amy0
NodeType: User Label: dbo
NodeType: User Label: guest
NodeType: User Label: INFORMATION_SCHEMA
NodeType: User Label: sys
NodeType: DatabaseRole Label: db_accessadmin
NodeType: DatabaseRole Label: db_backupoperator
NodeType: DatabaseRole Label: db_datareader
NodeType: DatabaseRole Label: db_datawriter
NodeType: DatabaseRole Label: db_ddladmin
NodeType: DatabaseRole Label: db_denydatareader
NodeType: DatabaseRole Label: db_denydatawriter
NodeType: DatabaseRole Label: db_owner
NodeType: DatabaseRole Label: db_securityadmin
NodeType: DatabaseRole Label: public
NodeType: DatabaseRole Label: SalesManagers
NodeType: DatabaseRole Label: SalesPersons
NodeType: Schema Label: dbo
NodeType: Schema Label: db_accessadmin
NodeType: Schema Label: db_backupoperator
NodeType: Schema Label: db_datareader
NodeType: Schema Label: db_datawriter
NodeType: Schema Label: db_ddladmin
NodeType: Schema Label: db_denydatareader
NodeType: Schema Label: db_denydatawriter
NodeType: Schema Label: db_owner
NodeType: Schema Label: db_securityadmin
NodeType: Schema Label: Demo
NodeType: Schema Label: guest
NodeType: Schema Label: HumanResources
NodeType: Schema Label: INFORMATION_SCHEMA
NodeType: Schema Label: Person
NodeType: Schema Label: sys
NodeType: DatabaseEncryptionKey Label:

File diff suppressed because one or more lines are too long

View File

@@ -6,6 +6,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.SqlTools.Credentials; using Microsoft.SqlTools.Credentials;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
@@ -78,9 +80,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
/// </summary> /// </summary>
public void RunQuery(TestServerType serverType, string databaseName, string queryText, bool throwOnError = false) public void RunQuery(TestServerType serverType, string databaseName, string queryText, bool throwOnError = false)
{ {
string uri = "";
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{ {
ConnectionInfo connInfo = InitLiveConnectionInfo(serverType, databaseName, queryTempFile.FilePath); uri = queryTempFile.FilePath;
ConnectionInfo connInfo = InitLiveConnectionInfo(serverType, databaseName, uri);
Query query = new Query(queryText, connInfo, new QueryExecutionSettings(), MemoryFileSystem.GetFileStreamFactory()); Query query = new Query(queryText, connInfo, new QueryExecutionSettings(), MemoryFileSystem.GetFileStreamFactory());
query.Execute(); query.Execute();
query.ExecutionTask.Wait(); query.ExecutionTask.Wait();
@@ -96,7 +101,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
string.Join(Environment.NewLine, errorBatches.Select(b => b.BatchText)))); string.Join(Environment.NewLine, errorBatches.Select(b => b.BatchText))));
} }
} }
DisconnectConnection(uri);
} }
}
public static async Task<T> CalculateRunTime<T>(Func<Task<T>> testToRun, bool printResult, [CallerMemberName] string testName = "")
{
TestTimer timer = new TestTimer() { PrintResult = printResult };
T result = await testToRun();
timer.EndAndPrint(testName);
return result;
} }
private ConnectionInfo InitLiveConnectionInfo(TestServerType serverType, string databaseName, string scriptFilePath) private ConnectionInfo InitLiveConnectionInfo(TestServerType serverType, string databaseName, string scriptFilePath)
@@ -119,6 +135,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
return connInfo; return connInfo;
} }
private void DisconnectConnection(string uri)
{
ConnectionService.Instance.Disconnect(new DisconnectParams
{
OwnerUri = uri,
Type = ConnectionType.Default
});
}
private static bool hasInitServices = false; private static bool hasInitServices = false;
private static void InitializeTestServices() private static void InitializeTestServices()