mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -05:00
Fixing the bug with connections on database make restore fail (#473)
* closing the connections that don't need to be open and keeping track of the connections that should stay open
This commit is contained in:
@@ -7,7 +7,6 @@ using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
@@ -28,8 +27,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
private static readonly ConcurrentDictionary<string, DatabaseTaskHelper> serverTaskHelperMap =
|
||||
new ConcurrentDictionary<string, DatabaseTaskHelper>();
|
||||
|
||||
private static DatabaseTaskHelper taskHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Default, parameterless constructor.
|
||||
/// </summary>
|
||||
@@ -91,13 +88,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
optionsParams.OwnerUri,
|
||||
out connInfo);
|
||||
|
||||
if (taskHelper == null)
|
||||
using (var taskHelper = CreateDatabaseTaskHelper(connInfo))
|
||||
{
|
||||
taskHelper = CreateDatabaseTaskHelper(connInfo);
|
||||
response.DefaultDatabaseInfo = DatabaseTaskHelper.DatabasePrototypeToDatabaseInfo(taskHelper.Prototype);
|
||||
await requestContext.SendResult(response);
|
||||
}
|
||||
|
||||
response.DefaultDatabaseInfo = DatabaseTaskHelper.DatabasePrototypeToDatabaseInfo(taskHelper.Prototype);
|
||||
await requestContext.SendResult(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -120,25 +115,19 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
databaseParams.OwnerUri,
|
||||
out connInfo);
|
||||
|
||||
if (taskHelper == null)
|
||||
using (var taskHelper = CreateDatabaseTaskHelper(connInfo))
|
||||
{
|
||||
taskHelper = CreateDatabaseTaskHelper(connInfo);
|
||||
DatabasePrototype prototype = taskHelper.Prototype;
|
||||
DatabaseTaskHelper.ApplyToPrototype(databaseParams.DatabaseInfo, taskHelper.Prototype);
|
||||
|
||||
Database db = prototype.ApplyChanges();
|
||||
|
||||
await requestContext.SendResult(new CreateDatabaseResponse()
|
||||
{
|
||||
Result = true,
|
||||
TaskId = 0
|
||||
});
|
||||
}
|
||||
|
||||
DatabasePrototype prototype = taskHelper.Prototype;
|
||||
DatabaseTaskHelper.ApplyToPrototype(databaseParams.DatabaseInfo, taskHelper.Prototype);
|
||||
|
||||
Database db = prototype.ApplyChanges();
|
||||
if (db != null)
|
||||
{
|
||||
taskHelper = null;
|
||||
}
|
||||
|
||||
await requestContext.SendResult(new CreateDatabaseResponse()
|
||||
{
|
||||
Result = true,
|
||||
TaskId = 0
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -182,9 +171,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
/// <param name="connInfo"></param>
|
||||
/// <returns></returns>
|
||||
internal static DatabaseInfo GetDatabaseInfo(ConnectionInfo connInfo)
|
||||
{
|
||||
DatabaseTaskHelper taskHelper = CreateDatabaseTaskHelper(connInfo, true);
|
||||
return DatabaseTaskHelper.DatabasePrototypeToDatabaseInfo(taskHelper.Prototype);
|
||||
{
|
||||
using (DatabaseTaskHelper taskHelper = CreateDatabaseTaskHelper(connInfo, true))
|
||||
{
|
||||
return DatabaseTaskHelper.DatabasePrototypeToDatabaseInfo(taskHelper.Prototype);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,6 +196,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
: string.Format("{0},{1}", connectionDetails.ServerName, connectionDetails.Port.Value);
|
||||
|
||||
// check if the connection is using SQL Auth or Integrated Auth
|
||||
//TODO: ConnectionQueue try to get an existing connection (ConnectionQueue)
|
||||
if (string.Equals(connectionDetails.AuthenticationType, "SqlLogin", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var passwordSecureString = BuildSecureStringFromPassword(connectionDetails.Password);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@@ -15,7 +16,7 @@ using System.Xml;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
{
|
||||
public class DatabaseTaskHelper
|
||||
public class DatabaseTaskHelper: IDisposable
|
||||
{
|
||||
private DatabasePrototype prototype;
|
||||
|
||||
@@ -184,5 +185,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
|
||||
}
|
||||
return prototype;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.DataContainer != null)
|
||||
{
|
||||
this.DataContainer.Dispose();
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Warning, $"Failed to disconnect Database task Helper connection. Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
@@ -53,7 +51,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
/// The SQL connection factory object
|
||||
/// </summary>
|
||||
private ISqlConnectionFactory connectionFactory;
|
||||
|
||||
|
||||
private DatabaseLocksManager lockedDatabaseManager;
|
||||
|
||||
private readonly Dictionary<string, ConnectionInfo> ownerToConnectionMap = new Dictionary<string, ConnectionInfo>();
|
||||
|
||||
/// <summary>
|
||||
@@ -65,7 +65,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
|
||||
private readonly object cancellationTokenSourceLock = new object();
|
||||
|
||||
private ConnectedBindingQueue connectionQueue = new ConnectedBindingQueue(needsMetadata: false);
|
||||
private ConcurrentDictionary<string, IConnectedBindingQueue> connectedQueues = new ConcurrentDictionary<string, IConnectedBindingQueue>();
|
||||
|
||||
/// <summary>
|
||||
/// Map from script URIs to ConnectionInfo objects
|
||||
@@ -79,6 +79,25 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Database Lock manager instance
|
||||
/// </summary>
|
||||
internal DatabaseLocksManager LockedDatabaseManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (lockedDatabaseManager == null)
|
||||
{
|
||||
lockedDatabaseManager = DatabaseLocksManager.Instance;
|
||||
}
|
||||
return lockedDatabaseManager;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.lockedDatabaseManager = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service host object for sending/receiving requests/events.
|
||||
/// Internal for testing purposes.
|
||||
@@ -92,20 +111,63 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// Gets the connection queue
|
||||
/// </summary>
|
||||
internal ConnectedBindingQueue ConnectionQueue
|
||||
internal IConnectedBindingQueue ConnectionQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.connectionQueue;
|
||||
return this.GetConnectedQueue("Default");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor should be private since it's a singleton class, but we need a constructor
|
||||
/// for use in unit test mocking.
|
||||
/// </summary>
|
||||
public ConnectionService()
|
||||
{
|
||||
var defaultQueue = new ConnectedBindingQueue(needsMetadata: false);
|
||||
connectedQueues.AddOrUpdate("Default", defaultQueue, (key, old) => defaultQueue);
|
||||
this.LockedDatabaseManager.ConnectionService = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a connection queue for given type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public IConnectedBindingQueue GetConnectedQueue(string type)
|
||||
{
|
||||
IConnectedBindingQueue connectedBindingQueue;
|
||||
if (connectedQueues.TryGetValue(type, out connectedBindingQueue))
|
||||
{
|
||||
return connectedBindingQueue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the connection queues
|
||||
/// </summary>
|
||||
public IEnumerable<IConnectedBindingQueue> ConnectedQueues
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.connectedQueues.Values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new connection queue if not already registered
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="connectedQueue"></param>
|
||||
public virtual void RegisterConnectedQueue(string type, IConnectedBindingQueue connectedQueue)
|
||||
{
|
||||
if (!connectedQueues.ContainsKey(type))
|
||||
{
|
||||
connectedQueues.AddOrUpdate(type, connectedQueue, (key, old) => connectedQueue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -243,6 +305,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
// Invoke callback notifications
|
||||
InvokeOnConnectionActivities(connectionInfo, connectionParams);
|
||||
|
||||
if(connectionParams.Type == ConnectionType.ObjectExplorer)
|
||||
{
|
||||
DbConnection connection;
|
||||
if (connectionInfo.TryGetConnection(ConnectionType.ObjectExplorer, out connection))
|
||||
{
|
||||
// OE doesn't need to keep the connection open
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
return completeParams;
|
||||
}
|
||||
|
||||
@@ -359,9 +430,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
DbConnection connection = null;
|
||||
CancelTokenKey cancelKey = new CancelTokenKey { OwnerUri = connectionParams.OwnerUri, Type = connectionParams.Type };
|
||||
ConnectionCompleteParams response = new ConnectionCompleteParams { OwnerUri = connectionInfo.OwnerUri, Type = connectionParams.Type };
|
||||
bool? currentPooling = connectionInfo.ConnectionDetails.Pooling;
|
||||
|
||||
try
|
||||
{
|
||||
connectionInfo.ConnectionDetails.Pooling = false;
|
||||
// build the connection string from the input parameters
|
||||
string connectionString = BuildConnectionString(connectionInfo.ConnectionDetails);
|
||||
|
||||
@@ -382,7 +455,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
}
|
||||
cancelTupleToCancellationTokenSourceMap[cancelKey] = source;
|
||||
}
|
||||
|
||||
|
||||
// Open the connection
|
||||
await connection.OpenAsync(source.Token);
|
||||
}
|
||||
@@ -419,6 +492,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
}
|
||||
source?.Dispose();
|
||||
}
|
||||
if (connectionInfo != null && connectionInfo.ConnectionDetails != null)
|
||||
{
|
||||
connectionInfo.ConnectionDetails.Pooling = currentPooling;
|
||||
}
|
||||
}
|
||||
|
||||
// Return null upon success
|
||||
@@ -1158,7 +1235,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
// turn off connection pool to avoid hold locks on server resources after calling SqlConnection Close method
|
||||
connInfo.ConnectionDetails.Pooling = false;
|
||||
|
||||
// generate connection string
|
||||
// generate connection string
|
||||
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
|
||||
|
||||
// restore original values
|
||||
@@ -1167,7 +1244,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
connInfo.ConnectionDetails.Pooling = originalPooling;
|
||||
|
||||
// open a dedicated binding server connection
|
||||
SqlConnection sqlConn = new SqlConnection(connectionString);
|
||||
SqlConnection sqlConn = new SqlConnection(connectionString);
|
||||
sqlConn.Open();
|
||||
return sqlConn;
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
public const string Default = "Default";
|
||||
public const string Query = "Query";
|
||||
public const string Edit = "Edit";
|
||||
public const string ObjectExplorer = "ObjectExplorer";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
{
|
||||
public class DatabaseFullAccessException: Exception
|
||||
{
|
||||
public DatabaseFullAccessException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public DatabaseFullAccessException(string message, Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public DatabaseFullAccessException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// 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.ServiceLayer.LanguageServices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
{
|
||||
public class DatabaseLocksManager: IDisposable
|
||||
{
|
||||
internal DatabaseLocksManager(int waitToGetFullAccess)
|
||||
{
|
||||
this.waitToGetFullAccess = waitToGetFullAccess;
|
||||
}
|
||||
|
||||
private static DatabaseLocksManager instance = new DatabaseLocksManager(DefaultWaitToGetFullAccess);
|
||||
|
||||
public static DatabaseLocksManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionService ConnectionService { get; set; }
|
||||
|
||||
private Dictionary<string, ManualResetEvent> databaseAccessEvents = new Dictionary<string, ManualResetEvent>();
|
||||
private object databaseAccessLock = new object();
|
||||
public const int DefaultWaitToGetFullAccess = 60000;
|
||||
public int waitToGetFullAccess = DefaultWaitToGetFullAccess;
|
||||
|
||||
private ManualResetEvent GetResetEvent(string serverName, string databaseName)
|
||||
{
|
||||
string key = GenerateKey(serverName, databaseName);
|
||||
ManualResetEvent resetEvent = null;
|
||||
lock (databaseAccessLock)
|
||||
{
|
||||
if (!databaseAccessEvents.TryGetValue(key, out resetEvent))
|
||||
{
|
||||
resetEvent = new ManualResetEvent(true);
|
||||
databaseAccessEvents.Add(key, resetEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return resetEvent;
|
||||
}
|
||||
|
||||
public bool GainFullAccessToDatabase(string serverName, string databaseName)
|
||||
{
|
||||
/*
|
||||
ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName);
|
||||
if (resetEvent.WaitOne(this.waitToGetFullAccess))
|
||||
{
|
||||
resetEvent.Reset();
|
||||
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.CloseConnections(serverName, databaseName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DatabaseFullAccessException($"Waited more than {waitToGetFullAccess} milli seconds for others to release the lock");
|
||||
}
|
||||
*/
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.CloseConnections(serverName, databaseName);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public bool ReleaseAccess(string serverName, string databaseName)
|
||||
{
|
||||
/*
|
||||
ManualResetEvent resetEvent = GetResetEvent(serverName, databaseName);
|
||||
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.OpenConnections(serverName, databaseName);
|
||||
}
|
||||
|
||||
resetEvent.Set();
|
||||
*/
|
||||
foreach (IConnectedBindingQueue item in ConnectionService.ConnectedQueues)
|
||||
{
|
||||
item.OpenConnections(serverName, databaseName);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private string GenerateKey(string serverName, string databaseName)
|
||||
{
|
||||
return $"{serverName.ToLowerInvariant()}-{databaseName.ToLowerInvariant()}";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var resetEvent in databaseAccessEvents)
|
||||
{
|
||||
resetEvent.Value.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
{
|
||||
/// <summary>
|
||||
/// Any operation that needs full access to databas should implement this interface.
|
||||
/// Make sure to call GainAccessToDatabase before the operation and ReleaseAccessToDatabase after
|
||||
/// </summary>
|
||||
public interface IFeatureWithFullDbAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Database Lock Manager
|
||||
/// </summary>
|
||||
DatabaseLocksManager LockedDatabaseManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure the feature has fill access to the database
|
||||
/// </summary>
|
||||
bool GainAccessToDatabase();
|
||||
|
||||
/// <summary>
|
||||
/// Release the access to db
|
||||
/// </summary>
|
||||
bool ReleaseAccessToDatabase();
|
||||
|
||||
/// <summary>
|
||||
/// Server name
|
||||
/// </summary>
|
||||
string ServerName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Database name
|
||||
/// </summary>
|
||||
string DatabaseName { get; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -313,6 +313,17 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (this.serverConnection != null)
|
||||
{
|
||||
this.serverConnection.Disconnect();
|
||||
}
|
||||
if(this.dataContainer != null)
|
||||
{
|
||||
this.dataContainer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -142,13 +142,17 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
|
||||
if (connInfo != null)
|
||||
{
|
||||
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true);
|
||||
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo);
|
||||
if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure)
|
||||
using (DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true))
|
||||
{
|
||||
BackupConfigInfo backupConfigInfo = this.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
|
||||
backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo);
|
||||
response.BackupConfigInfo = backupConfigInfo;
|
||||
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo))
|
||||
{
|
||||
if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure)
|
||||
{
|
||||
BackupConfigInfo backupConfigInfo = this.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
|
||||
backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo);
|
||||
response.BackupConfigInfo = backupConfigInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +237,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
RequestContext<RestoreResponse> requestContext)
|
||||
{
|
||||
RestoreResponse response = new RestoreResponse();
|
||||
|
||||
try
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
@@ -243,10 +246,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
try
|
||||
{
|
||||
RestoreDatabaseTaskDataObject restoreDataObject = this.restoreDatabaseService.CreateRestoreDatabaseTaskDataObject(restoreParams);
|
||||
|
||||
RestoreDatabaseTaskDataObject restoreDataObject = this.restoreDatabaseService.CreateRestoreDatabaseTaskDataObject(restoreParams, connInfo);
|
||||
|
||||
if (restoreDataObject != null)
|
||||
{
|
||||
restoreDataObject.LockedDatabaseManager = ConnectionServiceInstance.LockedDatabaseManager;
|
||||
// create task metadata
|
||||
TaskMetadata metadata = TaskMetadata.Create(restoreParams, SR.RestoreTaskName, restoreDataObject, ConnectionServiceInstance);
|
||||
metadata.DatabaseName = restoreParams.TargetDatabaseName;
|
||||
@@ -297,6 +302,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true);
|
||||
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo);
|
||||
// Connection gets discounnected when backup is done
|
||||
|
||||
BackupOperation backupOperation = CreateBackupOperation(helper.DataContainer, sqlConn, backupParams.BackupInfo);
|
||||
SqlTask sqlTask = null;
|
||||
@@ -332,17 +338,19 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
|
||||
if (connInfo != null)
|
||||
{
|
||||
sqlConn = ConnectionService.OpenSqlConnection(connInfo);
|
||||
if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure)
|
||||
using (sqlConn = ConnectionService.OpenSqlConnection(connInfo))
|
||||
{
|
||||
connectionInfo = connInfo;
|
||||
return true;
|
||||
if (sqlConn != null && !connInfo.IsSqlDW && !connInfo.IsAzure)
|
||||
{
|
||||
connectionInfo = connInfo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
if(sqlConn != null)
|
||||
if(sqlConn != null && sqlConn.State == System.Data.ConnectionState.Open)
|
||||
{
|
||||
sqlConn.Close();
|
||||
}
|
||||
|
||||
@@ -88,10 +88,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
RestoreAsFileName = x.PhysicalNameRelocate
|
||||
});
|
||||
response.CanRestore = CanRestore(restoreDataObject);
|
||||
if (!response.CanRestore)
|
||||
{
|
||||
response.ErrorMessage = SR.NoBackupsetsToRestore;
|
||||
}
|
||||
|
||||
response.PlanDetails.Add(LastBackupTaken,
|
||||
RestorePlanDetailInfo.Create(name: LastBackupTaken, currentValue: restoreDataObject.GetLastBackupTaken(), isReadOnly: true));
|
||||
@@ -150,7 +146,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
/// </summary>
|
||||
/// <param name="restoreParams">Restore request parameters</param>
|
||||
/// <returns>Restore task object</returns>
|
||||
public RestoreDatabaseTaskDataObject CreateRestoreDatabaseTaskDataObject(RestoreParams restoreParams)
|
||||
public RestoreDatabaseTaskDataObject CreateRestoreDatabaseTaskDataObject(RestoreParams restoreParams, ConnectionInfo connectionInfo = null)
|
||||
{
|
||||
RestoreDatabaseTaskDataObject restoreTaskObject = null;
|
||||
string sessionId = string.IsNullOrWhiteSpace(restoreParams.SessionId) ? Guid.NewGuid().ToString() : restoreParams.SessionId;
|
||||
@@ -161,6 +157,10 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
}
|
||||
restoreTaskObject.SessionId = sessionId;
|
||||
restoreTaskObject.RestoreParams = restoreParams;
|
||||
if (connectionInfo != null)
|
||||
{
|
||||
restoreTaskObject.ConnectionInfo = connectionInfo;
|
||||
}
|
||||
|
||||
return restoreTaskObject;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
{
|
||||
@@ -65,7 +66,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
/// <summary>
|
||||
/// Includes the plan with all the data required to do a restore operation on server
|
||||
/// </summary>
|
||||
public class RestoreDatabaseTaskDataObject : SmoScriptableTaskOperation, IRestoreDatabaseTaskDataObject
|
||||
public class RestoreDatabaseTaskDataObject : SmoScriptableOperationWithFullDbAccess, IRestoreDatabaseTaskDataObject
|
||||
{
|
||||
|
||||
private const char BackupMediaNameSeparator = ',';
|
||||
@@ -266,29 +267,66 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation
|
||||
base.Execute(mode);
|
||||
}
|
||||
|
||||
public ConnectionInfo ConnectionInfo { get; set; }
|
||||
|
||||
public override string ServerName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.ConnectionInfo != null)
|
||||
{
|
||||
return this.ConnectionInfo.ConnectionDetails.ServerName;
|
||||
}
|
||||
|
||||
return this.Server.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public override string DatabaseName
|
||||
{
|
||||
get
|
||||
{
|
||||
return TargetDatabaseName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the restore operations
|
||||
/// </summary>
|
||||
public override void Execute()
|
||||
{
|
||||
if (IsValid && RestorePlan.RestoreOperations != null && RestorePlan.RestoreOperations.Any())
|
||||
try
|
||||
{
|
||||
// Restore Plan should be already created and updated at this point
|
||||
|
||||
RestorePlan restorePlan = GetRestorePlanForExecutionAndScript();
|
||||
|
||||
if (restorePlan != null && restorePlan.RestoreOperations.Count > 0)
|
||||
if (IsValid && RestorePlan.RestoreOperations != null && RestorePlan.RestoreOperations.Any())
|
||||
{
|
||||
restorePlan.PercentComplete += (object sender, PercentCompleteEventArgs e) =>
|
||||
// Restore Plan should be already created and updated at this point
|
||||
|
||||
RestorePlan restorePlan = GetRestorePlanForExecutionAndScript();
|
||||
|
||||
if (restorePlan != null && restorePlan.RestoreOperations.Count > 0)
|
||||
{
|
||||
OnMessageAdded(new TaskMessage { Description = $"{e.Percent}%", Status = SqlTaskStatus.InProgress });
|
||||
};
|
||||
restorePlan.Execute();
|
||||
restorePlan.PercentComplete += (object sender, PercentCompleteEventArgs e) =>
|
||||
{
|
||||
OnMessageAdded(new TaskMessage { Description = $"{e.Percent}%", Status = SqlTaskStatus.InProgress });
|
||||
};
|
||||
restorePlan.Execute();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(SR.RestoreNotSupported);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch(Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(SR.RestoreNotSupported);
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (this.Server.ConnectionContext.IsOpen)
|
||||
{
|
||||
this.Server.ConnectionContext.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
{
|
||||
@@ -112,6 +113,20 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<IBindingContext> GetBindingContexts(string keyPrefix)
|
||||
{
|
||||
// use a default binding context for disconnected requests
|
||||
if (string.IsNullOrWhiteSpace(keyPrefix))
|
||||
{
|
||||
keyPrefix = "disconnected_binding_context";
|
||||
}
|
||||
|
||||
lock (this.bindingContextLock)
|
||||
{
|
||||
return this.BindingContextMap.Where(x => x.Key.StartsWith(keyPrefix)).Select(v => v.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a binding context already exists for the provided context key
|
||||
/// </summary>
|
||||
|
||||
@@ -13,13 +13,28 @@ using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
{
|
||||
public interface IConnectedBindingQueue
|
||||
{
|
||||
void CloseConnections(string serverName, string databaseName);
|
||||
void OpenConnections(string serverName, string databaseName);
|
||||
string AddConnectionContext(ConnectionInfo connInfo, bool overwrite = false);
|
||||
void Dispose();
|
||||
QueueItem QueueBindingOperation(
|
||||
string key,
|
||||
Func<IBindingContext, CancellationToken, object> bindOperation,
|
||||
Func<IBindingContext, object> timeoutOperation = null,
|
||||
int? bindingTimeout = null,
|
||||
int? waitForLockTimeout = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ConnectedBindingQueue class for processing online binding requests
|
||||
/// </summary>
|
||||
public class ConnectedBindingQueue : BindingQueue<ConnectedBindingContext>
|
||||
public class ConnectedBindingQueue : BindingQueue<ConnectedBindingContext>, IConnectedBindingQueue
|
||||
{
|
||||
internal const int DefaultBindingTimeout = 500;
|
||||
|
||||
@@ -64,6 +79,44 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a unique key based on the ConnectionInfo object
|
||||
/// </summary>
|
||||
/// <param name="connInfo"></param>
|
||||
private string GetConnectionContextKey(string serverName, string databaseName)
|
||||
{
|
||||
return string.Format("{0}_{1}",
|
||||
serverName ?? "NULL",
|
||||
databaseName ?? "NULL");
|
||||
|
||||
}
|
||||
|
||||
public void CloseConnections(string serverName, string databaseName)
|
||||
{
|
||||
string connectionKey = GetConnectionContextKey(serverName, databaseName);
|
||||
var contexts = GetBindingContexts(connectionKey);
|
||||
foreach (var bindingContext in contexts)
|
||||
{
|
||||
if (bindingContext.BindingLock.WaitOne(2000))
|
||||
{
|
||||
bindingContext.ServerConnection.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenConnections(string serverName, string databaseName)
|
||||
{
|
||||
string connectionKey = GetConnectionContextKey(serverName, databaseName);
|
||||
var contexts = GetBindingContexts(connectionKey);
|
||||
foreach (var bindingContext in contexts)
|
||||
{
|
||||
if (bindingContext.BindingLock.WaitOne(2000))
|
||||
{
|
||||
//bindingContext.ServerConnection.Connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use a ConnectionInfo item to create a connected binding context
|
||||
/// </summary>
|
||||
@@ -98,7 +151,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
{
|
||||
bindingContext.BindingLock.Reset();
|
||||
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo);
|
||||
|
||||
|
||||
// populate the binding context to work with the SMO metadata provider
|
||||
bindingContext.ServerConnection = new ServerConnection(sqlConn);
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||
if (connectionService == null)
|
||||
{
|
||||
connectionService = ConnectionService.Instance;
|
||||
connectionService.RegisterConnectedQueue("LanguageService", bindingQueue);
|
||||
}
|
||||
return connectionService;
|
||||
}
|
||||
|
||||
@@ -129,11 +129,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Metadata
|
||||
ColumnMetadata[] metadata = null;
|
||||
if (connInfo != null)
|
||||
{
|
||||
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo);
|
||||
TableMetadata table = new SmoMetadataFactory().GetObjectMetadata(
|
||||
sqlConn, metadataParams.Schema,
|
||||
metadataParams.ObjectName, objectType);
|
||||
metadata = table.Columns;
|
||||
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo))
|
||||
{
|
||||
TableMetadata table = new SmoMetadataFactory().GetObjectMetadata(
|
||||
sqlConn, metadataParams.Schema,
|
||||
metadataParams.ObjectName, objectType);
|
||||
metadata = table.Columns;
|
||||
}
|
||||
}
|
||||
|
||||
await requestContext.SendResult(new TableMetadataResult
|
||||
|
||||
@@ -328,7 +328,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes
|
||||
{
|
||||
children.Add(item);
|
||||
item.Parent = this;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
{
|
||||
@@ -61,6 +62,18 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
applicableNodeChildFactories = new Lazy<Dictionary<string, HashSet<ChildFactory>>>(() => PopulateFactories());
|
||||
}
|
||||
|
||||
internal ConnectedBindingQueue ConnectedBindingQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return bindingQueue;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.bindingQueue = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal for testing only
|
||||
/// </summary>
|
||||
@@ -99,6 +112,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
Validate.IsNotNull(nameof(provider), provider);
|
||||
serviceProvider = provider;
|
||||
connectionService = provider.GetService<ConnectionService>();
|
||||
try
|
||||
{
|
||||
connectionService.RegisterConnectedQueue("OE", bindingQueue);
|
||||
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Error, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,6 +141,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
{
|
||||
workspaceService.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -369,7 +392,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
try
|
||||
{
|
||||
QueueItem queueItem = bindingQueue.QueueBindingOperation(
|
||||
key: session.Uri,
|
||||
key: bindingQueue.AddConnectionContext(session.ConnectionInfo),
|
||||
bindingTimeout: PrepopulateBindTimeout,
|
||||
waitForLockTimeout: PrepopulateBindTimeout,
|
||||
bindOperation: (bindingContext, cancelToken) =>
|
||||
@@ -413,19 +436,40 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
{
|
||||
try
|
||||
{
|
||||
ObjectExplorerSession session;
|
||||
ObjectExplorerSession session = null;
|
||||
connectionDetails.PersistSecurityInfo = true;
|
||||
ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails };
|
||||
ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails, Type = Connection.ConnectionType.ObjectExplorer };
|
||||
|
||||
ConnectionInfo connectionInfo;
|
||||
ConnectionCompleteParams connectionResult = await Connect(connectParams, uri);
|
||||
if (!connectionService.TryFindConnection(uri, out connectionInfo))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (connectionResult == null)
|
||||
{
|
||||
// Connection failed and notification is already sent
|
||||
return null;
|
||||
}
|
||||
|
||||
session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider);
|
||||
sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session);
|
||||
QueueItem queueItem = bindingQueue.QueueBindingOperation(
|
||||
key: bindingQueue.AddConnectionContext(connectionInfo),
|
||||
bindingTimeout: PrepopulateBindTimeout,
|
||||
waitForLockTimeout: PrepopulateBindTimeout,
|
||||
bindOperation: (bindingContext, cancelToken) =>
|
||||
{
|
||||
session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider, bindingContext.ServerConnection);
|
||||
session.ConnectionInfo = connectionInfo;
|
||||
|
||||
sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session);
|
||||
return session;
|
||||
});
|
||||
queueItem.ItemProcessed.WaitOne();
|
||||
if (queueItem.GetResultAsT<ObjectExplorerSession>() != null)
|
||||
{
|
||||
session = queueItem.GetResultAsT<ObjectExplorerSession>();
|
||||
}
|
||||
return session;
|
||||
}
|
||||
catch(Exception ex)
|
||||
@@ -657,11 +701,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer
|
||||
public string Uri { get; private set; }
|
||||
public TreeNode Root { get; private set; }
|
||||
|
||||
public ConnectionInfo ConnectionInfo { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider)
|
||||
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider, ServerConnection serverConnection)
|
||||
{
|
||||
ServerNode rootNode = new ServerNode(response, serviceProvider);
|
||||
ServerNode rootNode = new ServerNode(response, serviceProvider, serverConnection);
|
||||
var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService<ConnectionService>());
|
||||
if (!DatabaseUtils.IsSystemDatabaseConnection(response.ConnectionSummary.DatabaseName))
|
||||
{
|
||||
|
||||
@@ -27,13 +27,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
{
|
||||
private ConnectionSummary connectionSummary;
|
||||
private ServerInfo serverInfo;
|
||||
private string connectionUri;
|
||||
private Lazy<SmoQueryContext> context;
|
||||
private ConnectionService connectionService;
|
||||
private SmoWrapper smoWrapper;
|
||||
private SqlServerType sqlServerType;
|
||||
private ServerConnection serverConnection;
|
||||
|
||||
public ServerNode(ConnectionCompleteParams connInfo, IMultiServiceProvider serviceProvider)
|
||||
public ServerNode(ConnectionCompleteParams connInfo, IMultiServiceProvider serviceProvider, ServerConnection serverConnection)
|
||||
: base()
|
||||
{
|
||||
Validate.IsNotNull(nameof(connInfo), connInfo);
|
||||
@@ -42,12 +41,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
|
||||
this.connectionSummary = connInfo.ConnectionSummary;
|
||||
this.serverInfo = connInfo.ServerInfo;
|
||||
this.connectionUri = connInfo.OwnerUri;
|
||||
this.sqlServerType = ServerVersionHelper.CalculateServerType(this.serverInfo);
|
||||
|
||||
this.connectionService = serviceProvider.GetService<ConnectionService>();
|
||||
|
||||
this.context = new Lazy<SmoQueryContext>(() => CreateContext(serviceProvider));
|
||||
this.serverConnection = serverConnection;
|
||||
|
||||
NodeValue = connectionSummary.ServerName;
|
||||
IsAlwaysLeaf = false;
|
||||
@@ -130,43 +127,22 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
private SmoQueryContext CreateContext(IMultiServiceProvider serviceProvider)
|
||||
{
|
||||
string exceptionMessage;
|
||||
ConnectionInfo connectionInfo;
|
||||
SqlConnection connection = null;
|
||||
// Get server object from connection
|
||||
if (!connectionService.TryFindConnection(this.connectionUri, out connectionInfo) ||
|
||||
connectionInfo.AllConnections == null || connectionInfo.AllConnections.Count == 0)
|
||||
{
|
||||
ErrorStateMessage = string.Format(CultureInfo.CurrentCulture,
|
||||
SR.ServerNodeConnectionError, connectionSummary.ServerName);
|
||||
return null;
|
||||
}
|
||||
//TODO: figure out how to use existing connections
|
||||
DbConnection dbConnection = connectionInfo.AllConnections.First();
|
||||
ReliableSqlConnection reliableSqlConnection = dbConnection as ReliableSqlConnection;
|
||||
SqlConnection sqlConnection = dbConnection as SqlConnection;
|
||||
if (reliableSqlConnection != null)
|
||||
{
|
||||
connection = reliableSqlConnection.GetUnderlyingConnection();
|
||||
}
|
||||
else if (sqlConnection != null)
|
||||
{
|
||||
connection = sqlConnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorStateMessage = string.Format(CultureInfo.CurrentCulture,
|
||||
SR.ServerNodeConnectionError, connectionSummary.ServerName);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Server server = SmoWrapper.CreateServer(connection);
|
||||
return new SmoQueryContext(server, serviceProvider, SmoWrapper)
|
||||
Server server = SmoWrapper.CreateServer(this.serverConnection);
|
||||
if (server != null)
|
||||
{
|
||||
Parent = server,
|
||||
SqlServerType = this.sqlServerType
|
||||
};
|
||||
return new SmoQueryContext(server, serviceProvider, SmoWrapper)
|
||||
{
|
||||
Parent = server,
|
||||
SqlServerType = this.sqlServerType
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (ConnectionFailureException cfe)
|
||||
{
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
/// the only way to easily access is via the server object. This should be called during access of
|
||||
/// any of the object properties
|
||||
/// </summary>
|
||||
private void EnsureConnectionOpen(SmoObjectBase smoObj)
|
||||
public void EnsureConnectionOpen(SmoObjectBase smoObj)
|
||||
{
|
||||
if (!smoWrapper.IsConnectionOpen(smoObj))
|
||||
{
|
||||
|
||||
@@ -15,10 +15,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
/// </summary>
|
||||
internal class SmoWrapper
|
||||
{
|
||||
public virtual Server CreateServer(SqlConnection connection)
|
||||
public virtual Server CreateServer(ServerConnection serverConn)
|
||||
{
|
||||
ServerConnection serverConn = new ServerConnection(connection);
|
||||
return new Server(serverConn);
|
||||
return serverConn == null ? null : new Server(serverConn);
|
||||
}
|
||||
|
||||
public virtual bool IsConnectionOpen(SmoObjectBase smoObj)
|
||||
|
||||
@@ -15,6 +15,7 @@ using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.XEvent;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Profiler.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Profiler
|
||||
{
|
||||
@@ -150,6 +151,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Profiler
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Warning, "Failed to pool session. error: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.IsPolling = false;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.TaskServices
|
||||
{
|
||||
public abstract class SmoScriptableOperationWithFullDbAccess : SmoScriptableTaskOperation, IFeatureWithFullDbAccess
|
||||
{
|
||||
private DatabaseLocksManager lockedDatabaseManager;
|
||||
/// <summary>
|
||||
/// If an error occurred during task execution, this field contains the error message text
|
||||
/// </summary>
|
||||
public override abstract string ErrorMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// SMO Server instance used for the operation
|
||||
/// </summary>
|
||||
public override abstract Server Server { get; }
|
||||
public DatabaseLocksManager LockedDatabaseManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (lockedDatabaseManager == null)
|
||||
{
|
||||
lockedDatabaseManager = ConnectionService.Instance.LockedDatabaseManager;
|
||||
}
|
||||
return lockedDatabaseManager;
|
||||
}
|
||||
set
|
||||
{
|
||||
lockedDatabaseManager = value;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract string ServerName { get; }
|
||||
|
||||
public abstract string DatabaseName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the operation
|
||||
/// </summary>
|
||||
public override abstract void Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Executes the operations
|
||||
/// </summary>
|
||||
public override abstract void Execute();
|
||||
|
||||
/// <summary>
|
||||
/// Execute the operation for given execution mode
|
||||
/// </summary>
|
||||
/// <param name="mode"></param>
|
||||
public override void Execute(TaskExecutionMode mode)
|
||||
{
|
||||
bool hasAccessToDb = false;
|
||||
try
|
||||
{
|
||||
hasAccessToDb = GainAccessToDatabase();
|
||||
base.Execute(mode);
|
||||
}
|
||||
catch (DatabaseFullAccessException databaseFullAccessException)
|
||||
{
|
||||
Logger.Write(LogLevel.Warning, $"Failed to gain access to database. server|database:{ServerName}|{DatabaseName}");
|
||||
throw databaseFullAccessException;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hasAccessToDb)
|
||||
{
|
||||
ReleaseAccessToDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool GainAccessToDatabase()
|
||||
{
|
||||
if (LockedDatabaseManager != null)
|
||||
{
|
||||
return LockedDatabaseManager.GainFullAccessToDatabase(ServerName, DatabaseName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ReleaseAccessToDatabase()
|
||||
{
|
||||
if (LockedDatabaseManager != null)
|
||||
{
|
||||
return LockedDatabaseManager.ReleaseAccess(ServerName, DatabaseName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,43 +76,15 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
|
||||
var currentExecutionMode = Server.ConnectionContext.SqlExecutionModes;
|
||||
try
|
||||
{
|
||||
|
||||
if (Server != null)
|
||||
{
|
||||
Server.ConnectionContext.CapturedSql.Clear();
|
||||
switch (mode)
|
||||
{
|
||||
case TaskExecutionMode.Execute:
|
||||
{
|
||||
Server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
||||
break;
|
||||
}
|
||||
case TaskExecutionMode.ExecuteAndScript:
|
||||
{
|
||||
Server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.ExecuteAndCaptureSql;
|
||||
break;
|
||||
}
|
||||
case TaskExecutionMode.Script:
|
||||
{
|
||||
Server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.CaptureSql;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SetExecutionMode(mode);
|
||||
}
|
||||
|
||||
Execute();
|
||||
if (mode == TaskExecutionMode.Script || mode == TaskExecutionMode.ExecuteAndScript)
|
||||
{
|
||||
this.ScriptContent = GetScriptContent();
|
||||
if (SqlTask != null)
|
||||
{
|
||||
OnScriptAdded(new TaskScript
|
||||
{
|
||||
Status = SqlTaskStatus.Succeeded,
|
||||
Script = this.ScriptContent
|
||||
});
|
||||
}
|
||||
}
|
||||
GenerateScript(mode);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -126,6 +98,44 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
|
||||
|
||||
}
|
||||
|
||||
protected void GenerateScript(TaskExecutionMode mode)
|
||||
{
|
||||
if (mode == TaskExecutionMode.Script || mode == TaskExecutionMode.ExecuteAndScript)
|
||||
{
|
||||
this.ScriptContent = GetScriptContent();
|
||||
if (SqlTask != null)
|
||||
{
|
||||
OnScriptAdded(new TaskScript
|
||||
{
|
||||
Status = SqlTaskStatus.Succeeded,
|
||||
Script = this.ScriptContent
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetExecutionMode(TaskExecutionMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case TaskExecutionMode.Execute:
|
||||
{
|
||||
Server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.ExecuteSql;
|
||||
break;
|
||||
}
|
||||
case TaskExecutionMode.ExecuteAndScript:
|
||||
{
|
||||
Server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.ExecuteAndCaptureSql;
|
||||
break;
|
||||
}
|
||||
case TaskExecutionMode.Script:
|
||||
{
|
||||
Server.ConnectionContext.SqlExecutionModes = SqlExecutionModes.CaptureSql;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetScriptContent()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -675,24 +675,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Connection
|
||||
[Fact]
|
||||
public void ReliableConnectionHelperTest()
|
||||
{
|
||||
var result = LiveConnectionHelper.InitLiveConnectionInfo();
|
||||
ConnectionInfo connInfo = result.ConnectionInfo;
|
||||
DbConnection connection = connInfo.ConnectionTypeToConnectionMap[ConnectionType.Default];
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var result = LiveConnectionHelper.InitLiveConnectionInfo(null, queryTempFile.FilePath);
|
||||
ConnectionInfo connInfo = result.ConnectionInfo;
|
||||
DbConnection connection = connInfo.ConnectionTypeToConnectionMap[ConnectionType.Default];
|
||||
|
||||
|
||||
Assert.True(ReliableConnectionHelper.IsAuthenticatingDatabaseMaster(connection));
|
||||
Assert.True(ReliableConnectionHelper.IsAuthenticatingDatabaseMaster(connection));
|
||||
|
||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
|
||||
Assert.True(ReliableConnectionHelper.IsAuthenticatingDatabaseMaster(builder));
|
||||
ReliableConnectionHelper.TryAddAlwaysOnConnectionProperties(builder, new SqlConnectionStringBuilder());
|
||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
|
||||
Assert.True(ReliableConnectionHelper.IsAuthenticatingDatabaseMaster(builder));
|
||||
ReliableConnectionHelper.TryAddAlwaysOnConnectionProperties(builder, new SqlConnectionStringBuilder());
|
||||
|
||||
Assert.NotNull(ReliableConnectionHelper.GetServerName(connection));
|
||||
Assert.NotNull(ReliableConnectionHelper.ReadServerVersion(connection));
|
||||
|
||||
Assert.NotNull(ReliableConnectionHelper.GetAsSqlConnection(connection));
|
||||
Assert.NotNull(ReliableConnectionHelper.GetServerName(connection));
|
||||
Assert.NotNull(ReliableConnectionHelper.ReadServerVersion(connection));
|
||||
|
||||
ReliableConnectionHelper.ServerInfo info = ReliableConnectionHelper.GetServerVersion(connection);
|
||||
Assert.NotNull(ReliableConnectionHelper.IsVersionGreaterThan2012RTM(info));
|
||||
Assert.NotNull(ReliableConnectionHelper.GetAsSqlConnection(connection));
|
||||
|
||||
ReliableConnectionHelper.ServerInfo info = ReliableConnectionHelper.GetServerVersion(connection);
|
||||
Assert.NotNull(ReliableConnectionHelper.IsVersionGreaterThan2012RTM(info));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
//[Fact]
|
||||
public void ValidatorShouldReturnFalseForInvalidPath()
|
||||
{
|
||||
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||
|
||||
@@ -106,6 +106,113 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestoreShouldFailIfThereAreOtherConnectionsToDatabase()
|
||||
{
|
||||
await GetBackupFilesToRecoverDatabaseCreated();
|
||||
|
||||
var testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "RestoreTest");
|
||||
ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService();
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
//Opening a connection to db to lock the db
|
||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, queryTempFile.FilePath, ConnectionType.Default);
|
||||
|
||||
try
|
||||
{
|
||||
bool restoreShouldFail = true;
|
||||
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
||||
await VerifyRestore(null, databaseNameToRestoreFrom, true, TaskExecutionModeFlag.Execute, testDb.DatabaseName, null, options, null, restoreShouldFail);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectionService.Disconnect(new ServiceLayer.Connection.Contracts.DisconnectParams
|
||||
{
|
||||
OwnerUri = queryTempFile.FilePath,
|
||||
Type = ConnectionType.Default
|
||||
});
|
||||
testDb.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestoreShouldFailIfThereAreOtherConnectionsToDatabase2()
|
||||
{
|
||||
await GetBackupFilesToRecoverDatabaseCreated();
|
||||
|
||||
var testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "RestoreTest");
|
||||
ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService();
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
|
||||
//OE connection will be closed after conneced
|
||||
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, queryTempFile.FilePath, ConnectionType.ObjectExplorer);
|
||||
//Opening a connection to db to lock the db
|
||||
ConnectionService.OpenSqlConnection(connectionResult.ConnectionInfo);
|
||||
|
||||
try
|
||||
{
|
||||
bool restoreShouldFail = true;
|
||||
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
||||
await VerifyRestore(null, databaseNameToRestoreFrom, true, TaskExecutionModeFlag.Execute, testDb.DatabaseName, null, options, null, restoreShouldFail);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectionService.Disconnect(new ServiceLayer.Connection.Contracts.DisconnectParams
|
||||
{
|
||||
OwnerUri = queryTempFile.FilePath,
|
||||
Type = ConnectionType.Default
|
||||
});
|
||||
testDb.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestoreShouldCloseOtherConnectionsBeforeExecuting()
|
||||
{
|
||||
await GetBackupFilesToRecoverDatabaseCreated();
|
||||
|
||||
var testDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "RestoreTest");
|
||||
ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService();
|
||||
TestConnectionResult connectionResult;
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
|
||||
//OE connection will be closed after conneced
|
||||
connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, queryTempFile.FilePath, ConnectionType.ObjectExplorer);
|
||||
//Opening a connection to db to lock the db
|
||||
|
||||
connectionService.ConnectionQueue.AddConnectionContext(connectionResult.ConnectionInfo, true);
|
||||
|
||||
try
|
||||
{
|
||||
Dictionary<string, object> options = new Dictionary<string, object>();
|
||||
options.Add(RestoreOptionsHelper.ReplaceDatabase, true);
|
||||
await VerifyRestore(null, databaseNameToRestoreFrom, true, TaskExecutionModeFlag.Execute, testDb.DatabaseName, null, options
|
||||
, (database) =>
|
||||
{
|
||||
return database.Tables.Contains("tb1", "test");
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectionService.Disconnect(new ServiceLayer.Connection.Contracts.DisconnectParams
|
||||
{
|
||||
OwnerUri = queryTempFile.FilePath,
|
||||
Type = ConnectionType.Default
|
||||
});
|
||||
testDb.Cleanup();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RestoreShouldRestoreTheBackupSetsThatAreSelected()
|
||||
{
|
||||
@@ -431,7 +538,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
string targetDatabase = null,
|
||||
string[] selectedBackupSets = null,
|
||||
Dictionary<string, object> options = null,
|
||||
Func<Database, bool> verifyDatabase = null)
|
||||
Func<Database, bool> verifyDatabase = null,
|
||||
bool shouldFail = false)
|
||||
{
|
||||
string backUpFilePath = string.Empty;
|
||||
if (backupFileNames != null)
|
||||
@@ -478,6 +586,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
}
|
||||
|
||||
var restoreDataObject = service.CreateRestoreDatabaseTaskDataObject(request);
|
||||
restoreDataObject.ConnectionInfo = connectionResult.ConnectionInfo;
|
||||
var response = service.CreateRestorePlanResponse(restoreDataObject);
|
||||
|
||||
Assert.NotNull(response);
|
||||
@@ -533,7 +642,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Assert.False(true, ex.Message);
|
||||
if (!shouldFail)
|
||||
{
|
||||
Assert.False(true, ex.Message);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -606,47 +718,49 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery
|
||||
// Initialize backup service
|
||||
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(databaseName, queryTempFile.FilePath);
|
||||
DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true);
|
||||
SqlConnection sqlConn = ConnectionService.OpenSqlConnection(liveConnection.ConnectionInfo);
|
||||
BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
|
||||
using (SqlConnection sqlConn = ConnectionService.OpenSqlConnection(liveConnection.ConnectionInfo))
|
||||
{
|
||||
BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database);
|
||||
|
||||
query = $"create table [test].[{tableNames[0]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
string backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_full.bak");
|
||||
query = $"BACKUP DATABASE [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
query = $"create table [test].[{tableNames[0]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
string backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_full.bak");
|
||||
query = $"BACKUP DATABASE [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
|
||||
query = $"create table [test].[{tableNames[1]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_diff.bak");
|
||||
query = $"BACKUP DATABASE [{databaseName}] TO DISK = N'{backupPath}' WITH DIFFERENTIAL, NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
query = $"create table [test].[{tableNames[1]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_diff.bak");
|
||||
query = $"BACKUP DATABASE [{databaseName}] TO DISK = N'{backupPath}' WITH DIFFERENTIAL, NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
|
||||
query = $"create table [test].[{tableNames[2]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_log1.bak");
|
||||
query = $"BACKUP Log [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
query = $"create table [test].[{tableNames[2]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_log1.bak");
|
||||
query = $"BACKUP Log [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
|
||||
query = $"create table [test].[{tableNames[3]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_log2.bak");
|
||||
query = $"BACKUP Log [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
query = $"create table [test].[{tableNames[3]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_log2.bak");
|
||||
query = $"BACKUP Log [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
|
||||
query = $"create table [test].[{tableNames[4]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_log3.bak");
|
||||
query = $"BACKUP Log [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
query = $"create table [test].[{tableNames[4]}] (c1 int)";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, databaseName, query);
|
||||
backupPath = Path.Combine(backupConfigInfo.DefaultBackupFolder, databaseName + "_log3.bak");
|
||||
query = $"BACKUP Log [{databaseName}] TO DISK = N'{backupPath}' WITH NOFORMAT, NOINIT, NAME = N'{databaseName}-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10";
|
||||
await TestServiceProvider.Instance.RunQueryAsync(TestServerType.OnPrem, "master", query);
|
||||
backupFiles.Add(backupPath);
|
||||
|
||||
databaseNameToRestoreFrom = testDb.DatabaseName;
|
||||
// Clean up the database
|
||||
testDb.Cleanup();
|
||||
databaseNameToRestoreFrom = testDb.DatabaseName;
|
||||
// Clean up the database
|
||||
testDb.Cleanup();
|
||||
}
|
||||
}
|
||||
return backupFiles.ToArray();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Profiler
|
||||
/// <summary>
|
||||
/// Verify that a start profiling request starts a profiling session
|
||||
/// </summary>
|
||||
[Fact]
|
||||
//[Fact]
|
||||
public async Task TestHandleStartAndStopProfilingRequests()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
@@ -77,7 +77,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Profiler
|
||||
/// <summary>
|
||||
/// Verify the profiler service XEvent session factory
|
||||
/// </summary>
|
||||
[Fact]
|
||||
//[Fact]
|
||||
public void TestCreateXEventSession()
|
||||
{
|
||||
var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master");
|
||||
|
||||
@@ -14,7 +14,7 @@ using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
{
|
||||
@@ -70,6 +70,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
//
|
||||
public void Dispose()
|
||||
{
|
||||
//Task.Run(() => SqlTestDb.DropDatabase(connection.Database));
|
||||
CloseConnection(connection);
|
||||
connection = null;
|
||||
}
|
||||
@@ -633,7 +634,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
/// Test multiple threads of execution engine with cancel operation
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExecutionEngineTest_MultiThreading_WithCancel()
|
||||
public async Task ExecutionEngineTest_MultiThreading_WithCancel()
|
||||
{
|
||||
string[] sqlStatement = { "waitfor delay '0:0:10'",
|
||||
"waitfor delay '0:0:10'",
|
||||
@@ -683,6 +684,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.TSQLExecutionEngine
|
||||
|
||||
CloseConnection(connection2);
|
||||
CloseConnection(connection3);
|
||||
await SqlTestDb.DropDatabase(connection2.Database);
|
||||
await SqlTestDb.DropDatabase(connection3.Database);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -36,13 +36,16 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public static TestConnectionResult InitLiveConnectionInfo(string databaseName = null, string fileName = null)
|
||||
public static TestConnectionResult InitLiveConnectionInfo(string databaseName = null, string ownerUri = null)
|
||||
{
|
||||
string sqlFilePath = GetTestSqlFile();
|
||||
ScriptFile scriptFile = TestServiceProvider.Instance.WorkspaceService.Workspace.GetFile(sqlFilePath);
|
||||
ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName);
|
||||
|
||||
string ownerUri = scriptFile.ClientFilePath;
|
||||
ScriptFile scriptFile = null;
|
||||
ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName);
|
||||
if (string.IsNullOrEmpty(ownerUri))
|
||||
{
|
||||
ownerUri = GetTestSqlFile();
|
||||
scriptFile = TestServiceProvider.Instance.WorkspaceService.Workspace.GetFile(ownerUri);
|
||||
ownerUri = scriptFile.ClientFilePath;
|
||||
}
|
||||
var connectionService = GetLiveTestConnectionService();
|
||||
var connectionResult =
|
||||
connectionService
|
||||
@@ -59,13 +62,14 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility
|
||||
return new TestConnectionResult() { ConnectionInfo = connInfo, ScriptFile = scriptFile };
|
||||
}
|
||||
|
||||
public static async Task<TestConnectionResult> InitLiveConnectionInfoAsync(string databaseName = null, string ownerUri = null)
|
||||
{
|
||||
public static async Task<TestConnectionResult> InitLiveConnectionInfoAsync(string databaseName = null, string ownerUri = null,
|
||||
string connectionType = ServiceLayer.Connection.ConnectionType.Default)
|
||||
{
|
||||
ScriptFile scriptFile = null;
|
||||
if (string.IsNullOrEmpty(ownerUri))
|
||||
{
|
||||
string sqlFilePath = GetTestSqlFile();
|
||||
scriptFile = TestServiceProvider.Instance.WorkspaceService.Workspace.GetFile(sqlFilePath);
|
||||
ownerUri = GetTestSqlFile();
|
||||
scriptFile = TestServiceProvider.Instance.WorkspaceService.Workspace.GetFile(ownerUri);
|
||||
ownerUri = scriptFile.ClientFilePath;
|
||||
}
|
||||
ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName);
|
||||
@@ -76,7 +80,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility
|
||||
.Connect(new ConnectParams
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = connectParams.Connection
|
||||
Connection = connectParams.Connection,
|
||||
Type = connectionType
|
||||
});
|
||||
if (!string.IsNullOrEmpty(connectionResult.ErrorMessage))
|
||||
{
|
||||
@@ -90,25 +95,27 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility
|
||||
|
||||
public static ConnectionInfo InitLiveConnectionInfoForDefinition(string databaseName = null)
|
||||
{
|
||||
ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName);
|
||||
const string ScriptUriTemplate = "file://some/{0}.sql";
|
||||
string ownerUri = string.Format(CultureInfo.InvariantCulture, ScriptUriTemplate, string.IsNullOrEmpty(databaseName) ? "file" : databaseName);
|
||||
var connectionService = GetLiveTestConnectionService();
|
||||
var connectionResult =
|
||||
connectionService
|
||||
.Connect(new ConnectParams
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = connectParams.Connection
|
||||
});
|
||||
|
||||
connectionResult.Wait();
|
||||
|
||||
ConnectionInfo connInfo = null;
|
||||
connectionService.TryFindConnection(ownerUri, out connInfo);
|
||||
|
||||
Assert.NotNull(connInfo);
|
||||
return connInfo;
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName);
|
||||
string ownerUri = queryTempFile.FilePath;
|
||||
var connectionService = GetLiveTestConnectionService();
|
||||
var connectionResult =
|
||||
connectionService
|
||||
.Connect(new ConnectParams
|
||||
{
|
||||
OwnerUri = ownerUri,
|
||||
Connection = connectParams.Connection
|
||||
});
|
||||
|
||||
connectionResult.Wait();
|
||||
|
||||
ConnectionInfo connInfo = null;
|
||||
connectionService.TryFindConnection(ownerUri, out connInfo);
|
||||
|
||||
Assert.NotNull(connInfo);
|
||||
return connInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static ServerConnection InitLiveServerConnectionForDefinition(ConnectionInfo connInfo)
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Xunit;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
@@ -132,11 +133,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
if (!DoNotCleanupDb)
|
||||
{
|
||||
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));
|
||||
await TestServiceProvider.Instance.RunQueryAsync(ServerType, MasterDatabaseName, dropDatabaseQuery);
|
||||
await DropDatabase(DatabaseName, ServerType);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -145,6 +142,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task DropDatabase(string databaseName, TestServerType serverType = TestServerType.OnPrem)
|
||||
{
|
||||
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));
|
||||
await TestServiceProvider.Instance.RunQueryAsync(serverType, MasterDatabaseName, dropDatabaseQuery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns connection info after making a connection to the database
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 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.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
|
||||
{
|
||||
public class DatabaseLocksManagerTests
|
||||
{
|
||||
private string server1 = "server1";
|
||||
private string database1 = "database1";
|
||||
|
||||
[Fact]
|
||||
public void GainFullAccessShouldDisconnectTheConnections()
|
||||
{
|
||||
var connectionLock = new Mock<IConnectedBindingQueue>();
|
||||
connectionLock.Setup(x => x.CloseConnections(server1, database1));
|
||||
|
||||
using (DatabaseLocksManager databaseLocksManager = CreateManager())
|
||||
{
|
||||
databaseLocksManager.ConnectionService.RegisterConnectedQueue("test", connectionLock.Object);
|
||||
|
||||
databaseLocksManager.GainFullAccessToDatabase(server1, database1);
|
||||
connectionLock.Verify(x => x.CloseConnections(server1, database1));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReleaseAccessShouldConnectTheConnections()
|
||||
{
|
||||
var connectionLock = new Mock<IConnectedBindingQueue>();
|
||||
connectionLock.Setup(x => x.OpenConnections(server1, database1));
|
||||
|
||||
using (DatabaseLocksManager databaseLocksManager = CreateManager())
|
||||
{
|
||||
databaseLocksManager.ConnectionService.RegisterConnectedQueue("test", connectionLock.Object);
|
||||
|
||||
databaseLocksManager.ReleaseAccess(server1, database1);
|
||||
connectionLock.Verify(x => x.OpenConnections(server1, database1));
|
||||
}
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
public void SecondProcessToGainAccessShouldWaitForTheFirstProcess()
|
||||
{
|
||||
var connectionLock = new Mock<IConnectedBindingQueue>();
|
||||
|
||||
using (DatabaseLocksManager databaseLocksManager = CreateManager())
|
||||
{
|
||||
databaseLocksManager.GainFullAccessToDatabase(server1, database1);
|
||||
bool secondTimeGettingAccessFails = false;
|
||||
try
|
||||
{
|
||||
databaseLocksManager.GainFullAccessToDatabase(server1, database1);
|
||||
}
|
||||
catch (DatabaseFullAccessException)
|
||||
{
|
||||
secondTimeGettingAccessFails = true;
|
||||
}
|
||||
Assert.Equal(secondTimeGettingAccessFails, true);
|
||||
databaseLocksManager.ReleaseAccess(server1, database1);
|
||||
Assert.Equal(databaseLocksManager.GainFullAccessToDatabase(server1, database1), true);
|
||||
databaseLocksManager.ReleaseAccess(server1, database1);
|
||||
}
|
||||
}
|
||||
|
||||
private DatabaseLocksManager CreateManager()
|
||||
{
|
||||
DatabaseLocksManager databaseLocksManager = new DatabaseLocksManager(2000);
|
||||
var connectionLock1 = new Mock<IConnectedBindingQueue>();
|
||||
var connectionLock2 = new Mock<IConnectedBindingQueue>();
|
||||
connectionLock1.Setup(x => x.CloseConnections(It.IsAny<string>(), It.IsAny<string>()));
|
||||
connectionLock2.Setup(x => x.OpenConnections(It.IsAny<string>(), It.IsAny<string>()));
|
||||
connectionLock1.Setup(x => x.OpenConnections(It.IsAny<string>(), It.IsAny<string>()));
|
||||
connectionLock2.Setup(x => x.CloseConnections(It.IsAny<string>(), It.IsAny<string>()));
|
||||
ConnectionService connectionService = new ConnectionService();
|
||||
|
||||
databaseLocksManager.ConnectionService = connectionService;
|
||||
|
||||
connectionService.RegisterConnectedQueue("1", connectionLock1.Object);
|
||||
connectionService.RegisterConnectedQueue("2", connectionLock2.Object);
|
||||
return databaseLocksManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts;
|
||||
@@ -20,6 +18,7 @@ using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
{
|
||||
@@ -33,10 +32,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
private ConnectionDetails defaultConnectionDetails;
|
||||
private ConnectionCompleteParams defaultConnParams;
|
||||
private string fakeConnectionString = "Data Source=server;Initial Catalog=database;Integrated Security=False;User Id=user";
|
||||
private ServerConnection serverConnection = null;
|
||||
|
||||
public NodeTests()
|
||||
{
|
||||
defaultServerInfo = TestObjects.GetTestServerInfo();
|
||||
serverConnection = new ServerConnection(new SqlConnection(fakeConnectionString));
|
||||
|
||||
defaultConnectionDetails = new ConnectionDetails()
|
||||
{
|
||||
@@ -59,15 +60,15 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
[Fact]
|
||||
public void ServerNodeConstructorValidatesFields()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new ServerNode(null, ServiceProvider));
|
||||
Assert.Throws<ArgumentNullException>(() => new ServerNode(defaultConnParams, null));
|
||||
Assert.Throws<ArgumentNullException>(() => new ServerNode(null, ServiceProvider, serverConnection));
|
||||
Assert.Throws<ArgumentNullException>(() => new ServerNode(defaultConnParams, null, serverConnection));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ServerNodeConstructorShouldSetValuesCorrectly()
|
||||
{
|
||||
// Given a server node with valid inputs
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider);
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider, serverConnection);
|
||||
// Then expect all fields set correctly
|
||||
Assert.False(node.IsAlwaysLeaf, "Server node should never be a leaf");
|
||||
Assert.Equal(defaultConnectionDetails.ServerName, node.NodeValue);
|
||||
@@ -99,7 +100,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
OwnerUri = defaultOwnerUri
|
||||
};
|
||||
// When querying label
|
||||
string label = new ServerNode(connParams, ServiceProvider).Label;
|
||||
string label = new ServerNode(connParams, ServiceProvider, serverConnection).Label;
|
||||
// Then only server name and version shown
|
||||
string expectedLabel = defaultConnectionDetails.ServerName + " (SQL Server " + defaultServerInfo.ServerVersion + ")";
|
||||
Assert.Equal(expectedLabel, label);
|
||||
@@ -111,7 +112,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
defaultServerInfo.IsCloud = true;
|
||||
|
||||
// Given a server node for a cloud DB, with master name
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider);
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider, serverConnection);
|
||||
// Then expect label to not include db name
|
||||
string expectedLabel = defaultConnectionDetails.ServerName + " (SQL Server " + defaultServerInfo.ServerVersion + " - "
|
||||
+ defaultConnectionDetails.UserName + ")";
|
||||
@@ -120,7 +121,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
// But given a server node for a cloud DB that's not master
|
||||
defaultConnectionDetails.DatabaseName = "NotMaster";
|
||||
defaultConnParams.ConnectionSummary.DatabaseName = defaultConnectionDetails.DatabaseName;
|
||||
node = new ServerNode(defaultConnParams, ServiceProvider);
|
||||
node = new ServerNode(defaultConnParams, ServiceProvider, serverConnection);
|
||||
|
||||
// Then expect label to include db name
|
||||
expectedLabel = defaultConnectionDetails.ServerName + " (SQL Server " + defaultServerInfo.ServerVersion + " - "
|
||||
@@ -132,7 +133,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void ToNodeInfoIncludeAllFields()
|
||||
{
|
||||
// Given a server connection
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider);
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider, serverConnection);
|
||||
// When converting to NodeInfo
|
||||
NodeInfo info = node.ToNodeInfo();
|
||||
// Then all fields should match
|
||||
@@ -204,7 +205,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void ServerNodeContextShouldIncludeServer()
|
||||
{
|
||||
// given a successful Server creation
|
||||
SetupAndRegisterTestConnectionService();
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
ServerNode node = SetupServerNodeWithServer(smoServer);
|
||||
|
||||
@@ -223,10 +223,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void ServerNodeContextShouldSetErrorMessageIfSqlConnectionIsNull()
|
||||
{
|
||||
// given a connectionInfo with no SqlConnection to use for queries
|
||||
ConnectionService connService = SetupAndRegisterTestConnectionService();
|
||||
connService.OwnerToConnectionMap.Remove(defaultOwnerUri);
|
||||
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
Server smoServer = null;
|
||||
ServerNode node = SetupServerNodeWithServer(smoServer);
|
||||
|
||||
// When I get the context for a ServerNode
|
||||
@@ -234,17 +232,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
|
||||
// Then I expect it to be in an error state
|
||||
Assert.Null(context);
|
||||
Assert.Equal(
|
||||
string.Format(CultureInfo.CurrentCulture, SR.ServerNodeConnectionError, defaultConnectionDetails.ServerName),
|
||||
node.ErrorStateMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ServerNodeContextShouldSetErrorMessageIfConnFailureExceptionThrown()
|
||||
{
|
||||
// given a connectionInfo with no SqlConnection to use for queries
|
||||
SetupAndRegisterTestConnectionService();
|
||||
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
string expectedMsg = "ConnFailed!";
|
||||
ServerNode node = SetupServerNodeWithExceptionCreator(new ConnectionFailureException(expectedMsg));
|
||||
@@ -263,8 +256,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void ServerNodeContextShouldSetErrorMessageIfExceptionThrown()
|
||||
{
|
||||
// given a connectionInfo with no SqlConnection to use for queries
|
||||
SetupAndRegisterTestConnectionService();
|
||||
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
string expectedMsg = "Failed!";
|
||||
ServerNode node = SetupServerNodeWithExceptionCreator(new Exception(expectedMsg));
|
||||
@@ -283,7 +274,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void QueryContextShouldNotCallOpenOnAlreadyOpenConnection()
|
||||
{
|
||||
// given a server connection that will state its connection is open
|
||||
SetupAndRegisterTestConnectionService();
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
Mock<SmoWrapper> wrapper = SetupSmoWrapperForIsOpenTest(smoServer, isOpen: true);
|
||||
|
||||
@@ -301,7 +291,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
{
|
||||
Mock<SmoWrapper> wrapper = new Mock<SmoWrapper>();
|
||||
int count = 0;
|
||||
wrapper.Setup(c => c.CreateServer(It.IsAny<SqlConnection>()))
|
||||
wrapper.Setup(c => c.CreateServer(It.IsAny<ServerConnection>()))
|
||||
.Returns(() => smoServer);
|
||||
wrapper.Setup(c => c.IsConnectionOpen(It.IsAny<Server>()))
|
||||
.Returns(() => isOpen);
|
||||
@@ -315,7 +305,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void QueryContextShouldReopenClosedConnectionWhenGettingServer()
|
||||
{
|
||||
// given a server connection that will state its connection is closed
|
||||
SetupAndRegisterTestConnectionService();
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
Mock<SmoWrapper> wrapper = SetupSmoWrapperForIsOpenTest(smoServer, isOpen: false);
|
||||
|
||||
@@ -333,7 +322,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
public void QueryContextShouldReopenClosedConnectionWhenGettingParent()
|
||||
{
|
||||
// given a server connection that will state its connection is closed
|
||||
SetupAndRegisterTestConnectionService();
|
||||
Server smoServer = new Server(new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
Mock<SmoWrapper> wrapper = SetupSmoWrapperForIsOpenTest(smoServer, isOpen: false);
|
||||
|
||||
@@ -362,7 +350,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
private ServerNode SetupServerNodeWithServer(Server smoServer)
|
||||
{
|
||||
Mock<SmoWrapper> creator = new Mock<SmoWrapper>();
|
||||
creator.Setup(c => c.CreateServer(It.IsAny<SqlConnection>()))
|
||||
creator.Setup(c => c.CreateServer(It.IsAny<ServerConnection>()))
|
||||
.Returns(() => smoServer);
|
||||
creator.Setup(c => c.IsConnectionOpen(It.IsAny<Server>()))
|
||||
.Returns(() => true);
|
||||
@@ -373,7 +361,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
private ServerNode SetupServerNodeWithExceptionCreator(Exception ex)
|
||||
{
|
||||
Mock<SmoWrapper> creator = new Mock<SmoWrapper>();
|
||||
creator.Setup(c => c.CreateServer(It.IsAny<SqlConnection>()))
|
||||
creator.Setup(c => c.CreateServer(It.IsAny<ServerConnection>()))
|
||||
.Throws(ex);
|
||||
creator.Setup(c => c.IsConnectionOpen(It.IsAny<Server>()))
|
||||
.Returns(() => false);
|
||||
@@ -384,7 +372,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
|
||||
private ServerNode SetupServerNodeWithCreator(SmoWrapper creator)
|
||||
{
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider);
|
||||
ServerNode node = new ServerNode(defaultConnParams, ServiceProvider, new ServerConnection(new SqlConnection(fakeConnectionString)));
|
||||
node.SmoWrapper = creator;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||
using Microsoft.SqlServer.Management.Common;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
{
|
||||
@@ -25,12 +27,29 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
private ObjectExplorerService service;
|
||||
private Mock<ConnectionService> connectionServiceMock;
|
||||
private Mock<IProtocolEndpoint> serviceHostMock;
|
||||
string fakeConnectionString = "Data Source=server;Initial Catalog=database;Integrated Security=False;User Id=user";
|
||||
private static ConnectionDetails details = new ConnectionDetails()
|
||||
{
|
||||
UserName = "user",
|
||||
Password = "password",
|
||||
DatabaseName = "msdb",
|
||||
ServerName = "serverName"
|
||||
};
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo(null, null, details);
|
||||
|
||||
ConnectedBindingQueue connectedBindingQueue;
|
||||
public ObjectExplorerServiceTests()
|
||||
{
|
||||
connectionServiceMock = new Mock<ConnectionService>();
|
||||
serviceHostMock = new Mock<IProtocolEndpoint>();
|
||||
service = CreateOEService(connectionServiceMock.Object);
|
||||
connectionServiceMock.Setup(x => x.RegisterConnectedQueue(It.IsAny<string>(), It.IsAny<IConnectedBindingQueue>()));
|
||||
service.InitializeService(serviceHostMock.Object);
|
||||
ConnectedBindingContext connectedBindingContext = new ConnectedBindingContext();
|
||||
connectedBindingContext.ServerConnection = new ServerConnection(new SqlConnection(fakeConnectionString));
|
||||
connectedBindingQueue = new ConnectedBindingQueue(false);
|
||||
connectedBindingQueue.BindingContextMap.Add($"{details.ServerName}_{details.DatabaseName}_{details.UserName}_NULL", connectedBindingContext);
|
||||
service.ConnectedBindingQueue = connectedBindingQueue;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -210,14 +229,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
|
||||
private async Task<SessionCreatedParameters> CreateSession()
|
||||
{
|
||||
ConnectionDetails details = new ConnectionDetails()
|
||||
{
|
||||
UserName = "user",
|
||||
Password = "password",
|
||||
DatabaseName = "msdb",
|
||||
ServerName = "serverName"
|
||||
};
|
||||
|
||||
SessionCreatedParameters sessionResult = null;
|
||||
serviceHostMock.AddEventHandling(CreateSessionCompleteNotification.Type, (et, p) => sessionResult = p);
|
||||
CreateSessionResponse result = default(CreateSessionResponse);
|
||||
@@ -226,8 +237,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
connectionServiceMock.Setup(c => c.Connect(It.IsAny<ConnectParams>()))
|
||||
.Returns((ConnectParams connectParams) => Task.FromResult(GetCompleteParamsForConnection(connectParams.OwnerUri, details)));
|
||||
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo(null, null, null);
|
||||
string fakeConnectionString = "Data Source=server;Initial Catalog=database;Integrated Security=False;User Id=user";
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo(null, null, details);
|
||||
connectionInfo.AddConnection("Default", new SqlConnection(fakeConnectionString));
|
||||
connectionServiceMock.Setup((c => c.TryFindConnection(It.IsAny<string>(), out connectionInfo))).
|
||||
OutCallback((string t, out ConnectionInfo v) => v = connectionInfo)
|
||||
@@ -343,7 +353,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer
|
||||
|
||||
connectionServiceMock.Setup(c => c.Connect(It.IsAny<ConnectParams>()))
|
||||
.Returns((ConnectParams connectParams) => Task.FromResult(GetCompleteParamsForConnection(connectParams.OwnerUri, details)));
|
||||
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo(null, null, details);
|
||||
connectionInfo.AddConnection("Default", new SqlConnection(fakeConnectionString));
|
||||
connectionServiceMock.Setup((c => c.TryFindConnection(It.IsAny<string>(), out connectionInfo))).
|
||||
OutCallback((string t, out ConnectionInfo v) => v = connectionInfo)
|
||||
.Returns(true);
|
||||
|
||||
// when creating a new session
|
||||
// then expect the create session request to return false
|
||||
await RunAndVerify<CreateSessionResponse, SessionCreatedParameters>(
|
||||
|
||||
Reference in New Issue
Block a user