Files
sqltoolsservice/external/Microsoft.SqlTools.CoreServices/Connection/ReliableConnection/AmbientSettings.cs
Karl Burtram ccf95aed77 Move unused forked code to external directory (#1192)
* Move unused forked code to external directory

* Fix SLN build errors

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

* Update PackageProjects step of pipeline
2021-04-16 15:33:35 -07:00

454 lines
19 KiB
C#

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