diff --git a/src/Microsoft.SqlTools.ServiceLayer/Admin/Database/DatabaseTaskHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/Admin/Database/DatabaseTaskHelper.cs index 445ca503..f875987a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Admin/Database/DatabaseTaskHelper.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Admin/Database/DatabaseTaskHelper.cs @@ -62,6 +62,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Admin { this.prototype = new DatabasePrototypeAzure(context); } + else if (majorVersionNumber == 16) + { + this.prototype = new DatabasePrototype160(context); + } else if (Utils.IsSql11OrLater(context.Server.Version.Major)) { this.prototype = new DatabasePrototype110(context); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseHandler.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseHandler.cs index d6589ca2..70aec2c3 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseHandler.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseHandler.cs @@ -35,6 +35,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement private static readonly Dictionary displayCompatLevels = new Dictionary(); private static readonly Dictionary displayContainmentTypes = new Dictionary(); private static readonly Dictionary displayRecoveryModels = new Dictionary(); + private static readonly Dictionary displayPageVerifyOptions = new Dictionary(); + private static readonly Dictionary displayRestrictAccessOptions = new Dictionary(); private static readonly Dictionary compatLevelEnums = new Dictionary(); private static readonly Dictionary containmentTypeEnums = new Dictionary(); @@ -65,6 +67,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement displayRecoveryModels.Add(RecoveryModel.BulkLogged, SR.general_recoveryModel_bulkLogged); displayRecoveryModels.Add(RecoveryModel.Simple, SR.general_recoveryModel_simple); + displayPageVerifyOptions.Add(PageVerify.Checksum, SR.prototype_db_prop_pageVerify_value_checksum); + displayPageVerifyOptions.Add(PageVerify.TornPageDetection, SR.prototype_db_prop_pageVerify_value_tornPageDetection); + displayPageVerifyOptions.Add(PageVerify.None, SR.prototype_db_prop_pageVerify_value_none); + + displayRestrictAccessOptions.Add(DatabaseUserAccess.Multiple, SR.prototype_db_prop_restrictAccess_value_multiple); + displayRestrictAccessOptions.Add(DatabaseUserAccess.Single, SR.prototype_db_prop_restrictAccess_value_single); + displayRestrictAccessOptions.Add(DatabaseUserAccess.Restricted, SR.prototype_db_prop_restrictAccess_value_restricted); + // 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) @@ -100,7 +110,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement public override Task InitializeObjectView(InitializeViewRequestParams requestParams) { // create a default data context and database object - using (var dataContainer = CreateDatabaseDataContainer(requestParams.ConnectionUri, requestParams.ObjectUrn, requestParams.IsNewObject)) + using (var dataContainer = CreateDatabaseDataContainer(requestParams.ConnectionUri, requestParams.ObjectUrn, requestParams.IsNewObject, requestParams.Database)) { if (dataContainer.Server == null) { @@ -115,6 +125,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement var azurePrototype = prototype as DatabasePrototypeAzure; bool isDw = azurePrototype != null && azurePrototype.AzureEdition == AzureEdition.DataWarehouse; bool isAzureDB = dataContainer.Server.ServerType == DatabaseEngineType.SqlAzureDatabase; + bool isManagedInstance = dataContainer.Server.DatabaseEngineEdition == DatabaseEngineEdition.SqlManagedInstance; var databaseViewInfo = new DatabaseViewInfo() { @@ -132,6 +143,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement { Name = smoDatabase.Name, CollationName = smoDatabase.Collation, + CompatibilityLevel = displayCompatLevels[smoDatabase.CompatibilityLevel], + ContainmentType = displayContainmentTypes[smoDatabase.ContainmentType], + RecoveryModel = displayRecoveryModels[smoDatabase.RecoveryModel], DateCreated = smoDatabase.CreateDate.ToString(), LastDatabaseBackup = smoDatabase.LastBackupDate == DateTime.MinValue ? SR.databaseBackupDate_None : smoDatabase.LastBackupDate.ToString(), LastDatabaseLogBackup = smoDatabase.LastLogBackupDate == DateTime.MinValue ? SR.databaseBackupDate_None : smoDatabase.LastLogBackupDate.ToString(), @@ -141,8 +155,28 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement Owner = smoDatabase.Owner, SizeInMb = smoDatabase.Size, SpaceAvailableInMb = ByteConverter.ConvertKbtoMb(smoDatabase.SpaceAvailable), - Status = smoDatabase.Status.ToString() + Status = smoDatabase.Status.ToString(), + AutoCreateIncrementalStatistics = smoDatabase.AutoCreateIncrementalStatisticsEnabled, + AutoCreateStatistics = smoDatabase.AutoCreateStatisticsEnabled, + AutoShrink = smoDatabase.AutoShrink, + AutoUpdateStatistics = smoDatabase.AutoUpdateStatisticsEnabled, + AutoUpdateStatisticsAsynchronously = smoDatabase.AutoUpdateStatisticsAsync, + EncryptionEnabled = smoDatabase.EncryptionEnabled }; + + if (!isManagedInstance) + { + databaseViewInfo.PageVerifyOptions = displayPageVerifyOptions.Values.ToArray(); + databaseViewInfo.RestrictAccessOptions = displayRestrictAccessOptions.Values.ToArray(); + ((DatabaseInfo)databaseViewInfo.ObjectInfo).DatabaseReadOnly = smoDatabase.ReadOnly; + ((DatabaseInfo)databaseViewInfo.ObjectInfo).RestrictAccess = displayRestrictAccessOptions[smoDatabase.UserAccess]; + ((DatabaseInfo)databaseViewInfo.ObjectInfo).PageVerify = displayPageVerifyOptions[smoDatabase.PageVerify]; + ((DatabaseInfo)databaseViewInfo.ObjectInfo).TargetRecoveryTimeInSec = smoDatabase.TargetRecoveryTime; + + if (prototype is DatabasePrototype160) { + ((DatabaseInfo)databaseViewInfo.ObjectInfo).IsLedgerDatabase = smoDatabase.IsLedger; + } + } } } @@ -259,7 +293,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement { var sqlScript = string.Empty; ConnectionInfo connectionInfo = this.GetConnectionInfo(detachParams.ConnectionUri); - using (var dataContainer = CreateDatabaseDataContainer(detachParams.ConnectionUri, detachParams.ObjectUrn, false)) + using (var dataContainer = CreateDatabaseDataContainer(detachParams.ConnectionUri, detachParams.ObjectUrn, false, null)) { var smoDatabase = dataContainer.SqlDialogSubject as Database; if (smoDatabase != null) @@ -326,9 +360,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement return builder.ToString(); } - private CDataContainer CreateDatabaseDataContainer(string connectionUri, string? objectURN, bool isNewDatabase) + private CDataContainer CreateDatabaseDataContainer(string connectionUri, string? objectURN, bool isNewDatabase, string? databaseName) { ConnectionInfo connectionInfo = this.GetConnectionInfo(connectionUri); + if (!isNewDatabase && !string.IsNullOrEmpty(databaseName)) + { + connectionInfo.ConnectionDetails.DatabaseName = databaseName; + } CDataContainer dataContainer = CDataContainer.CreateDataContainer(connectionInfo, databaseExists: !isNewDatabase); if (dataContainer.Server == null) { @@ -349,7 +387,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement throw new ArgumentException("Database name not provided."); } - using (var dataContainer = CreateDatabaseDataContainer(viewParams.ConnectionUri, viewParams.ObjectUrn, viewParams.IsNewObject)) + using (var dataContainer = CreateDatabaseDataContainer(viewParams.ConnectionUri, viewParams.ObjectUrn, viewParams.IsNewObject, viewParams.Database)) { if (dataContainer.Server == null) { @@ -363,7 +401,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement prototype.Name = database.Name; // Update database file names now that we have a database name - if (!prototype.HideFileSettings) + // Modifying logical file name is not supported in SQL Database Managed Instance. + if (!prototype.HideFileSettings && dataContainer.Server.DatabaseEngineEdition != DatabaseEngineEdition.SqlManagedInstance) { var sanitizedName = DatabaseUtils.SanitizeDatabaseFileName(prototype.Name); @@ -385,7 +424,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement } } - if (database.Owner != null) + if (database.Owner != null && viewParams.IsNewObject) { prototype.Owner = database.Owner; } @@ -401,11 +440,49 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement { prototype.DatabaseCompatibilityLevel = compatLevelEnums[database.CompatibilityLevel]; } - if (prototype is DatabasePrototype110 db110 && database.ContainmentType != null) + if (prototype is DatabasePrototype80 db80) { - db110.DatabaseContainmentType = containmentTypeEnums[database.ContainmentType]; + if (database.DatabaseReadOnly != null) + { + db80.IsReadOnly = (bool)database.DatabaseReadOnly; + } } + if (prototype is DatabasePrototype90 db90) + { + db90.AutoUpdateStatisticsAsync = database.AutoUpdateStatisticsAsynchronously; + db90.PageVerifyDisplay = database.PageVerify; + } + if (prototype is DatabasePrototype100 db100) + { + db100.EncryptionEnabled = database.EncryptionEnabled; + } + if (prototype is DatabasePrototype110 db110) + { + if (database.TargetRecoveryTimeInSec != null) { + db110.TargetRecoveryTime = (int)database.TargetRecoveryTimeInSec; + } + + if (database.ContainmentType != null) + { + db110.DatabaseContainmentType = containmentTypeEnums[database.ContainmentType]; + } + } + if (prototype is DatabasePrototype160 db160) + { + if (database.IsLedgerDatabase != null) + { + db160.IsLedger = (bool)database.IsLedgerDatabase; + } + } + + // AutoCreateStatisticsIncremental can only be set when AutoCreateStatistics is enabled + prototype.AutoCreateStatisticsIncremental = database.AutoCreateIncrementalStatistics; + prototype.AutoCreateStatistics = database.AutoCreateStatistics; + prototype.AutoShrink= database.AutoShrink; + prototype.AutoUpdateStatistics = database.AutoUpdateStatistics; + prototype.RestrictAccess = database.RestrictAccess; + if (prototype is DatabasePrototypeAzure dbAz) { // Set edition first since the prototype will fill all the Azure fields with default values diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseInfo.cs index 8d0b43cf..3efd2fbb 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseInfo.cs @@ -28,5 +28,16 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement public string? AzureServiceLevelObjective { get; set; } public string? AzureEdition { get; set; } public string? AzureMaxSize { get; set; } + public bool AutoCreateIncrementalStatistics { get; set; } + public bool AutoCreateStatistics { get; set; } + public bool AutoShrink { get; set; } + public bool AutoUpdateStatistics { get; set; } + public bool AutoUpdateStatisticsAsynchronously { get; set; } + public bool? IsLedgerDatabase { get; set; } + public string? PageVerify { get; set; } + public int? TargetRecoveryTimeInSec { get; set; } + public bool? DatabaseReadOnly { get; set; } + public bool EncryptionEnabled { get; set; } + public string? RestrictAccess { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseViewInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseViewInfo.cs index 4a1d95be..40e31a2a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseViewInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectManagement/ObjectTypes/Database/DatabaseViewInfo.cs @@ -21,6 +21,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectManagement public AzureEditionDetails[] AzureServiceLevelObjectives { get; set; } public string[] AzureEditions { get; set; } public AzureEditionDetails[] AzureMaxSizes { get; set; } + public string[] PageVerifyOptions { get; set; } + public string[] RestrictAccessOptions { get; set; } } public class AzureEditionDetails diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/DatabaseHandlerTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/DatabaseHandlerTests.cs index 4ae785b8..62e9f79d 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/DatabaseHandlerTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/DatabaseHandlerTests.cs @@ -293,6 +293,13 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).LastDatabaseBackup, Is.EqualTo(testDatabase.LastDatabaseBackup), $"Should have no database last backup date"); Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).LastDatabaseLogBackup, Is.EqualTo(testDatabase.LastDatabaseLogBackup), $"Should have no database backup log date"); Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).SizeInMb, Is.GreaterThan(0), $"Should have default database size when created"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).AutoCreateIncrementalStatistics, Is.True, $"AutoCreateIncrementalStatistics match with testdata"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).AutoCreateStatistics, Is.True, $"AutoCreateStatistics should match with testdata"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).AutoShrink, Is.False, $"AutoShrink should match with testdata"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).AutoUpdateStatistics, Is.True, $"AutoUpdateStatistics should match with testdata"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).AutoUpdateStatisticsAsynchronously, Is.False, $"AutoUpdateStatisticsAsynchronously should match with testdata"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).PageVerify, Is.EqualTo(testDatabase.PageVerify), $"PageVerify should match with testdata"); + Assert.That(((DatabaseInfo)databaseViewInfo.ObjectInfo).RestrictAccess, Is.EqualTo(testDatabase.RestrictAccess), $"RestrictAccess should match with testdata"); // cleanup await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn, throwIfNotExist: true); diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs index f863a419..3f10af9a 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectManagement/ObjectManagementTestUtils.cs @@ -72,7 +72,14 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement ContainmentType = "None", RecoveryModel = "Full", LastDatabaseBackup = "None", - LastDatabaseLogBackup = "None" + LastDatabaseLogBackup = "None", + PageVerify = "CHECKSUM", + RestrictAccess = "MULTI_USER", + AutoCreateIncrementalStatistics = true, + AutoCreateStatistics = true, + AutoShrink = false, + AutoUpdateStatistics = true, + AutoUpdateStatisticsAsynchronously = false }; }