mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Enabling QueryStore options to the database properties (#24255)
* Initial commit * updated query capture policy props * all done except, querydiskSpaceSection * disabling capture policy options on off mode but with custom mode enabled * adding query disk usage section with purge button * fg table replace * STS vbump to 4.9.0.34 * typo
This commit is contained in:
committed by
GitHub
parent
594aabb835
commit
aa006b9da7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "4.9.0.32",
|
"version": "4.9.0.34",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-net7.0.zip",
|
"Windows_86": "win-x86-net7.0.zip",
|
||||||
"Windows_64": "win-x64-net7.0.zip",
|
"Windows_64": "win-x64-net7.0.zip",
|
||||||
|
|||||||
@@ -1699,6 +1699,16 @@ export namespace GetAssociatedFilesRequest {
|
|||||||
export const type = new RequestType<GetAssociatedFilesRequestParams, string[], void, void>('admin/getassociatedfiles');
|
export const type = new RequestType<GetAssociatedFilesRequestParams, string[], void, void>('admin/getassociatedfiles');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace PurgeQueryStoreDataRequest {
|
||||||
|
export const type = new RequestType<purgeQueryStoreDataRequestParams, void, void, void>('objectManagement/purgeQueryStoreData');
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface purgeQueryStoreDataRequestParams {
|
||||||
|
connectionUri: string;
|
||||||
|
database: string;
|
||||||
|
objectUrn: string;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------- < Object Management > ------------------------------------
|
// ------------------------------- < Object Management > ------------------------------------
|
||||||
|
|
||||||
// ------------------------------- < Encryption IV/KEY updation Event > ------------------------------------
|
// ------------------------------- < Encryption IV/KEY updation Event > ------------------------------------
|
||||||
|
|||||||
7
extensions/mssql/src/mssql.d.ts
vendored
7
extensions/mssql/src/mssql.d.ts
vendored
@@ -1020,6 +1020,13 @@ declare module 'mssql' {
|
|||||||
* @returns An array of file path strings for each of the associated files.
|
* @returns An array of file path strings for each of the associated files.
|
||||||
*/
|
*/
|
||||||
getAssociatedFiles(connectionUri: string, primaryFilePath: string): Thenable<string[]>;
|
getAssociatedFiles(connectionUri: string, primaryFilePath: string): Thenable<string[]>;
|
||||||
|
/**
|
||||||
|
* Clears all query store data from the database
|
||||||
|
* @param connectionUri The URI of the server connection.
|
||||||
|
* @param database The target database.
|
||||||
|
* @param objectUrn SMO Urn of the database to be detached. More information: https://learn.microsoft.com/sql/relational-databases/server-management-objects-smo/overview-smo
|
||||||
|
*/
|
||||||
|
purgeQueryStoreData(connectionUri: string, database: string, objectUrn: string): Thenable<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseFileData {
|
export interface DatabaseFileData {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const DatabaseGeneralPropertiesDocUrl = 'https://learn.microsoft.com/sql/
|
|||||||
export const DatabaseOptionsPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-options-page'
|
export const DatabaseOptionsPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-options-page'
|
||||||
export const DropDatabaseDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/drop-database-transact-sql';
|
export const DropDatabaseDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/drop-database-transact-sql';
|
||||||
export const DatabaseScopedConfigurationPropertiesDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/alter-database-scoped-configuration-transact-sql'
|
export const DatabaseScopedConfigurationPropertiesDocUrl = 'https://learn.microsoft.com/sql/t-sql/statements/alter-database-scoped-configuration-transact-sql'
|
||||||
|
export const QueryStorePropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-query-store-page'
|
||||||
export const DatabaseFilesPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-files-page'
|
export const DatabaseFilesPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-files-page'
|
||||||
export const DatabaseFileGroupsPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-filegroups-page'
|
export const DatabaseFileGroupsPropertiesDocUrl = 'https://learn.microsoft.com/sql/relational-databases/databases/database-properties-filegroups-page'
|
||||||
|
|
||||||
|
|||||||
@@ -458,6 +458,7 @@ export interface Database extends ObjectManagement.SqlObject {
|
|||||||
isFilesTabSupported?: boolean;
|
isFilesTabSupported?: boolean;
|
||||||
files?: DatabaseFile[];
|
files?: DatabaseFile[];
|
||||||
filegroups?: FileGroup[];
|
filegroups?: FileGroup[];
|
||||||
|
queryStoreOptions?: QueryStoreOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseViewInfo extends ObjectManagement.ObjectViewInfo<Database> {
|
export interface DatabaseViewInfo extends ObjectManagement.ObjectViewInfo<Database> {
|
||||||
@@ -481,6 +482,32 @@ export interface DatabaseViewInfo extends ObjectManagement.ObjectViewInfo<Databa
|
|||||||
rowDataFileGroupsOptions?: string[];
|
rowDataFileGroupsOptions?: string[];
|
||||||
fileStreamFileGroupsOptions?: string[];
|
fileStreamFileGroupsOptions?: string[];
|
||||||
fileTypesOptions?: string[];
|
fileTypesOptions?: string[];
|
||||||
|
operationModeOptions?: string[];
|
||||||
|
statisticsCollectionIntervalOptions?: string[];
|
||||||
|
queryStoreCaptureModeOptions?: string[];
|
||||||
|
sizeBasedCleanupModeOptions?: string[];
|
||||||
|
staleThresholdOptions?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryStoreOptions {
|
||||||
|
actualMode: string;
|
||||||
|
dataFlushIntervalInMinutes: number;
|
||||||
|
statisticsCollectionInterval: string;
|
||||||
|
maxPlansPerQuery: number;
|
||||||
|
maxSizeInMB: number;
|
||||||
|
queryStoreCaptureMode: string;
|
||||||
|
sizeBasedCleanupMode: string;
|
||||||
|
staleQueryThresholdInDays: number;
|
||||||
|
waitStatisticsCaptureMode?: boolean;
|
||||||
|
capturePolicyOptions?: QueryStoreCapturePolicyOptions;
|
||||||
|
currentStorageSizeInMB: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryStoreCapturePolicyOptions {
|
||||||
|
executionCount: number;
|
||||||
|
staleThreshold: string;
|
||||||
|
totalCompileCPUTimeInMS: number;
|
||||||
|
totalExecutionCPUTimeInMS: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseScopedConfigurationsInfo {
|
export interface DatabaseScopedConfigurationsInfo {
|
||||||
|
|||||||
@@ -385,6 +385,7 @@ export const DatabaseStateText = localize('objectManagement.databaseProperties.d
|
|||||||
export const EncryptionEnabledText = localize('objectManagement.databaseProperties.encryptionEnabledText', "Encryption Enabled");
|
export const EncryptionEnabledText = localize('objectManagement.databaseProperties.encryptionEnabledText', "Encryption Enabled");
|
||||||
export const RestrictAccessText = localize('objectManagement.databaseProperties.restrictAccessText', "Restrict Access");
|
export const RestrictAccessText = localize('objectManagement.databaseProperties.restrictAccessText', "Restrict Access");
|
||||||
export const DatabaseScopedConfigurationTabHeader = localize('objectManagement.databaseProperties.databaseProperties.databaseScopedConfigurationTabHeader', "Database Scoped Configuration");
|
export const DatabaseScopedConfigurationTabHeader = localize('objectManagement.databaseProperties.databaseProperties.databaseScopedConfigurationTabHeader', "Database Scoped Configuration");
|
||||||
|
export const QueryStoreTabHeader = localize('objectManagement.databaseProperties.databaseProperties.queryStoreTabHeader', "Query Store");
|
||||||
export const DatabaseScopedOptionsColumnHeader = localize('objectManagement.databaseProperties.databaseScopedOptionsColumnHeader', "Database Scoped Options");
|
export const DatabaseScopedOptionsColumnHeader = localize('objectManagement.databaseProperties.databaseScopedOptionsColumnHeader', "Database Scoped Options");
|
||||||
export const ValueForPrimaryColumnHeader = localize('objectManagement.databaseProperties.valueForPrimaryColumnHeader', "Value for Primary");
|
export const ValueForPrimaryColumnHeader = localize('objectManagement.databaseProperties.valueForPrimaryColumnHeader', "Value for Primary");
|
||||||
export const ValueForSecondaryColumnHeader = localize('objectManagement.databaseProperties.valueForSecondaryColumnHeader', "Value for Secondary");
|
export const ValueForSecondaryColumnHeader = localize('objectManagement.databaseProperties.valueForSecondaryColumnHeader', "Value for Secondary");
|
||||||
@@ -440,6 +441,30 @@ export const FilestreamFilesText = localize('objectManagement.databaseProperties
|
|||||||
export const AddFilegroupText = localize('objectManagement.databaseProperties.addFilegroupButtonText', "Add Filegroup");
|
export const AddFilegroupText = localize('objectManagement.databaseProperties.addFilegroupButtonText', "Add Filegroup");
|
||||||
export const FilegroupExistsError = (name: string) => localize('objectManagement.databaseProperties.FilegroupExistsError', "File group '{0}' could not be added to the collection, because it already exists.", name);
|
export const FilegroupExistsError = (name: string) => localize('objectManagement.databaseProperties.FilegroupExistsError', "File group '{0}' could not be added to the collection, because it already exists.", name);
|
||||||
export const EmptyFilegroupNameError = localize('objectManagement.databaseProperties.emptyFilegroupNameError', "Cannot use empty object names for filegroups.");
|
export const EmptyFilegroupNameError = localize('objectManagement.databaseProperties.emptyFilegroupNameError', "Cannot use empty object names for filegroups.");
|
||||||
|
export const ActualOperationModeText = localize('objectManagement.databaseProperties.actualOperationModeText', "Operation Mode (Actual)");
|
||||||
|
export const RequestedOperationModeText = localize('objectManagement.databaseProperties.requestedOperationModeText', "Operation Mode (Requested)");
|
||||||
|
export const DataFlushIntervalInMinutesText = localize('objectManagement.databaseProperties.dataFlushIntervalInMinutesText', "Data Flush Interval (Minutes)");
|
||||||
|
export const StatisticsCollectionInterval = localize('objectManagement.databaseProperties.statisticsCollectionInterval', "Statistics Collection Interval");
|
||||||
|
export const MaxPlansPerQueryText = localize('objectManagement.databaseProperties.maxPlansPerQueryText', "Max Plans Per Query");
|
||||||
|
export const MaxSizeInMbText = localize('objectManagement.databaseProperties.maxSizeInMbText', "Max Size (MB)");
|
||||||
|
export const QueryStoreCaptureModeText = localize('objectManagement.databaseProperties.queryStoreCaptureModeText', "Query Store Capture Mode");
|
||||||
|
export const SizeBasedCleanupModeText = localize('objectManagement.databaseProperties.sizeBasedCleanupModeText', "Size Based Cleanup Mode");
|
||||||
|
export const StateQueryThresholdInDaysText = localize('objectManagement.databaseProperties.stateQueryThresholdInDaysText', "State Query Threshold (Days)");
|
||||||
|
export const WaitStatisticsCaptureModeText = localize('objectManagement.databaseProperties.waitStatisticsCaptureModeText', "Wait Statistics Capture Mode");
|
||||||
|
export const MonitoringSectionText = localize('objectManagement.databaseProperties.monitoringSectionText', "Monitoring");
|
||||||
|
export const QueryStoreRetentionSectionText = localize('objectManagement.databaseProperties.queryStoreRetentionSectionText', "Query Store Retention");
|
||||||
|
export const QueryStoreCapturePolicySectionText = localize('objectManagement.databaseProperties.queryStoreCapturePolicySectionText', "Query Store Capture Policy");
|
||||||
|
export const QueryStoreCurrentDiskUsageSectionText = localize('objectManagement.databaseProperties.queryStoreCurrentDiskUsageSectionText', "Current Disk Usage");
|
||||||
|
export const ExecutionCountText = localize('objectManagement.databaseProperties.executionCountText', "Execution Count");
|
||||||
|
export const StaleThresholdText = localize('objectManagement.databaseProperties.staleThresholdText', "Stale Threshold");
|
||||||
|
export const TotalCompileCPUTimeInMsText = localize('objectManagement.databaseProperties.totalCompileCPUTimeInMs', "Total Compile CPU Time (ms)");
|
||||||
|
export const TotalExecutionCPUTimeInMsText = localize('objectManagement.databaseProperties.totalExecutionCPUTimeInMsText', "Total Execution CPU Time (ms)");
|
||||||
|
export const QueryStoreCapturemodeCustomText = localize('objectManagement.databaseProperties.queryStoreCapturemodeCustomText', "Custom");
|
||||||
|
export const QueryStoreUsedText = localize('objectManagement.databaseProperties.queryStoreUsedText', "Query Store Used");
|
||||||
|
export const QueryStoreAvailableText = localize('objectManagement.databaseProperties.queryStoreAvailableText', "Query Store Available");
|
||||||
|
export const PurgeQueryDataButtonText = localize('objectManagement.databaseProperties.purgeQueryDataButtonText', "Purge Query Store Data");
|
||||||
|
export const YesText = localize('objectManagement.databaseProperties.yesText', "Yes");
|
||||||
|
export const PurgeQueryStoreDataMessage = (databaseName: string) => localize('objectManagement.databaseProperties.purgeQueryStoreDataMessage', "Are you sure you want to purge the Query Store data from '{0}'?", databaseName);
|
||||||
|
|
||||||
// Util functions
|
// Util functions
|
||||||
export function getNodeTypeDisplayName(type: string, inTitle: boolean = false): string {
|
export function getNodeTypeDisplayName(type: string, inTitle: boolean = false): string {
|
||||||
|
|||||||
@@ -90,6 +90,11 @@ export class ObjectManagementService extends BaseService implements IObjectManag
|
|||||||
const params: contracts.GetAssociatedFilesRequestParams = { connectionUri, primaryFilePath };
|
const params: contracts.GetAssociatedFilesRequestParams = { connectionUri, primaryFilePath };
|
||||||
return this.runWithErrorHandling(contracts.GetAssociatedFilesRequest.type, params);
|
return this.runWithErrorHandling(contracts.GetAssociatedFilesRequest.type, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async purgeQueryStoreData(connectionUri: string, database: string, objectUrn: string): Promise<void> {
|
||||||
|
const params: contracts.purgeQueryStoreDataRequestParams = { connectionUri, database, objectUrn };
|
||||||
|
return this.runWithErrorHandling(contracts.PurgeQueryStoreDataRequest.type, params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServerLevelSecurableTypes: SecurableTypeMetadata[] = [
|
const ServerLevelSecurableTypes: SecurableTypeMetadata[] = [
|
||||||
@@ -277,6 +282,10 @@ export class TestObjectManagementService implements IObjectManagementService {
|
|||||||
return this.delayAndResolve([]);
|
return this.delayAndResolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async purgeQueryStoreData(connectionUri: string, database: string, objectUrn: string): Promise<void> {
|
||||||
|
return this.delayAndResolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
private generateSearchResult(objectType: ObjectManagement.NodeType, schema: string | undefined, count: number): ObjectManagement.SearchResultItem[] {
|
private generateSearchResult(objectType: ObjectManagement.NodeType, schema: string | undefined, count: number): ObjectManagement.SearchResultItem[] {
|
||||||
let items: ObjectManagement.SearchResultItem[] = [];
|
let items: ObjectManagement.SearchResultItem[] = [];
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
|
|||||||
@@ -8,17 +8,23 @@ import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './obj
|
|||||||
import { DefaultInputWidth, DefaultTableWidth, DefaultMinTableRowCount, DefaultMaxTableRowCount, getTableHeight, DialogButton } from '../../ui/dialogBase';
|
import { DefaultInputWidth, DefaultTableWidth, DefaultMinTableRowCount, DefaultMaxTableRowCount, getTableHeight, DialogButton } from '../../ui/dialogBase';
|
||||||
import { IObjectManagementService } from 'mssql';
|
import { IObjectManagementService } from 'mssql';
|
||||||
import * as localizedConstants from '../localizedConstants';
|
import * as localizedConstants from '../localizedConstants';
|
||||||
import { CreateDatabaseDocUrl, DatabaseGeneralPropertiesDocUrl, DatabaseFilesPropertiesDocUrl, DatabaseOptionsPropertiesDocUrl, DatabaseScopedConfigurationPropertiesDocUrl, DatabaseFileGroupsPropertiesDocUrl } from '../constants';
|
import { CreateDatabaseDocUrl, DatabaseGeneralPropertiesDocUrl, DatabaseFilesPropertiesDocUrl, DatabaseOptionsPropertiesDocUrl, DatabaseScopedConfigurationPropertiesDocUrl, DatabaseFileGroupsPropertiesDocUrl, QueryStorePropertiesDocUrl } from '../constants';
|
||||||
import { Database, DatabaseFile, DatabaseScopedConfigurationsInfo, DatabaseViewInfo, FileGrowthType, FileGroup, FileGroupType } from '../interfaces';
|
import { Database, DatabaseFile, DatabaseScopedConfigurationsInfo, DatabaseViewInfo, FileGrowthType, FileGroup, FileGroupType } from '../interfaces';
|
||||||
import { convertNumToTwoDecimalStringInMB } from '../utils';
|
import { convertNumToTwoDecimalStringInMB } from '../utils';
|
||||||
import { isUndefinedOrNull } from '../../types';
|
import { isUndefinedOrNull } from '../../types';
|
||||||
import { deepClone } from '../../util/objects';
|
import { deepClone } from '../../util/objects';
|
||||||
import { DatabaseFileDialog } from './databaseFileDialog';
|
import { DatabaseFileDialog } from './databaseFileDialog';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
const MAXDOP_Max_Limit = 32767;
|
const MAXDOP_Max_Limit = 32767;
|
||||||
const PAUSED_RESUMABLE_INDEX_Max_Limit = 71582;
|
const PAUSED_RESUMABLE_INDEX_Max_Limit = 71582;
|
||||||
const DscTableRowLength = 15;
|
const DscTableRowLength = 15;
|
||||||
|
|
||||||
|
export const tableHeaderCssStylings = { 'border': 'solid 1px #C8C8C8' };
|
||||||
|
export const tableRowCssStylings = { 'border': 'solid 1px #C8C8C8' }
|
||||||
|
// export const tableHeaderCssStylings = { 'border-top': 'solid 1px #C8C8C8', 'border-bottom': 'none', 'border-left': 'none', 'border-right': 'none' };
|
||||||
|
// export const tableRowCssStylings = { 'border-top': 'solid 1px #ccc', 'border-bottom': 'none', 'border-left': 'none', 'border-right': 'none' }
|
||||||
|
|
||||||
export class DatabaseDialog extends ObjectManagementDialogBase<Database, DatabaseViewInfo> {
|
export class DatabaseDialog extends ObjectManagementDialogBase<Database, DatabaseViewInfo> {
|
||||||
// Database Properties tabs
|
// Database Properties tabs
|
||||||
private generalTab: azdata.Tab;
|
private generalTab: azdata.Tab;
|
||||||
@@ -26,6 +32,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
private optionsTab: azdata.Tab;
|
private optionsTab: azdata.Tab;
|
||||||
private fileGroupsTab: azdata.Tab;
|
private fileGroupsTab: azdata.Tab;
|
||||||
private dscTab: azdata.Tab;
|
private dscTab: azdata.Tab;
|
||||||
|
private queryStoreTab: azdata.Tab;
|
||||||
private optionsTabSectionsContainer: azdata.Component[] = [];
|
private optionsTabSectionsContainer: azdata.Component[] = [];
|
||||||
private activeTabId: string;
|
private activeTabId: string;
|
||||||
|
|
||||||
@@ -53,7 +60,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
private filestreamDatafileGroupsOptions: string[];
|
private filestreamDatafileGroupsOptions: string[];
|
||||||
// fileGroups Tab
|
// fileGroups Tab
|
||||||
private readonly fileGroupsTabId: string = 'fileGroupsDatabaseId';
|
private readonly fileGroupsTabId: string = 'fileGroupsDatabaseId';
|
||||||
private rowsFilegroupsTable: azdata.TableComponent;
|
private rowsFilegroupsTable: azdata.DeclarativeTableComponent;
|
||||||
private filestreamFilegroupsTable: azdata.TableComponent;
|
private filestreamFilegroupsTable: azdata.TableComponent;
|
||||||
private memoryOptimizedFilegroupsTable: azdata.TableComponent;
|
private memoryOptimizedFilegroupsTable: azdata.TableComponent;
|
||||||
private rowsFilegroupNameInput: azdata.InputBoxComponent;
|
private rowsFilegroupNameInput: azdata.InputBoxComponent;
|
||||||
@@ -96,6 +103,26 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
private dscSecondaryCheckboxForInputGroup: azdata.GroupContainer;
|
private dscSecondaryCheckboxForInputGroup: azdata.GroupContainer;
|
||||||
private setFocusToInput: azdata.InputBoxComponent = undefined;
|
private setFocusToInput: azdata.InputBoxComponent = undefined;
|
||||||
private currentRowObjectInfo: DatabaseScopedConfigurationsInfo;
|
private currentRowObjectInfo: DatabaseScopedConfigurationsInfo;
|
||||||
|
// Query store Tab
|
||||||
|
private readonly queryStoreTabId: string = 'queryStoreTabId';
|
||||||
|
private queryStoreTabSectionsContainer: azdata.Component[] = [];
|
||||||
|
private areQueryStoreOptionsEnabled: boolean;
|
||||||
|
private requestedOperationMode: azdata.DropDownComponent;
|
||||||
|
private dataFlushIntervalInMinutes: azdata.InputBoxComponent;
|
||||||
|
private statisticsCollectionInterval: azdata.DropDownComponent;
|
||||||
|
private maxPlansPerQuery: azdata.InputBoxComponent;
|
||||||
|
private maxSizeinMB: azdata.InputBoxComponent;
|
||||||
|
private queryStoreCaptureMode: azdata.DropDownComponent;
|
||||||
|
private sizeBasedCleanupMode: azdata.DropDownComponent;
|
||||||
|
private stateQueryThresholdInDays: azdata.InputBoxComponent;
|
||||||
|
private waitStatisticsCaptureMode: azdata.CheckBoxComponent;
|
||||||
|
private executionCount: azdata.InputBoxComponent;
|
||||||
|
private staleThreshold: azdata.DropDownComponent;
|
||||||
|
private totalCompileCPUTimeInMS: azdata.InputBoxComponent;
|
||||||
|
private totalExecutionCPUTimeInMS: azdata.InputBoxComponent;
|
||||||
|
private operationModeOffOption: string;
|
||||||
|
private purgeQueryDataButton: azdata.ButtonComponent;
|
||||||
|
|
||||||
|
|
||||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||||
super(objectManagementService, options);
|
super(objectManagementService, options);
|
||||||
@@ -123,6 +150,9 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
case this.dscTabId:
|
case this.dscTabId:
|
||||||
helpUrl = DatabaseScopedConfigurationPropertiesDocUrl;
|
helpUrl = DatabaseScopedConfigurationPropertiesDocUrl;
|
||||||
break;
|
break;
|
||||||
|
case this.queryStoreTabId:
|
||||||
|
helpUrl = QueryStorePropertiesDocUrl;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -219,6 +249,23 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
tabs.push(this.dscTab);
|
tabs.push(this.dscTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intialize Query Store Tab
|
||||||
|
if (!isUndefinedOrNull(this.objectInfo.queryStoreOptions)) {
|
||||||
|
this.initializeQueryStoreGeneralSection();
|
||||||
|
this.initializeQueryStoreMonitoringSection();
|
||||||
|
this.initializeQueryStoreRetentionSection();
|
||||||
|
if (!isUndefinedOrNull(this.objectInfo.queryStoreOptions.capturePolicyOptions)) {
|
||||||
|
this.initializeQueryStoreCapturePolicySection();
|
||||||
|
}
|
||||||
|
await this.initializeQueryStoreCurrentDiskStorageSection();
|
||||||
|
this.queryStoreTab = {
|
||||||
|
title: localizedConstants.QueryStoreTabHeader,
|
||||||
|
id: this.queryStoreTabId,
|
||||||
|
content: this.createGroup('', this.queryStoreTabSectionsContainer, false)
|
||||||
|
}
|
||||||
|
tabs.push(this.queryStoreTab);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize tab group with tabbed panel
|
// Initialize tab group with tabbed panel
|
||||||
const propertiesTabGroup = { title: '', tabs: tabs };
|
const propertiesTabGroup = { title: '', tabs: tabs };
|
||||||
const propertiesTabbedPannel = this.modelView.modelBuilder.tabbedPanel()
|
const propertiesTabbedPannel = this.modelView.modelBuilder.tabbedPanel()
|
||||||
@@ -616,8 +663,8 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
*/
|
*/
|
||||||
private async updateFileGroupsTablesfileCount(fileType: string): Promise<void> {
|
private async updateFileGroupsTablesfileCount(fileType: string): Promise<void> {
|
||||||
if (fileType === localizedConstants.RowsDataFileType) {
|
if (fileType === localizedConstants.RowsDataFileType) {
|
||||||
let data = this.getTableData(FileGroupType.RowsFileGroup);
|
// let data = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
await this.setTableData(this.rowsFilegroupsTable, data);
|
// await this.setTableData(this.rowsFilegroupsTable, data);
|
||||||
}
|
}
|
||||||
else if (fileType === localizedConstants.FilestreamFileType) {
|
else if (fileType === localizedConstants.FilestreamFileType) {
|
||||||
let data = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
let data = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
||||||
@@ -649,13 +696,13 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (table === this.rowsFilegroupsTable && this.rowsFilegroupsTable.selectedRows !== undefined && this.rowsFilegroupsTable.selectedRows.length === 1) {
|
// else if (table === this.rowsFilegroupsTable && this.rowsFilegroupsTable.selectedRows !== undefined && this.rowsFilegroupsTable.selectedRows.length === 1) {
|
||||||
const selectedRow = this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]];
|
// const selectedRow = this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]];
|
||||||
// Cannot delete a row file if the fileGroup is Primary.
|
// // Cannot delete a row file if the fileGroup is Primary.
|
||||||
if (selectedRow.name === 'PRIMARY' && selectedRow.id > 0) {
|
// if (selectedRow.name === 'PRIMARY' && selectedRow.id > 0) {
|
||||||
isEnabled = false;
|
// isEnabled = false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return isEnabled;
|
return isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,49 +760,62 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
*/
|
*/
|
||||||
private async initializeRowsFileGroupSection(): Promise<azdata.GroupContainer> {
|
private async initializeRowsFileGroupSection(): Promise<azdata.GroupContainer> {
|
||||||
const data = this.getTableData(FileGroupType.RowsFileGroup);
|
const data = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
this.rowsFilegroupsTable = this.modelView.modelBuilder.table().withProps({
|
this.rowsFilegroupsTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
columns: [{
|
columns: [{
|
||||||
type: azdata.ColumnType.text,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
value: localizedConstants.NameText,
|
width: 120,
|
||||||
width: 120
|
isReadOnly: false,
|
||||||
|
displayName: localizedConstants.NameText,
|
||||||
|
headerCssStyles: tableHeaderCssStylings,
|
||||||
|
rowCssStyles: tableRowCssStylings
|
||||||
}, {
|
}, {
|
||||||
type: azdata.ColumnType.text,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
value: localizedConstants.FilesText,
|
width: 60,
|
||||||
width: 60
|
isReadOnly: true,
|
||||||
|
displayName: localizedConstants.FilesText,
|
||||||
|
headerCssStyles: tableHeaderCssStylings,
|
||||||
|
rowCssStyles: tableRowCssStylings
|
||||||
}, {
|
}, {
|
||||||
type: azdata.ColumnType.checkBox,
|
valueType: azdata.DeclarativeDataType.boolean,
|
||||||
value: localizedConstants.ReadOnlyText,
|
width: 80,
|
||||||
width: 80
|
isReadOnly: false,
|
||||||
|
displayName: localizedConstants.ReadOnlyText,
|
||||||
|
headerCssStyles: tableHeaderCssStylings,
|
||||||
|
rowCssStyles: tableRowCssStylings
|
||||||
}, {
|
}, {
|
||||||
type: azdata.ColumnType.checkBox,
|
valueType: azdata.DeclarativeDataType.boolean,
|
||||||
value: localizedConstants.DefaultText,
|
width: 80,
|
||||||
width: 80
|
isReadOnly: false,
|
||||||
|
displayName: localizedConstants.DefaultText,
|
||||||
|
headerCssStyles: tableHeaderCssStylings,
|
||||||
|
rowCssStyles: tableRowCssStylings
|
||||||
}, {
|
}, {
|
||||||
type: azdata.ColumnType.checkBox,
|
valueType: azdata.DeclarativeDataType.boolean,
|
||||||
value: localizedConstants.AutogrowAllFilesText,
|
isReadOnly: false,
|
||||||
width: 110
|
displayName: localizedConstants.AutogrowAllFilesText,
|
||||||
|
width: 110,
|
||||||
|
headerCssStyles: tableHeaderCssStylings,
|
||||||
|
rowCssStyles: tableRowCssStylings
|
||||||
}],
|
}],
|
||||||
data: data,
|
data: data,
|
||||||
height: getTableHeight(data.length, DefaultMinTableRowCount, DefaultMaxTableRowCount),
|
height: getTableHeight(data.length, DefaultMinTableRowCount, DefaultMaxTableRowCount),
|
||||||
width: DefaultTableWidth,
|
width: DefaultTableWidth,
|
||||||
forceFitColumns: azdata.ColumnSizingMode.DataFit,
|
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
'margin-left': '10px'
|
'margin-left': '10px'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
this.rowsFilegroupNameInput = this.getFilegroupNameInput(this.rowsFilegroupsTable, FileGroupType.RowsFileGroup);
|
|
||||||
const addButtonComponent: DialogButton = {
|
const addButtonComponent: DialogButton = {
|
||||||
buttonAriaLabel: localizedConstants.AddFilegroupText,
|
buttonAriaLabel: localizedConstants.AddFilegroupText,
|
||||||
buttonHandler: () => this.onAddDatabaseFileGroupsButtonClicked(this.rowsFilegroupsTable)
|
buttonHandler: () => this.onAddDatabaseFileGroupsButtonClicked1(this.rowsFilegroupsTable)
|
||||||
};
|
};
|
||||||
const removeButtonComponent: DialogButton = {
|
const removeButtonComponent: DialogButton = {
|
||||||
buttonAriaLabel: localizedConstants.RemoveButton,
|
buttonAriaLabel: localizedConstants.RemoveButton,
|
||||||
buttonHandler: () => this.onRemoveDatabaseFileGroupsButtonClicked(this.rowsFilegroupsTable)
|
buttonHandler: () => this.onRemoveDatabaseFileGroupsButtonClicked1(this.rowsFilegroupsTable)
|
||||||
};
|
};
|
||||||
const rowsFileGroupButtonContainer = this.addButtonsForTable(this.rowsFilegroupsTable, addButtonComponent, removeButtonComponent);
|
const rowsFileGroupButtonContainer = this.addButtonsForTable1(this.rowsFilegroupsTable, addButtonComponent, removeButtonComponent);
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.rowsFilegroupsTable.onCellAction(async (arg: azdata.ICheckboxCellActionEventArgs) => {
|
this.rowsFilegroupsTable.onDataChanged(async (arg: azdata.ICheckboxCellActionEventArgs) => {
|
||||||
let filegroup = this.rowDataFileGroupsTableRows[arg.row];
|
let filegroup = this.rowDataFileGroupsTableRows[arg.row];
|
||||||
// Read-Only column
|
// Read-Only column
|
||||||
if (arg.column === 2) {
|
if (arg.column === 2) {
|
||||||
@@ -771,14 +831,14 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Refresh the table with updated data
|
// Refresh the table with updated data
|
||||||
let data = this.getTableData(FileGroupType.RowsFileGroup);
|
// let data = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
await this.setTableData(this.rowsFilegroupsTable, data);
|
//await this.setTableData(this.rowsFilegroupsTable, data);
|
||||||
this.onFormFieldChange();
|
this.onFormFieldChange();
|
||||||
}),
|
}),
|
||||||
this.rowsFilegroupsTable.onRowSelected(
|
this.rowsFilegroupsTable.onRowSelected(
|
||||||
async () => {
|
async () => {
|
||||||
if (this.rowsFilegroupsTable.selectedRows.length === 1) {
|
if (this.rowsFilegroupsTable.selectedRow === 1) {
|
||||||
const fileGroup = this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]];
|
const fileGroup = this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRow];
|
||||||
await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': fileGroup.id < 0 ? 'visible' : 'hidden' });
|
await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': fileGroup.id < 0 ? 'visible' : 'hidden' });
|
||||||
this.rowsFilegroupNameInput.value = fileGroup.name;
|
this.rowsFilegroupNameInput.value = fileGroup.name;
|
||||||
this.onFormFieldChange();
|
this.onFormFieldChange();
|
||||||
@@ -786,10 +846,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
return this.createGroup(localizedConstants.RowsFileGroupsSectionText, [this.rowsFilegroupsTable, rowsFileGroupButtonContainer], true);
|
||||||
const rowContainer = this.modelView.modelBuilder.flexContainer().withItems([this.rowsFilegroupNameInput]).component();
|
|
||||||
rowContainer.addItems([rowsFileGroupButtonContainer], { flex: '0 0 auto' });
|
|
||||||
return this.createGroup(localizedConstants.RowsFileGroupsSectionText, [this.rowsFilegroupsTable, rowContainer], true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -948,15 +1005,16 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
isDefault: false,
|
isDefault: false,
|
||||||
autogrowAllFiles: false
|
autogrowAllFiles: false
|
||||||
};
|
};
|
||||||
if (table === this.rowsFilegroupsTable) {
|
// if (table === this.rowsFilegroupsTable) {
|
||||||
newRow.type = FileGroupType.RowsFileGroup;
|
// newRow.type = FileGroupType.RowsFileGroup;
|
||||||
newRow.isReadOnly = false;
|
// newRow.isReadOnly = false;
|
||||||
newRow.isDefault = false;
|
// newRow.isDefault = false;
|
||||||
newRow.autogrowAllFiles = false
|
// newRow.autogrowAllFiles = false
|
||||||
this.objectInfo.filegroups?.push(newRow);
|
// this.objectInfo.filegroups?.push(newRow);
|
||||||
newData = this.getTableData(FileGroupType.RowsFileGroup);
|
// newData = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
}
|
// }
|
||||||
else if (table === this.filestreamFilegroupsTable) {
|
// else
|
||||||
|
if (table === this.filestreamFilegroupsTable) {
|
||||||
newRow.type = FileGroupType.FileStreamDataFileGroup;
|
newRow.type = FileGroupType.FileStreamDataFileGroup;
|
||||||
newRow.isReadOnly = false;
|
newRow.isReadOnly = false;
|
||||||
newRow.isDefault = false;
|
newRow.isDefault = false;
|
||||||
@@ -976,6 +1034,55 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async onAddDatabaseFileGroupsButtonClicked1(table: azdata.DeclarativeTableComponent): Promise<void> {
|
||||||
|
let newData: any[] | undefined;
|
||||||
|
let newRow: FileGroup = {
|
||||||
|
id: --this.newFileGroupTemporaryId,
|
||||||
|
name: '',
|
||||||
|
type: undefined,
|
||||||
|
isReadOnly: false,
|
||||||
|
isDefault: false,
|
||||||
|
autogrowAllFiles: false
|
||||||
|
};
|
||||||
|
if (table === this.rowsFilegroupsTable) {
|
||||||
|
newRow.type = FileGroupType.RowsFileGroup;
|
||||||
|
newRow.isReadOnly = false;
|
||||||
|
newRow.isDefault = false;
|
||||||
|
newRow.autogrowAllFiles = false
|
||||||
|
this.objectInfo.filegroups?.push(newRow);
|
||||||
|
newData = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newData !== undefined) {
|
||||||
|
// Refresh the table with new row data
|
||||||
|
this.updateFileGroupsOptionsAndTableRows();
|
||||||
|
// await this.setTableData(table, newData, DefaultMaxTableRowCount);
|
||||||
|
|
||||||
|
await table.updateProperties({
|
||||||
|
data: newData,
|
||||||
|
height: getTableHeight(newData.length, DefaultMinTableRowCount)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async onRemoveDatabaseFileGroupsButtonClicked1(table: azdata.DeclarativeTableComponent): Promise<void> {
|
||||||
|
if (table === this.rowsFilegroupsTable) {
|
||||||
|
if (this.rowsFilegroupsTable.selectedRow === 1) {
|
||||||
|
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRow]);
|
||||||
|
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
||||||
|
var newData = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
|
await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': 'hidden' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the individual table rows object and table with updated data
|
||||||
|
this.updateFileGroupsOptionsAndTableRows();
|
||||||
|
await table.updateProperties({
|
||||||
|
data: newData,
|
||||||
|
height: getTableHeight(newData.length, DefaultMinTableRowCount)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares the individual table rows for each filegroup type and list of filegroups options
|
* Prepares the individual table rows for each filegroup type and list of filegroups options
|
||||||
* This will be useful to get the selected row data from the table to get the filegroup property details, helps when have duplicate rows added
|
* This will be useful to get the selected row data from the table to get the filegroup property details, helps when have duplicate rows added
|
||||||
@@ -1000,15 +1107,16 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
* @param table table component
|
* @param table table component
|
||||||
*/
|
*/
|
||||||
private async onRemoveDatabaseFileGroupsButtonClicked(table: azdata.TableComponent): Promise<void> {
|
private async onRemoveDatabaseFileGroupsButtonClicked(table: azdata.TableComponent): Promise<void> {
|
||||||
if (table === this.rowsFilegroupsTable) {
|
// if (table === this.rowsFilegroupsTable) {
|
||||||
if (this.rowsFilegroupsTable.selectedRows.length === 1) {
|
// if (this.rowsFilegroupsTable.selectedRows.length === 1) {
|
||||||
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]]);
|
// const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]]);
|
||||||
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
// this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
||||||
var newData = this.getTableData(FileGroupType.RowsFileGroup);
|
// var newData = this.getTableData(FileGroupType.RowsFileGroup);
|
||||||
await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': 'hidden' });
|
// await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': 'hidden' });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else if (table === this.filestreamFilegroupsTable) {
|
// else
|
||||||
|
if (table === this.filestreamFilegroupsTable) {
|
||||||
if (this.filestreamFilegroupsTable.selectedRows.length === 1) {
|
if (this.filestreamFilegroupsTable.selectedRows.length === 1) {
|
||||||
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.filestreamDataFileGroupsTableRows[this.filestreamFilegroupsTable.selectedRows[0]]);
|
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.filestreamDataFileGroupsTableRows[this.filestreamFilegroupsTable.selectedRows[0]]);
|
||||||
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
||||||
@@ -1040,9 +1148,10 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
return this.createInputBox(async (value) => {
|
return this.createInputBox(async (value) => {
|
||||||
if (table.selectedRows.length === 1) {
|
if (table.selectedRows.length === 1) {
|
||||||
let fg = null;
|
let fg = null;
|
||||||
if (table === this.rowsFilegroupsTable) {
|
// if (table === this.rowsFilegroupsTable) {
|
||||||
fg = this.rowDataFileGroupsTableRows[table.selectedRows[0]];
|
// fg = this.rowDataFileGroupsTableRows[table.selectedRows[0]];
|
||||||
} else if (table === this.filestreamFilegroupsTable) {
|
// } else
|
||||||
|
if (table === this.filestreamFilegroupsTable) {
|
||||||
fg = this.filestreamDataFileGroupsTableRows[table.selectedRows[0]];
|
fg = this.filestreamDataFileGroupsTableRows[table.selectedRows[0]];
|
||||||
} else if (table === this.memoryOptimizedFilegroupsTable) {
|
} else if (table === this.memoryOptimizedFilegroupsTable) {
|
||||||
fg = this.memoryoptimizedFileGroupsTableRows[table.selectedRows[0]];
|
fg = this.memoryoptimizedFileGroupsTableRows[table.selectedRows[0]];
|
||||||
@@ -1581,6 +1690,245 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
|||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
|
//#region Database Properties - Query Store Tab
|
||||||
|
private initializeQueryStoreGeneralSection(): void {
|
||||||
|
let containers: azdata.Component[] = [];
|
||||||
|
const actualOperationMode = this.objectInfo.queryStoreOptions.actualMode;
|
||||||
|
this.operationModeOffOption = 'Off'
|
||||||
|
this.areQueryStoreOptionsEnabled = this.objectInfo.queryStoreOptions.actualMode !== this.operationModeOffOption;
|
||||||
|
// Operation Mode (Actual)
|
||||||
|
const operationModeActual = this.createInputBox(async () => { }, {
|
||||||
|
ariaLabel: localizedConstants.ActualOperationModeText,
|
||||||
|
inputType: 'text',
|
||||||
|
enabled: false,
|
||||||
|
value: actualOperationMode
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.ActualOperationModeText, operationModeActual));
|
||||||
|
|
||||||
|
// Operation Mode (Requested)
|
||||||
|
this.requestedOperationMode = this.createDropdown(localizedConstants.RequestedOperationModeText, async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.actualMode = newValue as string;
|
||||||
|
this.areQueryStoreOptionsEnabled = newValue !== this.operationModeOffOption;
|
||||||
|
await this.toggleQueryStoreOptions();
|
||||||
|
}, this.viewInfo.operationModeOptions, String(this.objectInfo.queryStoreOptions.actualMode), true, DefaultInputWidth);
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.RequestedOperationModeText, this.requestedOperationMode));
|
||||||
|
|
||||||
|
const generalSection = this.createGroup(localizedConstants.GeneralSectionHeader, containers, true);
|
||||||
|
this.queryStoreTabSectionsContainer.push(generalSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeQueryStoreMonitoringSection(): void {
|
||||||
|
let containers: azdata.Component[] = [];
|
||||||
|
// Data Flush Interval (Minutes)
|
||||||
|
this.dataFlushIntervalInMinutes = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.dataFlushIntervalInMinutes = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.DataFlushIntervalInMinutesText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.dataFlushIntervalInMinutes),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.DataFlushIntervalInMinutesText, this.dataFlushIntervalInMinutes));
|
||||||
|
|
||||||
|
// Statistics Collection Interval
|
||||||
|
this.statisticsCollectionInterval = this.createDropdown(localizedConstants.StatisticsCollectionInterval, async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.statisticsCollectionInterval = String(newValue);
|
||||||
|
}, this.viewInfo.statisticsCollectionIntervalOptions, this.objectInfo.queryStoreOptions.statisticsCollectionInterval, this.areQueryStoreOptionsEnabled, DefaultInputWidth);
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.StatisticsCollectionInterval, this.statisticsCollectionInterval));
|
||||||
|
|
||||||
|
const monitoringSection = this.createGroup(localizedConstants.MonitoringSectionText, containers, true);
|
||||||
|
this.queryStoreTabSectionsContainer.push(monitoringSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeQueryStoreRetentionSection(): void {
|
||||||
|
let containers: azdata.Component[] = [];
|
||||||
|
// Max Plans Per Query
|
||||||
|
this.maxPlansPerQuery = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.maxPlansPerQuery = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.MaxPlansPerQueryText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.maxPlansPerQuery),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.MaxPlansPerQueryText, this.maxPlansPerQuery));
|
||||||
|
|
||||||
|
// Max size (MB)
|
||||||
|
this.maxSizeinMB = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.maxSizeInMB = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.MaxSizeInMbText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.maxSizeInMB),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.MaxSizeInMbText, this.maxSizeinMB));
|
||||||
|
|
||||||
|
// Query Store Capture Mode
|
||||||
|
this.queryStoreCaptureMode = this.createDropdown(localizedConstants.QueryStoreCaptureModeText, async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.queryStoreCaptureMode = newValue as string;
|
||||||
|
await this.toggleQueryCapturePolicySection(newValue === localizedConstants.QueryStoreCapturemodeCustomText
|
||||||
|
&& this.requestedOperationMode.value !== this.operationModeOffOption);
|
||||||
|
}, this.viewInfo.queryStoreCaptureModeOptions, this.objectInfo.queryStoreOptions.queryStoreCaptureMode, this.areQueryStoreOptionsEnabled, DefaultInputWidth);
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.QueryStoreCaptureModeText, this.queryStoreCaptureMode));
|
||||||
|
|
||||||
|
// Size Based Cleanup Mode
|
||||||
|
this.sizeBasedCleanupMode = this.createDropdown(localizedConstants.SizeBasedCleanupModeText, async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.sizeBasedCleanupMode = newValue as string;
|
||||||
|
}, this.viewInfo.sizeBasedCleanupModeOptions, this.objectInfo.queryStoreOptions.sizeBasedCleanupMode, this.areQueryStoreOptionsEnabled, DefaultInputWidth);
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.SizeBasedCleanupModeText, this.sizeBasedCleanupMode));
|
||||||
|
|
||||||
|
// State Query Threshold (Days)
|
||||||
|
this.stateQueryThresholdInDays = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.staleQueryThresholdInDays = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.StateQueryThresholdInDaysText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.staleQueryThresholdInDays),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.StateQueryThresholdInDaysText, this.stateQueryThresholdInDays));
|
||||||
|
|
||||||
|
// Wait Statistics Capture Mode - supported from 2017 or higher
|
||||||
|
if (!isUndefinedOrNull(this.objectInfo.queryStoreOptions.waitStatisticsCaptureMode)) {
|
||||||
|
this.waitStatisticsCaptureMode = this.createCheckbox(localizedConstants.WaitStatisticsCaptureModeText, async (checked) => {
|
||||||
|
this.objectInfo.queryStoreOptions.waitStatisticsCaptureMode = checked;
|
||||||
|
}, this.objectInfo.queryStoreOptions.waitStatisticsCaptureMode, this.areQueryStoreOptionsEnabled);
|
||||||
|
containers.push(this.waitStatisticsCaptureMode);
|
||||||
|
}
|
||||||
|
const retentionSection = this.createGroup(localizedConstants.WaitStatisticsCaptureModeText, containers, true);
|
||||||
|
this.queryStoreTabSectionsContainer.push(retentionSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeQueryStoreCapturePolicySection(): void {
|
||||||
|
let containers: azdata.Component[] = [];
|
||||||
|
// Execution Count
|
||||||
|
this.executionCount = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.capturePolicyOptions.executionCount = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.ExecutionCountText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.capturePolicyOptions.executionCount),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.ExecutionCountText, this.executionCount));
|
||||||
|
|
||||||
|
// Stale Threshold
|
||||||
|
this.staleThreshold = this.createDropdown(localizedConstants.StaleThresholdText, async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.capturePolicyOptions.staleThreshold = newValue as string;
|
||||||
|
}, this.viewInfo.staleThresholdOptions, this.objectInfo.queryStoreOptions.capturePolicyOptions.staleThreshold, this.areQueryStoreOptionsEnabled, DefaultInputWidth);
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.StaleThresholdText, this.staleThreshold));
|
||||||
|
|
||||||
|
// Total Compile CPU Time (ms)
|
||||||
|
this.totalCompileCPUTimeInMS = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.capturePolicyOptions.totalCompileCPUTimeInMS = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.TotalCompileCPUTimeInMsText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.capturePolicyOptions.totalCompileCPUTimeInMS),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.TotalCompileCPUTimeInMsText, this.totalCompileCPUTimeInMS));
|
||||||
|
|
||||||
|
// Total Execution CPU Time (ms)
|
||||||
|
this.totalExecutionCPUTimeInMS = this.createInputBox(async (newValue) => {
|
||||||
|
this.objectInfo.queryStoreOptions.capturePolicyOptions.totalExecutionCPUTimeInMS = Number(newValue);
|
||||||
|
}, {
|
||||||
|
ariaLabel: localizedConstants.TotalExecutionCPUTimeInMsText,
|
||||||
|
inputType: 'number',
|
||||||
|
enabled: this.areQueryStoreOptionsEnabled,
|
||||||
|
value: String(this.objectInfo.queryStoreOptions.capturePolicyOptions.totalExecutionCPUTimeInMS),
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.TotalExecutionCPUTimeInMsText, this.totalExecutionCPUTimeInMS));
|
||||||
|
|
||||||
|
const policySection = this.createGroup(localizedConstants.QueryStoreCapturePolicySectionText, containers, true);
|
||||||
|
this.queryStoreTabSectionsContainer.push(policySection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeQueryStoreCurrentDiskStorageSection(): Promise<void> {
|
||||||
|
let containers: azdata.Component[] = [];
|
||||||
|
// Database Max size
|
||||||
|
const databaseName = this.createInputBox(async () => { }, {
|
||||||
|
ariaLabel: this.objectInfo.name,
|
||||||
|
inputType: 'text',
|
||||||
|
enabled: false,
|
||||||
|
value: localizedConstants.StringValueInMB(String(this.objectInfo.sizeInMb))
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(this.objectInfo.name, databaseName));
|
||||||
|
|
||||||
|
// Query Store Used
|
||||||
|
const queryStoreUsed = this.createInputBox(async () => { }, {
|
||||||
|
ariaLabel: localizedConstants.QueryStoreUsedText,
|
||||||
|
inputType: 'text',
|
||||||
|
enabled: false,
|
||||||
|
value: localizedConstants.StringValueInMB(String(this.objectInfo.queryStoreOptions.currentStorageSizeInMB))
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.QueryStoreUsedText, queryStoreUsed));
|
||||||
|
|
||||||
|
// Query Store Available
|
||||||
|
const queryStoreAvailable = this.createInputBox(async () => { }, {
|
||||||
|
ariaLabel: localizedConstants.QueryStoreAvailableText,
|
||||||
|
inputType: 'text',
|
||||||
|
enabled: false,
|
||||||
|
value: localizedConstants.StringValueInMB(String(this.objectInfo.queryStoreOptions.maxSizeInMB - this.objectInfo.queryStoreOptions.currentStorageSizeInMB))
|
||||||
|
});
|
||||||
|
containers.push(this.createLabelInputContainer(localizedConstants.QueryStoreAvailableText, queryStoreAvailable));
|
||||||
|
|
||||||
|
// Prge query data button
|
||||||
|
this.purgeQueryDataButton = this.createButton(localizedConstants.PurgeQueryDataButtonText, localizedConstants.PurgeQueryDataButtonText, async () => {
|
||||||
|
await this.purgeQueryStoreDataButtonClick();
|
||||||
|
});
|
||||||
|
this.purgeQueryDataButton.width = DefaultInputWidth;
|
||||||
|
await this.purgeQueryDataButton.updateCssStyles({ 'margin': '10px 0px, 0px, 0px' });
|
||||||
|
containers.push(this.createLabelInputContainer('', this.purgeQueryDataButton));
|
||||||
|
|
||||||
|
const diskUsageSection = this.createGroup(localizedConstants.QueryStoreCurrentDiskUsageSectionText, containers, true);
|
||||||
|
this.queryStoreTabSectionsContainer.push(diskUsageSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens confirmation warning for clearing the query store data for the database
|
||||||
|
*/
|
||||||
|
private async purgeQueryStoreDataButtonClick(): Promise<void> {
|
||||||
|
const response = await vscode.window.showWarningMessage(localizedConstants.PurgeQueryStoreDataMessage(this.objectInfo.name), localizedConstants.YesText);
|
||||||
|
if (response !== localizedConstants.YesText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.objectManagementService.purgeQueryStoreData(this.options.connectionUri, this.options.database, this.options.objectUrn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async toggleQueryStoreOptions(): Promise<void> {
|
||||||
|
this.dataFlushIntervalInMinutes.enabled
|
||||||
|
= this.statisticsCollectionInterval.enabled
|
||||||
|
= this.maxPlansPerQuery.enabled
|
||||||
|
= this.maxSizeinMB.enabled
|
||||||
|
= this.queryStoreCaptureMode.enabled
|
||||||
|
= this.sizeBasedCleanupMode.enabled
|
||||||
|
= this.stateQueryThresholdInDays.enabled = this.areQueryStoreOptionsEnabled;
|
||||||
|
if (!isUndefinedOrNull(this.objectInfo.queryStoreOptions.waitStatisticsCaptureMode)) {
|
||||||
|
this.waitStatisticsCaptureMode.enabled = this.areQueryStoreOptionsEnabled
|
||||||
|
}
|
||||||
|
await this.toggleQueryCapturePolicySection(this.areQueryStoreOptionsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async toggleQueryCapturePolicySection(enable: boolean): Promise<void> {
|
||||||
|
if (!isUndefinedOrNull(this.objectInfo.queryStoreOptions.capturePolicyOptions)) {
|
||||||
|
this.executionCount.enabled
|
||||||
|
= this.staleThreshold.enabled
|
||||||
|
= this.totalCompileCPUTimeInMS.enabled
|
||||||
|
= this.totalExecutionCPUTimeInMS.enabled = enable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
private initializeConfigureSLOSection(): azdata.GroupContainer {
|
private initializeConfigureSLOSection(): azdata.GroupContainer {
|
||||||
let containers: azdata.Component[] = [];
|
let containers: azdata.Component[] = [];
|
||||||
if (this.viewInfo.azureEditions?.length > 0) {
|
if (this.viewInfo.azureEditions?.length > 0) {
|
||||||
|
|||||||
@@ -328,6 +328,51 @@ export abstract class DialogBase<DialogResult> {
|
|||||||
return this.createButtonContainer(buttonComponents)
|
return this.createButtonContainer(buttonComponents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected addButtonsForTable1(table: azdata.DeclarativeTableComponent, addbutton: DialogButton, removeButton: DialogButton, editButton: DialogButton = undefined): azdata.FlexContainer {
|
||||||
|
let addButtonComponent: azdata.ButtonComponent;
|
||||||
|
let editButtonComponent: azdata.ButtonComponent;
|
||||||
|
let removeButtonComponent: azdata.ButtonComponent;
|
||||||
|
let buttonComponents: azdata.ButtonComponent[] = [];
|
||||||
|
const updateButtons = (isRemoveEnabled: boolean = undefined) => {
|
||||||
|
this.onFormFieldChange();
|
||||||
|
const tableSelectedRowsLengthCheck = table.selectedRow === 1 && table.selectedRow < table.data.length;
|
||||||
|
if (editButton !== undefined) {
|
||||||
|
editButtonComponent.enabled = tableSelectedRowsLengthCheck;
|
||||||
|
}
|
||||||
|
removeButtonComponent.enabled = !!isRemoveEnabled && tableSelectedRowsLengthCheck;
|
||||||
|
}
|
||||||
|
addButtonComponent = this.createButton(uiLoc.AddText, addbutton.buttonAriaLabel, async () => {
|
||||||
|
await addbutton.buttonHandler(addButtonComponent);
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
buttonComponents.push(addButtonComponent);
|
||||||
|
|
||||||
|
if (editButton !== undefined) {
|
||||||
|
editButtonComponent = this.createButton(uiLoc.EditText, editButton.buttonAriaLabel, async () => {
|
||||||
|
await editButton.buttonHandler(editButtonComponent);
|
||||||
|
updateButtons();
|
||||||
|
}, false);
|
||||||
|
buttonComponents.push(editButtonComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeButtonComponent = this.createButton(uiLoc.RemoveText, removeButton.buttonAriaLabel, async () => {
|
||||||
|
await removeButton.buttonHandler(removeButtonComponent);
|
||||||
|
if (table.selectedRow === 1) {
|
||||||
|
// table.selectedRow = [table.data.length - 1];
|
||||||
|
}
|
||||||
|
updateButtons();
|
||||||
|
}, false);
|
||||||
|
buttonComponents.push(removeButtonComponent);
|
||||||
|
|
||||||
|
this.disposables.push(table.onRowSelected(() => {
|
||||||
|
const isRemoveButtonEnabled = true;
|
||||||
|
updateButtons(isRemoveButtonEnabled);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return this.createButtonContainer(buttonComponents)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected createDropdown(ariaLabel: string, handler: (newValue: string) => Promise<void>, values: string[], value: string | undefined, enabled: boolean = true, width: number = DefaultInputWidth, editable?: boolean, strictSelection?: boolean): azdata.DropDownComponent {
|
protected createDropdown(ariaLabel: string, handler: (newValue: string) => Promise<void>, values: string[], value: string | undefined, enabled: boolean = true, width: number = DefaultInputWidth, editable?: boolean, strictSelection?: boolean): azdata.DropDownComponent {
|
||||||
// Automatically add an empty item to the beginning of the list if the current value is not specified.
|
// Automatically add an empty item to the beginning of the list if the current value is not specified.
|
||||||
// This is needed when no meaningful default value can be provided.
|
// This is needed when no meaningful default value can be provided.
|
||||||
|
|||||||
Reference in New Issue
Block a user