Feature/reliable connection (#44)

* Initial commit of reliable connection port

* Made ReliableSqlConnection inherit from DbConnection instead of IDbConnection

* Cleanup

* Fixed autocomplete service to use reliable connection

* Fix copyright headers

* Renamed ConnectResponse.Server to ServerInfo

* Removed unused using

* Addressing code review feedback
This commit is contained in:
Mitchell Sternke
2016-09-13 18:10:26 -07:00
committed by GitHub
parent 92eb1376c1
commit f2a5654a20
41 changed files with 6358 additions and 16 deletions

View File

@@ -11,6 +11,7 @@ using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Workspace;
@@ -185,6 +186,29 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
activity(connectionInfo);
}
// try to get information about the connected SQL Server instance
try
{
ReliableConnectionHelper.ServerInfo serverInfo = ReliableConnectionHelper.GetServerVersion(connectionInfo.SqlConnection);
response.ServerInfo = new Contracts.ServerInfo()
{
ServerMajorVersion = serverInfo.ServerMajorVersion,
ServerMinorVersion = serverInfo.ServerMinorVersion,
ServerReleaseVersion = serverInfo.ServerReleaseVersion,
EngineEditionId = serverInfo.EngineEditionId,
ServerVersion = serverInfo.ServerVersion,
ServerLevel = serverInfo.ServerLevel,
ServerEdition = serverInfo.ServerEdition,
IsCloud = serverInfo.IsCloud,
AzureVersion = serverInfo.AzureVersion,
OsVersion = serverInfo.OsVersion
};
}
catch(Exception ex)
{
response.Messages = ex.ToString();
}
// return the connection result
response.ConnectionId = connectionInfo.ConnectionId.ToString();
return response;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
{
/// <summary>

View File

@@ -20,6 +20,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
/// </summary>
public string Messages { get; set; }
/// <summary>
/// Information about the connected server.
/// </summary>
public ServerInfo ServerInfo { get; set; }
/// <summary>
/// Gets or sets the actual Connection established, including Database Name
/// </summary>

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.
//
namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts
{
/// <summary>
/// Contract for information on the connected SQL Server instance.
/// </summary>
public class ServerInfo
{
/// <summary>
/// The major version of the SQL Server instance.
/// </summary>
public int ServerMajorVersion { get; set; }
/// <summary>
/// The minor version of the SQL Server instance.
/// </summary>
public int ServerMinorVersion { get; set; }
/// <summary>
/// The build of the SQL Server instance.
/// </summary>
public int ServerReleaseVersion { get; set; }
/// <summary>
/// The ID of the engine edition of the SQL Server instance.
/// </summary>
public int EngineEditionId { get; set; }
/// <summary>
/// String containing the full server version text.
/// </summary>
public string ServerVersion { get; set; }
/// <summary>
/// String describing the product level of the server.
/// </summary>
public string ServerLevel { get; set; }
/// <summary>
/// The edition of the SQL Server instance.
/// </summary>
public string ServerEdition { get; set; }
/// <summary>
/// Whether the SQL Server instance is running in the cloud (Azure) or not.
/// </summary>
public bool IsCloud { get; set; }
/// <summary>
/// The version of Azure that the SQL Server instance is running on, if applicable.
/// </summary>
public int AzureVersion { get; set; }
/// <summary>
/// The Operating System version string of the machine running the SQL Server instance.
/// </summary>
public string OsVersion { get; set; }
}
}

View File

@@ -0,0 +1,452 @@
//
// 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.Reflection;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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";
private static readonly 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(LogLevel.Warning, Resources.LoggingAmbientSettings);
foreach (KeyValuePair<string, AmbientValue> setting in _configuration)
{
// Log Ambient Settings
Logger.Write(
LogLevel.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(LogLevel.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,137 @@
//
// 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 System.Data.SqlClient;
using System.Linq;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
{
/// <summary>
/// This class caches server information for subsequent use
/// </summary>
internal static class CachedServerInfo
{
private struct CachedInfo
{
public bool IsAzure;
public DateTime LastUpdate;
}
private static ConcurrentDictionary<string, CachedInfo> _cache;
private static object _cacheLock;
private const int _maxCacheSize = 1024;
private const int _deleteBatchSize = 512;
private const int MinimalQueryTimeoutSecondsForAzure = 300;
static CachedServerInfo()
{
_cache = new ConcurrentDictionary<string, CachedInfo>(StringComparer.OrdinalIgnoreCase);
_cacheLock = new object();
}
public static int GetQueryTimeoutSeconds(IDbConnection connection)
{
string dataSource = SafeGetDataSourceFromConnection(connection);
return GetQueryTimeoutSeconds(dataSource);
}
public static int GetQueryTimeoutSeconds(string dataSource)
{
//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 (string.IsNullOrWhiteSpace(dataSource)
|| (originalValue == 0))
{
return originalValue;
}
CachedInfo info;
bool hasFound = _cache.TryGetValue(dataSource, out info);
if (hasFound && info.IsAzure
&& originalValue < MinimalQueryTimeoutSecondsForAzure)
{
return MinimalQueryTimeoutSecondsForAzure;
}
else
{
return originalValue;
}
}
public static void AddOrUpdateIsAzure(IDbConnection connection, bool isAzure)
{
Validate.IsNotNull(nameof(connection), connection);
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
AddOrUpdateIsAzure(builder.DataSource, isAzure);
}
public static void AddOrUpdateIsAzure(string dataSource, bool isAzure)
{
Validate.IsNotNullOrWhitespaceString(nameof(dataSource), dataSource);
CachedInfo info;
bool hasFound = _cache.TryGetValue(dataSource, out info);
if (hasFound && info.IsAzure == isAzure)
{
return;
}
else
{
lock (_cacheLock)
{
if (! _cache.ContainsKey(dataSource))
{
//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 (string key in keysToDelete)
{
_cache.TryRemove(key, out info);
}
}
}
info.IsAzure = isAzure;
info.LastUpdate = DateTime.UtcNow;
_cache.AddOrUpdate(dataSource, info, (key, oldValue) => info);
}
}
}
private static string SafeGetDataSourceFromConnection(IDbConnection connection)
{
if (connection == null)
{
return null;
}
try
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
return builder.DataSource;
}
catch
{
Logger.Write(LogLevel.Error, String.Format(Resources.FailedToParseConnectionString, connection.ConnectionString));
return null;
}
}
}
}

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.ServiceLayer.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.ServiceLayer.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 System.Data.SqlClient;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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 System.Data.SqlClient;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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.ServiceLayer.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.ServiceLayer.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 System.Data.SqlClient;
using System.Diagnostics.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.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;
}
private void ValidateConnectionIsSet()
{
if (_connection == null)
{
throw new InvalidOperationException(Resources.ConnectionPropertyNotSet);
}
}
}
}
}

View File

@@ -0,0 +1,548 @@
//
// 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 System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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;
/// <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();
}
}
[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.GetQueryTimeoutSeconds(conn);
cmd.ExecuteNonQuery();
}
}
internal static void SetDefaultAnsiSettings(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;
}
// Configure the connection with proper ANSI settings and lock timeout
using (IDbCommand cmd = conn.CreateCommand())
{
cmd.CommandTimeout = CachedServerInfo.GetQueryTimeoutSeconds(conn);
cmd.CommandText = @"SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;";
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()
{
OpenConnection();
}
/// <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);
}
/// <summary>
/// Opens a database connection with the settings specified by the ConnectionString and ConnectionRetryPolicy properties.
/// </summary>
/// <returns>An object representing the open connection.</returns>
private SqlConnection OpenConnection()
{
// 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);
});
return _underlyingConnection;
}
public void OnConnectionStateChange(object sender, StateChangeEventArgs e)
{
SqlConnection conn = (SqlConnection)sender;
switch (e.CurrentState)
{
case ConnectionState.Open:
RetreiveSessionId();
break;
case ConnectionState.Broken:
case ConnectionState.Closed:
_azureSessionId = Guid.Empty;
break;
case ConnectionState.Connecting:
case ConnectionState.Executing:
case ConnectionState.Fetching:
default:
break;
}
}
private void RetreiveSessionId()
{
try
{
using (IDbCommand command = CreateReliableCommand())
{
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(LogLevel.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;
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,149 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.Connection.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.";
}
}
}
}

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.ServiceLayer.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,38 @@
//
// 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.ServiceLayer.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
{
}
}

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 System.Data.SqlClient;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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 sealed class DataTransferErrorDetectionStrategy : ErrorDetectionStrategyBase, IErrorDetectionStrategy
{
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(LogLevel.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 System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.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 System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.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, IErrorDetectionStrategy
{
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 System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.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 sealed class SqlAzureTemporaryAndIgnorableErrorDetectionStrategy : ErrorDetectionStrategyBase, IErrorDetectionStrategy
{
/// <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 System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.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, IErrorDetectionStrategy
{
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 System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.SqlTools.ServiceLayer.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 System.Data.SqlClient;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Threading;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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(LogLevel.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(LogLevel.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;
}
*/
private 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 };
}
private 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.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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;
}
private static void DataConnectionFailureRetry(RetryState retryState)
{
Logger.Write(LogLevel.Normal, 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);
}
private static void CommandFailureRetry(RetryState retryState, string commandKeyword)
{
Logger.Write(LogLevel.Normal, 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);
}
private static void CommandFailureIgnore(RetryState retryState, string commandKeyword)
{
Logger.Write(LogLevel.Normal, string.Format(
CultureInfo.InvariantCulture,
"{0} retry number {1}. Ignoring failure. Exception: {2}",
commandKeyword,
retryState.RetryCount,
retryState.LastError.ToString()));
RetryPolicyUtils.RaiseAmbientIgnoreMessage(retryState, SqlSchemaModelErrorCodes.ServiceActions.CommandRetry);
}
private static void CommandFailureRetry(RetryState retryState)
{
CommandFailureRetry(retryState, "Command");
}
private static void CommandFailureIgnore(RetryState retryState)
{
CommandFailureIgnore(retryState, "Command");
}
private static void CreateDatabaseCommandFailureRetry(RetryState retryState)
{
CommandFailureRetry(retryState, "Database Command");
}
private static void CreateDatabaseCommandFailureIgnore(RetryState retryState)
{
CommandFailureIgnore(retryState, "Database Command");
}
private static void ElementCommandFailureRetry(RetryState retryState)
{
CommandFailureRetry(retryState, "Element Command");
}
private static void ElementCommandFailureIgnore(RetryState retryState)
{
CommandFailureIgnore(retryState, "Element Command");
}
}
}

View File

@@ -0,0 +1,476 @@
//
// 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.Data.SqlClient;
using Microsoft.SqlTools.EditorServices.Utility;
namespace Microsoft.SqlTools.ServiceLayer.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)
{
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)
{
Logger.Write(LogLevel.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.ServiceLayer.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.ServiceLayer.Connection.ReliableConnection
{
static class SqlConnectionHelperScripts
{
public const string EngineEdition = "SELECT SERVERPROPERTY('EngineEdition'), SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'), (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'), (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 ', @@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.ServiceLayer.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.ServiceLayer.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 static int MemoryOptimizedObjectsValidation_NonMemoryOptimizedTableCannotBeAccessed = ValidationBaseCode + 68;
public static int MemoryOptimizedObjectsValidation_SyntaxNotSupportedOnHekatonElement = ValidationBaseCode + 69;
public static int MemoryOptimizedObjectsValidation_ValidatePrimaryKeyForSchemaAndDataTables = ValidationBaseCode + 70;
public static int MemoryOptimizedObjectsValidation_ValidatePrimaryKeyForSchemaOnlyTables = ValidationBaseCode + 71;
public static int MemoryOptimizedObjectsValidation_OnlyNotNullableColumnsOnIndexes = ValidationBaseCode + 72;
public static int MemoryOptimizedObjectsValidation_HashIndexesOnlyOnMemoryOptimizedObjects = ValidationBaseCode + 73;
public static int MemoryOptimizedObjectsValidation_OptionOnlyForHashIndexes = ValidationBaseCode + 74;
public static int IncrementalStatisticsValidation_FilterNotSupported = ValidationBaseCode + 75;
public static int IncrementalStatisticsValidation_ViewNotSupported = ValidationBaseCode + 76;
public static int IncrementalStatisticsValidation_IndexNotPartitionAligned = ValidationBaseCode + 77;
public static int AzureV12SurfaceAreaValidation = ValidationBaseCode + 78;
public static int DuplicatedTargetObjectReferencesInSecurityPolicy = ValidationBaseCode + 79;
public static int MultipleSecurityPoliciesOnTargetObject = ValidationBaseCode + 80;
public static int ExportedRowsMayBeIncomplete = ValidationBaseCode + 81;
public static 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 static int IndexesOnExternalTable = ValidationBaseCode + 112;
public static 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.ServiceLayer.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.ServiceLayer.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

@@ -4,7 +4,7 @@
//
using System.Data.Common;
using System.Data.SqlClient;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
@@ -20,7 +20,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
/// </summary>
public DbConnection CreateSqlConnection(string connectionString)
{
return new SqlConnection(connectionString);
RetryPolicy connectionRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
RetryPolicy commandRetryPolicy = RetryPolicyFactory.CreateDefaultConnectionRetryPolicy();
return new ReliableSqlConnection(connectionString, connectionRetryPolicy, commandRetryPolicy);
}
}
}

View File

@@ -5,13 +5,13 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
@@ -418,14 +418,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
scriptInfo.BuildingMetadataEvent.WaitOne(LanguageService.OnConnectionWaitTimeout);
scriptInfo.BuildingMetadataEvent.Reset();
var sqlConn = info.SqlConnection as SqlConnection;
var sqlConn = info.SqlConnection as ReliableSqlConnection;
if (sqlConn != null)
{
ServerConnection serverConn = new ServerConnection(sqlConn);
ServerConnection serverConn = new ServerConnection(sqlConn.GetUnderlyingConnection());
scriptInfo.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
scriptInfo.MetadataProvider = SmoMetadataProvider.CreateConnectedProvider(serverConn);
scriptInfo.Binder = BinderProvider.CreateBinder(scriptInfo.MetadataProvider);
scriptInfo.ServerConnection = new ServerConnection(sqlConn);
scriptInfo.ServerConnection = new ServerConnection(sqlConn.GetUnderlyingConnection());
this.ScriptParseInfoMap[info.OwnerUri] = scriptInfo;
}
}

View File

@@ -11,6 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.EditorServices.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
@@ -137,10 +138,10 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
// Register the message listener to *this instance* of the batch
// Note: This is being done to associate messages with batches
SqlConnection sqlConn = conn as SqlConnection;
ReliableSqlConnection sqlConn = conn as ReliableSqlConnection;
if (sqlConn != null)
{
sqlConn.InfoMessage += StoreDbMessage;
sqlConn.GetUnderlyingConnection().InfoMessage += StoreDbMessage;
}
// Create a command that we'll use for executing the query

View File

@@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.SqlParser.Parser;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -192,11 +193,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
await conn.OpenAsync();
SqlConnection sqlConn = conn as SqlConnection;
ReliableSqlConnection sqlConn = conn as ReliableSqlConnection;
if (sqlConn != null)
{
// Subscribe to database informational messages
sqlConn.InfoMessage += OnInfoMessage;
sqlConn.GetUnderlyingConnection().InfoMessage += OnInfoMessage;
}
// We need these to execute synchronously, otherwise the user will be very unhappy

View File

@@ -124,13 +124,28 @@ namespace Microsoft.SqlTools.EditorServices.Utility
}
}
/// <summary>
/// Throws ArgumentException if the value is null or an empty string.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
public static void IsNotNullOrEmptyString(string parameterName, string valueToCheck)
{
if (string.IsNullOrEmpty(valueToCheck))
{
throw new ArgumentException(
"Parameter contains a null, empty, or whitespace string.",
parameterName);
}
}
/// <summary>
/// Throws ArgumentException if the value is null, an empty string,
/// or a string containing only whitespace.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
public static void IsNotNullOrEmptyString(string parameterName, string valueToCheck)
public static void IsNotNullOrWhitespaceString(string parameterName, string valueToCheck)
{
if (string.IsNullOrWhiteSpace(valueToCheck))
{

View File

@@ -61,7 +61,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
/// </exception>
public ScriptFile GetFile(string filePath)
{
Validate.IsNotNullOrEmptyString("filePath", filePath);
Validate.IsNotNullOrWhitespaceString("filePath", filePath);
// Resolve the full file path
string resolvedFilePath = this.ResolveFilePath(filePath);
@@ -153,7 +153,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Workspace
/// <returns></returns>
public ScriptFile GetFileBuffer(string filePath, string initialBuffer)
{
Validate.IsNotNullOrEmptyString("filePath", filePath);
Validate.IsNotNullOrWhitespaceString("filePath", filePath);
// Resolve the full file path
string resolvedFilePath = this.ResolveFilePath(filePath);

View File

@@ -14,6 +14,7 @@
"System.Security.SecureString": "4.0.0",
"System.Collections.Specialized": "4.0.1",
"System.ComponentModel.TypeConverter": "4.1.0",
"System.Diagnostics.Contracts": "4.0.0",
"System.Diagnostics.TraceSource": "4.0.0",
"NETStandard.Library": "1.6.0",
"Microsoft.NETCore.Runtime.CoreCLR": "1.0.2",

View File

@@ -256,7 +256,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
// check that the connection was successful
Assert.NotEmpty(connectionResult.ConnectionId);
Assert.Null(connectionResult.Messages);
}
/// <summary>