//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Metadata.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.SqlCore.Metadata;
namespace Microsoft.SqlTools.ServiceLayer.Metadata
{
///
/// Main class for Metadata Service functionality
///
public sealed class MetadataService
{
private static readonly Lazy LazyInstance = new Lazy(() => new MetadataService());
public static MetadataService Instance => LazyInstance.Value;
private static ConnectionService connectionService = null;
///
/// Internal for testing purposes only
///
internal static ConnectionService ConnectionServiceInstance
{
get
{
connectionService ??= ConnectionService.Instance;
return connectionService;
}
set
{
connectionService = value;
}
}
///
/// Initializes the Metadata Service instance
///
///
///
public void InitializeService(ServiceHost serviceHost)
{
serviceHost.SetRequestHandler(MetadataListRequest.Type, HandleMetadataListRequest, true);
serviceHost.SetRequestHandler(TableMetadataRequest.Type, HandleGetTableRequest, true);
serviceHost.SetRequestHandler(ViewMetadataRequest.Type, HandleGetViewRequest, true);
}
///
/// Handle a metadata query request
///
internal async Task HandleMetadataListRequest(
MetadataQueryParams metadataParams,
RequestContext requestContext)
{
Func requestHandler = async () =>
{
ConnectionInfo connInfo;
MetadataService.ConnectionServiceInstance.TryFindConnection(
metadataParams.OwnerUri,
out connInfo);
var metadata = new List();
if (connInfo != null)
{
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Metadata"))
{
ReadMetadata(sqlConn, metadata);
}
}
await requestContext.SendResult(new MetadataQueryResult
{
Metadata = metadata.ToArray()
});
};
Task task = Task.Run(async () => await requestHandler()).ContinueWithOnFaulted(async t =>
{
await requestContext.SendError(t.Exception.ToString());
});
MetadataListTask = task;
}
internal Task MetadataListTask { get; set; }
///
/// Handle a table metadata query request
///
internal static async Task HandleGetTableRequest(
TableMetadataParams metadataParams,
RequestContext requestContext)
{
await HandleGetTableOrViewRequest(metadataParams, "table", requestContext);
}
///
/// Handle a view metadata query request
///
internal static async Task HandleGetViewRequest(
TableMetadataParams metadataParams,
RequestContext requestContext)
{
await HandleGetTableOrViewRequest(metadataParams, "view", requestContext);
}
///
/// Handle a table pr view metadata query request
///
private static async Task HandleGetTableOrViewRequest(
TableMetadataParams metadataParams,
string objectType,
RequestContext requestContext)
{
ConnectionInfo connInfo;
MetadataService.ConnectionServiceInstance.TryFindConnection(
metadataParams.OwnerUri,
out connInfo);
ColumnMetadata[] metadata = null;
if (connInfo != null)
{
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Metadata"))
{
TableMetadata table = new SmoMetadataFactory().GetObjectMetadata(
sqlConn, metadataParams.Schema,
metadataParams.ObjectName, objectType);
metadata = table.Columns;
}
}
await requestContext.SendResult(new TableMetadataResult
{
Columns = metadata
});
}
internal static bool IsSystemDatabase(string database)
{
// compare against master for now
return string.Compare("master", database, StringComparison.OrdinalIgnoreCase) == 0;
}
///
/// Read metadata for the current connection
///
internal static void ReadMetadata(SqlConnection sqlConn, List metadata)
{
string sql =
@"SELECT s.name AS schema_name, o.[name] AS object_name, o.[type] AS object_type
FROM sys.all_objects o
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE o.[type] IN ('P','V','U','AF','FN','IF','TF') ";
if (!IsSystemDatabase(sqlConn.Database))
{
sql += @"AND o.is_ms_shipped != 1 ";
}
sql += @"ORDER BY object_type, schema_name, object_name";
using (SqlCommand sqlCommand = new SqlCommand(sql, sqlConn))
{
using (var reader = sqlCommand.ExecuteReader())
{
while (reader.Read())
{
var schemaName = reader[0] as string;
var objectName = reader[1] as string;
var objectType = reader[2] as string;
MetadataType metadataType;
string metadataTypeName;
if (objectType.StartsWith("V"))
{
metadataType = MetadataType.View;
metadataTypeName = "View";
}
else if (objectType.StartsWith("P"))
{
metadataType = MetadataType.SProc;
metadataTypeName = "StoredProcedure";
}
else if (objectType == "AF" || objectType == "FN" || objectType == "IF" || objectType == "TF")
{
metadataType = MetadataType.Function;
metadataTypeName = "UserDefinedFunction";
}
else
{
metadataType = MetadataType.Table;
metadataTypeName = "Table";
}
metadata.Add(new ObjectMetadata
{
MetadataType = metadataType,
MetadataTypeName = metadataTypeName,
Schema = schemaName,
Name = objectName
});
}
}
}
}
}
}