mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-14 01:25:40 -05:00
Feature rename sql objects (#1686)
This commit is contained in:
@@ -40,6 +40,7 @@ using Microsoft.SqlTools.ServiceLayer.ModelManagement;
|
||||
using Microsoft.SqlTools.ServiceLayer.TableDesigner;
|
||||
using Microsoft.SqlTools.ServiceLayer.AzureBlob;
|
||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer
|
||||
@@ -179,6 +180,9 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
ExecutionPlanService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(ExecutionPlanService.Instance);
|
||||
|
||||
ObjectManagementService.Instance.InitializeService(serviceHost);
|
||||
serviceProvider.RegisterSingleService(ObjectManagementService.Instance);
|
||||
|
||||
serviceHost.InitializeRequestHandlers();
|
||||
}
|
||||
|
||||
|
||||
@@ -9613,6 +9613,14 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string ErrorConnectionNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.ErrorConnectionNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ConnectionServiceListDbErrorNotConnected(string uri)
|
||||
{
|
||||
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
|
||||
@@ -13878,6 +13886,8 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
|
||||
public const string GetUserDefinedObjectsFromModelFailed = "GetUserDefinedObjectsFromModelFailed";
|
||||
|
||||
public const string ErrorConnectionNotFound = "ErrorConnectionNotFound";
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
@@ -2415,4 +2415,7 @@ TableDesignerConfirmationText = I have read the summary and understand the poten
|
||||
|
||||
SqlProjectModelNotFound(string projectUri) = Could not find SQL model from project: {0}.
|
||||
UnsupportedModelType(string type) = Unsupported model type: {0}.
|
||||
GetUserDefinedObjectsFromModelFailed = Failed to get user defined objects from model.
|
||||
GetUserDefinedObjectsFromModelFailed = Failed to get user defined objects from model.
|
||||
|
||||
#ObjectManagement Service
|
||||
ErrorConnectionNotFound = The connection could not be found
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts
|
||||
{
|
||||
public class RenameRequestParams : GeneralRequestDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// SFC (SMO) URN identifying the object
|
||||
/// </summary>
|
||||
public string ObjectUrn { get; set; }
|
||||
/// <summary>
|
||||
/// the new name of the object
|
||||
/// </summary>
|
||||
public string NewName { get; set; }
|
||||
/// <summary>
|
||||
/// Connection uri
|
||||
/// </summary>
|
||||
public string ConnectionUri { get; set; }
|
||||
}
|
||||
public class RenameRequest
|
||||
{
|
||||
public static readonly RequestType<RenameRequestParams, bool> Type = RequestType<RenameRequestParams, bool>.Create("objectmanagement/rename");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// 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.Threading.Tasks;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// Main class for ObjectManagement Service functionality
|
||||
/// </summary>
|
||||
public class ObjectManagementService
|
||||
{
|
||||
private const string ObjectManagementServiceApplicationName = "azdata-object-management";
|
||||
private static Lazy<ObjectManagementService> objectManagementServiceInstance = new Lazy<ObjectManagementService>(() => new ObjectManagementService());
|
||||
public static ObjectManagementService Instance => objectManagementServiceInstance.Value;
|
||||
public static ConnectionService connectionService;
|
||||
private IProtocolEndpoint serviceHost;
|
||||
public ObjectManagementService() { }
|
||||
|
||||
/// <summary>
|
||||
/// Internal for testing purposes only
|
||||
/// </summary>
|
||||
internal static ConnectionService ConnectionServiceInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
connectionService ??= ConnectionService.Instance;
|
||||
return connectionService;
|
||||
}
|
||||
set
|
||||
{
|
||||
connectionService = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeService(IProtocolEndpoint serviceHost)
|
||||
{
|
||||
this.serviceHost = serviceHost;
|
||||
this.serviceHost.SetRequestHandler(RenameRequest.Type, HandleRenameRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to handle the renaming operation
|
||||
/// </summary>
|
||||
/// <param name="requestParams">parameters which are needed to execute renaming operation</param>
|
||||
/// <param name="requestContext">Request Context</param>
|
||||
/// <returns></returns>
|
||||
internal async Task HandleRenameRequest(RenameRequestParams requestParams, RequestContext<bool> requestContext)
|
||||
{
|
||||
Logger.Verbose("Handle Request in HandleProcessRenameEditRequest()");
|
||||
ConnectionInfo connInfo;
|
||||
|
||||
if (connectionService.TryFindConnection(
|
||||
requestParams.ConnectionUri,
|
||||
out connInfo))
|
||||
{
|
||||
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, ObjectManagementServiceApplicationName))
|
||||
{
|
||||
|
||||
IRenamable renameObject = this.GetRenamable(requestParams, sqlConn);
|
||||
|
||||
renameObject.Rename(requestParams.NewName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"The connection {requestParams.ConnectionUri} could not be found.");
|
||||
throw new Exception(SR.ErrorConnectionNotFound);
|
||||
}
|
||||
|
||||
await requestContext.SendResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to get the sql object, which should be renamed
|
||||
/// </summary>
|
||||
/// <param name="requestParams">parameters which are required for the rename operation</param>
|
||||
/// <param name="connection">the sqlconnection on the server to search for the sqlobject</param>
|
||||
/// <returns>the sql object if implements the interface IRenamable, so they can be renamed</returns>
|
||||
private IRenamable GetRenamable(RenameRequestParams requestParams, SqlConnection connection)
|
||||
{
|
||||
ServerConnection serverConnection = new ServerConnection(connection);
|
||||
Server server = new Server(serverConnection);
|
||||
SqlSmoObject dbObject = server.GetSmoObject(new Urn(requestParams.ObjectUrn));
|
||||
return (IRenamable)dbObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// 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.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
|
||||
{
|
||||
public class ObjectManagementServiceTests
|
||||
{
|
||||
private const string TableQuery = @"CREATE TABLE testTable1_RenamingTable (c1 int)";
|
||||
private const string OwnerUri = "testDB";
|
||||
private ObjectManagementService objectManagementService;
|
||||
private SqlTestDb testDb;
|
||||
private Mock<RequestContext<bool>> requestContextMock;
|
||||
|
||||
[SetUp]
|
||||
public async Task TestInitialize()
|
||||
{
|
||||
this.testDb = await SqlTestDb.CreateNewAsync(serverType: TestServerType.OnPrem, query: TableQuery, dbNamePrefix: "RenameTest");
|
||||
|
||||
requestContextMock = new Mock<RequestContext<bool>>();
|
||||
ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService();
|
||||
|
||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, OwnerUri, ConnectionType.Default);
|
||||
|
||||
ObjectManagementService.ConnectionServiceInstance = connectionService;
|
||||
this.objectManagementService = new ObjectManagementService();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public async Task TearDownTestDatabase()
|
||||
{
|
||||
await SqlTestDb.DropDatabase(testDb.DatabaseName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestRenameTable()
|
||||
{
|
||||
//arrange & act
|
||||
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
|
||||
|
||||
//assert
|
||||
requestContextMock.Verify(x => x.SendResult(It.Is<bool>(r => r == true)));
|
||||
|
||||
Query queryRenameObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.tables WHERE name='RenamingTable'");
|
||||
Assert.That(queryRenameObject.HasExecuted, Is.True, "The query to check for the renamed table was not executed");
|
||||
Assert.That(queryRenameObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the renamed table");
|
||||
Assert.That(queryRenameObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(1), "Did not find the table with the new name after the rename operation");
|
||||
|
||||
Query queryOldObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.tables WHERE name='testTable1_RenamingTable'");
|
||||
Assert.That(queryOldObject.HasExecuted, Is.True, "The query to check for the old table was not executed");
|
||||
Assert.That(queryOldObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the old table");
|
||||
Assert.That(queryOldObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(0), "Did find the old table which should have been renamed");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestRenameColumn()
|
||||
{
|
||||
//arrange & act
|
||||
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1']", testDb.DatabaseName)), requestContextMock.Object);
|
||||
|
||||
//assert
|
||||
requestContextMock.Verify(x => x.SendResult(It.Is<bool>(r => r == true)));
|
||||
|
||||
Query queryRenameObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.columns WHERE name='RenameColumn'");
|
||||
Assert.That(queryRenameObject.HasExecuted, Is.True, "The query to check for the renamed column was not executed");
|
||||
Assert.That(queryRenameObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the renamed column");
|
||||
Assert.That(queryRenameObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(1), "Did not find the column with the new name after the rename operation");
|
||||
|
||||
Query queryOldObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.columns WHERE name='C1'");
|
||||
Assert.That(queryOldObject.HasExecuted, Is.True, "The query to check for the old column was not executed");
|
||||
Assert.That(queryOldObject.HasErrored, Is.False, "There were errors on the execution of the query to check for the old column");
|
||||
Assert.That(queryOldObject.Batches[0].ResultSets[0].RowCount, Is.EqualTo(0), "Did find the old column which should have been renamed");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestRenameColumnNotExisting()
|
||||
{
|
||||
Assert.That(async () =>
|
||||
{
|
||||
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1_NOT']", testDb.DatabaseName)), requestContextMock.Object);
|
||||
}, Throws.Exception.TypeOf<FailedOperationException>(), "Did find the column, which should not have existed");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestRenameTableNotExisting()
|
||||
{
|
||||
Assert.That(async () =>
|
||||
{
|
||||
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_Not' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
|
||||
}, Throws.Exception.TypeOf<FailedOperationException>(), "Did find the table, which should not have existed");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestConnectionNotFound()
|
||||
{
|
||||
var testRenameRequestParams = new RenameRequestParams
|
||||
{
|
||||
NewName = "RenamingTable",
|
||||
ConnectionUri = "NOT_EXISTING",
|
||||
ObjectUrn = String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_Not' and @Schema='dbo']", testDb.DatabaseName),
|
||||
};
|
||||
Assert.That(async () =>
|
||||
{
|
||||
await objectManagementService.HandleRenameRequest(testRenameRequestParams, requestContextMock.Object);
|
||||
|
||||
}, Throws.Exception.TypeOf<Exception>(), "Did find the connection, which should not have existed");
|
||||
}
|
||||
|
||||
private RenameRequestParams InitRequestParams(string newName, string UrnOfObject)
|
||||
{
|
||||
return new RenameRequestParams
|
||||
{
|
||||
NewName = newName,
|
||||
ConnectionUri = OwnerUri,
|
||||
ObjectUrn = UrnOfObject,
|
||||
};
|
||||
}
|
||||
|
||||
private Query ExecuteQuery(string queryText)
|
||||
{
|
||||
TestConnectionResult conResult = LiveConnectionHelper.InitLiveConnectionInfo();
|
||||
ConnectionInfo connInfo = conResult.ConnectionInfo;
|
||||
IFileStreamFactory fileStreamFactory = MemoryFileSystem.GetFileStreamFactory(new ConcurrentDictionary<string, byte[]>());
|
||||
|
||||
QueryExecutionSettings settings = new QueryExecutionSettings() { IsSqlCmdMode = false };
|
||||
Query query = new Query(queryText, connInfo, settings, fileStreamFactory);
|
||||
query.Execute();
|
||||
query.ExecutionTask.Wait();
|
||||
return query;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user