Enabling Database scoped configurations tab to DB Properties (#2147)

* sending dsc values to ADS

* modifying dsc method with unsupportable property IsValuedefault

* getting the options and added a bool flag to maintian checkbox for secondary to save

* sending data to ads

* Ready for PR with minimal changes of loading UI as expected, TODO:saving logic

* Excluding maxdop and resumable options from primary value conversion for 1/0's

* Adding Id to the info, as we cannot depend on names, as names can be altered in future

* saving successfully, todo-diff servers, script (secondary - primary compare and dont update),test, send null for unsupported

* adding nullable dsc for unsupported servers

* fixing script generation for some properties that are not touched. the generated script is unharmed but unnecessary here

* adding test conditions for database scoped configurations

* adding switch case method to get the values

* Removing Loc string for the TSQL options

* removing unnecessary using statement

* Adding test case and fixing createDatabase issue

* Update src/Microsoft.SqlTools.ServiceLayer/Admin/Database/DatabasePrototype130.cs

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>

* comment update

---------

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
This commit is contained in:
Sai Avishkar Sreerama
2023-08-15 11:58:35 -05:00
committed by GitHub
parent 7c0da6b6b1
commit e4db70fb39
15 changed files with 281 additions and 71 deletions

View File

@@ -46,6 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
public DateTime lastLogBackupDate;
public DatabaseUserAccess restrictAccess;
public DatabaseStatus databaseState;
public DatabaseScopedConfigurationCollection databaseScopedConfigurations;
public DefaultCursor defaultCursor;
public CompatibilityLevel databaseCompatibilityLevel;
public ContainmentType databaseContainmentType;
@@ -556,6 +557,11 @@ WHERE do.database_id = @DbID
this.isLedger = db.IsLedger;
}
if (db.IsSupportedObject<DatabaseScopedConfiguration>())
{
this.databaseScopedConfigurations = db.DatabaseScopedConfigurations;
}
//Only fill in the Azure properties when connected to an Azure server
if (context.Server.ServerType == DatabaseEngineType.SqlAzureDatabase)
{
@@ -689,6 +695,7 @@ WHERE do.database_id = @DbID
this.maxSize = other.maxSize == null ? null : new DbSize(other.maxSize);
this.backupStorageRedundancy = other.backupStorageRedundancy;
this.isLedger = other.isLedger;
this.databaseScopedConfigurations = other.databaseScopedConfigurations;
}
/// <summary>
@@ -773,7 +780,8 @@ WHERE do.database_id = @DbID
(this.queryStoreEnabled == other.queryStoreEnabled) &&
(this.maxSize == other.maxSize) &&
(this.backupStorageRedundancy == other.backupStorageRedundancy) &&
(this.isLedger == other.isLedger);
(this.isLedger == other.isLedger) &&
(this.databaseScopedConfigurations == other.databaseScopedConfigurations);
return result;
}

View File

@@ -199,51 +199,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
{
db.EncryptionEnabled = this.currentState.encryptionEnabled;
}
// Check if we support database scoped configurations in this database. Since these were all added at the same time,
// only check if MaxDop is supported rather than each individual property.
if (db.IsSupportedProperty("MaxDop"))
{
if (!this.Exists || (db.MaxDop != this.MaxDop))
{
db.MaxDop = this.MaxDop;
}
if (!this.Exists || (db.MaxDopForSecondary != this.MaxDopForSecondary))
{
db.MaxDopForSecondary = this.MaxDopForSecondary;
}
if (!this.Exists || (db.LegacyCardinalityEstimation != this.currentState.legacyCardinalityEstimation))
{
db.LegacyCardinalityEstimation = this.currentState.legacyCardinalityEstimation;
}
if (!this.Exists || (db.LegacyCardinalityEstimationForSecondary != this.currentState.legacyCardinalityEstimationForSecondary))
{
db.LegacyCardinalityEstimationForSecondary = this.currentState.legacyCardinalityEstimationForSecondary;
}
if (!this.Exists || (db.ParameterSniffing != this.currentState.parameterSniffing))
{
db.ParameterSniffing = this.currentState.parameterSniffing;
}
if (!this.Exists || (db.ParameterSniffingForSecondary != this.currentState.parameterSniffingForSecondary))
{
db.ParameterSniffingForSecondary = this.currentState.parameterSniffingForSecondary;
}
if (!this.Exists || (db.QueryOptimizerHotfixes != this.currentState.queryOptimizerHotfixes))
{
db.QueryOptimizerHotfixes = this.currentState.queryOptimizerHotfixes;
}
if (!this.Exists || (db.QueryOptimizerHotfixesForSecondary != this.currentState.queryOptimizerHotfixesForSecondary))
{
db.QueryOptimizerHotfixesForSecondary = this.currentState.queryOptimizerHotfixesForSecondary;
}
}
}
#region Helper Methods

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 Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.ServiceLayer.Management;
using System.Collections.Generic;
using System.ComponentModel;
namespace Microsoft.SqlTools.ServiceLayer.Admin
{
/// <summary>
/// Database properties for SqlServer 2016
/// </summary>
internal class DatabasePrototype130 : DatabasePrototype110
{
// Properties that doen't support secondary value updates
// More info here: https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-database-scoped-configuration-transact-sql?view=sql-server-ver16
// The secondaryValUnsupportedPropsSet containst the configuration Ids of the below properties
// IDENTITY_CACHE(6)
// ELEVATE_ONLINE(11)
// ELEVATE_RESUMABLE(12)
// GLOBAL_TEMPORARY_TABLE_AUTO_DROP(21)
// PAUSED_RESUMABLE_INDEX_ABORT_DURATION_MINUTES(25)
private static readonly HashSet<int> secondaryValUnsupportedPropsSet = new HashSet<int> { 6, 11, 12, 21, 25 };
/// <summary>
/// Database properties for SqlServer 2016 class constructor
/// </summary>
public DatabasePrototype130(CDataContainer context)
: base(context)
{
}
[Category("Category_DatabaseScopedConfigurations")]
public DatabaseScopedConfigurationCollection DatabaseScopedConfiguration
{
get
{
return this.currentState.databaseScopedConfigurations;
}
set
{
this.currentState.databaseScopedConfigurations = value;
this.NotifyObservers();
}
}
protected override void SaveProperties(Database db)
{
base.SaveProperties(db);
for (int i = 0; i < db.DatabaseScopedConfigurations.Count; i++)
{
if (db.DatabaseScopedConfigurations[i].Value != this.currentState.databaseScopedConfigurations[i].Value)
{
db.DatabaseScopedConfigurations[i].Value = this.currentState.databaseScopedConfigurations[i].Value;
}
// Configurations that are not allowed secondary replicas are excluded.
if (db.DatabaseScopedConfigurations[i].ValueForSecondary != this.currentState.databaseScopedConfigurations[i].ValueForSecondary
&& !secondaryValUnsupportedPropsSet.Contains(db.DatabaseScopedConfigurations[i].Id))
{
db.DatabaseScopedConfigurations[i].ValueForSecondary = this.currentState.databaseScopedConfigurations[i].ValueForSecondary;
}
}
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
/// <summary>
/// Database properties for SqlServer 2017
/// </summary>
internal class DatabasePrototype140 : DatabasePrototype110
internal class DatabasePrototype140 : DatabasePrototype130
{
/// <summary>
/// Database properties for SqlServer 2017 class constructor
@@ -71,6 +71,11 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
}
}
}
protected override void SaveProperties(Database db)
{
base.SaveProperties(db);
}
}
}

View File

@@ -65,6 +65,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin
{
this.prototype = new DatabasePrototype160(context);
}
else if (majorVersionNumber >= 14)
{
this.prototype = new DatabasePrototype140(context);
}
else if (majorVersionNumber == 13)
{
this.prototype = new DatabasePrototype130(context);
}
else if (Utils.IsSql11OrLater(context.Server.Version.Major))
{
this.prototype = new DatabasePrototype110(context);

View File

@@ -12301,14 +12301,6 @@ namespace Microsoft.SqlTools.ServiceLayer
}
}
public static string prototype_db_prop_databasescopedconfig_value_primary
{
get
{
return Keys.GetString(Keys.prototype_db_prop_databasescopedconfig_value_primary);
}
}
public static string error_db_prop_invalidleadingColumns
{
get
@@ -17828,9 +17820,6 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string prototype_db_prop_databasescopedconfig_value_on = "prototype_db_prop_databasescopedconfig_value_on";
public const string prototype_db_prop_databasescopedconfig_value_primary = "prototype_db_prop_databasescopedconfig_value_primary";
public const string error_db_prop_invalidleadingColumns = "error_db_prop_invalidleadingColumns";

View File

@@ -6702,10 +6702,6 @@ The Query Processor estimates that implementing the following index could improv
<value>ON</value>
<comment></comment>
</data>
<data name="prototype_db_prop_databasescopedconfig_value_primary" xml:space="preserve">
<value>PRIMARY</value>
<comment></comment>
</data>
<data name="error_db_prop_invalidleadingColumns" xml:space="preserve">
<value>For the distribution policy HASH, the number of leading hash columns is optional but should be from 1 to 16 columns</value>
<comment></comment>

View File

@@ -2786,7 +2786,6 @@ prototype_db_prop_varDecimalEnabled = VarDecimal Storage Format Enabled
prototype_db_prop_encryptionEnabled = Encryption Enabled
prototype_db_prop_databasescopedconfig_value_off = OFF
prototype_db_prop_databasescopedconfig_value_on = ON
prototype_db_prop_databasescopedconfig_value_primary = PRIMARY
error_db_prop_invalidleadingColumns = For the distribution policy HASH, the number of leading hash columns is optional but should be from 1 to 16 columns
compatibilityLevel_sphinx = SQL Server 7.0 (70)
compatibilityLevel_shiloh = SQL Server 2000 (80)

View File

@@ -8216,11 +8216,6 @@ The Query Processor estimates that implementing the following index could improv
<target state="new">ON</target>
<note></note>
</trans-unit>
<trans-unit id="prototype_db_prop_databasescopedconfig_value_primary">
<source>PRIMARY</source>
<target state="new">PRIMARY</target>
<note></note>
</trans-unit>
<trans-unit id="error_db_prop_invalidleadingColumns">
<source>For the distribution policy HASH, the number of leading hash columns is optional but should be from 1 to 16 columns</source>
<target state="new">For the distribution policy HASH, the number of leading hash columns is optional but should be from 1 to 16 columns</target>

View File

@@ -23,7 +23,7 @@ using Microsoft.SqlTools.ServiceLayer.Utility.SqlScriptFormatters;
namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
/// <summary>
/// <summary>
/// Database object type handler
/// </summary>
public class DatabaseHandler : ObjectTypeHandler<DatabaseInfo, DatabaseViewContext>
@@ -44,8 +44,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
internal static readonly string[] AzureEditionNames;
internal static readonly string[] AzureBackupLevels;
internal static readonly string[] DscOnOffOptions;
internal static readonly string[] DscElevateOptions;
internal static readonly string[] DscEnableDisableOptions;
internal static readonly AzureEditionDetails[] AzureMaxSizes;
internal static readonly AzureEditionDetails[] AzureServiceLevels;
internal DatabaseScopedConfigurationCollection? databaseScopedConfigurationsCollection = null;
static DatabaseHandler()
{
@@ -75,6 +79,22 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
displayRestrictAccessOptions.Add(DatabaseUserAccess.Single, SR.prototype_db_prop_restrictAccess_value_single);
displayRestrictAccessOptions.Add(DatabaseUserAccess.Restricted, SR.prototype_db_prop_restrictAccess_value_restricted);
DscOnOffOptions = new[]{
CommonConstants.DatabaseScopedConfigurations_Value_On,
CommonConstants.DatabaseScopedConfigurations_Value_Off
};
DscElevateOptions = new[]{
CommonConstants.DatabaseScopedConfigurations_Value_Off,
CommonConstants.DatabaseScopedConfigurations_Value_When_supported,
CommonConstants.DatabaseScopedConfigurations_Value_Fail_Unsupported
};
DscEnableDisableOptions = new[]{
CommonConstants.DatabaseScopedConfigurations_Value_Enabled,
CommonConstants.DatabaseScopedConfigurations_Value_Disabled
};
// Set up maps from displayName to enum type so we can retrieve the equivalent enum types later when getting a Save/Script request.
// We can't use a simple Enum.Parse for that since the displayNames get localized.
foreach (CompatibilityLevel key in displayCompatLevels.Keys)
@@ -160,7 +180,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
AutoShrink = smoDatabase.AutoShrink,
AutoUpdateStatistics = smoDatabase.AutoUpdateStatisticsEnabled,
AutoUpdateStatisticsAsynchronously = smoDatabase.AutoUpdateStatisticsAsync,
EncryptionEnabled = smoDatabase.EncryptionEnabled
EncryptionEnabled = smoDatabase.EncryptionEnabled,
DatabaseScopedConfigurations = smoDatabase.IsSupportedObject<DatabaseScopedConfiguration>() ? GetDSCMetaData(smoDatabase.DatabaseScopedConfigurations) : null
};
if (!isAzureDB)
@@ -187,7 +208,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
((DatabaseInfo)databaseViewInfo.ObjectInfo).IsLedgerDatabase = smoDatabase.IsLedger;
}
}
databaseScopedConfigurationsCollection = smoDatabase.IsSupportedObject<DatabaseScopedConfiguration>() ? smoDatabase.DatabaseScopedConfigurations : null;
}
databaseViewInfo.DscOnOffOptions = DscOnOffOptions;
databaseViewInfo.DscElevateOptions = DscElevateOptions;
databaseViewInfo.DscEnableDisableOptions = DscEnableDisableOptions;
}
// azure sql db doesn't have a sysadmin fixed role
@@ -569,6 +594,36 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
db110.DatabaseContainmentType = containmentTypeEnums[database.ContainmentType];
}
}
if (prototype is DatabasePrototype130 db130)
{
if (!viewParams.IsNewObject && databaseScopedConfigurationsCollection != null && database.DatabaseScopedConfigurations != null)
{
foreach (DatabaseScopedConfigurationsInfo dsc in database.DatabaseScopedConfigurations)
{
foreach (DatabaseScopedConfiguration smoDscCollection in databaseScopedConfigurationsCollection)
{
if (smoDscCollection.Name == dsc.Name)
{
smoDscCollection.Value = dsc.ValueForPrimary == CommonConstants.DatabaseScopedConfigurations_Value_Enabled
? "1" : dsc.ValueForPrimary == CommonConstants.DatabaseScopedConfigurations_Value_Disabled
? "0" : dsc.ValueForPrimary;
// When sending the DSC seconday value to ADS, we convert the secondaryValue of 'PRIMARY' to match with primaryValue
// We need to set it back to 'PRIMARY' so that SMO would not generate any unnecessary scripts for unchanged properties
if (!(smoDscCollection.ValueForSecondary == CommonConstants.DatabaseScopedConfigurations_Value_Primary &&
dsc.ValueForPrimary.Equals(dsc.ValueForSecondary)))
{
smoDscCollection.ValueForSecondary = dsc.ValueForSecondary == CommonConstants.DatabaseScopedConfigurations_Value_Enabled
? "1" : dsc.ValueForSecondary == CommonConstants.DatabaseScopedConfigurations_Value_Disabled
? "0" : dsc.ValueForSecondary;
}
break;
}
}
}
db130.DatabaseScopedConfiguration = databaseScopedConfigurationsCollection;
}
}
// AutoCreateStatisticsIncremental can only be set when AutoCreateStatistics is enabled
prototype.AutoCreateStatisticsIncremental = database.AutoCreateIncrementalStatistics;
@@ -985,5 +1040,51 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
return sizes.ToArray();
}
/// <summary>
/// Prepares database scoped configurations list
/// </summary>
/// <param name="smoDSCMetaData"></param>
/// <returns>database scoped configurations metadata array</returns>
private static DatabaseScopedConfigurationsInfo[] GetDSCMetaData(DatabaseScopedConfigurationCollection smoDSCMetaData)
{
var dscMetaData = new List<DatabaseScopedConfigurationsInfo>();
foreach (DatabaseScopedConfiguration dsc in smoDSCMetaData)
{
string primaryValue = GetDscValue(dsc.Id, dsc.Value);
dscMetaData.Add(new DatabaseScopedConfigurationsInfo()
{
Id = dsc.Id,
Name = dsc.Name,
ValueForPrimary = primaryValue,
ValueForSecondary = dsc.ValueForSecondary == CommonConstants.DatabaseScopedConfigurations_Value_Primary ? primaryValue : GetDscValue(dsc.Id, dsc.ValueForSecondary)
});
}
return dscMetaData.ToArray();
}
/// <summary>
/// Gets primary and secondary value of the database scoped configuration property
/// </summary>
/// <param name="dsc"></param>
/// <returns>Value of the primary/secondary</returns>
private static string GetDscValue(int id, string value)
{
// MAXDOP(Id = 1) and PAUSED_RESUMABLE_INDEX_ABORT_DURATION_MINUTES(Id = 25) are integer numbers but coming as string value type and they need to send as is.
if (id == 1 || id == 25)
{
return value;
}
switch (value)
{
case "1":
return CommonConstants.DatabaseScopedConfigurations_Value_Enabled;
case "0":
return CommonConstants.DatabaseScopedConfigurations_Value_Disabled;
default:
return value;
}
}
}
}

View File

@@ -39,5 +39,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
public bool? DatabaseReadOnly { get; set; }
public bool EncryptionEnabled { get; set; }
public string? RestrictAccess { get; set; }
public DatabaseScopedConfigurationsInfo[]? DatabaseScopedConfigurations { get; set; }
}
public class DatabaseScopedConfigurationsInfo
{
public int Id { get; set; }
public string Name { get; set; }
public string ValueForPrimary { get; set; }
public string ValueForSecondary { get; set; }
}
}

View File

@@ -25,6 +25,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
public AzureEditionDetails[] AzureMaxSizes { get; set; }
public string[] PageVerifyOptions { get; set; }
public string[] RestrictAccessOptions { get; set; }
public string[] DscOnOffOptions { get; set; }
public string[] DscElevateOptions { get; set; }
public string[] DscEnableDisableOptions { get; set; }
}
public class AzureEditionDetails

View File

@@ -19,5 +19,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
public const string TempDbDatabaseName = "tempdb";
public const string DefaultBatchSeperator = "GO";
// Database scoped configurations property values
public const string DatabaseScopedConfigurations_Value_On = "ON";
public const string DatabaseScopedConfigurations_Value_Off = "OFF";
public const string DatabaseScopedConfigurations_Value_Primary = "PRIMARY";
public const string DatabaseScopedConfigurations_Value_When_supported = "WHEN_SUPPORTED";
public const string DatabaseScopedConfigurations_Value_Fail_Unsupported = "FAIL_UNSUPPORTED";
public const string DatabaseScopedConfigurations_Value_Enabled = "ENABLED";
public const string DatabaseScopedConfigurations_Value_Disabled = "DISABLED";
}
}