mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-03-24 13:50:29 -04: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.TableDesigner;
|
||||||
using Microsoft.SqlTools.ServiceLayer.AzureBlob;
|
using Microsoft.SqlTools.ServiceLayer.AzureBlob;
|
||||||
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer
|
namespace Microsoft.SqlTools.ServiceLayer
|
||||||
@@ -179,6 +180,9 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
ExecutionPlanService.Instance.InitializeService(serviceHost);
|
ExecutionPlanService.Instance.InitializeService(serviceHost);
|
||||||
serviceProvider.RegisterSingleService(ExecutionPlanService.Instance);
|
serviceProvider.RegisterSingleService(ExecutionPlanService.Instance);
|
||||||
|
|
||||||
|
ObjectManagementService.Instance.InitializeService(serviceHost);
|
||||||
|
serviceProvider.RegisterSingleService(ObjectManagementService.Instance);
|
||||||
|
|
||||||
serviceHost.InitializeRequestHandlers();
|
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)
|
public static string ConnectionServiceListDbErrorNotConnected(string uri)
|
||||||
{
|
{
|
||||||
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
|
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
|
||||||
@@ -13878,6 +13886,8 @@ namespace Microsoft.SqlTools.ServiceLayer
|
|||||||
|
|
||||||
public const string GetUserDefinedObjectsFromModelFailed = "GetUserDefinedObjectsFromModelFailed";
|
public const string GetUserDefinedObjectsFromModelFailed = "GetUserDefinedObjectsFromModelFailed";
|
||||||
|
|
||||||
|
public const string ErrorConnectionNotFound = "ErrorConnectionNotFound";
|
||||||
|
|
||||||
|
|
||||||
private Keys()
|
private Keys()
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
@@ -2416,3 +2416,6 @@ TableDesignerConfirmationText = I have read the summary and understand the poten
|
|||||||
SqlProjectModelNotFound(string projectUri) = Could not find SQL model from project: {0}.
|
SqlProjectModelNotFound(string projectUri) = Could not find SQL model from project: {0}.
|
||||||
UnsupportedModelType(string type) = Unsupported model type: {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