Move unused forked code to external directory (#1192)

* Move unused forked code to external directory

* Fix SLN build errors

* Add back resource provider core since it's referenced by main resource provider project

* Update PackageProjects step of pipeline
This commit is contained in:
Karl Burtram
2021-04-16 15:33:35 -07:00
committed by GitHub
parent dc6555a823
commit ccf95aed77
229 changed files with 10058 additions and 10124 deletions

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.DataProtocol.Contracts.Connection;
namespace Microsoft.SqlTools.CoreServices.Connection
{
/// <summary>
/// Used to uniquely identify a CancellationTokenSource associated with both
/// a string URI and a string connection type.
/// </summary>
public class CancelTokenKey : CancelConnectParams, IEquatable<CancelTokenKey>
{
public override bool Equals(object obj)
{
CancelTokenKey other = obj as CancelTokenKey;
if (other == null)
{
return false;
}
return other.OwnerUri == OwnerUri && other.Type == Type;
}
public bool Equals(CancelTokenKey obj)
{
return obj.OwnerUri == OwnerUri && obj.Type == Type;
}
public override int GetHashCode()
{
return OwnerUri.GetHashCode() ^ Type.GetHashCode();
}
}
}

View File

@@ -0,0 +1,50 @@
//
// 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.DataProtocol.Contracts.Connection;
namespace Microsoft.SqlTools.CoreServices.Connection
{
/// <summary>
/// Extension methods to ConnectParams
/// </summary>
public static class ConnectParamsExtensions
{
/// <summary>
/// Check that the fields in ConnectParams are all valid
/// </summary>
public static bool IsValid(this ConnectParams parameters, out string errorMessage)
{
errorMessage = string.Empty;
if (string.IsNullOrEmpty(parameters.OwnerUri))
{
errorMessage = SR.ConnectionParamsValidateNullOwnerUri;
}
else if (parameters.Connection == null)
{
errorMessage = SR.ConnectionParamsValidateNullConnection;
}
else if (!string.IsNullOrEmpty(parameters.Connection.ConnectionString))
{
// Do not check other connection parameters if a connection string is present
return string.IsNullOrEmpty(errorMessage);
}
else if (string.IsNullOrEmpty(parameters.Connection.ServerName))
{
errorMessage = SR.ConnectionParamsValidateNullServerName;
}
else if (string.IsNullOrEmpty(parameters.Connection.AuthenticationType) || parameters.Connection.AuthenticationType == "SqlLogin")
{
// For SqlLogin, username cannot be empty
if (string.IsNullOrEmpty(parameters.Connection.UserName))
{
errorMessage = SR.ConnectionParamsValidateNullSqlAuth("UserName");
}
}
return string.IsNullOrEmpty(errorMessage);
}
}
}

View File

@@ -0,0 +1,171 @@
//
// 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.Collections.Generic;
using System.Data.Common;
using Microsoft.SqlTools.DataProtocol.Contracts.Connection;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection
{
/// <summary>
/// Information pertaining to a unique connection instance.
/// </summary>
public class ConnectionInfo
{
/// <summary>
/// Constructor
/// </summary>
public ConnectionInfo(ISqlConnectionFactory factory, string ownerUri, ConnectionDetails details)
{
Factory = factory;
OwnerUri = ownerUri;
ConnectionDetails = details;
ConnectionId = Guid.NewGuid();
IntellisenseMetrics = new InteractionMetrics<double>(new int[] {50, 100, 200, 500, 1000, 2000});
}
/// <summary>
/// Unique Id, helpful to identify a connection info object
/// </summary>
public Guid ConnectionId { get; private set; }
/// <summary>
/// URI identifying the owner/user of the connection. Could be a file, service, resource, etc.
/// </summary>
public string OwnerUri { get; private set; }
/// <summary>
/// Factory used for creating the SQL connection associated with the connection info.
/// </summary>
public ISqlConnectionFactory Factory { get; private set; }
/// <summary>
/// Properties used for creating/opening the SQL connection.
/// </summary>
public ConnectionDetails ConnectionDetails { get; private set; }
/// <summary>
/// A map containing all connections to the database that are associated with
/// this ConnectionInfo's OwnerUri.
/// This is internal for testing access only
/// </summary>
internal readonly ConcurrentDictionary<string, DbConnection> ConnectionTypeToConnectionMap =
new ConcurrentDictionary<string, DbConnection>();
/// <summary>
/// Intellisense Metrics
/// </summary>
public InteractionMetrics<double> IntellisenseMetrics { get; private set; }
/// <summary>
/// Returns true if the db connection is to any cloud instance
/// </summary>
public bool IsCloud { get; set; }
/// <summary>
/// Returns true if the db connection is to a SQL db instance
/// </summary>
public bool IsSqlDb { get; set; }
/// Returns true if the sql connection is to a DW instance
/// </summary>
public bool IsSqlDW { get; set; }
/// <summary>
/// Returns the major version number of the db we are connected to
/// </summary>
public int MajorVersion { get; set; }
/// <summary>
/// All DbConnection instances held by this ConnectionInfo
/// </summary>
public ICollection<DbConnection> AllConnections
{
get
{
return ConnectionTypeToConnectionMap.Values;
}
}
/// <summary>
/// All connection type strings held by this ConnectionInfo
/// </summary>
/// <returns></returns>
public ICollection<string> AllConnectionTypes
{
get
{
return ConnectionTypeToConnectionMap.Keys;
}
}
public bool HasConnectionType(string connectionType)
{
connectionType = connectionType ?? ConnectionType.Default;
return ConnectionTypeToConnectionMap.ContainsKey(connectionType);
}
/// <summary>
/// The count of DbConnectioninstances held by this ConnectionInfo
/// </summary>
public int CountConnections
{
get
{
return ConnectionTypeToConnectionMap.Count;
}
}
/// <summary>
/// Try to get the DbConnection associated with the given connection type string.
/// </summary>
/// <returns>true if a connection with type connectionType was located and out connection was set,
/// false otherwise </returns>
/// <exception cref="ArgumentException">Thrown when connectionType is null or empty</exception>
public bool TryGetConnection(string connectionType, out DbConnection connection)
{
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
return ConnectionTypeToConnectionMap.TryGetValue(connectionType, out connection);
}
/// <summary>
/// Adds a DbConnection to this object and associates it with the given
/// connection type string. If a connection already exists with an identical
/// connection type string, it is not overwritten. Ignores calls where connectionType = null
/// </summary>
/// <exception cref="ArgumentException">Thrown when connectionType is null or empty</exception>
public void AddConnection(string connectionType, DbConnection connection)
{
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
ConnectionTypeToConnectionMap.TryAdd(connectionType, connection);
}
/// <summary>
/// Removes the single DbConnection instance associated with string connectionType
/// </summary>
/// <exception cref="ArgumentException">Thrown when connectionType is null or empty</exception>
public void RemoveConnection(string connectionType)
{
Validate.IsNotNullOrEmptyString("Connection Type", connectionType);
DbConnection connection;
ConnectionTypeToConnectionMap.TryRemove(connectionType, out connection);
}
/// <summary>
/// Removes all DbConnection instances held by this object
/// </summary>
public void RemoveAllConnections()
{
foreach (var type in AllConnectionTypes)
{
DbConnection connection;
ConnectionTypeToConnectionMap.TryRemove(type, out connection);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Data.Common;
using Microsoft.Data.SqlClient;
using Microsoft.SqlTools.CoreServices.Connection.ReliableConnection;
public static class ConnectionUtils
{
public static SqlConnection GetAsSqlConnection(DbConnection connection)
{
SqlConnection sqlConn = connection as SqlConnection;
if (sqlConn == null)
{
// It's not actually a SqlConnection, so let's try a reliable SQL connection
ReliableSqlConnection reliableConn = connection as ReliableSqlConnection;
if (reliableConn == null)
{
// If we don't have connection we can use with SMO, just give up on using SMO
return null;
}
// We have a reliable connection, use the underlying connection
sqlConn = reliableConn.GetUnderlyingConnection();
}
return sqlConn;
}
}

View File

@@ -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.CoreServices.Connection
{
public class DatabaseFullAccessException: Exception
{
public DatabaseFullAccessException()
: base()
{
}
public DatabaseFullAccessException(string message, Exception exception)
: base(message, exception)
{
}
public DatabaseFullAccessException(string message)
: base(message)
{
}
}
}

View File

@@ -0,0 +1,114 @@
//
// 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.CoreServices.LanguageServices;
using System;
using System.Collections.Generic;
using System.Threading;
namespace Microsoft.SqlTools.CoreServices.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 ConnectionServiceCore ConnectionService { get; set; }
private Dictionary<string, ManualResetEvent> databaseAccessEvents = new Dictionary<string, ManualResetEvent>();
private object databaseAccessLock = new object();
public const int DefaultWaitToGetFullAccess = 10000;
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)
{
/*
* TODO: add the lock so not two process can get full access at the same time
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, DefaultWaitToGetFullAccess);
}
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, DefaultWaitToGetFullAccess);
}
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();
}
}
}
}

View File

@@ -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.CoreServices.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; }
}
}

View File

@@ -0,0 +1,20 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Data.Common;
namespace Microsoft.SqlTools.CoreServices.Connection
{
/// <summary>
/// Interface for the SQL Connection factory
/// </summary>
public interface ISqlConnectionFactory
{
/// <summary>
/// Create a new SQL Connection object
/// </summary>
DbConnection CreateSqlConnection(string connectionString);
}
}

View File

@@ -0,0 +1,453 @@
//
// 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.Generic;
using System.Diagnostics;
using System.Reflection;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// This class represents connection (and other) settings specified by called of the DacFx API. DacFx
/// cannot rely on the registry to supply override values therefore setting overrides must be made
/// by the top-of-the-stack
/// </summary>
internal sealed class AmbientSettings
{
private const string LogicalContextName = "__LocalContextConfigurationName";
internal enum StreamBackingStore
{
// MemoryStream
Memory = 0,
// FileStream
File = 1
}
// Internal for test purposes
internal const string MasterReferenceFilePathIndex = "MasterReferenceFilePath";
internal const string DatabaseLockTimeoutIndex = "DatabaseLockTimeout";
internal const string QueryTimeoutIndex = "QueryTimeout";
internal const string LongRunningQueryTimeoutIndex = "LongRunningQueryTimeout";
internal const string AlwaysRetryOnTransientFailureIndex = "AlwaysRetryOnTransientFailure";
internal const string MaxDataReaderDegreeOfParallelismIndex = "MaxDataReaderDegreeOfParallelism";
internal const string ConnectionRetryHandlerIndex = "ConnectionRetryHandler";
internal const string TraceRowCountFailureIndex = "TraceRowCountFailure";
internal const string TableProgressUpdateIntervalIndex = "TableProgressUpdateInterval";
internal const string UseOfflineDataReaderIndex = "UseOfflineDataReader";
internal const string StreamBackingStoreForOfflineDataReadingIndex = "StreamBackingStoreForOfflineDataReading";
internal const string DisableIndexesForDataPhaseIndex = "DisableIndexesForDataPhase";
internal const string ReliableDdlEnabledIndex = "ReliableDdlEnabled";
internal const string ImportModelDatabaseIndex = "ImportModelDatabase";
internal const string SupportAlwaysEncryptedIndex = "SupportAlwaysEncrypted";
internal const string SkipObjectTypeBlockingIndex = "SkipObjectTypeBlocking";
internal const string DoNotSerializeQueryStoreSettingsIndex = "DoNotSerializeQueryStoreSettings";
internal const string AlwaysEncryptedWizardMigrationIndex = "AlwaysEncryptedWizardMigration";
internal static AmbientData _defaultSettings;
static AmbientSettings()
{
_defaultSettings = new AmbientData();
}
/// <summary>
/// Access to the default ambient settings. Access to these settings is made available
/// for SSDT scenarios where settings are read from the registry and not set explicitly through
/// the API
/// </summary>
public static AmbientData DefaultSettings
{
get { return _defaultSettings; }
}
public static string MasterReferenceFilePath
{
get { return GetValue<string>(MasterReferenceFilePathIndex); }
}
public static int LockTimeoutMilliSeconds
{
get { return GetValue<int>(DatabaseLockTimeoutIndex); }
}
public static int QueryTimeoutSeconds
{
get { return GetValue<int>(QueryTimeoutIndex); }
}
public static int LongRunningQueryTimeoutSeconds
{
get { return GetValue<int>(LongRunningQueryTimeoutIndex); }
}
public static Action<SqlServerRetryError> ConnectionRetryMessageHandler
{
get { return GetValue<Action<SqlServerRetryError>>(ConnectionRetryHandlerIndex); }
}
public static bool AlwaysRetryOnTransientFailure
{
get { return GetValue<bool>(AlwaysRetryOnTransientFailureIndex); }
}
public static int MaxDataReaderDegreeOfParallelism
{
get { return GetValue<int>(MaxDataReaderDegreeOfParallelismIndex); }
}
public static int TableProgressUpdateInterval
{
// value of zero means do not fire 'heartbeat' progress events. Non-zero values will
// fire a heartbeat progress event every n seconds.
get { return GetValue<int>(TableProgressUpdateIntervalIndex); }
}
public static bool TraceRowCountFailure
{
get { return GetValue<bool>(TraceRowCountFailureIndex); }
}
public static bool UseOfflineDataReader
{
get { return GetValue<bool>(UseOfflineDataReaderIndex); }
}
public static StreamBackingStore StreamBackingStoreForOfflineDataReading
{
get { return GetValue<StreamBackingStore>(StreamBackingStoreForOfflineDataReadingIndex); }
}
public static bool DisableIndexesForDataPhase
{
get { return GetValue<bool>(DisableIndexesForDataPhaseIndex); }
}
public static bool ReliableDdlEnabled
{
get { return GetValue<bool>(ReliableDdlEnabledIndex); }
}
public static bool ImportModelDatabase
{
get { return GetValue<bool>(ImportModelDatabaseIndex); }
}
/// <summary>
/// Setting that shows whether Always Encrypted is supported.
/// If false, then reverse engineering and script interpretation of a database with any Always Encrypted object will fail
/// </summary>
public static bool SupportAlwaysEncrypted
{
get { return GetValue<bool>(SupportAlwaysEncryptedIndex); }
}
public static bool AlwaysEncryptedWizardMigration
{
get { return GetValue<bool>(AlwaysEncryptedWizardMigrationIndex); }
}
/// <summary>
/// Setting that determines whether checks for unsupported object types are performed.
/// If false, unsupported object types will prevent extract from being performed.
/// Default value is false.
/// </summary>
public static bool SkipObjectTypeBlocking
{
get { return GetValue<bool>(SkipObjectTypeBlockingIndex); }
}
/// <summary>
/// Setting that determines whether the Database Options that store Query Store settings will be left out during package serialization.
/// Default value is false.
/// </summary>
public static bool DoNotSerializeQueryStoreSettings
{
get { return GetValue<bool>(DoNotSerializeQueryStoreSettingsIndex); }
}
/// <summary>
/// Called by top-of-stack API to setup/configure settings that should be used
/// throughout the API (lower in the stack). The settings are reverted once the returned context
/// has been disposed.
/// </summary>
public static IStackSettingsContext CreateSettingsContext()
{
return new StackConfiguration();
}
private static T1 GetValue<T1>(string configIndex)
{
IAmbientDataDirectAccess config = _defaultSettings;
return (T1)config.Data[configIndex].Value;
}
/// <summary>
/// Data-transfer object that represents a specific configuration
/// </summary>
public class AmbientData : IAmbientDataDirectAccess
{
private readonly Dictionary<string, AmbientValue> _configuration;
public AmbientData()
{
_configuration = new Dictionary<string, AmbientValue>(StringComparer.OrdinalIgnoreCase);
_configuration[DatabaseLockTimeoutIndex] = new AmbientValue(5000);
_configuration[QueryTimeoutIndex] = new AmbientValue(60);
_configuration[LongRunningQueryTimeoutIndex] = new AmbientValue(0);
_configuration[AlwaysRetryOnTransientFailureIndex] = new AmbientValue(false);
_configuration[ConnectionRetryHandlerIndex] = new AmbientValue(typeof(Action<SqlServerRetryError>), null);
_configuration[MaxDataReaderDegreeOfParallelismIndex] = new AmbientValue(8);
_configuration[TraceRowCountFailureIndex] = new AmbientValue(false); // default: throw DacException on rowcount mismatch during import/export data validation
_configuration[TableProgressUpdateIntervalIndex] = new AmbientValue(300); // default: fire heartbeat progress update events every 5 minutes
_configuration[UseOfflineDataReaderIndex] = new AmbientValue(false);
_configuration[StreamBackingStoreForOfflineDataReadingIndex] = new AmbientValue(StreamBackingStore.File); //applicable only when UseOfflineDataReader is set to true
_configuration[MasterReferenceFilePathIndex] = new AmbientValue(typeof(string), null);
// Defect 1210884: Enable an option to allow secondary index, check and fk constraints to stay enabled during data upload with import in DACFX for IES
_configuration[DisableIndexesForDataPhaseIndex] = new AmbientValue(true);
_configuration[ReliableDdlEnabledIndex] = new AmbientValue(false);
_configuration[ImportModelDatabaseIndex] = new AmbientValue(true);
_configuration[SupportAlwaysEncryptedIndex] = new AmbientValue(false);
_configuration[AlwaysEncryptedWizardMigrationIndex] = new AmbientValue(false);
_configuration[SkipObjectTypeBlockingIndex] = new AmbientValue(false);
_configuration[DoNotSerializeQueryStoreSettingsIndex] = new AmbientValue(false);
}
public string MasterReferenceFilePath
{
get { return (string)_configuration[MasterReferenceFilePathIndex].Value; }
set { _configuration[MasterReferenceFilePathIndex].Value = value; }
}
public int LockTimeoutMilliSeconds
{
get { return (int)_configuration[DatabaseLockTimeoutIndex].Value; }
set { _configuration[DatabaseLockTimeoutIndex].Value = value; }
}
public int QueryTimeoutSeconds
{
get { return (int)_configuration[QueryTimeoutIndex].Value; }
set { _configuration[QueryTimeoutIndex].Value = value; }
}
public int LongRunningQueryTimeoutSeconds
{
get { return (int)_configuration[LongRunningQueryTimeoutIndex].Value; }
set { _configuration[LongRunningQueryTimeoutIndex].Value = value; }
}
public bool AlwaysRetryOnTransientFailure
{
get { return (bool)_configuration[AlwaysRetryOnTransientFailureIndex].Value; }
set { _configuration[AlwaysRetryOnTransientFailureIndex].Value = value; }
}
public Action<SqlServerRetryError> ConnectionRetryMessageHandler
{
get { return (Action<SqlServerRetryError>)_configuration[ConnectionRetryHandlerIndex].Value; }
set { _configuration[ConnectionRetryHandlerIndex].Value = value; }
}
public bool TraceRowCountFailure
{
get { return (bool)_configuration[TraceRowCountFailureIndex].Value; }
set { _configuration[TraceRowCountFailureIndex].Value = value; }
}
public int TableProgressUpdateInterval
{
get { return (int)_configuration[TableProgressUpdateIntervalIndex].Value; }
set { _configuration[TableProgressUpdateIntervalIndex].Value = value; }
}
public bool UseOfflineDataReader
{
get { return (bool)_configuration[UseOfflineDataReaderIndex].Value; }
set { _configuration[UseOfflineDataReaderIndex].Value = value; }
}
public StreamBackingStore StreamBackingStoreForOfflineDataReading
{
get { return (StreamBackingStore)_configuration[StreamBackingStoreForOfflineDataReadingIndex].Value; }
set { _configuration[StreamBackingStoreForOfflineDataReadingIndex].Value = value; }
}
public bool DisableIndexesForDataPhase
{
get { return (bool)_configuration[DisableIndexesForDataPhaseIndex].Value; }
set { _configuration[DisableIndexesForDataPhaseIndex].Value = value; }
}
public bool ReliableDdlEnabled
{
get { return (bool)_configuration[ReliableDdlEnabledIndex].Value; }
set { _configuration[ReliableDdlEnabledIndex].Value = value; }
}
public bool ImportModelDatabase
{
get { return (bool)_configuration[ImportModelDatabaseIndex].Value; }
set { _configuration[ImportModelDatabaseIndex].Value = value; }
}
internal bool SupportAlwaysEncrypted
{
get { return (bool)_configuration[SupportAlwaysEncryptedIndex].Value; }
set { _configuration[SupportAlwaysEncryptedIndex].Value = value; }
}
internal bool AlwaysEncryptedWizardMigration
{
get { return (bool)_configuration[AlwaysEncryptedWizardMigrationIndex].Value; }
set { _configuration[AlwaysEncryptedWizardMigrationIndex].Value = value; }
}
internal bool SkipObjectTypeBlocking
{
get { return (bool)_configuration[SkipObjectTypeBlockingIndex].Value; }
set { _configuration[SkipObjectTypeBlockingIndex].Value = value; }
}
internal bool DoNotSerializeQueryStoreSettings
{
get { return (bool)_configuration[DoNotSerializeQueryStoreSettingsIndex].Value; }
set { _configuration[DoNotSerializeQueryStoreSettingsIndex].Value = value; }
}
/// <summary>
/// Provides a way to bulk populate settings from a dictionary
/// </summary>
public void PopulateSettings(IDictionary<string, object> settingsCollection)
{
if (settingsCollection != null)
{
Dictionary<string, object> newSettings = new Dictionary<string, object>();
// We know all the values are set on the current configuration
foreach (KeyValuePair<string, object> potentialPair in settingsCollection)
{
AmbientValue currentValue;
if (_configuration.TryGetValue(potentialPair.Key, out currentValue))
{
object newValue = potentialPair.Value;
newSettings[potentialPair.Key] = newValue;
}
}
if (newSettings.Count > 0)
{
foreach (KeyValuePair<string, object> newSetting in newSettings)
{
_configuration[newSetting.Key].Value = newSetting.Value;
}
}
}
}
/// <summary>
/// Logs the Ambient Settings
/// </summary>
public void TraceSettings()
{
// NOTE: logging as warning so we can get this data in the IEService DacFx logs
Logger.Write(TraceEventType.Warning, Resources.LoggingAmbientSettings);
foreach (KeyValuePair<string, AmbientValue> setting in _configuration)
{
// Log Ambient Settings
Logger.Write(
TraceEventType.Warning,
string.Format(
Resources.AmbientSettingFormat,
setting.Key,
setting.Value == null ? setting.Value : setting.Value.Value));
}
}
Dictionary<string, AmbientValue> IAmbientDataDirectAccess.Data
{
get { return _configuration; }
}
}
/// <summary>
/// This class is used as value in the dictionary to ensure that the type of value is correct.
/// </summary>
private class AmbientValue
{
private readonly Type _type;
private readonly bool _isTypeNullable;
private object _value;
public AmbientValue(object value)
: this(value == null ? null : value.GetType(), value)
{
}
public AmbientValue(Type type, object value)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
_type = type;
_isTypeNullable = !type.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(type) != null;
Value = value;
}
public object Value
{
get { return _value; }
set
{
if ((_isTypeNullable && value == null) || _type.GetTypeInfo().IsInstanceOfType(value))
{
_value = value;
}
else
{
Logger.Write(TraceEventType.Error, string.Format(Resources.UnableToAssignValue, value.GetType().FullName, _type.FullName));
}
}
}
}
/// <summary>
/// This private interface allows pass-through access directly to member data
/// </summary>
private interface IAmbientDataDirectAccess
{
Dictionary<string, AmbientValue> Data { get; }
}
/// <summary>
/// This class encapsulated the concept of configuration that is set on the stack and
/// flows across multiple threads as part of the logical call context
/// </summary>
private sealed class StackConfiguration : IStackSettingsContext
{
private readonly AmbientData _data;
public StackConfiguration()
{
_data = new AmbientData();
//CallContext.LogicalSetData(LogicalContextName, _data);
}
public AmbientData Settings
{
get { return _data; }
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
//CallContext.LogicalSetData(LogicalContextName, null);
}
}
}
}

View File

@@ -0,0 +1,285 @@
//
// 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.Data;
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// This class caches server information for subsequent use
/// </summary>
internal class CachedServerInfo
{
/// <summary>
/// Singleton service instance
/// </summary>
private static readonly Lazy<CachedServerInfo> instance
= new Lazy<CachedServerInfo>(() => new CachedServerInfo());
/// <summary>
/// Gets the singleton instance
/// </summary>
public static CachedServerInfo Instance
{
get
{
return instance.Value;
}
}
public enum CacheVariable {
IsSqlDw,
IsAzure,
IsCloud
}
#region CacheKey implementation
internal class CacheKey : IEquatable<CacheKey>
{
private string dataSource;
private string dbName;
public CacheKey(SqlConnectionStringBuilder builder)
{
Validate.IsNotNull(nameof(builder), builder);
dataSource = builder.DataSource;
dbName = GetDatabaseName(builder);
}
internal static string GetDatabaseName(SqlConnectionStringBuilder builder)
{
string dbName = string.Empty;
if (!string.IsNullOrEmpty((builder.InitialCatalog)))
{
dbName = builder.InitialCatalog;
}
else if (!string.IsNullOrEmpty((builder.AttachDBFilename)))
{
dbName = builder.AttachDBFilename;
}
return dbName;
}
public override bool Equals(object obj)
{
if (obj == null) { return false; }
CacheKey keyObj = obj as CacheKey;
if (keyObj == null) { return false; }
else { return Equals(keyObj); }
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = (hash * 23) + (dataSource != null ? dataSource.GetHashCode() : 0);
hash = (hash * 23) + (dbName != null ? dbName.GetHashCode() : 0);
return hash;
}
}
public bool Equals(CacheKey other)
{
return string.Equals(dataSource, other.dataSource, StringComparison.OrdinalIgnoreCase)
&& string.Equals(dbName, other.dbName, StringComparison.OrdinalIgnoreCase);
}
}
#endregion
private struct CachedInfo
{
public bool IsAzure;
public DateTime LastUpdate;
public bool IsSqlDw;
}
private const int _maxCacheSize = 1024;
private const int _deleteBatchSize = 512;
private const int MinimalQueryTimeoutSecondsForAzure = 300;
private ConcurrentDictionary<CacheKey, CachedInfo> _cache;
private object _cacheLock;
/// <summary>
/// Internal constructor for testing purposes. For all code use, please use the <see cref="CachedServerInfo.Instance"/>
/// default instance.
/// </summary>
internal CachedServerInfo()
{
_cache = new ConcurrentDictionary<CacheKey, CachedInfo>();
_cacheLock = new object();
}
public int GetQueryTimeoutSeconds(IDbConnection connection)
{
SqlConnectionStringBuilder connStringBuilder = SafeGetConnectionStringFromConnection(connection);
return GetQueryTimeoutSeconds(connStringBuilder);
}
public int GetQueryTimeoutSeconds(SqlConnectionStringBuilder builder)
{
//keep existing behavior and return the default ambient settings
//if the provided data source is null or whitespace, or the original
//setting is already 0 which means no limit.
int originalValue = AmbientSettings.QueryTimeoutSeconds;
if (builder == null || string.IsNullOrWhiteSpace(builder.DataSource)
|| (originalValue == 0))
{
return originalValue;
}
CachedInfo info;
bool hasFound = TryGetCacheValue(builder, out info);
if (hasFound && info.IsAzure
&& originalValue < MinimalQueryTimeoutSecondsForAzure)
{
return MinimalQueryTimeoutSecondsForAzure;
}
else
{
return originalValue;
}
}
public void AddOrUpdateIsCloud(IDbConnection connection, bool isCloud)
{
AddOrUpdateCache(connection, isCloud, CacheVariable.IsCloud);
}
public void AddOrUpdateIsAzure(IDbConnection connection, bool isAzure)
{
AddOrUpdateCache(connection, isAzure, CacheVariable.IsAzure);
}
public void AddOrUpdateIsSqlDw(IDbConnection connection, bool isSqlDw)
{
AddOrUpdateCache(connection, isSqlDw, CacheVariable.IsSqlDw);
}
private void AddOrUpdateCache(IDbConnection connection, bool newState, CacheVariable cacheVar)
{
Validate.IsNotNull(nameof(connection), connection);
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
AddOrUpdateCache(builder, newState, cacheVar);
}
internal void AddOrUpdateCache(SqlConnectionStringBuilder builder, bool newState, CacheVariable cacheVar)
{
Validate.IsNotNull(nameof(builder), builder);
Validate.IsNotNullOrWhitespaceString(nameof(builder) + ".DataSource", builder.DataSource);
CachedInfo info;
bool hasFound = TryGetCacheValue(builder, out info);
if ((cacheVar == CacheVariable.IsSqlDw && hasFound && info.IsSqlDw == newState) ||
(cacheVar == CacheVariable.IsAzure && hasFound && info.IsAzure == newState))
{
// No change needed
return;
}
else
{
lock (_cacheLock)
{
// Clean older keys, update info, and add this back into the cache
CacheKey key = new CacheKey(builder);
CleanupCache(key);
if (cacheVar == CacheVariable.IsSqlDw)
{
info.IsSqlDw = newState;
}
else if (cacheVar == CacheVariable.IsAzure)
{
info.IsAzure = newState;
}
info.LastUpdate = DateTime.UtcNow;
_cache.AddOrUpdate(key, info, (k, oldValue) => info);
}
}
}
private void CleanupCache(CacheKey newKey)
{
if (!_cache.ContainsKey(newKey))
{
//delete a batch of old elements when we try to add a new one and
//the capacity limitation is hit
if (_cache.Keys.Count > _maxCacheSize - 1)
{
var keysToDelete = _cache
.OrderBy(x => x.Value.LastUpdate)
.Take(_deleteBatchSize)
.Select(pair => pair.Key);
foreach (CacheKey key in keysToDelete)
{
CachedInfo info;
_cache.TryRemove(key, out info);
}
}
}
}
public bool TryGetIsSqlDw(IDbConnection connection, out bool isSqlDw)
{
Validate.IsNotNull(nameof(connection), connection);
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
return TryGetIsSqlDw(builder, out isSqlDw);
}
public bool TryGetIsSqlDw(SqlConnectionStringBuilder builder, out bool isSqlDw)
{
Validate.IsNotNull(nameof(builder), builder);
Validate.IsNotNullOrWhitespaceString(nameof(builder) + ".DataSource", builder.DataSource);
CachedInfo info;
bool hasFound = TryGetCacheValue(builder, out info);
if(hasFound)
{
isSqlDw = info.IsSqlDw;
return true;
}
isSqlDw = false;
return false;
}
private static SqlConnectionStringBuilder SafeGetConnectionStringFromConnection(IDbConnection connection)
{
if (connection == null)
{
return null;
}
try
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
return builder;
}
catch
{
Logger.Write(TraceEventType.Error, String.Format(Resources.FailedToParseConnectionString, connection.ConnectionString));
return null;
}
}
private bool TryGetCacheValue(SqlConnectionStringBuilder builder, out CachedInfo value)
{
CacheKey key = new CacheKey(builder);
return _cache.TryGetValue(key, out value);
}
}
}

View File

@@ -0,0 +1,17 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Contains common constants used throughout ReliableConnection code.
/// </summary>
internal static class Constants
{
internal const int UndefinedErrorCode = 0;
internal const string Local = "(local)";
}
}

View File

@@ -0,0 +1,214 @@
//
// 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.Globalization;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// This class is used to encapsulate all the information needed by the DataSchemaErrorTaskService to create a corresponding entry in the Visual Studio Error List.
/// A component should add this Error Object to the <see cref="ErrorManager"/> for such purpose.
/// Errors and their children are expected to be thread-safe. Ideally, this means that
/// the objects are just data-transfer-objects initialized during construction.
/// </summary>
[Serializable]
internal class DataSchemaError
{
internal const string DefaultPrefix = "SQL";
private const int MaxErrorCode = 99999;
protected const int UndefinedErrorCode = 0;
public DataSchemaError() : this(string.Empty, ErrorSeverity.Unknown)
{
}
public DataSchemaError(string message, ErrorSeverity severity)
: this(message, string.Empty, severity)
{
}
public DataSchemaError(string message, Exception innerException, ErrorSeverity severity)
: this(message, innerException, string.Empty, 0, severity)
{
}
public DataSchemaError(string message, string document, ErrorSeverity severity)
: this(message, document, 0, 0, DefaultPrefix, UndefinedErrorCode, severity)
{
}
public DataSchemaError(string message, string document, int errorCode, ErrorSeverity severity)
: this(message, document, 0, 0, DefaultPrefix, errorCode, severity)
{
}
public DataSchemaError(string message, string document, int line, int column, ErrorSeverity severity)
: this(message, document,line, column, DefaultPrefix, UndefinedErrorCode, severity)
{
}
public DataSchemaError(DataSchemaError source, ErrorSeverity severity)
: this(source.Message, source.Document, source.Line, source.Column, source.Prefix, source.ErrorCode, severity)
{
}
public DataSchemaError(
Exception exception,
string prefix,
int errorCode,
ErrorSeverity severity)
: this(exception, string.Empty, 0, 0, prefix, errorCode, severity)
{
}
public DataSchemaError(
string message,
Exception exception,
string prefix,
int errorCode,
ErrorSeverity severity)
: this(message, exception, string.Empty, 0, 0, prefix, errorCode, severity)
{
}
public DataSchemaError(
Exception exception,
string document,
int line,
int column,
string prefix,
int errorCode,
ErrorSeverity severity)
: this(exception.Message, exception, document, line, column, prefix, errorCode, severity)
{
}
public DataSchemaError(
string message,
string document,
int line,
int column,
string prefix,
int errorCode,
ErrorSeverity severity)
: this(message, null, document, line, column, prefix, errorCode, severity)
{
}
public DataSchemaError(
string message,
Exception exception,
string document,
int line,
int column,
string prefix,
int errorCode,
ErrorSeverity severity)
{
if (errorCode > MaxErrorCode || errorCode < 0)
{
throw new ArgumentOutOfRangeException("errorCode");
}
Document = document;
Severity = severity;
Line = line;
Column = column;
Message = message;
Exception = exception;
ErrorCode = errorCode;
Prefix = prefix;
IsPriorityEditable = true;
}
/// <summary>
/// The filename of the error. It corresponds to the File column on the Visual Studio Error List window.
/// </summary>
public string Document { get; set; }
/// <summary>
/// The severity of the error
/// </summary>
public ErrorSeverity Severity { get; private set; }
public int ErrorCode { get; private set; }
/// <summary>
/// Line Number of the error
/// </summary>
public int Line { get; set; }
/// <summary>
/// Column Number of the error
/// </summary>
public int Column { get; set; }
/// <summary>
/// Prefix of the error
/// </summary>
public string Prefix { get; private set; }
/// <summary>
/// If the error has any special help topic, this property may hold the ID to the same.
/// </summary>
public string HelpKeyword { get; set; }
/// <summary>
/// Exception associated with the error, or null
/// </summary>
public Exception Exception { get; set; }
/// <summary>
/// Message
/// </summary>
public string Message { get; set; }
/// <summary>
/// Should this message honor the "treat warnings as error" flag?
/// </summary>
public Boolean IsPriorityEditable { get; set; }
/// <summary>
/// Represents the error code used in MSBuild output. This is the prefix and the
/// error code
/// </summary>
/// <returns></returns>
public string BuildErrorCode
{
get { return FormatErrorCode(Prefix, ErrorCode); }
}
internal Boolean IsBuildErrorCodeDefined
{
get { return (ErrorCode != UndefinedErrorCode); }
}
/// <summary>
/// true if this error is being displayed in ErrorList. More of an Accounting Mechanism to be used internally.
/// </summary>
internal bool IsOnDisplay { get; set; }
internal static string FormatErrorCode(string prefix, int code)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0}{1:d5}",
prefix,
code);
}
/// <summary>
/// String form of this error.
/// NB: This is for debugging only.
/// </summary>
/// <returns>String form of the error.</returns>
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "{0} - {1}({2},{3}): {4}", FormatErrorCode(Prefix, ErrorCode), Document, Line, Column, Message);
}
}
}

View File

@@ -0,0 +1,71 @@
//
// 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.Data;
using Microsoft.Data.SqlClient;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Wraps <see cref="IDbCommand"/> objects that could be a <see cref="SqlCommand"/> or
/// a <see cref="ReliableSqlConnection.ReliableSqlCommand"/>, providing common methods across both.
/// </summary>
internal sealed class DbCommandWrapper
{
private readonly IDbCommand _command;
private readonly bool _isReliableCommand;
public DbCommandWrapper(IDbCommand command)
{
Validate.IsNotNull(nameof(command), command);
if (command is ReliableSqlConnection.ReliableSqlCommand)
{
_isReliableCommand = true;
}
else if (!(command is SqlCommand))
{
throw new InvalidOperationException(Resources.InvalidCommandType);
}
_command = command;
}
public static bool IsSupportedCommand(IDbCommand command)
{
return command is ReliableSqlConnection.ReliableSqlCommand
|| command is SqlCommand;
}
public event StatementCompletedEventHandler StatementCompleted
{
add
{
SqlCommand sqlCommand = GetAsSqlCommand();
sqlCommand.StatementCompleted += value;
}
remove
{
SqlCommand sqlCommand = GetAsSqlCommand();
sqlCommand.StatementCompleted -= value;
}
}
/// <summary>
/// Gets this as a SqlCommand by casting (if we know it is actually a SqlCommand)
/// or by getting the underlying command (if it's a ReliableSqlCommand)
/// </summary>
private SqlCommand GetAsSqlCommand()
{
if (_isReliableCommand)
{
return ((ReliableSqlConnection.ReliableSqlCommand) _command).GetUnderlyingCommand();
}
return (SqlCommand) _command;
}
}
}

View File

@@ -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 System;
using System.Data;
using Microsoft.Data.SqlClient;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Wraps <see cref="IDbConnection"/> objects that could be a <see cref="SqlConnection"/> or
/// a <see cref="ReliableSqlConnection"/>, providing common methods across both.
/// </summary>
internal sealed class DbConnectionWrapper
{
private readonly IDbConnection _connection;
private readonly bool _isReliableConnection;
public DbConnectionWrapper(IDbConnection connection)
{
Validate.IsNotNull(nameof(connection), connection);
if (connection is ReliableSqlConnection)
{
_isReliableConnection = true;
}
else if (!(connection is SqlConnection))
{
throw new InvalidOperationException(Resources.InvalidConnectionType);
}
_connection = connection;
}
public static bool IsSupportedConnection(IDbConnection connection)
{
return connection is ReliableSqlConnection
|| connection is SqlConnection;
}
public event SqlInfoMessageEventHandler InfoMessage
{
add
{
SqlConnection conn = GetAsSqlConnection();
conn.InfoMessage += value;
}
remove
{
SqlConnection conn = GetAsSqlConnection();
conn.InfoMessage -= value;
}
}
public string DataSource
{
get
{
if (_isReliableConnection)
{
return ((ReliableSqlConnection) _connection).DataSource;
}
return ((SqlConnection)_connection).DataSource;
}
}
public string ServerVersion
{
get
{
if (_isReliableConnection)
{
return ((ReliableSqlConnection)_connection).ServerVersion;
}
return ((SqlConnection)_connection).ServerVersion;
}
}
/// <summary>
/// Gets this as a SqlConnection by casting (if we know it is actually a SqlConnection)
/// or by getting the underlying connection (if it's a ReliableSqlConnection)
/// </summary>
public SqlConnection GetAsSqlConnection()
{
if (_isReliableConnection)
{
return ((ReliableSqlConnection) _connection).GetUnderlyingConnection();
}
return (SqlConnection) _connection;
}
/*
TODO - IClonable does not exist in .NET Core.
/// <summary>
/// Clones the connection and ensures it's opened.
/// If it's a SqlConnection it will clone it,
/// and for ReliableSqlConnection it will clone the underling connection.
/// The reason the entire ReliableSqlConnection is not cloned is that it includes
/// several callbacks and we don't want to try and handle deciding how to clone these
/// yet.
/// </summary>
public SqlConnection CloneAndOpenConnection()
{
SqlConnection conn = GetAsSqlConnection();
SqlConnection clonedConn = ((ICloneable) conn).Clone() as SqlConnection;
clonedConn.Open();
return clonedConn;
}
*/
}
}

View File

@@ -0,0 +1,15 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
internal enum ErrorSeverity
{
Unknown = 0,
Error,
Warning,
Message
}
}

View File

@@ -0,0 +1,19 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// This interface controls the lifetime of settings created as part of the
/// top-of-stack API. Changes made to this context's AmbientData instance will
/// flow to lower in the stack while this object is not disposed.
/// </summary>
internal interface IStackSettingsContext : IDisposable
{
AmbientSettings.AmbientData Settings { get; }
}
}

View File

@@ -0,0 +1,247 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// This code is copied from the source described in the comment below.
// =======================================================================================
// Microsoft Windows Server AppFabric Customer Advisory Team (CAT) Best Practices Series
//
// This sample is supplemental to the technical guidance published on the community
// blog at http://blogs.msdn.com/appfabriccat/ and copied from
// sqlmain ./sql/manageability/mfx/common/
//
// =======================================================================================
// Copyright © 2012 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. YOU BEAR THE RISK OF USING IT.
// =======================================================================================
// namespace Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling.SqlAzure
// namespace Microsoft.SqlServer.Management.Common
using System;
using System.Data;
using System.Data.Common;
using Microsoft.Data.SqlClient;
using System.Diagnostics.Contracts;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Provides a reliable way of opening connections to and executing commands
/// taking into account potential network unreliability and a requirement for connection retry.
/// </summary>
internal sealed partial class ReliableSqlConnection
{
internal class ReliableSqlCommand : DbCommand
{
private const int Dummy = 0;
private readonly SqlCommand _command;
// connection is settable
private ReliableSqlConnection _connection;
public ReliableSqlCommand()
: this(null, Dummy)
{
}
public ReliableSqlCommand(ReliableSqlConnection connection)
: this(connection, Dummy)
{
Contract.Requires(connection != null);
}
private ReliableSqlCommand(ReliableSqlConnection connection, int dummy)
{
if (connection != null)
{
_connection = connection;
_command = connection.CreateSqlCommand();
}
else
{
_command = new SqlCommand();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_command.Dispose();
}
}
/// <summary>
/// Gets or sets the text command to run against the data source.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
public override string CommandText
{
get { return _command.CommandText; }
set { _command.CommandText = value; }
}
/// <summary>
/// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.
/// </summary>
public override int CommandTimeout
{
get { return _command.CommandTimeout; }
set { _command.CommandTimeout = value; }
}
/// <summary>
/// Gets or sets a value that specifies how the <see cref="System.Data.Common.DbCommand.CommandText"/> property is interpreted.
/// </summary>
public override CommandType CommandType
{
get { return _command.CommandType; }
set { _command.CommandType = value; }
}
/// <summary>
/// Gets or sets the <see cref="System.Data.Common.DbConnection"/> used by this <see cref="System.Data.Common.DbCommand"/>.
/// </summary>
protected override DbConnection DbConnection
{
get
{
return _connection;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
ReliableSqlConnection newConnection = value as ReliableSqlConnection;
if (newConnection == null)
{
throw new InvalidOperationException(Resources.OnlyReliableConnectionSupported);
}
_connection = newConnection;
_command.Connection = _connection._underlyingConnection;
}
}
/// <summary>
/// Gets the <see cref="System.Data.IDataParameterCollection"/>.
/// </summary>
protected override DbParameterCollection DbParameterCollection
{
get { return _command.Parameters; }
}
/// <summary>
/// Gets or sets the transaction within which the Command object of a .NET Framework data provider executes.
/// </summary>
protected override DbTransaction DbTransaction
{
get { return _command.Transaction; }
set { _command.Transaction = value as SqlTransaction; }
}
/// <summary>
/// Gets or sets a value indicating whether the command object should be visible in a customized interface control.
/// </summary>
public override bool DesignTimeVisible
{
get { return _command.DesignTimeVisible; }
set { _command.DesignTimeVisible = value; }
}
/// <summary>
/// Gets or sets how command results are applied to the System.Data.DataRow when
/// used by the System.Data.IDataAdapter.Update(System.Data.DataSet) method of
/// a <see cref="System.Data.Common.DbDataAdapter"/>.
/// </summary>
public override UpdateRowSource UpdatedRowSource
{
get { return _command.UpdatedRowSource; }
set { _command.UpdatedRowSource = value; }
}
/// <summary>
/// Attempts to cancels the execution of an <see cref="System.Data.IDbCommand"/>.
/// </summary>
public override void Cancel()
{
_command.Cancel();
}
/// <summary>
/// Creates a new instance of an <see cref="System.Data.IDbDataParameter"/> object.
/// </summary>
/// <returns>An <see cref="IDbDataParameter"/> object.</returns>
protected override DbParameter CreateDbParameter()
{
return _command.CreateParameter();
}
/// <summary>
/// Executes an SQL statement against the Connection object of a .NET Framework
/// data provider, and returns the number of rows affected.
/// </summary>
/// <returns>The number of rows affected.</returns>
public override int ExecuteNonQuery()
{
ValidateConnectionIsSet();
return _connection.ExecuteNonQuery(_command);
}
/// <summary>
/// Executes the <see cref="System.Data.IDbCommand.CommandText"/> against the <see cref="System.Data.IDbCommand.Connection"/>
/// and builds an <see cref="System.Data.IDataReader"/> using one of the <see cref="System.Data.CommandBehavior"/> values.
/// </summary>
/// <param name="behavior">One of the <see cref="System.Data.CommandBehavior"/> values.</param>
/// <returns>An <see cref="System.Data.IDataReader"/> object.</returns>
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
ValidateConnectionIsSet();
return (DbDataReader)_connection.ExecuteReader(_command, behavior);
}
/// <summary>
/// Executes the query, and returns the first column of the first row in the
/// resultset returned by the query. Extra columns or rows are ignored.
/// </summary>
/// <returns>The first column of the first row in the resultset.</returns>
public override object ExecuteScalar()
{
ValidateConnectionIsSet();
return _connection.ExecuteScalar(_command);
}
/// <summary>
/// Creates a prepared (or compiled) version of the command on the data source.
/// </summary>
public override void Prepare()
{
_command.Prepare();
}
internal SqlCommand GetUnderlyingCommand()
{
return _command;
}
internal void ValidateConnectionIsSet()
{
if (_connection == null)
{
throw new InvalidOperationException(Resources.ConnectionPropertyNotSet);
}
}
}
}
}

View File

@@ -0,0 +1,610 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// This code is copied from the source described in the comment below.
// =======================================================================================
// Microsoft Windows Server AppFabric Customer Advisory Team (CAT) Best Practices Series
//
// This sample is supplemental to the technical guidance published on the community
// blog at http://blogs.msdn.com/appfabriccat/ and copied from
// sqlmain ./sql/manageability/mfx/common/
//
// =======================================================================================
// Copyright © 2012 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. YOU BEAR THE RISK OF USING IT.
// =======================================================================================
// namespace Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling.SqlAzure
// namespace Microsoft.SqlServer.Management.Common
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Provides a reliable way of opening connections to and executing commands
/// taking into account potential network unreliability and a requirement for connection retry.
/// </summary>
internal sealed partial class ReliableSqlConnection : DbConnection, IDisposable
{
private const string QueryAzureSessionId = "SELECT CONVERT(NVARCHAR(36), CONTEXT_INFO())";
private readonly SqlConnection _underlyingConnection;
private readonly RetryPolicy _connectionRetryPolicy;
private RetryPolicy _commandRetryPolicy;
private Guid _azureSessionId;
private bool _isSqlDwDatabase;
/// <summary>
/// Initializes a new instance of the ReliableSqlConnection class with a given connection string
/// and a policy defining whether to retry a request if the connection fails to be opened or a command
/// fails to be successfully executed.
/// </summary>
/// <param name="connectionString">The connection string used to open the SQL Azure database.</param>
/// <param name="connectionRetryPolicy">The retry policy defining whether to retry a request if a connection fails to be established.</param>
/// <param name="commandRetryPolicy">The retry policy defining whether to retry a request if a command fails to be executed.</param>
public ReliableSqlConnection(string connectionString, RetryPolicy connectionRetryPolicy, RetryPolicy commandRetryPolicy)
{
_underlyingConnection = new SqlConnection(connectionString);
_connectionRetryPolicy = connectionRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
_commandRetryPolicy = commandRetryPolicy ?? RetryPolicyFactory.CreateNoRetryPolicy();
_underlyingConnection.StateChange += OnConnectionStateChange;
_connectionRetryPolicy.RetryOccurred += RetryConnectionCallback;
_commandRetryPolicy.RetryOccurred += RetryCommandCallback;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or
/// resetting managed and unmanaged resources.
/// </summary>
/// <param name="disposing">A flag indicating that managed resources must be released.</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_connectionRetryPolicy != null)
{
_connectionRetryPolicy.RetryOccurred -= RetryConnectionCallback;
}
if (_commandRetryPolicy != null)
{
_commandRetryPolicy.RetryOccurred -= RetryCommandCallback;
}
_underlyingConnection.StateChange -= OnConnectionStateChange;
if (_underlyingConnection.State == ConnectionState.Open)
{
_underlyingConnection.Close();
}
_underlyingConnection.Dispose();
}
}
/// <summary>
/// Determines if a connection is being made to a SQL DW database.
/// </summary>
/// <param name="conn">A connection object.</param>
private bool IsSqlDwConnection(IDbConnection conn)
{
//Set the connection only if it has not been set earlier.
//This is assuming that it is highly unlikely for a connection to change between instances.
//Hence any subsequent calls to this method will just return the cached value and not
//verify again if this is a SQL DW database connection or not.
if (!CachedServerInfo.Instance.TryGetIsSqlDw(conn, out _isSqlDwDatabase))
{
_isSqlDwDatabase = ReliableConnectionHelper.IsSqlDwDatabase(conn);
CachedServerInfo.Instance.AddOrUpdateIsSqlDw(conn, _isSqlDwDatabase);;
}
return _isSqlDwDatabase;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
internal static void SetLockAndCommandTimeout(IDbConnection conn)
{
Validate.IsNotNull(nameof(conn), conn);
// Make sure we use the underlying connection as ReliableConnection.Open also calls
// this method
ReliableSqlConnection reliableConn = conn as ReliableSqlConnection;
if (reliableConn != null)
{
conn = reliableConn._underlyingConnection;
}
const string setLockTimeout = @"set LOCK_TIMEOUT {0}";
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = string.Format(CultureInfo.InvariantCulture, setLockTimeout, AmbientSettings.LockTimeoutMilliSeconds);
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = CachedServerInfo.Instance.GetQueryTimeoutSeconds(conn);
cmd.ExecuteNonQuery();
}
}
internal static void SetDefaultAnsiSettings(IDbConnection conn, bool isSqlDw)
{
Validate.IsNotNull(nameof(conn), conn);
// Make sure we use the underlying connection as ReliableConnection.Open also calls
// this method
ReliableSqlConnection reliableConn = conn as ReliableSqlConnection;
if (reliableConn != null)
{
conn = reliableConn._underlyingConnection;
}
// Configure the connection with proper ANSI settings and lock timeout
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandTimeout = CachedServerInfo.Instance.GetQueryTimeoutSeconds(conn);
if (!isSqlDw)
{
cmd.CommandText = @"SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;";
}
else
{
cmd.CommandText = @"SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET ARITHABORT ON; SET CONCAT_NULL_YIELDS_NULL ON; SET QUOTED_IDENTIFIER ON;"; //SQL DW does not support NUMERIC_ROUNDABORT
}
cmd.ExecuteNonQuery();
}
}
/// <summary>
/// Gets or sets the connection string for opening a connection to the SQL Azure database.
/// </summary>
public override string ConnectionString
{
get { return _underlyingConnection.ConnectionString; }
set { _underlyingConnection.ConnectionString = value; }
}
/// <summary>
/// Gets the policy which decides whether to retry a connection request, based on how many
/// times the request has been made and the reason for the last failure.
/// </summary>
public RetryPolicy ConnectionRetryPolicy
{
get { return _connectionRetryPolicy; }
}
/// <summary>
/// Gets the policy which decides whether to retry a command, based on how many
/// times the request has been made and the reason for the last failure.
/// </summary>
public RetryPolicy CommandRetryPolicy
{
get { return _commandRetryPolicy; }
set
{
Validate.IsNotNull(nameof(value), value);
if (_commandRetryPolicy != null)
{
_commandRetryPolicy.RetryOccurred -= RetryCommandCallback;
}
_commandRetryPolicy = value;
_commandRetryPolicy.RetryOccurred += RetryCommandCallback;
}
}
/// <summary>
/// Gets the server name from the underlying connection.
/// </summary>
public override string DataSource
{
get { return _underlyingConnection.DataSource; }
}
/// <summary>
/// Gets the server version from the underlying connection.
/// </summary>
public override string ServerVersion
{
get { return _underlyingConnection.ServerVersion; }
}
/// <summary>
/// If the underlying SqlConnection absolutely has to be accessed, for instance
/// to pass to external APIs that require this type of connection, then this
/// can be used.
/// </summary>
/// <returns><see cref="SqlConnection"/></returns>
public SqlConnection GetUnderlyingConnection()
{
return _underlyingConnection;
}
/// <summary>
/// Begins a database transaction with the specified System.Data.IsolationLevel value.
/// </summary>
/// <param name="level">One of the System.Data.IsolationLevel values.</param>
/// <returns>An object representing the new transaction.</returns>
protected override DbTransaction BeginDbTransaction(IsolationLevel level)
{
return _underlyingConnection.BeginTransaction(level);
}
/// <summary>
/// Changes the current database for an open Connection object.
/// </summary>
/// <param name="databaseName">The name of the database to use in place of the current database.</param>
public override void ChangeDatabase(string databaseName)
{
_underlyingConnection.ChangeDatabase(databaseName);
}
/// <summary>
/// Opens a database connection with the settings specified by the ConnectionString
/// property of the provider-specific Connection object.
/// </summary>
public override void Open()
{
// Check if retry policy was specified, if not, disable retries by executing the Open method using RetryPolicy.NoRetry.
_connectionRetryPolicy.ExecuteAction(() =>
{
if (_underlyingConnection.State != ConnectionState.Open)
{
_underlyingConnection.Open();
}
SetLockAndCommandTimeout(_underlyingConnection);
SetDefaultAnsiSettings(_underlyingConnection, IsSqlDwConnection(_underlyingConnection));
});
}
/// <summary>
/// Opens a database connection with the settings specified by the ConnectionString
/// property of the provider-specific Connection object.
/// </summary>
public override Task OpenAsync(CancellationToken token)
{
// Make sure that the token isn't cancelled before we try
if (token.IsCancellationRequested)
{
return Task.FromCanceled(token);
}
// Check if retry policy was specified, if not, disable retries by executing the Open method using RetryPolicy.NoRetry.
try
{
return _connectionRetryPolicy.ExecuteAction(async () =>
{
if (_underlyingConnection.State != ConnectionState.Open)
{
await _underlyingConnection.OpenAsync(token);
}
SetLockAndCommandTimeout(_underlyingConnection);
SetDefaultAnsiSettings(_underlyingConnection, IsSqlDwConnection(_underlyingConnection));
});
}
catch (Exception e)
{
return Task.FromException(e);
}
}
/// <summary>
/// Closes the connection to the database.
/// </summary>
public override void Close()
{
_underlyingConnection.Close();
}
/// <summary>
/// Gets the time to wait while trying to establish a connection before terminating
/// the attempt and generating an error.
/// </summary>
public override int ConnectionTimeout
{
get { return _underlyingConnection.ConnectionTimeout; }
}
/// <summary>
/// Creates and returns an object implementing the IDbCommand interface which is associated
/// with the underlying SqlConnection.
/// </summary>
/// <returns>A <see cref="IDbCommand"/> object.</returns>
protected override DbCommand CreateDbCommand()
{
return CreateReliableCommand();
}
/// <summary>
/// Creates and returns an object implementing the IDbCommand interface which is associated
/// with the underlying SqlConnection.
/// </summary>
/// <returns>A <see cref="SqlCommand"/> object.</returns>
public SqlCommand CreateSqlCommand()
{
return _underlyingConnection.CreateCommand();
}
/// <summary>
/// Gets the name of the current database or the database to be used after a
/// connection is opened.
/// </summary>
public override string Database
{
get { return _underlyingConnection.Database; }
}
/// <summary>
/// Gets the current state of the connection.
/// </summary>
public override ConnectionState State
{
get { return _underlyingConnection.State; }
}
/// <summary>
/// Adds an info message event listener.
/// </summary>
/// <param name="handler">An info message event listener.</param>
public void AddInfoMessageHandler(SqlInfoMessageEventHandler handler)
{
_underlyingConnection.InfoMessage += handler;
}
/// <summary>
/// Removes an info message event listener.
/// </summary>
/// <param name="handler">An info message event listener.</param>
public void RemoveInfoMessageHandler(SqlInfoMessageEventHandler handler)
{
_underlyingConnection.InfoMessage -= handler;
}
/// <summary>
/// Clears underlying connection pool.
/// </summary>
public void ClearPool()
{
if (_underlyingConnection != null)
{
SqlConnection.ClearPool(_underlyingConnection);
}
}
private void RetryCommandCallback(RetryState retryState)
{
RetryPolicyUtils.RaiseSchemaAmbientRetryMessage(retryState, SqlSchemaModelErrorCodes.ServiceActions.CommandRetry, _azureSessionId);
}
private void RetryConnectionCallback(RetryState retryState)
{
RetryPolicyUtils.RaiseSchemaAmbientRetryMessage(retryState, SqlSchemaModelErrorCodes.ServiceActions.ConnectionRetry, _azureSessionId);
}
public void OnConnectionStateChange(object sender, StateChangeEventArgs e)
{
SqlConnection conn = (SqlConnection)sender;
switch (e.CurrentState)
{
case ConnectionState.Open:
RetrieveSessionId();
break;
case ConnectionState.Broken:
case ConnectionState.Closed:
_azureSessionId = Guid.Empty;
break;
case ConnectionState.Connecting:
case ConnectionState.Executing:
case ConnectionState.Fetching:
default:
break;
}
}
private void RetrieveSessionId()
{
try
{
using (IDbCommand command = CreateReliableCommand())
{
IDbConnection connection = command.Connection;
if (!IsSqlDwConnection(connection))
{
command.CommandText = QueryAzureSessionId;
object result = command.ExecuteScalar();
// Only returns a session id for SQL Azure
if (DBNull.Value != result)
{
string sessionId = (string)command.ExecuteScalar();
_azureSessionId = new Guid(sessionId);
}
}
}
}
catch (SqlException exception)
{
Logger.Write(TraceEventType.Error, Resources.UnableToRetrieveAzureSessionId + exception.ToString());
}
}
/// <summary>
/// Creates and returns a ReliableSqlCommand object associated
/// with the underlying SqlConnection.
/// </summary>
/// <returns>A <see cref="ReliableSqlCommand"/> object.</returns>
private ReliableSqlCommand CreateReliableCommand()
{
return new ReliableSqlCommand(this);
}
private void VerifyConnectionOpen(IDbCommand command)
{
// Verify whether or not the connection is valid and is open. This code may be retried therefore
// it is important to ensure that a connection is re-established should it have previously failed.
if (command.Connection == null)
{
command.Connection = this;
}
if (command.Connection.State != ConnectionState.Open)
{
SqlConnection.ClearPool(_underlyingConnection);
command.Connection.Open();
}
}
private IDataReader ExecuteReader(IDbCommand command, CommandBehavior behavior)
{
Tuple<string, bool>[] sessionSettings = null;
return _commandRetryPolicy.ExecuteAction<IDataReader>(() =>
{
VerifyConnectionOpen(command);
sessionSettings = CacheOrReplaySessionSettings(command, sessionSettings);
return command.ExecuteReader(behavior);
});
}
// Because retry loses session settings, cache session settings or reply if the settings are already cached.
internal Tuple<string, bool>[] CacheOrReplaySessionSettings(IDbCommand originalCommand, Tuple<string, bool>[] sessionSettings)
{
if (sessionSettings == null)
{
sessionSettings = QuerySessionSettings(originalCommand);
}
else
{
SetSessionSettings(originalCommand.Connection, sessionSettings);
}
return sessionSettings;
}
private object ExecuteScalar(IDbCommand command)
{
Tuple<string,bool>[] sessionSettings = null;
return _commandRetryPolicy.ExecuteAction(() =>
{
VerifyConnectionOpen(command);
sessionSettings = CacheOrReplaySessionSettings(command, sessionSettings);
return command.ExecuteScalar();
});
}
private Tuple<string, bool>[] QuerySessionSettings(IDbCommand originalCommand)
{
Tuple<string,bool>[] sessionSettings = new Tuple<string,bool>[2];
IDbConnection connection = originalCommand.Connection;
if (IsSqlDwConnection(connection))
{
// SESSIONPROPERTY is not supported. Use default values for now
sessionSettings[0] = Tuple.Create("ANSI_NULLS", true);
sessionSettings[1] = Tuple.Create("QUOTED_IDENTIFIER", true);
}
else
{
using (IDbCommand localCommand = connection.CreateCommand())
{
// Executing a reader requires preservation of any pending transaction created by the calling command
localCommand.Transaction = originalCommand.Transaction;
localCommand.CommandText = "SELECT ISNULL(SESSIONPROPERTY ('ANSI_NULLS'), 0), ISNULL(SESSIONPROPERTY ('QUOTED_IDENTIFIER'), 1)";
using (IDataReader reader = localCommand.ExecuteReader())
{
if (reader.Read())
{
sessionSettings[0] = Tuple.Create("ANSI_NULLS", ((int)reader[0] == 1));
sessionSettings[1] = Tuple.Create("QUOTED_IDENTIFIER", ((int)reader[1] ==1));
}
else
{
Debug.Assert(false, "Reader cannot be empty");
}
}
}
}
return sessionSettings;
}
private void SetSessionSettings(IDbConnection connection, params Tuple<string, bool>[] settings)
{
List<string> setONOptions = new List<string>();
List<string> setOFFOptions = new List<string>();
if(settings != null)
{
foreach (Tuple<string, bool> setting in settings)
{
if (setting.Item2)
{
setONOptions.Add(setting.Item1);
}
else
{
setOFFOptions.Add(setting.Item1);
}
}
}
SetSessionSettings(connection, setONOptions, "ON");
SetSessionSettings(connection, setOFFOptions, "OFF");
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
private static void SetSessionSettings(IDbConnection connection, List<string> sessionOptions, string onOff)
{
if (sessionOptions.Count > 0)
{
using (IDbCommand localCommand = connection.CreateCommand())
{
StringBuilder builder = new StringBuilder("SET ");
for (int i = 0; i < sessionOptions.Count; i++)
{
if (i > 0)
{
builder.Append(',');
}
builder.Append(sessionOptions[i]);
}
builder.Append(" ");
builder.Append(onOff);
localCommand.CommandText = builder.ToString();
localCommand.ExecuteNonQuery();
}
}
}
private int ExecuteNonQuery(IDbCommand command)
{
Tuple<string, bool>[] sessionSettings = null;
return _commandRetryPolicy.ExecuteAction<int>(() =>
{
VerifyConnectionOpen(command);
sessionSettings = CacheOrReplaySessionSettings(command, sessionSettings);
return command.ExecuteNonQuery();
});
}
}
}

View File

@@ -0,0 +1,157 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Contains string resources used throughout ReliableConnection code.
/// </summary>
internal static class Resources
{
internal static string AmbientSettingFormat
{
get
{
return "{0}: {1}";
}
}
internal static string ConnectionPassedToIsCloudShouldBeOpen
{
get
{
return "connection passed to IsCloud should be open.";
}
}
internal static string ConnectionPropertyNotSet
{
get
{
return "Connection property has not been initialized.";
}
}
internal static string ExceptionCannotBeRetried
{
get
{
return "Exception cannot be retried because of err #{0}:{1}";
}
}
internal static string ErrorParsingConnectionString
{
get
{
return "Error parsing connection string {0}";
}
}
internal static string FailedToCacheIsCloud
{
get
{
return "failed to cache the server property of IsAzure";
}
}
internal static string FailedToParseConnectionString
{
get
{
return "failed to parse the provided connection string: {0}";
}
}
internal static string IgnoreOnException
{
get
{
return "Retry number {0}. Ignoring Exception: {1}";
}
}
internal static string InvalidCommandType
{
get
{
return "Unsupported command object. Use SqlCommand or ReliableSqlCommand.";
}
}
internal static string InvalidConnectionType
{
get
{
return "Unsupported connection object. Use SqlConnection or ReliableSqlConnection.";
}
}
internal static string LoggingAmbientSettings
{
get
{
return "Logging Ambient Settings...";
}
}
internal static string Mode
{
get
{
return "Mode";
}
}
internal static string OnlyReliableConnectionSupported
{
get
{
return "Connection property can only be set to a value of type ReliableSqlConnection.";
}
}
internal static string RetryOnException
{
get
{
return "Retry number {0}. Delaying {1} ms before next retry. Exception: {2}";
}
}
internal static string ThrottlingTypeInfo
{
get
{
return "ThrottlingTypeInfo";
}
}
internal static string UnableToAssignValue
{
get
{
return "Unable to assign the value of type {0} to {1}";
}
}
internal static string UnableToRetrieveAzureSessionId
{
get
{
return "Unable to retrieve Azure session-id.";
}
}
internal static string ServerInfoCacheMiss
{
get
{
return "Server Info does not have the requested property in the cache";
}
}
}
}

View File

@@ -0,0 +1,61 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// This code is copied from the source described in the comment below.
// =======================================================================================
// Microsoft Windows Server AppFabric Customer Advisory Team (CAT) Best Practices Series
//
// This sample is supplemental to the technical guidance published on the community
// blog at http://blogs.msdn.com/appfabriccat/ and copied from
// sqlmain ./sql/manageability/mfx/common/
//
// =======================================================================================
// Copyright © 2012 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. YOU BEAR THE RISK OF USING IT.
// =======================================================================================
// namespace Microsoft.SQL.CAT.BestPractices.SqlAzure.Framework
// namespace Microsoft.SqlServer.Management.Common
using System;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Defines a arguments for event handler which will be invoked whenever a retry condition is encountered.
/// </summary>
internal sealed class RetryCallbackEventArgs : EventArgs
{
private readonly int _retryCount;
private readonly Exception _exception;
private readonly TimeSpan _delay;
public RetryCallbackEventArgs(int retryCount, Exception exception, TimeSpan delay)
{
_retryCount = retryCount;
_exception = exception;
_delay = delay;
}
public TimeSpan Delay
{
get { return _delay; }
}
public Exception Exception
{
get { return _exception; }
}
public int RetryCount
{
get { return _retryCount; }
}
}
}

View File

@@ -0,0 +1,45 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// This code is copied from the source described in the comment below.
// =======================================================================================
// Microsoft Windows Server AppFabric Customer Advisory Team (CAT) Best Practices Series
//
// This sample is supplemental to the technical guidance published on the community
// blog at http://blogs.msdn.com/appfabriccat/ and copied from
// sqlmain ./sql/manageability/mfx/common/
//
// =======================================================================================
// Copyright © 2012 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. YOU BEAR THE RISK OF USING IT.
// =======================================================================================
// namespace Microsoft.SQL.CAT.BestPractices.SqlAzure.Framework
// namespace Microsoft.SqlServer.Management.Common
using System;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// The special type of exception that provides managed exit from a retry loop. The user code can use this
/// exception to notify the retry policy that no further retry attempts are required.
/// </summary>
[Serializable]
internal sealed class RetryLimitExceededException : Exception
{
internal RetryLimitExceededException() : base()
{
}
internal RetryLimitExceededException(string m, Exception e) : base(m, e)
{
}
}
}

View File

@@ -0,0 +1,44 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal abstract partial class RetryPolicy
{
/// <summary>
/// Provides the error detection logic for temporary faults that are commonly found during data transfer.
/// </summary>
internal class DataTransferErrorDetectionStrategy : ErrorDetectionStrategyBase
{
private static readonly DataTransferErrorDetectionStrategy instance = new DataTransferErrorDetectionStrategy();
public static DataTransferErrorDetectionStrategy Instance
{
get { return instance; }
}
protected override bool CanRetrySqlException(SqlException sqlException)
{
// Enumerate through all errors found in the exception.
foreach (SqlError err in sqlException.Errors)
{
RetryPolicyUtils.AppendThrottlingDataIfIsThrottlingError(sqlException, err);
if (RetryPolicyUtils.IsNonRetryableDataTransferError(err.Number))
{
Logger.Write(TraceEventType.Error, string.Format(Resources.ExceptionCannotBeRetried, err.Number, err.Message));
return false;
}
}
// Default is to treat all SqlException as retriable.
return true;
}
}
}
}

View File

@@ -0,0 +1,97 @@
//
// 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 Microsoft.Data.SqlClient;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal abstract partial class RetryPolicy
{
public interface IErrorDetectionStrategy
{
/// <summary>
/// Determines whether the specified exception represents a temporary failure that can be compensated by a retry.
/// </summary>
/// <param name="ex">The exception object to be verified.</param>
/// <returns>True if the specified exception is considered as temporary, otherwise false.</returns>
bool CanRetry(Exception ex);
/// <summary>
/// Determines whether the specified exception can be ignored.
/// </summary>
/// <param name="ex">The exception object to be verified.</param>
/// <returns>True if the specified exception is considered as non-harmful.</returns>
bool ShouldIgnoreError(Exception ex);
}
/// <summary>
/// Base class with common retry logic. The core behavior for retrying non SqlExceptions is the same
/// across retry policies
/// </summary>
internal abstract class ErrorDetectionStrategyBase : IErrorDetectionStrategy
{
public bool CanRetry(Exception ex)
{
if (ex != null)
{
SqlException sqlException;
if ((sqlException = ex as SqlException) != null)
{
return CanRetrySqlException(sqlException);
}
if (ex is InvalidOperationException)
{
// Operations can throw this exception if the connection is killed before the write starts to the server
// However if there's an inner SqlException it may be a CLR load failure or other non-transient error
if (ex.InnerException != null
&& ex.InnerException is SqlException)
{
return CanRetry(ex.InnerException);
}
return true;
}
if (ex is TimeoutException)
{
return true;
}
}
return false;
}
public bool ShouldIgnoreError(Exception ex)
{
if (ex != null)
{
SqlException sqlException;
if ((sqlException = ex as SqlException) != null)
{
return ShouldIgnoreSqlException(sqlException);
}
if (ex is InvalidOperationException)
{
// Operations can throw this exception if the connection is killed before the write starts to the server
// However if there's an inner SqlException it may be a CLR load failure or other non-transient error
if (ex.InnerException != null
&& ex.InnerException is SqlException)
{
return ShouldIgnoreError(ex.InnerException);
}
}
}
return false;
}
protected virtual bool ShouldIgnoreSqlException(SqlException sqlException)
{
return false;
}
protected abstract bool CanRetrySqlException(SqlException sqlException);
}
}
}

View File

@@ -0,0 +1,43 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.Data.SqlClient;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal abstract partial class RetryPolicy
{
/// <summary>
/// Provides the error detection logic for temporary faults that are commonly found in SQL Azure.
/// The same errors CAN occur on premise also, but they are not seen as often.
/// </summary>
internal sealed class NetworkConnectivityErrorDetectionStrategy : ErrorDetectionStrategyBase
{
private static NetworkConnectivityErrorDetectionStrategy instance = new NetworkConnectivityErrorDetectionStrategy();
public static NetworkConnectivityErrorDetectionStrategy Instance
{
get { return instance; }
}
protected override bool CanRetrySqlException(SqlException sqlException)
{
// Enumerate through all errors found in the exception.
bool foundRetryableError = false;
foreach (SqlError err in sqlException.Errors)
{
RetryPolicyUtils.AppendThrottlingDataIfIsThrottlingError(sqlException, err);
if (!RetryPolicyUtils.IsRetryableNetworkConnectivityError(err.Number))
{
// If any error is not retryable then cannot retry
return false;
}
foundRetryableError = true;
}
return foundRetryableError;
}
}
}
}

View File

@@ -0,0 +1,63 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal abstract partial class RetryPolicy
{
/// <summary>
/// Provides the error detection logic for temporary faults that are commonly found in SQL Azure.
/// This strategy is similar to SqlAzureTemporaryErrorDetectionStrategy, but it exposes ways
/// to accept a certain exception and treat it as passing.
/// For example, if we are retrying, and we get a failure that an object already exists, we might
/// want to consider this as passing since the first execution that has timed out (or failed for some other temporary error)
/// might have managed to create the object.
/// </summary>
internal class SqlAzureTemporaryAndIgnorableErrorDetectionStrategy : ErrorDetectionStrategyBase
{
/// <summary>
/// Azure error that can be ignored
/// </summary>
private readonly IList<int> ignorableAzureErrors = null;
public SqlAzureTemporaryAndIgnorableErrorDetectionStrategy(params int[] ignorableErrors)
{
this.ignorableAzureErrors = ignorableErrors;
}
protected override bool CanRetrySqlException(SqlException sqlException)
{
// Enumerate through all errors found in the exception.
bool foundRetryableError = false;
foreach (SqlError err in sqlException.Errors)
{
RetryPolicyUtils.AppendThrottlingDataIfIsThrottlingError(sqlException, err);
if (!RetryPolicyUtils.IsRetryableAzureError(err.Number))
{
return false;
}
foundRetryableError = true;
}
return foundRetryableError;
}
protected override bool ShouldIgnoreSqlException(SqlException sqlException)
{
int errorNumber = sqlException.Number;
if (ignorableAzureErrors == null)
{
return false;
}
return ignorableAzureErrors.Contains(errorNumber);
}
}
}
}

View File

@@ -0,0 +1,43 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.Data.SqlClient;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal abstract partial class RetryPolicy
{
/// <summary>
/// Provides the error detection logic for temporary faults that are commonly found in SQL Azure.
/// The same errors CAN occur on premise also, but they are not seen as often.
/// </summary>
internal sealed class SqlAzureTemporaryErrorDetectionStrategy : ErrorDetectionStrategyBase
{
private static SqlAzureTemporaryErrorDetectionStrategy instance = new SqlAzureTemporaryErrorDetectionStrategy();
public static SqlAzureTemporaryErrorDetectionStrategy Instance
{
get { return instance; }
}
protected override bool CanRetrySqlException(SqlException sqlException)
{
// Enumerate through all errors found in the exception.
bool foundRetryableError = false;
foreach (SqlError err in sqlException.Errors)
{
RetryPolicyUtils.AppendThrottlingDataIfIsThrottlingError(sqlException, err);
if (!RetryPolicyUtils.IsRetryableAzureError(err.Number))
{
// If any error is not retryable then cannot retry
return false;
}
foundRetryableError = true;
}
return foundRetryableError;
}
}
}
}

View File

@@ -0,0 +1,357 @@
//
// 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.Generic;
using Microsoft.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal abstract partial class RetryPolicy
{
/// <summary>
/// Implements an object holding the decoded reason code returned from SQL Azure when encountering throttling conditions.
/// </summary>
[Serializable]
public class ThrottlingReason
{
/// <summary>
/// Returns the error number that corresponds to throttling conditions reported by SQL Azure.
/// </summary>
public const int ThrottlingErrorNumber = 40501;
/// <summary>
/// Gets an unknown throttling condition in the event the actual throttling condition cannot be determined.
/// </summary>
public static ThrottlingReason Unknown
{
get
{
var unknownCondition = new ThrottlingReason() { ThrottlingMode = ThrottlingMode.Unknown };
unknownCondition.throttledResources.Add(Tuple.Create(ThrottledResourceType.Unknown, ThrottlingType.Unknown));
return unknownCondition;
}
}
/// <summary>
/// Maintains a collection of key-value pairs where a key is resource type and a value is the type of throttling applied to the given resource type.
/// </summary>
private readonly IList<Tuple<ThrottledResourceType, ThrottlingType>> throttledResources = new List<Tuple<ThrottledResourceType, ThrottlingType>>(9);
/// <summary>
/// Provides a compiled regular expression used for extracting the reason code from the error message.
/// </summary>
private static readonly Regex sqlErrorCodeRegEx = new Regex(@"Code:\s*(\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Gets the value that reflects the throttling mode in SQL Azure.
/// </summary>
public ThrottlingMode ThrottlingMode
{
get;
private set;
}
/// <summary>
/// Gets the list of resources in SQL Azure that were subject to throttling conditions.
/// </summary>
public IEnumerable<Tuple<ThrottledResourceType, ThrottlingType>> ThrottledResources
{
get
{
return this.throttledResources;
}
}
/// <summary>
/// Determines throttling conditions from the specified SQL exception.
/// </summary>
/// <param name="ex">The <see cref="SqlException"/> object containing information relevant to an error returned by SQL Server when encountering throttling conditions.</param>
/// <returns>An instance of the object holding the decoded reason codes returned from SQL Azure upon encountering throttling conditions.</returns>
public static ThrottlingReason FromException(SqlException ex)
{
if (ex != null)
{
foreach (SqlError error in ex.Errors)
{
if (error.Number == ThrottlingErrorNumber)
{
return FromError(error);
}
}
}
return Unknown;
}
/// <summary>
/// Determines the throttling conditions from the specified SQL error.
/// </summary>
/// <param name="error">The <see cref="SqlError"/> object containing information relevant to a warning or error returned by SQL Server.</param>
/// <returns>An instance of the object holding the decoded reason codes returned from SQL Azure when encountering throttling conditions.</returns>
public static ThrottlingReason FromError(SqlError error)
{
if (error != null)
{
var match = sqlErrorCodeRegEx.Match(error.Message);
int reasonCode = 0;
if (match.Success && Int32.TryParse(match.Groups[1].Value, out reasonCode))
{
return FromReasonCode(reasonCode);
}
}
return Unknown;
}
/// <summary>
/// Determines the throttling conditions from the specified reason code.
/// </summary>
/// <param name="reasonCode">The reason code returned by SQL Azure which contains the throttling mode and the exceeded resource types.</param>
/// <returns>An instance of the object holding the decoded reason codes returned from SQL Azure when encountering throttling conditions.</returns>
public static ThrottlingReason FromReasonCode(int reasonCode)
{
if (reasonCode > 0)
{
// Decode throttling mode from the last 2 bits.
ThrottlingMode throttlingMode = (ThrottlingMode)(reasonCode & 3);
var condition = new ThrottlingReason() { ThrottlingMode = throttlingMode };
// Shift 8 bits to truncate throttling mode.
int groupCode = reasonCode >> 8;
// Determine throttling type for all well-known resources that may be subject to throttling conditions.
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.PhysicalDatabaseSpace, (ThrottlingType)(groupCode & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.PhysicalLogSpace, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.LogWriteIODelay, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.DataReadIODelay, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.CPU, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.DatabaseSize, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.Internal, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.WorkerThreads, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
condition.throttledResources.Add(Tuple.Create(ThrottledResourceType.Internal, (ThrottlingType)((groupCode = groupCode >> 2) & 3)));
return condition;
}
else
{
return Unknown;
}
}
/// <summary>
/// Gets a value indicating whether physical data file space throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnDataSpace
{
get
{
return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.PhysicalDatabaseSpace).Count() > 0;
}
}
/// <summary>
/// Gets a value indicating whether physical log space throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnLogSpace
{
get
{
return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.PhysicalLogSpace).Count() > 0;
}
}
/// <summary>
/// Gets a value indicating whether transaction activity throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnLogWrite
{
get { return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.LogWriteIODelay).Count() > 0; }
}
/// <summary>
/// Gets a value indicating whether data read activity throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnDataRead
{
get { return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.DataReadIODelay).Count() > 0; }
}
/// <summary>
/// Gets a value indicating whether CPU throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnCPU
{
get { return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.CPU).Count() > 0; }
}
/// <summary>
/// Gets a value indicating whether database size throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnDatabaseSize
{
get { return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.DatabaseSize).Count() > 0; }
}
/// <summary>
/// Gets a value indicating whether concurrent requests throttling was reported by SQL Azure.
/// </summary>
public bool IsThrottledOnWorkerThreads
{
get { return this.throttledResources.Where(x => x.Item1 == ThrottledResourceType.WorkerThreads).Count() > 0; }
}
/// <summary>
/// Gets a value indicating whether throttling conditions were not determined with certainty.
/// </summary>
public bool IsUnknown
{
get { return ThrottlingMode == ThrottlingMode.Unknown; }
}
/// <summary>
/// Returns a textual representation the current ThrottlingReason object including the information held with respect to throttled resources.
/// </summary>
/// <returns>A string that represents the current ThrottlingReason object.</returns>
public override string ToString()
{
StringBuilder result = new StringBuilder();
result.AppendFormat(Resources.Mode, ThrottlingMode);
var resources = this.throttledResources.Where(x => x.Item1 != ThrottledResourceType.Internal).
Select<Tuple<ThrottledResourceType, ThrottlingType>, string>(x => String.Format(CultureInfo.CurrentCulture, Resources.ThrottlingTypeInfo, x.Item1, x.Item2)).
OrderBy(x => x).ToArray();
result.Append(String.Join(", ", resources));
return result.ToString();
}
}
#region ThrottlingMode enumeration
/// <summary>
/// Defines the possible throttling modes in SQL Azure.
/// </summary>
public enum ThrottlingMode
{
/// <summary>
/// Corresponds to "No Throttling" throttling mode whereby all SQL statements can be processed.
/// </summary>
NoThrottling = 0,
/// <summary>
/// Corresponds to "Reject Update / Insert" throttling mode whereby SQL statements such as INSERT, UPDATE, CREATE TABLE and CREATE INDEX are rejected.
/// </summary>
RejectUpdateInsert = 1,
/// <summary>
/// Corresponds to "Reject All Writes" throttling mode whereby SQL statements such as INSERT, UPDATE, DELETE, CREATE, DROP are rejected.
/// </summary>
RejectAllWrites = 2,
/// <summary>
/// Corresponds to "Reject All" throttling mode whereby all SQL statements are rejected.
/// </summary>
RejectAll = 3,
/// <summary>
/// Corresponds to an unknown throttling mode whereby throttling mode cannot be determined with certainty.
/// </summary>
Unknown = -1
}
#endregion
#region ThrottlingType enumeration
/// <summary>
/// Defines the possible throttling types in SQL Azure.
/// </summary>
public enum ThrottlingType
{
/// <summary>
/// Indicates that no throttling was applied to a given resource.
/// </summary>
None = 0,
/// <summary>
/// Corresponds to a Soft throttling type. Soft throttling is applied when machine resources such as, CPU, IO, storage, and worker threads exceed
/// predefined safety thresholds despite the load balancers best efforts.
/// </summary>
Soft = 1,
/// <summary>
/// Corresponds to a Hard throttling type. Hard throttling is applied when the machine is out of resources, for example storage space.
/// With hard throttling, no new connections are allowed to the databases hosted on the machine until resources are freed up.
/// </summary>
Hard = 2,
/// <summary>
/// Corresponds to an unknown throttling type in the event when the throttling type cannot be determined with certainty.
/// </summary>
Unknown = 3
}
#endregion
#region ThrottledResourceType enumeration
/// <summary>
/// Defines the types of resources in SQL Azure which may be subject to throttling conditions.
/// </summary>
public enum ThrottledResourceType
{
/// <summary>
/// Corresponds to "Physical Database Space" resource which may be subject to throttling.
/// </summary>
PhysicalDatabaseSpace = 0,
/// <summary>
/// Corresponds to "Physical Log File Space" resource which may be subject to throttling.
/// </summary>
PhysicalLogSpace = 1,
/// <summary>
/// Corresponds to "Transaction Log Write IO Delay" resource which may be subject to throttling.
/// </summary>
LogWriteIODelay = 2,
/// <summary>
/// Corresponds to "Database Read IO Delay" resource which may be subject to throttling.
/// </summary>
DataReadIODelay = 3,
/// <summary>
/// Corresponds to "CPU" resource which may be subject to throttling.
/// </summary>
CPU = 4,
/// <summary>
/// Corresponds to "Database Size" resource which may be subject to throttling.
/// </summary>
DatabaseSize = 5,
/// <summary>
/// Corresponds to "SQL Worker Thread Pool" resource which may be subject to throttling.
/// </summary>
WorkerThreads = 7,
/// <summary>
/// Corresponds to an internal resource which may be subject to throttling.
/// </summary>
Internal = 6,
/// <summary>
/// Corresponds to an unknown resource type in the event when the actual resource cannot be determined with certainty.
/// </summary>
Unknown = -1
}
#endregion
}
}

View File

@@ -0,0 +1,542 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// This code is copied from the source described in the comment below.
// =======================================================================================
// Microsoft Windows Server AppFabric Customer Advisory Team (CAT) Best Practices Series
//
// This sample is supplemental to the technical guidance published on the community
// blog at http://blogs.msdn.com/appfabriccat/ and copied from
// sqlmain ./sql/manageability/mfx/common/
//
// =======================================================================================
// Copyright © 2012 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
// EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. YOU BEAR THE RISK OF USING IT.
// =======================================================================================
// namespace Microsoft.SQL.CAT.BestPractices.SqlAzure.Framework
// namespace Microsoft.SqlServer.Management.Common
using System;
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Threading;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Implements a policy defining and implementing the retry mechanism for unreliable actions.
/// </summary>
internal abstract partial class RetryPolicy
{
/// <summary>
/// Defines a callback delegate which will be invoked whenever a retry condition is encountered.
/// </summary>
/// <param name="retryState">The state of current retry attempt.</param>
internal delegate void RetryCallbackDelegate(RetryState retryState);
/// <summary>
/// Defines a callback delegate which will be invoked whenever an error is ignored on retry.
/// </summary>
/// <param name="retryState">The state of current retry attempt.</param>
internal delegate void IgnoreErrorCallbackDelegate(RetryState retryState);
private readonly IErrorDetectionStrategy _errorDetectionStrategy;
protected RetryPolicy(IErrorDetectionStrategy strategy)
{
Contract.Assert(strategy != null);
_errorDetectionStrategy = strategy;
this.FastFirstRetry = true;
//TODO Defect 1078447 Validate whether CommandTimeout needs to be used differently in schema/data scenarios
this.CommandTimeoutInSeconds = AmbientSettings.LongRunningQueryTimeoutSeconds;
}
/// <summary>
/// An instance of a callback delegate which will be invoked whenever a retry condition is encountered.
/// </summary>
public event RetryCallbackDelegate RetryOccurred;
/// <summary>
/// An instance of a callback delegate which will be invoked whenever an error is ignored on retry.
/// </summary>
public event IgnoreErrorCallbackDelegate IgnoreErrorOccurred;
/// <summary>
/// Gets or sets a value indicating whether or not the very first retry attempt will be made immediately
/// whereas the subsequent retries will remain subject to retry interval.
/// </summary>
public bool FastFirstRetry { get; set; }
/// <summary>
/// Gets or sets the timeout in seconds of sql commands
/// </summary>
public int CommandTimeoutInSeconds
{
get;
set;
}
/// <summary>
/// Gets the error detection strategy of this retry policy
/// </summary>
internal IErrorDetectionStrategy ErrorDetectionStrategy
{
get
{
return _errorDetectionStrategy;
}
}
/// <summary>
/// We should only ignore errors if they happen after the first retry.
/// This flag is used to allow the ignore even on first try, for testing purposes.
/// </summary>
/// <remarks>
/// This flag is currently being used for TESTING PURPOSES ONLY.
/// </remarks>
internal bool ShouldIgnoreOnFirstTry
{
get;
set;
}
protected static bool IsLessThanMaxRetryCount(int currentRetryCount, int maxRetryCount)
{
return currentRetryCount <= maxRetryCount;
}
/// <summary>
/// Repetitively executes the specified action while it satisfies the current retry policy.
/// </summary>
/// <param name="action">A delegate representing the executable action which doesn't return any results.</param>
/// <param name="token">Cancellation token to cancel action between retries.</param>
public void ExecuteAction(Action action, CancellationToken? token = null)
{
ExecuteAction(
_ => action(), token);
}
/// <summary>
/// Repetitively executes the specified action while it satisfies the current retry policy.
/// </summary>
/// <param name="action">A delegate representing the executable action which doesn't return any results.</param>
/// <param name="token">Cancellation token to cancel action between retries.</param>
public void ExecuteAction(Action<RetryState> action, CancellationToken? token = null)
{
ExecuteAction<object>(
retryState =>
{
action(retryState);
return null;
}, token);
}
/// <summary>
/// Repetitively executes the specified action while it satisfies the current retry policy.
/// </summary>
/// <typeparam name="T">The type of result expected from the executable action.</typeparam>
/// <param name="func">A delegate representing the executable action which returns the result of type T.</param>
/// <param name="token">Cancellation token to cancel action between retries.</param>
/// <returns>The result from the action.</returns>
public T ExecuteAction<T>(Func<T> func, CancellationToken? token = null)
{
return ExecuteAction(
_ => func(), token);
}
/// <summary>
/// Repetitively executes the specified action while it satisfies the current retry policy.
/// </summary>
/// <typeparam name="R">The type of result expected from the executable action.</typeparam>
/// <param name="func">A delegate representing the executable action which returns the result of type R.</param>
/// <param name="token">Cancellation token to cancel action between retries.</param>
/// <returns>The result from the action.</returns>
public R ExecuteAction<R>(Func<RetryState, R> func, CancellationToken? token = null)
{
RetryState retryState = CreateRetryState();
if (token != null)
{
token.Value.ThrowIfCancellationRequested();
}
while (true)
{
try
{
return func(retryState);
}
catch (RetryLimitExceededException limitExceededEx)
{
// The user code can throw a RetryLimitExceededException to force the exit from the retry loop.
// The RetryLimitExceeded exception can have an inner exception attached to it. This is the exception
// which we will have to throw up the stack so that callers can handle it.
if (limitExceededEx.InnerException != null)
{
throw limitExceededEx.InnerException;
}
return default(R);
}
catch (Exception ex)
{
retryState.LastError = ex;
if (retryState.RetryCount > 0 || this.ShouldIgnoreOnFirstTry)
{
// If we can ignore this error, then break out of the loop and consider this execution as passing
// We return the default value for the type R
if (ShouldIgnoreError(retryState))
{
OnIgnoreErrorOccurred(retryState);
return default(R);
}
}
retryState.RetryCount++;
if (!ShouldRetry(retryState))
{
throw;
}
}
OnRetryOccurred(retryState);
if ((retryState.RetryCount > 1 || !FastFirstRetry) && !retryState.IsDelayDisabled)
{
Thread.Sleep(retryState.Delay);
}
// check for cancellation after delay.
if (token != null)
{
token.Value.ThrowIfCancellationRequested();
}
}
}
protected virtual RetryState CreateRetryState()
{
return new RetryState();
}
public bool IsRetryableException(Exception ex)
{
return ErrorDetectionStrategy.CanRetry(ex);
}
public bool ShouldRetry(RetryState retryState)
{
bool canRetry = ErrorDetectionStrategy.CanRetry(retryState.LastError);
bool shouldRetry = canRetry
&& ShouldRetryImpl(retryState);
Logger.Write(TraceEventType.Error,
string.Format(
CultureInfo.InvariantCulture,
"Retry requested: Retry count = {0}. Delay = {1}, SQL Error Number = {2}, Can retry error = {3}, Will retry = {4}",
retryState.RetryCount,
retryState.Delay,
GetErrorNumber(retryState.LastError),
canRetry,
shouldRetry));
// Perform an extra check in the delay interval. Should prevent from accidentally ending up with the value of -1 which will block a thread indefinitely.
// In addition, any other negative numbers will cause an ArgumentOutOfRangeException fault which will be thrown by Thread.Sleep.
if (retryState.Delay.TotalMilliseconds < 0)
{
retryState.Delay = TimeSpan.Zero;
}
return shouldRetry;
}
public bool ShouldIgnoreError(RetryState retryState)
{
bool shouldIgnoreError = ErrorDetectionStrategy.ShouldIgnoreError(retryState.LastError);
Logger.Write(TraceEventType.Error,
string.Format(
CultureInfo.InvariantCulture,
"Ignore Error requested: Retry count = {0}. Delay = {1}, SQL Error Number = {2}, Should Ignore Error = {3}",
retryState.RetryCount,
retryState.Delay,
GetErrorNumber(retryState.LastError),
shouldIgnoreError));
return shouldIgnoreError;
}
/* TODO - Error code does not exist in SqlException for .NET Core
private static int? GetErrorCode(Exception ex)
{
SqlException sqlEx= ex as SqlException;
if (sqlEx == null)
{
return null;
}
return sqlEx.ErrorCode;
}
*/
internal static int? GetErrorNumber(Exception ex)
{
SqlException sqlEx = ex as SqlException;
if (sqlEx == null)
{
return null;
}
return sqlEx.Number;
}
protected abstract bool ShouldRetryImpl(RetryState retryState);
/// <summary>
/// Notifies the subscribers whenever a retry condition is encountered.
/// </summary>
/// <param name="retryState">The state of current retry attempt.</param>
protected virtual void OnRetryOccurred(RetryState retryState)
{
var retryOccurred = RetryOccurred;
if (retryOccurred != null)
{
retryOccurred(retryState);
}
}
/// <summary>
/// Notifies the subscribers whenever an error is ignored on retry.
/// </summary>
/// <param name="retryState">The state of current retry attempt.</param>
protected virtual void OnIgnoreErrorOccurred(RetryState retryState)
{
var ignoreErrorOccurred = IgnoreErrorOccurred;
if (ignoreErrorOccurred != null)
{
ignoreErrorOccurred(retryState);
}
}
internal class FixedDelayPolicy : RetryPolicy
{
private readonly int _maxRetryCount;
private readonly TimeSpan _intervalBetweenRetries;
/// <summary>
/// Constructs a new instance of the TRetryPolicy class with the specified number of retry attempts and time interval between retries.
/// </summary>
/// <param name="strategy">The <see cref="RetryPolicy.IErrorDetectionStrategy"/> to use when checking whether an error is retryable</param>
/// <param name="maxRetryCount">The max number of retry attempts. Should be 1-indexed.</param>
/// <param name="intervalBetweenRetries">The interval between retries.</param>
public FixedDelayPolicy(IErrorDetectionStrategy strategy, int maxRetryCount, TimeSpan intervalBetweenRetries)
: base(strategy)
{
Contract.Assert(maxRetryCount >= 0, "maxRetryCount cannot be a negative number");
Contract.Assert(intervalBetweenRetries.Ticks >= 0, "intervalBetweenRetries cannot be negative");
_maxRetryCount = maxRetryCount;
_intervalBetweenRetries = intervalBetweenRetries;
}
protected override bool ShouldRetryImpl(RetryState retryState)
{
Contract.Assert(retryState != null);
if (IsLessThanMaxRetryCount(retryState.RetryCount, _maxRetryCount))
{
retryState.Delay = _intervalBetweenRetries;
return true;
}
retryState.Delay = TimeSpan.Zero;
return false;
}
}
internal class ProgressiveRetryPolicy : RetryPolicy
{
private readonly int _maxRetryCount;
private readonly TimeSpan _initialInterval;
private readonly TimeSpan _increment;
/// <summary>
/// Constructs a new instance of the TRetryPolicy class with the specified number of retry attempts and parameters defining the progressive delay between retries.
/// </summary>
/// <param name="strategy">The <see cref="RetryPolicy.IErrorDetectionStrategy"/> to use when checking whether an error is retryable</param>
/// <param name="maxRetryCount">The maximum number of retry attempts. Should be 1-indexed.</param>
/// <param name="initialInterval">The initial interval which will apply for the first retry.</param>
/// <param name="increment">The incremental time value which will be used for calculating the progressive delay between retries.</param>
public ProgressiveRetryPolicy(IErrorDetectionStrategy strategy, int maxRetryCount, TimeSpan initialInterval, TimeSpan increment)
: base(strategy)
{
Contract.Assert(maxRetryCount >= 0, "maxRetryCount cannot be a negative number");
Contract.Assert(initialInterval.Ticks >= 0, "retryInterval cannot be negative");
Contract.Assert(increment.Ticks >= 0, "retryInterval cannot be negative");
_maxRetryCount = maxRetryCount;
_initialInterval = initialInterval;
_increment = increment;
}
protected override bool ShouldRetryImpl(RetryState retryState)
{
Contract.Assert(retryState != null);
if (IsLessThanMaxRetryCount(retryState.RetryCount, _maxRetryCount))
{
retryState.Delay = TimeSpan.FromMilliseconds(_initialInterval.TotalMilliseconds + (_increment.TotalMilliseconds * (retryState.RetryCount - 1)));
return true;
}
retryState.Delay = TimeSpan.Zero;
return false;
}
}
internal class ExponentialDelayRetryPolicy : RetryPolicy
{
private readonly int _maxRetryCount;
private readonly double _intervalFactor;
private readonly TimeSpan _minInterval;
private readonly TimeSpan _maxInterval;
/// <summary>
/// Constructs a new instance of the TRetryPolicy class with the specified number of retry attempts and parameters defining the progressive delay between retries.
/// </summary>
/// <param name="strategy">The <see cref="RetryPolicy.IErrorDetectionStrategy"/> to use when checking whether an error is retryable</param>
/// <param name="maxRetryCount">The maximum number of retry attempts.</param>
/// <param name="intervalFactor">Controls the speed at which the delay increases - the retryCount is raised to this power as
/// part of the function </param>
/// <param name="minInterval">Minimum interval between retries. The basis for all backoff calculations</param>
/// <param name="maxInterval">Maximum interval between retries. Backoff will not take longer than this period.</param>
public ExponentialDelayRetryPolicy(IErrorDetectionStrategy strategy, int maxRetryCount, double intervalFactor, TimeSpan minInterval, TimeSpan maxInterval)
: base(strategy)
{
Contract.Assert(maxRetryCount >= 0, "maxRetryCount cannot be a negative number");
Contract.Assert(intervalFactor > 1, "intervalFactor Must be > 1 so that the delay increases exponentially");
Contract.Assert(minInterval.Ticks >= 0, "minInterval cannot be negative");
Contract.Assert(maxInterval.Ticks >= 0, "maxInterval cannot be negative");
Contract.Assert(maxInterval.Ticks >= minInterval.Ticks, "maxInterval must be greater than minInterval");
_maxRetryCount = maxRetryCount;
_intervalFactor = intervalFactor;
_minInterval = minInterval;
_maxInterval = maxInterval;
}
protected override bool ShouldRetryImpl(RetryState retryState)
{
Contract.Assert(retryState != null);
if (IsLessThanMaxRetryCount(retryState.RetryCount, _maxRetryCount))
{
retryState.Delay = RetryPolicyUtils.CalcExponentialRetryDelay(retryState.RetryCount, _intervalFactor, _minInterval, _maxInterval);
return true;
}
retryState.Delay = TimeSpan.Zero;
return false;
}
}
internal class TimeBasedRetryPolicy : RetryPolicy
{
private readonly TimeSpan _minTotalRetryTimeLimit;
private readonly TimeSpan _maxTotalRetryTimeLimit;
private readonly double _totalRetryTimeLimitRate;
private readonly TimeSpan _minInterval;
private readonly TimeSpan _maxInterval;
private readonly double _intervalFactor;
private readonly Stopwatch _stopwatch;
public TimeBasedRetryPolicy(
IErrorDetectionStrategy strategy,
TimeSpan minTotalRetryTimeLimit,
TimeSpan maxTotalRetryTimeLimit,
double totalRetryTimeLimitRate,
TimeSpan minInterval,
TimeSpan maxInterval,
double intervalFactor)
: base(strategy)
{
Contract.Assert(minTotalRetryTimeLimit.Ticks >= 0);
Contract.Assert(maxTotalRetryTimeLimit.Ticks >= minTotalRetryTimeLimit.Ticks);
Contract.Assert(totalRetryTimeLimitRate >= 0);
Contract.Assert(minInterval.Ticks >= 0);
Contract.Assert(maxInterval.Ticks >= minInterval.Ticks);
Contract.Assert(intervalFactor >= 1);
_minTotalRetryTimeLimit = minTotalRetryTimeLimit;
_maxTotalRetryTimeLimit = maxTotalRetryTimeLimit;
_totalRetryTimeLimitRate = totalRetryTimeLimitRate;
_minInterval = minInterval;
_maxInterval = maxInterval;
_intervalFactor = intervalFactor;
_stopwatch = Stopwatch.StartNew();
}
protected override bool ShouldRetryImpl(RetryState retryStateObj)
{
Contract.Assert(retryStateObj is RetryStateEx);
RetryStateEx retryState = (RetryStateEx)retryStateObj;
// Calculate the delay as exponential value based on the number of retries.
retryState.Delay =
RetryPolicyUtils.CalcExponentialRetryDelay(
retryState.RetryCount,
_intervalFactor,
_minInterval,
_maxInterval);
// Add the delay to the total retry time
retryState.TotalRetryTime = retryState.TotalRetryTime + retryState.Delay;
// Calculate the maximum total retry time depending on how long ago was the task (this retry policy) started.
// Longer running tasks are less eager to abort since, more work is has been done.
TimeSpan totalRetryTimeLimit = checked(TimeSpan.FromMilliseconds(
Math.Max(
Math.Min(
_stopwatch.ElapsedMilliseconds * _totalRetryTimeLimitRate,
_maxTotalRetryTimeLimit.TotalMilliseconds),
_minTotalRetryTimeLimit.TotalMilliseconds)));
if (retryState.TotalRetryTime <= totalRetryTimeLimit)
{
return true;
}
retryState.Delay = TimeSpan.Zero;
return false;
}
protected override RetryState CreateRetryState()
{
return new RetryStateEx { TotalRetryTime = TimeSpan.Zero };
}
internal sealed class RetryStateEx : RetryState
{
public TimeSpan TotalRetryTime { get; set; }
}
}
}
}

View File

@@ -0,0 +1,459 @@
//
// 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.Diagnostics;
using System.Globalization;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal sealed class RetryPolicyDefaults
{
/// <summary>
/// The default number of retry attempts.
/// </summary>
public const int DefaulSchemaRetryCount = 6;
/// <summary>
/// The default number of retry attempts for create database.
/// </summary>
public const int DefaultCreateDatabaseRetryCount = 5;
/// <summary>
/// The default amount of time defining an interval between retries.
/// </summary>
public static readonly TimeSpan DefaultSchemaMinInterval = TimeSpan.FromSeconds(2.75);
/// <summary>
/// The default factor to use when determining exponential backoff between retries.
/// </summary>
public const double DefaultBackoffIntervalFactor = 2.0;
/// <summary>
/// The default maximum time between retries.
/// </summary>
public static readonly TimeSpan DefaultMaxRetryInterval = TimeSpan.FromSeconds(60);
/// <summary>
/// The default number of retry attempts.
/// </summary>
public static readonly int DefaultDataCommandRetryCount = 5;
/// <summary>
/// The default number of retry attempts for a connection related error
/// </summary>
public static readonly int DefaultConnectionRetryCount = 6;
/// <summary>
/// The default amount of time defining an interval between retries.
/// </summary>
public static readonly TimeSpan DefaultDataMinInterval = TimeSpan.FromSeconds(1.0);
/// <summary>
/// The default amount of time defining a time increment between retry attempts in the progressive delay policy.
/// </summary>
public static readonly TimeSpan DefaultProgressiveRetryIncrement = TimeSpan.FromMilliseconds(500);
}
/// <summary>
/// Implements a collection of the RetryPolicyInfo elements holding retry policy settings.
/// </summary>
internal sealed class RetryPolicyFactory
{
/// <summary>
/// Returns a default policy that does no retries, it just invokes action exactly once.
/// </summary>
public static readonly RetryPolicy NoRetryPolicy = RetryPolicyFactory.CreateNoRetryPolicy();
/// <summary>
/// Returns a default policy that does no retries, it just invokes action exactly once.
/// </summary>
public static readonly RetryPolicy PrimaryKeyViolationRetryPolicy = RetryPolicyFactory.CreatePrimaryKeyCommandRetryPolicy();
/// <summary>
/// Implements a strategy that ignores any transient errors.
/// Internal for testing purposes only
/// </summary>
internal sealed class TransientErrorIgnoreStrategy : RetryPolicy.IErrorDetectionStrategy
{
private static readonly TransientErrorIgnoreStrategy _instance = new TransientErrorIgnoreStrategy();
public static TransientErrorIgnoreStrategy Instance
{
get { return _instance; }
}
public bool CanRetry(Exception ex)
{
return false;
}
public bool ShouldIgnoreError(Exception ex)
{
return false;
}
}
/// <summary>
/// Creates and returns a default Retry Policy for Schema based operations.
/// </summary>
/// <returns>An instance of <see cref="RetryPolicy"/> class.</returns>
internal static RetryPolicy CreateDefaultSchemaCommandRetryPolicy(bool useRetry, int retriesPerPhase = RetryPolicyDefaults.DefaulSchemaRetryCount)
{
RetryPolicy policy;
if (useRetry)
{
policy = new RetryPolicy.ExponentialDelayRetryPolicy(
RetryPolicy.SqlAzureTemporaryErrorDetectionStrategy.Instance,
retriesPerPhase,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
policy.FastFirstRetry = false;
}
else
{
policy = CreateNoRetryPolicy();
}
return policy;
}
/// <summary>
/// Creates and returns a default Retry Policy for Schema based connection operations.
/// </summary>
/// <remarks>The RetryOccured event is wired to raise an RaiseAmbientRetryMessage message for a connection retry. </remarks>
/// <returns>An instance of <see cref="RetryPolicy"/> class.</returns>
internal static RetryPolicy CreateSchemaConnectionRetryPolicy(int retriesPerPhase)
{
RetryPolicy policy = new RetryPolicy.ExponentialDelayRetryPolicy(
RetryPolicy.SqlAzureTemporaryErrorDetectionStrategy.Instance,
retriesPerPhase,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
policy.RetryOccurred += DataConnectionFailureRetry;
return policy;
}
/// <summary>
/// Creates and returns a default Retry Policy for Schema based command operations.
/// </summary>
/// <remarks>The RetryOccured event is wired to raise an RaiseAmbientRetryMessage message for a command retry. </remarks>
/// <returns>An instance of <see cref="RetryPolicy"/> class.</returns>
internal static RetryPolicy CreateSchemaCommandRetryPolicy(int retriesPerPhase)
{
RetryPolicy policy = new RetryPolicy.ExponentialDelayRetryPolicy(
RetryPolicy.SqlAzureTemporaryErrorDetectionStrategy.Instance,
retriesPerPhase,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
policy.FastFirstRetry = false;
policy.RetryOccurred += CommandFailureRetry;
return policy;
}
/// <summary>
/// Creates and returns a Retry Policy for database creation operations.
/// </summary>
/// <param name="ignorableErrorNumbers">Errors to ignore if they occur after first retry</param>
/// <remarks>
/// The RetryOccured event is wired to raise an RaiseAmbientRetryMessage message for a command retry.
/// The IgnoreErrorOccurred event is wired to raise an RaiseAmbientIgnoreMessage message for ignore.
/// </remarks>
/// <returns>An instance of <see cref="RetryPolicy"/> class.</returns>
internal static RetryPolicy CreateDatabaseCommandRetryPolicy(params int[] ignorableErrorNumbers)
{
RetryPolicy.SqlAzureTemporaryAndIgnorableErrorDetectionStrategy errorDetectionStrategy =
new RetryPolicy.SqlAzureTemporaryAndIgnorableErrorDetectionStrategy(ignorableErrorNumbers);
// 30, 60, 60, 60, 60 second retries
RetryPolicy policy = new RetryPolicy.ExponentialDelayRetryPolicy(
errorDetectionStrategy,
RetryPolicyDefaults.DefaultCreateDatabaseRetryCount /* maxRetryCount */,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
TimeSpan.FromSeconds(30) /* minInterval */,
TimeSpan.FromSeconds(60) /* maxInterval */);
policy.FastFirstRetry = false;
policy.RetryOccurred += CreateDatabaseCommandFailureRetry;
policy.IgnoreErrorOccurred += CreateDatabaseCommandFailureIgnore;
return policy;
}
/// <summary>
/// Creates and returns an "ignoreable" command Retry Policy.
/// </summary>
/// <param name="ignorableErrorNumbers">Errors to ignore if they occur after first retry</param>
/// <remarks>
/// The RetryOccured event is wired to raise an RaiseAmbientRetryMessage message for a command retry.
/// The IgnoreErrorOccurred event is wired to raise an RaiseAmbientIgnoreMessage message for ignore.
/// </remarks>
/// <returns>An instance of <see cref="RetryPolicy"/> class.</returns>
internal static RetryPolicy CreateElementCommandRetryPolicy(params int[] ignorableErrorNumbers)
{
Debug.Assert(ignorableErrorNumbers != null);
RetryPolicy.SqlAzureTemporaryAndIgnorableErrorDetectionStrategy errorDetectionStrategy =
new RetryPolicy.SqlAzureTemporaryAndIgnorableErrorDetectionStrategy(ignorableErrorNumbers);
RetryPolicy policy = new RetryPolicy.ExponentialDelayRetryPolicy(
errorDetectionStrategy,
RetryPolicyDefaults.DefaulSchemaRetryCount,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
policy.FastFirstRetry = false;
policy.RetryOccurred += ElementCommandFailureRetry;
policy.IgnoreErrorOccurred += ElementCommandFailureIgnore;
return policy;
}
/// <summary>
/// Creates and returns an "primary key violation" command Retry Policy.
/// </summary>
/// <param name="ignorableErrorNumbers">Errors to ignore if they occur after first retry</param>
/// <remarks>
/// The RetryOccured event is wired to raise an RaiseAmbientRetryMessage message for a command retry.
/// The IgnoreErrorOccurred event is wired to raise an RaiseAmbientIgnoreMessage message for ignore.
/// </remarks>
/// <returns>An instance of <see cref="RetryPolicy"/> class.</returns>
internal static RetryPolicy CreatePrimaryKeyCommandRetryPolicy()
{
RetryPolicy.SqlAzureTemporaryAndIgnorableErrorDetectionStrategy errorDetectionStrategy =
new RetryPolicy.SqlAzureTemporaryAndIgnorableErrorDetectionStrategy(SqlErrorNumbers.PrimaryKeyViolationErrorNumber);
RetryPolicy policy = new RetryPolicy.ExponentialDelayRetryPolicy(
errorDetectionStrategy,
RetryPolicyDefaults.DefaulSchemaRetryCount,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
policy.FastFirstRetry = true;
policy.RetryOccurred += CommandFailureRetry;
policy.IgnoreErrorOccurred += CommandFailureIgnore;
return policy;
}
/// <summary>
/// Creates a Policy that will never allow retries to occur.
/// </summary>
/// <returns></returns>
public static RetryPolicy CreateNoRetryPolicy()
{
return new RetryPolicy.FixedDelayPolicy(TransientErrorIgnoreStrategy.Instance, 0, TimeSpan.Zero);
}
/// <summary>
/// Creates a Policy that is optimized for data-related script update operations.
/// This is extremely error tolerant and uses a Time based delay policy that backs
/// off until some overall length of delay has occurred. It is not as long-running
/// as the ConnectionManager data transfer retry policy since that's intended for bulk upload
/// of large amounts of data, whereas this is for individual batch scripts executed by the
/// batch execution engine.
/// </summary>
/// <returns></returns>
public static RetryPolicy CreateDataScriptUpdateRetryPolicy()
{
return new RetryPolicy.TimeBasedRetryPolicy(
RetryPolicy.DataTransferErrorDetectionStrategy.Instance,
TimeSpan.FromMinutes(7),
TimeSpan.FromMinutes(7),
0.1,
TimeSpan.FromMilliseconds(250),
TimeSpan.FromSeconds(30),
1.5);
}
/// <summary>
/// Returns the default retry policy dedicated to handling exceptions with SQL connections
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateFastDataRetryPolicy()
{
RetryPolicy retryPolicy = new RetryPolicy.FixedDelayPolicy(
RetryPolicy.NetworkConnectivityErrorDetectionStrategy.Instance,
RetryPolicyDefaults.DefaultDataCommandRetryCount,
TimeSpan.FromMilliseconds(5));
retryPolicy.FastFirstRetry = true;
retryPolicy.RetryOccurred += DataConnectionFailureRetry;
return retryPolicy;
}
/// <summary>
/// Returns the default retry policy dedicated to handling exceptions with SQL connections.
/// No logging or other message handler is attached to the policy
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateDefaultSchemaConnectionRetryPolicy()
{
return CreateDefaultConnectionRetryPolicy();
}
/// <summary>
/// Returns the default retry policy dedicated to handling exceptions with SQL connections.
/// Adds an event handler to log and notify listeners of data connection retries
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateDefaultDataConnectionRetryPolicy()
{
RetryPolicy retryPolicy = CreateDefaultConnectionRetryPolicy();
retryPolicy.RetryOccurred += DataConnectionFailureRetry;
return retryPolicy;
}
/// <summary>
/// Returns the default retry policy dedicated to handling exceptions with SQL connections
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateDefaultConnectionRetryPolicy()
{
// Note: No longer use Ado.net Connection Pooling and hence do not need TimeBasedRetryPolicy to
// conform to the backoff requirements in this case
RetryPolicy retryPolicy = new RetryPolicy.ExponentialDelayRetryPolicy(
RetryPolicy.NetworkConnectivityErrorDetectionStrategy.Instance,
RetryPolicyDefaults.DefaultConnectionRetryCount,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
retryPolicy.FastFirstRetry = true;
return retryPolicy;
}
/// <summary>
/// Returns the default retry policy dedicated to handling retryable conditions with data transfer SQL commands.
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateDefaultDataSqlCommandRetryPolicy()
{
RetryPolicy retryPolicy = new RetryPolicy.ExponentialDelayRetryPolicy(
RetryPolicy.SqlAzureTemporaryErrorDetectionStrategy.Instance,
RetryPolicyDefaults.DefaultDataCommandRetryCount,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultDataMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
retryPolicy.FastFirstRetry = true;
retryPolicy.RetryOccurred += CommandFailureRetry;
return retryPolicy;
}
/// <summary>
/// Returns the default retry policy dedicated to handling retryable conditions with data transfer SQL commands.
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateDefaultDataTransferRetryPolicy()
{
RetryPolicy retryPolicy = new RetryPolicy.TimeBasedRetryPolicy(
RetryPolicy.DataTransferErrorDetectionStrategy.Instance,
TimeSpan.FromMinutes(20),
TimeSpan.FromMinutes(240),
0.1,
TimeSpan.FromMilliseconds(250),
TimeSpan.FromMinutes(2),
2);
retryPolicy.FastFirstRetry = true;
retryPolicy.RetryOccurred += CommandFailureRetry;
return retryPolicy;
}
/// <summary>
/// Returns the retry policy to handle data migration for column encryption.
/// </summary>
/// <returns>The RetryPolicy policy</returns>
public static RetryPolicy CreateColumnEncryptionTransferRetryPolicy()
{
RetryPolicy retryPolicy = new RetryPolicy.TimeBasedRetryPolicy(
RetryPolicy.DataTransferErrorDetectionStrategy.Instance,
TimeSpan.FromMinutes(5),
TimeSpan.FromMinutes(5),
0.1,
TimeSpan.FromMilliseconds(250),
TimeSpan.FromMinutes(2),
2);
retryPolicy.FastFirstRetry = true;
retryPolicy.RetryOccurred += CommandFailureRetry;
return retryPolicy;
}
internal static void DataConnectionFailureRetry(RetryState retryState)
{
Logger.Write(TraceEventType.Information, string.Format(CultureInfo.InvariantCulture,
"Connection retry number {0}. Delaying {1} ms before retry. Exception: {2}",
retryState.RetryCount,
retryState.Delay.TotalMilliseconds.ToString(CultureInfo.InvariantCulture),
retryState.LastError.ToString()));
RetryPolicyUtils.RaiseAmbientRetryMessage(retryState, SqlSchemaModelErrorCodes.ServiceActions.ConnectionRetry);
}
internal static void CommandFailureRetry(RetryState retryState, string commandKeyword)
{
Logger.Write(TraceEventType.Information, string.Format(
CultureInfo.InvariantCulture,
"{0} retry number {1}. Delaying {2} ms before retry. Exception: {3}",
commandKeyword,
retryState.RetryCount,
retryState.Delay.TotalMilliseconds.ToString(CultureInfo.InvariantCulture),
retryState.LastError.ToString()));
RetryPolicyUtils.RaiseAmbientRetryMessage(retryState, SqlSchemaModelErrorCodes.ServiceActions.CommandRetry);
}
internal static void CommandFailureIgnore(RetryState retryState, string commandKeyword)
{
Logger.Write(TraceEventType.Information, string.Format(
CultureInfo.InvariantCulture,
"{0} retry number {1}. Ignoring failure. Exception: {2}",
commandKeyword,
retryState.RetryCount,
retryState.LastError.ToString()));
RetryPolicyUtils.RaiseAmbientIgnoreMessage(retryState, SqlSchemaModelErrorCodes.ServiceActions.CommandRetry);
}
internal static void CommandFailureRetry(RetryState retryState)
{
CommandFailureRetry(retryState, "Command");
}
internal static void CommandFailureIgnore(RetryState retryState)
{
CommandFailureIgnore(retryState, "Command");
}
internal static void CreateDatabaseCommandFailureRetry(RetryState retryState)
{
CommandFailureRetry(retryState, "Database Command");
}
internal static void CreateDatabaseCommandFailureIgnore(RetryState retryState)
{
CommandFailureIgnore(retryState, "Database Command");
}
internal static void ElementCommandFailureRetry(RetryState retryState)
{
CommandFailureRetry(retryState, "Element Command");
}
internal static void ElementCommandFailureIgnore(RetryState retryState)
{
CommandFailureIgnore(retryState, "Element Command");
}
}
}

View File

@@ -0,0 +1,486 @@
//
// 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.Generic;
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
internal static class RetryPolicyUtils
{
/// <summary>
/// Approved list of transient errors that should be retryable during Network connection stages
/// </summary>
private static readonly HashSet<int> _retryableNetworkConnectivityErrors;
/// <summary>
/// Approved list of transient errors that should be retryable on Azure
/// </summary>
private static readonly HashSet<int> _retryableAzureErrors;
/// <summary>
/// Blocklist of non-transient errors that should stop retry during data transfer operations
/// </summary>
private static readonly HashSet<int> _nonRetryableDataTransferErrors;
static RetryPolicyUtils()
{
_retryableNetworkConnectivityErrors = new HashSet<int>
{
/// A severe error occurred on the current command. The results, if any, should be discarded.
0,
//// DBNETLIB Error Code: 20
//// The instance of SQL Server you attempted to connect to does not support encryption.
(int) ProcessNetLibErrorCode.EncryptionNotSupported,
//// DBNETLIB Error Code: -2
//// Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
(int)ProcessNetLibErrorCode.Timeout,
//// SQL Error Code: 64
//// A connection was successfully established with the server, but then an error occurred during the login process.
//// (provider: TCP Provider, error: 0 - The specified network name is no longer available.)
64,
//// SQL Error Code: 233
//// The client was unable to establish a connection because of an error during connection initialization process before login.
//// Possible causes include the following: the client tried to connect to an unsupported version of SQL Server; the server was too busy
//// to accept new connections; or there was a resource limitation (insufficient memory or maximum allowed connections) on the server.
//// (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
233,
//// SQL Error Code: 10053
//// A transport-level error has occurred when receiving results from the server.
//// An established connection was aborted by the software in your host machine.
10053,
//// SQL Error Code: 10054
//// A transport-level error has occurred when sending the request to the server.
//// (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
10054,
//// SQL Error Code: 10060
//// A network-related or instance-specific error occurred while establishing a connection to SQL Server.
//// The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server
//// is configured to allow remote connections. (provider: TCP Provider, error: 0 - A connection attempt failed
//// because the connected party did not properly respond after a period of time, or established connection failed
//// because connected host has failed to respond.)"}
10060,
// SQL Error Code: 11001
// A network-related or instance-specific error occurred while establishing a connection to SQL Server.
// The server was not found or was not accessible. Verify that the instance name is correct and that SQL
// Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - No such host is known.)
11001,
//// SQL Error Code: 40613
//// Database XXXX on server YYYY is not currently available. Please retry the connection later. If the problem persists, contact customer
//// support, and provide them the session tracing ID of ZZZZZ.
40613,
};
_retryableAzureErrors = new HashSet<int>
{
//// SQL Error Code: 40
//// Could not open a connection to SQL Server
//// (provider: Named Pipes Provider, error: 40 Could not open a connection to SQL Server)
40,
//// SQL Error Code: 121
//// A transport-level error has occurred when receiving results from the server.
//// (provider: TCP Provider, error: 0 - The semaphore timeout period has expired.)
121,
//// SQL Error Code: 913 (noticed intermittently on SNAP runs with connected unit tests)
//// Could not find database ID %d. Database may not be activated yet or may be in transition. Reissue the query once the database is available.
//// If you do not think this error is due to a database that is transitioning its state and this error continues to occur, contact your primary support provider.
//// Please have available for review the Microsoft SQL Server error log and any additional information relevant to the circumstances when the error occurred.
913,
//// SQL Error Code: 1205
//// Transaction (Process ID %d) was deadlocked on %.*ls resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
1205,
//// SQL Error Code: 40501
//// The service is currently busy. Retry the request after 10 seconds. Code: (reason code to be decoded).
RetryPolicy.ThrottlingReason.ThrottlingErrorNumber,
//// SQL Error Code: 10928
//// Resource ID: %d. The %s limit for the database is %d and has been reached.
10928,
//// SQL Error Code: 10929
//// Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current usage for the database is %d.
//// However, the server is currently too busy to support requests greater than %d for this database.
10929,
//// SQL Error Code: 40143
//// The service has encountered an error processing your request. Please try again.
40143,
//// SQL Error Code: 40197
//// The service has encountered an error processing your request. Please try again.
40197,
//// Sql Error Code: 40549 (not supposed to be used anymore as of Q2 2011)
//// Session is terminated because you have a long-running transaction. Try shortening your transaction.
40549,
//// Sql Error Code: 40550 (not supposed to be used anymore as of Q2 2011)
//// The session has been terminated because it has acquired too many locks. Try reading or modifying fewer rows in a single transaction.
40550,
//// Sql Error Code: 40551 (not supposed to be used anymore as of Q2 2011)
//// The session has been terminated because of excessive TEMPDB usage. Try modifying your query to reduce the temporary table space usage.
40551,
//// Sql Error Code: 40552 (not supposed to be used anymore as of Q2 2011)
//// The session has been terminated because of excessive transaction log space usage. Try modifying fewer rows in a single transaction.
40552,
//// Sql Error Code: 40553 (not supposed to be used anymore as of Q2 2011)
//// The session has been terminated because of excessive memory usage. Try modifying your query to process fewer rows.
40553,
//// SQL Error Code: 40627
//// Operation on server YYY and database XXX is in progress. Please wait a few minutes before trying again.
40627,
//// SQL Error Code: 40671 (DB CRUD)
//// Unable to '%.*ls' '%.*ls' on server '%.*ls'. Please retry the connection later.
40671,
//// SQL Error Code: 40676 (DB CRUD)
//// '%.*ls' request was received but may not be processed completely at this time,
//// please query the sys.dm_operation_status table in the master database for status.
40676,
//// SQL Error Code: 45133
//// A connection failed while the operation was still in progress, and the outcome of the operation is unknown.
45133,
};
foreach(int errorNum in _retryableNetworkConnectivityErrors)
{
_retryableAzureErrors.Add(errorNum);
}
_nonRetryableDataTransferErrors = new HashSet<int>
{
//// Syntax error
156,
//// Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'. The duplicate key value is %ls.
2601,
//// Violation of %ls constraint '%.*ls'. Cannot insert duplicate key in object '%.*ls'. The duplicate key value is %ls.
2627,
//// Cannot find index '%.*ls'.
2727,
//// SqlClr stack error
6522,
//// Divide by zero error encountered.
8134,
//// Could not repair this error.
8922,
//// Bug 1110540: This error means the table is corrupted due to hardware failure, so we do not want to retry.
//// Table error: Object ID %d. The text, ntext, or image node at page %S_PGID, slot %d, text ID %I64d is referenced by page %S_PGID, slot %d, but was not seen in the scan.
8965,
//// The query processor is unable to produce a plan because the clustered index is disabled.
8655,
//// The query processor is unable to produce a plan because table is unavailable because the heap is corrupted
8674,
//// SqlClr permission / load error.
//// Example Message: An error occurred in the Microsoft .NET Framework while trying to load assembly
10314,
//// '%ls' is not supported in this version of SQL Server.
40514,
//// The database 'XYZ' has reached its size quota. Partition or delete data, drop indexes, or consult the documentation for possible resolutions
40544,
};
}
public static bool IsRetryableNetworkConnectivityError(int errorNumber)
{
// .NET core has a bug on OSX/Linux that makes this error number always zero (issue 12472)
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return errorNumber != 0 && _retryableNetworkConnectivityErrors.Contains(errorNumber);
}
return _retryableNetworkConnectivityErrors.Contains(errorNumber);
}
public static bool IsRetryableAzureError(int errorNumber)
{
return _retryableAzureErrors.Contains(errorNumber) || _retryableNetworkConnectivityErrors.Contains(errorNumber);
}
public static bool IsNonRetryableDataTransferError(int errorNumber)
{
return _nonRetryableDataTransferErrors.Contains(errorNumber);
}
public static void AppendThrottlingDataIfIsThrottlingError(SqlException sqlException, SqlError error)
{
//// SQL Error Code: 40501
//// The service is currently busy. Retry the request after 10 seconds. Code: (reason code to be decoded).
if(error.Number == RetryPolicy.ThrottlingReason.ThrottlingErrorNumber)
{
// Decode the reason code from the error message to determine the grounds for throttling.
var condition = RetryPolicy.ThrottlingReason.FromError(error);
// Attach the decoded values as additional attributes to the original SQL exception.
sqlException.Data[condition.ThrottlingMode.GetType().Name] = condition.ThrottlingMode.ToString();
sqlException.Data[condition.GetType().Name] = condition;
}
}
/// <summary>
/// Calculates the length of time to delay a retry based on the number of retries up to this point.
/// As the number of retries increases, the timeout increases exponentially based on the intervalFactor.
/// Uses default values for the intervalFactor (<see cref="RetryPolicyDefaults.DefaultBackoffIntervalFactor"/>), minInterval
/// (<see cref="RetryPolicyDefaults.DefaultSchemaMinInterval"/>) and maxInterval (<see cref="RetryPolicyDefaults.DefaultMaxRetryInterval"/>)
/// </summary>
/// <param name="currentRetryCount">Total number of retries including the current retry</param>
/// <returns>TimeSpan defining the length of time to delay</returns>
internal static TimeSpan CalcExponentialRetryDelayWithSchemaDefaults(int currentRetryCount)
{
return CalcExponentialRetryDelay(currentRetryCount,
RetryPolicyDefaults.DefaultBackoffIntervalFactor,
RetryPolicyDefaults.DefaultSchemaMinInterval,
RetryPolicyDefaults.DefaultMaxRetryInterval);
}
/// <summary>
/// Calculates the length of time to delay a retry based on the number of retries up to this point.
/// As the number of retries increases, the timeout increases exponentially based on the intervalFactor.
/// A very large retry count can cause huge delay, so the maxInterval is used to cap delay time at a sensible
/// upper bound
/// </summary>
/// <param name="currentRetryCount">Total number of retries including the current retry</param>
/// <param name="intervalFactor">Controls the speed at which the delay increases - the retryCount is raised to this power as
/// part of the function </param>
/// <param name="minInterval">Minimum interval between retries. The basis for all backoff calculations</param>
/// <param name="maxInterval">Maximum interval between retries. Backoff will not take longer than this period.</param>
/// <returns>TimeSpan defining the length of time to delay</returns>
internal static TimeSpan CalcExponentialRetryDelay(int currentRetryCount, double intervalFactor, TimeSpan minInterval, TimeSpan maxInterval)
{
try
{
return checked(TimeSpan.FromMilliseconds(
Math.Max(
Math.Min(
Math.Pow(intervalFactor, currentRetryCount - 1) * minInterval.TotalMilliseconds,
maxInterval.TotalMilliseconds
),
minInterval.TotalMilliseconds)
));
}
catch (OverflowException)
{
// If numbers are too large, could conceivably overflow the double.
// Since the maxInterval is the largest TimeSpan expected, can safely return this here
return maxInterval;
}
}
internal static void RaiseAmbientRetryMessage(RetryState retryState, int errorCode)
{
Action<SqlServerRetryError> retryMsgHandler = AmbientSettings.ConnectionRetryMessageHandler;
if (retryMsgHandler != null)
{
string msg = SqlServerRetryError.FormatRetryMessage(
retryState.RetryCount,
retryState.Delay,
retryState.LastError);
retryMsgHandler(new SqlServerRetryError(
msg,
retryState.LastError,
retryState.RetryCount,
errorCode,
ErrorSeverity.Warning));
}
}
internal static void RaiseAmbientIgnoreMessage(RetryState retryState, int errorCode)
{
Action<SqlServerRetryError> retryMsgHandler = AmbientSettings.ConnectionRetryMessageHandler;
if (retryMsgHandler != null)
{
string msg = SqlServerRetryError.FormatIgnoreMessage(
retryState.RetryCount,
retryState.LastError);
retryMsgHandler(new SqlServerRetryError(
msg,
retryState.LastError,
retryState.RetryCount,
errorCode,
ErrorSeverity.Warning));
}
}
/// <summary>
/// Traces the Schema retry information before raising the retry message
/// </summary>
/// <param name="retryState"></param>
/// <param name="errorCode"></param>
/// <param name="azureSessionId"></param>
internal static void RaiseSchemaAmbientRetryMessage(RetryState retryState, int errorCode, Guid azureSessionId)
{
if (azureSessionId != Guid.Empty)
{
Logger.Write(TraceEventType.Warning, string.Format(
"Retry occurred: session: {0}; attempt - {1}; delay - {2}; exception - \"{3}\"",
azureSessionId,
retryState.RetryCount,
retryState.Delay,
retryState.LastError
));
RaiseAmbientRetryMessage(retryState, errorCode);
}
}
#region ProcessNetLibErrorCode enumeration
/// <summary>
/// Error codes reported by the DBNETLIB module.
/// </summary>
internal enum ProcessNetLibErrorCode
{
/// <summary>
/// Zero bytes were returned
/// </summary>
ZeroBytes = -3,
/// <summary>
/// Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
/// </summary>
Timeout = -2,
/// <summary>
/// An unknown net lib error
/// </summary>
Unknown = -1,
/// <summary>
/// Out of memory
/// </summary>
InsufficientMemory = 1,
/// <summary>
/// User or machine level access denied
/// </summary>
AccessDenied = 2,
/// <summary>
/// Connection was already busy processing another request
/// </summary>
ConnectionBusy = 3,
/// <summary>
/// The connection was broken without a proper disconnect
/// </summary>
ConnectionBroken = 4,
/// <summary>
/// The connection has reached a limit
/// </summary>
ConnectionLimit = 5,
/// <summary>
/// Name resolution failed for the given server name
/// </summary>
ServerNotFound = 6,
/// <summary>
/// Network transport could not be found
/// </summary>
NetworkNotFound = 7,
/// <summary>
/// A resource required could not be allocated
/// </summary>
InsufficientResources = 8,
/// <summary>
/// Network stack denied the request as too busy
/// </summary>
NetworkBusy = 9,
/// <summary>
/// Unable to access the requested network
/// </summary>
NetworkAccessDenied = 10,
/// <summary>
/// Internal error
/// </summary>
GeneralError = 11,
/// <summary>
/// The network mode was set incorrectly
/// </summary>
IncorrectMode = 12,
/// <summary>
/// The given name was not found
/// </summary>
NameNotFound = 13,
/// <summary>
/// Connection was invalid
/// </summary>
InvalidConnection = 14,
/// <summary>
/// A read or write error occurred
/// </summary>
ReadWriteError = 15,
/// <summary>
/// Unable to allocate an additional handle
/// </summary>
TooManyHandles = 16,
/// <summary>
/// The server reported an error
/// </summary>
ServerError = 17,
/// <summary>
/// SSL failed
/// </summary>
SSLError = 18,
/// <summary>
/// Encryption failed with an error
/// </summary>
EncryptionError = 19,
/// <summary>
/// Remote endpoint does not support encryption
/// </summary>
EncryptionNotSupported = 20
}
#endregion
}
}

View File

@@ -0,0 +1,86 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
internal class RetryState
{
private int _retryCount = 0;
private TimeSpan _delay = TimeSpan.Zero;
private Exception _lastError = null;
private bool _isDelayDisabled = false;
/// <summary>
/// Gets or sets the current retry attempt count.
/// </summary>
public int RetryCount
{
get
{
return _retryCount;
}
set
{
_retryCount = value;
}
}
/// <summary>
/// Gets or sets the delay indicating how long the current thread will be suspended for before the next iteration will be invoked.
/// </summary>
public TimeSpan Delay
{
get
{
return _delay;
}
set
{
_delay = value;
}
}
/// <summary>
/// Gets or sets the exception which caused the retry conditions to occur.
/// </summary>
public Exception LastError
{
get
{
return _lastError;
}
set
{
_lastError = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether we should ignore delay in order to be able to execute our tests faster
/// </summary>
/// <remarks>Intended for test use ONLY</remarks>
internal bool IsDelayDisabled
{
get
{
return _isDelayDisabled;
}
set
{
_isDelayDisabled = value;
}
}
public virtual void Reset()
{
this.IsDelayDisabled = false;
this.RetryCount = 0;
this.Delay = TimeSpan.Zero;
this.LastError = null;
}
}
}

View File

@@ -0,0 +1,51 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
static class SqlConnectionHelperScripts
{
public const string EngineEdition = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WITH (NOLOCK) WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT)";
public const string EngineEditionWithLock = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), SERVERPROPERTY ('MachineName'), (SELECT CASE WHEN EXISTS (SELECT TOP 1 1 from [sys].[all_columns] WHERE name = N'xml_index_type' AND OBJECT_ID(N'sys.xml_indexes') = object_id) THEN 1 ELSE 0 END AS SXI_PRESENT)";
public const string CheckDatabaseReadonly = @"EXEC sp_dboption '{0}', 'read only'";
public const string GetDatabaseFilePathAndName = @"
DECLARE @filepath nvarchar(260),
@rc int
EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',N'Software\Microsoft\MSSQLServer\MSSQLServer',N'DefaultData', @filepath output, 'no_output'
IF ((@filepath IS NOT NULL) AND (CHARINDEX(N'\', @filepath, len(@filepath)) = 0))
SELECT @filepath = @filepath + N'\'
IF (@filepath IS NULL)
SELECT @filepath = [sdf].[physical_name]
FROM [master].[sys].[database_files] AS [sdf]
WHERE [file_id] = 1
SELECT @filepath AS FilePath
";
public const string GetDatabaseLogPathAndName = @"
DECLARE @filepath nvarchar(260),
@rc int
EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',N'Software\Microsoft\MSSQLServer\MSSQLServer',N'DefaultLog', @filepath output, 'no_output'
IF ((@filepath IS NOT NULL) AND (CHARINDEX(N'\', @filepath, len(@filepath)) = 0))
SELECT @filepath = @filepath + N'\'
IF (@filepath IS NULL)
SELECT @filepath = [ldf].[physical_name]
FROM [master].[sys].[database_files] AS [ldf]
WHERE [file_id] = 2
SELECT @filepath AS FilePath
";
public const string GetOsVersion = @"SELECT OSVersion = RIGHT(@@version, LEN(@@version)- 3 -charindex (' on ', LOWER(@@version)))";
}
}

View File

@@ -0,0 +1,32 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Constants for SQL Error numbers
/// </summary>
internal static class SqlErrorNumbers
{
// Database XYZ already exists. Choose a different database name.
internal const int DatabaseAlreadyExistsErrorNumber = 1801;
// Cannot drop the database 'x', because it does not exist or you do not have permission.
internal const int DatabaseAlreadyDroppedErrorNumber = 3701;
// Database 'x' was created\altered successfully, but some properties could not be displayed.
internal const int DatabaseCrudMetadataUpdateErrorNumber = 45166;
// Violation of PRIMARY KEY constraint 'x'.
// Cannot insert duplicate key in object 'y'. The duplicate key value is (z).
internal const int PrimaryKeyViolationErrorNumber = 2627;
// There is already an object named 'x' in the database.
internal const int ObjectAlreadyExistsErrorNumber = 2714;
// Cannot drop the object 'x', because it does not exist or you do not have permission.
internal const int ObjectAlreadyDroppedErrorNumber = 3701;
}
}

View File

@@ -0,0 +1,465 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
internal static class SqlSchemaModelErrorCodes
{
private const int ParserErrorCodeStartIndex = 46000;
private const int ParserErrorCodeEndIndex = 46499;
public static bool IsParseErrorCode(int errorCode)
{
return
(errorCode >= ParserErrorCodeStartIndex) &&
(errorCode <= ParserErrorCodeEndIndex);
}
public static bool IsInterpretationErrorCode(int errorCode)
{
return
(errorCode >= Interpretation.InterpretationBaseCode) &&
(errorCode <= Interpretation.InterpretationEndCode);
}
public static bool IsStatementFilterError(int errorCode)
{
return
(errorCode > StatementFilter.StatementFilterBaseCode) &&
(errorCode <= StatementFilter.StatementFilterMaxErrorCode);
}
public static class StatementFilter
{
public const int StatementFilterBaseCode = 70000;
public const int UnrecognizedStatement = StatementFilterBaseCode + 1;
public const int ServerObject = StatementFilterBaseCode + 2;
public const int AtMostTwoPartName = StatementFilterBaseCode + 3;
public const int AlterTableAddColumn = StatementFilterBaseCode + 4;
public const int ConstraintAll = StatementFilterBaseCode + 5;
public const int TriggerAll = StatementFilterBaseCode + 6;
public const int CreateSchemaWithoutName = StatementFilterBaseCode + 7;
public const int CreateSchemaElements = StatementFilterBaseCode + 8;
public const int AlterAssembly = StatementFilterBaseCode + 9;
public const int CreateStoplist = StatementFilterBaseCode + 10;
public const int UnsupportedPermission = StatementFilterBaseCode + 11;
public const int TopLevelExecuteWithResultSets = StatementFilterBaseCode + 12;
public const int AlterTableAddConstraint = StatementFilterBaseCode + 13;
public const int DatabaseOnlyObjectInServerProject = StatementFilterBaseCode + 14;
public const int UnsupportedBySqlAzure = StatementFilterBaseCode + 15;
public const int UnsupportedSecurityObjectKind = StatementFilterBaseCode + 16;
public const int StatementNotSupportedForCurrentRelease = StatementFilterBaseCode + 17;
public const int ServerPermissionsNotAllowed = StatementFilterBaseCode + 18;
public const int DeprecatedSyntax = StatementFilterBaseCode + 19;
public const int SetRemoteData = StatementFilterBaseCode + 20;
public const int StatementFilterMaxErrorCode = StatementFilterBaseCode + 499;
}
public static class Interpretation
{
public const int InterpretationBaseCode = 70500;
public const int InvalidTopLevelStatement = InterpretationBaseCode + 1;
public const int InvalidAssemblySource = InterpretationBaseCode + 2;
public const int InvalidDatabaseName = InterpretationBaseCode + 3;
public const int OnlyTwoPartNameAllowed = InterpretationBaseCode + 4;
public const int SecurityObjectCannotBeNull = InterpretationBaseCode + 5;
public const int UnknownPermission = InterpretationBaseCode + 6;
public const int UnsupportedAll = InterpretationBaseCode + 7;
public const int InvalidColumnList = InterpretationBaseCode + 8;
public const int ColumnsAreNotAllowed = InterpretationBaseCode + 9;
public const int InvalidDataType = InterpretationBaseCode + 10;
public const int InvalidObjectName = InterpretationBaseCode + 11;
public const int InvalidObjectChildName = InterpretationBaseCode + 12;
public const int NoGlobalTemporarySymmetricKey = InterpretationBaseCode + 13;
public const int NoGlobalTemporarySymmetricKey_Warning = InterpretationBaseCode + 14;
public const int NameCannotBeNull = InterpretationBaseCode + 15;
public const int NameCannotBeNull_Warning = InterpretationBaseCode + 16;
public const int InvalidLoginName = InterpretationBaseCode + 17;
public const int InvalidLoginName_Warning = InterpretationBaseCode + 18;
public const int MoreAliasesThanColumns = InterpretationBaseCode + 19;
public const int FewerAliasesThanColumns = InterpretationBaseCode + 20;
public const int InvalidTimestampReturnType = InterpretationBaseCode + 21;
public const int VariableParameterAtTopLevelStatement = InterpretationBaseCode + 22;
public const int CannotCreateTempTable = InterpretationBaseCode + 23;
public const int MultipleNullabilityConstraintError = InterpretationBaseCode + 24;
public const int MultipleNullabilityConstraintWarning = InterpretationBaseCode + 25;
public const int ColumnIsntAllowedForAssemblySource = InterpretationBaseCode + 26;
public const int InvalidUserName = InterpretationBaseCode + 27;
public const int InvalidWindowsLogin = InterpretationBaseCode + 28;
public const int InvalidWindowsLogin_Warning = InterpretationBaseCode + 29;
public const int CannotHaveUsingForPrimaryXmlIndex = InterpretationBaseCode + 30;
public const int UsingIsRequiredForSecondaryXmlIndex = InterpretationBaseCode + 31;
public const int XmlIndexTypeIsRequiredForSecondaryXmlIndex = InterpretationBaseCode + 32;
public const int UnsupportedAlterCryptographicProvider = InterpretationBaseCode + 33;
public const int HttpForSoapOnly = InterpretationBaseCode + 34;
public const int UnknownEventTypeOrGroup = InterpretationBaseCode + 35;
public const int CannotAddLogFileToFilegroup = InterpretationBaseCode + 36;
public const int BuiltInTypeExpected = InterpretationBaseCode + 37;
public const int MissingArgument = InterpretationBaseCode + 38;
public const int InvalidArgument = InterpretationBaseCode + 39;
public const int IncompleteBoundingBoxCoordinates = InterpretationBaseCode + 40;
public const int XMaxLessThanXMin = InterpretationBaseCode + 41;
public const int YMaxLessThanYMin = InterpretationBaseCode + 42;
public const int InvalidCoordinate = InterpretationBaseCode + 43;
public const int InvalidValue = InterpretationBaseCode + 44;
public const int InvalidIdentityValue = InterpretationBaseCode + 45;
public const int InvalidPriorityLevel = InterpretationBaseCode + 46;
public const int TriggerIsNotForEvent = InterpretationBaseCode + 47;
public const int SyntaxError = InterpretationBaseCode + 48;
public const int UnsupportedPintable = InterpretationBaseCode + 49;
public const int DuplicateEventType = InterpretationBaseCode + 50;
public const int ClearAndBasicAreNotAllowed = InterpretationBaseCode + 51;
public const int AssemblyCorruptErrorCode = InterpretationBaseCode + 57;
public const int DynamicQuery = InterpretationBaseCode + 58;
public const int OnlyLcidAllowed = InterpretationBaseCode + 59;
public const int WildCardNotAllowed = InterpretationBaseCode + 60;
public const int CannotBindSchema = InterpretationBaseCode + 61;
public const int TableTypeNotAllowFunctionCall = InterpretationBaseCode + 62;
public const int ColumnNotAllowed = InterpretationBaseCode + 63;
public const int OwnerRequiredForEndpoint = InterpretationBaseCode + 64;
public const int PartitionNumberMustBeInteger = InterpretationBaseCode + 65;
public const int DuplicatedPartitionNumber = InterpretationBaseCode + 66;
public const int FromPartitionGreaterThanToPartition = InterpretationBaseCode + 67;
public const int CannotSpecifyPartitionNumber = InterpretationBaseCode + 68;
public const int MissingColumnNameError = InterpretationBaseCode + 69;
public const int MissingColumnNameWarning = InterpretationBaseCode + 70;
public const int UnknownTableSourceError = InterpretationBaseCode + 71;
public const int UnknownTableSourceWarning = InterpretationBaseCode + 72;
public const int TooManyPartsForCteOrAliasError = InterpretationBaseCode + 73;
public const int TooManyPartsForCteOrAliasWarning = InterpretationBaseCode + 74;
public const int ServerAuditInvalidQueueDelayValue = InterpretationBaseCode + 75;
public const int WrongEventType = InterpretationBaseCode + 76;
public const int CantCreateUddtFromXmlError = InterpretationBaseCode + 77;
public const int CantCreateUddtFromXmlWarning = InterpretationBaseCode + 78;
public const int CantCreateUddtFromUddtError = InterpretationBaseCode + 79;
public const int CantCreateUddtFromUddtWarning = InterpretationBaseCode + 80;
public const int ForReplicationIsNotSupported = InterpretationBaseCode + 81;
public const int TooLongIdentifier = InterpretationBaseCode + 82;
public const int InvalidLanguageTerm = InterpretationBaseCode + 83;
public const int InvalidParameterOrOption = InterpretationBaseCode + 85;
public const int TableLevelForeignKeyWithNoColumnsError = InterpretationBaseCode + 86;
public const int TableLevelForeignKeyWithNoColumnsWarning = InterpretationBaseCode + 87;
public const int ConstraintEnforcementIsIgnored = InterpretationBaseCode + 88;
public const int DeprecatedBackupOption = InterpretationBaseCode + 89;
public const int UndeclaredVariableParameter = InterpretationBaseCode + 90;
public const int UnsupportedAlgorithm = InterpretationBaseCode + 91;
public const int InvalidLanguageNameOrAliasWarning = InterpretationBaseCode + 92;
public const int UnsupportedRevoke = InterpretationBaseCode + 93;
public const int InvalidPermissionTypeAgainstObject = InterpretationBaseCode + 94;
public const int InvalidPermissionObjectType = InterpretationBaseCode + 95;
public const int CannotDetermineSecurableFromPermission = InterpretationBaseCode + 96;
public const int InvalidColumnListForSecurableType = InterpretationBaseCode + 97;
public const int InvalidUserDefaultLanguage = InterpretationBaseCode + 98;
public const int CannotSpecifyGridParameterForAutoGridSpatialIndex = InterpretationBaseCode + 99;
public const int UnsupportedSpatialTessellationScheme = InterpretationBaseCode + 100;
public const int CannotSpecifyBoundingBoxForGeography = InterpretationBaseCode + 101;
public const int InvalidSearchPropertyId = InterpretationBaseCode + 102;
public const int OnlineSpatialIndex = InterpretationBaseCode + 103;
public const int SqlCmdVariableInObjectName = InterpretationBaseCode + 104;
public const int SubqueriesNotAllowed = InterpretationBaseCode + 105;
public const int ArgumentReplaceNotSupported = InterpretationBaseCode + 106;
public const int DuplicateArgument = InterpretationBaseCode + 107;
public const int UnsupportedNoPopulationChangeTrackingOption = InterpretationBaseCode + 108;
public const int UnsupportedResourceManagerLocationProperty = InterpretationBaseCode + 109;
public const int RequiredExternalDataSourceLocationPropertyMissing = InterpretationBaseCode + 110;
public const int UnsupportedSerdeMethodProperty = InterpretationBaseCode + 111;
public const int UnsupportedFormatOptionsProperty = InterpretationBaseCode + 112;
public const int RequiredSerdeMethodPropertyMissing = InterpretationBaseCode + 113;
public const int TableLevelIndexWithNoColumnsError = InterpretationBaseCode + 114;
public const int TableLevelIndexWithNoColumnsWarning = InterpretationBaseCode + 115;
public const int InvalidIndexOption = InterpretationBaseCode + 116;
public const int TypeAndSIDMustBeUsedTogether = InterpretationBaseCode + 117;
public const int TypeCannotBeUsedWithLoginOption = InterpretationBaseCode + 118;
public const int InvalidUserType = InterpretationBaseCode + 119;
public const int InvalidUserSid = InterpretationBaseCode + 120;
public const int InvalidPartitionFunctionDataType = InterpretationBaseCode + 121;
public const int RequiredExternalTableLocationPropertyMissing = InterpretationBaseCode + 122;
public const int UnsupportedRejectSampleValueProperty = InterpretationBaseCode + 123;
public const int RequiredExternalDataSourceDatabasePropertyMissing = InterpretationBaseCode + 124;
public const int RequiredExternalDataSourceShardMapNamePropertyMissing = InterpretationBaseCode + 125;
public const int InvalidPropertyForExternalDataSourceType = InterpretationBaseCode + 126;
public const int UnsupportedExternalDataSourceTypeInCurrentPlatform = InterpretationBaseCode + 127;
public const int UnsupportedExternalTableProperty = InterpretationBaseCode + 128;
public const int MaskingFunctionIsEmpty = InterpretationBaseCode + 129;
public const int InvalidMaskingFunctionFormat = InterpretationBaseCode + 130;
public const int CannotCreateAlwaysEncryptedObject = InterpretationBaseCode + 131;
public const int ExternalTableSchemaOrObjectNameMissing = InterpretationBaseCode + 132;
public const int CannotCreateTemporalTableWithoutHistoryTableName = InterpretationBaseCode + 133;
public const int TemporalPeriodColumnMustNotBeNullable = InterpretationBaseCode + 134;
public const int InterpretationEndCode = InterpretationBaseCode + 499;
}
public static class ModelBuilder
{
private const int ModelBuilderBaseCode = 71000;
public const int CannotFindMainElement = ModelBuilderBaseCode + 1;
public const int CannotFindColumnSourceGrantForColumnRevoke = ModelBuilderBaseCode + 2;
public const int AssemblyReferencesNotSupported = ModelBuilderBaseCode + 3;
public const int NoSourceForColumn = ModelBuilderBaseCode + 5;
public const int MoreThanOneStatementPerBatch = ModelBuilderBaseCode + 6;
public const int MaximumSizeExceeded = ModelBuilderBaseCode + 7;
}
public static class Validation
{
private const int ValidationBaseCode = 71500;
public const int AllReferencesMustBeResolved = ValidationBaseCode + 1;
public const int AllReferencesMustBeResolved_Warning = ValidationBaseCode + 2;
public const int AssemblyVisibilityRule = ValidationBaseCode + 3;
public const int BreakContinueOnlyInWhile = ValidationBaseCode + 4;
public const int ClrObjectAssemblyReference_InvalidAssembly = ValidationBaseCode + 5;
public const int ClrObjectAssemblyReference = ValidationBaseCode + 6;
public const int ColumnUserDefinedTableType = ValidationBaseCode + 7;
public const int DuplicateName = ValidationBaseCode + 8;
public const int DuplicateName_Warning = ValidationBaseCode + 9;
public const int DuplicateVariableParameterName_TemporaryTable = ValidationBaseCode + 10;
public const int DuplicateVariableParameterName_Variable = ValidationBaseCode + 11;
public const int EndPointRule_DATABASE_MIRRORING = ValidationBaseCode + 12;
public const int EndPointRule_SERVICE_BROKER = ValidationBaseCode + 13;
public const int ForeignKeyColumnTypeNumberMustMatch_NumberOfColumns = ValidationBaseCode + 14;
public const int ForeignKeyColumnTypeNumberMustMatch_TypeMismatch = ValidationBaseCode + 15;
public const int ForeignKeyReferencePKUnique = ValidationBaseCode + 16;
public const int FullTextIndexColumn = ValidationBaseCode + 17;
public const int IdentityColumnValidation_InvalidType = ValidationBaseCode + 18;
public const int IdentityColumnValidation_MoreThanOneIdentity = ValidationBaseCode + 19;
public const int InsertIntoIdentityColumn = ValidationBaseCode + 20;
public const int MatchingSignatureNotFoundInAssembly = ValidationBaseCode + 21;
public const int MatchingTypeNotFoundInAssembly = ValidationBaseCode + 22;
public const int MaxColumnInIndexKey = ValidationBaseCode + 25;
public const int MaxColumnInTable_1024Columns = ValidationBaseCode + 26;
public const int MultiFullTextIndexOnTable = ValidationBaseCode + 28;
public const int NonNullPrimaryKey_NonNullSimpleColumn = ValidationBaseCode + 29;
public const int NonNullPrimaryKey_NotPersistedComputedColumn = ValidationBaseCode + 30;
public const int OneClusteredIndex = ValidationBaseCode + 31;
public const int OneMasterKey = ValidationBaseCode + 32;
public const int OnePrimaryKey = ValidationBaseCode + 33;
public const int PrimaryXMLIndexClustered = ValidationBaseCode + 34;
public const int SelectAssignRetrieval = ValidationBaseCode + 35;
public const int SubroutineParameterReadOnly_NonUDTTReadOnly = ValidationBaseCode + 36;
public const int SubroutineParameterReadOnly_UDTTReadOnly = ValidationBaseCode + 37;
public const int UsingXMLIndex = ValidationBaseCode + 38;
public const int VardecimalOptionRule = ValidationBaseCode + 39;
public const int WildCardExpansion = ValidationBaseCode + 40;
public const int WildCardExpansion_Warning = ValidationBaseCode + 41;
public const int XMLIndexOnlyXMLTypeColumn = ValidationBaseCode + 42;
public const int TableVariablePrefix = ValidationBaseCode + 44;
public const int FileStream_FILESTREAMON = ValidationBaseCode + 45;
public const int FileStream_ROWGUIDCOLUMN = ValidationBaseCode + 46;
public const int MaxColumnInTable100_Columns = ValidationBaseCode + 47;
public const int XMLIndexOnlyXMLTypeColumn_SparseColumnSet = ValidationBaseCode + 48;
public const int ClrObjectAssemblyReference_ParameterTypeMismatch = ValidationBaseCode + 50;
public const int OneDefaultConstraintPerColumn = ValidationBaseCode + 51;
public const int PermissionStatementValidation_DuplicatePermissionOnSecurable = ValidationBaseCode + 52;
public const int PermissionStatementValidation_ConflictingPermissionsOnSecurable = ValidationBaseCode + 53;
public const int PermissionStatementValidation_ConflictingColumnStatements = ValidationBaseCode + 54;
public const int PermissionOnObjectSecurableValidation_InvalidPermissionForObject = ValidationBaseCode + 55;
public const int SequenceValueValidation_ValueOutOfRange = ValidationBaseCode + 56;
public const int SequenceValueValidation_InvalidDataType = ValidationBaseCode + 57;
public const int MismatchedName_Warning = ValidationBaseCode + 58;
public const int DifferentNameCasing_Warning = ValidationBaseCode + 59;
public const int OneClusteredIndexAzure = ValidationBaseCode + 60;
public const int AllExternalReferencesMustBeResolved = ValidationBaseCode + 61;
public const int AllExternalReferencesMustBeResolved_Warning = ValidationBaseCode + 62;
public const int ExternalObjectWildCardExpansion_Warning = ValidationBaseCode + 63;
public const int UnsupportedElementForDataPackage = ValidationBaseCode + 64;
public const int InvalidFileStreamOptions = ValidationBaseCode + 65;
public const int StorageShouldNotSetOnDifferentInstance = ValidationBaseCode + 66;
public const int TableShouldNotHaveStorage = ValidationBaseCode + 67;
public const int MemoryOptimizedObjectsValidation_NonMemoryOptimizedTableCannotBeAccessed = ValidationBaseCode + 68;
public const int MemoryOptimizedObjectsValidation_SyntaxNotSupportedOnHekatonElement = ValidationBaseCode + 69;
public const int MemoryOptimizedObjectsValidation_ValidatePrimaryKeyForSchemaAndDataTables = ValidationBaseCode + 70;
public const int MemoryOptimizedObjectsValidation_ValidatePrimaryKeyForSchemaOnlyTables = ValidationBaseCode + 71;
public const int MemoryOptimizedObjectsValidation_OnlyNotNullableColumnsOnIndexes = ValidationBaseCode + 72;
public const int MemoryOptimizedObjectsValidation_HashIndexesOnlyOnMemoryOptimizedObjects = ValidationBaseCode + 73;
public const int MemoryOptimizedObjectsValidation_OptionOnlyForHashIndexes = ValidationBaseCode + 74;
public const int IncrementalStatisticsValidation_FilterNotSupported = ValidationBaseCode + 75;
public const int IncrementalStatisticsValidation_ViewNotSupported = ValidationBaseCode + 76;
public const int IncrementalStatisticsValidation_IndexNotPartitionAligned = ValidationBaseCode + 77;
public const int AzureV12SurfaceAreaValidation = ValidationBaseCode + 78;
public const int DuplicatedTargetObjectReferencesInSecurityPolicy = ValidationBaseCode + 79;
public const int MultipleSecurityPoliciesOnTargetObject = ValidationBaseCode + 80;
public const int ExportedRowsMayBeIncomplete = ValidationBaseCode + 81;
public const int ExportedRowsMayContainSomeMaskedData = ValidationBaseCode + 82;
public const int EncryptedColumnValidation_EncryptedPrimaryKey = ValidationBaseCode + 83;
public const int EncryptedColumnValidation_EncryptedUniqueColumn = ValidationBaseCode + 84;
public const int EncryptedColumnValidation_EncryptedCheckConstraint = ValidationBaseCode + 85;
public const int EncryptedColumnValidation_PrimaryKeyForeignKeyEncryptionMismatch = ValidationBaseCode + 86;
public const int EncryptedColumnValidation_UnsupportedDataType = ValidationBaseCode + 87;
public const int MemoryOptimizedObjectsValidation_UnSupportedOption = ValidationBaseCode + 88;
public const int MasterKeyExistsForCredential = ValidationBaseCode + 89;
public const int MemoryOptimizedObjectsValidation_InvalidForeignKeyRelationship = ValidationBaseCode + 90;
public const int MemoryOptimizedObjectsValidation_UnsupportedForeignKeyReference = ValidationBaseCode + 91;
public const int EncryptedColumnValidation_RowGuidColumn = ValidationBaseCode + 92;
public const int EncryptedColumnValidation_EncryptedClusteredIndex = ValidationBaseCode + 93;
public const int EncryptedColumnValidation_EncryptedNonClusteredIndex = ValidationBaseCode + 94;
public const int EncryptedColumnValidation_DependentComputedColumn = ValidationBaseCode + 95;
public const int EncryptedColumnValidation_EncryptedFullTextColumn = ValidationBaseCode + 96;
public const int EncryptedColumnValidation_EncryptedSparseColumnSet = ValidationBaseCode + 97;
public const int EncryptedColumnValidation_EncryptedStatisticsColumn = ValidationBaseCode + 98;
public const int EncryptedColumnValidation_EncryptedPartitionColumn = ValidationBaseCode + 99;
public const int EncryptedColumnValidation_PrimaryKeyChangeTrackingColumn = ValidationBaseCode + 100;
public const int EncryptedColumnValidation_ChangeDataCaptureOn = ValidationBaseCode + 101;
public const int EncryptedColumnValidation_FilestreamColumn = ValidationBaseCode + 102;
public const int EncryptedColumnValidation_MemoryOptimizedTable = ValidationBaseCode + 103;
public const int EncryptedColumnValidation_MaskedEncryptedColumn = ValidationBaseCode + 104;
public const int EncryptedColumnValidation_EncryptedIdentityColumn = ValidationBaseCode + 105;
public const int EncryptedColumnValidation_EncryptedDefaultConstraint = ValidationBaseCode + 106;
public const int TemporalValidation_InvalidPeriodSpecification = ValidationBaseCode + 107;
public const int TemporalValidation_MultipleCurrentTables = ValidationBaseCode + 108;
public const int TemporalValidation_SchemaMismatch = ValidationBaseCode + 109;
public const int TemporalValidation_ComputedColumns = ValidationBaseCode + 110;
public const int TemporalValidation_NoAlwaysEncryptedCols = ValidationBaseCode + 111;
public const int IndexesOnExternalTable = ValidationBaseCode + 112;
public const int TriggersOnExternalTable = ValidationBaseCode + 113;
public const int StretchValidation_ExportBlocked = ValidationBaseCode + 114;
public const int StretchValidation_ImportBlocked = ValidationBaseCode + 115;
public const int DeploymentBlocked = ValidationBaseCode + 116;
public const int NoBlockPredicatesTargetingViews = ValidationBaseCode + 117;
public const int SchemaBindingOnSecurityPoliciesValidation = ValidationBaseCode + 118;
public const int SecurityPredicateTargetObjectValidation = ValidationBaseCode + 119;
public const int TemporalValidation_SchemaMismatch_ColumnCount = ValidationBaseCode + 120;
public const int AkvValidation_AuthenticationFailed = ValidationBaseCode + 121;
public const int TemporalValidation_PrimaryKey = ValidationBaseCode + 122;
}
public static class SqlMSBuild
{
private const int MSBuildBaseCode = 72000;
public const int FileDoesNotExist = MSBuildBaseCode + 1;
public const int UnknownDeployError = MSBuildBaseCode + 2;
public const int InvalidProperty = MSBuildBaseCode + 3;
public const int CollationError = MSBuildBaseCode + 4;
public const int InvalidSqlClrDefinition = MSBuildBaseCode + 5;
public const int SQL_PrePostFatalParserError = MSBuildBaseCode + 6;
public const int SQL_PrePostSyntaxCheckError = MSBuildBaseCode + 7;
public const int SQL_PrePostVariableError = MSBuildBaseCode + 8;
public const int SQL_CycleError = MSBuildBaseCode + 9;
public const int SQL_NoConnectionStringNoServerVerification = MSBuildBaseCode + 10;
public const int SQL_VardecimalMismatch = MSBuildBaseCode + 11;
public const int SQL_NoAlterFileSystemObject = MSBuildBaseCode + 12;
public const int SQL_SqlCmdVariableOverrideError = MSBuildBaseCode + 13;
public const int SQL_BatchError = MSBuildBaseCode + 14;
public const int SQL_DataLossError = MSBuildBaseCode + 15;
public const int SQL_ExecutionError = MSBuildBaseCode + 16;
public const int SQL_UncheckedConstraint = MSBuildBaseCode + 17;
public const int SQL_UnableToImportElements = MSBuildBaseCode + 18;
public const int SQL_TargetReadOnlyError = MSBuildBaseCode + 19;
public const int SQL_UnsupportedCompatibilityMode = MSBuildBaseCode + 20;
public const int SQL_IncompatibleDSPVersions = MSBuildBaseCode + 21;
public const int SQL_CouldNotLoadSymbols = MSBuildBaseCode + 22;
public const int SQL_ContainmentlMismatch = MSBuildBaseCode + 23;
public const int SQL_PrePostExpectedNoTSqlError = MSBuildBaseCode + 24;
public const int ReferenceErrorCode = MSBuildBaseCode + 25;
public const int FileError = MSBuildBaseCode + 26;
public const int MissingReference = MSBuildBaseCode + 27;
public const int SerializationError = MSBuildBaseCode + 28;
public const int DeploymentContributorVerificationError = MSBuildBaseCode + 29;
public const int Deployment_PossibleRuntimeError = MSBuildBaseCode + 30;
public const int Deployment_BlockingDependency = MSBuildBaseCode + 31;
public const int Deployment_TargetObjectLoss = MSBuildBaseCode + 32;
public const int Deployment_MissingDependency = MSBuildBaseCode + 33;
public const int Deployment_PossibleDataLoss = MSBuildBaseCode + 34;
public const int Deployment_NotSupportedOperation = MSBuildBaseCode + 35;
public const int Deployment_Information = MSBuildBaseCode + 36;
public const int Deployment_UnsupportedDSP = MSBuildBaseCode + 37;
public const int Deployment_SkipManagementScopedChange = MSBuildBaseCode + 38;
public const int StaticCodeAnalysis_GeneralException = MSBuildBaseCode + 39;
public const int StaticCodeAnalysis_ResultsFileIOException = MSBuildBaseCode + 40;
public const int StaticCodeAnalysis_FailToCreateTaskHost = MSBuildBaseCode + 41;
public const int StaticCodeAnalysis_InvalidDataSchemaModel = MSBuildBaseCode + 42;
public const int StaticCodeAnalysis_InvalidElement = MSBuildBaseCode + 43;
public const int Deployment_NoClusteredIndex = MSBuildBaseCode + 44;
public const int Deployment_DetailedScriptExecutionError = MSBuildBaseCode + 45;
}
public static class Refactoring
{
private const int RefactoringBaseCode = 72500;
public const int FailedToLoadFile = RefactoringBaseCode + 1;
}
/// <summary>
/// These codes are used to message specific actions for extract and deployment operations.
/// The primary consumer of these codes is the Import/Export service.
/// </summary>
public static class ServiceActions
{
public const int ServiceActionsBaseCode = 73000;
public const int ServiceActionsMaxCode = 73000 + 0xFF;
// Note: These codes are defined so that the lower 3 bits indicate one of three
// event stages: Started (0x01), Done/Complete (0x02), Done/Failed (0x04)
public const int DeployInitializeStart = ServiceActionsBaseCode + 0x01;
public const int DeployInitializeSuccess = ServiceActionsBaseCode + 0x02;
public const int DeployInitializeFailure = ServiceActionsBaseCode + 0x04;
public const int DeployAnalysisStart = ServiceActionsBaseCode + 0x11;
public const int DeployAnalysisSuccess = ServiceActionsBaseCode + 0x12;
public const int DeployAnalysisFailure = ServiceActionsBaseCode + 0x14;
public const int DeployExecuteScriptStart = ServiceActionsBaseCode + 0x21;
public const int DeployExecuteScriptSuccess = ServiceActionsBaseCode + 0x22;
public const int DeployExecuteScriptFailure = ServiceActionsBaseCode + 0x24;
public const int DataImportStart = ServiceActionsBaseCode + 0x41;
public const int DataImportSuccess = ServiceActionsBaseCode + 0x42;
public const int DataImportFailure = ServiceActionsBaseCode + 0x44;
public const int ExtractSchemaStart = ServiceActionsBaseCode + 0x61;
public const int ExtractSchemaSuccess = ServiceActionsBaseCode + 0x62;
public const int ExtractSchemaFailure = ServiceActionsBaseCode + 0x64;
public const int ExportVerifyStart = ServiceActionsBaseCode + 0x71;
public const int ExportVerifySuccess = ServiceActionsBaseCode + 0x72;
public const int ExportVerifyFailure = ServiceActionsBaseCode + 0x74;
public const int ExportDataStart = ServiceActionsBaseCode + 0x81;
public const int ExportDataSuccess = ServiceActionsBaseCode + 0x82;
public const int ExportDataFailure = ServiceActionsBaseCode + 0x84;
public const int EnableIndexesDataStart = ServiceActionsBaseCode + 0xb1;
public const int EnableIndexesDataSuccess = ServiceActionsBaseCode + 0xb2;
public const int EnableIndexesDataFailure = ServiceActionsBaseCode + 0xb4;
public const int DisableIndexesDataStart = ServiceActionsBaseCode + 0xc1;
public const int DisableIndexesDataSuccess = ServiceActionsBaseCode + 0xc2;
public const int DisableIndexesDataFailure = ServiceActionsBaseCode + 0xc4;
public const int EnableIndexDataStart = ServiceActionsBaseCode + 0xd1;
public const int EnableIndexDataSuccess = ServiceActionsBaseCode + 0xd2;
public const int EnableIndexDataFailure = ServiceActionsBaseCode + 0xd4;
public const int DisableIndexDataStart = ServiceActionsBaseCode + 0xe1;
public const int DisableIndexDataSuccess = ServiceActionsBaseCode + 0xe2;
public const int DisableIndexDataFailure = ServiceActionsBaseCode + 0xe4;
public const int ColumnEncryptionDataMigrationStart = ServiceActionsBaseCode + 0xf1;
public const int ColumnEncryptionDataMigrationSuccess = ServiceActionsBaseCode + 0xf2;
public const int ColumnEncryptionDataMigrationFailure = ServiceActionsBaseCode + 0xf4;
// These codes do not set the lower 3 bits
public const int ConnectionRetry = ServiceActionsBaseCode + 0x90;
public const int CommandRetry = ServiceActionsBaseCode + 0x91;
public const int GeneralProgress = ServiceActionsBaseCode + 0x92;
public const int TypeFidelityLoss = ServiceActionsBaseCode + 0x93;
public const int TableProgress = ServiceActionsBaseCode + 0x94;
public const int ImportBlocked = ServiceActionsBaseCode + 0x95;
public const int DataPrecisionLoss = ServiceActionsBaseCode + 0x96;
public const int DataRowCount = ServiceActionsBaseCode + 0x98;
public const int DataException = ServiceActionsBaseCode + 0xA0;
public const int LogEntry = ServiceActionsBaseCode + 0xA1;
public const int GeneralInfo = ServiceActionsBaseCode + 0xA2;
}
}
}

View File

@@ -0,0 +1,74 @@
//
// 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.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Represents an error produced by SQL Server database schema provider
/// </summary>
[Serializable]
internal class SqlServerError : DataSchemaError
{
private const string SqlServerPrefix = "SQL";
private const string DefaultHelpKeyword = "vs.teamsystem.datatools.DefaultErrorMessageHelp";
public SqlServerError(string message, string document, ErrorSeverity severity)
: this(message, null, document, 0, 0, Constants.UndefinedErrorCode, severity)
{
}
public SqlServerError(string message, string document, int errorCode, ErrorSeverity severity)
: this(message, null, document, 0, 0, errorCode, severity)
{
}
public SqlServerError(Exception exception, string document, int errorCode, ErrorSeverity severity)
: this(exception, document, 0, 0, errorCode, severity)
{
}
public SqlServerError(string message, string document, int line, int column, ErrorSeverity severity)
: this(message, null, document, line, column, Constants.UndefinedErrorCode, severity)
{
}
public SqlServerError(
Exception exception,
string document,
int line,
int column,
int errorCode,
ErrorSeverity severity) :
this(exception.Message, exception, document, line, column, errorCode, severity)
{
}
public SqlServerError(
string message,
string document,
int line,
int column,
int errorCode,
ErrorSeverity severity) :
this(message, null, document, line, column, errorCode, severity)
{
}
public SqlServerError(
string message,
Exception exception,
string document,
int line,
int column,
int errorCode,
ErrorSeverity severity) :
base(message, exception, document, line, column, SqlServerPrefix, errorCode, severity)
{
this.HelpKeyword = DefaultHelpKeyword;
}
}
}

View File

@@ -0,0 +1,54 @@
//
// 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.Globalization;
namespace Microsoft.SqlTools.CoreServices.Connection.ReliableConnection
{
/// <summary>
/// Captures extended information about a specific error and a retry
/// </summary>
internal class SqlServerRetryError : SqlServerError
{
private int _retryCount;
private int _errorCode;
public SqlServerRetryError(string message, Exception ex, int retryCount, int errorCode, ErrorSeverity severity)
: base(ex, message, errorCode, severity)
{
_retryCount = retryCount;
_errorCode = errorCode;
}
public int RetryCount
{
get { return _retryCount; }
}
public static string FormatRetryMessage(int retryCount, TimeSpan delay, Exception transientException)
{
string message = string.Format(
CultureInfo.CurrentCulture,
Resources.RetryOnException,
retryCount,
delay.TotalMilliseconds.ToString(CultureInfo.CurrentCulture),
transientException.ToString());
return message;
}
public static string FormatIgnoreMessage(int retryCount, Exception exception)
{
string message = string.Format(
CultureInfo.CurrentCulture,
Resources.IgnoreOnException,
retryCount,
exception.ToString());
return message;
}
}
}

View File

@@ -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.Data.Common;
using Microsoft.SqlTools.CoreServices.Connection.ReliableConnection;
namespace Microsoft.SqlTools.CoreServices.Connection
{
/// <summary>
/// Factory class to create SqlClientConnections
/// The purpose of the factory is to make it easier to mock out the database
/// in 'offline' unit test scenarios.
/// </summary>
public class SqlConnectionFactory : ISqlConnectionFactory
{
/// <summary>
/// Creates a new SqlConnection object
/// </summary>
public DbConnection CreateSqlConnection(string connectionString)
{
RetryPolicy connectionRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
RetryPolicy commandRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
return new ReliableSqlConnection(connectionString, connectionRetryPolicy, commandRetryPolicy);
}
}
}

View File

@@ -0,0 +1,429 @@
//
// 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.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.SqlTools.Hosting.Utility;
using System.Diagnostics;
namespace Microsoft.SqlTools.CoreServices.LanguageServices
{
/// <summary>
/// Main class for the Binding Queue
/// </summary>
public class BindingQueue<T> : IDisposable where T : IBindingContext, new()
{
internal const int QueueThreadStackSize = 5 * 1024 * 1024;
private CancellationTokenSource processQueueCancelToken = null;
private ManualResetEvent itemQueuedEvent = new ManualResetEvent(initialState: false);
private object bindingQueueLock = new object();
private LinkedList<QueueItem> bindingQueue = new LinkedList<QueueItem>();
private object bindingContextLock = new object();
private Task queueProcessorTask;
/// <summary>
/// Map from context keys to binding context instances
/// Internal for testing purposes only
/// </summary>
internal Dictionary<string, IBindingContext> BindingContextMap { get; set; }
/// <summary>
/// Constructor for a binding queue instance
/// </summary>
public BindingQueue()
{
this.BindingContextMap = new Dictionary<string, IBindingContext>();
this.StartQueueProcessor();
}
public void StartQueueProcessor()
{
this.queueProcessorTask = StartQueueProcessorAsync();
}
/// <summary>
/// Stops the binding queue by sending cancellation request
/// </summary>
/// <param name="timeout"></param>
public bool StopQueueProcessor(int timeout)
{
this.processQueueCancelToken.Cancel();
return this.queueProcessorTask.Wait(timeout);
}
/// <summary>
/// Returns true if cancellation is requested
/// </summary>
/// <returns></returns>
public bool IsCancelRequested
{
get
{
return this.processQueueCancelToken.IsCancellationRequested;
}
}
/// <summary>
/// Queue a binding request item
/// </summary>
public virtual QueueItem QueueBindingOperation(
string key,
Func<IBindingContext, CancellationToken, object> bindOperation,
Func<IBindingContext, object> timeoutOperation = null,
Func<Exception, object> errorHandler = null,
int? bindingTimeout = null,
int? waitForLockTimeout = null)
{
// don't add null operations to the binding queue
if (bindOperation == null)
{
return null;
}
QueueItem queueItem = new QueueItem()
{
Key = key,
BindOperation = bindOperation,
TimeoutOperation = timeoutOperation,
ErrorHandler = errorHandler,
BindingTimeout = bindingTimeout,
WaitForLockTimeout = waitForLockTimeout
};
lock (this.bindingQueueLock)
{
this.bindingQueue.AddLast(queueItem);
}
this.itemQueuedEvent.Set();
return queueItem;
}
/// <summary>
/// Checks if a particular binding context is connected or not
/// </summary>
/// <param name="key"></param>
public bool IsBindingContextConnected(string key)
{
lock (this.bindingContextLock)
{
IBindingContext context;
if (this.BindingContextMap.TryGetValue(key, out context))
{
return context.IsConnected;
}
return false;
}
}
/// <summary>
/// Gets or creates a binding context for the provided context key
/// </summary>
/// <param name="key"></param>
protected IBindingContext GetOrCreateBindingContext(string key)
{
// use a default binding context for disconnected requests
if (string.IsNullOrWhiteSpace(key))
{
key = "disconnected_binding_context";
}
lock (this.bindingContextLock)
{
if (!this.BindingContextMap.ContainsKey(key))
{
this.BindingContextMap.Add(key, new T());
}
return this.BindingContextMap[key];
}
}
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>
protected bool BindingContextExists(string key)
{
lock (this.bindingContextLock)
{
return this.BindingContextMap.ContainsKey(key);
}
}
/// <summary>
/// Remove the binding queue entry
/// </summary>
protected void RemoveBindingContext(string key)
{
lock (this.bindingContextLock)
{
if (this.BindingContextMap.ContainsKey(key))
{
// disconnect existing connection
var bindingContext = this.BindingContextMap[key];
if (bindingContext.ServerConnection != null && bindingContext.ServerConnection.IsOpen)
{
bindingContext.ServerConnection.Disconnect();
}
// remove key from the map
this.BindingContextMap.Remove(key);
}
}
}
public bool HasPendingQueueItems
{
get
{
lock (this.bindingQueueLock)
{
return this.bindingQueue.Count > 0;
}
}
}
/// <summary>
/// Gets the next pending queue item
/// </summary>
private QueueItem GetNextQueueItem()
{
lock (this.bindingQueueLock)
{
if (this.bindingQueue.Count == 0)
{
return null;
}
QueueItem queueItem = this.bindingQueue.First.Value;
this.bindingQueue.RemoveFirst();
return queueItem;
}
}
/// <summary>
/// Starts the queue processing thread
/// </summary>
private Task StartQueueProcessorAsync()
{
if (this.processQueueCancelToken != null)
{
this.processQueueCancelToken.Dispose();
}
this.processQueueCancelToken = new CancellationTokenSource();
return Task.Factory.StartNew(
ProcessQueue,
this.processQueueCancelToken.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
/// <summary>
/// The core queue processing method
/// </summary>
/// <param name="state"></param>
private void ProcessQueue()
{
CancellationToken token = this.processQueueCancelToken.Token;
WaitHandle[] waitHandles = new WaitHandle[2]
{
this.itemQueuedEvent,
token.WaitHandle
};
while (true)
{
// wait for with an item to be queued or the a cancellation request
WaitHandle.WaitAny(waitHandles);
if (token.IsCancellationRequested)
{
break;
}
try
{
// dispatch all pending queue items
while (this.HasPendingQueueItems)
{
QueueItem queueItem = GetNextQueueItem();
if (queueItem == null)
{
continue;
}
IBindingContext bindingContext = GetOrCreateBindingContext(queueItem.Key);
if (bindingContext == null)
{
queueItem.ItemProcessed.Set();
continue;
}
bool lockTaken = false;
try
{
// prefer the queue item binding item, otherwise use the context default timeout
int bindTimeout = queueItem.BindingTimeout ?? bindingContext.BindingTimeout;
// handle the case a previous binding operation is still running
if (!bindingContext.BindingLock.WaitOne(queueItem.WaitForLockTimeout ?? 0))
{
queueItem.Result = queueItem.TimeoutOperation != null
? queueItem.TimeoutOperation(bindingContext)
: null;
continue;
}
bindingContext.BindingLock.Reset();
lockTaken = true;
// execute the binding operation
object result = null;
CancellationTokenSource cancelToken = new CancellationTokenSource();
// run the operation in a separate thread
var bindTask = Task.Run(() =>
{
try
{
result = queueItem.BindOperation(
bindingContext,
cancelToken.Token);
}
catch (Exception ex)
{
Logger.Write(TraceEventType.Error, "Unexpected exception on the binding queue: " + ex.ToString());
if (queueItem.ErrorHandler != null)
{
result = queueItem.ErrorHandler(ex);
}
}
});
// check if the binding tasks completed within the binding timeout
if (bindTask.Wait(bindTimeout))
{
queueItem.Result = result;
}
else
{
cancelToken.Cancel();
// if the task didn't complete then call the timeout callback
if (queueItem.TimeoutOperation != null)
{
queueItem.Result = queueItem.TimeoutOperation(bindingContext);
}
lockTaken = false;
bindTask
.ContinueWith((a) => bindingContext.BindingLock.Set())
.ContinueWithOnFaulted(t => Logger.Write(TraceEventType.Error, "Binding queue threw exception " + t.Exception.ToString()));
}
}
catch (Exception ex)
{
// catch and log any exceptions raised in the binding calls
// set item processed to avoid deadlocks
Logger.Write(TraceEventType.Error, "Binding queue threw exception " + ex.ToString());
}
finally
{
if (lockTaken)
{
bindingContext.BindingLock.Set();
}
queueItem.ItemProcessed.Set();
}
// if a queue processing cancellation was requested then exit the loop
if (token.IsCancellationRequested)
{
break;
}
}
}
finally
{
lock (this.bindingQueueLock)
{
// verify the binding queue is still empty
if (this.bindingQueue.Count == 0)
{
// reset the item queued event since we've processed all the pending items
this.itemQueuedEvent.Reset();
}
}
}
}
}
/// <summary>
/// Clear queued items
/// </summary>
public void ClearQueuedItems()
{
lock (this.bindingQueueLock)
{
if (this.bindingQueue.Count > 0)
{
this.bindingQueue.Clear();
}
}
}
public void Dispose()
{
if (this.processQueueCancelToken != null)
{
this.processQueueCancelToken.Dispose();
}
if (itemQueuedEvent != null)
{
itemQueuedEvent.Dispose();
}
if (this.BindingContextMap != null)
{
foreach (var item in this.BindingContextMap)
{
if (item.Value != null && item.Value.ServerConnection != null && item.Value.ServerConnection.SqlConnectionObject != null)
{
item.Value.ServerConnection.SqlConnectionObject.Close();
}
}
}
}
}
}

View File

@@ -0,0 +1,217 @@
//
// 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;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.Common;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Parser;
using Microsoft.SqlTools.CoreServices.Utility;
namespace Microsoft.SqlTools.CoreServices.LanguageServices
{
/// <summary>
/// Class for the binding context for connected sessions
/// </summary>
public class ConnectedBindingContext : IBindingContext
{
private ParseOptions parseOptions;
private ManualResetEvent bindingLock;
private ServerConnection serverConnection;
/// <summary>
/// Connected binding context constructor
/// </summary>
public ConnectedBindingContext()
{
this.bindingLock = new ManualResetEvent(initialState: true);
this.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout;
this.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
}
/// <summary>
/// Gets or sets a flag indicating if the binder is connected
/// </summary>
public bool IsConnected { get; set; }
/// <summary>
/// Gets or sets the binding server connection
/// </summary>
public ServerConnection ServerConnection
{
get
{
return this.serverConnection;
}
set
{
this.serverConnection = value;
// reset the parse options so the get recreated for the current connection
this.parseOptions = null;
}
}
/// <summary>
/// Gets or sets the metadata display info provider
/// </summary>
public MetadataDisplayInfoProvider MetadataDisplayInfoProvider { get; set; }
/// <summary>
/// Gets or sets the SMO metadata provider
/// </summary>
public SmoMetadataProvider SmoMetadataProvider { get; set; }
/// <summary>
/// Gets or sets the binder
/// </summary>
public IBinder Binder { get; set; }
/// <summary>
/// Gets the binding lock object
/// </summary>
public ManualResetEvent BindingLock
{
get
{
return this.bindingLock;
}
}
/// <summary>
/// Gets or sets the binding operation timeout in milliseconds
/// </summary>
public int BindingTimeout { get; set; }
/// <summary>
/// Gets the Language Service ServerVersion
/// </summary>
public ServerVersion ServerVersion
{
get
{
return this.ServerConnection != null
? this.ServerConnection.ServerVersion
: null;
}
}
/// <summary>
/// Gets the current DataEngineType
/// </summary>
public DatabaseEngineType DatabaseEngineType
{
get
{
return this.ServerConnection != null
? this.ServerConnection.DatabaseEngineType
: DatabaseEngineType.Standalone;
}
}
/// <summary>
/// Gets the current connections TransactSqlVersion
/// </summary>
public TransactSqlVersion TransactSqlVersion
{
get
{
return this.IsConnected
? GetTransactSqlVersion(this.ServerVersion)
: TransactSqlVersion.Current;
}
}
/// <summary>
/// Gets the current DatabaseCompatibilityLevel
/// </summary>
public DatabaseCompatibilityLevel DatabaseCompatibilityLevel
{
get
{
return this.IsConnected
? GetDatabaseCompatibilityLevel(this.ServerVersion)
: DatabaseCompatibilityLevel.Current;
}
}
/// <summary>
/// Gets the current ParseOptions
/// </summary>
public ParseOptions ParseOptions
{
get
{
if (this.parseOptions == null)
{
this.parseOptions = new ParseOptions(
batchSeparator: CommonConstants.DefaultBatchSeperator,
isQuotedIdentifierSet: true,
compatibilityLevel: DatabaseCompatibilityLevel,
transactSqlVersion: TransactSqlVersion);
}
return this.parseOptions;
}
}
/// <summary>
/// Gets the database compatibility level from a server version
/// </summary>
/// <param name="serverVersion"></param>
private static DatabaseCompatibilityLevel GetDatabaseCompatibilityLevel(ServerVersion serverVersion)
{
int versionMajor = Math.Max(serverVersion.Major, 8);
switch (versionMajor)
{
case 8:
return DatabaseCompatibilityLevel.Version80;
case 9:
return DatabaseCompatibilityLevel.Version90;
case 10:
return DatabaseCompatibilityLevel.Version100;
case 11:
return DatabaseCompatibilityLevel.Version110;
case 12:
return DatabaseCompatibilityLevel.Version120;
case 13:
return DatabaseCompatibilityLevel.Version130;
default:
return DatabaseCompatibilityLevel.Current;
}
}
/// <summary>
/// Gets the transaction sql version from a server version
/// </summary>
/// <param name="serverVersion"></param>
private static TransactSqlVersion GetTransactSqlVersion(ServerVersion serverVersion)
{
int versionMajor = Math.Max(serverVersion.Major, 9);
switch (versionMajor)
{
case 9:
case 10:
// In case of 10.0 we still use Version 10.5 as it is the closest available.
return TransactSqlVersion.Version105;
case 11:
return TransactSqlVersion.Version110;
case 12:
return TransactSqlVersion.Version120;
case 13:
return TransactSqlVersion.Version130;
default:
return TransactSqlVersion.Current;
}
}
}
}

View File

@@ -0,0 +1,231 @@
//
// 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 Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
using Microsoft.SqlTools.CoreServices.Connection;
using Microsoft.SqlTools.DataProtocol.Contracts.Connection;
using System.Threading;
using Microsoft.SqlTools.CoreServices.Workspace;
using Microsoft.SqlTools.CoreServices.SqlContext;
namespace Microsoft.SqlTools.CoreServices.LanguageServices
{
public interface IConnectedBindingQueue
{
void CloseConnections(string serverName, string databaseName, int millisecondsTimeout);
void OpenConnections(string serverName, string databaseName, int millisecondsTimeout);
string AddConnectionContext(ConnectionInfo connInfo, string featureName = null, bool overwrite = false);
void Dispose();
QueueItem QueueBindingOperation(
string key,
Func<IBindingContext, CancellationToken, object> bindOperation,
Func<IBindingContext, object> timeoutOperation = null,
Func<Exception, object> errorHandler = null,
int? bindingTimeout = null,
int? waitForLockTimeout = null);
}
public class SqlConnectionOpener
{
/// <summary>
/// Virtual method used to support mocking and testing
/// </summary>
public virtual SqlConnection OpenSqlConnection(ConnectionInfo connInfo, string featureName)
{
return ConnectionServiceCore.OpenSqlConnection(connInfo, featureName);
}
}
/// <summary>
/// ConnectedBindingQueue class for processing online binding requests
/// </summary>
public class ConnectedBindingQueue : BindingQueue<ConnectedBindingContext>, IConnectedBindingQueue
{
internal const int DefaultBindingTimeout = 500;
internal const int DefaultMinimumConnectionTimeout = 30;
/// <summary>
/// flag determing if the connection queue requires online metadata objects
/// it's much cheaper to not construct these objects if not needed
/// </summary>
private bool needsMetadata;
private SqlConnectionOpener connectionOpener;
/// <summary>
/// Gets the current settings
/// </summary>
internal SqlToolsSettings CurrentSettings
{
get { return SettingsService<SqlToolsSettings>.Instance.CurrentSettings; }
}
public ConnectedBindingQueue()
: this(true)
{
}
public ConnectedBindingQueue(bool needsMetadata)
{
this.needsMetadata = needsMetadata;
this.connectionOpener = new SqlConnectionOpener();
}
// For testing purposes only
internal void SetConnectionOpener(SqlConnectionOpener opener)
{
this.connectionOpener = opener;
}
/// <summary>
/// Generate a unique key based on the ConnectionInfo object
/// </summary>
/// <param name="connInfo"></param>
private string GetConnectionContextKey(ConnectionInfo connInfo)
{
ConnectionDetails details = connInfo.ConnectionDetails;
string key = string.Format("{0}_{1}_{2}_{3}",
details.ServerName ?? "NULL",
details.DatabaseName ?? "NULL",
details.UserName ?? "NULL",
details.AuthenticationType ?? "NULL"
);
if (!string.IsNullOrEmpty(details.DatabaseDisplayName))
{
key += "_" + details.DatabaseDisplayName;
}
if (!string.IsNullOrEmpty(details.GroupId))
{
key += "_" + details.GroupId;
}
return key;
}
/// <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, int millisecondsTimeout)
{
string connectionKey = GetConnectionContextKey(serverName, databaseName);
var contexts = GetBindingContexts(connectionKey);
foreach (var bindingContext in contexts)
{
if (bindingContext.BindingLock.WaitOne(millisecondsTimeout))
{
bindingContext.ServerConnection.Disconnect();
}
}
}
public void OpenConnections(string serverName, string databaseName, int millisecondsTimeout)
{
string connectionKey = GetConnectionContextKey(serverName, databaseName);
var contexts = GetBindingContexts(connectionKey);
foreach (var bindingContext in contexts)
{
if (bindingContext.BindingLock.WaitOne(millisecondsTimeout))
{
try
{
bindingContext.ServerConnection.Connect();
}
catch
{
//TODO: remove the binding context?
}
}
}
}
public void RemoveBindingContext(ConnectionInfo connInfo)
{
string connectionKey = GetConnectionContextKey(connInfo);
if (BindingContextExists(connectionKey))
{
RemoveBindingContext(connectionKey);
}
}
/// <summary>
/// Use a ConnectionInfo item to create a connected binding context
/// </summary>
/// <param name="connInfo">Connection info used to create binding context</param>
/// <param name="overwrite">Overwrite existing context</param>
public virtual string AddConnectionContext(ConnectionInfo connInfo, string featureName = null, bool overwrite = false)
{
if (connInfo == null)
{
return string.Empty;
}
// lookup the current binding context
string connectionKey = GetConnectionContextKey(connInfo);
if (BindingContextExists(connectionKey))
{
if (overwrite)
{
RemoveBindingContext(connectionKey);
}
else
{
// no need to populate the context again since the context already exists
return connectionKey;
}
}
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
if (bindingContext.BindingLock.WaitOne())
{
try
{
bindingContext.BindingLock.Reset();
SqlConnection sqlConn = connectionOpener.OpenSqlConnection(connInfo, featureName);
// populate the binding context to work with the SMO metadata provider
bindingContext.ServerConnection = new ServerConnection(sqlConn);
if (this.needsMetadata)
{
bindingContext.SmoMetadataProvider = SmoMetadataProvider.CreateConnectedProvider(bindingContext.ServerConnection);
bindingContext.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
bindingContext.MetadataDisplayInfoProvider.BuiltInCasing =
this.CurrentSettings.SqlTools.IntelliSense.LowerCaseSuggestions.Value
? CasingStyle.Lowercase : CasingStyle.Uppercase;
bindingContext.Binder = BinderProvider.CreateBinder(bindingContext.SmoMetadataProvider);
}
bindingContext.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout;
bindingContext.IsConnected = true;
}
catch (Exception)
{
bindingContext.IsConnected = false;
}
finally
{
bindingContext.BindingLock.Set();
}
}
return connectionKey;
}
}
}

View File

@@ -0,0 +1,81 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Threading;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.Common;
using Microsoft.SqlServer.Management.SqlParser.MetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Parser;
namespace Microsoft.SqlTools.CoreServices.LanguageServices
{
/// <summary>
/// The context used for binding requests
/// </summary>
public interface IBindingContext
{
/// <summary>
/// Gets or sets a flag indicating if the context is connected
/// </summary>
bool IsConnected { get; set; }
/// <summary>
/// Gets or sets the binding server connection
/// </summary>
ServerConnection ServerConnection { get; set; }
/// <summary>
/// Gets or sets the metadata display info provider
/// </summary>
MetadataDisplayInfoProvider MetadataDisplayInfoProvider { get; set; }
/// <summary>
/// Gets or sets the SMO metadata provider
/// </summary>
SmoMetadataProvider SmoMetadataProvider { get; set; }
/// <summary>
/// Gets or sets the binder
/// </summary>
IBinder Binder { get; set; }
/// <summary>
/// Gets the binding lock object
/// </summary>
ManualResetEvent BindingLock { get; }
/// <summary>
/// Gets or sets the binding operation timeout in milliseconds
/// </summary>
int BindingTimeout { get; set; }
/// <summary>
/// Gets or sets the current connection parse options
/// </summary>
ParseOptions ParseOptions { get; }
/// <summary>
/// Gets or sets the current connection server version
/// </summary>
ServerVersion ServerVersion { get; }
/// <summary>
/// Gets or sets the database engine type
/// </summary>
DatabaseEngineType DatabaseEngineType { get; }
/// <summary>
/// Gets or sets the T-SQL version
/// </summary>
TransactSqlVersion TransactSqlVersion { get; }
/// <summary>
/// Gets or sets the database compatibility level
/// </summary>
DatabaseCompatibilityLevel DatabaseCompatibilityLevel { get; }
}
}

View File

@@ -0,0 +1,22 @@
//
// 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.CoreServices.LanguageServices
{
public static class LanguageContants {
private const int OneSecond = 1000;
internal const int DiagnosticParseDelay = 750;
internal const int HoverTimeout = 500;
internal const int BindingTimeout = 500;
internal const int OnConnectionWaitTimeout = 300 * OneSecond;
internal const int PeekDefinitionTimeout = 10 * OneSecond;
}
}

View File

@@ -0,0 +1,77 @@
//
// 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;
namespace Microsoft.SqlTools.CoreServices.LanguageServices
{
/// <summary>
/// Class that stores the state of a binding queue request item
/// </summary>
public class QueueItem
{
/// <summary>
/// QueueItem constructor
/// </summary>
public QueueItem()
{
this.ItemProcessed = new ManualResetEvent(initialState: false);
}
/// <summary>
/// Gets or sets the queue item key
/// </summary>
public string Key { get; set; }
/// <summary>
/// Gets or sets the bind operation callback method
/// </summary>
public Func<IBindingContext, CancellationToken, object> BindOperation { get; set; }
/// <summary>
/// Gets or sets the timeout operation to call if the bind operation doesn't finish within timeout period
/// </summary>
public Func<IBindingContext, object> TimeoutOperation { get; set; }
/// <summary>
/// Gets or sets the operation to call if the bind operation encounters an unexpected exception.
/// Supports returning an object in case of the exception occurring since in some cases we need to be
/// tolerant of error cases and still return some value
/// </summary>
public Func<Exception, object> ErrorHandler { get; set; }
/// <summary>
/// Gets or sets an event to signal when this queue item has been processed
/// </summary>
public virtual ManualResetEvent ItemProcessed { get; set; }
/// <summary>
/// Gets or sets the result of the queued task
/// </summary>
public object Result { get; set; }
/// <summary>
/// Gets or sets the binding operation timeout in milliseconds
/// </summary>
public int? BindingTimeout { get; set; }
/// <summary>
/// Gets or sets the timeout for how long to wait for the binding lock
/// </summary>
public int? WaitForLockTimeout { get; set; }
/// <summary>
/// Converts the result of the execution to type T
/// </summary>
public T GetResultAsT<T>() where T : class
{
//var task = this.ResultsTask;
return (this.Result != null)
? this.Result as T
: null;
}
}
}

View File

@@ -0,0 +1,100 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using Microsoft.SqlTools.Hosting.Contracts;
namespace Microsoft.SqlTools.CoreServices.LanguageServices.Contracts
{
public class TelemetryProperties
{
public string EventName { get; set; }
/// <summary>
/// Telemetry properties
/// </summary>
public Dictionary<string, string> Properties { get; set; }
/// <summary>
/// Telemetry measures
/// </summary>
public Dictionary<string, double> Measures { get; set; }
}
/// <summary>
/// Parameters sent back with an IntelliSense ready event
/// </summary>
public class TelemetryParams
{
public TelemetryProperties Params { get; set; }
}
/// <summary>
/// Event sent when the language service needs to add a telemetry event
/// </summary>
public class TelemetryNotification
{
public static readonly
EventType<TelemetryParams> Type =
EventType<TelemetryParams>.Create("telemetry/sqlevent");
}
/// <summary>
/// List of telemetry events
/// </summary>
public static class TelemetryEventNames
{
/// <summary>
/// telemetry event name for auto complete response time
/// </summary>
public const string IntellisenseQuantile = "IntellisenseQuantile";
/// <summary>
/// telemetry event name for when definition is requested
/// </summary>
public const string PeekDefinitionRequested = "PeekDefinitionRequested";
/// <summary>
/// telemetry event name for when definition is requested
/// </summary>
public const string FormatCode = "FormatCode";
}
/// <summary>
/// List of properties used in telemetry events
/// </summary>
public static class TelemetryPropertyNames
{
/// <summary>
/// Is a connection to an Azure database or not
/// </summary>
public const string IsAzure = "IsAzure";
/// <summary>
/// Did an event succeed or not
/// </summary>
public const string Succeeded = "Succeeded";
/// <summary>
/// Was the action against a connected file or similar resource, or not
/// </summary>
public const string Connected = "Connected";
/// <summary>
/// Format type property - should be one of <see cref="DocumentFormatType"/> or <see cref="RangeFormatType"/>
/// </summary>
public const string FormatType = "FormatType";
/// <summary>
/// A full document format
/// </summary>
public const string DocumentFormatType = "Document";
/// <summary>
/// A document range format
/// </summary>
public const string RangeFormatType = "Range";
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,880 @@
# String resource file
#
# When processed by the String Resource Tool, this file generates
# both a .CS and a .RESX file with the same name as the file.
# The .CS file contains a class which can be used to access these
# string resources, including the ability to format in
# parameters, which are identified with the .NET {x} format
# (see String.Format help).
#
# Comments below assume the file name is SR.strings.
#
# Lines starting with a semicolon ";" are also treated as comments, but
# in a future version they will be extracted and made available in LocStudio
# Put your comments to localizers _before_ the string they apply to.
#
# SMO build specific comment
# after generating the .resx file, run srgen on it and get the .resx file
# please remember to also check that .resx in, along with the
# .strings and .cs files
[strings]
############################################################################
# Connection Service
ConnectionServiceConnectErrorNullParams = Connection parameters cannot be null
ConnectionServiceListDbErrorNullOwnerUri = OwnerUri cannot be null or empty
ConnectionServiceListDbErrorNotConnected(string uri) = SpecifiedUri '{0}' does not have existing connection
ConnectionServiceDbErrorDefaultNotConnected(string uri) = Specified URI '{0}' does not have a default connection
ConnectionServiceConnStringInvalidAuthType(string authType) = Invalid value '{0}' for AuthenticationType. Valid values are 'Integrated' and 'SqlLogin'.
ConnectionServiceConnStringInvalidIntent(string intent) = Invalid value '{0}' for ApplicationIntent. Valid values are 'ReadWrite' and 'ReadOnly'.
ConnectionServiceConnectionCanceled = Connection canceled
### Connection Params Validation Errors
ConnectionParamsValidateNullOwnerUri = OwnerUri cannot be null or empty
ConnectionParamsValidateNullConnection = Connection details object cannot be null
ConnectionParamsValidateNullServerName = ServerName cannot be null or empty
ConnectionParamsValidateNullSqlAuth(string component) = {0} cannot be null or empty when using SqlLogin authentication
### General connection service strings
AzureSqlDbEdition = Azure SQL DB
AzureSqlDwEdition = Azure SQL Data Warehouse
AzureSqlStretchEdition = Azure SQL Stretch Database
AzureSqlAnalyticsOnDemandEdition = Azure SQL Analytics on-demand
############################################################################
# Query Execution Service
### Cancel Request
QueryServiceCancelAlreadyCompleted = The query has already completed, it cannot be cancelled
QueryServiceCancelDisposeFailed = Query successfully cancelled, failed to dispose query. Owner URI not found.
QueryServiceQueryCancelled = Query was canceled by user
### Subset Request
QueryServiceSubsetBatchNotCompleted = The batch has not completed, yet
QueryServiceSubsetBatchOutOfRange = Batch index cannot be less than 0 or greater than the number of batches
QueryServiceSubsetResultSetOutOfRange = Result set index cannot be less than 0 or greater than the number of result sets
### Data Reader Exceptions
QueryServiceDataReaderByteCountInvalid = Maximum number of bytes to return must be greater than zero
QueryServiceDataReaderCharCountInvalid = Maximum number of chars to return must be greater than zero
QueryServiceDataReaderXmlCountInvalid = Maximum number of XML bytes to return must be greater than zero
### File Stream Wrapper Exceptions
QueryServiceFileWrapperWriteOnly = Access method cannot be write-only
QueryServiceFileWrapperNotInitialized = FileStreamWrapper must be initialized before performing operations
QueryServiceFileWrapperReadOnly = This FileStreamWrapper cannot be used for writing
### Query Request
QueryServiceAffectedOneRow = (1 row affected)
QueryServiceAffectedRows(long rows) = ({0} rows affected)
QueryServiceCompletedSuccessfully = Commands completed successfully.
QueryServiceErrorFormat(int msg, int lvl, int state, int line, string newLine, string message) = Msg {0}, Level {1}, State {2}, Line {3}{4}{5}
QueryServiceQueryFailed(string message) = Query failed: {0}
QueryServiceColumnNull = (No column name)
QueryServiceCellNull = NULL
QueryServiceRequestsNoQuery = The requested query does not exist
QueryServiceQueryInvalidOwnerUri = Cannot connect to the database due to invalid OwnerUri
QueryServiceQueryInProgress = A query is already in progress for this editor session. Please cancel this query or wait for its completion.
QueryServiceMessageSenderNotSql = Sender for OnInfoMessage event must be a SqlConnection
QueryServiceResultSetAddNoRows = Cannot add row to result buffer, data reader does not contain rows
QueryServiceResultSetHasNoResults = Query has no results to return
QueryServiceResultSetTooLarge = Result set has too many rows to be safely loaded
### Save As Requests
QueryServiceSaveAsResultSetNotComplete = Result cannot be saved until query execution has completed
QueryServiceSaveAsMiscStartingError = Internal error occurred while starting save task
QueryServiceSaveAsInProgress = A save request to the same path is in progress
QueryServiceSaveAsFail(string fileName, string message) = Failed to save {0}: {1}
### MISC
QueryServiceResultSetNotRead = Cannot read subset unless the results have been read from the server
QueryServiceResultSetStartRowOutOfRange = Start row cannot be less than 0 or greater than the number of rows in the result set
QueryServiceResultSetRowCountOutOfRange = Row count must be a positive integer
QueryServiceResultSetNoColumnSchema = Could not retrieve column schema for result set
QueryServiceExecutionPlanNotFound = Could not retrieve an execution plan from the result set
############################################################################
# Language Service
PeekDefinitionAzureError(string errorMessage) = This feature is currently not supported on Azure SQL DB and Data Warehouse: {0}
PeekDefinitionError(string errorMessage) = An unexpected error occurred during Peek Definition execution: {0}
PeekDefinitionNoResultsError = No results were found.
PeekDefinitionDatabaseError = No database object was retrieved.
PeekDefinitionNotConnectedError = Please connect to a server.
PeekDefinitionTimedoutError = Operation timed out.
PeekDefinitionTypeNotSupportedError = This object type is currently not supported by this feature.
ErrorEmptyStringReplacement = Replacement of an empty string by an empty string.
############################################################################
# Workspace Service
WorkspaceServicePositionLineOutOfRange = Position is outside of file line range
WorkspaceServicePositionColumnOutOfRange(int line) = Position is outside of column range for line {0}
WorkspaceServiceBufferPositionOutOfOrder(int sLine, int sCol, int eLine, int eCol) = Start position ({0}, {1}) must come before or be equal to the end position ({2}, {3})
############################################################################
# Edit Data Service
EditDataObjectNotFound = Table or view requested for edit could not be found
EditDataSessionNotFound = Edit session does not exist.
EditDataSessionAlreadyExists = Edit session already exists.
EditDataSessionNotInitialized = Edit session has not been initialized
EditDataSessionAlreadyInitialized = Edit session has already been initialized
EditDataSessionAlreadyInitializing = Edit session has already been initialized or is in the process of initializing
EditDataMetadataNotExtended = Table metadata does not have extended properties
EditDataMetadataObjectNameRequired = A object name must be provided
EditDataMetadataTooManyIdentifiers = Explicitly specifying server or database is not supported
EditDataFilteringNegativeLimit = Result limit cannot be negative
EditDataUnsupportedObjectType(string typeName) = Database object {0} cannot be used for editing.
EditDataQueryFailed = Query execution failed, see messages for details
EditDataQueryNotCompleted = Query has not completed execution
EditDataQueryImproperResultSets = Query did not generate exactly one result set
EditDataFailedAddRow = Failed to add new row to update cache
EditDataRowOutOfRange = Given row ID is outside the range of rows in the edit cache
EditDataUpdatePending = An update is already pending for this row and must be reverted first
EditDataUpdateNotPending = Given row ID does not have pending update
EditDataObjectMetadataNotFound = Table or view metadata could not be found
EditDataInvalidFormat(string colName, string colType) = Invalid format for column '{0}', column is defined as {1}
EditDataInvalidFormatBinary = Invalid format for binary column
EditDataInvalidFormatBoolean = Allowed values for boolean columns are 0, 1, "true", or "false"
EditDataCreateScriptMissingValue(string colName) = The column '{0}' is defined as NOT NULL but was not given a value
EditDataDeleteSetCell = A delete is pending for this row, a cell update cannot be applied.
EditDataColumnIdOutOfRange = Column ID must be in the range of columns for the query
EditDataColumnCannotBeEdited = Column cannot be edited
EditDataColumnNoKeyColumns = No key columns were found
EditDataScriptFilePathNull = An output filename must be provided
EditDataCommitInProgress = A commit task is in progress. Please wait for completion.
EditDataComputedColumnPlaceholder = <TBD>
EditDataTimeOver24Hrs = TIME column values must be between 00:00:00.0000000 and 23:59:59.9999999
EditDataNullNotAllowed = NULL is not allowed for this column
EditDataValueTooLarge(string value, string columnType) = Value {0} is too large to fit in column of type {1}
############################################################################
# DacFx Resources
EE_BatchSqlMessageNoProcedureInfo = Msg {0}, Level {1}, State {2}, Line {3}
EE_BatchSqlMessageWithProcedureInfo = Msg {0}, Level {1}, State {2}, Procedure {3}, Line {4}
EE_BatchSqlMessageNoLineInfo = Msg {0}, Level {1}, State {2}
EE_BatchError_Exception = An error occurred while the batch was being processed. The error message is: {0}
EE_BatchExecutionInfo_RowsAffected = ({0} row(s) affected)
EE_ExecutionNotYetCompleteError = The previous execution is not yet complete.
EE_ScriptError_Error = A scripting error occurred.
EE_ScriptError_ParsingSyntax = Incorrect syntax was encountered while {0} was being parsed.
EE_ScriptError_FatalError = A fatal error occurred.
EE_ExecutionInfo_FinalizingLoop = Batch execution completed {0} times...
EE_ExecutionInfo_QueryCancelledbyUser = You cancelled the query.
EE_BatchExecutionError_Halting = An error occurred while the batch was being executed.
EE_BatchExecutionError_Ignoring = An error occurred while the batch was being executed, but the error has been ignored.
EE_ExecutionInfo_InitializingLoop = Beginning execution loop
EE_ExecutionError_CommandNotSupported = Command {0} is not supported.
EE_ExecutionError_VariableNotFound = The variable {0} could not be found.
BatchParserWrapperExecutionEngineError = SQL Execution error: {0}
BatchParserWrapperExecutionError = Batch parser wrapper execution: {0} found... at line {1}: {2} Description: {3}
BatchParserWrapperExecutionEngineBatchMessage = Batch parser wrapper execution engine batch message received: Message: {0} Detailed message: {1}
BatchParserWrapperExecutionEngineBatchResultSetProcessing = Batch parser wrapper execution engine batch ResultSet processing: DataReader.FieldCount: {0} DataReader.RecordsAffected: {1}
BatchParserWrapperExecutionEngineBatchResultSetFinished = Batch parser wrapper execution engine batch ResultSet finished.
BatchParserWrapperExecutionEngineBatchCancelling = Canceling batch parser wrapper batch execution.
EE_ScriptError_Warning = Scripting warning.
TroubleshootingAssistanceMessage = For more information about this error, see the troubleshooting topics in the product documentation.
BatchParser_CircularReference = File '{0}' recursively included.
BatchParser_CommentNotTerminated = Missing end comment mark '*/'.
BatchParser_StringNotTerminated = Unclosed quotation mark after the character string.
BatchParser_IncorrectSyntax = Incorrect syntax was encountered while parsing '{0}'.
BatchParser_VariableNotDefined = Variable {0} is not defined.
############################################################################
# Workspace Service
TestLocalizationConstant = test
############################################################################
# Utilities
SqlScriptFormatterDecimalMissingPrecision = Exact numeric column is missing numeric precision or numeric scale
SqlScriptFormatterLengthTypeMissingSize = Column with length is missing size
SqlScriptFormatterScalarTypeMissingScale = Scalar column missing scale
############################################################################
# Object Explorer Service
TreeNodeError = Error expanding: {0}
ServerNodeConnectionError = Error connecting to {0}
SchemaHierarchy_Aggregates = Aggregates
SchemaHierarchy_ServerRoles = Server Roles
SchemaHierarchy_ApplicationRoles = Application Roles
SchemaHierarchy_Assemblies = Assemblies
SchemaHierarchy_AssemblyFiles = Assembly Files
SchemaHierarchy_AsymmetricKeys = Asymmetric Keys
SchemaHierarchy_DatabaseAsymmetricKeys = Asymmetric Keys
SchemaHierarchy_DataCompressionOptions = Data Compression Options
SchemaHierarchy_Certificates = Certificates
SchemaHierarchy_FileTables = FileTables
SchemaHierarchy_DatabaseCertificates = Certificates
SchemaHierarchy_CheckConstraints = Check Constraints
SchemaHierarchy_Columns = Columns
SchemaHierarchy_Constraints = Constraints
SchemaHierarchy_Contracts = Contracts
SchemaHierarchy_Credentials = Credentials
SchemaHierarchy_ErrorMessages = Error Messages
SchemaHierarchy_ServerRoleMembership = Server Role Membership
SchemaHierarchy_DatabaseOptions = Database Options
SchemaHierarchy_DatabaseRoles = Database Roles
SchemaHierarchy_RoleMemberships = Role Memberships
SchemaHierarchy_DatabaseTriggers = Database Triggers
SchemaHierarchy_DefaultConstraints = Default Constraints
SchemaHierarchy_Defaults = Defaults
SchemaHierarchy_Sequences = Sequences
SchemaHierarchy_Endpoints = Endpoints
SchemaHierarchy_EventNotifications = Event Notifications
SchemaHierarchy_ServerEventNotifications = Server Event Notifications
SchemaHierarchy_ExtendedProperties = Extended Properties
SchemaHierarchy_FileGroups = Filegroups
SchemaHierarchy_ForeignKeys = Foreign Keys
SchemaHierarchy_FullTextCatalogs = Full-Text Catalogs
SchemaHierarchy_FullTextIndexes = Full-Text Indexes
SchemaHierarchy_Functions = Functions
SchemaHierarchy_Indexes = Indexes
SchemaHierarchy_InlineFunctions = Inline Functions
SchemaHierarchy_Keys = Keys
SchemaHierarchy_LinkedServers = Linked Servers
SchemaHierarchy_LinkedServerLogins = Linked Server Logins
SchemaHierarchy_Logins = Logins
SchemaHierarchy_MasterKey = Master Key
SchemaHierarchy_MasterKeys = Master Keys
SchemaHierarchy_MessageTypes = Message Types
SchemaHierarchy_MultiSelectFunctions = Table-Valued Functions
SchemaHierarchy_Parameters = Parameters
SchemaHierarchy_PartitionFunctions = Partition Functions
SchemaHierarchy_PartitionSchemes = Partition Schemes
SchemaHierarchy_Permissions = Permissions
SchemaHierarchy_PrimaryKeys = Primary Keys
SchemaHierarchy_Programmability = Programmability
SchemaHierarchy_Queues = Queues
SchemaHierarchy_RemoteServiceBindings = Remote Service Bindings
SchemaHierarchy_ReturnedColumns = Returned Columns
SchemaHierarchy_Roles = Roles
SchemaHierarchy_Routes = Routes
SchemaHierarchy_Rules = Rules
SchemaHierarchy_Schemas = Schemas
SchemaHierarchy_Security = Security
SchemaHierarchy_ServerObjects = Server Objects
SchemaHierarchy_Management = Management
SchemaHierarchy_ServerTriggers = Triggers
SchemaHierarchy_ServiceBroker = Service Broker
SchemaHierarchy_Services = Services
SchemaHierarchy_Signatures = Signatures
SchemaHierarchy_LogFiles = Log Files
SchemaHierarchy_Statistics = Statistics
SchemaHierarchy_Storage = Storage
SchemaHierarchy_StoredProcedures = Stored Procedures
SchemaHierarchy_SymmetricKeys = Symmetric Keys
SchemaHierarchy_Synonyms = Synonyms
SchemaHierarchy_Tables = Tables
SchemaHierarchy_Triggers = Triggers
SchemaHierarchy_Types = Types
SchemaHierarchy_UniqueKeys = Unique Keys
SchemaHierarchy_UserDefinedDataTypes = User-Defined Data Types
SchemaHierarchy_UserDefinedTypes = User-Defined Types (CLR)
SchemaHierarchy_Users = Users
SchemaHierarchy_Views = Views
SchemaHierarchy_XmlIndexes = XML Indexes
SchemaHierarchy_XMLSchemaCollections = XML Schema Collections
SchemaHierarchy_UserDefinedTableTypes = User-Defined Table Types
SchemaHierarchy_FilegroupFiles = Files
MissingCaption = Missing Caption
SchemaHierarchy_BrokerPriorities = Broker Priorities
SchemaHierarchy_CryptographicProviders = Cryptographic Providers
SchemaHierarchy_DatabaseAuditSpecifications = Database Audit Specifications
SchemaHierarchy_DatabaseEncryptionKeys = Database Encryption Keys
SchemaHierarchy_EventSessions = Event Sessions
SchemaHierarchy_FullTextStopLists = Full Text Stoplists
SchemaHierarchy_ResourcePools = Resource Pools
SchemaHierarchy_ServerAudits = Audits
SchemaHierarchy_ServerAuditSpecifications = Server Audit Specifications
SchemaHierarchy_SpatialIndexes = Spatial Indexes
SchemaHierarchy_WorkloadGroups = Workload Groups
SchemaHierarchy_SqlFiles = SQL Files
SchemaHierarchy_ServerFunctions = Server Functions
SchemaHierarchy_SqlType = SQL Type
SchemaHierarchy_ServerOptions = Server Options
SchemaHierarchy_DatabaseDiagrams = Database Diagrams
SchemaHierarchy_SystemTables = System Tables
SchemaHierarchy_Databases = Databases
SchemaHierarchy_SystemContracts = System Contracts
SchemaHierarchy_SystemDatabases = System Databases
SchemaHierarchy_SystemMessageTypes = System Message Types
SchemaHierarchy_SystemQueues = System Queues
SchemaHierarchy_SystemServices = System Services
SchemaHierarchy_SystemStoredProcedures = System Stored Procedures
SchemaHierarchy_SystemViews = System Views
SchemaHierarchy_DataTierApplications = Data-tier Applications
SchemaHierarchy_ExtendedStoredProcedures = Extended Stored Procedures
SchemaHierarchy_SystemAggregateFunctions = Aggregate Functions
SchemaHierarchy_SystemApproximateNumerics = Approximate Numerics
SchemaHierarchy_SystemBinaryStrings = Binary Strings
SchemaHierarchy_SystemCharacterStrings = Character Strings
SchemaHierarchy_SystemCLRDataTypes = CLR Data Types
SchemaHierarchy_SystemConfigurationFunctions = Configuration Functions
SchemaHierarchy_SystemCursorFunctions = Cursor Functions
SchemaHierarchy_SystemDataTypes = System Data Types
SchemaHierarchy_SystemDateAndTime = Date and Time
SchemaHierarchy_SystemDateAndTimeFunctions = Date and Time Functions
SchemaHierarchy_SystemExactNumerics = Exact Numerics
SchemaHierarchy_SystemFunctions = System Functions
SchemaHierarchy_SystemHierarchyIdFunctions = Hierarchy Id Functions
SchemaHierarchy_SystemMathematicalFunctions = Mathematical Functions
SchemaHierarchy_SystemMetadataFunctions = Metadata Functions
SchemaHierarchy_SystemOtherDataTypes = Other Data Types
SchemaHierarchy_SystemOtherFunctions = Other Functions
SchemaHierarchy_SystemRowsetFunctions = Rowset Functions
SchemaHierarchy_SystemSecurityFunctions = Security Functions
SchemaHierarchy_SystemSpatialDataTypes = Spatial Data Types
SchemaHierarchy_SystemStringFunctions = String Functions
SchemaHierarchy_SystemSystemStatisticalFunctions = System Statistical Functions
SchemaHierarchy_SystemTextAndImageFunctions = Text and Image Functions
SchemaHierarchy_SystemUnicodeCharacterStrings = Unicode Character Strings
SchemaHierarchy_AggregateFunctions = Aggregate Functions
SchemaHierarchy_ScalarValuedFunctions = Scalar-valued Functions
SchemaHierarchy_TableValuedFunctions = Table-valued Functions
SchemaHierarchy_SystemExtendedStoredProcedures = System Extended Stored Procedures
SchemaHierarchy_BuiltInType = Built-in Types
SchemaHierarchy_BuiltInServerRole = Built-in Server Roles
SchemaHierarchy_UserWithPassword = User with Password
SchemaHierarchy_SearchPropertyList = Search Property List
SchemaHierarchy_SecurityPolicies = Security Policies
SchemaHierarchy_SecurityPredicates = Security Predicates
SchemaHierarchy_ServerRole = Server Role
SchemaHierarchy_SearchPropertyLists = Search Property Lists
SchemaHierarchy_ColumnStoreIndexes = Column Store Indexes
SchemaHierarchy_TableTypeIndexes = Table Type Indexes
SchemaHierarchy_Server = Server
SchemaHierarchy_SelectiveXmlIndexes = Selective XML Indexes
SchemaHierarchy_XmlNamespaces = XML Namespaces
SchemaHierarchy_XmlTypedPromotedPaths = XML Typed Promoted Paths
SchemaHierarchy_SqlTypedPromotedPaths = T-SQL Typed Promoted Paths
SchemaHierarchy_DatabaseScopedCredentials = Database Scoped Credentials
SchemaHierarchy_ExternalDataSources = External Data Sources
SchemaHierarchy_ExternalFileFormats = External File Formats
SchemaHierarchy_ExternalResources = External Resources
SchemaHierarchy_ExternalTables = External Tables
SchemaHierarchy_AlwaysEncryptedKeys = Always Encrypted Keys
SchemaHierarchy_ColumnMasterKeys = Column Master Keys
SchemaHierarchy_ColumnEncryptionKeys = Column Encryption Keys
SchemaHierarchy_SubroutineParameterLabelFormatString = {0} ({1}, {2}, {3})
SchemaHierarchy_SubroutineParameterNoDefaultLabel = No default
SchemaHierarchy_SubroutineParameterInputLabel = Input
SchemaHierarchy_SubroutineParameterInputOutputLabel = Input/Output
SchemaHierarchy_SubroutineParameterInputReadOnlyLabel = Input/ReadOnly
SchemaHierarchy_SubroutineParameterInputOutputReadOnlyLabel = Input/Output/ReadOnly
SchemaHierarchy_SubroutineParameterDefaultLabel = Default
SchemaHierarchy_NullColumn_Label = null
SchemaHierarchy_NotNullColumn_Label = not null
SchemaHierarchy_UDDTLabelWithType = {0} ({1}, {2})
SchemaHierarchy_UDDTLabelWithoutType = {0} ({1})
SchemaHierarchy_ComputedColumnLabelWithType = {0} ({1}Computed, {2}, {3})
SchemaHierarchy_ComputedColumnLabelWithoutType = {0} ({1}Computed)
SchemaHierarchy_ColumnSetLabelWithoutType = {0} (Column Set, {1})
SchemaHierarchy_ColumnSetLabelWithType = {0} (Column Set, {1}{2}, {3})
SchemaHierarchy_ColumnSetLabelWithTypeAndKeyString = {0} (Column Set, {1}, {2}, {3})
UniqueIndex_LabelPart = Unique
NonUniqueIndex_LabelPart = Non-Unique
ClusteredIndex_LabelPart = Clustered
NonClusteredIndex_LabelPart = Non-Clustered
History_LabelPart = History
SystemVersioned_LabelPart = System-Versioned
External_LabelPart = External
FileTable_LabelPart = File Table
DatabaseNotAccessible = The database {0} is not accessible.
############################################################################
# Scripting Service
ScriptingParams_ConnectionString_Property_Invalid = Error parsing ScriptingParams.ConnectionString property.
ScriptingParams_FilePath_Property_Invalid = Invalid directory specified by the ScriptingParams.FilePath property.
ScriptingListObjectsCompleteParams_ConnectionString_Property_Invalid = Error parsing ScriptingListObjectsCompleteParams.ConnectionString property.
StoredProcedureScriptParameterComment = -- TODO: Set parameter values here.
ScriptingGeneralError = An error occurred while scripting the objects.
ScriptingExecuteNotSupportedError = Scripting as Execute is only supported for Stored Procedures
############################################################################
# Admin Service
unavailable = Unavailable
filegroup_dialog_defaultFilegroup = Current default filegroup: {0}
filegroup_dialog_title = New Filegroup for {0}
filegroups_default = Default
filegroups_files = Files
filegroups_name = Name
filegroups_readonly = Read-Only
general_autogrowth = Autogrowth / Maxsize
general_builderText = ...
general_default = &lt;default&gt;
general_fileGroup = Filegroup
general_fileName = Logical Name
general_fileType = File Type
general_initialSize = Initial Size (MB)
general_newFilegroup = &lt;new filegroup&gt;
general_path = Path
general_physicalFileName = File Name
general_rawDevice = &lt;raw device&gt;
general_recoveryModel_bulkLogged = Bulk-logged
general_recoveryModel_full = Full
general_recoveryModel_simple = Simple
general_titleSearchOwner = Select Database Owner
prototype_autogrowth_disabled = None
prototype_autogrowth_restrictedGrowthByMB = By {0} MB, Limited to {1} MB
prototype_autogrowth_restrictedGrowthByPercent = By {0} percent, Limited to {1} MB
prototype_autogrowth_unrestrictedGrowthByMB = By {0} MB, Unlimited
prototype_autogrowth_unrestrictedGrowthByPercent = By {0} percent, Unlimited
prototype_autogrowth_unlimitedfilestream = Unlimited
prototype_autogrowth_limitedfilestream = Limited to {0} MB
prototype_db_category_automatic = Automatic
prototype_db_category_servicebroker = Service Broker
prototype_db_category_collation = Collation
prototype_db_category_cursor = Cursor
prototype_db_category_misc = Miscellaneous
prototype_db_category_recovery = Recovery
prototype_db_category_state = State
prototype_db_prop_ansiNullDefault = ANSI NULL Default
prototype_db_prop_ansiNulls = ANSI NULLS Enabled
prototype_db_prop_ansiPadding = ANSI Padding Enabled
prototype_db_prop_ansiWarnings = ANSI Warnings Enabled
prototype_db_prop_arithabort = Arithmetic Abort Enabled
prototype_db_prop_autoClose = Auto Close
prototype_db_prop_autoCreateStatistics = Auto Create Statistics
prototype_db_prop_autoShrink = Auto Shrink
prototype_db_prop_autoUpdateStatistics = Auto Update Statistics
prototype_db_prop_autoUpdateStatisticsAsync = Auto Update Statistics Asynchronously
prototype_db_prop_caseSensitive = Case Sensitive
prototype_db_prop_closeCursorOnCommit = Close Cursor on Commit Enabled
prototype_db_prop_collation = Collation
prototype_db_prop_concatNullYieldsNull = Concatenate Null Yields Null
prototype_db_prop_databaseCompatibilityLevel = Database Compatibility Level
prototype_db_prop_databaseState = Database State
prototype_db_prop_defaultCursor = Default Cursor
prototype_db_prop_fullTextIndexing = Full-Text Indexing Enabled
prototype_db_prop_numericRoundAbort = Numeric Round-Abort
prototype_db_prop_pageVerify = Page Verify
prototype_db_prop_quotedIdentifier = Quoted Identifiers Enabled
prototype_db_prop_readOnly = Database Read-Only
prototype_db_prop_recursiveTriggers = Recursive Triggers Enabled
prototype_db_prop_restrictAccess = Restrict Access
prototype_db_prop_selectIntoBulkCopy = Select Into/Bulk Copy
prototype_db_prop_honorBrokerPriority = Honor Broker Priority
prototype_db_prop_serviceBrokerGuid = Service Broker Identifier
prototype_db_prop_brokerEnabled = Broker Enabled
prototype_db_prop_truncateLogOnCheckpoint = Truncate Log on Checkpoint
prototype_db_prop_dbChaining = Cross-database Ownership Chaining Enabled
prototype_db_prop_trustworthy = Trustworthy
prototype_db_prop_dateCorrelationOptimization = Date Correlation Optimization Enabled
prototype_db_prop_parameterization = Parameterization
prototype_db_prop_parameterization_value_forced = Forced
prototype_db_prop_parameterization_value_simple = Simple
prototype_file_dataFile = ROWS Data
prototype_file_logFile = LOG
prototype_file_filestreamFile = FILESTREAM Data
prototype_file_noFileGroup = Not Applicable
prototype_file_defaultpathstring = &lt;default path&gt;
title_openConnectionsMustBeClosed = Open Connections
warning_openConnectionsMustBeClosed=To change the database properties, SQL Server must close all other connections to the database_ Are you sure you want to change the properties and close all other connections?
prototype_db_prop_databaseState_value_autoClosed = AUTO_CLOSED
prototype_db_prop_databaseState_value_emergency = EMERGENCY
prototype_db_prop_databaseState_value_inaccessible = INACCESSIBLE
prototype_db_prop_databaseState_value_normal = NORMAL
prototype_db_prop_databaseState_value_offline = OFFLINE
prototype_db_prop_databaseState_value_recovering = RECOVERING
prototype_db_prop_databaseState_value_recoveryPending = RECOVERY PENDING
prototype_db_prop_databaseState_value_restoring = RESTORING
prototype_db_prop_databaseState_value_shutdown=SHUTDOWN
prototype_db_prop_databaseState_value_standby = STANDBY
prototype_db_prop_databaseState_value_suspect = SUSPECT
prototype_db_prop_defaultCursor_value_global = GLOBAL
prototype_db_prop_defaultCursor_value_local = LOCAL
prototype_db_prop_restrictAccess_value_multiple = MULTI_USER
prototype_db_prop_restrictAccess_value_restricted = RESTRICTED_USER
prototype_db_prop_restrictAccess_value_single = SINGLE_USER
prototype_db_prop_pageVerify_value_checksum = CHECKSUM
prototype_db_prop_pageVerify_value_none = NONE
prototype_db_prop_pageVerify_value_tornPageDetection = TORN_PAGE_DETECTION
prototype_db_prop_varDecimalEnabled = VarDecimal Storage Format Enabled
compatibilityLevel_katmai = SQL Server 2008 (100)
prototype_db_prop_encryptionEnabled = Encryption Enabled
prototype_db_prop_databasescopedconfig_value_off = OFF
prototype_db_prop_databasescopedconfig_value_on = ON
prototype_db_prop_databasescopedconfig_value_primary = PRIMARY
error_db_prop_invalidleadingColumns = For the distribution policy HASH, the number of leading hash columns is optional but should be from 1 to 16 columns
compatibilityLevel_denali = SQL Server 2012 (110)
compatibilityLevel_sql14 = SQL Server 2014 (120)
compatibilityLevel_sql15 = SQL Server 2016 (130)
compatibilityLevel_sqlvNext = SQL Server vNext (140)
general_containmentType_None = None
general_containmentType_Partial = Partial
filegroups_filestreamFiles = FILESTREAM Files
prototype_file_noApplicableFileGroup = No Applicable Filegroup
NeverBackedUp = Never
Error_InvalidDirectoryName = Path {0} is not a valid directory
Error_ExistingDirectoryName = For directory {0} a file with name {1} already exists
############################################################################
# Backup Service
BackupTaskName = Backup Database
# Backup File Validation Errors
BackupPathIsFolderError = Please provide a file path instead of directory path
InvalidBackupPathError = The provided path is invalid
############################################################################
# Task Service
TaskInProgress = In progress
TaskCompleted = Completed
###########################################################################
# Restore
ConflictWithNoRecovery = Specifying this option when restoring a backup with the NORECOVERY option is not permitted.
InvalidPathForDatabaseFile = Invalid path for database file: '{0}'
Log = Log
RestorePlanFailed = Failed to create restore plan
RestoreNotSupported = Restore database is not supported
RestoreTaskName = Restore Database
RestoreCopyOnly = (Copy Only)
RestoreBackupSetComponent = Component
RestoreBackupSetName = Name
RestoreBackupSetType = Type
RestoreBackupSetServer = Server
RestoreBackupSetDatabase = Database
RestoreBackupSetPosition = Position
RestoreBackupSetFirstLsn = First LSN
RestoreBackupSetLastLsn = Last LSN
RestoreBackupSetCheckpointLsn = Checkpoint LSN
RestoreBackupSetFullLsn = Full LSN
RestoreBackupSetStartDate = Start Date
RestoreBackupSetFinishDate = Finish Date
RestoreBackupSetSize = Size
RestoreBackupSetUserName = User Name
RestoreBackupSetExpiration = Expiration
TheLastBackupTaken = The last backup taken ({0})
NoBackupsetsToRestore = No backupset selected to be restored
############################################################################
# Generate Script
ScriptTaskName = scripting
############################################################################
# File Browser Validation Errors
InvalidPathError = Cannot access the specified path on the server: {0}
############################################################################
# Profiler
ProfilerConnectionNotFound = Connection not found

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Microsoft.SqlTools.CoreServices</PackageId>
<AssemblyName>Microsoft.SqlTools.CoreServices</AssemblyName>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<ApplicationIcon />
<StartupObject />
<Copyright><EFBFBD> Microsoft Corporation. All rights reserved.</Copyright>
<PackageDescription>
A collection of core services that can be reused by a Database Management Protocol-based service using the Microsoft.SqlTools.Hosting framework.
</PackageDescription>
<Description>$(PackageDescription)</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" />
<PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="System.Text.Encoding.CodePages" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" />
<PackageReference Include="System.Runtime.Loader" />
<PackageReference Include="System.Composition" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.SqlTools.Hosting.v2\Microsoft.SqlTools.Hosting.v2.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.DataProtocol.Contracts\Microsoft.SqlTools.DataProtocol.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\sr.resx" />
<None Include="Localization\sr.strings" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: InternalsVisibleTo("Microsoft.SqlTools.Hosting.UnitTests")]
[assembly: InternalsVisibleTo("Microsoft.SqlTools.Services.UnitTests")]
// Allowing internals visible access to Moq library to help testing
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

View File

@@ -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 System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.Hosting.Utility;
using Newtonsoft.Json;
namespace Microsoft.SqlTools.CoreServices.SqlContext
{
/// <summary>
/// Handles backwards compatibility of settings by checking for settings in a priority list. If a settings
/// group such as Intellisense is defined on a serialized setting it's used in the order of mssql, then sql, then
/// falls back to a default value.
/// </summary>
public class CompoundToolsSettingsValues: ISqlToolsSettingsValues
{
private List<ISqlToolsSettingsValues> priorityList = new List<ISqlToolsSettingsValues>();
private SqlToolsSettingsValues defaultValues;
public CompoundToolsSettingsValues(ISqlToolsSettingsValues mssql, ISqlToolsSettingsValues all)
{
Validate.IsNotNull(nameof(mssql), mssql);
Validate.IsNotNull(nameof(all), all);
priorityList.Add(mssql);
priorityList.Add(all);
// Always add in a fallback which has default values to be used.
defaultValues = new SqlToolsSettingsValues(createDefaults: true);
priorityList.Add(defaultValues);
}
private T GetSettingOrDefault<T>(Func<ISqlToolsSettingsValues, T> lookup)
where T : new()
{
T value = priorityList.Select( (settings) => lookup(settings)).Where(val => val != null).FirstOrDefault();
return value != null ? value : new T();
}
/// <summary>
/// Gets or sets the detailed IntelliSense settings
/// </summary>
public IntelliSenseSettings IntelliSense
{
get
{
return GetSettingOrDefault((settings) => settings.IntelliSense);
}
set
{
priorityList[0].IntelliSense = value;
}
}
// /// <summary>
// /// Gets or sets the query execution settings
// /// </summary>
// public QueryExecutionSettings QueryExecutionSettings
// {
// get
// {
// return GetSettingOrDefault((settings) => settings.QueryExecutionSettings);
// }
// set
// {
// priorityList[0].QueryExecutionSettings = value;
// }
// }
// /// <summary>
// /// Gets or sets the formatter settings
// /// </summary>
// public FormatterSettings Format
// {
// get
// {
// return GetSettingOrDefault((settings) => settings.Format);
// }
// set
// {
// priorityList[0].Format = value;
// }
// }
/// <summary>
/// Gets or sets the object explorer settings
/// </summary>
public ObjectExplorerSettings ObjectExplorer
{
get
{
return GetSettingOrDefault((settings) => settings.ObjectExplorer);
}
set
{
priorityList[0].ObjectExplorer = value;
}
}
}
}

View File

@@ -0,0 +1,33 @@
//
// 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.CoreServices.SqlContext
{
/// <summary>
/// Defines the common settings used by the tools service
/// </summary>
public interface ISqlToolsSettingsValues
{
/// <summary>
/// Intellisense specific settings
/// </summary>
IntelliSenseSettings IntelliSense { get; set; }
/// <summary>
/// Query execution specific settings
/// </summary>
// QueryExecutionSettings QueryExecutionSettings { get; set; }
// /// <summary>
// /// Formatter settings
// /// </summary>
// FormatterSettings Format { get; set; }
/// <summary>
/// Object Explorer specific settings
/// </summary>
ObjectExplorerSettings ObjectExplorer { get; set; }
}
}

View File

@@ -0,0 +1,68 @@
//
// 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.CoreServices.SqlContext
{
/// <summary>
/// Class for serialization and deserialization of IntelliSense settings
/// </summary>
public class IntelliSenseSettings
{
/// <summary>
/// Initialize the IntelliSense settings defaults
/// </summary>
public IntelliSenseSettings()
{
this.EnableIntellisense = true;
this.EnableSuggestions = true;
this.LowerCaseSuggestions = false;
this.EnableErrorChecking = true;
this.EnableQuickInfo = true;
}
/// <summary>
/// Gets or sets a flag determining if IntelliSense is enabled
/// </summary>
/// <returns></returns>
public bool EnableIntellisense { get; set; }
/// <summary>
/// Gets or sets a flag determining if suggestions are enabled
/// </summary>
/// <returns></returns>
public bool? EnableSuggestions { get; set; }
/// <summary>
/// Gets or sets a flag determining if built-in suggestions should be lowercase
/// </summary>
public bool? LowerCaseSuggestions { get; set; }
/// <summary>
/// Gets or sets a flag determining if diagnostics are enabled
/// </summary>
public bool? EnableErrorChecking { get; set; }
/// <summary>
/// Gets or sets a flag determining if quick info is enabled
/// </summary>
public bool? EnableQuickInfo { get; set; }
/// <summary>
/// Update the Intellisense settings
/// </summary>
/// <param name="settings"></param>
public void Update(IntelliSenseSettings settings)
{
if (settings != null)
{
this.EnableIntellisense = settings.EnableIntellisense;
this.EnableSuggestions = settings.EnableSuggestions;
this.LowerCaseSuggestions = settings.LowerCaseSuggestions;
this.EnableErrorChecking = settings.EnableErrorChecking;
this.EnableQuickInfo = settings.EnableQuickInfo;
}
}
}
}

View File

@@ -0,0 +1,32 @@
//
// 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.CoreServices.SqlContext
{
/// <summary>
/// Contract for receiving object explorer settings as part of workspace settings
/// </summary>
public class ObjectExplorerSettings
{
public static int DefaultCreateSessionTimeout = 45;
public static int DefaultExpandTimeout = 45;
public ObjectExplorerSettings()
{
CreateSessionTimeout = DefaultCreateSessionTimeout;
ExpandTimeout = DefaultExpandTimeout;
}
/// <summary>
/// Number of seconds to wait before fail create session request with timeout error
/// </summary>
public int CreateSessionTimeout { get; set; }
/// <summary>
/// Number of seconds to wait before fail expand request with timeout error
/// </summary>
public int ExpandTimeout { get; set; }
}
}

View File

@@ -0,0 +1,145 @@
//
// 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.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace Microsoft.SqlTools.CoreServices.SqlContext
{
/// <summary>
/// Class for serialization and deserialization of the settings the SQL Tools Service needs.
/// </summary>
public class SqlToolsSettings
{
private ISqlToolsSettingsValues sqlTools = null;
private SqlToolsSettingsValues mssqlTools = null;
private SqlToolsSettingsValues allSqlTools = null;
public ISqlToolsSettingsValues SqlTools
{
get
{
if (this.sqlTools == null)
{
this.sqlTools = new CompoundToolsSettingsValues(MssqlTools, AllSqlTools);
}
return this.sqlTools;
}
set
{
this.sqlTools = value;
}
}
/// <summary>
/// Gets or sets the underlying settings value object
/// </summary>
[JsonProperty("mssql")]
public SqlToolsSettingsValues MssqlTools
{
get
{
if (this.mssqlTools == null)
{
this.mssqlTools = new SqlToolsSettingsValues(false);
}
return this.mssqlTools;
}
set
{
this.mssqlTools = value;
}
}
/// <summary>
/// Gets or sets the underlying settings value object
/// </summary>
[JsonProperty("sql")]
public SqlToolsSettingsValues AllSqlTools
{
get
{
if (this.allSqlTools == null)
{
this.allSqlTools = new SqlToolsSettingsValues(false);
}
return this.allSqlTools;
}
set
{
this.sqlTools = value;
}
}
/// <summary>
/// Query excution settings forwarding property
/// </summary>
// public QueryExecutionSettings QueryExecutionSettings
// {
// get { return this.SqlTools.QueryExecutionSettings; }
// }
/// <summary>
/// Updates the extension settings
/// </summary>
/// <param name="settings"></param>
public void Update(SqlToolsSettings settings)
{
if (settings != null)
{
this.SqlTools.IntelliSense.Update(settings.SqlTools.IntelliSense);
}
}
/// <summary>
/// Gets a flag determining if diagnostics are enabled
/// </summary>
public bool IsDiagnosticsEnabled
{
get
{
return this.SqlTools.IntelliSense.EnableIntellisense
&& this.SqlTools.IntelliSense.EnableErrorChecking.Value;
}
}
/// <summary>
/// Gets a flag determining if suggestions are enabled
/// </summary>
public bool IsSuggestionsEnabled
{
get
{
return this.SqlTools.IntelliSense.EnableIntellisense
&& this.SqlTools.IntelliSense.EnableSuggestions.Value;
}
}
/// <summary>
/// Gets a flag determining if quick info is enabled
/// </summary>
public bool IsQuickInfoEnabled
{
get
{
return this.SqlTools.IntelliSense.EnableIntellisense
&& this.SqlTools.IntelliSense.EnableQuickInfo.Value;
}
}
/// <summary>
/// Gets a flag determining if IntelliSense is enabled
/// </summary>
public bool IsIntelliSenseEnabled
{
get
{
return this.SqlTools.IntelliSense.EnableIntellisense;
}
}
}
}

View File

@@ -0,0 +1,54 @@
//
// 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.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace Microsoft.SqlTools.CoreServices.SqlContext
{
/// <summary>
/// Class that is used to serialize and deserialize SQL Tools settings
/// </summary>
public class SqlToolsSettingsValues : ISqlToolsSettingsValues
{
/// <summary>
/// Initializes the Sql Tools settings values
/// </summary>
public SqlToolsSettingsValues(bool createDefaults = true)
{
if (createDefaults)
{
IntelliSense = new IntelliSenseSettings();
// QueryExecutionSettings = new QueryExecutionSettings();
// Format = new FormatterSettings();
}
}
/// <summary>
/// Gets or sets the detailed IntelliSense settings
/// </summary>
public IntelliSenseSettings IntelliSense { get; set; }
// /// <summary>
// /// Gets or sets the query execution settings
// /// </summary>
// [JsonProperty("query")]
// public QueryExecutionSettings QueryExecutionSettings { get; set; }
// /// <summary>
// /// Gets or sets the formatter settings
// /// </summary>
// [JsonProperty("format")]
// public FormatterSettings Format { get; set; }
/// <summary>
/// Gets or sets the formatter settings
/// </summary>
[JsonProperty("objectExplorer")]
public ObjectExplorerSettings ObjectExplorer { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
//
// 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.CoreServices.Utility
{
/// <summary>
/// Common Constant values used across multiple services
/// </summary>
public static class CommonConstants
{
public const string MasterDatabaseName = "master";
public const string MsdbDatabaseName = "msdb";
public const string ModelDatabaseName = "model";
public const string TempDbDatabaseName = "tempdb";
public const string DefaultBatchSeperator = "GO";
}
}

View File

@@ -0,0 +1,26 @@
//
// 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.CoreServices.Utility
{
public class DatabaseUtils
{
/// <summary>
/// Check if the database is a system database
/// </summary>
/// <param name="databaseName">the name of database</param>
/// <returns>return true if the database is a system database</returns>
public static bool IsSystemDatabaseConnection(string databaseName)
{
return (string.IsNullOrWhiteSpace(databaseName) ||
string.Compare(databaseName, CommonConstants.MasterDatabaseName, StringComparison.OrdinalIgnoreCase) == 0 ||
string.Compare(databaseName, CommonConstants.MsdbDatabaseName, StringComparison.OrdinalIgnoreCase) == 0 ||
string.Compare(databaseName, CommonConstants.ModelDatabaseName, StringComparison.OrdinalIgnoreCase) == 0 ||
string.Compare(databaseName, CommonConstants.TempDbDatabaseName, StringComparison.OrdinalIgnoreCase) == 0);
}
}
}

View File

@@ -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.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices
{
/// <summary>
/// A class to calculate the value for the metrics using the given bucket
/// </summary>
public class InteractionMetrics<T>
{
/// <summary>
/// Creates new instance given a bucket of metrics
/// </summary>
public InteractionMetrics(int[] metrics)
{
Validate.IsNotNull("metrics", metrics);
if(metrics.Length == 0)
{
throw new ArgumentOutOfRangeException("metrics");
}
Counters = new ConcurrentDictionary<string, T>();
if (!IsSorted(metrics))
{
Array.Sort(metrics);
}
Metrics = metrics;
}
private ConcurrentDictionary<string, T> Counters { get; }
private object perfCountersLock = new object();
/// <summary>
/// The metrics bucket
/// </summary>
public int[] Metrics { get; private set; }
/// <summary>
/// Returns true if the given list is sorted
/// </summary>
private bool IsSorted(int[] metrics)
{
if (metrics.Length > 1)
{
int previous = metrics[0];
for (int i = 1; i < metrics.Length; i++)
{
if(metrics[i] < previous)
{
return false;
}
previous = metrics[i];
}
}
return true;
}
/// <summary>
/// Update metric value given new number
/// </summary>
public void UpdateMetrics(double duration, T newValue, Func<string, T, T> updateValueFactory)
{
int metric = Metrics[Metrics.Length - 1];
for (int i = 0; i < Metrics.Length; i++)
{
if (duration <= Metrics[i])
{
metric = Metrics[i];
break;
}
}
string key = metric.ToString();
Counters.AddOrUpdate(key, newValue, updateValueFactory);
}
/// <summary>
/// Returns the quantile
/// </summary>
public Dictionary<string, T> Quantile
{
get
{
return Counters.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
}
}

View File

@@ -0,0 +1,122 @@
//
// 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.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlTools.DataProtocol.Contracts.Workspace;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.CoreServices.Workspace
{
/// <summary>
/// Class for handling requests/events that deal with the state of the workspace, including the
/// opening and closing of files, the changing of configuration, etc.
/// </summary>
/// <typeparam name="TConfig">
/// The type of the class used for serializing and deserializing the configuration. Must be the
/// actual type of the instance otherwise deserialization will be incomplete.
/// </typeparam>
public class SettingsService<TConfig> where TConfig : class, new()
{
#region Singleton Instance Implementation
private static Lazy<SettingsService<TConfig>> instance = new Lazy<SettingsService<TConfig>>(() => new SettingsService<TConfig>());
public static SettingsService<TConfig> Instance
{
get { return instance.Value; }
}
/// <summary>
/// Default, parameterless constructor.
/// TODO: Figure out how to make this truely singleton even with dependency injection for tests
/// </summary>
public SettingsService()
{
ConfigChangeCallbacks = new List<ConfigChangeCallback>();
CurrentSettings = new TConfig();
}
#endregion
#region Properties
/// <summary>
/// Current settings for the workspace
/// </summary>
public TConfig CurrentSettings { get; internal set; }
/// <summary>
/// Delegate for callbacks that occur when the configuration for the workspace changes
/// </summary>
/// <param name="newSettings">The settings that were just set</param>
/// <param name="oldSettings">The settings before they were changed</param>
/// <param name="eventContext">Context of the event that triggered the callback</param>
/// <returns></returns>
public delegate Task ConfigChangeCallback(TConfig newSettings, TConfig oldSettings, EventContext eventContext);
/// <summary>
/// List of callbacks to call when the configuration of the workspace changes
/// </summary>
private List<ConfigChangeCallback> ConfigChangeCallbacks { get; set; }
#endregion
#region Public Methods
public void InitializeService(IServiceHost serviceHost)
{
// Register the handlers for when changes to the workspae occur
serviceHost.SetAsyncEventHandler(DidChangeConfigurationNotification<TConfig>.Type, HandleDidChangeConfigurationNotification);
}
/// <summary>
/// Adds a new task to be called when the configuration has been changed. Use this to
/// handle changing configuration and changing the current configuration.
/// </summary>
/// <param name="task">Task to handle the request</param>
public void RegisterConfigChangeCallback(ConfigChangeCallback task)
{
ConfigChangeCallbacks.Add(task);
}
#endregion
#region Event Handlers
/// <summary>
/// Handles the configuration change event
/// </summary>
internal async Task HandleDidChangeConfigurationNotification(
DidChangeConfigurationParams<TConfig> configChangeParams,
EventContext eventContext)
{
try
{
Logger.Write(TraceEventType.Verbose, "HandleDidChangeConfigurationNotification");
// Propagate the changes to the event handlers
var configUpdateTasks = ConfigChangeCallbacks.Select(
t => t(configChangeParams.Settings, CurrentSettings, eventContext));
await Task.WhenAll(configUpdateTasks);
}
catch (Exception ex)
{
Logger.Write(TraceEventType.Error, "Unknown error " + ex.ToString());
// Swallow exceptions here to prevent us from crashing
// TODO: this probably means the ScriptFile model is in a bad state or out of sync with the actual file; we should recover here
return;
}
}
#endregion
}
}