diff --git a/extensions/mssql/src/objectManagement/commands.ts b/extensions/mssql/src/objectManagement/commands.ts index 8c5e15ca65..5417d0e673 100644 --- a/extensions/mssql/src/objectManagement/commands.ts +++ b/extensions/mssql/src/objectManagement/commands.ts @@ -15,7 +15,7 @@ import * as uiLoc from '../ui/localizedConstants'; import { UserDialog } from './ui/userDialog'; import { IObjectManagementService, ObjectManagement } from 'mssql'; import * as constants from '../constants'; -import { refreshParentNode } from './utils'; +import { refreshParentNode, escapeSingleQuotes } from './utils'; import { TelemetryReporter } from '../telemetry'; import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './ui/objectManagementDialogBase'; import { ServerRoleDialog } from './ui/serverRoleDialog'; @@ -113,8 +113,8 @@ async function handleObjectPropertiesDialogCommand(context: azdata.ObjectExplore try { const parentUrn = context.nodeInfo ? await getParentUrn(context) : undefined; const objectType = context.nodeInfo ? context.nodeInfo.nodeType as ObjectManagement.NodeType : (context.connectionProfile.databaseName === '' ? ObjectManagement.NodeType.Server : ObjectManagement.NodeType.Database); - const objectName = context.nodeInfo ? context.nodeInfo.label : objectManagementLoc.PropertiesHeader; - const objectUrn = context.nodeInfo ? context.nodeInfo!.metadata!.urn : undefined; + const objectName = context.nodeInfo ? context.nodeInfo.label : (!context.connectionProfile.databaseName ? context.connectionProfile.serverName : context.connectionProfile.databaseName); + const objectUrn = context.nodeInfo ? context.nodeInfo!.metadata!.urn : (context.connectionProfile.databaseName === '' ? 'Server' : `Server/Database[@Name='${escapeSingleQuotes(context.connectionProfile.databaseName)}']`); const options: ObjectManagementDialogOptions = { connectionUri: connectionUri, diff --git a/extensions/mssql/src/objectManagement/constants.ts b/extensions/mssql/src/objectManagement/constants.ts index 2b078b8da6..82a6350317 100644 --- a/extensions/mssql/src/objectManagement/constants.ts +++ b/extensions/mssql/src/objectManagement/constants.ts @@ -43,5 +43,3 @@ export const enum TelemetryActions { } export const ObjectManagementViewName = 'ObjectManagement'; - -export const AzureSQLMI = 'Azure SQL Database Managed Instance'; diff --git a/extensions/mssql/src/objectManagement/interfaces.ts b/extensions/mssql/src/objectManagement/interfaces.ts index f72593d9ae..96acf72bc8 100644 --- a/extensions/mssql/src/objectManagement/interfaces.ts +++ b/extensions/mssql/src/objectManagement/interfaces.ts @@ -482,7 +482,7 @@ export interface Server extends ObjectManagement.SqlObject { rootDirectory: string; serverCollation: string; serviceTier: string; - storageSpaceUsageInGB: number; + storageSpaceUsageInMB: number; minServerMemory: number; maxServerMemory: number; } diff --git a/extensions/mssql/src/objectManagement/localizedConstants.ts b/extensions/mssql/src/objectManagement/localizedConstants.ts index eceb0d7a3d..5bba61cdc6 100644 --- a/extensions/mssql/src/objectManagement/localizedConstants.ts +++ b/extensions/mssql/src/objectManagement/localizedConstants.ts @@ -252,11 +252,11 @@ export const IsHadrEnabledText = localize('objectManagement.isHadrEnabled', "Is export const IsPolyBaseInstalledText = localize('objectManagement.isPolyBaseInstalled', "Is PolyBase Installed"); export const IsXTPSupportedText = localize('objectManagement.isXTPSupported', "Is XTP Supported"); export const ProductText = localize('objectManagement.product', "Product"); -export const ReservedStorageSizeInMBText = localize('objectManagement.reservedStorageSizeInMB', "Reserved Storage Size"); +export const ReservedStorageSizeInMBText = localize('objectManagement.reservedStorageSizeInMB', "Reserved Storage Size (MB)"); export const RootDirectoryText = localize('objectManagement.rootDirectory', "Root Directory"); export const ServerCollationText = localize('objectManagement.serverCollation', "Server Collation"); export const ServiceTierText = localize('objectManagement.serviceTier', "Service Tier"); -export const StorageSpaceUsageInGBText = localize('objectManagement.storageSpaceUsageInGB', "Storage Space Usage"); +export const StorageSpaceUsageInMBText = localize('objectManagement.storageSpaceUsageInMB', "Storage Space Usage (MB)"); export const VersionText = localize('objectManagement.versionText', "Version"); diff --git a/extensions/mssql/src/objectManagement/ui/serverPropertiesDialog.ts b/extensions/mssql/src/objectManagement/ui/serverPropertiesDialog.ts index 7933d3f3e6..c82e383d23 100644 --- a/extensions/mssql/src/objectManagement/ui/serverPropertiesDialog.ts +++ b/extensions/mssql/src/objectManagement/ui/serverPropertiesDialog.ts @@ -6,7 +6,7 @@ import * as azdata from 'azdata'; import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase'; import { IObjectManagementService } from 'mssql'; import * as localizedConstants from '../localizedConstants'; -import { AzureSQLMI, ViewServerPropertiesDocUrl } from '../constants'; +import { ViewServerPropertiesDocUrl } from '../constants'; import { Server, ServerViewInfo } from '../interfaces'; export class ServerPropertiesDialog extends ObjectManagementDialogBase { @@ -29,23 +29,32 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase { + const serverInfo = await azdata.connection.getServerInfo(this.options.objectExplorerContext.connectionProfile.id); + this.engineEdition = serverInfo.engineEditionId; this.initializeGeneralSection(); this.initializeMemorySection(); const serverPropertiesTabGroup = { title: '', tabs: [this.generalTab, this.memoryTab] }; @@ -98,7 +107,7 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase { }, this.objectInfo.product, this.options.isNewObject); const productContainer = this.createLabelInputContainer(localizedConstants.ProductText, this.productInput); - this.reservedStorageSizeInMBInput = this.createInputBox(localizedConstants.ReservedStorageSizeInMBText, async () => { }, this.objectInfo.reservedStorageSizeMB.toString().concat(' MB'), this.options.isNewObject); + this.reservedStorageSizeInMBInput = this.createInputBox(localizedConstants.ReservedStorageSizeInMBText, async () => { }, localizedConstants.StringValueInMB(this.objectInfo.reservedStorageSizeMB.toString()), this.options.isNewObject); const reservedStorageSizeInMBContainer = this.createLabelInputContainer(localizedConstants.ReservedStorageSizeInMBText, this.reservedStorageSizeInMBInput); this.rootDirectoryInput = this.createInputBox(localizedConstants.RootDirectoryText, async () => { }, this.objectInfo.rootDirectory, this.options.isNewObject); @@ -110,8 +119,8 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase { }, this.objectInfo.serviceTier, this.options.isNewObject); const serviceTierContainer = this.createLabelInputContainer(localizedConstants.ServiceTierText, this.serviceTierInput); - this.storageSpaceUsageInGBInput = this.createInputBox(localizedConstants.StorageSpaceUsageInGBText, async () => { }, this.objectInfo.storageSpaceUsageInGB.toString().concat(' GB'), this.options.isNewObject); - const storageSpaceUsageInGbContainer = this.createLabelInputContainer(localizedConstants.StorageSpaceUsageInGBText, this.storageSpaceUsageInGBInput); + this.storageSpaceUsageInMBInput = this.createInputBox(localizedConstants.StorageSpaceUsageInMBText, async () => { }, localizedConstants.StringValueInMB(this.objectInfo.storageSpaceUsageInMB.toString()), this.options.isNewObject); + const storageSpaceUsageInMbContainer = this.createLabelInputContainer(localizedConstants.StorageSpaceUsageInMBText, this.storageSpaceUsageInMBInput); this.versionInput = this.createInputBox(localizedConstants.VersionText, async () => { }, this.objectInfo.version, this.options.isNewObject); const versionContainer = this.createLabelInputContainer(localizedConstants.VersionText, this.versionInput); @@ -136,9 +145,9 @@ export class ServerPropertiesDialog extends ObjectManagementDialogBase { this.objectInfo.minServerMemory = +newValue; - }, this.objectInfo.minServerMemory.toString(), true, 'number'); + }, this.objectInfo.minServerMemory.toString(), isEnabled, 'number'); const minMemoryContainer = this.createLabelInputContainer(localizedConstants.minServerMemoryText, this.minServerMemoryInput); this.maxServerMemoryInput = this.createInputBox(localizedConstants.maxServerMemoryText, async (newValue) => { this.objectInfo.maxServerMemory = +newValue; - }, this.objectInfo.maxServerMemory.toString(), true, 'number'); + }, this.objectInfo.maxServerMemory.toString(), isEnabled, 'number'); const maxMemoryContainer = this.createLabelInputContainer(localizedConstants.maxServerMemoryText, this.maxServerMemoryInput); this.memorySection = this.createGroup('', [ diff --git a/extensions/mssql/src/objectManagement/utils.ts b/extensions/mssql/src/objectManagement/utils.ts index 9d509f11f5..482c23b6d0 100644 --- a/extensions/mssql/src/objectManagement/utils.ts +++ b/extensions/mssql/src/objectManagement/utils.ts @@ -47,3 +47,8 @@ export function isValidSQLPassword(password: string, userName: string = 'sa'): b export function convertNumToTwoDecimalStringinMB(value: number): string { return localizedConstants.StringValueInMB(value?.toFixed(2)); } + +// Escape single quotes +export function escapeSingleQuotes(value: string): string { + return value.replace(/'/g, "\'"); +} diff --git a/extensions/mssql/src/test/util/utils.test.ts b/extensions/mssql/src/test/util/utils.test.ts new file mode 100644 index 0000000000..d5b34ef63b --- /dev/null +++ b/extensions/mssql/src/test/util/utils.test.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { escapeSingleQuotes } from '../../objectManagement/utils'; +import 'mocha'; +import * as should from 'should'; + +describe('escapeSingleQuotes Method Tests', () => { + + it('Should return original string if no single quotes', function (): void { + const dbName = "My Database"; + const testString: string = "Server/Database[@Name='My Database']"; + const ret = `Server/Database[@Name='${escapeSingleQuotes(dbName)}']`; + should(ret).equal(testString); + }); + + it('Should return original string if it contains single quotes', function (): void { + const dbName = "My'Database"; + const testString: string = "Server/Database[@Name='My'Database']"; + const ret = `Server/Database[@Name='${escapeSingleQuotes(dbName)}']`; + should(ret).equal(testString); + }); + + it('Should return escaped original string if it contains an escaped single quote', function (): void { + const dbName = "My Database\'WithEscapedSingleQuote"; + const testString: string = "Server/Database[@Name='My Database\'WithEscapedSingleQuote']"; + const ret = `Server/Database[@Name='${escapeSingleQuotes(dbName)}']`; + should(ret).equal(testString); + }); +});