Provide default indexes for database option values instead of reordering options lists (#2156)

* Also re-add <default> to owners list for new database dialog
This commit is contained in:
Cory Rivera
2023-07-27 16:11:49 -07:00
committed by GitHub
parent 325f49ff20
commit 46fa477717
3 changed files with 100 additions and 100 deletions

View File

@@ -173,7 +173,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
((DatabaseInfo)databaseViewInfo.ObjectInfo).PageVerify = displayPageVerifyOptions[smoDatabase.PageVerify];
((DatabaseInfo)databaseViewInfo.ObjectInfo).TargetRecoveryTimeInSec = smoDatabase.TargetRecoveryTime;
if (prototype is DatabasePrototype160) {
if (prototype is DatabasePrototype160)
{
((DatabaseInfo)databaseViewInfo.ObjectInfo).IsLedgerDatabase = smoDatabase.IsLedger;
}
}
@@ -239,16 +240,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
logins.Add(login.Name);
}
// If we don't have a default database owner, then move the current login to the front of the list to use as the default.
string firstOwner = prototype.Exists ? prototype.Owner : dataContainer.Server.ConnectionContext.TrueLogin;
int swapIndex = logins.FindIndex(login => login.Equals(firstOwner, StringComparison.InvariantCultureIgnoreCase));
if (swapIndex > 0)
{
logins.RemoveAt(swapIndex);
logins.Insert(0, firstOwner);
}
// Add <default> to the start of the list in addition to defined logins
logins.Insert(0, SR.general_default);
databaseViewInfo.LoginNames = logins.ToArray();
databaseViewInfo.LoginNames = new OptionsCollection() { Options = logins.ToArray(), DefaultValueIndex = 0 };
}
return Task.FromResult(new InitializeViewResult { ViewInfo = databaseViewInfo, Context = context });
@@ -423,7 +418,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
}
if (database.Owner != null && viewParams.IsNewObject)
if (database.Owner != null && database.Owner != SR.general_default && viewParams.IsNewObject)
{
prototype.Owner = database.Owner;
}
@@ -458,7 +453,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
if (prototype is DatabasePrototype110 db110)
{
if (database.TargetRecoveryTimeInSec != null) {
if (database.TargetRecoveryTimeInSec != null)
{
db110.TargetRecoveryTime = (int)database.TargetRecoveryTimeInSec;
}
@@ -532,56 +528,63 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// Get supported database collations for this server.
/// </summary>
/// <returns>A string array containing the display names of the collations. The first element will be "<default>" if this is either a new database or a Sphinx server.
private string[] GetCollations(Server server, DatabasePrototype prototype, bool isNewObject)
/// <returns>An <see cref="OptionsCollection"/> of the supported collations and the default collation's index.</returns>
private OptionsCollection GetCollations(Server server, DatabasePrototype prototype, bool isNewObject)
{
var collationItems = new List<string>();
bool isSphinxServer = (server.VersionMajor < minimumVersionForWritableCollation);
// if the server is shiloh or later, add specific collations to the list
if (!isSphinxServer)
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
// Writable collations are not supported for Sphinx and earlier
if (server.VersionMajor < minimumVersionForWritableCollation)
{
return options;
}
using (DataTable serverCollationsTable = server.EnumCollations())
{
DataTable serverCollationsTable = server.EnumCollations();
if (serverCollationsTable != null)
{
var collationItems = new List<string>();
foreach (DataRow serverCollation in serverCollationsTable.Rows)
{
string collationName = (string)serverCollation["Name"];
collationItems.Add(collationName);
}
// If this database already exists, then use its collation as the default value.
// Otherwise use the server's collation as the default value.
string firstCollation = prototype.Exists ? prototype.Collation : server.Collation;
int defaultIndex = collationItems.FindIndex(collation => collation.Equals(firstCollation, StringComparison.InvariantCultureIgnoreCase));
if (defaultIndex > 0)
{
options.DefaultValueIndex = defaultIndex;
}
options.Options = collationItems.ToArray();
}
}
// If this database already exists, then put its collation at the front of the list.
// Otherwise use the server's collation as the default first value.
string firstCollation = prototype.Exists ? prototype.Collation : server.Collation;
int index = collationItems.FindIndex(collation => collation.Equals(firstCollation, StringComparison.InvariantCultureIgnoreCase));
if (index > 0)
{
collationItems.RemoveAt(index);
collationItems.Insert(0, firstCollation);
}
return collationItems.ToArray();
return options;
}
/// <summary>
/// Gets the prototype's current collation.
/// </summary>
private string[] GetCollationsWithPrototypeCollation(DatabasePrototype prototype)
/// <returns>An <see cref="OptionsCollection"/> of the prototype's collation and the default collation's index.</returns>
private OptionsCollection GetCollationsWithPrototypeCollation(DatabasePrototype prototype)
{
return new string[] { prototype.Collation };
return new OptionsCollection() { Options = new string[] { prototype.Collation }, DefaultValueIndex = 0 };
}
/// <summary>
/// Get supported database containment types for this server.
/// </summary>
/// <returns>A string array containing the display names of the containment types. This array is empty if containment types are not supported for this server.</returns>
private string[] GetContainmentTypes(Server server, DatabasePrototype prototype)
/// <returns>An <see cref="OptionsCollection"/> of the supported containment types and the default containment type's index.</returns>
private OptionsCollection GetContainmentTypes(Server server, DatabasePrototype prototype)
{
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
// Containment types are only supported for Denali and later, and only if the server is not a managed instance
if (!(SqlMgmtUtils.IsSql11OrLater(server.ServerVersion)) || server.IsAnyManagedInstance())
{
return Array.Empty<string>();
return options;
}
var containmentTypes = new List<string>();
@@ -596,39 +599,36 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
containmentTypes.Add(displayContainmentTypes[ContainmentType.None]);
containmentTypes.Add(displayContainmentTypes[ContainmentType.Partial]);
// Put the prototype's current containment type at the front of the list
var swapIndex = 0;
// Use the prototype's current containment type as the default value
var defaultIndex = 0;
switch (dbContainmentType)
{
case ContainmentType.None:
break;
case ContainmentType.Partial:
swapIndex = 1;
defaultIndex = 1;
break;
default:
break;
}
if (swapIndex > 0)
{
var value = containmentTypes[swapIndex];
containmentTypes.RemoveAt(swapIndex);
containmentTypes.Insert(0, value);
}
return containmentTypes.ToArray();
options.DefaultValueIndex = defaultIndex;
options.Options = containmentTypes.ToArray();
return options;
}
/// <summary>
/// Get supported database recovery models for this server.
/// </summary>
/// <returns>A string array containing the display names of the recovery models. This array is empty if recovery models are not supported for this server.</returns>
private string[] GetRecoveryModels(Server server, DatabasePrototype prototype)
/// <returns>An <see cref="OptionsCollection"/> of the supported recovery models and the default recovery model's index.</returns>
private OptionsCollection GetRecoveryModels(Server server, DatabasePrototype prototype)
{
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
// Recovery models are only supported if the server is shiloh or later and is not a Managed Instance
var recoveryModelEnabled = (minimumVersionForRecoveryModel <= server.VersionMajor) && !server.IsAnyManagedInstance();
if (server.GetDisabledProperties().Contains("RecoveryModel") || !recoveryModelEnabled)
{
return Array.Empty<string>();
return options;
}
var recoveryModels = new List<string>();
@@ -653,31 +653,27 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
}
}
// Put the prototype's current recovery model at the front of the list
if (recoveryModelEnabled)
// Use the prototype's current recovery model as the default value
if (recoveryModels.Count > 1)
{
var swapIndex = 0;
var defaultIndex = 0;
switch (prototype.RecoveryModel)
{
case RecoveryModel.BulkLogged:
swapIndex = 1;
defaultIndex = 1;
break;
case RecoveryModel.Simple:
swapIndex = 2;
defaultIndex = 2;
break;
default:
break;
}
if (swapIndex > 0)
{
var value = recoveryModels[swapIndex];
recoveryModels.RemoveAt(swapIndex);
recoveryModels.Insert(0, value);
}
options.DefaultValueIndex = defaultIndex;
}
return recoveryModels.ToArray();
options.Options = recoveryModels.ToArray();
return options;
}
private DatabaseFile[] GetDatabaseFiles(Database database)
@@ -712,9 +708,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
/// <summary>
/// Get supported database compatibility levels for this Azure server.
/// </summary>
/// <returns>A string array containing the display names of the compatibility levels. This array is empty if the database has a compatibility level we don't recognize.</returns>
private string[] GetCompatibilityLevelsAzure(DatabasePrototype prototype)
/// <returns>An <see cref="OptionsCollection"/> of the supported compatibility levels and the default compatibility level's index.</returns>
private OptionsCollection GetCompatibilityLevelsAzure(DatabasePrototype prototype)
{
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
// For Azure we loop through all of the possible compatibility levels. We do this because there's only one compat level active on a
// version at a time, but that can change at any point so in order to reduce maintenance required when that happens we'll just find
// the one that matches the current set level and display that
@@ -723,26 +720,29 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
if (level == prototype.DatabaseCompatibilityLevel)
{
// Azure can't change the compat level so we only include the current version
return new string[] { displayCompatLevels[level] };
options.Options = new string[] { displayCompatLevels[level] };
return options;
}
}
// If we couldn't find the prototype's current compatibility level, then treat compatibillity levels as unsupported for this server
return Array.Empty<string>();
return options;
}
/// <summary>
/// Get supported database compatibility levels for this server.
/// </summary>
/// <returns>A string array containing the display names of the compatibility levels. This array is empty if this is either a Sphinx server or if the database has a compatibility level we don't recognize.</returns>
private string[] GetCompatibilityLevels(int sqlServerVersion, DatabasePrototype prototype)
/// <returns>An <see cref="OptionsCollection"/> of the supported compatibility levels and the default compatibility level's index.</returns>
private OptionsCollection GetCompatibilityLevels(int sqlServerVersion, DatabasePrototype prototype)
{
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
// Unlikely that we are hitting such an old SQL Server, but leaving to preserve
// the original semantic of this method.
if (sqlServerVersion < 8)
{
// we do not know this version number, we do not know the possible compatibility levels for the server
return Array.Empty<string>();
return options;
}
var compatibilityLevels = new List<string>();
@@ -809,24 +809,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
break;
}
// set the first compatability level for this list based on the prototype
// set the default compatability level for this list based on the prototype
for (var i = 0; i < compatibilityLevels.Count; i++)
{
var level = compatibilityLevels[i];
var prototypeLevel = displayCompatLevels[prototype.DatabaseCompatibilityLevel];
if (level == prototypeLevel)
{
if (i > 0)
{
compatibilityLevels.RemoveAt(i);
compatibilityLevels.Insert(0, level);
}
return compatibilityLevels.ToArray();
options.DefaultValueIndex = i;
options.Options = compatibilityLevels.ToArray();
return options;
}
}
// previous loop did not find the prototype compatibility level in this server's compatability options, so treat compatibility levels as unsupported for this server
return Array.Empty<string>();
return options;
}
/// <summary>
@@ -839,16 +836,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
if (AzureSqlDbHelper.TryGetServiceObjectiveInfo(edition, out var serviceInfoPair))
{
// Move default value to the front of the list
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
var serviceLevelsList = new List<string>(serviceInfoPair.Value);
var defaultIndex = serviceInfoPair.Key;
if (defaultIndex >= 0 && defaultIndex < serviceLevelsList.Count)
{
var defaultServiceObjective = serviceLevelsList[defaultIndex];
serviceLevelsList.RemoveAt(defaultIndex);
serviceLevelsList.Insert(0, defaultServiceObjective);
options.DefaultValueIndex = defaultIndex;
}
var details = new AzureEditionDetails() { EditionDisplayName = edition.DisplayName, Details = serviceLevelsList.ToArray() };
options.Options = serviceLevelsList.ToArray();
var details = new AzureEditionDetails() { EditionDisplayName = edition.DisplayName, EditionOptions = options };
levels.Add(details);
}
else
@@ -869,16 +865,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
if (AzureSqlDbHelper.TryGetDatabaseSizeInfo(edition, out var sizeInfoPair))
{
// Move default value to the front of the list
var options = new OptionsCollection() { Options = Array.Empty<string>(), DefaultValueIndex = 0 };
var sizeInfoList = new List<DbSize>(sizeInfoPair.Value);
var defaultIndex = sizeInfoPair.Key;
if (defaultIndex >= 0 && defaultIndex < sizeInfoList.Count)
{
var defaultSizeInfo = sizeInfoList[defaultIndex];
sizeInfoList.RemoveAt(defaultIndex);
sizeInfoList.Insert(0, defaultSizeInfo);
options.DefaultValueIndex = defaultIndex;
}
var details = new AzureEditionDetails() { EditionDisplayName = edition.DisplayName, Details = sizeInfoList.Select(info => info.ToString()).ToArray() };
options.Options = sizeInfoList.Select(info => info.ToString()).ToArray();
var details = new AzureEditionDetails() { EditionDisplayName = edition.DisplayName, EditionOptions = options };
sizes.Add(details);
}
else

View File

@@ -9,11 +9,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
{
public class DatabaseViewInfo : SqlObjectViewInfo
{
public string[] LoginNames { get; set; }
public string[] CollationNames { get; set; }
public string[] CompatibilityLevels { get; set; }
public string[] ContainmentTypes { get; set; }
public string[] RecoveryModels { get; set; }
public OptionsCollection LoginNames { get; set; }
public OptionsCollection CollationNames { get; set; }
public OptionsCollection CompatibilityLevels { get; set; }
public OptionsCollection ContainmentTypes { get; set; }
public OptionsCollection RecoveryModels { get; set; }
public DatabaseFile[] Files { get; set; }
public bool IsAzureDB { get; set; }
@@ -28,7 +28,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
public class AzureEditionDetails
{
public string EditionDisplayName { get; set; }
public string[] Details { get; set; }
public OptionsCollection EditionOptions { get; set; }
}
public class DatabaseFile
@@ -38,4 +38,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement
public string Path { get; set; }
public string FileGroup { get; set; }
}
public class OptionsCollection {
public string[] Options { get; set; }
public int DefaultValueIndex { get; set; }
}
}

View File

@@ -187,10 +187,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
[Test]
public void GetAzureServiceLevelObjectivesTest()
{
var actualLevelsMap = new Dictionary<string, string[]>();
var actualLevelsMap = new Dictionary<string, OptionsCollection>();
foreach (AzureEditionDetails serviceDetails in DatabaseHandler.AzureServiceLevels)
{
actualLevelsMap.Add(serviceDetails.EditionDisplayName, serviceDetails.Details);
actualLevelsMap.Add(serviceDetails.EditionDisplayName, serviceDetails.EditionOptions);
}
var expectedDefaults = new Dictionary<AzureEdition, string>()
@@ -209,11 +209,11 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
var expectedServiceLevels = expectedLevelInfo.Value;
var actualServiceLevels = actualLevelsMap[edition.DisplayName];
Assert.That(actualServiceLevels, Is.EquivalentTo(expectedServiceLevels), "Did not get expected SLO list for edition '{0}'", edition.DisplayName);
Assert.That(actualServiceLevels.Options, Is.EquivalentTo(expectedServiceLevels), "Did not get expected SLO list for edition '{0}'", edition.DisplayName);
var expectedDefaultIndex = expectedLevelInfo.Key;
var expectedDefault = expectedServiceLevels[expectedDefaultIndex];
var actualDefault = actualServiceLevels[0];
var actualDefault = actualServiceLevels.Options[actualServiceLevels.DefaultValueIndex];
Assert.That(actualDefault, Is.EqualTo(expectedDefault), "Did not get expected default SLO for edition '{0}'", edition.DisplayName);
}
else
@@ -226,10 +226,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
[Test]
public void GetAzureMaxSizesTest()
{
var actualSizesMap = new Dictionary<string, string[]>();
var actualSizesMap = new Dictionary<string, OptionsCollection>();
foreach (AzureEditionDetails sizeDetails in DatabaseHandler.AzureMaxSizes)
{
actualSizesMap.Add(sizeDetails.EditionDisplayName, sizeDetails.Details);
actualSizesMap.Add(sizeDetails.EditionDisplayName, sizeDetails.EditionOptions);
}
var expectedDefaults = new Dictionary<AzureEdition, string>()
@@ -248,11 +248,11 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
var expectedSizes = expectedSizeInfo.Value.Select(size => size.ToString()).ToArray();
var actualSizes = actualSizesMap[edition.DisplayName];
Assert.That(actualSizes, Is.EquivalentTo(expectedSizes), "Did not get expected size list for edition '{0}'", edition.DisplayName);
Assert.That(actualSizes.Options, Is.EquivalentTo(expectedSizes), "Did not get expected size list for edition '{0}'", edition.DisplayName);
var expectedDefaultIndex = expectedSizeInfo.Key;
var expectedDefault = expectedSizes[expectedDefaultIndex];
var actualDefault = actualSizes[0];
var actualDefault = actualSizes.Options[actualSizes.DefaultValueIndex];
Assert.That(actualDefault, Is.EqualTo(expectedDefault.ToString()), "Did not get expected default size for edition '{0}'", edition.DisplayName);
}
else